diff -Nru muse-2.1.2/al/al.cpp muse-3.0.2+ds1/al/al.cpp --- muse-2.1.2/al/al.cpp 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/al/al.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -27,5 +27,5 @@ //int mtcType = 0; //int division = 384; bool debugMsg = false; - }; + } diff -Nru muse-2.1.2/al/dsp.cpp muse-3.0.2+ds1/al/dsp.cpp --- muse-2.1.2/al/dsp.cpp 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/al/dsp.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -153,7 +153,7 @@ dsp = 0; } -void Dsp::cpy(float* dst, float* src, unsigned n) +void Dsp::cpy(float* dst, float* src, unsigned n, bool addDenormal) { // FIXME: Changed by T356. Not defined. Where are these??? //#if defined(ARCH_X86) || defined(ARCH_X86_64) @@ -176,8 +176,17 @@ //: "%ecx", "%esi", "%edi", "memory" #else - //printf("Dsp: using memcpy\n"); - memcpy(dst, src, sizeof(float) * n); + if(addDenormal) + { + for(unsigned i = 0; i < n; ++i) + dst[i] = src[i] + MusEGlobal::denormalBias; + } + else + { + //printf("Dsp: using memcpy\n"); + memcpy(dst, src, sizeof(float) * n); + } + #endif } diff -Nru muse-2.1.2/al/dsp.h muse-3.0.2+ds1/al/dsp.h --- muse-2.1.2/al/dsp.h 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/al/dsp.h 2017-12-04 21:01:18.000000000 +0000 @@ -69,7 +69,7 @@ for (unsigned i = 0; i < n; ++i) dst[i] += src[i]; } - virtual void cpy(float* dst, float* src, unsigned n); + virtual void cpy(float* dst, float* src, unsigned n, bool addDenormal = false); /* { // Changed by T356. Not defined. Where are these??? diff -Nru muse-2.1.2/al/dspSSE.cpp muse-3.0.2+ds1/al/dspSSE.cpp --- muse-2.1.2/al/dspSSE.cpp 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/al/dspSSE.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -55,7 +55,7 @@ movl 12(%ebp), %esi #; src movl %edi, %eax - andl $12, %eax #; mask alignemnt offset + andl $12, %eax #; mask alignment offset movl %esi, %ebx andl $12, %ebx #; mask alignment offset @@ -191,7 +191,7 @@ movl 12(%ebp), %esi #; src movl %edi, %eax - andl $12, %eax #; mask alignemnt offset + andl $12, %eax #; mask alignment offset movl %esi, %ebx andl $12, %ebx #; mask alignment offset diff -Nru muse-2.1.2/al/sig.cpp muse-3.0.2+ds1/al/sig.cpp --- muse-2.1.2/al/sig.cpp 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/al/sig.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -21,11 +21,9 @@ //============================================================================= -///#include "al.h" -#include "gconfig.h" // Tim +#include "gconfig.h" #include "sig.h" -///#include "xml.h" - +#include "operations.h" namespace AL { @@ -105,37 +103,65 @@ normalize(); } -/* -void SigList::add(unsigned tick, int z, int n) - { - if (z == 0 || n == 0) { - printf("SigList::add illegal signature %d/%d\n", z, n); - // Added by Tim. - return; - } - tick = raster1(tick, 0); - iSigEvent e = upper_bound(tick); - if(e == end()) - { - printf("SigList::add Signal not found tick:%d\n", tick); - return; - } - - if (tick == e->second->tick) { - e->second->sig.z = z; - e->second->sig.n = n; - } - else { - SigEvent* ne = e->second; - SigEvent* ev = new SigEvent(ne->sig.z, ne->sig.n, ne->tick); - ne->sig.z = z; - ne->sig.n = n; - ne->tick = tick; - insert(std::pair (tick, ev)); - } +void SigList::add(unsigned tick, SigEvent* e, bool do_normalize) +{ + TimeSignature ts = e->sig; + std::pair res = insert(std::pair (tick, e)); + if(!res.second) + { + fprintf(stderr, "SigList::add insert failed: siglist:%p sig:%p %d/%d tick:%d\n", + this, e, ts.z, ts.n, e->tick); + } + else + { + iSigEvent ise = res.first; + ++ise; // There is always a 'next' sig event - there is always one at index MAX_TICK. + SigEvent* ne = ise->second; + + // Swap the values. (This is how the sig list works.) + e->sig = ne->sig; + e->tick = ne->tick; + ne->sig = ts; + ne->tick = tick; + + if(do_normalize) normalize(); - } -*/ + } +} + +//--------------------------------------------------------- +// addOperation +//--------------------------------------------------------- + +void SigList::addOperation(unsigned tick, const TimeSignature& s, MusECore::PendingOperationList& ops) +{ + //if (tick > MAX_TICK) + // tick = MAX_TICK; + + if (s.z == 0 || s.n == 0) { + fprintf(stderr, "SigList::addOperation illegal signature %d/%d\n", s.z, s.n); + return; + } + iSigEvent e = upper_bound(tick); + if(tick == e->second->tick) + ops.add(MusECore::PendingOperationItem(this, e, s, MusECore::PendingOperationItem::ModifySig)); + else + { + MusECore::PendingOperationItem poi(this, 0, tick, MusECore::PendingOperationItem::AddSig); + MusECore::iPendingOperation ipo = ops.findAllocationOp(poi); + if(ipo != ops.end()) + { + MusECore::PendingOperationItem& poi = *ipo; + // Simply replace the value. + poi._sig_event->sig = s; + } + else + { + poi._sig_event = new SigEvent(s, tick); // These are the desired tick and sig but... + ops.add(poi); // add will do the proper swapping with next event. + } + } +} //--------------------------------------------------------- // del @@ -161,6 +187,36 @@ normalize(); } +void SigList::del(iSigEvent e, bool do_normalize) + { + iSigEvent ne = e; + ++ne; + if (ne == end()) { + printf("SigList::del() HALLO\n"); + return; + } + ne->second->sig = e->second->sig; + ne->second->tick = e->second->tick; + erase(e); + if(do_normalize) + normalize(); + } + +//--------------------------------------------------------- +// delOperation +//--------------------------------------------------------- + +void SigList::delOperation(unsigned tick, MusECore::PendingOperationList& ops) +{ + iSigEvent e = find(tick); + if (e == end()) { + printf("SigList::delOperation tick:%d not found\n", tick); + return; + } + MusECore::PendingOperationItem poi(this, e, MusECore::PendingOperationItem::DeleteSig); + ops.add(poi); +} + //--------------------------------------------------------- // SigList::normalize //--------------------------------------------------------- @@ -236,7 +292,6 @@ ciSigEvent i = upper_bound(tick); if (i == end()) { printf("ticksMeasure: not found %d\n", tick); - // abort(); return 0; } return ticksMeasure(i->second->sig); @@ -259,8 +314,6 @@ int SigList::ticks_beat(int n) const { - - ///int m = AL::division; int m = MusEGlobal::config.division; switch (n) { @@ -287,7 +340,6 @@ ciSigEvent i = upper_bound(tick); if (i == end()) { printf("timesig(%d): not found\n", tick); - // abort(); return TimeSignature(4,4); } return i->second->sig; @@ -298,7 +350,6 @@ ciSigEvent i = upper_bound(tick); if (i == end()) { printf("timesig(%d): not found\n", tick); - // abort(); z = 4; n = 4; } @@ -367,7 +418,6 @@ ciSigEvent e = upper_bound(t); if (e == end()) { printf("SigList::raster(%x,)\n", t); - // abort(); return t; } int delta = t - e->second->tick; @@ -392,7 +442,6 @@ if(e == end()) { printf("SigList::raster1 event not found tick:%d\n", t); - //return 0; return t; } @@ -451,20 +500,6 @@ return raster; } -//--------------------------------------------------------- -// SigList::write -//--------------------------------------------------------- - -#if 0 -void SigList::write(MusECore::Xml& xml) const - { - xml.stag("siglist"); - for (ciSigEvent i = begin(); i != end(); ++i) - i->second->write(xml, i->first); - xml.etag("siglist"); - } -#endif - void SigList::write(int level, MusECore::Xml& xml) const { xml.tag(level++, "siglist"); @@ -473,31 +508,6 @@ xml.tag(level, "/siglist"); } -//--------------------------------------------------------- -// SigList::read -//--------------------------------------------------------- - -#if 0 -void SigList::read(QDomNode node) - { - while (!node.isNull()) { - QDomElement e = node.toElement(); - if (e.tagName() == "sig") { - SigEvent* t = new SigEvent(); - unsigned tick = t->read(node); - iSigEvent pos = find(tick); - if (pos != end()) - erase(pos); - insert(std::pair (tick, t)); - } - else - printf("MusE:SigList: unknown tag %s\n", e.tagName().toLatin1().constData()); - node = node.nextSibling(); - } - normalize(); - } -#endif - void SigList::read(MusECore::Xml& xml) { for (;;) { @@ -532,21 +542,6 @@ } } -//--------------------------------------------------------- -// SigEvent::write -//--------------------------------------------------------- - -#if 0 -void SigEvent::write(MusECore::Xml& xml, int at) const - { - xml.stag(QString("sig at=\"%1\"").arg(at)); - xml.tag("tick", tick); - xml.tag("nom", sig.z); - xml.tag("denom", sig.n); - xml.etag("sig"); - } -#endif - void SigEvent::write(int level, MusECore::Xml& xml, int at) const { xml.tag(level++, "sig at=\"%d\"", at); @@ -556,35 +551,6 @@ xml.tag(level, "/sig"); } -//--------------------------------------------------------- -// SigEvent::read -//--------------------------------------------------------- - -#if 0 -int SigEvent::read(QDomNode node) - { - QDomElement e = node.toElement(); - int at = e.attribute("at", "0").toInt(); - node = node.firstChild(); - - while (!node.isNull()) { - QDomElement e = node.toElement(); - if (e.tagName() == "tick") - tick = e.text().toInt(); - else if (e.tagName() == "nom") - sig.z = e.text().toInt(); - else if (e.tagName() == "denom") - sig.n = e.text().toInt(); - else - printf("MusE:SigEvent: unknown tag %s\n", e.tagName().toLatin1().constData()); - node = node.nextSibling(); - } - return at; - } - -} -#endif - int SigEvent::read(MusECore::Xml& xml) { int at = 0; diff -Nru muse-2.1.2/al/sig.h muse-3.0.2+ds1/al/sig.h --- muse-2.1.2/al/sig.h 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/al/sig.h 2017-12-04 21:01:18.000000000 +0000 @@ -29,6 +29,8 @@ namespace MusECore { class Xml; +class PendingOperationList; +struct PendingOperationItem; } namespace AL { @@ -37,8 +39,6 @@ #define MAX_TICK (0x7fffffff/100) #endif -///class Xml; - //--------------------------------------------------------- // TimeSignature //--------------------------------------------------------- @@ -58,9 +58,6 @@ TimeSignature sig; unsigned tick; // signature valid from this position int bar; // precomputed - - ///int read(QDomNode); - ///void write(MusECore::Xml&, int) const; int read(MusECore::Xml&); void write(int, MusECore::Xml&, int) const; @@ -83,21 +80,22 @@ typedef SIGLIST::const_reverse_iterator criSigEvent; class SigList : public SIGLIST { + friend struct MusECore::PendingOperationItem; + int ticks_beat(int N) const; - void normalize(); int ticksMeasure(const TimeSignature&) const; int ticksMeasure(int z, int n) const; + void add(unsigned tick, SigEvent* e, bool do_normalize = true); + void del(iSigEvent, bool do_normalize = true); public: SigList(); ~SigList(); void clear(); void add(unsigned tick, const TimeSignature& s); - //void add(unsigned tick, int z, int n); void del(unsigned tick); - - ///void read(QDomNode); - ///void write(MusECore::Xml&) const; + void normalize(); + void read(MusECore::Xml&); void write(int, MusECore::Xml&) const; @@ -114,6 +112,9 @@ unsigned raster1(unsigned tick, int raster) const; // round down unsigned raster2(unsigned tick, int raster) const; // round up int rasterStep(unsigned tick, int raster) const; + + void addOperation(unsigned tick, const TimeSignature& s, MusECore::PendingOperationList& ops); + void delOperation(unsigned tick, MusECore::PendingOperationList& ops); }; extern SigList sigmap; diff -Nru muse-2.1.2/al/xml.cpp muse-3.0.2+ds1/al/xml.cpp --- muse-2.1.2/al/xml.cpp 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/al/xml.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -23,7 +23,7 @@ #include "xml.h" #include "al.h" -#include +#include #include #include @@ -197,7 +197,7 @@ // // start from dummy "muse" property, assuming this is the - // first muse propertie in widget hierarchy + // first muse property in widget hierarchy // int from = meta->indexOfProperty("muse") + 1; int n = meta->propertyCount(); diff -Nru muse-2.1.2/AUTHORS muse-3.0.2+ds1/AUTHORS --- muse-2.1.2/AUTHORS 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/AUTHORS 2017-12-04 21:01:18.000000000 +0000 @@ -22,7 +22,7 @@ Mathias Lundgren lunar_shuttle Joachim Schiele qknight -Fluidsynth logo designed by Josh "Swami" Green. +Fluidsynth logo designed by Josh "Element" Green. diff -Nru muse-2.1.2/awl/aslider.cpp muse-3.0.2+ds1/awl/aslider.cpp --- muse-2.1.2/awl/aslider.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/aslider.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -26,6 +26,8 @@ #include #include +#include "muse_math.h" + namespace Awl { //--------------------------------------------------------- @@ -198,7 +200,7 @@ double AbstractSlider::value() const { if (_log) - return pow(10.0, _value*0.05f); + return muse_db2val(_value); if (_integer) return rint(_value); return _value; diff -Nru muse-2.1.2/awl/awl.cpp muse-3.0.2+ds1/awl/awl.cpp --- muse-2.1.2/awl/awl.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/awl.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -25,5 +25,5 @@ namespace Awl { // int sampleRate = 44100; // int mtcType = 0; - }; + } diff -Nru muse-2.1.2/awl/awl.h muse-3.0.2+ds1/awl/awl.h --- muse-2.1.2/awl/awl.h 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/awl.h 2017-12-04 21:01:18.000000000 +0000 @@ -26,7 +26,7 @@ namespace Awl { // extern int sampleRate; // extern int mtcType; - }; + } #endif diff -Nru muse-2.1.2/awl/CMakeLists.txt muse-3.0.2+ds1/awl/CMakeLists.txt --- muse-2.1.2/awl/CMakeLists.txt 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP (awl_mocs +QT5_WRAP_CPP (awl_mocs aslider.h # awlplugin.h checkbox.h diff -Nru muse-2.1.2/awl/floatentry.cpp muse-3.0.2+ds1/awl/floatentry.cpp --- muse-2.1.2/awl/floatentry.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/floatentry.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -29,6 +29,8 @@ #include #include +#include "muse_math.h" + #define TIMER1 400 #define TIMER2 200 #define TIMEC 7 @@ -42,8 +44,8 @@ // FloatEntry //--------------------------------------------------------- -FloatEntry::FloatEntry(QWidget* parent) - : QLineEdit(parent) +FloatEntry::FloatEntry(QWidget* parent, bool leftMouseButtonCanDecrease) + : QLineEdit(parent), _leftMouseButtonCanDecrease(leftMouseButtonCanDecrease) { _id = 0; _minValue = 0.0; @@ -262,7 +264,7 @@ switch (button) { case Qt::LeftButton: - if (!MusEGlobal::config.leftMouseButtonCanDecrease) + if (!_leftMouseButtonCanDecrease) return; // else fall through case Qt::MidButton: @@ -359,7 +361,7 @@ { double rv; if (_log) - rv = pow(10.0, _value * 0.05f); + rv = muse_db2val(_value); else rv = _value; return rv; diff -Nru muse-2.1.2/awl/floatentry.h muse-3.0.2+ds1/awl/floatentry.h --- muse-2.1.2/awl/floatentry.h 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/floatentry.h 2018-01-29 20:07:03.000000000 +0000 @@ -57,6 +57,7 @@ QString _suffix; int _precision; bool _log; + bool _leftMouseButtonCanDecrease; double calcIncrement() const; @@ -91,7 +92,7 @@ void valueChanged(double, int); public: - FloatEntry(QWidget*); + FloatEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); virtual QSize sizeHint() const; virtual double value() const; int id() const { return _id; } @@ -121,6 +122,9 @@ void setSuffix(const QString& s) { _suffix = s; } bool log() const { return _log; } void setLog(bool v) { _log = v; } + + bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } + void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } diff -Nru muse-2.1.2/awl/knob.cpp muse-3.0.2+ds1/awl/knob.cpp --- muse-2.1.2/awl/knob.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/knob.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -31,7 +31,7 @@ //--------------------------------------------------------- // Knob -/// this is the AwlKnob contructor +/// this is the AwlKnob constructor //--------------------------------------------------------- Knob::Knob(QWidget* parent) @@ -158,9 +158,9 @@ // paintEvent //--------------------------------------------------------- -void Knob::paintEvent(QPaintEvent* ev) +void Knob::paintEvent(QPaintEvent* /*ev*/) { - QRect rr(ev->rect()); + //QRect rr(ev->rect()); QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); @@ -169,18 +169,19 @@ int w = width() - _scaleWidth - 2 * _border; int h = height() - _scaleWidth/2 - 2 * _border; - int xoffset, yoffset; - if (_center) - h -= _markSize; - if (w > h) { + int xoffset, yoffset; + if (_center) { + h -= _markSize; + } + if (w > h) { yoffset = 0; - xoffset = (w - h) / 2; + xoffset = (w - h) / 2; w = h; } - else { + else { xoffset = 0; - // yoffset = (h - w) / 2; // center - yoffset = h - w; // top align + // yoffset = (h - w) / 2; // center + yoffset = h - w; // top align h = w; } diff -Nru muse-2.1.2/awl/knob.h muse-3.0.2+ds1/awl/knob.h --- muse-2.1.2/awl/knob.h 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/knob.h 2017-12-04 21:01:18.000000000 +0000 @@ -66,7 +66,7 @@ Knob(QWidget* parent = 0); ~Knob(); virtual QSize sizeHint() const { return QSize(50, 50); } - virtual int heightForWidth(int w) { return w; } + virtual int heightForWidth(int w) const { return w; } //! return text decoration QString text() const { return _text; } diff -Nru muse-2.1.2/awl/midimeter.cpp muse-3.0.2+ds1/awl/midimeter.cpp --- muse-2.1.2/awl/midimeter.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/midimeter.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -74,7 +74,7 @@ // paintEvent //--------------------------------------------------------- -void MidiMeter::paintEvent(QPaintEvent* ev) +void MidiMeter::paintEvent(QPaintEvent* /*ev*/) { int pixel = height() - sliderSize().height(); double range = maxValue() - minValue(); @@ -82,10 +82,10 @@ if (_invert) ppos = pixel - ppos; - QRect rr(ev->rect()); + //QRect rr(ev->rect()); QPainter p(this); - QColor sc(isEnabled() ? _scaleColor : Qt::gray); + //QColor sc(isEnabled() ? _scaleColor : Qt::gray); QColor svc(isEnabled() ? _scaleValueColor : Qt::gray); p.setBrush(svc); diff -Nru muse-2.1.2/awl/midimslider.cpp muse-3.0.2+ds1/awl/midimslider.cpp --- muse-2.1.2/awl/midimslider.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/midimslider.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -74,7 +74,7 @@ // drawScale //--------------------------------------------------------- -void MidiMeterSlider::paintEvent(QPaintEvent* ev) +void MidiMeterSlider::paintEvent(QPaintEvent* /*ev*/) { int pixel = height() - sliderSize().height(); double range = maxValue() - minValue(); @@ -82,7 +82,7 @@ if (_invert) ppos = pixel - ppos; - QRect rr(ev->rect()); + //QRect rr(ev->rect()); QPainter p(this); QColor sc(isEnabled() ? _scaleColor : Qt::gray); diff -Nru muse-2.1.2/awl/midipanentry.cpp muse-3.0.2+ds1/awl/midipanentry.cpp --- muse-2.1.2/awl/midipanentry.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/midipanentry.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -29,8 +29,8 @@ // MidiPanEntry //--------------------------------------------------------- -MidiPanEntry::MidiPanEntry(QWidget* parent) - : FloatEntry(parent) +MidiPanEntry::MidiPanEntry(QWidget* parent, bool leftMouseButtonCanDecrease) + : FloatEntry(parent, leftMouseButtonCanDecrease) { setRange(-64.0f, 63.0f); setFrame(true); diff -Nru muse-2.1.2/awl/midipanentry.h muse-3.0.2+ds1/awl/midipanentry.h --- muse-2.1.2/awl/midipanentry.h 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/midipanentry.h 2018-01-29 20:07:03.000000000 +0000 @@ -34,6 +34,8 @@ class MidiPanEntry : public FloatEntry { Q_OBJECT + bool _leftMouseButtonCanDecrease; + protected: virtual void valueChange(); @@ -42,8 +44,11 @@ FloatEntry::setValue(v - 64.0f); } public: - MidiPanEntry(QWidget* parent); + MidiPanEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); virtual double value() const { return _value + 64.0f; } + + bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } + void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } diff -Nru muse-2.1.2/awl/midivolentry.cpp muse-3.0.2+ds1/awl/midivolentry.cpp --- muse-2.1.2/awl/midivolentry.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/midivolentry.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -29,8 +29,8 @@ // MidiVolEntry //--------------------------------------------------------- -MidiVolEntry::MidiVolEntry(QWidget* parent) - : FloatEntry(parent) +MidiVolEntry::MidiVolEntry(QWidget* parent, bool leftMouseButtonCanDecrease) + : FloatEntry(parent, leftMouseButtonCanDecrease) { _max = 127; setRange(-98.0f, 0.0f); diff -Nru muse-2.1.2/awl/midivolentry.h muse-3.0.2+ds1/awl/midivolentry.h --- muse-2.1.2/awl/midivolentry.h 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/midivolentry.h 2018-01-29 20:07:03.000000000 +0000 @@ -37,11 +37,16 @@ int _max; + bool _leftMouseButtonCanDecrease; + public: virtual void setValue(double); void setMax(int val) { _max = val; } int max() const { return _max; } - MidiVolEntry(QWidget* parent); + MidiVolEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); + + bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } + void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } diff -Nru muse-2.1.2/awl/panentry.cpp muse-3.0.2+ds1/awl/panentry.cpp --- muse-2.1.2/awl/panentry.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/panentry.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -28,8 +28,8 @@ // PanEntry //--------------------------------------------------------- -PanEntry::PanEntry(QWidget* parent) - : FloatEntry(parent) +PanEntry::PanEntry(QWidget* parent, bool leftMouseButtonCanDecrease) + : FloatEntry(parent, leftMouseButtonCanDecrease) { setRange(-1.0, 1.0); } diff -Nru muse-2.1.2/awl/panentry.h muse-3.0.2+ds1/awl/panentry.h --- muse-2.1.2/awl/panentry.h 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/panentry.h 2018-01-29 20:07:03.000000000 +0000 @@ -33,9 +33,14 @@ class PanEntry : public FloatEntry { Q_OBJECT + + bool _leftMouseButtonCanDecrease; public: - PanEntry(QWidget*); + PanEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); + + bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } + void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } diff -Nru muse-2.1.2/awl/pitchedit.cpp muse-3.0.2+ds1/awl/pitchedit.cpp --- muse-2.1.2/awl/pitchedit.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/pitchedit.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -76,11 +76,11 @@ // mapTextToValue //--------------------------------------------------------- -int PitchEdit::valueFromText(bool* ok) const +int PitchEdit::valueFromText(const QString & /*text*/) const { printf("AwlPitchEdit: mapTextToValue: not impl.\n"); - if (ok) - *ok = false; + //if (text) + //*text = false; return 0; } diff -Nru muse-2.1.2/awl/pitchedit.h muse-3.0.2+ds1/awl/pitchedit.h --- muse-2.1.2/awl/pitchedit.h 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/pitchedit.h 2017-12-04 21:01:18.000000000 +0000 @@ -40,7 +40,7 @@ protected: virtual QString textFromValue(int v) const; - virtual int valueFromText(bool* ok) const; + virtual int valueFromText(const QString&) const; virtual void keyPressEvent(QKeyEvent*); signals: diff -Nru muse-2.1.2/awl/pitchlabel.cpp muse-3.0.2+ds1/awl/pitchlabel.cpp --- muse-2.1.2/awl/pitchlabel.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/pitchlabel.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -81,7 +81,7 @@ if (_pitchMode) s = pitch2string(_value); else - s.sprintf("%d", _value); + s.setNum(_value); setText(s); } diff -Nru muse-2.1.2/awl/posedit.cpp muse-3.0.2+ds1/awl/posedit.cpp --- muse-2.1.2/awl/posedit.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/posedit.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -259,16 +259,23 @@ void PosEdit::updateValue() { - char buffer[64]; + QString s; if (_smpte) { _pos.msf(&cur_minute, &cur_sec, &cur_frame, &cur_subframe); - sprintf(buffer, "%03d:%02d:%02d:%02d", cur_minute, cur_sec, cur_frame, cur_subframe); + s = QString("%1:%2:%3:%4") + .arg(cur_minute, 3, 10, QLatin1Char('0')) + .arg(cur_sec, 2, 10, QLatin1Char('0')) + .arg(cur_frame, 2, 10, QLatin1Char('0')) + .arg(cur_subframe, 2, 10, QLatin1Char('0')); } else { _pos.mbt(&cur_bar, &cur_beat, &cur_tick); - sprintf(buffer, "%04d.%02d.%03d", cur_bar+1, cur_beat+1, cur_tick); + s = QString("%1.%2.%3") + .arg(cur_bar + 1, 4, 10, QLatin1Char('0')) + .arg(cur_beat + 1, 2, 10, QLatin1Char('0')) + .arg(cur_tick, 3, 10, QLatin1Char('0')); } - lineEdit()->setText(buffer); + lineEdit()->setText(s); } //--------------------------------------------------------- diff -Nru muse-2.1.2/awl/poslabel.cpp muse-3.0.2+ds1/awl/poslabel.cpp --- muse-2.1.2/awl/poslabel.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/poslabel.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -68,12 +68,19 @@ if (_smpte) { int min, sec, frame, subframe; pos.msf(&min, &sec, &frame, &subframe); - s.sprintf("%03d:%02d:%02d:%02d", min, sec, frame, subframe); + s = QString("%1:%2:%3:%4") + .arg(min, 3, 10, QLatin1Char('0')) + .arg(sec, 2, 10, QLatin1Char('0')) + .arg(frame, 2, 10, QLatin1Char('0')) + .arg(subframe, 2, 10, QLatin1Char('0')); } else { int measure, beat, tick; pos.mbt(&measure, &beat, &tick); - s.sprintf("%04d.%02d.%03u", measure+1, beat+1, tick); + s = QString("%1.%2.%3") + .arg(measure + 1, 4, 10, QLatin1Char('0')) + .arg(beat + 1, 2, 10, QLatin1Char('0')) + .arg(tick, 3, 10, QLatin1Char('0')); } setText(s); } diff -Nru muse-2.1.2/awl/sigedit.h muse-3.0.2+ds1/awl/sigedit.h --- muse-2.1.2/awl/sigedit.h 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/sigedit.h 2017-12-04 21:01:18.000000000 +0000 @@ -32,8 +32,8 @@ #include namespace AL { - class TimeSignature; - }; + struct TimeSignature; + } namespace Awl { diff -Nru muse-2.1.2/awl/slider.cpp muse-3.0.2+ds1/awl/slider.cpp --- muse-2.1.2/awl/slider.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/slider.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -200,7 +200,7 @@ // r - phys coord system //--------------------------------------------------------- -void Slider::paintEvent(QPaintEvent* ev) +void Slider::paintEvent(QPaintEvent* /*ev*/) { int h = height(); int w = width(); @@ -212,7 +212,7 @@ if ((orient == Qt::Vertical && _invert) || (orient == Qt::Horizontal && !_invert)) ppos = pixel - ppos; - QRect rr(ev->rect()); + //QRect rr(ev->rect()); QPainter p(this); QColor sc(isEnabled() ? _scaleColor : Qt::gray); diff -Nru muse-2.1.2/awl/tcanvas.cpp muse-3.0.2+ds1/awl/tcanvas.cpp --- muse-2.1.2/awl/tcanvas.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/tcanvas.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -483,7 +483,7 @@ return; wpos.setY(y); - // dont move ruler: + // don't move ruler: int ww = _widget->width(); int wh = _widget->height(); @@ -599,12 +599,12 @@ QString s; if (sr == 0) { p.setFont(_font2); - s.sprintf("%d:00", min); + s = QString("%1:00").arg(min); yy = y; } else { p.setFont(_font1); - s.sprintf("%02d", sr); + s = QString("%1").arg(sr, 2, 10, QLatin1Char('0')); yy = y + 7; } int xp = pos2pix(AL::Pos(sec * AL::sampleRate, AL::FRAMES)); @@ -622,7 +622,7 @@ for (int min = min1; min < min2; ++min) { QString s; p.setFont(_font2); - s.sprintf("%d", min); + s = QString("%1").arg(min); int xp = pos2pix(AL::Pos(min * AL::sampleRate * 60, AL::FRAMES)); p.setPen(Qt::black); p.drawLine(xp, y, xp, rulerHeight); diff -Nru muse-2.1.2/awl/utils.cpp muse-3.0.2+ds1/awl/utils.cpp --- muse-2.1.2/awl/utils.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/utils.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -42,8 +42,7 @@ if (v < 0 || v > 127) return QString("----"); int octave = (v / 12) - 2; - QString o; - o.sprintf("%d", octave); + QString o = QString::number(octave); int i = v % 12; QString s(octave < 0 ? valu[i] : vall[i]); return s + o; diff -Nru muse-2.1.2/awl/volentry.cpp muse-3.0.2+ds1/awl/volentry.cpp --- muse-2.1.2/awl/volentry.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/volentry.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -27,8 +27,8 @@ // VolEntry //--------------------------------------------------------- -VolEntry::VolEntry(QWidget* parent) - : FloatEntry(parent) +VolEntry::VolEntry(QWidget* parent, bool leftMouseButtonCanDecrease) + : FloatEntry(parent, leftMouseButtonCanDecrease) { setRange(-60.0f, 10.0f); setSpecialText(tr("off")); diff -Nru muse-2.1.2/awl/volentry.h muse-3.0.2+ds1/awl/volentry.h --- muse-2.1.2/awl/volentry.h 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/volentry.h 2018-01-29 20:07:03.000000000 +0000 @@ -34,8 +34,13 @@ class VolEntry : public FloatEntry { Q_OBJECT + bool _leftMouseButtonCanDecrease; + public: - VolEntry(QWidget* parent); + VolEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); + + bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } + void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } diff -Nru muse-2.1.2/awl/volslider.cpp muse-3.0.2+ds1/awl/volslider.cpp --- muse-2.1.2/awl/volslider.cpp 2013-03-28 15:17:19.000000000 +0000 +++ muse-3.0.2+ds1/awl/volslider.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -25,6 +25,8 @@ #include +#include "muse_math.h" + namespace Awl { //--------------------------------------------------------- @@ -82,7 +84,7 @@ double VolSlider::value() const { - return _log ? (_value <= _minValue) ? 0.0f : pow(10.0, _value*0.05f) + return _log ? (_value <= _minValue) ? 0.0 : muse_db2val(_value) : _value; } diff -Nru muse-2.1.2/ChangeLog muse-3.0.2+ds1/ChangeLog --- muse-2.1.2/ChangeLog 2013-03-28 20:34:30.000000000 +0000 +++ muse-3.0.2+ds1/ChangeLog 2018-01-30 21:00:17.000000000 +0000 @@ -1,3 +1,1228 @@ +30.01.2018 + *** MusE 3.0.2 *** +29.01.2018 + - Silly but very severe bug fixed with selecting midi device for newly + created midi tracks. Looking for the last created midi device started + out of bounds, instant crash when built for release (rj) +28.01.2018 + - Fixes for #604: muse 3.0 fails to build: many undefined references. (Tim) + Packagers wanted to use -no-undefined linker flag. But this also requires + our own -DMODULES_BUILD_STATIC. The fix of 14.01.2018 fixed the main app, + but all the MESS synths still failed to build. + This MAJOR FIX rewrites all the plugins and some main app stuff, so that + there are NO unaccounted dependencies and the two flags should work. + Overview: Several complete core objects and several declarations/definitions + were seperated from the core to their own modules or header files: + sysex_helper_module memory_module evdata_module mpevent_module + wavepreview_module xml_module, and midictrl_consts.h, midi_consts.h + These modules all build SHARED regardless of MODULES_BUILD_STATIC. + Several of the MESS synths directly used core components. Eliminated now. + A huge remaining problem was Deicsonze synth directly used the core's + plugin system (eek!), while SimpleDrums used its own plugin system. + So a new common SimplePlugin library was built. It is similar to our + main app plugin system but simplified for LADSPA, yet still has + instance reference counting and auto-library-close. + Both Deicsonze and SimpleDrums now use this library instead. + (It is an interesting 'clean' plugin system from the ground up. Would be + good to build upon this library to replace the main plugin system!) + TODO: There is a flaw in how our plugin systems connect 2-1 or 1-2 ins/outs. + TODO: There are still a couple of ugly but harmless dependencies, such as + some synths depend on our widgets library but only depnd on widgets which + don't depend on other things, and one synth depends on WavePreview. +23.01.2018 + - Added an appdata file. Distros want it these days (Orcan) +17.01.2018 + - Added ability to /not/ specify a default out, instead a created midi track + defaults to the port with highest number (usually the last created) + this is also the default behaviour now (rj) +16.01.2018 + *** MusE 3.0.1 *** +14.01.2018 + - Moved structure.* into muse/arranger/. -DMODULES_BUILD_STATIC=TRUE should + work now. (Orcan) + - Print out note name on events in Pianoroll + possibly this should be a setting (rj) +11.01.2018 + - Fixed looping not playing first note in loops since 17.12.2017. (Tim) + Was a line in MidiDevice::handleSeek() I was not sure of. + - Fixed midi sustain controller problems since 17.12.2017. (Tim) + Sustain was not handled right, and was being reset to zero. +08.01.2018 + - Fixed build against newer gcc (4.5+ with c++11 enabled) and glibc 2.27+ (Orcan) +08.01.2018 + - Oops! Fix #602 Wave tracks lose sync if muted / unmuted. (Tim) + Slight rework of WaveTrack::getData(). + TODO: Same thing happens if clicking track off/on. +06.01.2018 + *** MusE 3.0.0 *** + Final release of MusE 3.0.0 + - Changed remaining toLatin1() into toLocal8Bit() for fluidsynth path operations. (rj) +05.01.2018 + - Merged feature from Michael Oswald, Mid transformer support for (rj) + randomizing event +04.01.2018 + - Fixed Zynaddsubfx LV2 not working in Muse (#599) (Tim) + (X11UI ui type would not show after the first show, and looked ugly.) + Used QMainWindow instead of QWindow for X11UI LV2 ui type. + Added a flag state->uiIsOpening which forces the gui update timer + to re-send all controls and program to the gui upon re-opening in + LV2PluginWrapper_Window::updateGui + LV2Synth::lv2ui_SendChangedControls. +02.01.2018 + - Fix fluidsynth not loading font although it shows valid filename. (Tim) + Out-of-scope QString byte array constData in LoadFontWorker::execLoadFont. +01.01.2018 + - Ported QThread and removal of QSocketNotifier from + platformindependence branch (rj) + - Removed Gui calls from nongui thread of fluidsynth plugin (rj) + as they caused a lockup. + TODO: The gui calls were there to handle when a soundfont was + not found, to be able to load an alternative. Currently + the fonts are omitted silently, a better solution should be found. + - fixed regression, context menu for tracks had vanished (rj) + - Some attempts to fix 'global cut' crash. Working on it...(Tim) + - New SVG icons for transport stop, play, ff, rew, rew-to-start. (Tim) +31.12.2017 + - As requested: Track monitor now works regardless of track mute. (Tim) + Allows to listen to input while muting playback material. + Note that turning a track off, or 'proxy muting' it will still + mute the track. + - Fix monitored midi output on wrong channel. Editing error from 17.12.2017 + - New master record icon, dual-state red LED style. + - Missing ifdefs for RTAUDIO, build patch from Dennis Schulmeister added (rj) + - Moved instantiation of Add Track Menu so it is only done once (rj) +29.12.2017 + - Revert some new undo stuff and replace with pending operations. (Tim) + Undo was behaving oddly for things like mute, rec, monitor, off etc. +27.12.2017 + - Jack is now the default audio device selection. (Tim) + If that fails, it tries rtAudio with Pulse, and if that fails, + the dummy driver. +24.12.2017 + - Oops! Fix years-old bug preventing a just-moved part from sounding. (Tim) + The MovePart undo/operations were not properly erasing/reinserting + the part because they were passed null track and oldTrack. + For ex. if a wave part was moved before another wave part, it + looked OK on screen but it did not sound. + - Reverted small piece of code that made song dirty too sensitive. + - TODO: Revert a few places, causing some undo operations to be + included in undo/redo history when they weren't meant to be. + - Added sample-rate change according to song file when using RtAudio (rj) + as backend. + - Removed the RtAudioJack option, brings trouble and can't find any (rj) + reasonable use for it. +23.12.2017 + - Some fixes to rtAudio support. (Tim) + Fix compile error on unknown 'DummyAudio'. + Automatically chooses nearest lower sample rate if requested + rate is unsupported. Prevents freeze in MusE msg system. + If driver cannot be started, it warns with a message box but MusE + still runs so that user can change the driver. + NOTE: If system is not set up properly, selecting Pulse backend + while Jack is already running may cause MusE to freeze. Likewise, + selecting Jack backend may cause Pulse to pause. +22.12.2017 + - Added backwards compatibility hack for Splitter, (atleast some) old songs + have only two splits for the arranger, so we inject a third to mimic + tree splits (rj) + - RtAudio with PULSE AUDIO backend is now default. (rj) + Rather drastic change but it should make it much easier for new users. + TODO: Display the current audio driver somewhere in the UI + - Harmonized audio device selection so they are all available in the + config dialog, midi only still can be selected with -a and + RtAudio+Pulse can be selected with -t (rj) +19.12.2017 + - Enabled basic stereo input support (rj) + - RtAudio buffer allocation rework (rj) + - Make RtAudio fallback instead of dummyaudio if jack isn't recognized. (rj) +18.12.2017 + - Fixed horizontal scrolling with shift, again (rj) + - Wired rtaudio to config dialog so backend device, buffers size + and sample rate can be set (rj) + TODO: + > Add proper output port support + > Add input ports + > Make config changes without restart + - backported build fixes in lilv/util.c from 0.24.2 to our version + to allow building without deprecation warnings (by default we treat + them as errors) (rj) +17.12.2017 + * Substantial midi engine redesign: (Tim) + - Merge from engine_fixes_2017 branch: + - To ease working with multiple threads, new classes were developed: + MemoryQueue, and templates TypedMemoryPool LockFreeMPSCBuffer + and LockFreeMPSCRingBuffer. Pooled-memory SeqMPEventList was added + to compliment MPEventList. + - Midi device processing is now completely de-coupled from other threads, + using only MPSC Ring Buffers. It just does 'what it is told' now. + There is no more 'sending back' of midi controller values from midi + device process code to the audio thread - that was like a SERIAL manner. + Example: Previously a keyboard sent a controller, the audio thread picked + it up and sent it to an ALSA device where it's processed in the seq thread, + which then sets the muse hw midi controller representing it, and finally + the gui controls are updated with these values soon after. + Now instead, a keyboard sends a controller, the audio thread picks it up + and sends it in a PARALLEL manner to TWO places at once: An ALSA device + ring buffer where it's processed in the seq thread, AND a ring buffer + processed in the audio thread for setting the muse hw midi controller, + and finally the gui controls are updated soon after. + As one result, midi controls are now updated properly from automation + graphs - independently - whether the device actually got the messages, + or even if no device () selected for the port ! + TODO: Now that we have MPSC ring buffers, I am hoping to allow ALSA midi + to shine, by taking midi input and bypassing the audio thread and + 'directly' talking (putting) to other devices' threads. Eventually the + ALSA thread will 'collect' and process events completely independent + of the audio thread, autonomously without audio buffer size related delays. + - External midi clock sync: Complete redesign! Does not 'miss' recording + the first few notes any more. + There is no more pervasive 'duality' of processing midi in 'ticks' when + externally syncing vs 'frames' for normal tempo map use when not + externally syncing. + Instead, a brief external clock frame timestamp history is kept, and + ALL midi processing is done in FRAMES now, regardless of whether + externally syncing. Yay. + - Large System Exclusive (sysex) message support: MusE can now record + and playback very large sysex messages. Works great with ALSA. + TODO: Jack midi does not support large sysex recording. + - Some fixes to the 'retake' (restart recording) feature. Complete re-write. + Much more stable and realtime-safe now. But still slow with wave tracks. + - All mute, solo, and off controls make the song dirty now. + - Metronome fixes: Fixed silent metronome when any track was soloed! + Fixed metronome dialog: Not updating when song loaded. + Fixed non-realtime-safe reloading of metronome samples. + - Piano and drum list: No more slow waiting for audio thread when each note played. + Fast! Completely de-coupled by putting to the ring buffers instead. + - Several other smaller related and unrelated fixes along the way. + - Enhancements to midi port/device popup menu in track list + drum list. (Tim) + Sorted by device type now. + - New! Synthesizer tracks can change their port from the track list. + * Initial support for RtAudio - Pulse Audio sound output (rj) + For the moment severely limited + - only samplerate 44100 + - only output stereo + - no input +15.12.2017 + - Added support for track pad horizontal scrolling. + Involved adjusting scrolling constants and moving to pixelDelta() for + QWheelEvent, may need further adjustment (rj) +14.12.2017 + - fixed track info drawing to be drawn correctly when using big fonts (rj) +04.12.2017 + - Fixes to instruments, idf files, and sysexes. (Tim) + Fix error in Yamaha 9000pro idf file causing + "Cannot convert string to sysex r ct bn bt f7". + Several submitted idf files have F0...F7 enclosing sysexes, which was not + allowed, so to avoid changing them all, MusE is now more forgiving and + accepts enclosing F0...F7 in idf files and when editing sysex events. +03.12.2017 + - PopupMenu and RoutePopupMenu fixes and touchups. (Tim) + Fix problems with submenus. + Sort and present midi device items by device type. + Slight rearrangement of title items etc. +02.12.2017 + - Clear 'recently opened' file list upon MusE self-restarting. (Tim) +01.12.2017 + - Further to 29.11.2017: Fixed LV2 synths + rack plugins position/size. (Tim) + NOTE: AFAIK we have no control over LV2 plugin 'external' windows, + only the ones where we create the window. + TODO: Same fix for MESS synths... +29.11.2017 + - Bug #571 Plugin GUIs are too narrow: Fixed. (Tim) + Fixed generic GUI windows' initial position/size. + Fixed saving/restoring pos/size of generic GUIs. + Reported a Qt BUG #64773 regarding ignoring setting of position upon show()! + For said bug, wrote a MusE workaround. Tested all generic GUIs OK. + TODO: Similar fix for 'native' synth GUIs (where it supports it...) + TODO: Similar check and fix other non-QDialog usages for pos/size settings. +20.11.2017 + - Removed: Arranger 'Alt' button (and hidden Pianoroll) midi Trackinfo. (Tim) +19.11.2017 + - LV2: Fix continuous "LV2SimpleRTFifo:put(): fifo is full" messages. (Tim) + Rewrote LV2EvBuf ctor: Can't just always call _buffer.resize() here, + which fills with items. Use reserve + assign(1) item for an output buffer, + and resize for an input buffer. Some plugins expose midi output sequence + ports even though they do NOT use them at all. This fix cures an oversight + which caused our receiving buffer to always be full in such cases, never + having a chance to be cleared by the plugin. + - Bug #572: Fix Alt+P crash with unselected regions. (Tim) +16.11.2017 + - LV2: Fix bug #587 All Gtk-based guis crashing on close. (Tim) + Rewrote how we delete things on close. +05.11.2017 + - Fix #592 MusE doesn't record some controllers. (Tim) +08.06.2017 + - QString::sprintf is obsoleted. Changed most sprintf and all QString::sprintf to + QString::arg usage or similar code, or snprintf. Compiler warned of some suspicious + sprintf usage with not enough dest space. Fixed a few other compiler complaints. (Tim) + - lv2Gtk2Support module: Build with -Wno-error to avoid stopping on warnings. (Tim) + - Track list: Fix double error message box on track rename with duplicate name. (Tim) +06.06.2017 + - More frozen MDI sub windows fixes. (Tim) + When style or stylesheet is changed in Appearance, warning now is 'changes don't + take effect until restarted'. Don't even attempt to set style/stylesheet if user + answers no to self-restart prompt. Moved initial setup of style/stylesheet in main() + from after muse->show to just after QApplication is created - now the only + place allowed to set them, except for hacks if 'Fix frozen windows' setting is on. + Note: Setting a stylesheet may still cause it to freeze. Working on the issue... + ------------------------------------------------------------------------------- + [EDIT]: 29/11/2017 The 'frozen MDI sub window' thing was a KDE Breeze/Oxygen style bug. + Fixed upstream in recent versions with help from that team. + ------------------------------------------------------------------------------- +03.06.2017 + * GCC 7 fixes: (Tim) + - LV2 GTK2 support is now optional, due to a current exception breakage in gtkmm. + - Fixed many places using switch/case 'fallthrough'. With gcc7 these now generate + warnings which MusE treats as errors. I used 'comment warning suppressor indicators': + // FALLTHROUGH + - Fixed slight de-referencing problem in JackAudioDevice::portName(). +26.05.2017 + - Further to 22.05: [Sigh] More timer fixes: (Tim) + Info: Alsa HR timer now has hard-coded 1000 Hz limit, unsettable, unreadable :-o + As recommended by ALSA support, step through and try freqs as before. + Fixed div by 0 crash in ALSA timer if freq too high - set ticks to 1 if 0. + In view of the above, the RTC timer now takes priority. MusE tries it first, + and falls back to ALSA (typically the HR timer or else the system timer). + Updated README with modern info about setting up systems for timers. + - Bug #567 from Krzysztof Gajdemski: Older Qt: Build fails at QMetaObject::inherits(). + Oops, remove line. Next line qobject_cast() will take care of it. +22.05.2017 + - Oops, fix ALSA timer fix of 14.05 not working. (Tim) + From kernel 4.4.0-77, some weird default 1000Hz limit on HR timer frequency! + Step through several frequencies looking for the best one. +21.05.2017 + - More mixer strip focus tabbing fixes, including strip-to-strip. Should be OK now. (Tim) +20.05.2017 + - Oops, fix midi meters when midi sliders are in dB mode. (Tim) +14.05.2017 + - Some work on ALSA timer. For ex. not working at all in openSuSE. (Tim) + TODO: More tests. Cleanups. Remove diagnostic printf's. +13.05.2017 + - More frozen MDI windows fixes: (Tim) + Added option "General Settings > Gui Behaviour > Fix frozen MDI sub windows". + Move the theme and style stuff in MusE::changeConfig() into a separate + MusE::updateThemeAndStyle(). Many places call changeConfig, but only + a couple really needed updateThemeAndStyle(). + TODO: Benign XCB connection errors may still occur. Remove diagnostic printf's. +24.04.2017 + - Added Monitor track list column. (Tim) + - Streamlined and tightened up the record-arm and bounce operations. (Tim) + - Monitor on record-arm is on by default now, upon first installation. (Tim) + - Fixed a problem with arranger track resizing taking much too long with + many tracks and mixer open - added a separate SC_TRACK_RESIZED. (Tim) + - Bumped up songfile version to 3.1, so that incompatible binary header data + can be skipped, since a new Monitor column was added. Changed other files. (Tim) +10.04.2017 + - Slight tweak: Don't allow the strips to gain focus. (Tim) + - Added new Czech translation from Pavel Fric. (Tim) +08.04.2017 + - Crash fix: File > New > Audio.med > change wave track 1 to stereo -> crashes soon after. (Tim) + In AudioTrack::copyData(): In two sections concerning '2ch' -> '1ch' mixing: Move 'sp' inside + channel loop. Was allowed to increment beyond, into other variable data, causing silent corruption. + - Also, fixed template "audo.med": Both audio output track's outputs were routed to same jack port. (Tim) + - Slightly simpler track list solo icons. (Tim) +04.04.2017 + * More routing and mixer relief: (Tim) + - New: As requested: Monitor on Record Arm option. Right click any strip, or Global Settings. + - New: Mixer strip volume/pan affect multiple selected tracks. Works fully with midi <-> audio strips. + Works in mixer and arranger, but purposely NOT in pianoroll to avoid accidental changes to other tracks. + Tally of things that currently can operate on multiple selected tracks: + Track list: Channel, port, mute, solo. Strip: Volume, pan, routing buttons. + - New: Strip keyboard focusing and operations: All strip controls now accept keyboard + focus. Strip volume box is now the default PROXY focus for all strips. Any strip control + having focus can be adjusted with up/down keys. Regardless of who has focus, + volume and pan always can be adjusted with universal keys. + Keys: Up/down: Adjust any focused control. + Alt + Up/down: Adjust volume regardless of focused control. + Alt + Left/right: Adjust pan regardless of focused control. + Ctrl + Alt + Left/right: Switch to a neighbouring strip. + Escape: "Get me out of this focused control to something higher-level!" + (Back to the arranger canvas, or the mixer window.) + - New: Show midi volume as dB option. Right click any strip, or Global Settings. + - To achieve midi volume as dB and support inter-strip increments, class MidiCtrlValList hwVal + member type was changed from INT to DOUBLE. Was required to support fractional increments. + - New: Midi strip instrument and patch labels respond to enter/return key to open popup menu. + - New: As requested: Strip name labels: Double-click to edit track name, or right-click context menu. + - New: Strips: 'Tabs' hold palettes of controls. Extra controls have been moved there from right. + The tabs accept keyboard focus and can be activated by hitting return/enter or clicking. + - Fixed: Yay! Finally the strip volume boxes have proper focus and mouse-over rectangles. + There is a dumb problem with KDE Oxygen and Breeze themes: They turn off ALL such rectangles + if the widget-to-font height ratio is too small. This required a HACK to force the + rectangles to be drawn. Fixed with new class LineEdit, now is base of class DEntry. + - Fixed: Strip labels now show colour with a stylesheet active. Looks good with dark stylesheets. + It was a problem with the order and way in which stylesheets were set. (And they override + fonts, so you can't SET then READ a widget font, it always returns what the stylesheet set.) + - Fixed: Strip volume scale drawing was offset too much to the left sometimes. + - Several other fixes along the way. +16.03.2017 + - Draw the frame around all mixer strips. (Tim) +15.03.2017 + * Routing and mixer relief: (Tim) + - Fixed auto-connect routing: Midi tracks by default now only connect to the first + suitable midi port input or output instead of all of them at once. + - Multi-selected tracks editing: New: Routing buttons apply to ALL selected tracks + ie. select two or more tracks and the routing buttons apply to each track. + The selected tracks must be type-compatible. TODO: All other strip controls. + - Added some SVG icons: Mixer window now scales almost perfectly to chosen font. + Moved 'Monitor' button to between 'Off' and 'Rec arm' buttons. + - New class IconButton replaces clumsy ToolButton and icon usage. Has TWO different + icon groups, perfect for our 'proxy' solo and mute. + - All mute buttons now show 'proxy' mute ie. when any OTHER track is soloed. + - All 'Monitor' buttons are now off by default. + * Bug fixes: + - Fixes for 'frozen' Arranger not responding: + NEW! Changing theme style in Appearance dialog automatically restarts MusE. + If a stylesheet is chosen, 'style' is forced to 'Fusion' for now until fixed upstream. + - Fixed duplicate events: From multiple tracks connected to the same inputs and output + if 'Monitor' was active. Protection from same-time events (only) in MPEventList::add(). + - Fixed: Track ordering incorrect after deleting multiple tracks then undoing. + - Fixed: Big CPU % spikes when clicking almost anything in the mixer window. + Excessive redrawMixer() calls: Fix some controls/strip mouse handling: Accept, not ignore. + New specific SC_TRACK_MOVED flag replaces the more general SC_TRACK_MODIFIED when + moving tracks in Arranger or Mixer, and filter out other flags causing redrawMixer(). +16.02.2017 + * Recording from synthesizer midi output supported: (Tim) + - All synths are now marked as both readable/writable. + - Synths can now be selected as a midi input route. + - Received events from a synth will absolutely NOT be echoed back to the + SAME synth instance under any circumstances, even if 'monitor' button is on. + - Recording: buildMidiEventList(): Added protection from duplicate events + sent by certain broken plugins or malformed midi import files. + (Our 'live stuck notes' mechanism already protects from playing 'live' duplicate notes.) + - If output port's instrument has no note-off mode, do not use our 'live stuck notes' + mechanism. This allows for example drum trigger input devices sending NO note-offs, + although you must first select an instrument having no note-off mode. +14.01.2017 + - Drum editor list: Added 'Set field', 'Set row', 'Set list' to right-click menu. (Tim) +13.01.2017 + - Replaced midi editors' control panel knob with compact knob and LCD patch editor. (Tim) + Both use the 'waitless' method of 09.01.2017. + - Improvements to compact knob look and functionality. (Should look OK with 8pt font now.) + - Clear focus after LCD patch edit popup editor closes. FIXME There are three sections, + need to clear focus for now, we can't really have focus hanging around there for the + unaware user - need to add a section 'hilighter/selector'. +09.01.2017 + - FIXED: Midi strip controls much too slow with large audio (Jack) period. (Tim) + - Created and installed new template class 'LockFreeBuffer' to replace clumsy + per-type / fixed capacity multiple implementations. Works with ANY type, and + capacity can be set per-instance. + - Replaced 'waiting' calls of midi strip controls, with the new LockFreeBuffer. + Midi controls are now fast and smooth, like audio controls, even at high Jack periods. + - TODO: Fix midi controller graphs much too slow as well! +07.01.2017 + * New - Monitoring feature: (Tim) + - Midi Tracks and Wave Tracks have a 'Monitor' button. + Push to pass input through to output. + Midi Track 'Midi Thru' was re-branded and re-purposed as 'Monitor'. + - Meters: When in 'rec-arm' mode but not 'Monitor', meters are active. + Wave Tracks: NOTE: Be aware that when 'rec-armed', the meters show + the RECORDING levels pre-fader/rack etc. NOT post-fader. + This was a technical necessity, but actually has benefits: The user + can see the actual recording levels before the signal hits the disk. + - Since monitoring is important and can cause unaware unwanted mixing or + missing audio, the 'Monitor' buttons flash to indicate being active. + - TODO: Add an Arranger track list 'Monitor' column. + Make Wave Tracks NOT monitor by default ?? + - FIXED: Wave tracks now mix playback audio with incoming audio - + you can now hear what's playing as you are recording. +21.12.2016 + - Make some cmake system commands friendlier. (Tim) + - Remove some more old setuid stuff. +20.12.2016 + - Removed libuuid dependency. Replaced with QUuid. (Tim) + Tested OK copying and pasting/cloning parts and notes (places using uuids). +18.12.2016 + * ALSA support is now OPTIONAL ! : + * MusE can now be built as a PURE Jack application: + - Also fixed some problems with various config combinations of DSSI, liblo, and ALSA support. + Note: The smaller DSSI ALSA Compatibility package is NOT suitable for our DSSI support. (Tim) +17.12.2016 + * Eliminated midi sequencer thread when ALSA midi driver is not enabled: (Tim) + When 'ALSA' button is not clicked in midi config dialog, MusE is running PURE Jack, + ie no sequencer thread (midiSeq), and no timer. Pure Jack. +16.12.2016 + - Bug #520 by asbaklm: "Muse fails to start, produces stack smashing error": (Tim) + Further to Bug #548 fix 09.11.2016: + (Very old RTC timer was still causing it to crash upon the resolution request ioctl.) + Did looong overdue ALSA timer code rewrite. Picks best timer such as 1,000,000,000Hz HR Timer. + - ALSA Timer is now checked and chosen first over the RTC timer. RTC could still be chosen + as a LAST resort. Apparently RTC works OK for some users (not using DrumGizmo?). +15.12.2016 + - Update: Fluidsynth MESS plugin GUI updates the song's maps when + soundfonts are removed or the channel or drum column is changed. (Tim) + - Besides drum patches, note sample names are now cached for ALL melodic + patches as well, they can be obtained although nothing uses them yet. (Tim) +14.12.2016 + * New - Fluidsynth MESS synth plugin automatically shows correct drum note names: (Tim) + - No more manually loading a drum map file ;-) + - Load up some SoundFont(s) in the plugin, and try it out. Works with multiple + SoundFonts in different channels (be sure to mark 'drum' column). + - Automatically looks for libinstpatch-1.0 (from Swami) and uses if found. + - Door is open for our Instrument Editor to allow PER-CHANNEL drum maps,since + the system supports it now. You could add it manually to .idf files for now. + - TODO: Remove (L) (R) etc. from name strings. ie. find a better way to make + reasonable names out of multi-sample notes. + Does not update maps yet when fonts removed. Please toggle the track channel + or some other way force a map update manually for now. + - Thanks to Swami author 'Element Green' for help. +11.12.2016 + - LV2 Synth Tracks: Fixed patch not changing when patch list popped up. (Tim) + Was simple change to include QVariant::UInt in *::patchPopupActivated(). +10.12.2016 + - Fixed lingering Arranger bug not restoring size properly on project reload. (Tim) +09.12.2016 + * More 'New drum' track fixes. 'Old drums' invisible now, going soon: (Tim) + - Midi Instrument Editor: A default Patch Number is allowed now + (HBank = LBank = Prg = don't care). See GM instr for ex. + ALL drum map items are editable now including Hide, Mute, Channel, Port, Anote. + When 'Save' is clicked, song is updated immediately with changes to the instrument. + - Drum Tracks: Added Track Drum Map overrides. Right-click any map item to see options. + Channel, Port and ANote can be edited now. + - Added a standard GM drum map to our GM instrument, on the default patch (see above). + - Old midi export bug: Some controllers not exported! My typo: Changed 0x1000 to 0x10000 + in exportmidi.cpp:addController(). + - Midi Import/Export Dialog: Added two options for how drum controllers should be exported, + see their tooltips/whatsthis for more detailed info: + Apply drum map Port, Channel and ANote overrides + Drum map channel overrides go to a separate track + - Automatic conversion of loaded projects to use new drum tracks instead of old. + Warning: The project will be SAVED with NEW drum tracks! + - Old drum tracks hidden now. Will be removed later since they are no longer needed. +09.11.2016 + - Bug #548 by funkmuscle: New DrumGizmo version 0.9.11 causes crash. (Tim) + Removed seteuid()/setreuid() code supporting ancient givertcap tool (kernel privileges). + Was interfering with DrumGizmo's use of semaphores. + TODO: Remove further old rtcaps code such as main.cpp:getCapabilities(). + Tested OK so far, with self- or externally- started Jack, or dummy audio driver, + and as root user as well, and with Jack running non-realtime. + Note: This may also prevent things like our RTC timer code from accessing the RTC + as non-root, but RTC is old and should be replaced anyway, and it usually required + 'poking' it beforehand in a terminal anyway - unless MusE is run as root which still + works as always. +30.09.2016: + * A fix for 'New drum' tracks!: + - Drum maps were not being updated according to track's current port and channel, + and current state of program controller. So a hook into the global heartbeat timer + was added and all drum maps are updated if necessary. Tested OK so far with + multiple parts open in Drum Editor, and changing various ports, channels, and patches. + NOTE: This means the Drum List can, and will, CHANGE DYNAMICALLY for example + with more than one program change in a part, or simply dialing in a new port, channel + or patch numer! TODO: Test + fix/improve/optimize more, and remove debugging messages. (Tim) +29.09.2016: + *** MusE 3.0.0pre2 *** + Second prerelease of MusE 3.0.0 + + - Bug #526 by jphaenlin: Crash on cut with scissors while master open. (Tim) + Seems old: In class MidiEditor: _pl (parts list) can be NULL, so protect the rest of the code. +28.09.2016: + - Completed Robert's mixer strip selection + keyboard navigation features. (Tim) + Now works in BOTH mixers A and B. Tested OK all functions. + Use the arrow keys and ctrl and shift to navigate strips and adjust volume and pan. +25.09.2016: + - Midi ports/synths configuration dialog: Allow multiple selections in device list. (Tim) +24.09.2016: + - Anti-alias the small routing channel circles. (Tim) +23.09.2016: + * Revenge of the knobs!: + - Complete re-design of mixer strips, reinstating knobs but much smaller and compact. (Tim) + - Switchable between knobs and sliders, via Global Settings or via right clicking any mixer strip. (Tim) + - Also switchable value display, to minimize clutter. + - Replaced clumsy three-slider midi patch editor with single-line custom LCD editor. (Tim) +17.08.2016: + - Bug by Martin Drautzburg: ALSA Midi controllers not working. (Tim) + - Bugs by Michael Oswald: Change 3 delete to delete[]. In LV2Synth ctor, free _nPname later. (Tim) +03.08.2016: + - Merged fix for bug where configuration storage had stopped working, from AndreeeCZ (rj) + - Updated templates to 3.0 (rj) + - Fixed color-theme switching no longer working (rj) + - Added another idf from Dennis Schulmeister, for Yamaha Motif XS (rj) +27.06.2016: + - Added instrument definition files from Dennis Schulmeister for: + * Korg Krome (Bank Map KORG).idf + * Korg Krome (Bank Map GM2).idf + * Roland-MT32.idf + * Lexicon-MX200.idf + * Yamaha-Rex50.idf +20.06.2016: + - Added connection between arranger and mixer so selecting a track carries over to the + mixer. Mostly a visual queue but some keyboard navigation and editing is possible (rj) +19.06.2016: + - Removed XML printout that warned of malformed xml, mostly due to the parser being + incomplete, still prints the warning when debug is enabled and when we are reasonably + sure it's wrong. (rj) + - Added keyboard navigation between strips in mixer. Ctrl+left/right to move between + strips. Up/Down to change volume slider (TODO add pan left/right) (rj) + - Strips can also be selected with clicking and ctrl+clicking to select more than + one. (TODO select strips from arranger too and allow to move all selected strips)(rj) +16.06.2016: + - Removed the ability to delete strips from the mixer, atleast temporarily, as it was + crashing (rj) +02.06.2016: + - Rolled back transport icons to previous version (the new version looked bad with some themes) + and made a new rec icon (rj) +30.05.2016: + - LV2: Fix playback crash with certain midi event times, in LV2SynthIF::getData(). (Tim) + - Midi file export: Added missing Aftertouch, PolyAftertouch support! (Tim) +28.05.2016: + - LV2, LinuxVST: Fixed Aftertouch, PolyAftertouch, Pitch improper composition. (Tim) + - LV2: Fixed sending redundant program changes with every small seek. (Tim) + - LV2, DSSI: Support 16384 Banks, arranged as two 8-bit-wide, 7-bit High and Low Bank values. (Tim) + - Thanks to Will Godfrey for reporting and testing with yoshimi. +21.05.2016: + - Fixed bug with ctrl+rightclick resize of notes no longer working (rj) + - Fixed shortcut for Pointer tool in right click tool menu (rj) +20.05.2016: + - Fixed bug with reversed Input/Output hidden in mixer menu (rj) +19.05.2016: + - Upon startup, Jack midi inputs/outputs are (finally) now paired together + into single MidiDevices just like the ALSA MidiDevices. (Tim) + How it works: If the DIFFERENCE between two Jack midi port names are EXACTLY + the words "capture" and "playback", or "input" and "output", or "in" and "out", + or finally "c" and "p", then the ports are considered a matched pair. + (The best names or aliases are picked, "system:" are avoided, similar to our + persistent port routes feature.) +16.05.2016: + - Updated lv2Support tree to latest versions. (Tim) + Instructions on how to do future upgrades added to lv2Support/ChangeLog. + - Added new cmake option 'ENABLE_LV2_SUPPLIED', to use supplied vs. system + lv2 support libraries (lv2, lilv, serd, sord, sratom etc). Default off. (Tim) + - Removed pre-built lv2Gtk2Helper libraries and folder. (Tim) + Replaced with lv2Gtk2Support folder, always built by cmake. + * Thus, GTK2 dev packages gtkmm-2, gtk+-2 are now REQUIRED for LV2 Gtk2 UI support. + - Fix compile stoppage warning if using Jack-1. The very old 'case 4: hack' in + processSync and JackAudioDevice::getState(). (Tim) + - Suppress unwanted Jack info terminal output. (Tim) +13.05.2016: + - Added instrument file for Classic Cantabile SP-250 / Medeli SP4200 from + Mariotte91 at fontenay.tk (rj) +29.04.2016: + *Important bug fix: About two dozen CORRUPT usages of QByteArray::constData(). (Tim) + In: routepopup.cpp lv2host.cpp helper.cpp dssihost.cpp jackmidi.cpp conf.cpp + vst_native.cpp minstrument.cpp + Symptoms could vary wildly. On my system MusE refused to load the config file. + - Fixed compile stoppage due to latest Jack obsoleting jack_port_set_name(). (Tim) + Created 'dlopen' function pointers for jack_port_set_name() and the brand new + jack_port_rename(), and arbitrated between them. + - Disable ElidedLabel auto-font size for now (midi strip instrument/patch labels). (Tim) +22,04.2016: + *** MusE 3.0.0pre1 *** + First prerelease of MusE 3.0.0 +21.04.2016: + - New: Accu-Slider: All sliders support 'shift' control for single-stepping + even with thin sliders. (Tim) + The shift control changes from 'coarse' to 'fine' adjustment mode so + you can zero-in on an exact value even with very short sliders. +14.04.2016: + - Added missing Aux slider and Midi Program slider Appearance colours. (Tim) + - Complete rewrite of 13.04.2016. Non-modal colour chooser, fully interactive. (Tim) + - Do not save part colour names in colour configuration files. (Tim) +13.04.2016: + * New - Live colour editing and Colour Chooser Dialog in Appearance Settings: (Tim) + - All target colours are updated live in realtime. With functional Apply, Cancel, and OK. +12.04.2016: + - Implement meter colours from appearance settings. (Tim) + - Finish default colour scheme. (Tim) + - Fix obvious crash when loading another song: Do not connect songChanged() + directly to Arranger and Pianoroll trackinfo's Strips. Instead, the Strips' + songChanged() is called by Arranger and Pianoroll. (Tim) +11.04.2016: + (By Tim:) + - Completed (mostly) work on arranger, trackinfo, strip makeovers. + * New Appearance Settings: + - Several new 'Mixer' colour settings: Sliders, gain, pan, aux etc. + - New: Load and save colour settings. Share or backup your custom colours! + - New: Aliased small fonts for certain controls (brighter, sharper!), + with adjustable on/off size in Settings. + * New audio/midi mixer strip 'Component Rack' system, streamlined and unified: + - Configurable controllers, properties, and other selectable 'components'. + - Not quite ready yet, user-configurability hidden for now... + * New UI control functions: + - All descendants of SliderBase (Slider, CompactSlider, Knob, ThinSlider etc) + now support three adjustments 'modes': + Normal: Control moves with mouse. + Jump: Holding ctrl/middle mouse button jumps immediately to the value. + No-track: Holding meta changes the value only when mouse is released. + - Reworked automation rec/play functions to fully support these modes. + * Bug fixes: + - New: Discrete trackinfo panel parts have been replaced with a + unified class TrackInfoWidget, where classes WidgetStack, Scrollbar, + and TrackInfoLayout are combined into one widget. + Undesirable recursive things in the previous layout have been minimized + by using this container to hold everything. + - Full rework: Bottom area of arranger: Caused runaway width in some cases! + TODO: Drum editor has a similar sizing problem, difficult to shrink sometimes. + - Reworked Global and Appearance Settings dialogs to be smaller. + - Some other small fixes. +06.04.2016: + - Fixed show/hide menu choices in mixer for strips (rj) + - Remember last mixdown directory (rj) +24.03.2016: + - Changed lilv/lib.c: lilv_lib_open(): Added RTLD_DEEPBIND to dlopen() call. (Tim) + Fixes at least two Qt4-based external-ui synths (synthv1, drumk) crashing. + See corresponding UI dlopen call in LV2Synth::lv2ui_ShowNativeGui() + for a similar fix and test results (better but the UI still CRASHES here). + No other such plugins were found, in order to test this fix further... + - LV2: Fix very slow right-click synth 'presets' menu with many items (ex. amsynth). (Tim) +21.03.2016: + * New Plugin Path editor in Global Settings: (Tim) + The LADSPA_PATH DSSI_PATH VST_PATH LINUX_VST_PATH and LV2_PATH environment variables + are now read only ONCE upon first-time run, to fill the Global Settings boxes. + Afterwards these variables are SET (internally) by MusE at each startup, from the + paths in the Global Settings boxes, so that the various plugin systems can find plugins. + (Actually, LV2 /required/ setting LV2_PATH since we use the recommended lilv_world_load_all, + and there doesn't seem to be any other way to set the paths programmatically in that case.) + - Updated -h help text to reflect changes. +21.03.2016: + - LV2 fix: Support PowerOf2BlockLength - Was missing! Supports more plugins! (Tim) + - LV2 fix: Support FixedBlockLength - Our process routines weren't respecting this! (Tim) + If any controller input was adjusted, a plugin would 'gurgle' or might soon CRASH. + - LV2: Add debugging output (-D option) while discovering plugins. (Tim) + - LV2: Report if a plugin is ignored due to any unsupported 'required feature'. (Tim) + + Tested OK! With MOD Pitch Shifter series of plugins (they require fixed-block AND power-of-2). + At both 1024 and 128 block size, these plugins' controllers responded smoothly, both with + rapid manual adjustment and graphs, and did not 'gurgle' and did not crash after much abuse. + +19.03.2016: + - Fix toolbars initial positions in all windows. They were all on one line. (Tim) + Our shared toolbar system can now use a toolbar's objectName to /replace/ + an existing toolbar, in MusE::setCurrentMenuSharingTopwin(). + This keeps common toolbars in-place (by default) in all the windows, + especially as applied to the Main Window and the Arranger. + - Rebuild Tempo Signature and CPU toolbars to allow shrinking. (Tim) + - Fix problems with arranger minimum size and saving/restoring window sizes. (Tim) +14.03.2016: + * Arranger Track Info panel makeover: (Tim) + - Add midi strip (finally!) + - Add splitter handle for resizing the panel (finally!) + - Add 'Alt' button to switch between midi strip and old midi track info. + - TODO: Splitter initial default sizing issues. Saving/reloading should be OK though. + Also maybe some crashes. Massive cleanups. +12.03.2016: + - Fix routing popup menus consuming all CPU power upon mouse movement. (Tim) + In RoutePopupMenu::event(), removed "case QEvent::MouseMove". Seems to test OK without it. +11.03.2016: + - Make it slightly easier to recompile lvGtk2Helper library: Separate 32/64 bit makefiles + and automatic cmake choosing of user-compiled over pre-built version. (Tim) +09.03.2016: + - Converted python scripts to python3 and explicitly launch with python3 (rj) + - Fixed focus issue with running scripts, if launched from an editor, the editor + would be pushed back behind the arranger (rj) +08.03.2016: + - Project dialog put focus on name (rj) + - Punch in recording now works better when recording is started after the punch point. Live wave recording doesn't seem + to work though. (rj) +07.03.2016: + - Pianoroll: Make midi strip vertical scrollbar appear to the RIGHT of the strip, not intrude into strip. (Tim) + Similar to how arranger trackinfo scrollbar works. Much harder than it sounds: + Added new custom trackinfo_layout module. Moved class ScrollBar into its own module. Added Splitter::setPosition(). + - Fix regression in MusE::readPart() causing not to re-open saved open midi editors. (Tim) +01.03.2016: + - Fix remaining audio engine bug from 31.10.2015: Certain routing conditions produced garbage audio output, + and pinned channel meters. (Tim) + Fairly substantial re-write of AudioTrack::copyData(), ::getData() and variants, especially input routing. + Removed AudioTrack::addData(). Cleanups of massive comments etc. Test OK with many multi-channel scenarios. +26.02.2016: + - Save state of: Routing preferredRouteNameOrAlias and routerGroupingChannels, + and routing dialog button routerExpandVertically. (Tim) +25.02.2016: + Plugin generic UI fixes, new features: (Tim) + - New: Class Meter now has orientation: Horizontal or Vertical. + - New: Class Meter now has scale markings (just like class Slider). + - Fix bug #470: Built-in MusE UI (such as LADSPA TAP Reverberator) not responding to controls. + - Replaced plugin generic UI ThinSlider with Slider. + - New: Plugin generic UI sliders have scale markings. + - Fixed backwards plugin generic UI output meters: Replaced VerticalMeter with Meter, + and enabled meter scale markings. + - Removed class ThinSlider and VerticalMeter from cmake build (but not from project). + - Related: Mixer strip volume sliders: Reinstate a 'thumb' knob. +24.02.2016: + - Remember the state of ALSA button in Midi Configuration dialog. (Tim) + It is a global setting, not per-song. +22.02.2016: + Merge from midi_engine_fixes branch: (Tim) + - Added MidiTrack 'stuck notes' and 'stuck live notes' lists and support. + For better handling of stuck notes for example when muted or rec-arm turned off. + - Split 'stuck notes' handling between devices and tracks. + - Removed midi instruments 'null parameters' setting, moved into General Settings midi tab as + 'Send null parameters' checkbox, default is off. Removed the setting from all .idf files. + - Added General Settings midi tab 'Optimize controllers' (don't send dupl params or values). Default off. + - Finished note-off / zero-velocity work of 13.01.2016 (it required the live stuck notes list above). + - Apply midi track velocity/compression settings (and drum volume) to note-offs, not just note-ons. + - Removed 'curProg' members from SynthI, and no longer read or saved in song. + Replaced with MidiOutputParams struct in class MidiDevice. It holds current banks and prog, + and current (N)RPN parameters and data, to faciltate the 'Optimize controllers' setting above. + The current program settings for all channels are still stored in the song's MidiPort controllers. + - Added class MidiEncoder to handle all midi controller input encoding. (WIP) + - Added more latency functions to track and effects rack. (WIP) +17.02.2016: + (By Tim) + - Audio automation graphs fixes: + - Greatly reduced CPU usage while playing and graph(s) showing. + (Update only required rectangles and paint only requested rectangles - not the whole darn window.) + - Fixed flickering when moving mouse. + - Added drawing layers: Draw lines, then vertices, then text. With moderate transparency. + - Added shaded box for text, to stand out better. + - Vertex selection: Guaranteed to be able to select a vertex even in densely crowded multiple graphs. + - New vertices: Guaranteed to be able to add vertices to lines even in densely crowded multiple graphs. + Note that preference is now given to selecting/moving existing vertices over adding new ones. + The vertex 'hit' zone is such that if you really wanted to add another vertex to an already dense + graph portion, then you should zoom in more. + - Undo system fix in Undo::merge_combo(): + if (this->combobreaker || other.combobreaker) // Flawed? + if (other.combobreaker) // Changed. Test OK. + - Audio automation graphs: Added Undo support. Smart Undo support using the above Undo fix. + Dragging a vertex around makes only ONE Undo step. + This is the first real 'editing' usage of Smart Undo using Florian's combo/breaker system. + Score Editor uses it for undo-able note selection. But there was a bug where it required two undo clicks, + and the above Undo fix seems to cure it now, one click only. + Another good example of Smart Undo usage (TODO): Editing the Track List channel column. +12.02.2016: + - Added instrument definition for Yamaha S30/S80 made by Dennis Schulmeister (rj) +09.02.2016: + (By Tim) + - Non-shared event lists followup: Wave event sndfile handles are now truly + unique - overlapping multiple compressed audio file instances will play + now instead of halting MusE. + TODO: When a wave part is edited, ensure other instances' caches are updated. + Note: The master sndfile list is now meaningless. (It now allows MULTIPLE instances of SAME wave file.) + It will either soon be REMOVED, or embellished/re-purposed. As will the Clip List Editor. + It is kept for now, so that the Clip List Editor can help debug what is going on. + - Fixed crashes when using wave editing functions. Possibly fixes recording spikes too? + - Close or re-open sndfiles in deleted or undeleted wave tracks/parts/events. + - Fixed part copying in Duplicate Tracks function. Dialog added new modes. + - Revert old: Do not auto-snap newly recorded wave parts to the grid. (Importing never did this.) + - Streamlined some complex multi-cycle operations (stopRolling, recordStop, deleteTracks etc.) + into single undoable operation lists (one cycle). + - Eliminated the (very old) brief total audio dropout on transport stop or rec stop. + - Fixed the (very old) audio FIFO underrun copious errors, at song load then play. + - Added undo of recorded audio automation controller events. + - Good speed ups to midi sustain controller real-time stop/start disabling/reenabling loops. + (Rearranged the loops - it was iterating all 200 ports x 16 channels). + - Still some related finishing work to do... +07.02.2016: + - Added widgets dependency on instruments, liste and deicsonze targets (Orcan) +06.02.2016: + - Fixed wave writing corruption when realtime wavedrawing was enabled (rj) + - Arm all selected tracks in the arranger when clicking REC (rj) +29.01.2016: + - add shortcuts for mute tracks (SHIFT+m) and solo tracks (SHIFT+S) (rj) +28.01.2016: + - print DSP load in CPU toolbar + - allow gain in channelstrip to attenuate also (rj) +25.01.2016: + - added midi drum name to simpledrums sample config (rj) +22.01.2016: + - raise config dialogs after apply to keep them visible (rj) + - removed startSong from the config in the default case + as we already know what should be there (rj) + - Pianoroll/drum editor: Ensure new events are selected. (Tim) + - Refix: Midi import empty tracks: Small revert. Test OK with user-supplied broken/fixed .mid (Tim) +21.01.2016: + - Midi import: Ignore empty tracks with zero data. Fixed corruption if so. (Tim) + - Midi import/export: Delete allocated memory. + *New: Midi export now has 'Use running status', and the instr/mode + port/device + metas can now be turned off for a 'cleaner' file. + - New wavedrawing mode (set to default) with drawing only a tiny outline of the wave (rj) + - DidYouKnow dialog now allows multiline help texts (rj) + - Several of the colors written to the config were not properly read back, now fixed (rj) + - Fix drum editor: In Canvas::findCurrentItem: Selecting notes required 2 clicks, + this also fixes dragging of selected same-time notes not dragging correct notes. (Tim) +20.01.2016: + - Fixed mixer crash when deleting track in updateStripList. (Tim) + - Fixed audio rack plugin problems: From README.effects-rack: + "For mono tracks with plugins having more than one audio input or output, + MusE uses the first input or output and ignores the rest." + This was not happening. It was using the second output. Fixed. + Optimization: When changing track channels: Previously we deleted all rack plugin + instances and rebuilt them. Now it only deletes or rebuilds what is necessary. + Fixed: A good side effect of the above is that rack plugin GUIs no longer + disappear when changing track channels :-) + Fixed: Multiple-instance plugin GUIs were being updated from the last instance's + control values. Now they are updated from the first instance (ie. 'left'). + - Arranger fixes: Fixed problems with channel unwanted double increments. + Fixed problems with popup editors, click/doubleclick passing to parent, + for example channel editor box causing 'track lock' column icon to light. + *Arranger Feature - Multiple selected tracks common property editing: + - You can now edit a property and it will change all the selected tracks. + Currently works for track channel column and midi output port. + +18.01.2016: + - Strips are raised when dragged in mixer (they should also get a solid background) + - When sorting mixer as arranger moving strips in mixer affects arranger also (rj) +16.01.2016: + - Added ability to rearrange mixer A and/or sync it to the arranger look + Features: + - Traditional look + - Sync to arranger + - Manually rearrange mixer with drag and drop + - Allow individual strips to be hidden (re-enable them from the view menu) + Still bugs to iron out and allow mixer B to also work (rj) +08.01.2016 + - Fixed icon pos for transport toolbar (rj) +15.01.2016 + - Routing popup menu: Fix stereo grouping mode not connecting both channels + if second channel is clicked. (Tim) +14.01.2016 + By Tim: + - System-wide accuracy fixes, part 1: + Enforcement of audio controller double-precision floating point accuracy internally, + all the way up to the lowest-level structure Port (in plugin.h). Port now has a double value + besides the existing float value. All track, plugin, and synth code changed to use double-precision + wherever possible (only Track can take advantage of double - plugins/synths only use floats). + + - Due to inaccuracy in log*() functions, muse_math.h was added where special macros are defined for: + muse_log10r() and muse_val2dbr() (round to the nearest .000001), and muse_db2val(). + These use log10 and pow10. Some critical code will use the poor-accuracy fast_log*() functions + in fastlog.h for speed, while other code takes advatage of (requires) our more accurate macros. + - Audio strips: Ensured and verified accuracy and round-trip consistency of vol, aux, gain sliders + and labels, and their controllers. + Fixed strip slider problems honouring Settings 'Slider minimum value'. + This value now means 'off' or 'muted' (-inf dB), not some value below it like before. Test OK. +13.01.2016 + By Tim: + - More note-off fixes related to 21.12.2015: Added Instrument Definition Files NoteOffMode attribute. + Enforcement of internal note-off velocity usage, and no internal midi Event zero note-on velocities. + - Fixed old -l locale switch problem by adding QLocale::setDefault(); +12.01.2016 + By Tim: + - Mixer makeover in progress: + - Replaced strip knobs with horizontal sliders. + - General redesign of strip look and sliders. + - Strips are expandable now with a right-edge 'handle'. + - Fixed old mixer problem with unpainted areas (solved by QWidget::setUpdatesEnabled()). +06.01.2016 + - Added 'clipper led' to mixer's audio strips (danvd) +02.01.2016 + - Added Native VST FX support (rack insert plugins) (danvd) +27.12.2015 + - Midi Instrument Editor: Fixed errors from ancient missing Qt3 signals. + Fixed problems with instrument / patch / controller lists. Tests OK now. (Tim) +21.12.2015 + - Rewrited midi events code for lv2 host. Fix for #435 (danvd) +13.12.2015 + - Added support to resize notes and parts to the left with pencil tool (issue #292) (danvd) +11.12.2015 + - Added LV2 UI open behavior (danvd) +06.12.2015 + - Added live update of wave parts while recording (danvd) +03.12.2015 + - Added Native VST shell plugins support (danvd) +03.12.2015 + - Added non-discard restart recording function (a-la multi-take, SHIFT+space is default shortcut) (danvd) +01.12.2015 + - Added recording restart function (CTRL+Space to activate while recording) (danvd) +21.11.2015: + - Auto resample imported wave files to project's sample rate (danvd) +18.11.2015: + - Added double click on Port column to open the gui of a synth (rj) +31.10.2015 + By Tim E. Real: + * New routing capabilities: + - New: Persistent routes: If a device disappears (say USB Midi) we remember it. + Currently works for Jack audio and midi routes. + TODO: Expand concept to include persistent Synth Tracks, Rack Plugins and Tracks. + + - New: Multi-channel Audio Track routing. Any channel to any channel. + Fully functional (although maybe a sound engine bug - an unconnected + channel may have repeating nonsense sound data.) + Audio tracks still only have up to two channels (or N synth audio channels), + but this feature may help later for true multi-channel Audio Tracks. + + Users connect individual Audio or Midi channels with 'Channel Routes', + or all at once with 'Omni' Routes (similar to previous behaviour). + + - New: Graphical Routing dialog redone and enhanced. Almost complete. + Added to routing popups as "Open Advanced Router". + Middle Connections View window, which can be scrolled. + Thick lines are Omni Routes and thin ones are Channel Routes. + Each pane's tree has textual Omni nodes, plus some have a + 'Channel Bar' sub node (an array of connectable Channels). + All nodes are organized in 'Category' nodes. + Itemized Master Connection List synchronized with left and right panes. + Filter buttons: Show only selected nodes in left or right pane + Show only routeable nodes in the other (right or left) pane + Show all 200 Midi Ports (else it filters unused/unrouted ones) + + - Routing popup menus redone. Test OK, some cosmetic tweaks aside. + Shows Channel Routing matrices, plus easy single-click Omni Routes. + Uniform audio and midi routing popup menus. + Full keyboard support. + - Note: Keyboard action activation for ALL popup menus, including routing, + has been changed from space bar to Enter. Hold ctrl to 'stay open'. + + - New: Routing popup menus for Midi Tracks now allow you to do Jack Midi + routing from the menus! Like "Connect this Midi Track to Jack Midi port + XYZ via Midi Port 123:MyJackMidiDeviceName". + + - Midi Track to Audio Input routing (for soloing chaining) greatly simplified. + Instead of a cumbersome dual system of Midi Track to Midi Port route and + Midi Port to Audio Input route PER track channel, now it is simply a more + direct Midi Track to Audio Input Omni Route. No 'channels' involved. + This behaviour is different than before, but should still serve the purpose + of soloing chaining. + + - Instead of one route for all 16 Midi Track channels using a bit-wise mask for + the Route::channel member, it is now reverted back to one route per channel. + This makes all routing uniform (Route::channel now always means 'a channel'), + no special code for Midi Track routes. + (Applies only to Midi Track input routes for now.) + + * File operations: + - A new system of warning users about loading older song file versions + is in place and operational. It uses the MusE File Version number + found in the xml module. Increment it wisely given these new warnings, + especially if 'auto-converters' are 'quietly' enacted while loading. + - Added conversion code to transform older loaded files into the format + which this version currently stands at. + - Saving and loading tests OK so far but may need a few tweaks. + + * Midi config dialog: + - New: Device List added, with 'Delete' button. + - New: Command-line flag disables startup 'midi auto-discover/fill devices/ports'. + - Default Jack Midi Device names have been changed. + Instead of 'mirroring' the long Jack Midi port names, helpful but NOT good - a + different Jack port or multiple ports can be connected making the name meaningless - + Midi Devices are now simply named like "Jack Midi 1". + - Fixed improper Native VST version number display in synth list. + +23.10.2015 + - Moving to 3.0.0 release (danvd) +23.09.2015 + - New cpu usage metering algorithm without jack dependency (danvd) +01.07.2015 + - Implemented "Normalize wave part" menu item for wave track parts (danvd) +24.06.2015: + - Added information in About box about supported plugins. Feel free to + add more status info! (rj) +30.05.2015: + - Patch from Michele Ballabio to compile muse without LV2 enabled added (rj) +15.05.2015 + - Added some missing initializations for native VST synths (rj) + - Added some instructions for compiling with Steinberg VST SDK instead + of the bundled Vestige header (rj) + +05.05.2015 + - Added multichannel support for SimpleDrums (+routing ability) (danvd) + - Added channel meters for SimpleDrums (danvd) + - Updated SimpleDrums config to version 3 (added routing config saving/restoring) (danvd) +15.04.2015: + - Fixed bug #363, Len not initializing correctly when importing midi files (rj) +03.04.2015: + - Made raster colors in midi editor editable and section dividers in track list (rj) +15.03.2015: + - Changed string handling to allow international characters in fluid synth + as well as wave tracks, this has affected several parts of muse, not the + least undo/redo. Internally handling of strings have been changed to QString + instead of char * in several places. This to minimize conversions as this + is a big source of errors. String conversions from .toLatin1 have been changed + to toLocal8Bit and QString::fromLocal8Bit in many places. I think this is + right way but it needs more testing. Fixes bug #361 (rj) +02.03.2015: + - Slow motion moving of editing of automation with shift key (rj) +04.02.2015 + - Added smooth transition from big to small meter value (danvd) +31.01.2015 + - Load lv2 gtk2 ui helper on the first native gui call (danvd) + - Removed lots of warnings, tested both on clang and gcc (danvd) + - Added -Werror to DEBUG build type (danvd) +28.01.2015: + - MusE 2.2.1 release (rj) + - Optimizations to lv2 rtfifo class (danvd) + - Fix for potential crash on startup due to defaultStyle (danvd) +20.01.2015: + - Change toggle functions in track list to not include output tracks (rj) + - Added ability to change all midi tracks or all drum tracks to the same port with + control clicking on the output port name (rj) +18.01.2015: + - Added shortcut to edit track name, F2 (rj) + - Added multi toggle of mute, solo and midi track type. + If multiple tracks are selected and one of them is clicked they will all be affected by the operation + Additionally for solo and mute, if the Control key is used, ALL tracks will be toggled. (rj) +17.01.2015: + - Added link library sndfile for SimpleDrums (rj) + - typo with duplicate tracks shortcut fixed (rj) +15.01.2015: + - Ported code to Qt5. Port works rather stable - no crashes and artifacts + during 4 days testing (danvd) + - Added scrollable submenus option to global settings (danvd) + - Added CPU load toolbar (danvd) + - Optimized RT Fifo class used for lv2 plugin <-> UI communication. + This reduced CPU load by 1-2% :) (danvd) + - Integrated Presets submenu into midi track's programs menu for LV2 synths (danvd) + - Implemented presets saving and updating for LV2 synths/rack plugins (danvd) + - LV2 presets are stored in common format and can be + shared between lv2 hosts (danvd) + - Integrated lv2,lilv,sord,serd,sratom,zix libraries into MusE's source tree. + Now MusE can be compiled without external LV2 dependences at all (danvd) + - Reworked Appearance::defaultStyle static QString variable (static var init fiasco fix) (danvd) +15.01.2015: + - Fixed bug with setting name for dupliated tracks and reworked a bit (rj) + - Some shortcut changes (rj) + - Changed metronome accent to only affect offbeat (rj) +12.01.2015: + - Added new Czech translation from Pavel Fric (rj) +06.01.2015: + - MusE 2.2 release (rj) +02.01.2015: + - Moved LV2 effects to own submenu when adding in arranger, + same solution as for Windows VST synths, a bit hacky (rj) +01.01.2015: + - Calculate filenames when duplicate tracks improved (rj) + - fixed two crash bugs when instantiating plugins did not succeed (rj) + - Added LV2_PATH info in help (rj) +28.12.2014: + - Some more features to metronome, click selection etc, + needs some more testing (rj) +27.12.2014: + - Pretty print customData in song file for lv2 plugins (rj) + - Added progress dialog to midi filter execution (rj) +22.12.2014: + - MusE 2.2beta2 release (danvd) +21.12.2014: + - Added support for LV2 path MAP and MAKE extensions (danvd) + - Added support for LV2 LOG extension (danvd) + - Revorked synths menu popup (added scroll ability) (danvd) + - Improved lv2 synth programs popup (added bank submenus) (danvd) +19.12.2014: + - Added support for CV type lv2 ports (danvd) + - Implemented Atom_Event_Transfer support for plugin<->UI communication (danvd) + - Plugins with unknown port types are skipped properly now (danvd) + - Absent default plugin values are initizlized to zero now (danvd) + - Improved UI resize callback (some X11 uis were cropped) (danvd) +05.12.2014: + - added LV2 host support (danvd) + - MusE 2.2beta1 testing release. Mainly for lv2 host testing and feedback (danvd) +08.11.2014: + - Readded plugin parameters persistence in the plugin dialog (rj) +19.10.2014: + - Moved Wine VST synths to separate sub menu (rj) + - Allow unselecting default midi output port (rj) + - Fixed value update of pitch knob in SimpleDrums plugin (rj) +15.10.2014: + - Fixed crash bug iterating through events in script executor (rj) + - Added script RemoveAftertouch (rj) +12.10.2014: + - Reworked the PluginDialog to use a ui file and give more filter possibilities (rj) + - Added [HOME] button to file open dialog (rj) + - Added QMessageBox warning about missing synths (rj) + +10.10.2014: + - Don't allow changing the port of a soft synth, it makes no sense (rj) +07.10.2014: + - Changed detection of liblo to make it a strict requirement as the code would not + build without it when DSSI was enabled (rj) +27.09.2014: + - Added QMessageBox warning about missing plugins (rj) + - Ignore undo/redo while recording (rj) +01.09.2014: + - Fix crash reported by LakeIshikawa: Pressing delete while clicking or dragging events or parts. (Tim) + Ignore commands (like del) in each editor's ::cmd() handler if current canvas drag mode is not DRAG_OFF. + - Added Arranger menu item Edit -> Delete, does same thing as pressing delete, either parts or tracks. + - Added new SongChanged flags: SC_PART_SELECTION SC_TRACK_SELECTION, to distinguish from events SC_SELECTION. + - Fixed copy/paste problem: Paste copies not clones, if the original parts/tracks have been deleted. + Added 'bool ClonePart::is_deleted', set/cleared when parts/tracks deleted/added. Checked in Part::readFromXml(). +16.06.2014: + - Transformation to non-shared event lists (started by Florian) is virtually complete. (Tim) + Events have an ID now, shared among clone parts. + New: Fast pending operations lists: They replace RT "execute/revert ops stage-2". + - A few more commands support Undo/Redo now, like setting global tempo. + - Handful of (un)related fixes along the way. +29.04.2014: + - Added more hints to DidYouKnow dialog (rj) + - Improved README.txt for share/scripts (rj) + - Improved scripting interface, picks time signature from the start of the + part, with -D argument extra debug info is written and the data file + used for data transfer isn't removed. (rj) + - Added new midi script TempoDelay (rj) + - Improved swedish translation (rj) + - Changed behaviour of selecting midi device for new midi tracks. Now there + is no default set, instead the most recent midi device is selected. Though + if a default midi channel is selected this will take precedence (rj) + - +28.04.2014: + - Fixed issue #350 (rj) +11.01.2014: + - Fix song not 'dirty' on most operations (close was not prompting to save). (Tim) + Added 'emit sigDirty()' at end of Song::executeOperationGroup3() and ::revertOperationGroup3() + if operations list not empty. +10.01.2014: + - Instrument Editor now basically complete: Added Initialization Sequence editor. (Tim) + Initialization sequence sysex's can be entered manually or chosen from pre-defined list + using sysex event editor. + - Sysex event editor now allows selection from pre-defined Instrument Sysex list. + - Revised and edited Roland SD-50.idf by Patrick (split into GM2/nonGM2). + - Fixed GM2.idf file prog numbers wrong. + - MusE now imports GM2 midi files. (Properly selects GM2 instrument.) + - Fixed several build-system ordering problems. Also some code cleanups, tweaks. +06.01.2014: + - Midi instrument can now be changed from midi track info panel. (Tim) + - Some minor code tweaks, code cleanup in mtrackinfo.cpp, confmport.cpp. +06.01.2014: + - Added (and fixed) Roland SD-50 .idf file by Patrick. Needs more work: split in two .idfs (Tim) + - Added GM2 .idf file created by me Tim. + - Fixed a couple of improper "&apos" xml substitutions in xml.cpp +17.10.2013: + - Fixed drag&drop plugins sometimes not working when built with optimize (rj) +10.10.2013: + - Added a visual metronome to Bigtime (rj) +09.10.2013: + - Changed track dragging to move instead of swap (rj) + - Removed projects from 'recent' menu if they no longer exist (rj) +08.10.2013: + - Added line drawing of tempo in graphical master track editor (rj) + - Fixed bug (issue #342 Moving Aux sends crashes muse) (rj) +03.10.2013: + - Added keyboard shortcuts in midi editors for functions, Step record, Midi input, + Play events, Inc/Dec velocity (rj) +28.09.2013: + - Changed default shortcut in PianoRoll for moving to current position from C to I + the former was conflicting with the default global shortcut for metronome (rj) +27.09.2013: + - Changed beat lines to a darker shade (rj) +02.09.2013: + - Display git hash and date in the about box instead of svn revision (Orcan) + - Bumped the year string to 2013 in the about box +01.09.2013: + - Mods/fixes to Midi Input Transformator plugin: (Tim) + Fixed some bugs when reading procVal2Op from song. + 'Program' is now allowed 'Event type' selection. + 'Toggle' is a new Processing Value#2 Operation. It toggles between Value#1 and Value#2. + This means for example a standard midi foot controller (which only sends program changes) + can now be used to send TOGGLED midi CC values, say back to an effects unit + thus the foot controller becomes like regular 'chained' stomp boxes, operating + upon individual effects. +14.05.2013: + - When midi in is enabled in drum editor the selected track is moved along with + the triggered key (rj) +12.05.2013: + - Fixed bug with playback of drums clicking on notes in the new drum editor, + it was playing the wrong instrument (rj) +08.05.2013: + - Yet another MAJOR audio engine and plugin/synth process chain re-write. (Tim...) + - Track controllers (vol, pan) now sample-accurate. + - Track controllers (vol, pan) slope limited for near-noiseless operation. TODO: User settings. + - Fixed: "Duplicate Selected Tracks" crashes. Also now copies plugins and synths too. + - DSSI + Native VST: Fixed not remembering generic/native GUI pos/size. TODO: DSSI native GUI. + - Fixed regression by flo: Sliders were recording multiple automation points at same value. + - Fixed canvases not remembering position/zoom - everything reopens at exact same pos/zoom now. + - Fixed automatable audio GUI controls 'jumpy' in TOUCH mode if heavy graphics causing slowdowns. + When pressed, any control now truly 'disengages' from competing/intefering controller stream. + - Improved audio automation modes, + READ mode can now be interrupted by GUI or ext control. + - MESS synths (esp Deicsonze): Controls (like Track Info program) and synth controls now mirror + each other, both ways. + - Deicsonze softsynth: Fixed several problems: Not remembering settings + ladspa plugin settings, + midi controllers were in wrong domain - moved to NRPN14 type. TODO: Fix 'biased' midi controllers + like pan and detune etc. + - Native VST: Call idle periodically. Makes some plugins like Glitch work, as per LAD mail. +05.04.2013: + - Change window title when there are unsaved changes (rj) + - Add auto-save feature, when enabled tries to save after 5 minutes + when there has been changes, unless transport is running (rj) +04.04.2013: + - Added pitch control to SimpleDrums, first version (rj) + - Fixed Ctrl+arrows in the arranger so the track list is scrolled (rj) +29.03.2013: + - MusE 2.1.2 released! (rj) 28.03.2013: - Allow reading old drummaps for new style drumtracks (rj) - Added metronome icon in main window (rj) @@ -68,7 +1293,7 @@ - Fixed gain adjustment with 'Other' choice in wave editor, it was reversed (rj) 02.02.2013: * Solved: "Old style" Drums: Track channel/port re-enabled, drum list columns default to them. (Tim...) - The drum list port and channel columns are now blank by default until overriden by user. + The drum list port and channel columns are now blank by default until overridden by user. When blank they default to the track port and channel. Internally, -1 is now an acceptable value for members DrumMap::port and ::channel, meaning default to track. All built-in drum maps have been changed, as well as the initial and blank DrumMap structures. @@ -428,7 +1653,7 @@ - Remap or remove mappers when rack items moved or removed. 04.06.2012: - Side branch r2_0_extra, copied from release_2_0: - * Feature: Midi control of audio paramters. (Tim) + * Feature: Midi control of audio parameters. (Tim) - More important timing, accuracy fixes. 03.06.2012: - Merged documentation from local source to repository LaTeX document, 80% done (rj) @@ -724,7 +1949,7 @@ Fixed mixer windows raising above others, even briefly: Do not raise mixer windows in MusE::focusInEvent(). - Fixed bigtime leak: Not destroyed. Give it a parent in MusE::showBigtime(). 20.12.2011: - - Litle break for some polish. Route button icons. Rebuild midi track info ui file - was 3 columns not 2. (Tim) + - Little break for some polish. Route button icons. Rebuild midi track info ui file - was 3 columns not 2. (Tim) * Fixed: Hopefully, finally: Non-appearing track info scroll bar. (Tim) Simple movement of recursive call of sb->setVisible() to end of TLLayout::setGeometry(). - Audio strip prefader now controls audio. (Tim) @@ -916,7 +2141,7 @@ 27.10.2011: - Adapt context menu in arranger after what track types are currently visible there Now it only allows to create the track types which are set to be visible (rj) - - Uninitalized value fixes in ScoreEdit (flo) + - Uninitialized value fixes in ScoreEdit (flo) 26.10.2011: - Changed visibility of utility tracks so they are not seen by default in arranger (rj) - Added a default audio connection in default.med (rj) @@ -1086,7 +2311,7 @@ TODO: Fix some painting corruption, improve discrete display, add transparency etc. etc. - Applied compilation patch to rhythmbase.ui by Jean-Damien Durand. (Tim) 28.08.2011: - - Fixed wierd column expansion for [rec] column by removing autoexpand of the last column. Still + - Fixed weird column expansion for [rec] column by removing autoexpand of the last column. Still something fishy with moving columns (rj) 27.08.2011: - Changed mousewheel behaviour, vertical scrolling is default and horizontal scrolling (shift) is reversed (rj) @@ -1167,7 +2392,7 @@ - removed "midi in" button from score editor (flo93) - added a spinbox to the arranger's "Ch:" column (flo93) 29.05.2011: - - moved cut,copy'n'paste to functions.cpp, removed unneccessary duplication (flo93) + - moved cut,copy'n'paste to functions.cpp, removed unnecessary duplication (flo93) - changed behaviour of paste: now the pasted, not the original notes are selected (flo93) 28.05.2011: - fixed dragging and resizing of track header, also changed default index of new @@ -1203,19 +2428,19 @@ Function-related changes: - made all the stuff in the "functions" menu global - added crescendo, legato functions, improved erase function - - removed unneccessary _to and _quant* - stuff from piano roll and drum edit + - removed unnecessary _to and _quant* - stuff from piano roll and drum edit this also involved changing the Toolbar1 - changed behaviour for step-recording: the note-length is now the "raster"- value instead of the "quant"-value - changed behaviour for scripts: the "quant"-parameter they get isn't the quant-combo's setting, but the snap-combo's now - - removed unneccessary short cuts: SHRT_OVER_QUANTIZE etc. instead added + - removed unnecessary short cuts: SHRT_OVER_QUANTIZE etc. instead added SHRT_QUANTIZE Score-Editor-related changes: - using the proper AL::raster functions in the score editor - added support for selections to the score editor - - distance between staves is now automatically increased if neccessary + - distance between staves is now automatically increased if necessary - fixed "change pitch freezes scoreeditor"-bug - added clef-combobox to midi tracks (rj and flo) - don't install the *.mf files any more @@ -1322,8 +2547,8 @@ * editing tempo signature is now atleast working * editing time position is now atleast working * double clicking a line moves the playpointer to this position if we are not playing - * changed adding tempo/sig/key so they pick the postion from the play pointer instead - of just incrementing the time position, I can't fathom when that would be preferrable + * changed adding tempo/sig/key so they pick the position from the play pointer instead + of just incrementing the time position, I can't fathom when that would be preferable (if there is a time, I'm sure someone will let me know :) ) - song changed not implemented yet - undo/redo is not implemented @@ -1589,7 +2814,7 @@ o Removed explicit rpath to ${QT_LIBRARY_DIR}. cmake should add it automatically if there's need. o Removed unnecessary linkage to libQtSvg. - o utils are installed with executable permissons. + o utils are installed with executable permissions. 25.12.2010: - Added fancy background selection widget with live preview to Appearance Settings. (Orcan) - Applied Geoff King's second capitalization patch. @@ -1904,7 +3129,7 @@ - Fixed audio and midi strips and converted to Qt4. Looks OK now, but will be better... (Tim) TODO: Convert Slider, Knob, EffectRack, ComboBox classes etc. (After that the strips will be perfect.) 08.11.2010 - - Add missing link to libdl.so. Caused bulid failure on Fedora 14 (Orcan) + - Add missing link to libdl.so. Caused build failure on Fedora 14 (Orcan) - Fixed strip name label colours, also name label auto-font-sizing. (Tim) - Updated some window flags (stay on top, delete on close...) (Tim) 07.11.2010 @@ -2103,9 +3328,9 @@ when adding or deleting markers. (T356) * Fixed: Graphics corruption in arranger and track list when vertically scrolling. (T356) - Changed rectangle to full w/h in 'shift down' section of View::setYPos() and TList::setYPos(). - Should not have to do this, but it cured my problems. No noticable change in speed. - - Arranger corruption occured on more than one machine with xorg nv, ati drivers. More severe arranger AND track list - corruption occured with proprietary drivers. Corruption is also observed in several other windows (ex. ladspa browser) + Should not have to do this, but it cured my problems. No noticeable change in speed. + - Arranger corruption occurred on more than one machine with xorg nv, ati drivers. More severe arranger AND track list + corruption occurred with proprietary drivers. Corruption is also observed in several other windows (ex. ladspa browser) but there's not much I can do, the corruption is found in ordinary usage of QListView for example. * Changed: Increased arranger vertical scroll step, was too slow to scroll. (T356) * Possible fix: Auto-scroll in pianoroll and arranger take waaay too long to stop scrolling. (T356) @@ -2152,7 +3377,7 @@ * Fixed: Shortcut for moving between tracks in arranger. Ctrl+Up/Down (rj) 19.04.2010 * Patch: Fix for libdir bug causing synths to not be available on some 64bit systems. By Orcan Ogetbil (rj) - * Fixed: Drawing of grid when snaping to less than measure (rj) + * Fixed: Drawing of grid when snapping to less than measure (rj) 12.04.2010 * Fixed: LADSPA rack effects: Do not display LADSPA output controls (such as latency). (T356) * Fixed: Bug when cancelling 'save as' operation, says 'file exists'. Caused by my earlier compressed save fixes. (T356) @@ -2163,8 +3388,8 @@ 06.04.2010 * Fixed: Jack midi, and DSSI: Midi controller knobs, sliders, boxes etc. not updating with current values. (T356) - Copied code in MidiPort::sendEvent() to JackMidi::processMidi() and DssiSynthIF::getData(). - * Fixed: DSSI: Crashing with unkown controllers sent to synths. (T356) - - Ignore unkown controllers in DssiSynthIF::processEvent(). + * Fixed: DSSI: Crashing with unknown controllers sent to synths. (T356) + - Ignore unknown controllers in DssiSynthIF::processEvent(). 05.04.2010 * Added: Rec enabled track moved with selection when only one track is rec enabled (rj) * Changed: Made canvas show grid the default (rj) @@ -2231,7 +3456,7 @@ * Fixed: Jack midi output: Stuck or missing notes. (T356) - Must only be one jack_midi_event_reserve() per event in handle_jack_midi_out_events(). 31.01.2010 - * Changed: Midi sync window: Clock is now seperated from other real time commands (play/stop etc). (T356) + * Changed: Midi sync window: Clock is now separated from other real time commands (play/stop etc). (T356) 31.01.2010 * Fixed: Midi sync in: Should be very solid and 'in time' now, play/stop/continue. (T356) - Re-coded to begin incrementing immediately upon first-clock detection. Forbid MusE to send transport commands @@ -2285,7 +3510,7 @@ 19.01.2010 * Fixed: Saving/loading compressed .gz/.bz2 MusE .med files, + loading compressed .mid/.kar files (save broken, off for now). (T356) - Changed filedialog.cpp:getSaveFileName() and MusE::loadProjectFile1. - - Added seperate save file dialog filters in globals.cpp. + - Added separate save file dialog filters in globals.cpp. - Disabled exporting of compressed midi/karaoke files (.mid/.kar) for now because a compressed file is opened as a pipe, and pipes can't seek, resulting in a corrupted midi file in MidiFile::writeTrack(). @@ -2357,7 +3582,7 @@ * Fixed: Add Synth popup menu: Show names AND descriptions. Now sorted, too. Also separate MESS, DSSI, VST, and Other. (T356) - Added app.cpp:populateAddSynth() function. * Fixed: Increased buffers for reading xml. Song files with large patchdata from vstis caused crashes (rj) - * Fixed: Self inflicted bug with setting inital song loading in general configuration (rj) + * Fixed: Self inflicted bug with setting initial song loading in general configuration (rj) 06.12.2009: * Fixed: List popup synths by description. Cleaned up soft synth main list. Added persistent names, descriptions, ver, etc. (T356) 05.12.2009: @@ -2649,7 +3874,7 @@ - My fault. Message is displayed when there is one output and one or more wave tracks, and the user has clicked on the output but not a track, too. * Added: Start 'Wave editor' edit menu item, with configurable shortcut key. (T356) - - This will handle mutiple wave part editing. The existing right-click wave part popup menu + - This will handle multiple wave part editing. The existing right-click wave part popup menu 'wave edit' item did that too, but now has been reverted back to opening a single wavepart only, just like midi parts. * Streamlined: All reading of 'part' xml is now done by a new routine. (T356) @@ -2791,7 +4016,7 @@ * Fixed: Some issues with 'Follow Song' setting and position cursor position/visibility. (T356) - Added 'xorg' in ppos calculations in Canvas::setPos() (and others like WaveView, but that still isn't quite right.) - For windows which have an appreciable x origin (space at the left of the contents), the play cursor was - too far right or not visible sometimes, especialy with large horizontal zoom, particularly the arranger. + too far right or not visible sometimes, especially with large horizontal zoom, particularly the arranger. With 'Follow Continuous', the play cursor should now stay about in the middle of the windows. With 'Follow Page', the view should now more or less always move in full 'pages'. 27.03.2009 @@ -2855,7 +4080,7 @@ * Fixed: Midi controller graphs were displaying 'quantization' vertical lines instead of 'snap' lines. (T356) - Changed CtrlCanvas::draw() to use raster instead of quant value. - Also changed gray background colour to darkGray, so 'major' snap lines can be seen. - * Added: Send 'null' midi controller parameters after each RPN/NRPN controler event. (T356) + * Added: Send 'null' midi controller parameters after each RPN/NRPN controller event. (T356) - Symptom: After muse sends any RPN/NRPN controller event, operating the 'data' (MSB/LSB) midi controllers affects that RPN/NRPN controller. - Cause: The RPN/NRPN parameters are still active, open to any subsequent data adjustments. @@ -3202,7 +4427,7 @@ - Added formatted program number display (like 1-1-120) for 'Val B' column. - Fixed Poly After Touch events display in list. They were being listed as Channel After Touch. - Fixed crash when 'Delete events' clicked with no actual items selected. - - 'Edit Contoller Event' dialog: + - 'Edit Controller Event' dialog: - Fixed 'Create New Controller' popup list: Now it actually creates the selected controller. - Fixed 'Program' controller not showing correct program after selecting other controllers in list. - Fixed too high initial value displayed for controllers without an initial value in the instrument file. @@ -3210,7 +4435,7 @@ - Fixed uneditable note box. Switched from PitchLabel to PitchEdit. * Feature added - Pianoroll and drum editor controller graphs now have a static manual adjustment knob ! (T356) - Now you don't have to enter controller graph values to adjust a setting, just turn the knob. - * Changed 'Create New Controller' in event editor 'Edit Contoller Event' dialog, and 'add new...' controller popup + * Changed 'Create New Controller' in event editor 'Edit Controller Event' dialog, and 'add new...' controller popup list in piano roll. (T356) - Now they only list controllers not already added, instead of all of them. * Fixed 'Position Edit' controls (0001.01.000 + up/down buttons) - click 'down' nothing happens. (T356) @@ -3359,7 +4584,7 @@ * Fixed arranger part operations (move etc.) to use selected part. (T356) * Fixed arranger keyboard left and right previous/next part selection. (T356) - Now you can use left/right arrow keys to cycle through parts in a track, - expecially useful for overlapping parts! + especially useful for overlapping parts! * Regarding part selection - A remaining problem is that there is no 'z-order' drawing scheme. The part item list has no such ordering support. - This means a selected part will (now) appear on top of others, but when unselected, @@ -3390,7 +4615,7 @@ time will tell if this is the right solution (rj) 02.13 * Feature request: 1650957 space bar should start/stop playback fixed by - adding this possiblity, old style is possible with property setting in + adding this possibility, old style is possible with property setting in configuration. (rj) 02.09 * Fixed bug 1645575 - Eraser tool causes segfault in drum editor (T356) @@ -3490,7 +4715,7 @@ * fixed faulty off value for aux when set to max (rj) * Allow wheeling of tracks from track list (rj) 08.11 - * reenabled mute for outputs (rj) + * re-enabled mute for outputs (rj) 07.11 * Fixed timing bug causing MusE to depend on jack buffer setting this needs testing, it involved uncommenting some code that might @@ -3925,7 +5150,7 @@ * Added an error popup when importing wave files fails. (rj) 30.3 * [DONE] midi -> edit instrument is not implemented -> remove it (js) - * [DONE] same for random rythm gen -> is not implemented -> remove it (js) + * [DONE] same for random rhythm gen -> is not implemented -> remove it (js) * [DONE] BUG: pianoroll editor -> tools resize wrong, they should stay on max they need instead of width fit (js) * have to go to the dentist on 7.3.2005, god help me, i fear this will kill me (js) 29.3 @@ -4009,7 +5234,7 @@ 14.12 * Disable of fluidsynth works (rj) * Added test for libsamplerate (rj) - * Reenabled --enable-suid-install (rj) + * Re-enabled --enable-suid-install (rj) * Added to simpledrums.h (rj) * Added -no-rtti to simpledrums (ml) @@ -4026,7 +5251,7 @@ * crash bug on missing event in sig.cpp fixed (rj) * Changed default timer resolution to 1024 (rj) * Applied fix from Levi D. Burton to allow midi thread to run - realtime allthough Jack is not (rj) + realtime although Jack is not (rj) * New version (0.22) of DeicsOnze from Alin Weiller (rj) 9.12 @@ -4063,7 +5288,7 @@ available, though not easily selectable yet. (rj) * Made some changes to how threads are created, for systems where thread creation has been erratic, linux2.6 in various - configurations. Not yet verified if it makes any differance. (rj) + configurations. Not yet verified if it makes any difference. (rj) 08.11 * Backported audio metronome (rj) * Backported open/save dialog improvements (rj) @@ -4121,7 +5346,7 @@ midi sync slave modes as they are currently not working (ws) * enabled sending midi clock (ws) 28.06. - * splitted removeTrack()/insertTrack() into three phases: pre realtime + * split removeTrack()/insertTrack() into three phases: pre realtime actions - realtime actions - post realtime actions; this allows to move memory allocations out of realtime task (ws) * changed undo/redo of tracks: synti instances are now really deleted on @@ -4275,7 +5500,7 @@ * after loading of template, treat current project as "untitled" (ws) * removed data structure "Clip". All information are now in WaveEvent; this simplifies the structure a lot and makes reference counting more - reliable. Unfortunatly this also means a new incompatible *.med file + reliable. Unfortunately this also means a new incompatible *.med file version. (ws) * changed reference counting of Event class; simplified and more reliable (ws) 21.04. @@ -4310,13 +5535,13 @@ * changed CTRL_VAL_UNKNONW as it conflicts with valid values for CTRL_PROGRAM (ws) 13.4. - * dont crash on missing LADSPA plugin (ws) + * don't crash on missing LADSPA plugin (ws) * set metronome precount default to "false". Precount is not implemented. (ws) * fixed crash when toggling stereo or pre buttons in mixer (ws) * synchronize channel number in mixer/arranger-tracklist (ws) * changed all float printf("%f") to equivalent qt-string - routines; dont localize decimal point so that the + routines; don't localize decimal point so that the strings can be properly parsed; this should fix some save/restore problems in localized MusE versions (ws) 12.4 @@ -4338,7 +5563,7 @@ - added another vertical line in drum editor 2.4 - integrated new icons (ws) - increased required QT-Version to 3.2 in configure.ac (ws) -1.4 - added vertikal line in track list as suggested by Joachim Schiele +1.4 - added vertical line in track list as suggested by Joachim Schiele - fixed synchronisation issue between mixer and tracklist (changing midi channel can add/remove mixer strip) (ws) - Changed pan range to -1 +1 (rj) @@ -4370,7 +5595,7 @@ 4.3 - Fluidsynti playback restored. Gain restored. (ml) 3.3 - Fluidsynti major rewrite, not fully functioning though (ml) - fixed crash on reload song with open mixer - - fixed crash on saving *.med: dont save aux values for channels + - fixed crash on saving *.med: don't save aux values for channels which have no aux send (like aux strips) - remove empty record wav files on MusE exit - fixed crash on undo controller editing (ws) @@ -4658,7 +5883,7 @@ - added swedish translations (Robert Jonsson) - fixed editing of pitch events in list editor - fixed crash in score editor - - check tempo entry values; dont allow invalid values which could + - check tempo entry values; don't allow invalid values which could crash MusE - fixed not functioning "Abort" button in MidiTransform dialog - fixed Ctrl-editing in drum editor @@ -4713,7 +5938,7 @@ - removed/changed obsolete qt code - update for current iiwu cvs - fixed initialisation bug in wave editor - - dont link iiwu libs static + - don't link iiwu libs static - (bh) added ladcca support - fixed midifile export - arranger, pianoroll editor, drum editor: tool popup menu @@ -4743,7 +5968,7 @@ 0.6.0pre3 - fixed drawing of drum parts in drum editor - - on reading *.med files reject events which dont't fit into part (more robust + - on reading *.med files reject events which don't fit into part (more robust handling of defective med files) - remove also synth gui when removing synth - implemented some of Frank Neumann's usability suggestions: @@ -4832,12 +6057,12 @@ - simplified organ soft synth parameter handling - removed illegal controller message optimizations - implementation of "panic" button - - first instantiated synti did'nt show up in port list + - first instantiated synti didn't show up in port list - size of resized drum and pianoroll editor windows are now remembered - fixed crash when configuring null audio device - removing soft synth instance did not stop midi thread; alsa client was not removed - - (bh) lots of buid system changes and general cleanups + - (bh) lots of build system changes and general cleanups - (bh) removed the use of the UICBASES make variable; .ui files can now be added straight into _SOURCES make variables with the new SUFFIXES support in automake 1.6 @@ -4991,7 +6216,7 @@ 0.4.14: - some makefile and compilation changes - audio play: noise between audioparts during playback - - dont stop at end of song when "loop" is active + - don't stop at end of song when "loop" is active - default magnification in wave-view set to 1 - fixed a audio route initialization bug - new metronome configuration: precount configuration added @@ -5076,7 +6301,7 @@ This allows for routing of different midi channels to different tracks while recording. - changed max number of midi ports from 8 to 16 - - fixed serveral bugs iiwu software synthesizer + - fixed several bugs iiwu software synthesizer - fixed compilation problems with some ALSA versions - fixed: changing track name changed record flag - fixed: remove midi editor if associated track is removed @@ -5267,7 +6492,7 @@ alsa version has no more problems with OSS raw midi emulation - new: colored activity display in arranger (Daniel Mack) - new: context sensitive extensions to "right mouse click - pulldown menues" for arranger parts (Daniel Mack) + pulldown menus" for arranger parts (Daniel Mack) - new: gui prototypes for extendend configuration of raw midi devices and audio mixdown file selection - fixed: quirks with OSS midi devices configuration @@ -5315,7 +6540,7 @@ - new: on popular request: "RewindToStart" button - fixed: changing devices while playing - fixed: arranger: could not scroll to the end of song - - fixed: song lenght on midi import + - fixed: song length on midi import - fixed: fatal error in handling "note off" events - new: "sustain" is reset on stop @@ -5350,7 +6575,7 @@ 0.2.12: - fixed: wave files/tracks/parts: calculation of tick<->time; it should be possible again to import/play simple waves - - fixed: funny things happend when muting all audio tracks + - fixed: funny things happened when muting all audio tracks - fixed: core if no active record track - new: Rob Naccarato started with documentation; press in MusE and have a look @@ -5379,7 +6604,7 @@ - fixed: wave form display in arranger - fixed: core on click in arranger "no track area " with pencil tool 0.2.8: - - fixed: oss midi devices now work agin + - fixed: oss midi devices now work again - reorganized midi event dispatcher - fixed: pitchbend for midialsa (Xavier) 0.2.7: @@ -5581,7 +6806,7 @@ - bug: path handling for project file: project files are now saved in the correct directory - bug: canvas initial scaling - - bug: core if configured device didnt exists + - bug: core if configured device didn't exists - bug: ctrl editor produced values > 127 - feature: Arranger: Parts are now displayed with a horizontal offset - feature: Arranger: added save/restore for some configuration values @@ -5678,7 +6903,7 @@ - error: arranger->tracklist: resize failed if columns are swapped - enhanced file selector for background image selection - more WhatsThis and ToolTip Help - - Backport to QT202: Filedialog: Filterlists dont work + - Backport to QT202: Filedialog: Filterlists don't work - Midi Config: changed signal click() to rightButtonClick() - missing initialisation in song constructor - new subdirectory for controller editor @@ -5690,7 +6915,7 @@ - misc toolbars changed to read qt toolbars 0.0.2: - changed color for cpos to red - - dont play metronome clicks with time < current + - don't play metronome clicks with time < current - doubleclick on arranger trackname: entrywidget now gets input focus - midi device configuration: reworked diff -Nru muse-2.1.2/CMakeLists.txt muse-3.0.2+ds1/CMakeLists.txt --- muse-2.1.2/CMakeLists.txt 2013-03-28 18:04:04.000000000 +0000 +++ muse-3.0.2+ds1/CMakeLists.txt 2018-01-29 21:10:59.000000000 +0000 @@ -20,23 +20,33 @@ # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= +project(muse) include(FindPkgConfig) include(CheckIncludeFile) +include(CheckIncludeFiles) +include (CheckCSourceCompiles) include(cmake/Summary.cmake) -project(muse) CMAKE_MINIMUM_REQUIRED(VERSION 2.4.1) if (COMMAND cmake_policy) - cmake_policy(SET CMP0003 NEW) + cmake_policy(SET CMP0003 NEW) + cmake_policy (SET CMP0007 NEW) endif(COMMAND cmake_policy) if (NOT DEFINED LIB_SUFFIX) SET(LIB_SUFFIX "" CACHE STRING "Suffix for installed library path. Ex. 64 for lib64") endif (NOT DEFINED LIB_SUFFIX) +SET(VST_SDK_QUIRK "") if (NOT DEFINED VST_HEADER_PATH) SET(VST_HEADER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/vestige" CACHE PATH "Path to vst header files (aeffectx.h). Default is ./vestige. See ENABLE_VST_VESTIGE.") +else (NOT DEFINED VST_HEADER_PATH) + if (NOT APPLE) + if (UNIX) + SET(VST_SDK_QUIRK "-DVST_SDK_QUIRK") # "Building with real vstsdk under Linux requires a quirk") + endif (UNIX) + endif (NOT APPLE) endif (NOT DEFINED VST_HEADER_PATH) message(" vst header path: " ${VST_HEADER_PATH} ) include_directories(${VST_HEADER_PATH}) @@ -51,7 +61,7 @@ endif (NOT LIB_INSTALL_DIR) if (LIB_SUFFIX) - message(" Install libraries to: " ${LIB_INSTALL_DIR} ) + message(" Install libraries to: " ${LIB_INSTALL_DIR} ) endif (LIB_SUFFIX) IF(NOT DEFINED SHARE_INSTALL_PREFIX) @@ -62,16 +72,19 @@ #set(CMAKE_BUILD_TYPE release) # If no CMAKE_BUILD_TYPE is given on the command line, -# cmake either uses the cached value, or 'empty' (plain un-opt build). +# cmake either uses the cached value, or 'empty' (plain un-opt build). # And yet the only way a user can reset a cached CMAKE_BUILD_TYPE # is with "-DCMAKE_BUILD_TYPE=". So we cannot interfere with this. # We should probably not attempt to do this at all. # Installation instructions now updated to be more specific, give options. #endif (NOT DEFINED CMAKE_BUILD_TYPE) -#if (NOT CMAKE_BUILD_TYPE) -# message("No CMAKE_BUILD_TYPE specified. Setting to Release.") -# set(CMAKE_BUILD_TYPE release) -#endif (NOT CMAKE_BUILD_TYPE) + +#UPDATE: most of users even don't know what CMAKE_BUILD_TYPE is. +#So it will be better to set unspecified build type to RelWithDebInfo +if (NOT CMAKE_BUILD_TYPE) + message("No CMAKE_BUILD_TYPE specified. Setting to RelWithDebInfo.") + set(CMAKE_BUILD_TYPE RelWithDebInfo) +endif (NOT CMAKE_BUILD_TYPE) # for debugging the make system uncomment next line: @@ -81,13 +94,13 @@ set(CMAKE_SKIP_RULE_DEPENDENCY TRUE) # The MusE version number. -SET(MusE_VERSION_MAJOR 2) -SET(MusE_VERSION_MINOR 1) -SET(MusE_VERSION_PATCH 0) -SET(MusE_VERSION "2.1.2") -SET(MusE_VERSION_FULL "2.1.2") -SET(MusE_INSTALL_NAME "muse-2.1") -SET(MusE_EXEC_NAME "muse2") +SET(MusE_VERSION_MAJOR 3) +SET(MusE_VERSION_MINOR 0) +SET(MusE_VERSION_PATCH 1) +SET(MusE_VERSION "3.0.2") +SET(MusE_VERSION_FULL "3.0.2") +SET(MusE_INSTALL_NAME "muse-3.0") +SET(MusE_EXEC_NAME "muse3") ## The directory where we will install the shared components: SET(MusE_MODULES_DIR ${LIB_INSTALL_DIR}/${MusE_INSTALL_NAME}/modules) @@ -109,24 +122,47 @@ ## Lib directory SET(MusE_LIB_DIR ${LIB_INSTALL_DIR}/${MusE_INSTALL_NAME}) -FIND_PROGRAM(SVNVER svnversion) -if (${SVNVER} STREQUAL "SVNVER-NOTFOUND") - SET (MusE_SVNVER 0) -else (${SVNVER} STREQUAL "SVNVER-NOTFOUND") - EXEC_PROGRAM( svnversion - ARGS ${PROJECT_SOURCE_DIR} - OUTPUT_VARIABLE MusE_SVNVER ) -endif (${SVNVER} STREQUAL "SVNVER-NOTFOUND") - -option ( ENABLE_LASH "Enable LASH Audio Session Handler (or LADISH compatibility layer)" ON) -option ( ENABLE_OSC "Enable Lightweight Open Sound Control (liblo) (DSSI also recommended)" ON) -option ( ENABLE_DSSI "Enable Disposable Soft Synth Interface (dssi) (OSC also recommended)" ON) -option ( ENABLE_VST "Enable VST/win support (deprecated)" OFF) -option ( ENABLE_VST_NATIVE "Enable Native VST support (see ENABLE_VST_VESTIGE and VST_HEADER_PATH)" ON) -option ( ENABLE_VST_VESTIGE "Set VST header type is Vestige" ON) -option ( ENABLE_FLUID "Enable fluidsynth softsynth plugins." ON) -option ( ENABLE_EXPERIMENTAL "Enable building experimental features." OFF) -option ( ENABLE_PYTHON "Enable Python control support." OFF) +include(FindGit) +if (GIT_FOUND) + EXEC_PROGRAM( ${GIT_EXECUTABLE} + ARGS "status" + OUTPUT_VARIABLE GIT_RET_NOTUSED + RETURN_VALUE IS_GIT_REPO ) + if(IS_GIT_REPO EQUAL 0) + EXEC_PROGRAM( ${GIT_EXECUTABLE} + ARGS "log -1 --format='%ci'" + OUTPUT_VARIABLE MusE_GITDATE ) + EXEC_PROGRAM( ${GIT_EXECUTABLE} + ARGS "log -1 --format='%t'" + OUTPUT_VARIABLE MusE_GITHASH ) + EXEC_PROGRAM( ${GIT_EXECUTABLE} + ARGS "rev-parse --abbrev-ref HEAD" + OUTPUT_VARIABLE MusE_GITBRANCH ) + SET(MusE_GITSTRING "git: ${MusE_GITBRANCH} - ${MusE_GITHASH} - ${MusE_GITDATE}") + else (IS_GIT_REPO EQUAL 0) + SET(MusE_GITSTRING "") + endif (IS_GIT_REPO EQUAL 0) +else (GIT_FOUND) + SET(MusE_GITSTRING "") +endif (GIT_FOUND) + + +option ( ENABLE_RTAUDIO "Multi platform rtaudio audio compatibility fallback" ON) +option ( ENABLE_ALSA "Enable Advanced Linux Sound Architecture (ALSA)" ON) +option ( ENABLE_LASH "Enable LASH Audio Session Handler (or LADISH compatibility layer)" ON) +option ( ENABLE_OSC "Enable Lightweight Open Sound Control (liblo) (DSSI also recommended)" ON) +option ( ENABLE_DSSI "Enable Disposable Soft Synth Interface (dssi) (OSC also needed)" ON) +option ( ENABLE_VST "Enable VST/win support (deprecated)" OFF) +option ( ENABLE_VST_NATIVE "Enable Native VST support (see ENABLE_VST_VESTIGE and VST_HEADER_PATH)" ON) +option ( ENABLE_VST_VESTIGE "Set VST header type is Vestige" ON) +option ( ENABLE_LV2 "Enable LV2 plugins and synths support" ON) +option ( ENABLE_LV2_SUPPLIED "Use supplied or system LV2 libraries (lv2, lilv, sord)" OFF) +option ( ENABLE_LV2_GTK2 "Enable LV2 user interface GTK2 support" ON) +option ( ENABLE_LV2_DEBUG "Enable LV2 debugging messages" OFF) +option ( ENABLE_FLUID "Enable fluidsynth softsynth plugins." ON) +option ( ENABLE_INSTPATCH "Enable Instrument Patch Library support (enhances soundfont support)." ON) +option ( ENABLE_EXPERIMENTAL "Enable various experimental features (not recommended)." OFF) +option ( ENABLE_PYTHON "Enable experimental python control support (not recommended)." OFF) option ( UPDATE_TRANSLATIONS "Update source translation share/locale/*.ts files (WARNING: This will modify the .ts files in the source tree!!)" OFF) option ( MODULES_BUILD_STATIC "Build type of internal modules" OFF) @@ -134,9 +170,13 @@ SET(MODULES_BUILD STATIC ) else ( MODULES_BUILD_STATIC ) SET(MODULES_BUILD SHARED ) - SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON) - SET(CMAKE_INSTALL_RPATH ${MusE_MODULES_DIR}) +# SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON) +# SET(CMAKE_INSTALL_RPATH ${MusE_MODULES_DIR}) endif ( MODULES_BUILD_STATIC ) +# We need these always, for a few of the shared modules in the muse/core build, +# even when building muse/core as static. Otherwise it can't find them on run. Tim. +SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON) +SET(CMAKE_INSTALL_RPATH ${MusE_MODULES_DIR}) ## ## Just print a notice if this is OS X @@ -151,27 +191,49 @@ endif (APPLE) ## -## look for Qt4 +## look for Qt5 ## -set(QT_MIN_VERSION "4.3.0") + +set(QT_MIN_VERSION "5.1.0") set(QT_USE_QTXML TRUE) set(QT_USE_QTDESIGNER TRUE) set(QT_USE_QTSVG TRUE) -find_package(Qt4) +find_package(Qt5Widgets) -if (NOT QT4_FOUND) - message(FATAL_ERROR "Fatal error: QT (version >= 4.3.0) required.\n" - "Cmake tries to detect QT4 by searching for 'qmake' in your PATH\n" - "If you have QT4 installed, make sure qmake is found in your PATH." +if (NOT Qt5Widgets_FOUND) + message(FATAL_ERROR "Fatal error: QT (version >= 5.1.0) required.\n" + "Cmake tries to detect QT5 by searching for 'Qt5Widgets' package\n" + "If you have QT5 installed, make sure 'Qt5Widgets' package is in PKG_CONFIG_PATH." ) -endif (NOT QT4_FOUND) +endif (NOT Qt5Widgets_FOUND) + +find_package(Qt5Core REQUIRED) +find_package(Qt5UiTools REQUIRED) +find_package(Qt5LinguistTools REQUIRED) +find_package(Qt5Xml REQUIRED) +find_package(Qt5Svg REQUIRED) # Needed for plugins factory: SET(QT_USE_QTUITOOLS TRUE) -include(${QT_USE_FILE}) +include_directories (${Qt5Widgets_INCLUDE_DIRS}) +include_directories (${Qt5Core_INCLUDE_DIRS}) +include_directories (${Qt5UiTools_INCLUDE_DIRS}) +include_directories (${Qt5Xml_INCLUDE_DIRS}) +include_directories (${Qt5Svg_INCLUDE_DIRS}) + +LINK_DIRECTORIES(${Qt5Widgets_LIBRARY_DIRS}) +LINK_DIRECTORIES(${Qt5Core_LIBRARY_DIRS}) +LINK_DIRECTORIES(${Qt5UiTools_LIBRARY_DIRS}) +LINK_DIRECTORIES(${Qt5Xml_LIBRARY_DIRS}) +LINK_DIRECTORIES(${Qt5Svg_LIBRARY_DIRS}) + +SET(QT_LIBRARIES ${Qt5Widgets_LIBRARIES} ${Qt5UiTools_LIBRARIES} ${Qt5Xml_LIBRARIES} ${Qt5Svg_LIBRARIES} ${Qt5Core_LIBRARIES}) + + +##include(${QT_USE_FILE}) ## ## Begin MANDATORY packages... @@ -183,19 +245,20 @@ CHECK_INCLUDE_FILE(ladspa.h HAVE_LADSPA_H) if(NOT HAVE_LADSPA_H) - message(FATAL_ERROR "** ERROR: header file ladspa.h is required, but was not found.") + message(FATAL_ERROR "** ERROR: header file ladspa.h is required, but was not found." + ${HAVE_LADSPA_H}) endif(NOT HAVE_LADSPA_H) -## -## alsa >= 0.9.0 -## - -if (APPLE) - message("Disabling ALSA support due to OS X build.") -else (APPLE) -PKG_CHECK_MODULES(ALSA REQUIRED alsa>=0.9.0) -include_directories(${ALSA_INCLUDE_DIRS}) -endif (APPLE) +# ## +# ## alsa >= 0.9.0 +# ## +# +# if (APPLE) +# message("Disabling ALSA support due to OS X build.") +# else (APPLE) +# PKG_CHECK_MODULES(ALSA REQUIRED alsa>=0.9.0) +# include_directories(${ALSA_INCLUDE_DIRS}) +# endif (APPLE) ## ## find sndfile >= 1.0.0 @@ -212,13 +275,6 @@ include_directories(${SAMPLERATE_INCLUDE_DIRS}) ## -## find libuuid -## - -PKG_CHECK_MODULES(UUID REQUIRED uuid>=0.0.1) -include_directories(${UUID_INCLUDE_DIRS}) - -## ## find jack >= 0.103.0 ## @@ -234,7 +290,34 @@ ## ## -## find LASH +## alsa >= 0.9.0 +## + +## If requesting DSSI as well, try to grab ALSA too. +if (ENABLE_DSSI OR ENABLE_ALSA) + PKG_CHECK_MODULES(ALSA alsa>=0.9.0) + if (ALSA_FOUND) + include_directories(${ALSA_INCLUDE_DIRS}) + set(ALSA_SUPPORT ON) +## Try to fall back on the cut-down compatibility library +## TODO: NO-GO: Tested: Incomplete, missing several functions and defs needed for our DSSI implementation ! +# else (ALSA_FOUND) +# if (ENABLE_DSSI) +# PKG_CHECK_MODULES(DSSI_ALSA_COMPAT libdssialsacompat) +# if (DSSI_ALSA_COMPAT_FOUND) +# include_directories(${DSSI_ALSA_COMPAT_INCLUDE_DIRS}) +# set(DSSI_ALSA_COMPAT_SUPPORT ON) +# else (DSSI_ALSA_COMPAT_FOUND) +# message("ALSA disabled") +# endif (DSSI_ALSA_COMPAT_FOUND) +# endif (ENABLE_DSSI) + endif (ALSA_FOUND) +else (ENABLE_DSSI OR ENABLE_ALSA) + message("ALSA disabled") +endif (ENABLE_DSSI OR ENABLE_ALSA) + +## +## find LASH ## if (ENABLE_LASH) @@ -242,24 +325,20 @@ if (LASH_FOUND) include_directories(${LASH_INCLUDE_DIRS}) set(HAVE_LASH ON) - endif (LASH_FOUND) + endif (LASH_FOUND) else (ENABLE_LASH) message("LASH disabled") endif (ENABLE_LASH) -## -## check for liblo >= 0.23 -## - -if (ENABLE_OSC) - PKG_CHECK_MODULES(LIBLO liblo>=0.23) - if (LIBLO_FOUND) - include_directories(${LIBLO_INCLUDE_DIRS}) - set(OSC_SUPPORT ON) - endif (LIBLO_FOUND) -else (ENABLE_OSC) - message("OSC disabled") -endif (ENABLE_OSC) +if (ENABLE_RTAUDIO) + PKG_CHECK_MODULES(RTAUDIO rtaudio>=4.0) + if (RTAUDIO_FOUND) + include_directories(${RTAUDIO_INCLUDE_DIRS}) + set(HAVE_RTAUDIO ON) + endif (RTAUDIO_FOUND) +else (ENABLE_RTAUDIO) + message("RTAUDIO disabled") +endif (ENABLE_RTAUDIO) ## ## check for python >= 2.4 @@ -284,21 +363,32 @@ endif (ENABLE_PYTHON) ## -## check for dssi >= 0.9.0 +## check for dssi >= 0.9.0 and liblo >= 0.23 ## -if (ENABLE_DSSI) +## If requesting DSSI as well, try to grab OSC too. +if (ENABLE_DSSI OR ENABLE_OSC) + PKG_CHECK_MODULES(LIBLO liblo>=0.23) + if (LIBLO_FOUND) + include_directories(${LIBLO_INCLUDE_DIRS}) + set(OSC_SUPPORT ON) + endif (LIBLO_FOUND) +else (ENABLE_DSSI OR ENABLE_OSC) + message("OSC disabled") +endif (ENABLE_DSSI OR ENABLE_OSC) + +if (ENABLE_DSSI AND (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) PKG_CHECK_MODULES(DSSI dssi>=0.9.0) if (DSSI_FOUND) - include_directories(${DSSI_INCLUDE_DIRS}) - set(DSSI_SUPPORT ON) + include_directories(${DSSI_INCLUDE_DIRS}) + set(DSSI_SUPPORT ON) endif (DSSI_FOUND) -else (ENABLE_DSSI) +else (ENABLE_DSSI AND (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) message("DSSI disabled") -endif (ENABLE_DSSI) +endif (ENABLE_DSSI AND (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) ## -## Depricated FST-based VST support +## Deprecated FST-based VST support ## if (ENABLE_VST) @@ -325,7 +415,7 @@ message("Native VST support enabled") set (VST_NATIVE_SUPPORT TRUE) endif (VST_HEADER_CHECK STREQUAL "VST_HEADER_CHECK-NOTFOUND") - + else (ENABLE_VST_NATIVE) message("Native VST support disabled") endif (ENABLE_VST_NATIVE) @@ -335,11 +425,86 @@ if (ENABLE_VST_VESTIGE) SET (VST_VESTIGE_SUPPORT TRUE) -else (ENABLE_VST_VESTIGE) +else (ENABLE_VST_VESTIGE) SET (VST_VESTIGE_SUPPORT FALSE) endif (ENABLE_VST_VESTIGE) ## +## LV2 support +## + +if (ENABLE_LV2) + set(LV2_SUPPORT ON) + + ## + ## find Gtk2 + ## + + if(ENABLE_LV2_GTK2) + find_package(GTK2 COMPONENTS gtk gtkmm) + if(GTK2_FOUND) + set(HAVE_GTK2 ON) + endif(GTK2_FOUND) + endif(ENABLE_LV2_GTK2) + + set (muse_lilv_ver "0.22.0" ) + set (muse_lv2_ver "1.12.0") + set (muse_sratom_ver "0.4.6") + set (muse_sord_ver "0.14.0") + set (muse_serd_ver "0.22.0") + + PKG_CHECK_MODULES(LILV lilv-0>=${muse_lilv_ver}) + if (LILV_FOUND) + include_directories(${LILV_INCLUDE_DIRS}) + endif (LILV_FOUND) + + PKG_CHECK_MODULES(SORD sord-0>=${muse_sord_ver}) + if (SORD_FOUND) + include_directories(${SORD_INCLUDE_DIRS}) + endif (SORD_FOUND) + + PKG_CHECK_MODULES(LV2 lv2>=${muse_lv2_ver}) + if (LV2_FOUND) + include_directories(${LV2_INCLUDE_DIRS}) + endif (LV2_FOUND) + + if (ENABLE_LV2_SUPPLIED) + set (LV2_USE_SUPPLIED_VERSION TRUE) + else (ENABLE_LV2_SUPPLIED) + set (LV2_USE_SUPPLIED_VERSION FALSE) + endif (ENABLE_LV2_SUPPLIED) + + if (NOT (LILV_FOUND AND SORD_FOUND AND LV2_FOUND)) + set (LV2_USE_SUPPLIED_VERSION TRUE) + endif (NOT (LILV_FOUND AND SORD_FOUND AND LV2_FOUND)) + + if (LV2_USE_SUPPLIED_VERSION) + PKG_CHECK_MODULES(PCRE libpcre) + set (muse_lv2_dir "${PROJECT_SOURCE_DIR}/muse/lv2Support/lv2-${muse_lv2_ver}") + set (muse_sratom_dir "${PROJECT_SOURCE_DIR}/muse/lv2Support/sratom-${muse_sratom_ver}") + set (muse_sord_dir "${PROJECT_SOURCE_DIR}/muse/lv2Support/sord-${muse_sord_ver}") + set (muse_serd_dir "${PROJECT_SOURCE_DIR}/muse/lv2Support/serd-${muse_serd_ver}") + set (muse_lilv_dir "${PROJECT_SOURCE_DIR}/muse/lv2Support/lilv-${muse_lilv_ver}" ) + include_directories( + ${muse_lv2_dir} + ${muse_sord_dir} + ${muse_sratom_dir} + ${muse_serd_dir} + ${muse_lilv_dir} + ) + endif (LV2_USE_SUPPLIED_VERSION) + +else (ENABLE_LV2) + message("LV2 disabled") +endif (ENABLE_LV2) + + +if (ENABLE_LV2_DEBUG) + set (DEBUG_LV2 ON) + add_definitions(-DDEBUG_LV2) +endif (ENABLE_LV2_DEBUG) + +## ## TODO ## ## Optimizations @@ -356,13 +521,22 @@ # synth library named 'fluid_synth' to be built later. PKG_CHECK_MODULES(FLUIDSYN fluidsynth>=0.9.0) if (FLUIDSYN_FOUND) - include_directories(${FLUIDSYN_INCLUDE_DIRS}) set(HAVE_FLUIDSYNTH ON) endif (FLUIDSYN_FOUND) else ( ENABLE_FLUID ) message("Fluidsynth disabled") endif ( ENABLE_FLUID ) +if ( ENABLE_INSTPATCH ) + PKG_CHECK_MODULES(INSTPATCH libinstpatch-1.0) + if (INSTPATCH_FOUND) + include_directories(${INSTPATCH_INCLUDE_DIRS}) + set(HAVE_INSTPATCH ON) + endif (INSTPATCH_FOUND) +else ( ENABLE_INSTPATCH ) + message("Inst(rument) Patch support disabled") +endif ( ENABLE_INSTPATCH ) + ## ## End OPTIONAL packages. ## @@ -380,26 +554,13 @@ ${PROJECT_BINARY_DIR}/config.h ) -add_custom_command( - OUTPUT ${PROJECT_BINARY_DIR}/all.h - COMMAND cp ${PROJECT_SOURCE_DIR}/all.h ${PROJECT_BINARY_DIR}/all.h - DEPENDS ${PROJECT_SOURCE_DIR}/all.h - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - ) +file (COPY ${PROJECT_SOURCE_DIR}/all.h DESTINATION ${PROJECT_BINARY_DIR}) +file (RENAME ${PROJECT_BINARY_DIR}/all.h ${PROJECT_BINARY_DIR}/all-pic.h) -add_custom_command( - OUTPUT ${PROJECT_BINARY_DIR}/all-pic.h - COMMAND cp ${PROJECT_SOURCE_DIR}/all.h ${PROJECT_BINARY_DIR}/all-pic.h - DEPENDS ${PROJECT_SOURCE_DIR}/all.h - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - ) +file (COPY ${PROJECT_SOURCE_DIR}/all.h DESTINATION ${PROJECT_BINARY_DIR}) +file (RENAME ${PROJECT_BINARY_DIR}/all.h ${PROJECT_BINARY_DIR}/all-pic-debug.h) -add_custom_command( - OUTPUT ${PROJECT_BINARY_DIR}/all-pic-debug.h - COMMAND cp ${PROJECT_SOURCE_DIR}/all.h ${PROJECT_BINARY_DIR}/all-pic-debug.h - DEPENDS ${PROJECT_SOURCE_DIR}/all.h - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - ) +file (COPY ${PROJECT_SOURCE_DIR}/all.h DESTINATION ${PROJECT_BINARY_DIR}) include_directories( . @@ -446,13 +607,14 @@ INCLUDE(CPack) ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") -set(CMAKE_CXX_FLAGS "-Wall -Wextra -Winvalid-pch -fno-exceptions -fPIC ${CMAKE_CXX_FLAGS}") -set(CMAKE_CXX_FLAGS_RELEASE "-O2 -fomit-frame-pointer -ffast-math -fstrength-reduce -fPIC ${CMAKE_CXX_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_DEBUG "-g -DQT_DEBUG -fPIC ${CMAKE_CXX_FLAGS_DEBUG}") +set(CMAKE_CXX_FLAGS "-std=c++11 -Werror=format-security -Wextra -Winvalid-pch -fexceptions -Wall -fPIC ${CMAKE_CXX_FLAGS}" CACHE STRING "Default CXX flags" FORCE) +set(CMAKE_CXX_FLAGS_RELEASE "-std=c++11 -O2 -fomit-frame-pointer -ffast-math -fno-finite-math-only -Wall -Wextra -fPIC ${CMAKE_CXX_FLAGS_RELEASE}" CACHE STRING "Release CXX flags" FORCE) +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-std=c++11 -O2 -fomit-frame-pointer -ffast-math -fno-finite-math-only -Wall -Wextra -fPIC ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" CACHE STRING "Release w/deb info CXX flags" FORCE) +set(CMAKE_CXX_FLAGS_DEBUG "-std=c++11 -g -O0 -DQT_DEBUG -Werror -Wall -Wextra -fPIC ${CMAKE_CXX_FLAGS_DEBUG}" CACHE STRING "Debug CXX flags" FORCE) # NOTE: share/ directory needs to be at the end so that the translations # are scanned before coming to share/locale -subdirs(doc al awl grepmidi man plugins muse synti packaging utils demos share) +subdirs(doc libs al awl grepmidi man plugins muse synti packaging utils demos share) ## Install doc files file (GLOB doc_files @@ -483,48 +645,89 @@ "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake") ## -## Report errors and warnings and hints +## Report errors and warnings and hints ## message("\n") -if (NOT ALSA_FOUND) - message("** ERROR: alsa >= 0.9.0 is required, but development files were not found.") -endif (NOT ALSA_FOUND) +## Mandatory: +## ============= if (NOT SNDFILE_FOUND) message("** ERROR: sndfile >= 1.0.0 is required, but development files were not found.") -endif (NOT SNDFILE_FOUND) +endif (NOT SNDFILE_FOUND) if (NOT SAMPLERATE_FOUND) message("** ERROR: samplerate >= 0.1.0 is required, but development files were not found.") -endif (NOT SAMPLERATE_FOUND) - -if (NOT UUID_FOUND) - message("** ERROR: uuid >= 0.0.1 is required, but development files were not found.") -endif (NOT UUID_FOUND) +endif (NOT SAMPLERATE_FOUND) if (NOT JACK_FOUND) message("** ERROR: jack >= 0.103 is required, but development files were not found.") endif (NOT JACK_FOUND) + +## Optional: +## ============= + +if (ENABLE_RTAUDIO AND (NOT RTAUDIO_FOUND)) + message("** WARNING: rtaudio (>= 4.0) was enabled, but development files were not found. ") +endif (ENABLE_RTAUDIO AND (NOT RTAUDIO_FOUND)) + if (ENABLE_LASH AND (NOT LASH_FOUND)) message("** WARNING: lash (>= 0.2) was enabled, but development files were not found. ") message("** HINT: Don't have LASH? Try installing the LADISH LASH compatibility package instead.") endif (ENABLE_LASH AND (NOT LASH_FOUND)) -if (ENABLE_OSC AND (NOT LIBLO_FOUND)) - message("** WARNING: liblo (>= 0.23) (Lightweight Open Sound Control) was enabled, but development files were not found.") -endif (ENABLE_OSC AND (NOT LIBLO_FOUND)) - if (ENABLE_PYTHON AND (NOT PYTHONLIBS_FOUND)) message("** WARNING: python was enabled, but development files were not found.") endif (ENABLE_PYTHON AND (NOT PYTHONLIBS_FOUND)) +if (ENABLE_ALSA AND (NOT ALSA_FOUND)) + message("** WARNING: ALSA >= 0.9.0 was enabled, but development files were not found.") +endif (ENABLE_ALSA AND (NOT ALSA_FOUND)) + +if (DSSI_ALSA_COMPAT_FOUND) + message("** WARNING: Using smaller DSSI ALSA Compatibility package instead of full ALSA. ") +endif (DSSI_ALSA_COMPAT_FOUND) + if (ENABLE_DSSI AND (NOT DSSI_FOUND)) - message("** WARNING: dssi (>= 0.9.0) was enabled, but development files were not found.") + if (NOT (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) +# message("** WARNING: DSSI was enabled, but ALSA was not enabled or DSSI") +# message(" ALSA Compatibility development files were not found.") + message("** WARNING: DSSI (>= 0.9.0) was enabled, but ALSA development files were not found.") + else (NOT (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) + message("** WARNING: DSSI (>= 0.9.0) was enabled, but development files were not found.") + endif (NOT (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) +else (ENABLE_DSSI AND (NOT DSSI_FOUND)) + if (NOT (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) +# TODO: +# message("** WARNING: DSSI was enabled, but ALSA was not enabled or DSSI") +# message(" ALSA Compatibility development files were not found.") +# message(" HINT: DSSI needs either full ALSA or the smaller DSSI ALSA Compatibility packages.") + message("** WARNING: DSSI was enabled, but ALSA was not enabled.") + message(" HINT: MusE DSSI support needs full ALSA development files package.") + endif (NOT (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) + + if (ENABLE_DSSI AND (NOT LIBLO_FOUND)) + message("** WARNING: DSSI is enabled, but liblo (Lightweight Open Sound Control) development files were not found.") + message(" DSSI support will be built but will NOT have OSC support.") + endif (ENABLE_DSSI AND (NOT LIBLO_FOUND)) endif (ENABLE_DSSI AND (NOT DSSI_FOUND)) +if (ENABLE_OSC AND (NOT LIBLO_FOUND)) + message("** WARNING: liblo (>= 0.23) (Lightweight Open Sound Control) was enabled, but development files were not found.") +else (ENABLE_OSC AND (NOT LIBLO_FOUND)) + if (ENABLE_OSC AND (NOT ENABLE_DSSI)) + message("** WARNING: OSC is enabled, but DSSI was not enabled.") + message(" Currently OSC is only used for DSSI support - you likely want to enable DSSI as well.") + else (ENABLE_OSC AND (NOT ENABLE_DSSI)) + if (NOT DSSI_FOUND) + message("** WARNING: OSC is enabled and DSSI is enabled, but DSSI development files were not found.") + message(" OSC support will be built, but not currently used by anything until future features do.") + endif (NOT DSSI_FOUND) + endif (ENABLE_OSC AND (NOT ENABLE_DSSI)) +endif (ENABLE_OSC AND (NOT LIBLO_FOUND)) + if (ENABLE_VST_NATIVE AND (NOT AEFFECT_H_FOUND)) message("** WARNING: Native VST was enabled, but development files were not found.") message("** HINT: Check the VST_HEADER_PATH variable. Points to ./vestige by default.") @@ -534,16 +737,42 @@ message("** WARNING: fluidsynth (>= 0.9.0) was enabled, but development files were not found.") endif (ENABLE_FLUID AND (NOT FLUIDSYN_FOUND)) +if (ENABLE_INSTPATCH AND (NOT INSTPATCH_FOUND)) + message("** WARNING: libinstpatch (>= 1.0) was enabled, but development files were not found.") + message(" Automatic drum lists for fluidsynth MESS plugin will not be available.") +endif (ENABLE_INSTPATCH AND (NOT INSTPATCH_FOUND)) + +if (ENABLE_LV2) + if (ENABLE_LV2_GTK2 AND (NOT GTK2_FOUND)) + message("** WARNING: LV2 GTK2 support is enabled, but Gtk2 (gtkmm-2, gtk+-2) development files were not found. LV2 Gtk2 UI support is disabled.") + endif (ENABLE_LV2_GTK2 AND (NOT GTK2_FOUND)) + + if (LV2_USE_SUPPLIED_VERSION AND (NOT ENABLE_LV2_SUPPLIED)) + message("** WARNING: System LV2 support libraries were chosen, but development files were not found or too old:") + message(" Requires lv2 >= " ${muse_lv2_ver} ", lilv-0 >= " ${muse_lilv_ver} ", sord-0 >= " ${muse_sord_ver}) + message(" Building supplied internal versions instead.") + endif (LV2_USE_SUPPLIED_VERSION AND (NOT ENABLE_LV2_SUPPLIED)) +endif (ENABLE_LV2) + message("") ## Show a summary of what we got +summary_add("ALSA support" ALSA_SUPPORT) +# TODO: +# summary_add("DSSI ALSA Compatibility support" DSSI_ALSA_COMPAT_SUPPORT) +summary_add("RTAudio support" HAVE_RTAUDIO) summary_add("Lash support" HAVE_LASH) summary_add("OSC (Liblo) support" OSC_SUPPORT) summary_add("Python support" PYTHON_SUPPORT) summary_add("DSSI support" DSSI_SUPPORT) +summary_add("LV2 support" LV2_SUPPORT) +if(LV2_SUPPORT) + summary_add("LV2 Gtk2 UI support" HAVE_GTK2) +endif(LV2_SUPPORT) #summary_add("VST support" VST_SUPPORT) summary_add("Native VST support" VST_NATIVE_SUPPORT) summary_add("Fluidsynth support" HAVE_FLUIDSYNTH) +summary_add("Instpatch support" HAVE_INSTPATCH) summary_add("Experimental features" ENABLE_EXPERIMENTAL) summary_show() @@ -557,7 +786,7 @@ if (NOT CMAKE_BUILD_TYPE) message(" Build type: CMAKE_BUILD_TYPE is empty. Plain un-optimized build.") else (NOT CMAKE_BUILD_TYPE) - message(" Build type: " ${CMAKE_BUILD_TYPE} ) + message(" Build type: " ${CMAKE_BUILD_TYPE} ) endif (NOT CMAKE_BUILD_TYPE) message("") diff -Nru muse-2.1.2/CMakeLists.txt.user muse-3.0.2+ds1/CMakeLists.txt.user --- muse-2.1.2/CMakeLists.txt.user 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/CMakeLists.txt.user 2018-01-29 20:09:16.000000000 +0000 @@ -0,0 +1,430 @@ + + + + + + EnvironmentId + {dcbb62ab-f6c0-4bd8-a0ef-c4fa15620020} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + true + false + 0 + true + true + 0 + 8 + true + 1 + true + true + true + false + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {4cb66e43-1f2b-40b1-b627-b6cc4257ccbd} + 0 + 0 + 1 + + + CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++ + + /home/ddskrjo/dev/muse-git_build_release + + + + + all + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + + + all + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Default + Default + CMakeProjectManager.CMakeBuildConfiguration + + + + CMAKE_BUILD_TYPE:STRING=Debug + + /home/ddskrjo/dev/muse-git/build-muse3-Desktop-Debug + + + + + + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Debug + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + + CMAKE_BUILD_TYPE:STRING=Release + + /home/ddskrjo/dev/muse-git/build-muse3-Desktop-Release + + + + + + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release + Release + CMakeProjectManager.CMakeBuildConfiguration + + + + CMAKE_BUILD_TYPE:STRING=RelWithDebInfo + + /home/ddskrjo/dev/muse-git/build-muse3-Desktop-Release with Debug Information + + + + + + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Release with Debug Information + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + + + CMAKE_BUILD_TYPE:STRING=MinSizeRel + + /home/ddskrjo/dev/muse-git/build-muse3-Desktop-Minimum Size Release + + + + + + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Build + + ProjectExplorer.BuildSteps.Build + + + + + + clean + + true + Make + + CMakeProjectManager.MakeStep + + 1 + Clean + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + Minimum Size Release + Minimum Size Release + CMakeProjectManager.CMakeBuildConfiguration + + 5 + + + 0 + Deploy + + ProjectExplorer.BuildSteps.Deploy + + 1 + Deploy locally + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + grepmidi + + + /home/ddskrjo/dev/muse-git_build_release/grepmidi + -1 + + grepmidi + + CMakeProjectManager.CMakeRunConfiguration.grepmidi + 3768 + false + true + false + false + true + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + muse + + + /home/ddskrjo/dev/muse-git_build_release/muse + 2 + + muse + + CMakeProjectManager.CMakeRunConfiguration.muse + 3768 + false + true + false + false + true + + 2 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 18 + + + Version + 18 + + diff -Nru muse-2.1.2/compile_muse.sh muse-3.0.2+ds1/compile_muse.sh --- muse-2.1.2/compile_muse.sh 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/compile_muse.sh 2017-12-04 21:01:18.000000000 +0000 @@ -29,5 +29,7 @@ mkdir build fi cd build -cmake -DCMAKE_BUILD_TYPE=release .. && make && echo "Build was OK, now enter the 'build' dir and run 'make install' as root" + +# to put the resulting binary in a specific location add -DCMAKE_INSTALL_PREFIX= +cmake -DCMAKE_BUILD_TYPE=release .. && make clean all && echo "Build was OK, now enter the 'build' dir and run 'make install' as root" diff -Nru muse-2.1.2/config.h.in muse-3.0.2+ds1/config.h.in --- muse-2.1.2/config.h.in 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/config.h.in 2018-01-06 20:31:35.000000000 +0000 @@ -20,16 +20,22 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //============================================================================= +#cmakedefine ALSA_SUPPORT +#cmakedefine HAVE_RTAUDIO +#cmakedefine DSSI_ALSA_COMPAT_SUPPORT #cmakedefine HAVE_LASH #cmakedefine OSC_SUPPORT #cmakedefine DSSI_SUPPORT +#cmakedefine LV2_SUPPORT +#cmakedefine HAVE_INSTPATCH +#cmakedefine HAVE_GTK2 #cmakedefine VST_SUPPORT #cmakedefine VST_NATIVE_SUPPORT #cmakedefine VST_VESTIGE_SUPPORT #cmakedefine USE_SSE #define VERSION "${MusE_VERSION_FULL}" -#define SVNVERSION "${MusE_SVNVER}" +#define GITSTRING "${MusE_GITSTRING}" #define PACKAGE_NAME "MusE" #define DOCDIR "${MusE_DOC_DIR}" #define SHAREDIR "${MusE_SHARE_DIR}" diff -Nru muse-2.1.2/debian/changelog muse-3.0.2+ds1/debian/changelog --- muse-2.1.2/debian/changelog 2016-09-02 12:08:59.000000000 +0000 +++ muse-3.0.2+ds1/debian/changelog 2018-12-16 13:37:01.000000000 +0000 @@ -1,3 +1,39 @@ +muse (3.0.2+ds1-1) unstable; urgency=medium + + * Team upload. + + [ Ondřej Nový ] + * d/copyright: Use https protocol in Format field + * d/control: Set Vcs-* to salsa.debian.org + * d/changelog: Remove trailing whitespaces + * d/watch: Use https protocol + + [ Felipe Sateler ] + * Change maintainer address to debian-multimedia@lists.debian.org + + [ Sebastian Ramacher ] + * New upstream release. (Closes: #887794) + - Fix build with GCC 7. (Closes: #853565) + - Switch to Qt5. (Closes: #875049) + * debian/watch: Update watch file for 3.0.x and handle repacking. + * debian/copyright: + - Remove embedded lilv, lv2, serd, sord and sratom. + - Drop ladspa.h paragraph. + * debian/patches: + - Refresh patches. + - Apply upstream patches to fix build with Qt 5.11. + - Drop 1002-abs-errors.patch. The issue was fixed upstream. (Closes: + #836432) + * debian/: Bump debhelper compat to 11. + * debian/control: + - Also depend on python3 for some scripts. + - Update build dependencies based on new requirements. + - Bump Standards-Version. + * debian/rules: Do not override CMake build type. The default does the right + thing. + + -- Sebastian Ramacher Sun, 16 Dec 2018 14:37:01 +0100 + muse (2.1.2-3) unstable; urgency=medium * Team upload. @@ -436,7 +472,7 @@ + [10_checkbox_fix] Most parts merged upstream. * debian/watch: Added. - + -- Free Ekanayaka Wed, 13 Oct 2004 17:35:22 +0200 muse (0.6.3-2) unstable; urgency=medium @@ -613,11 +649,11 @@ muse (0.6.0release-1) unstable; urgency=low * New upstream release - * Added support for fluidsynth which is the + * Added support for fluidsynth which is the successor of iiwusynth (closes: #187074) * Defined QTDIR which hopefully enables the build on some archs and possibly using pbuilder. - + -- Olaf Stetzer Tue, 20 May 2003 22:47:03 +0200 muse (0.6.0pre8-1) unstable; urgency=low diff -Nru muse-2.1.2/debian/compat muse-3.0.2+ds1/debian/compat --- muse-2.1.2/debian/compat 2016-09-02 09:55:35.000000000 +0000 +++ muse-3.0.2+ds1/debian/compat 2018-12-16 13:18:40.000000000 +0000 @@ -1 +1 @@ -9 +11 diff -Nru muse-2.1.2/debian/control muse-3.0.2+ds1/debian/control --- muse-2.1.2/debian/control 2016-09-02 09:55:35.000000000 +0000 +++ muse-3.0.2+ds1/debian/control 2018-12-16 13:37:01.000000000 +0000 @@ -1,38 +1,42 @@ Source: muse Section: sound Priority: optional -Maintainer: Debian Multimedia Maintainers +Maintainer: Debian Multimedia Maintainers Uploaders: Alessio Treglia , Fabrice Coutadeur Build-Depends: cmake, - debhelper (>= 9~), + debhelper (>= 11), dssi-dev, libasound2-dev, libdssialsacompat-dev [!linux-any], libfluidsynth-dev, libjack-dev, + liblilv-dev (>= 0.22), liblo-dev, - libqt4-dev, + libqt5svg5-dev, libsamplerate0-dev, libsndfile1-dev, - libx11-dev, - uuid-dev -Standards-Version: 3.9.8 + libsord-dev (>= 0.14), + lv2-dev (>= 1.12), + qtbase5-dev, + qttools5-dev +Standards-Version: 4.2.1 Homepage: http://www.muse-sequencer.org/ -Vcs-Git: https://anonscm.debian.org/git/pkg-multimedia/muse.git -Vcs-Browser: https://anonscm.debian.org/cgit/pkg-multimedia/muse.git +Vcs-Git: https://salsa.debian.org/multimedia-team/muse.git +Vcs-Browser: https://salsa.debian.org/multimedia-team/muse Package: muse Architecture: any Depends: python, + python3, ${misc:Depends}, ${shlibs:Depends} Suggests: jackd -Description: Qt4-based audio/MIDI sequencer +Description: Qt-based audio/MIDI sequencer MusE is a MIDI/audio sequencer with recording and editing capabilities. Some Highlights: . diff -Nru muse-2.1.2/debian/copyright muse-3.0.2+ds1/debian/copyright --- muse-2.1.2/debian/copyright 2016-09-02 12:08:59.000000000 +0000 +++ muse-3.0.2+ds1/debian/copyright 2018-12-16 13:27:12.000000000 +0000 @@ -1,8 +1,13 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: MusE Upstream-Contact: Werner Schweer Source: http://sourceforge.net/projects/lmuse/files/muse-2.0/ Copyright: 1999-2010 Werner Schweer and others +Files-Excluded: muse/lv2Support/lilv-* + muse/lv2Support/lv2-* + muse/lv2Support/serd-* + muse/lv2Support/sord-* + muse/lv2Support/sratom-* License: GPL-2+ Files: * @@ -37,10 +42,6 @@ Copyright: 2000 Werner Schweer (ws@seh.de) License: GPL-2+ -Files: muse/ladspa.h -Copyright: 2000-2002 Richard W.E. Furse, Paul -License: LGPL-2.1+ - Files: debian/* Copyright: 2011-2013 Alessio Treglia @@ -64,20 +65,3 @@ . You should have received a copy of the GNU General Public License along with this program. If not, see . - -License: LGPL-2.1+ - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - . - This library 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 - Lesser General Public License for more details. - . - On Debian systems, the complete text of the GNU Lesser General - Public License can be found in `/usr/share/common-licenses/LGPL-2.1'. - . - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . diff -Nru muse-2.1.2/debian/patches/0001-Use-a-more-consistent-versioning.patch muse-3.0.2+ds1/debian/patches/0001-Use-a-more-consistent-versioning.patch --- muse-2.1.2/debian/patches/0001-Use-a-more-consistent-versioning.patch 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/debian/patches/0001-Use-a-more-consistent-versioning.patch 2018-12-16 13:18:40.000000000 +0000 @@ -0,0 +1,24 @@ +From: Alessio Treglia +Date: Sun, 16 Dec 2018 14:05:33 +0100 +Subject: Use a more consistent versioning. + +Forwarded: not-needed +--- + CMakeLists.txt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index b646679..95e10a4 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -99,8 +99,8 @@ SET(MusE_VERSION_MINOR 0) + SET(MusE_VERSION_PATCH 1) + SET(MusE_VERSION "3.0.2") + SET(MusE_VERSION_FULL "3.0.2") +-SET(MusE_INSTALL_NAME "muse-3.0") +-SET(MusE_EXEC_NAME "muse3") ++SET(MusE_INSTALL_NAME "muse") ++SET(MusE_EXEC_NAME "muse") + + ## The directory where we will install the shared components: + SET(MusE_MODULES_DIR ${LIB_INSTALL_DIR}/${MusE_INSTALL_NAME}/modules) diff -Nru muse-2.1.2/debian/patches/0002-Add-f-option-to-desktop-file-Exec-key.patch muse-3.0.2+ds1/debian/patches/0002-Add-f-option-to-desktop-file-Exec-key.patch --- muse-2.1.2/debian/patches/0002-Add-f-option-to-desktop-file-Exec-key.patch 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/debian/patches/0002-Add-f-option-to-desktop-file-Exec-key.patch 2018-12-16 13:18:40.000000000 +0000 @@ -0,0 +1,22 @@ +From: James Cowgill +Date: Sun, 16 Dec 2018 14:05:33 +0100 +Subject: Add %f option to desktop file Exec key + +This allows muse to appear to file managers as capable of opening midi files. +--- + packaging/muse.desktop.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/packaging/muse.desktop.in b/packaging/muse.desktop.in +index cfba023..029b911 100644 +--- a/packaging/muse.desktop.in ++++ b/packaging/muse.desktop.in +@@ -6,7 +6,7 @@ Comment=Midi Music Editor + Comment[fr]=Éditeur MIDI de musique + Icon=muse_icon + Type=Application +-Exec=${MusE_EXEC_NAME} ++Exec=${MusE_EXEC_NAME} %f + Terminal=false + Categories=Sequencer;Midi;X-Jack;X-Sequencers;X-MIDI;Audio;AudioVideo; + MimeType=audio/midi;application/x-muse;application/xml; diff -Nru muse-2.1.2/debian/patches/0003-Fixes-compile-issue-with-QButtonGroup.patch muse-3.0.2+ds1/debian/patches/0003-Fixes-compile-issue-with-QButtonGroup.patch --- muse-2.1.2/debian/patches/0003-Fixes-compile-issue-with-QButtonGroup.patch 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/debian/patches/0003-Fixes-compile-issue-with-QButtonGroup.patch 2018-12-16 13:18:40.000000000 +0000 @@ -0,0 +1,21 @@ +From: William Stevens +Date: Fri, 14 Sep 2018 17:08:10 -0400 +Subject: Fixes compile issue with QButtonGroup + +Observed on ArchLinux. GCC 8.2.1. Qt 5.11.1. +--- + muse/widgets/genset.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/muse/widgets/genset.h b/muse/widgets/genset.h +index 6a04134..292ac7a 100644 +--- a/muse/widgets/genset.h ++++ b/muse/widgets/genset.h +@@ -29,6 +29,7 @@ + #include "cobject.h" + #include "mdisettings.h" + ++#include + #include + #include + diff -Nru muse-2.1.2/debian/patches/0004-Added-missing-includes-needed-by-newer-Qt-5.11.patch muse-3.0.2+ds1/debian/patches/0004-Added-missing-includes-needed-by-newer-Qt-5.11.patch --- muse-2.1.2/debian/patches/0004-Added-missing-includes-needed-by-newer-Qt-5.11.patch 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/debian/patches/0004-Added-missing-includes-needed-by-newer-Qt-5.11.patch 2018-12-16 13:18:40.000000000 +0000 @@ -0,0 +1,21 @@ +From: Orcan Ogetbil +Date: Mon, 28 May 2018 12:13:40 -0400 +Subject: Added missing #includes needed by newer Qt >= 5.11 + +--- + muse/mplugins/mitplugin.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/muse/mplugins/mitplugin.cpp b/muse/mplugins/mitplugin.cpp +index 2cc2f20..be657e7 100644 +--- a/muse/mplugins/mitplugin.cpp ++++ b/muse/mplugins/mitplugin.cpp +@@ -21,6 +21,8 @@ + // + //========================================================= + ++#include ++ + #include "mitplugin.h" + #include "app.h" + #include "event.h" diff -Nru muse-2.1.2/debian/patches/1001-buildsystem.patch muse-3.0.2+ds1/debian/patches/1001-buildsystem.patch --- muse-2.1.2/debian/patches/1001-buildsystem.patch 2016-09-02 12:08:59.000000000 +0000 +++ muse-3.0.2+ds1/debian/patches/1001-buildsystem.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -Description: Use a more consistent versioning. -Author: Alessio Treglia -Forwarded: not-needed ---- - CMakeLists.txt | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - ---- muse.orig/CMakeLists.txt -+++ muse/CMakeLists.txt -@@ -86,8 +86,8 @@ SET(MusE_VERSION_MINOR 1) - SET(MusE_VERSION_PATCH 0) - SET(MusE_VERSION "2.1.2") - SET(MusE_VERSION_FULL "2.1.2") --SET(MusE_INSTALL_NAME "muse-2.1") --SET(MusE_EXEC_NAME "muse2") -+SET(MusE_INSTALL_NAME "muse") -+SET(MusE_EXEC_NAME "muse") - - ## The directory where we will install the shared components: - SET(MusE_MODULES_DIR ${LIB_INSTALL_DIR}/${MusE_INSTALL_NAME}/modules) diff -Nru muse-2.1.2/debian/patches/1002-abs-errors.patch muse-3.0.2+ds1/debian/patches/1002-abs-errors.patch --- muse-2.1.2/debian/patches/1002-abs-errors.patch 2016-09-02 09:55:35.000000000 +0000 +++ muse-3.0.2+ds1/debian/patches/1002-abs-errors.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -Description: Fix errors related to the abs() function -Author: Philip Chung -Bug-Debian: https://bugs.debian.org/831147 ---- - midiedit/ecanvas.cpp | 1 + - sync.cpp | 4 ++-- - widgets/knob.cpp | 2 +- - widgets/knob_and_meter.cpp | 2 +- - 4 files changed, 5 insertions(+), 4 deletions(-) - ---- muse.orig/muse/widgets/knob.cpp -+++ muse/muse/widgets/knob.cpp -@@ -172,7 +172,7 @@ void Knob::drawKnob(QPainter* p, const Q - QPen pn; - pn.setCapStyle(Qt::FlatCap); - -- pn.setColor(d_shinyColor.lighter(l_const + abs(value() * l_slope))); -+ pn.setColor(d_shinyColor.lighter(l_const + std::abs(value() * l_slope))); - pn.setWidth(d_shineWidth * 2); - p->setPen(pn); - p->drawArc(aRect, 0, 360 * 16); ---- muse.orig/muse/widgets/knob_and_meter.cpp -+++ muse/muse/widgets/knob_and_meter.cpp -@@ -171,7 +171,7 @@ void KnobWithMeter::drawKnob(QPainter* p - QPen pn; - pn.setCapStyle(Qt::FlatCap); - -- pn.setColor(d_shinyColor.lighter(l_const + abs(value() * l_slope))); -+ pn.setColor(d_shinyColor.lighter(l_const + std::abs(value() * l_slope))); - pn.setWidth(d_shineWidth * 2); - p->setPen(pn); - p->drawArc(aRect, 0, 360 * 16); ---- muse.orig/muse/midiedit/ecanvas.cpp -+++ muse/muse/midiedit/ecanvas.cpp -@@ -24,6 +24,7 @@ - #include - #include - #include -+#include - #include - #include - #include ---- muse.orig/muse/sync.cpp -+++ muse/muse/sync.cpp -@@ -961,7 +961,7 @@ void MidiSeq::realtimeSystemInput(int po - if(_preDetect && pole == 0) - { - double real_tempo = 60.0/(avg_diff * 24.0); -- double real_tempo_diff = abs(real_tempo - _lastRealTempo); -+ double real_tempo_diff = std::abs(real_tempo - _lastRealTempo); - - // If the tempo changed a large amount, reset. - if(real_tempo_diff >= 10.0) // TODO: User-adjustable? -@@ -1009,7 +1009,7 @@ void MidiSeq::realtimeSystemInput(int po - if(pole == fin_idx) - { - double real_tempo = 60.0/(avg_diff * 24.0); -- double real_tempo_diff = abs(real_tempo - _lastRealTempo); -+ double real_tempo_diff = std::abs(real_tempo - _lastRealTempo); - - if(real_tempo_diff >= _tempoQuantizeAmount/2.0) // Anti-hysteresis - { diff -Nru muse-2.1.2/debian/patches/1003-desktop.patch muse-3.0.2+ds1/debian/patches/1003-desktop.patch --- muse-2.1.2/debian/patches/1003-desktop.patch 2016-09-02 09:55:35.000000000 +0000 +++ muse-3.0.2+ds1/debian/patches/1003-desktop.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -Description: Add %f option to desktop file Exec key - This allows muse to appear to file managers as capable of opening midi files. -Author: James Cowgill ---- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ---- a/packaging/muse.desktop.in -+++ b/packaging/muse.desktop.in -@@ -4,7 +4,7 @@ GenericName=Audio/MIDI Sequencer - Comment=Midi Music Editor - Icon=muse_icon - Type=Application --Exec=${MusE_EXEC_NAME} -+Exec=${MusE_EXEC_NAME} %f - Terminal=false - Categories=Sequencer;Midi;X-Jack;X-Sequencers;X-MIDI;Audio;AudioVideo; - MimeType=audio/midi;application/x-muse;application/xml; diff -Nru muse-2.1.2/debian/patches/series muse-3.0.2+ds1/debian/patches/series --- muse-2.1.2/debian/patches/series 2016-09-02 09:55:35.000000000 +0000 +++ muse-3.0.2+ds1/debian/patches/series 2018-12-16 13:18:40.000000000 +0000 @@ -1,3 +1,4 @@ -1001-buildsystem.patch -1002-abs-errors.patch -1003-desktop.patch +0001-Use-a-more-consistent-versioning.patch +0002-Add-f-option-to-desktop-file-Exec-key.patch +0003-Fixes-compile-issue-with-QButtonGroup.patch +0004-Added-missing-includes-needed-by-newer-Qt-5.11.patch diff -Nru muse-2.1.2/debian/rules muse-3.0.2+ds1/debian/rules --- muse-2.1.2/debian/rules 2016-09-02 12:08:59.000000000 +0000 +++ muse-3.0.2+ds1/debian/rules 2018-12-16 13:23:48.000000000 +0000 @@ -1,14 +1,14 @@ #!/usr/bin/make -f %: - dh $@ --parallel + dh $@ override_dh_clean: dh_clean -Xmuse/widgets/arrangercolumns.cpp~ \ -Xmuse/widgets/arrangercolumns.h~ -X*~ override_dh_auto_configure: - dh_auto_configure -- -DCMAKE_BUILD_TYPE=release -DLIB_INSTALL_DIR=/usr/lib + dh_auto_configure -- -DLIB_INSTALL_DIR=/usr/lib override_dh_install: dh_install -pmuse diff -Nru muse-2.1.2/debian/watch muse-3.0.2+ds1/debian/watch --- muse-2.1.2/debian/watch 2016-09-02 09:55:35.000000000 +0000 +++ muse-3.0.2+ds1/debian/watch 2018-12-16 12:46:39.000000000 +0000 @@ -1,3 +1,3 @@ -version=3 -opts=uversionmangle=s/(alpha|beta|rc)/~$1/ \ -http://sf.net/lmuse/muse-(.*)\.tar\.gz +version=4 +opts=uversionmangle=s/(alpha|beta|rc)/~$1/,dversionmangle=s/@DEB_EXT@//,repacksuffix=+ds1 \ +https://sf.net/lmuse/ muse-.*/muse-(.*)\.tar\.gz diff -Nru muse-2.1.2/doc/developer_docs.tex muse-3.0.2+ds1/doc/developer_docs.tex --- muse-2.1.2/doc/developer_docs.tex 2013-03-28 15:17:03.000000000 +0000 +++ muse-3.0.2+ds1/doc/developer_docs.tex 2018-01-26 21:59:38.000000000 +0000 @@ -25,7 +25,7 @@ %% "feature requests" as flo did in r1497 IS one). %% Below that, feel free to change the logical arrangement %% (making paragraphs to subsections and similar) if you deem it -%% neccessary. +%% necessary. %% %% Whenever referring to code symbols, constants or source/header %% files, please use \sym{someClass::someSymbol}, \usym{UPPERCASE_THING} @@ -404,7 +404,7 @@ that is, a list of \sym{CtrlList}s, that is, a list of lists of controller-objects which hold the control points of the automation graph. The \sym{CtrlList} also stores whether the list is meant discrete -(a new control point results in a value-jump) or continous (a new control +(a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value). While \sym{PluginI::{\_}controlFifo} can be queried very quickly and @@ -434,7 +434,7 @@ \paragraph{Just use the current data access functions} By just using the current functions for accessing automation data, we might get a quick-and-dirty solution, which however wastes way too -much CPU ressources. This is because on \emph{every single frame}, we +much CPU resources. This is because on \emph{every single frame}, we need to do a binary search on multiple controller lists. @@ -489,7 +489,7 @@ \subsection{Use cases} \paragraph{Saving CPU} -On slow systems, this is neccessary for songs with lots of, or demanding +On slow systems, this is necessary for songs with lots of, or demanding (or both) soft synthes / plugins. Even if the synth or plugin is so demanding that your system is not able to produce sound in real-time, then with this feature you'll be able to use the synth (this will make @@ -597,7 +597,7 @@ which might include window name, MDI-ness etc. \item A toolbar which contains controls suitable for every single slot. \item A container with one or more slots; the slots can be scrolled in - y-direction if there are multipe slots. + y-direction if there are multiple slots. \item A time-scrollbar with zoom \end{itemize} @@ -634,7 +634,7 @@ controllers like "pan", the unbiased values shall be transformed, that is, a pan of 64, with $\textrm{bias}=2$ and $\textrm{gain}=0.5$, shall be transformed to 66 (because 64 is actually 0, while 0 is actually -64). -These values shall be set in the arranger and whereever the actual +These values shall be set in the arranger and wherever the actual controller/automation values can be edited. \section{Enabled-indicator while recording} @@ -652,7 +652,7 @@ \section{Linear automation editing} While holding some modifier key (like shift), operating the MusE-native- GUI sliders shall only generate control points when clicking and when -releasing the slider. This will result in linear graphs for continous +releasing the slider. This will result in linear graphs for continuous controllers, and in large steps for discrete controllers (which is in particular useful for stuff like "which low/high-pass filter type to use"). @@ -682,7 +682,7 @@ % TODO: unified automation and midi ctrls: % both shall be editable through the same editors % four modes: 1. discrete -% 2. continous (plus a global and per-port setting for the max rate) +% 2. continuous (plus a global and per-port setting for the max rate) % 3. switch (not necessarily ON/OFF; signals with color plus text annotation) % 4. raw (no graph, instead a box with the value sent out (for "all voices off") % they shall be copy-and-pastable, at least between compatible modes diff -Nru muse-2.1.2/doc/documentation.tex muse-3.0.2+ds1/doc/documentation.tex --- muse-2.1.2/doc/documentation.tex 2013-03-28 15:17:03.000000000 +0000 +++ muse-3.0.2+ds1/doc/documentation.tex 2018-01-26 21:59:38.000000000 +0000 @@ -25,7 +25,7 @@ %% "feature requests" as flo did in r1497 IS one). %% Below that, feel free to change the logical arrangement %% (making paragraphs to subsections and similar) if you deem it -%% neccessary. +%% necessary. %% %% Whenever referring to code symbols, constants or source/header %% files, please use \sym{someClass::someSymbol}, \usym{UPPERCASE_THING} @@ -166,7 +166,7 @@ You are, if you have printed this document, holding in your hand the written documentation for the audio and midi sequencer MusE version 2.\\ \url{http://www.muse-sequencer.org} is MusE's home on the internet where -everything MusE releated should be possible to find, software, this +everything MusE related should be possible to find, software, this documentation, forums, mailing lists, bug reporting, FAQs. If you have this document but not the software head on over there to find what it's all about. @@ -377,7 +377,7 @@ \subsubsection{Recording Midi} %TODO: walkthrough of recording midi TBD \subsubsection{Recording Audio} -At this point we'll make a a slight detour into full on audio recording. Getting +At this point we'll make a slight detour into full on audio recording. Getting audio out of MusE has already been covered in the previous chapters so we will concentrate on the additional steps needed to record onto an audio track.\\ \\ @@ -700,7 +700,7 @@ \emph{Tip:} If the Song Type is GM, GS, or XG, you may need to store %% FIXME Ref to song type desired values at transport position zero, otherwise your adjustments -may be overriden by the instrument when the transport is moved back +may be overridden by the instrument when the transport is moved back to position zero. If this behaviour is undesired, you can set the Song Type to 'NO' meaning no song type. %% FIXME Ref to explanation of instruments and default controller values @@ -820,7 +820,7 @@ \subsection{The audio effects rack} \label{effects_rack} All audio track types (Input, Output, Group, Wave, Synth, and Aux) have an effects rack into which audio plugins can be inserted in a chain. -Currently each rack can accomodate up to four plugins. +Currently each rack can accommodate up to four plugins. MusE currently supports LADSPA plugins and DSSI synth and effects plugins. @@ -1018,7 +1018,7 @@ \subsubsection{Audio settings} \paragraph{Minimum control period} Plugins can usually process an arbitrarily small (or large) amount -of samples. If some plugin control value changes continously, to provide +of samples. If some plugin control value changes continuously, to provide ideal listening experience, MusE would need to call the plugin 44100 times a second, asking for one single value at a time. With the minimum control period setting, the user can force MusE to ask the plugin for diff -Nru muse-2.1.2/doc/html/single/developer_docs/developer_docs.html muse-3.0.2+ds1/doc/html/single/developer_docs/developer_docs.html --- muse-2.1.2/doc/html/single/developer_docs/developer_docs.html 2013-03-28 15:16:29.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/single/developer_docs/developer_docs.html 2018-01-26 21:59:38.000000000 +0000 @@ -1297,7 +1297,7 @@ `<<91 <<< c@amelhyph<269>>trlList also stores whether the list is meant discrete -(a new control point results in a value-jump) or continous (a new control +(a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value).

@@ -1372,7 +1372,7 @@ By just using the current functions for accessing automation data, we might get a quick-and-dirty solution, which however wastes way too -much CPU ressources. This is because on every single frame, we +much CPU resources. This is because on every single frame, we need to do a binary search on multiple controller lists.

@@ -1451,7 +1451,7 @@

Saving CPU

-On slow systems, this is neccessary for songs with lots of, or demanding +On slow systems, this is necessary for songs with lots of, or demanding (or both) soft synthes / plugins. Even if the synth or plugin is so demanding that your system is not able to produce sound in real-time, then with this feature you'll be able to use the synth (this will make @@ -1611,7 +1611,7 @@
  • A toolbar which contains controls suitable for every single slot.
  • A container with one or more slots; the slots can be scrolled in - y-direction if there are multipe slots. + y-direction if there are multiple slots.
  • A time-scrollbar with zoom
  • @@ -1681,7 +1681,7 @@ SRC="img3.png" ALT="$\textrm{gain}=0.5$">, shall be transformed to 66 (because 64 is actually 0, while 0 is actually -64). -These values shall be set in the arranger and whereever the actual +These values shall be set in the arranger and wherever the actual controller/automation values can be edited.

    @@ -1708,7 +1708,7 @@ While holding some modifier key (like shift), operating the MusE-native- GUI sliders shall only generate control points when clicking and when -releasing the slider. This will result in linear graphs for continous +releasing the slider. This will result in linear graphs for continuous controllers, and in large steps for discrete controllers (which is in particular useful for stuff like "which low/high-pass filter type to use"). diff -Nru muse-2.1.2/doc/html/single/developer_docs/index.html muse-3.0.2+ds1/doc/html/single/developer_docs/index.html --- muse-2.1.2/doc/html/single/developer_docs/index.html 2013-03-28 15:16:29.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/single/developer_docs/index.html 2018-01-26 21:59:38.000000000 +0000 @@ -1297,7 +1297,7 @@ `<<91 <<< c@amelhyph<269>>trlList also stores whether the list is meant discrete -(a new control point results in a value-jump) or continous (a new control +(a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value).

    @@ -1372,7 +1372,7 @@ By just using the current functions for accessing automation data, we might get a quick-and-dirty solution, which however wastes way too -much CPU ressources. This is because on every single frame, we +much CPU resources. This is because on every single frame, we need to do a binary search on multiple controller lists.

    @@ -1451,7 +1451,7 @@

    Saving CPU

    -On slow systems, this is neccessary for songs with lots of, or demanding +On slow systems, this is necessary for songs with lots of, or demanding (or both) soft synthes / plugins. Even if the synth or plugin is so demanding that your system is not able to produce sound in real-time, then with this feature you'll be able to use the synth (this will make @@ -1611,7 +1611,7 @@
  • A toolbar which contains controls suitable for every single slot.
  • A container with one or more slots; the slots can be scrolled in - y-direction if there are multipe slots. + y-direction if there are multiple slots.
  • A time-scrollbar with zoom
  • @@ -1681,7 +1681,7 @@ SRC="img3.png" ALT="$\textrm{gain}=0.5$">, shall be transformed to 66 (because 64 is actually 0, while 0 is actually -64). -These values shall be set in the arranger and whereever the actual +These values shall be set in the arranger and wherever the actual controller/automation values can be edited.

    @@ -1708,7 +1708,7 @@ While holding some modifier key (like shift), operating the MusE-native- GUI sliders shall only generate control points when clicking and when -releasing the slider. This will result in linear graphs for continous +releasing the slider. This will result in linear graphs for continuous controllers, and in large steps for discrete controllers (which is in particular useful for stuff like "which low/high-pass filter type to use"). diff -Nru muse-2.1.2/doc/html/single/documentation/documentation.html muse-3.0.2+ds1/doc/html/single/documentation/documentation.html --- muse-2.1.2/doc/html/single/documentation/documentation.html 2013-03-28 15:16:28.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/single/documentation/documentation.html 2018-01-26 21:59:38.000000000 +0000 @@ -131,7 +131,7 @@ written documentation for the audio and midi sequencer MusE version 2.
    http://www.muse-sequencer.org is MusE's home on the internet where -everything MusE releated should be possible to find, software, this +everything MusE related should be possible to find, software, this documentation, forums, mailing lists, bug reporting, FAQs. If you have this document but not the software head on over there to find what it's all about. @@ -469,7 +469,7 @@

    Recording Audio

    -At this point we'll make a a slight detour into full on audio recording. Getting +At this point we'll make a slight detour into full on audio recording. Getting audio out of MusE has already been covered in the previous chapters so we will concentrate on the additional steps needed to record onto an audio track.
    @@ -1003,7 +1003,7 @@

    Tip: If the Song Type is GM, GS, or XG, you may need to store desired values at transport position zero, otherwise your adjustments -may be overriden by the instrument when the transport is moved back +may be overridden by the instrument when the transport is moved back to position zero. If this behaviour is undesired, you can set the Song Type to 'NO' meaning no song type. @@ -1200,7 +1200,7 @@ All audio track types (Input, Output, Group, Wave, Synth, and Aux) have an effects rack into which audio plugins can be inserted in a chain. -Currently each rack can accomodate up to four plugins. +Currently each rack can accommodate up to four plugins.

    MusE currently supports LADSPA plugins and DSSI synth and effects @@ -1524,7 +1524,7 @@ Minimum control period Plugins can usually process an arbitrarily small (or large) amount -of samples. If some plugin control value changes continously, to provide +of samples. If some plugin control value changes continuously, to provide ideal listening experience, MusE would need to call the plugin 44100 times a second, asking for one single value at a time. With the minimum control period setting, the user can force MusE to ask the plugin for diff -Nru muse-2.1.2/doc/html/single/documentation/index.html muse-3.0.2+ds1/doc/html/single/documentation/index.html --- muse-2.1.2/doc/html/single/documentation/index.html 2013-03-28 15:16:28.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/single/documentation/index.html 2018-01-26 21:59:38.000000000 +0000 @@ -131,7 +131,7 @@ written documentation for the audio and midi sequencer MusE version 2.
    http://www.muse-sequencer.org is MusE's home on the internet where -everything MusE releated should be possible to find, software, this +everything MusE related should be possible to find, software, this documentation, forums, mailing lists, bug reporting, FAQs. If you have this document but not the software head on over there to find what it's all about. @@ -469,7 +469,7 @@

    Recording Audio

    -At this point we'll make a a slight detour into full on audio recording. Getting +At this point we'll make a slight detour into full on audio recording. Getting audio out of MusE has already been covered in the previous chapters so we will concentrate on the additional steps needed to record onto an audio track.
    @@ -1003,7 +1003,7 @@

    Tip: If the Song Type is GM, GS, or XG, you may need to store desired values at transport position zero, otherwise your adjustments -may be overriden by the instrument when the transport is moved back +may be overridden by the instrument when the transport is moved back to position zero. If this behaviour is undesired, you can set the Song Type to 'NO' meaning no song type. @@ -1200,7 +1200,7 @@ All audio track types (Input, Output, Group, Wave, Synth, and Aux) have an effects rack into which audio plugins can be inserted in a chain. -Currently each rack can accomodate up to four plugins. +Currently each rack can accommodate up to four plugins.

    MusE currently supports LADSPA plugins and DSSI synth and effects @@ -1524,7 +1524,7 @@ Minimum control period Plugins can usually process an arbitrarily small (or large) amount -of samples. If some plugin control value changes continously, to provide +of samples. If some plugin control value changes continuously, to provide ideal listening experience, MusE would need to call the plugin 44100 times a second, asking for one single value at a time. With the minimum control period setting, the user can force MusE to ask the plugin for diff -Nru muse-2.1.2/doc/html/split/developer_docs/node10.html muse-3.0.2+ds1/doc/html/split/developer_docs/node10.html --- muse-2.1.2/doc/html/split/developer_docs/node10.html 2013-03-28 15:16:45.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/developer_docs/node10.html 2018-01-26 21:59:38.000000000 +0000 @@ -161,7 +161,7 @@

    Saving CPU

    -On slow systems, this is neccessary for songs with lots of, or demanding +On slow systems, this is necessary for songs with lots of, or demanding (or both) soft synthes / plugins. Even if the synth or plugin is so demanding that your system is not able to produce sound in real-time, then with this feature you'll be able to use the synth (this will make diff -Nru muse-2.1.2/doc/html/split/developer_docs/node11.html muse-3.0.2+ds1/doc/html/split/developer_docs/node11.html --- muse-2.1.2/doc/html/split/developer_docs/node11.html 2013-03-28 15:16:45.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/developer_docs/node11.html 2018-01-26 21:59:38.000000000 +0000 @@ -79,7 +79,7 @@
  • A toolbar which contains controls suitable for every single slot.
  • A container with one or more slots; the slots can be scrolled in - y-direction if there are multipe slots. + y-direction if there are multiple slots.
  • A time-scrollbar with zoom
  • diff -Nru muse-2.1.2/doc/html/split/developer_docs/node12.html muse-3.0.2+ds1/doc/html/split/developer_docs/node12.html --- muse-2.1.2/doc/html/split/developer_docs/node12.html 2013-03-28 15:16:45.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/developer_docs/node12.html 2018-01-26 21:59:38.000000000 +0000 @@ -86,7 +86,7 @@ SRC="img3.png" ALT="$\textrm{gain}=0.5$">, shall be transformed to 66 (because 64 is actually 0, while 0 is actually -64). -These values shall be set in the arranger and whereever the actual +These values shall be set in the arranger and wherever the actual controller/automation values can be edited.

    diff -Nru muse-2.1.2/doc/html/split/developer_docs/node14.html muse-3.0.2+ds1/doc/html/split/developer_docs/node14.html --- muse-2.1.2/doc/html/split/developer_docs/node14.html 2013-03-28 15:16:45.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/developer_docs/node14.html 2018-01-26 21:59:38.000000000 +0000 @@ -62,7 +62,7 @@ While holding some modifier key (like shift), operating the MusE-native- GUI sliders shall only generate control points when clicking and when -releasing the slider. This will result in linear graphs for continous +releasing the slider. This will result in linear graphs for continuous controllers, and in large steps for discrete controllers (which is in particular useful for stuff like "which low/high-pass filter type to use"). diff -Nru muse-2.1.2/doc/html/split/developer_docs/node7.html muse-3.0.2+ds1/doc/html/split/developer_docs/node7.html --- muse-2.1.2/doc/html/split/developer_docs/node7.html 2013-03-28 15:16:45.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/developer_docs/node7.html 2018-01-26 21:59:38.000000000 +0000 @@ -121,7 +121,7 @@ `<<91 <<< c@amelhyph<269>>trlList also stores whether the list is meant discrete -(a new control point results in a value-jump) or continous (a new control +(a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value).

    @@ -196,7 +196,7 @@ By just using the current functions for accessing automation data, we might get a quick-and-dirty solution, which however wastes way too -much CPU ressources. This is because on every single frame, we +much CPU resources. This is because on every single frame, we need to do a binary search on multiple controller lists.

    diff -Nru muse-2.1.2/doc/html/split/documentation/node11.html muse-3.0.2+ds1/doc/html/split/documentation/node11.html --- muse-2.1.2/doc/html/split/documentation/node11.html 2013-03-28 15:16:44.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/documentation/node11.html 2018-01-26 21:59:38.000000000 +0000 @@ -200,7 +200,7 @@ Minimum control period Plugins can usually process an arbitrarily small (or large) amount -of samples. If some plugin control value changes continously, to provide +of samples. If some plugin control value changes continuously, to provide ideal listening experience, MusE would need to call the plugin 44100 times a second, asking for one single value at a time. With the minimum control period setting, the user can force MusE to ask the plugin for diff -Nru muse-2.1.2/doc/html/split/documentation/node2.html muse-3.0.2+ds1/doc/html/split/documentation/node2.html --- muse-2.1.2/doc/html/split/documentation/node2.html 2013-03-28 15:16:44.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/documentation/node2.html 2018-01-26 21:59:38.000000000 +0000 @@ -64,7 +64,7 @@ written documentation for the audio and midi sequencer MusE version 2.
    http://www.muse-sequencer.org is MusE's home on the internet where -everything MusE releated should be possible to find, software, this +everything MusE related should be possible to find, software, this documentation, forums, mailing lists, bug reporting, FAQs. If you have this document but not the software head on over there to find what it's all about. diff -Nru muse-2.1.2/doc/html/split/documentation/node4.html muse-3.0.2+ds1/doc/html/split/documentation/node4.html --- muse-2.1.2/doc/html/split/documentation/node4.html 2013-03-28 15:16:44.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/documentation/node4.html 2018-01-26 21:59:38.000000000 +0000 @@ -431,7 +431,7 @@

    Recording Audio

    -At this point we'll make a a slight detour into full on audio recording. Getting +At this point we'll make a slight detour into full on audio recording. Getting audio out of MusE has already been covered in the previous chapters so we will concentrate on the additional steps needed to record onto an audio track.
    diff -Nru muse-2.1.2/doc/html/split/documentation/node6.html muse-3.0.2+ds1/doc/html/split/documentation/node6.html --- muse-2.1.2/doc/html/split/documentation/node6.html 2013-03-28 15:16:44.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/documentation/node6.html 2018-01-26 21:59:38.000000000 +0000 @@ -410,7 +410,7 @@

    Tip: If the Song Type is GM, GS, or XG, you may need to store desired values at transport position zero, otherwise your adjustments -may be overriden by the instrument when the transport is moved back +may be overridden by the instrument when the transport is moved back to position zero. If this behaviour is undesired, you can set the Song Type to 'NO' meaning no song type. diff -Nru muse-2.1.2/doc/html/split/documentation/node9.html muse-3.0.2+ds1/doc/html/split/documentation/node9.html --- muse-2.1.2/doc/html/split/documentation/node9.html 2013-03-28 15:16:44.000000000 +0000 +++ muse-3.0.2+ds1/doc/html/split/documentation/node9.html 2018-01-26 21:59:38.000000000 +0000 @@ -92,7 +92,7 @@ All audio track types (Input, Output, Group, Wave, Synth, and Aux) have an effects rack into which audio plugins can be inserted in a chain. -Currently each rack can accomodate up to four plugins. +Currently each rack can accommodate up to four plugins.

    MusE currently supports LADSPA plugins and DSSI synth and effects diff -Nru muse-2.1.2/grepmidi/grepmidi.cpp muse-3.0.2+ds1/grepmidi/grepmidi.cpp --- muse-2.1.2/grepmidi/grepmidi.cpp 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/grepmidi/grepmidi.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -157,6 +157,8 @@ case 0xe0: getc(f); ++cpos; + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH case 0xc0: case 0xd0: if (a == -1) { @@ -298,7 +300,7 @@ FILE* f; if (p && strcmp(p, ".gz") == 0) { char buffer[512]; - sprintf(buffer, "gunzip < %s", name); + snprintf(buffer, 512, "gunzip < %s", name); f = popen(buffer, "r"); } else { diff -Nru muse-2.1.2/libs/CMakeLists.txt muse-3.0.2+ds1/libs/CMakeLists.txt --- muse-2.1.2/libs/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/libs/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,65 @@ +#============================================================================= +# MusE +# Linux Music Editor +# +# CMakeLists.txt +# (C) Copyright 2014 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 +# as published by the Free Software Foundation; either 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(${PROJECT_SOURCE_DIR}/pch.txt) + +## +## List of source files to compile +## +file (GLOB muse_string_source_files + strntcpy.cpp + ) + +## +## Define target +## +add_library(muse_string ${MODULES_BUILD} + ${PROJECT_BINARY_DIR}/all-pic.h + ${muse_string_source_files} + ) + +## +## Append to the list of translations +## +set (FILES_TO_TRANSLATE + ${FILES_TO_TRANSLATE} + ${muse_string_source_files} + CACHE INTERNAL "" + ) + +## +## Compilation flags and target name +## +set_target_properties( muse_string + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all-pic.h" + ## OUTPUT_NAME muse_helper_str + ) + +## +## Install location +## +if ( ${MODULES_BUILD} STREQUAL SHARED ) + install(TARGETS muse_string + DESTINATION ${MusE_MODULES_DIR} + ) +endif ( ${MODULES_BUILD} STREQUAL SHARED ) diff -Nru muse-2.1.2/libs/strntcpy.cpp muse-3.0.2+ds1/libs/strntcpy.cpp --- muse-2.1.2/libs/strntcpy.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/libs/strntcpy.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,51 @@ +//========================================================= +// MusE +// Linux Music Editor +// strntcpy.cpp +// (C) Copyright 2014 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 +// 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 "strntcpy.h" + +namespace MusELib { + +// Copies at most size bytes, always null-terminating the destination. It does not null-fill remaining destination bytes. +// NULL source is accepted. Returns the destination, or zero if size is zero. +// Note that there is a similar strlcpy() in BSD (libbsd in linux), as well as QT's qstrncpy(). +char* strntcpy(char *dest, const char *src, int size) +{ + if(size == 0) + return 0; + if(!src) + { + dest[0] = '\0'; + return dest; + } + int i = 0; + while(i < size) + { + dest[i] = src[i]; + if(src[i++] == '\0') + break; + } + dest[--i] = '\0'; + return dest; +} + + +} // namespace MusELib diff -Nru muse-2.1.2/libs/strntcpy.h muse-3.0.2+ds1/libs/strntcpy.h --- muse-2.1.2/libs/strntcpy.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/libs/strntcpy.h 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,34 @@ +//========================================================= +// MusE +// Linux Music Editor +// strntcpy.h +// (C) Copyright 2014 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 +// 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 __STRNTCPY_H__ +#define __STRNTCPY_H__ + +namespace MusELib { + +// Copies at most size bytes, always null-terminating the destination. It does not null-fill remaining destination bytes. +// Note that there is a similar strlcpy() in BSD (libbsd in linux), as well as QT's qstrncpy(). +extern char* strntcpy(char *dest, const char *src, int size); + +} // namespace MusELib + +#endif \ No newline at end of file diff -Nru muse-2.1.2/LICENSE muse-3.0.2+ds1/LICENSE --- muse-2.1.2/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/LICENSE 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,2 @@ +please see muse3/COPYING. + diff -Nru muse-2.1.2/muse/app.cpp muse-3.0.2+ds1/muse/app.cpp --- muse-2.1.2/muse/app.cpp 2013-03-28 20:34:30.000000000 +0000 +++ muse-3.0.2+ds1/muse/app.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: app.cpp,v 1.113.2.68 2009/12/21 14:51:51 spamatica Exp $ // // (C) Copyright 1999-2011 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -34,11 +34,15 @@ #include #include #include -#include +#include #include #include +#include +#include +#include #include +#include #include "app.h" #include "master/lmaster.h" @@ -88,33 +92,38 @@ #include "songinfo.h" #include "ticksynth.h" #include "transport.h" +#include "tlist.h" #include "waveedit.h" #include "widgets/projectcreateimpl.h" #include "widgets/menutitleitem.h" #include "tools.h" #include "widgets/unusedwavefiles.h" #include "functions.h" -#include "trackdrummapupdater.h" #include "songpos_toolbar.h" #include "sig_tempo_toolbar.h" +#include "cpu_toolbar.h" + namespace MusECore { extern void exitJackAudio(); extern void exitDummyAudio(); extern void exitOSC(); extern void exitMidiAlsa(); + +#ifdef HAVE_RTAUDIO +extern void exitRtAudio(); +#endif } namespace MusEGui { extern void deleteIcons(); -//extern void cacheJackRouteNames(); static pthread_t watchdogThread; //ErrorHandler *error; #define PROJECT_LIST_LEN 6 -static QString* projectList[PROJECT_LIST_LEN]; +QStringList projectRecentList; #ifdef HAVE_LASH #include @@ -133,7 +142,7 @@ //--------------------------------------------------------- void microSleep(long msleep) { - bool sleepOk=-1; + int sleepOk=-1; while(sleepOk==-1) sleepOk=usleep(msleep); @@ -145,86 +154,81 @@ bool MusE::seqStart() { - if (MusEGlobal::audio->isRunning()) { - printf("seqStart(): already running\n"); - return true; + if(MusEGlobal::audio) + { + if(!MusEGlobal::audio->isRunning()) + { + // Start the audio. (Re)connect audio inputs and outputs. Force-fill the audio pre-fetch buffers for the current cpos. + if(MusEGlobal::audio->start()) + { + // + // wait for jack callback + // + for(int i = 0; i < 60; ++i) + { + if(MusEGlobal::audio->isRunning()) + break; + sleep(1); } - - if (!MusEGlobal::audio->start()) { - QMessageBox::critical( MusEGlobal::muse, tr("Failed to start audio!"), - tr("Was not able to start audio, check if jack is running.\n")); - return false; + if(!MusEGlobal::audio->isRunning()) + { + QMessageBox::critical( MusEGlobal::muse, tr("Failed to start audio!"), + tr("Timeout waiting for audio to run. Check if jack is running or try another driver.\n")); + } + } + else + { + QMessageBox::critical( MusEGlobal::muse, tr("Failed to start audio!"), + tr("Was not able to start audio, check if jack is running or try another driver.\n")); } + } + } + else + fprintf(stderr, "seqStart(): audio is NULL\n"); - // - // wait for jack callback - // - for(int i = 0; i < 60; ++i) + + // Now it is safe to ask the driver for realtime priority + + int pfprio = 0; + // TODO: Hm why is prefetch priority so high again? Was it to overcome some race problems? Should it be lowest - disk thread? + if(MusEGlobal::audioDevice) { - if(MusEGlobal::audio->isRunning()) - break; - sleep(1); + MusEGlobal::realTimePriority = MusEGlobal::audioDevice->realtimePriority(); + if(MusEGlobal::debugMsg) + fprintf(stderr, "MusE::seqStart: getting audio driver MusEGlobal::realTimePriority:%d\n", MusEGlobal::realTimePriority); + + // NOTE: MusEGlobal::realTimeScheduling can be true (gotten using jack_is_realtime()), + // while the determined MusEGlobal::realTimePriority can be 0. + // MusEGlobal::realTimePriority is gotten using pthread_getschedparam() on the client thread + // in JackAudioDevice::realtimePriority() which is a bit flawed - it reports there's no RT... + if(MusEGlobal::realTimeScheduling) + { + if(MusEGlobal::realTimePriority - 5 >= 0) + pfprio = MusEGlobal::realTimePriority - 5; + } + // FIXME: The realTimePriority of the Jack thread seems to always be 5 less than the value passed to jackd command. } - if(!MusEGlobal::audio->isRunning()) + else + fprintf(stderr, "seqStart(): audioDevice is NULL\n"); + + if(MusEGlobal::audioPrefetch) { - QMessageBox::critical( MusEGlobal::muse, tr("Failed to start audio!"), - tr("Timeout waiting for audio to run. Check if jack is running.\n")); - } - // - // now its safe to ask the driver for realtime - // priority - - MusEGlobal::realTimePriority = MusEGlobal::audioDevice->realtimePriority(); - if(MusEGlobal::debugMsg) - printf("MusE::seqStart: getting audio driver MusEGlobal::realTimePriority:%d\n", MusEGlobal::realTimePriority); - - int pfprio = 0; - int midiprio = 0; - - // NOTE: MusEGlobal::realTimeScheduling can be true (gotten using jack_is_realtime()), - // while the determined MusEGlobal::realTimePriority can be 0. - // MusEGlobal::realTimePriority is gotten using pthread_getschedparam() on the client thread - // in JackAudioDevice::realtimePriority() which is a bit flawed - it reports there's no RT... - if(MusEGlobal::realTimeScheduling) - { - if(MusEGlobal::realTimePriority - 5 >= 0) - pfprio = MusEGlobal::realTimePriority - 5; - if(MusEGlobal::realTimePriority - 1 >= 0) - midiprio = MusEGlobal::realTimePriority - 1; - } - - if(MusEGlobal::midiRTPrioOverride > 0) - midiprio = MusEGlobal::midiRTPrioOverride; - - // FIXME FIXME: The MusEGlobal::realTimePriority of the Jack thread seems to always be 5 less than the value passed to jackd command. - - MusEGlobal::audioPrefetch->start(pfprio); - - MusEGlobal::audioPrefetch->msgSeek(0, true); // force - - MusEGlobal::midiSeq->start(midiprio); - - int counter=0; - while (++counter) { - if (counter > 1000) { - fprintf(stderr,"midi sequencer thread does not start!? Exiting...\n"); - exit(33); + if(!MusEGlobal::audioPrefetch->isRunning()) + { + MusEGlobal::audioPrefetch->start(pfprio); + // In case prefetch is not filled, do it now. + MusEGlobal::audioPrefetch->msgSeek(MusEGlobal::audio->pos().frame()); // Don't force. } - MusEGlobal::midiSeqRunning = MusEGlobal::midiSeq->isRunning(); - if (MusEGlobal::midiSeqRunning) - break; - usleep(1000); - if(MusEGlobal::debugMsg) - printf("looping waiting for sequencer thread to start\n"); } - if(!MusEGlobal::midiSeqRunning) - { - fprintf(stderr, "midiSeq is not running! Exiting...\n"); - exit(33); - } + else + fprintf(stderr, "seqStart(): audioPrefetch is NULL\n"); + + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->start(0); // Prio unused, set in start. + return true; } - + //--------------------------------------------------------- // stop //--------------------------------------------------------- @@ -236,7 +240,8 @@ MusEGlobal::song->setStop(true); MusEGlobal::song->setStopPlay(false); - MusEGlobal::midiSeq->stop(true); + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->stop(true); MusEGlobal::audio->stop(true); MusEGlobal::audioPrefetch->stop(true); if (MusEGlobal::realTimeScheduling && watchdogThread) @@ -267,33 +272,19 @@ } //--------------------------------------------------------- -// addProject +// addProject to recent list //--------------------------------------------------------- void addProject(const QString& name) - { - for (int i = 0; i < PROJECT_LIST_LEN; ++i) { - if (projectList[i] == 0) - break; - if (name == *projectList[i]) { - int dst = i; - int src = i+1; - int n = PROJECT_LIST_LEN - i - 1; - delete projectList[i]; - for (int k = 0; k < n; ++k) - projectList[dst++] = projectList[src++]; - projectList[dst] = 0; - break; - } - } - QString** s = &projectList[PROJECT_LIST_LEN - 2]; - QString** d = &projectList[PROJECT_LIST_LEN - 1]; - if (*d) - delete *d; - for (int i = 0; i < PROJECT_LIST_LEN-1; ++i) - *d-- = *s--; - projectList[0] = new QString(name); - } +{ + if (projectRecentList.contains(name)) + return; + + projectRecentList.push_front(name); + if (projectRecentList.size() > PROJECT_LIST_LEN) + projectRecentList.pop_back(); + +} //--------------------------------------------------------- // MusE @@ -302,8 +293,9 @@ MusE::MusE() : QMainWindow() { setIconSize(ICON_SIZE); - setFocusPolicy(Qt::NoFocus); + setFocusPolicy(Qt::NoFocus); MusEGlobal::muse = this; // hack + _isRestartingApp = false; clipListEdit = 0; midiSyncConfig = 0; midiRemoteConfig = 0; @@ -323,10 +315,12 @@ //audioMixer = 0; mixer1 = 0; mixer2 = 0; + routeDialog = 0; watchdogThread = 0; editInstrument = 0; //routingPopupMenu = 0; progress = 0; + saveIncrement = 0; activeTopWin = NULL; currentMenuSharingTopwin = NULL; waitingForTopwin = NULL; @@ -344,9 +338,34 @@ MusEGlobal::heartBeatTimer = new QTimer(this); MusEGlobal::heartBeatTimer->setObjectName("timer"); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), MusEGlobal::song, SLOT(beat())); + connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); connect(this, SIGNAL(activeTopWinChanged(MusEGui::TopWin*)), SLOT(activeTopWinChangedSlot(MusEGui::TopWin*))); - new MusECore::TrackDrummapUpdater(this); // no need for keeping the reference, the thing autoconnects on its own. - + connect(MusEGlobal::song, SIGNAL(sigDirty()), this, SLOT(setDirty())); + + blinkTimer = new QTimer(this); + blinkTimer->setObjectName("blinkTimer"); + connect(blinkTimer, SIGNAL(timeout()), SLOT(blinkTimerSlot())); + blinkTimer->start( 250 ); // Every quarter second, for a flash rate of 2 Hz. + + saveTimer = new QTimer(this); + connect(saveTimer, SIGNAL(timeout()), this, SLOT(saveTimerSlot())); + saveTimer->start( 60 * 1000 ); // every minute + + messagePollTimer = new QTimer(this); + messagePollTimer->setObjectName("messagePollTimer"); + connect(messagePollTimer, SIGNAL(timeout()), SLOT(messagePollTimerSlot())); + // A zero-millisecond poll timer. Oops, no can't do that in the gui thread, + // it spikes the CPU usage because it eats up all the idle time. Use say, 50Hz 20msec. + messagePollTimer->start(20); + + //init cpuload stuff + clock_gettime(CLOCK_REALTIME, &lastSysTime); + lastCpuTime.tv_sec = 0; + lastCpuTime.tv_usec = 0; + fAvrCpuLoad = 0.0f; + avrCpuLoadCounter = 0; + fCurCpuLoad = 0.0f; + #ifdef ENABLE_PYTHON //--------------------------------------------------- // Python bridge @@ -364,35 +383,35 @@ //--------------------------------------------------- // undo/redo //--------------------------------------------------- - + MusEGlobal::undoRedo = new QActionGroup(this); MusEGlobal::undoRedo->setExclusive(false); - MusEGlobal::undoAction = new QAction(QIcon(*MusEGui::undoIconS), tr("Und&o"), + MusEGlobal::undoAction = new QAction(QIcon(*MusEGui::undoIconS), tr("Und&o"), MusEGlobal::undoRedo); - MusEGlobal::redoAction = new QAction(QIcon(*MusEGui::redoIconS), tr("Re&do"), + MusEGlobal::redoAction = new QAction(QIcon(*MusEGui::redoIconS), tr("Re&do"), MusEGlobal::undoRedo); MusEGlobal::undoAction->setWhatsThis(tr("undo last change to song")); MusEGlobal::redoAction->setWhatsThis(tr("redo last undo")); MusEGlobal::undoAction->setEnabled(false); MusEGlobal::redoAction->setEnabled(false); - connect(MusEGlobal::redoAction, SIGNAL(activated()), MusEGlobal::song, SLOT(redo())); - connect(MusEGlobal::undoAction, SIGNAL(activated()), MusEGlobal::song, SLOT(undo())); + connect(MusEGlobal::redoAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(redo())); + connect(MusEGlobal::undoAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(undo())); //--------------------------------------------------- // Transport //--------------------------------------------------- - + MusEGlobal::transportAction = new QActionGroup(this); MusEGlobal::transportAction->setExclusive(false); - + MusEGlobal::loopAction = new QAction(QIcon(*MusEGui::loop1Icon), tr("Loop"), MusEGlobal::transportAction); MusEGlobal::loopAction->setCheckable(true); MusEGlobal::loopAction->setWhatsThis(tr("loop between left mark and right mark")); connect(MusEGlobal::loopAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setLoop(bool))); - + MusEGlobal::punchinAction = new QAction(QIcon(*MusEGui::punchin1Icon), tr("Punchin"), MusEGlobal::transportAction); MusEGlobal::punchinAction->setCheckable(true); @@ -411,25 +430,25 @@ tseparator->setSeparator(true); MusEGlobal::transportAction->addAction(tseparator); - MusEGlobal::startAction = new QAction(QIcon(*MusEGui::startIcon), + MusEGlobal::startAction = new QAction(*MusEGui::rewindToStartSVGIcon, tr("Start"), MusEGlobal::transportAction); MusEGlobal::startAction->setWhatsThis(tr("rewind to start position")); - connect(MusEGlobal::startAction, SIGNAL(activated()), MusEGlobal::song, SLOT(rewindStart())); + connect(MusEGlobal::startAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(rewindStart())); - MusEGlobal::rewindAction = new QAction(QIcon(*MusEGui::frewindIcon), + MusEGlobal::rewindAction = new QAction(*MusEGui::rewindSVGIcon, tr("Rewind"), MusEGlobal::transportAction); MusEGlobal::rewindAction->setWhatsThis(tr("rewind current position")); - connect(MusEGlobal::rewindAction, SIGNAL(activated()), MusEGlobal::song, SLOT(rewind())); + connect(MusEGlobal::rewindAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(rewind())); - MusEGlobal::forwardAction = new QAction(QIcon(*MusEGui::fforwardIcon), + MusEGlobal::forwardAction = new QAction(*MusEGui::fastForwardSVGIcon, tr("Forward"), MusEGlobal::transportAction); MusEGlobal::forwardAction->setWhatsThis(tr("move current position")); - connect(MusEGlobal::forwardAction, SIGNAL(activated()), MusEGlobal::song, SLOT(forward())); + connect(MusEGlobal::forwardAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(forward())); - MusEGlobal::stopAction = new QAction(QIcon(*MusEGui::stopIcon), + MusEGlobal::stopAction = new QAction(*MusEGui::stopSVGIcon, tr("Stop"), MusEGlobal::transportAction); MusEGlobal::stopAction->setCheckable(true); @@ -437,22 +456,28 @@ MusEGlobal::stopAction->setChecked(true); connect(MusEGlobal::stopAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setStop(bool))); - MusEGlobal::playAction = new QAction(QIcon(*MusEGui::playIcon), tr("Play"), MusEGlobal::transportAction); + MusEGlobal::playAction = new QAction(*MusEGui::playSVGIcon, + tr("Play") + " (" + shrtToStr(MusEGui::SHRT_PLAY_TOGGLE) + + ")
    "+ tr("Restart rec")+" (" + QKeySequence(MusEGui::shortcuts[MusEGui::SHRT_REC_RESTART].key).toString() + ")", + MusEGlobal::transportAction); MusEGlobal::playAction->setCheckable(true); MusEGlobal::playAction->setWhatsThis(tr("start sequencer play")); MusEGlobal::playAction->setChecked(false); connect(MusEGlobal::playAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setPlay(bool))); - MusEGlobal::recordAction = new QAction(QIcon(*MusEGui::recordIcon), tr("Record"), MusEGlobal::transportAction); + MusEGlobal::recordAction = new QAction(*MusEGui::recMasterSVGIcon, tr("Record"), MusEGlobal::transportAction); MusEGlobal::recordAction->setCheckable(true); MusEGlobal::recordAction->setWhatsThis(tr("to record press record and then play")); connect(MusEGlobal::recordAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setRecord(bool))); MusEGlobal::panicAction = new QAction(QIcon(*MusEGui::panicIcon), tr("Panic"), this); + QMenu* panicPopupMenu = new QMenu(this); + MusEGlobal::panicAction->setMenu(panicPopupMenu); + MusEGlobal::panicAction->setWhatsThis(tr("send note off to all midi channels")); - connect(MusEGlobal::panicAction, SIGNAL(activated()), MusEGlobal::song, SLOT(panic())); + connect(MusEGlobal::panicAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(panic())); MusEGlobal::metronomeAction = new QAction(QIcon(*MusEGui::metronomeIcon), tr("Metronome"), this); MusEGlobal::metronomeAction->setCheckable(true); @@ -464,11 +489,11 @@ //----Actions //-------- File Actions - fileNewAction = new QAction(QIcon(*MusEGui::filenewIcon), tr("&New"), this); + fileNewAction = new QAction(QIcon(*MusEGui::filenewIcon), tr("&New"), this); fileNewAction->setToolTip(tr("Create New Song")); fileNewAction->setWhatsThis(tr("Create New Song")); - fileOpenAction = new QAction(QIcon(*MusEGui::openIcon), tr("&Open"), this); + fileOpenAction = new QAction(QIcon(*MusEGui::openIcon), tr("&Open"), this); fileOpenAction->setToolTip(tr("Click this button to open a new song.
    " "You can also select the Open command from the File menu.")); @@ -477,7 +502,7 @@ openRecent = new QMenu(tr("Open &Recent"), this); - fileSaveAction = new QAction(QIcon(*MusEGui::saveIcon), tr("&Save"), this); + fileSaveAction = new QAction(QIcon(*MusEGui::saveIcon), tr("&Save"), this); fileSaveAction->setToolTip(tr("Click this button to save the song you are " "editing. You will be prompted for a file name.\n" @@ -492,7 +517,7 @@ fileExportMidiAction = new QAction(tr("Export Midifile"), this); fileImportPartAction = new QAction(tr("Import Part"), this); - fileImportWaveAction = new QAction(tr("Import Wave File"), this); + fileImportWaveAction = new QAction(tr("Import Audio File"), this); fileMoveWaveFiles = new QAction(tr("Find unused wave files"), this); quitAction = new QAction(tr("&Quit"), this); @@ -550,8 +575,8 @@ windowsTileAction = new QAction(tr("Tile"), this); windowsRowsAction = new QAction(tr("In rows"), this); windowsColumnsAction = new QAction(tr("In columns"), this); - - + + //-------- Settings Actions settingsGlobalAction = new QAction(QIcon(*MusEGui::settings_globalsettingsIcon), tr("Global Settings"), this); settingsShortcutsAction = new QAction(QIcon(*MusEGui::settings_configureshortcutsIcon), tr("Configure Shortcuts"), this); @@ -573,6 +598,8 @@ //-------- Help Actions helpManualAction = new QAction(tr("&Manual"), this); helpHomepageAction = new QAction(tr("&MusE Homepage"), this); + helpDidYouKnow = new QAction(tr("&Did you know?"), this); + helpReportAction = new QAction(tr("&Report Bug..."), this); helpAboutAction = new QAction(tr("&About MusE"), this); @@ -580,23 +607,23 @@ //---- Connections //-------- File connections - connect(fileNewAction, SIGNAL(activated()), SLOT(loadTemplate())); - connect(fileOpenAction, SIGNAL(activated()), SLOT(loadProject())); + connect(fileNewAction, SIGNAL(triggered()), SLOT(loadTemplate())); + connect(fileOpenAction, SIGNAL(triggered()), SLOT(loadProject())); connect(openRecent, SIGNAL(aboutToShow()), SLOT(openRecentMenu())); connect(openRecent, SIGNAL(triggered(QAction*)), SLOT(selectProject(QAction*))); - - connect(fileSaveAction, SIGNAL(activated()), SLOT(save())); - connect(fileSaveAsAction, SIGNAL(activated()), SLOT(saveAs())); - connect(fileImportMidiAction, SIGNAL(activated()), SLOT(importMidi())); - connect(fileExportMidiAction, SIGNAL(activated()), SLOT(exportMidi())); - connect(fileImportPartAction, SIGNAL(activated()), SLOT(importPart())); - - connect(fileImportWaveAction, SIGNAL(activated()), SLOT(importWave())); - connect(fileMoveWaveFiles, SIGNAL(activated()), SLOT(findUnusedWaveFiles())); - connect(quitAction, SIGNAL(activated()), SLOT(quitDoc())); + connect(fileSaveAction, SIGNAL(triggered()), SLOT(save())); + connect(fileSaveAsAction, SIGNAL(triggered()), SLOT(saveAs())); - connect(editSongInfoAction, SIGNAL(activated()), SLOT(startSongInfo())); + connect(fileImportMidiAction, SIGNAL(triggered()), SLOT(importMidi())); + connect(fileExportMidiAction, SIGNAL(triggered()), SLOT(exportMidi())); + connect(fileImportPartAction, SIGNAL(triggered()), SLOT(importPart())); + + connect(fileImportWaveAction, SIGNAL(triggered()), SLOT(importWave())); + connect(fileMoveWaveFiles, SIGNAL(triggered()), SLOT(findUnusedWaveFiles())); + connect(quitAction, SIGNAL(triggered()), SLOT(quitDoc())); + + connect(editSongInfoAction, SIGNAL(triggered()), SLOT(startSongInfo())); //-------- View connections connect(viewTransportAction, SIGNAL(toggled(bool)), SLOT(toggleTransport(bool))); @@ -609,10 +636,10 @@ connect(fullscreenAction, SIGNAL(toggled(bool)), SLOT(setFullscreen(bool))); //-------- Midi connections - connect(midiEditInstAction, SIGNAL(activated()), SLOT(startEditInstrument())); - connect(midiResetInstAction, SIGNAL(activated()), SLOT(resetMidiDevices())); - connect(midiInitInstActions, SIGNAL(activated()), SLOT(initMidiDevices())); - connect(midiLocalOffAction, SIGNAL(activated()), SLOT(localOff())); + connect(midiEditInstAction, SIGNAL(triggered()), SLOT(startEditInstrument())); + connect(midiResetInstAction, SIGNAL(triggered()), SLOT(resetMidiDevices())); + connect(midiInitInstActions, SIGNAL(triggered()), SLOT(initMidiDevices())); + connect(midiLocalOffAction, SIGNAL(triggered()), SLOT(localOff())); connect(midiTrpAction, SIGNAL(triggered()), midiPluginSignalMapper, SLOT(map())); connect(midiInputTrfAction, SIGNAL(triggered()), midiPluginSignalMapper, SLOT(map())); @@ -630,25 +657,25 @@ #endif connect(midiPluginSignalMapper, SIGNAL(mapped(int)), this, SLOT(startMidiInputPlugin(int))); - + //-------- Audio connections - connect(audioBounce2TrackAction, SIGNAL(activated()), SLOT(bounceToTrack())); - connect(audioBounce2FileAction, SIGNAL(activated()), SLOT(bounceToFile())); - connect(audioRestartAction, SIGNAL(activated()), SLOT(seqRestart())); + connect(audioBounce2TrackAction, SIGNAL(triggered()), SLOT(bounceToTrack())); + connect(audioBounce2FileAction, SIGNAL(triggered()), SLOT(bounceToFile())); + connect(audioRestartAction, SIGNAL(triggered()), SLOT(seqRestart())); //-------- Automation connections - connect(autoMixerAction, SIGNAL(activated()), SLOT(switchMixerAutomation())); - connect(autoSnapshotAction, SIGNAL(activated()), SLOT(takeAutomationSnapshot())); - connect(autoClearAction, SIGNAL(activated()), SLOT(clearAutomation())); + connect(autoMixerAction, SIGNAL(triggered()), SLOT(switchMixerAutomation())); + connect(autoSnapshotAction, SIGNAL(triggered()), SLOT(takeAutomationSnapshot())); + connect(autoClearAction, SIGNAL(triggered()), SLOT(clearAutomation())); //-------- Settings connections - connect(settingsGlobalAction, SIGNAL(activated()), SLOT(configGlobalSettings())); - connect(settingsShortcutsAction, SIGNAL(activated()), SLOT(configShortCuts())); - connect(settingsMetronomeAction, SIGNAL(activated()), SLOT(configMetronome())); - connect(settingsMidiSyncAction, SIGNAL(activated()), SLOT(configMidiSync())); - connect(settingsMidiIOAction, SIGNAL(activated()), SLOT(configMidiFile())); - connect(settingsAppearanceAction, SIGNAL(activated()), SLOT(configAppearance())); - connect(settingsMidiPortAction, SIGNAL(activated()), SLOT(configMidiPorts())); + connect(settingsGlobalAction, SIGNAL(triggered()), SLOT(configGlobalSettings())); + connect(settingsShortcutsAction, SIGNAL(triggered()), SLOT(configShortCuts())); + connect(settingsMetronomeAction, SIGNAL(triggered()), SLOT(configMetronome())); + connect(settingsMidiSyncAction, SIGNAL(triggered()), SLOT(configMidiSync())); + connect(settingsMidiIOAction, SIGNAL(triggered()), SLOT(configMidiFile())); + connect(settingsAppearanceAction, SIGNAL(triggered()), SLOT(configAppearance())); + connect(settingsMidiPortAction, SIGNAL(triggered()), SLOT(configMidiPorts())); connect(dontFollowAction, SIGNAL(triggered()), followSignalMapper, SLOT(map())); connect(followPageAction, SIGNAL(triggered()), followSignalMapper, SLOT(map())); @@ -661,71 +688,85 @@ connect(followSignalMapper, SIGNAL(mapped(int)), this, SLOT(cmd(int))); //-------- Help connections - connect(helpManualAction, SIGNAL(activated()), SLOT(startHelpBrowser())); - connect(helpHomepageAction, SIGNAL(activated()), SLOT(startHomepageBrowser())); - connect(helpReportAction, SIGNAL(activated()), SLOT(startBugBrowser())); - connect(helpAboutAction, SIGNAL(activated()), SLOT(about())); + connect(helpManualAction, SIGNAL(triggered()), SLOT(startHelpBrowser())); + connect(helpHomepageAction, SIGNAL(triggered()), SLOT(startHomepageBrowser())); + connect(helpReportAction, SIGNAL(triggered()), SLOT(startBugBrowser())); + connect(helpDidYouKnow, SIGNAL(triggered()), SLOT(showDidYouKnowDialog())); + connect(helpAboutAction, SIGNAL(triggered()), SLOT(about())); //-------------------------------------------------- // Toolbar //-------------------------------------------------- - + // when adding a toolbar to the main window, remember adding it to // either the requiredToolbars or optionalToolbars list! + // NOTICE: Please ensure that any tool bar object names here match the names + // assigned in the 'toolbar' creation section of TopWin::TopWin(), + // or any other TopWin class. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). - QToolBar* songpos_tb; - songpos_tb = addToolBar(tr("Song Position")); - songpos_tb->setObjectName("Song Position"); - songpos_tb->addWidget(new MusEGui::SongPosToolbarWidget(songpos_tb)); - songpos_tb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - songpos_tb->setContextMenuPolicy(Qt::PreventContextMenu); - - QToolBar* tempo_tb; - tempo_tb = addToolBar(tr("Tempo")); - tempo_tb->setObjectName("Tempo"); - MusEGui::TempoToolbarWidget* tempo_tb_widget = new MusEGui::TempoToolbarWidget(tempo_tb); - tempo_tb->addWidget(tempo_tb_widget); - - QToolBar* sig_tb; - sig_tb = addToolBar(tr("Signature")); - sig_tb->setObjectName("Signature"); - MusEGui::SigToolbarWidget* sig_tb_widget = new MusEGui::SigToolbarWidget(tempo_tb); - sig_tb->addWidget(sig_tb_widget); - tools = addToolBar(tr("File Buttons")); tools->setObjectName("File Buttons"); tools->addAction(fileNewAction); tools->addAction(fileOpenAction); tools->addAction(fileSaveAction); tools->addAction(QWhatsThis::createAction(this)); - + QToolBar* undoToolbar = addToolBar(tr("Undo/Redo")); - undoToolbar->setObjectName("Undo/Redo (global)"); + undoToolbar->setObjectName("Undo/Redo tools"); undoToolbar->addActions(MusEGlobal::undoRedo->actions()); - QToolBar* transportToolbar = addToolBar(tr("Transport")); - transportToolbar->setObjectName("Transport (global)"); - transportToolbar->addActions(MusEGlobal::transportAction->actions()); - QToolBar* panicToolbar = addToolBar(tr("Panic")); - panicToolbar->setObjectName("Panic (global)"); + panicToolbar->setObjectName("Panic tool"); panicToolbar->addAction(MusEGlobal::panicAction); QToolBar* metronomeToolbar = addToolBar(tr("Metronome")); - metronomeToolbar->setObjectName("Metronome"); + metronomeToolbar->setObjectName("Metronome tool"); metronomeToolbar->addAction(MusEGlobal::metronomeAction); + // Already has an object name. + cpuLoadToolbar = new CpuToolbar(tr("Cpu load"), this); + addToolBar(cpuLoadToolbar); + connect(cpuLoadToolbar, SIGNAL(resetClicked()), SLOT(resetXrunsCounter())); + + QToolBar* songpos_tb; + songpos_tb = addToolBar(tr("Song Position")); + songpos_tb->setObjectName("Song Position tool"); + songpos_tb->addWidget(new MusEGui::SongPosToolbarWidget(songpos_tb)); + songpos_tb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + songpos_tb->setContextMenuPolicy(Qt::PreventContextMenu); + + addToolBarBreak(); + + QToolBar* transportToolbar = addToolBar(tr("Transport")); + transportToolbar->setObjectName("Transport tool"); + transportToolbar->addActions(MusEGlobal::transportAction->actions()); + + // Already has an object name. + TempoToolbar* tempo_tb = new TempoToolbar(tr("Tempo"), this); + addToolBar(tempo_tb); + + // Already has an object name. + SigToolbar* sig_tb = new SigToolbar(tr("Signature"), this); + addToolBar(sig_tb); + requiredToolbars.push_back(tools); - optionalToolbars.push_back(songpos_tb); - optionalToolbars.push_back(sig_tb); - optionalToolbars.push_back(tempo_tb); + requiredToolbars.push_back(cpuLoadToolbar); optionalToolbars.push_back(undoToolbar); - optionalToolbars.push_back(transportToolbar); optionalToolbars.push_back(panicToolbar); optionalToolbars.push_back(metronomeToolbar); + optionalToolbars.push_back(songpos_tb); + optionalToolbars.push_back(NULL); // Toolbar break + optionalToolbars.push_back(transportToolbar); + optionalToolbars.push_back(tempo_tb); + optionalToolbars.push_back(sig_tb); - QSocketNotifier* ss = new QSocketNotifier(MusEGlobal::audio->getFromThreadFdr(), QSocketNotifier::Read, this); - connect(ss, SIGNAL(activated(int)), MusEGlobal::song, SLOT(seqSignal(int))); + + QSocketNotifier* ss = new QSocketNotifier(MusEGlobal::audio->getFromThreadFdr(), QSocketNotifier::Read, this); + connect(ss, SIGNAL(activated(int)), MusEGlobal::song, SLOT(seqSignal(int))); //--------------------------------------------------- // Popups @@ -785,7 +826,7 @@ menuView->addSeparator(); menuView->addAction(fullscreenAction); - + //------------------------------------------------------------- // popup Midi //------------------------------------------------------------- @@ -793,7 +834,7 @@ menu_functions = new QMenu(tr("&Midi"), this); menuBar()->addMenu(menu_functions); trailingMenus.push_back(menu_functions); - + MusEGlobal::song->populateScriptMenu(menuScriptPlugins, this); menu_functions->addMenu(menuScriptPlugins); menu_functions->addAction(midiEditInstAction); @@ -811,6 +852,10 @@ menu_functions->addAction(midiInitInstActions); menu_functions->addAction(midiLocalOffAction); + panicPopupMenu->addAction(midiResetInstAction); + panicPopupMenu->addAction(midiInitInstActions); + panicPopupMenu->addAction(midiLocalOffAction); + //------------------------------------------------------------- // popup Audio //------------------------------------------------------------- @@ -818,7 +863,7 @@ menu_audio = new QMenu(tr("&Audio"), this); menuBar()->addMenu(menu_audio); trailingMenus.push_back(menu_audio); - + menu_audio->addAction(audioBounce2TrackAction); menu_audio->addAction(audioBounce2FileAction); menu_audio->addSeparator(); @@ -832,7 +877,7 @@ menuAutomation = new QMenu(tr("A&utomation"), this); menuBar()->addMenu(menuAutomation); trailingMenus.push_back(menuAutomation); - + menuAutomation->addAction(autoMixerAction); menuAutomation->addSeparator(); menuAutomation->addAction(autoSnapshotAction); @@ -845,11 +890,11 @@ menuWindows = new QMenu(tr("&Windows"), this); menuBar()->addMenu(menuWindows); trailingMenus.push_back(menuWindows); - - menuWindows->addAction(windowsCascadeAction); - menuWindows->addAction(windowsTileAction); - menuWindows->addAction(windowsRowsAction); - menuWindows->addAction(windowsColumnsAction); + + menuWindows->addAction(windowsCascadeAction); + menuWindows->addAction(windowsTileAction); + menuWindows->addAction(windowsRowsAction); + menuWindows->addAction(windowsColumnsAction); //------------------------------------------------------------- // popup Settings @@ -858,7 +903,7 @@ menuSettings = new QMenu(tr("MusE Se&ttings"), this); menuBar()->addMenu(menuSettings); trailingMenus.push_back(menuSettings); - + menuSettings->addAction(settingsGlobalAction); menuSettings->addAction(settingsShortcutsAction); menuSettings->addMenu(follow); @@ -881,9 +926,10 @@ menu_help = new QMenu(tr("&Help"), this); menuBar()->addMenu(menu_help); trailingMenus.push_back(menu_help); - + menu_help->addAction(helpManualAction); menu_help->addAction(helpHomepageAction); + menu_help->addAction(helpDidYouKnow); menu_help->addSeparator(); menu_help->addAction(helpReportAction); menu_help->addSeparator(); @@ -895,16 +941,17 @@ // Central Widget //--------------------------------------------------- - + mdiArea=new QMdiArea(this); mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation); mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + mdiArea->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); setCentralWidget(mdiArea); - connect(windowsTileAction, SIGNAL(activated()), this, SLOT(tileSubWindows())); - connect(windowsRowsAction, SIGNAL(activated()), this, SLOT(arrangeSubWindowsRows())); - connect(windowsColumnsAction, SIGNAL(activated()), this, SLOT(arrangeSubWindowsColumns())); - connect(windowsCascadeAction, SIGNAL(activated()), mdiArea, SLOT(cascadeSubWindows())); + connect(windowsTileAction, SIGNAL(triggered()), this, SLOT(tileSubWindows())); + connect(windowsRowsAction, SIGNAL(triggered()), this, SLOT(arrangeSubWindowsRows())); + connect(windowsColumnsAction, SIGNAL(triggered()), this, SLOT(arrangeSubWindowsColumns())); + connect(windowsCascadeAction, SIGNAL(triggered()), mdiArea, SLOT(cascadeSubWindows())); arrangerView = new MusEGui::ArrangerView(this); @@ -912,48 +959,39 @@ toplevels.push_back(arrangerView); arrangerView->hide(); _arranger=arrangerView->getArranger(); - - connect(tempo_tb_widget, SIGNAL(returnPressed()), arrangerView, SLOT(focusCanvas())); - connect(tempo_tb_widget, SIGNAL(escapePressed()), arrangerView, SLOT(focusCanvas())); - connect(sig_tb_widget, SIGNAL(returnPressed()), arrangerView, SLOT(focusCanvas())); - connect(sig_tb_widget, SIGNAL(escapePressed()), arrangerView, SLOT(focusCanvas())); - + + connect(tempo_tb, SIGNAL(returnPressed()), arrangerView, SLOT(focusCanvas())); + connect(tempo_tb, SIGNAL(escapePressed()), arrangerView, SLOT(focusCanvas())); + connect(sig_tb, SIGNAL(returnPressed()), arrangerView, SLOT(focusCanvas())); + connect(sig_tb, SIGNAL(escapePressed()), arrangerView, SLOT(focusCanvas())); + //--------------------------------------------------- // read list of "Recent Projects" //--------------------------------------------------- QString prjPath(MusEGlobal::configPath); prjPath += QString("/projects"); - FILE* f = fopen(prjPath.toLatin1().constData(), "r"); - if (f == 0) { - perror("open projectfile"); - for (int i = 0; i < PROJECT_LIST_LEN; ++i) - projectList[i] = 0; - } - else { - for (int i = 0; i < PROJECT_LIST_LEN; ++i) { - char buffer[256]; - if (fgets(buffer, 256, f)) { - int n = strlen(buffer); - if (n && buffer[n-1] == '\n') - buffer[n-1] = 0; - projectList[i] = *buffer ? new QString(buffer) : 0; - } - else - break; - } - fclose(f); - } + QFile projFile(prjPath); + if (!projFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + perror("open projectfile"); + projectRecentList.clear(); + } + else + { + for (int i = 0; i < PROJECT_LIST_LEN; ++i) + { + if (projFile.atEnd()) { + break; + } + projectRecentList.append(projFile.readLine().simplified()); + } + } - arrangerView->populateAddTrack(); - arrangerView->updateShortcuts(); - transport = new MusEGui::Transport(this, "transport"); bigtime = 0; MusEGlobal::song->blockSignals(false); - - changeConfig(false); + QSettings settings("MusE", "MusE-qt"); restoreGeometry(settings.value("MusE/geometry").toByteArray()); @@ -970,6 +1008,41 @@ MusEGlobal::heartBeatTimer->start(1000/MusEGlobal::config.guiRefresh); } +void MusE::heartBeat() +{ + cpuLoadToolbar->setValues(MusEGlobal::song->cpuLoad(), + MusEGlobal::song->dspLoad(), + MusEGlobal::song->xRunsCount()); +} + +void MusE::populateAddTrack() +{ + arrangerView->populateAddTrack(); + arrangerView->updateShortcuts(); + +} + +void MusE::blinkTimerSlot() +{ + MusEGlobal::blinkTimerPhase = !MusEGlobal::blinkTimerPhase; +} + +void MusE::messagePollTimerSlot() +{ + if(MusEGlobal::song) + MusEGlobal::song->processIpcInEventBuffers(); +} + +//--------------------------------------------------------- +// setDirty +//--------------------------------------------------------- + +void MusE::setDirty() + { + MusEGlobal::song->dirty = true; + setWindowTitle(projectTitle(project.absoluteFilePath()) + " "); + } + //--------------------------------------------------- // loadDefaultSong // if no songname entered on command line: @@ -987,7 +1060,7 @@ name = argv[0]; else if (MusEGlobal::config.startMode == 0) { if (argc < 2) - name = projectList[0] ? *projectList[0] : MusEGui::getUniqueUntitledName(); + name = !projectRecentList.isEmpty() ? projectRecentList.first() : MusEGui::getUniqueUntitledName(); else name = argv[0]; printf("starting with selected song %s\n", MusEGlobal::config.startSong.toLatin1().constData()); @@ -1001,6 +1074,8 @@ else { name = MusEGlobal::config.startSong; + if (name == "default.med") + name = MusEGlobal::museGlobalShare + QString("/templates/default.med"); loadConfig = MusEGlobal::config.startSongLoadConfig; } useTemplate = true; @@ -1020,9 +1095,9 @@ } printf("starting with pre configured song %s\n", MusEGlobal::config.startSong.toLatin1().constData()); } - loadProjectFile(name, useTemplate, loadConfig); + loadProjectFile(name, useTemplate, loadConfig); } - + //--------------------------------------------------------- // resetDevices //--------------------------------------------------------- @@ -1038,9 +1113,9 @@ void MusE::initMidiDevices() { - //MusEGlobal::audio->msgIdle(true); + //MusEGlobal::audio->msgIdle(true); MusEGlobal::audio->msgInitMidiDevices(); - //MusEGlobal::audio->msgIdle(false); + //MusEGlobal::audio->msgIdle(false); } //--------------------------------------------------------- @@ -1092,11 +1167,11 @@ } } progress->setLabelText(label); - progress->setWindowModality(Qt::WindowModal); +// progress->setWindowModality(Qt::WindowModal); // REMOVE Tim. Persistent routes. Removed for version warning dialog to take priority. FIXME progress->setCancelButton(0); if (!songTemplate) progress->setMinimumDuration(0); // if we are loading a template it will probably be fast and we can wait before showing the dialog - + // // stop audio threads if running // @@ -1109,6 +1184,8 @@ qApp->processEvents(); } seqStop(); + // REMOVE Tim. Persistent routes. TESTING. + //MusEGlobal::audio->msgIdle(true); } microSleep(100000); progress->setValue(10); @@ -1117,6 +1194,9 @@ progress->setValue(90); if (restartSequencer) seqStart(); + // REMOVE Tim. Persistent routes. TESTING. + //MusEGlobal::audio->msgIdle(false); + //MusEGlobal::song->connectPorts(); arrangerView->updateVisibleTracksButtons(); progress->setValue(100); @@ -1127,7 +1207,7 @@ // Prompt and send init sequences. MusEGlobal::audio->msgInitMidiDevices(false); - + if (MusEGlobal::song->getSongInfo().length()>0 && MusEGlobal::song->showSongInfoOnStartup()) { startSongInfo(false); } @@ -1145,11 +1225,11 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool doReadMidiPorts) { if (mixer1) - mixer1->clear(); + mixer1->clearAndDelete(); if (mixer2) - mixer2->clear(); + mixer2->clearAndDelete(); _arranger->clear(); // clear track info - if (clearSong(doReadMidiPorts)) // Allow not touching things like midi ports. + if (clearSong(doReadMidiPorts)) // Allow not touching things like midi ports. return; progress->setValue(20); @@ -1161,21 +1241,21 @@ QApplication::restoreOverrideCursor(); return; } - project.setFile(MusEGui::getUniqueUntitledName()); + project.setFile(MusEGui::getUniqueUntitledName()); MusEGlobal::museProject = MusEGlobal::museProjectInitPath; QDir::setCurrent(QDir::homePath()); } else { - printf("Setting project path to %s\n", fi.absolutePath().toLatin1().constData()); + printf("Setting project path to %s\n", fi.absolutePath().toLocal8Bit().constData()); MusEGlobal::museProject = fi.absolutePath(); project.setFile(name); QDir::setCurrent(MusEGlobal::museProject); } QString ex = fi.completeSuffix().toLower(); - QString mex = ex.section('.', -1, -1); + QString mex = ex.section('.', -1, -1); if((mex == "gz") || (mex == "bz2")) - mex = ex.section('.', -2, -2); - + mex = ex.section('.', -2, -2); + if (ex.isEmpty() || mex == "med") { // // read *.med file @@ -1215,7 +1295,7 @@ } if (!songTemplate) { addProject(project.absoluteFilePath()); - setWindowTitle(QString("MusE: Song: ") + MusEGui::projectTitleFromFilename(project.absoluteFilePath())); + setWindowTitle(projectTitle(project.absoluteFilePath())); } MusEGlobal::song->dirty = false; progress->setValue(30); @@ -1230,14 +1310,14 @@ showBigtime(MusEGlobal::config.bigTimeVisible); showMixer1(MusEGlobal::config.mixer1Visible); showMixer2(MusEGlobal::config.mixer2Visible); - - // Added p3.3.43 Make sure the geometry is correct because showMixerX() will NOT + + // Added p3.3.43 Make sure the geometry is correct because showMixerX() will NOT // set the geometry if the mixer has already been created. if(mixer1) { //if(mixer1->geometry().size() != MusEGlobal::config.mixer1.geometry.size()) // Moved below // mixer1->resize(MusEGlobal::config.mixer1.geometry.size()); - + if(mixer1->geometry().topLeft() != MusEGlobal::config.mixer1.geometry.topLeft()) mixer1->move(MusEGlobal::config.mixer1.geometry.topLeft()); } @@ -1245,12 +1325,15 @@ { //if(mixer2->geometry().size() != MusEGlobal::config.mixer2.geometry.size()) // Moved below // mixer2->resize(MusEGlobal::config.mixer2.geometry.size()); - + if(mixer2->geometry().topLeft() != MusEGlobal::config.mixer2.geometry.topLeft()) mixer2->move(MusEGlobal::config.mixer2.geometry.topLeft()); } - - //showMarker(MusEGlobal::config.markerVisible); // Moved below. Tim. + +// REMOVE Tim. Removed. Already taken care of by settings. Reinstated! MDI window was +// not restoring on project reload. Didn't want to have to re-enable this, IIRC there +// was a problem with using this (interference with other similar competing settings), +// but here we go... Quick tested OK with normal and 'Borland/Mac' GUI modes. resize(MusEGlobal::config.geometryMain.size()); move(MusEGlobal::config.geometryMain.topLeft()); @@ -1258,14 +1341,16 @@ transport->show(); transport->move(MusEGlobal::config.geometryTransport.topLeft()); showTransport(MusEGlobal::config.transportVisible); - + progress->setValue(40); transport->setMasterFlag(MusEGlobal::song->masterFlag()); MusEGlobal::punchinAction->setChecked(MusEGlobal::song->punchin()); MusEGlobal::punchoutAction->setChecked(MusEGlobal::song->punchout()); MusEGlobal::loopAction->setChecked(MusEGlobal::song->loop()); - MusEGlobal::song->update(); + // Inform the rest of the app the song changed, with all flags MINUS + // these flags which are already sent in the call to MusE::read() above: + MusEGlobal::song->update(~SC_TRACK_INSERTED); MusEGlobal::song->updatePos(); arrangerView->clipboardChanged(); // enable/disable "Paste" arrangerView->selectionChanged(); // enable/disable "Copy" & "Paste" @@ -1277,37 +1362,16 @@ { if(mixer1->geometry().size() != MusEGlobal::config.mixer1.geometry.size()) mixer1->resize(MusEGlobal::config.mixer1.geometry.size()); - } + } if(mixer2) { if(mixer2->geometry().size() != MusEGlobal::config.mixer2.geometry.size()) mixer2->resize(MusEGlobal::config.mixer2.geometry.size()); - } - + } + // Moved here from above due to crash with a song loaded and then File->New. // Marker view list was not updated, had non-existent items from marker list (cleared in ::clear()). - showMarker(MusEGlobal::config.markerVisible); - - if (songTemplate) - { - // maximize the arranger when in traditional SDI mode - if (MusEGui::TopWin::_defaultSubwin[MusEGui::TopWin::ARRANGER]) - { - bool maximizeArranger=true; - for (int i=0; ishowMaximized(); - bringToFront(arrangerView); - } - } - } + showMarker(MusEGlobal::config.markerVisible); } //--------------------------------------------------------- @@ -1317,11 +1381,11 @@ void MusE::setUntitledProject() { setConfigDefaults(); - QString name(MusEGui::getUniqueUntitledName()); + QString name(MusEGui::getUniqueUntitledName()); MusEGlobal::museProject = MusEGlobal::museProjectInitPath; QDir::setCurrent(QDir::homePath()); project.setFile(name); - setWindowTitle(tr("MusE: Song: %1").arg(MusEGui::projectTitleFromFilename(name))); + setWindowTitle(projectTitle(name)); writeTopwinState=true; } @@ -1342,7 +1406,7 @@ void MusE::setFollow() { MusECore::Song::FollowMode fm = MusEGlobal::song->follow(); - + dontFollowAction->setChecked(fm == MusECore::Song::NO); followPageAction->setChecked(fm == MusECore::Song::JUMP); followCtsAction->setChecked(fm == MusECore::Song::CONTINUOUS); @@ -1385,7 +1449,7 @@ bool MusE::save() { - if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) + if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) return saveAs(); else return save(project.filePath(), false, writeTopwinState); @@ -1397,16 +1461,20 @@ bool MusE::save(const QString& name, bool overwriteWarn, bool writeTopwins) { - QString backupCommand; +// QString backupCommand; + QFile currentName(name); if (QFile::exists(name)) { - backupCommand.sprintf("cp \"%s\" \"%s.backup\"", name.toLatin1().constData(), name.toLatin1().constData()); + currentName.copy(name+".backup"); + //backupCommand.sprintf("cp \"%s\" \"%s.backup\"", name.toLatin1().constData(), name.toLatin1().constData()); } else if (QFile::exists(name + QString(".med"))) { - backupCommand.sprintf("cp \"%s.med\" \"%s.med.backup\"", name.toLatin1().constData(), name.toLatin1().constData()); + QString currentName2(name+".med"); + currentName.copy(name+".med.backup"); + //backupCommand.sprintf("cp \"%s.med\" \"%s.med.backup\"", name.toLatin1().constData(), name.toLatin1().constData()); } - if (!backupCommand.isEmpty()) - system(backupCommand.toLatin1().constData()); +// if (!backupCommand.isEmpty()) +// system(backupCommand.toLatin1().constData()); bool popenFlag; FILE* f = MusEGui::fileOpen(this, name, QString(".med"), "w", popenFlag, false, overwriteWarn); @@ -1416,7 +1484,7 @@ write(xml, writeTopwins); if (ferror(f)) { QString s = "Write File\n" + name + "\nfailed: " - + QString(strerror(errno)); + + QString(strerror(errno)); QMessageBox::critical(this, tr("MusE: Write File failed"), s); popenFlag? pclose(f) : fclose(f); @@ -1426,6 +1494,8 @@ else { popenFlag? pclose(f) : fclose(f); MusEGlobal::song->dirty = false; + setWindowTitle(projectTitle(project.absoluteFilePath())); + saveIncrement = 0; return true; } } @@ -1460,19 +1530,21 @@ "Save Current Project?"), tr("&Save"), tr("S&kip"), tr("&Cancel"), 0, 2); if (n == 0) { - if (!save()) // dont quit if save failed + if (!save()) // don't quit if save failed { + setRestartingApp(false); // Cancel any restart. event->ignore(); QApplication::restoreOverrideCursor(); return; - } + } } else if (n == 2) { + setRestartingApp(false); // Cancel any restart. event->ignore(); QApplication::restoreOverrideCursor(); return; - } + } } seqStop(); @@ -1486,29 +1558,35 @@ QSettings settings("MusE", "MusE-qt"); settings.setValue("MusE/geometry", saveGeometry()); - + writeGlobalConfiguration(); // save "Open Recent" list QString prjPath(MusEGlobal::configPath); prjPath += "/projects"; - FILE* f = fopen(prjPath.toLatin1().constData(), "w"); - if (f) { - for (int i = 0; i < PROJECT_LIST_LEN; ++i) { - fprintf(f, "%s\n", projectList[i] ? projectList[i]->toLatin1().constData() : ""); - } - fclose(f); - } + QFile f(prjPath); + f.open(QIODevice::WriteOnly | QIODevice::Text); + if (f.exists()) { + QTextStream out(&f); + for (int i = 0; i < projectRecentList.size(); ++i) { + out << projectRecentList[i] << "\n"; + } + } if(MusEGlobal::debugMsg) printf("MusE: Exiting JackAudio\n"); MusECore::exitJackAudio(); if(MusEGlobal::debugMsg) printf("MusE: Exiting DummyAudio\n"); MusECore::exitDummyAudio(); +#ifdef HAVE_RTAUDIO + if(MusEGlobal::debugMsg) + printf("MusE: Exiting RtAudio\n"); + MusECore::exitRtAudio(); +#endif if(MusEGlobal::debugMsg) printf("MusE: Exiting Metronome\n"); MusECore::exitMetronome(); - + MusEGlobal::song->cleanupForQuit(); // Give midi devices a chance to close first, above in cleanupForQuit. @@ -1526,9 +1604,9 @@ d.remove(filename); d.remove(f.completeBaseName() + ".wca"); } - + #ifdef HAVE_LASH - // Disconnect gracefully from LASH. + // Disconnect gracefully from LASH. if(lash_client) { if(MusEGlobal::debugMsg) @@ -1536,21 +1614,24 @@ lash_event_t* lashev = lash_event_new_with_type (LASH_Quit); lash_send_event(lash_client, lashev); } -#endif - +#endif + if(MusEGlobal::debugMsg) printf("MusE: Exiting Dsp\n"); AL::exitDsp(); - + if(MusEGlobal::debugMsg) printf("MusE: Exiting OSC\n"); MusECore::exitOSC(); - + delete MusEGlobal::audioPrefetch; delete MusEGlobal::audio; - delete MusEGlobal::midiSeq; + + // Destroy the sequencer object if it exists. + MusECore::exitMidiSequencer(); + delete MusEGlobal::song; - + if(MusEGlobal::debugMsg) printf("MusE: Deleting icons\n"); deleteIcons(); @@ -1558,7 +1639,7 @@ if(MusEGlobal::debugMsg) printf("MusE: Deleting all parentless dialogs and widgets\n"); deleteParentlessDialogs(); - + qApp->quit(); } @@ -1581,18 +1662,16 @@ markerView = new MusEGui::MarkerView(this); connect(markerView, SIGNAL(closed()), SLOT(markerClosed())); - // Nov 21, 2012 Hey this causes the thing not to open at all, EVER, on Lubuntu and some others! - //markerView->show(); // ??? REMOVE Tim. Superfluous? toplevels.push_back(markerView); } - if(markerView->isVisible() != flag) + if(markerView->isVisible() != flag) markerView->setVisible(flag); if(viewMarkerAction->isChecked() != flag) viewMarkerAction->setChecked(flag); // ??? TEST: Recursion? Does this call toggleMarker if called from menu? No. Why? It should. REMOVE Tim. Or keep. if (!flag) if (currentMenuSharingTopwin == markerView) setCurrentMenuSharingTopwin(NULL); - + updateWindowMenu(); } @@ -1615,13 +1694,13 @@ if ((*lit)->isVisible() && (*lit)->widget() != markerView) { if (MusEGlobal::debugMsg) - printf("bringing '%s' to front instead of closed marker window\n",(*lit)->widget()->windowTitle().toAscii().data()); + printf("bringing '%s' to front instead of closed marker window\n",(*lit)->widget()->windowTitle().toLatin1().data()); bringToFront((*lit)->widget()); - break; + break; } - + } //--------------------------------------------------------- @@ -1639,7 +1718,7 @@ void MusE::showArranger(bool flag) { - if(arrangerView->isVisible() != flag) + if(arrangerView->isVisible() != flag) arrangerView->setVisible(flag); if(viewArrangerAction->isChecked() != flag) viewArrangerAction->setChecked(flag); @@ -1655,7 +1734,7 @@ void MusE::arrangerClosed() { - if(viewArrangerAction->isChecked()) + if(viewArrangerAction->isChecked()) viewArrangerAction->setChecked(false); updateWindowMenu(); @@ -1665,13 +1744,13 @@ if ((*lit)->isVisible() && (*lit)->widget() != arrangerView) { if (MusEGlobal::debugMsg) - printf("bringing '%s' to front instead of closed arranger window\n",(*lit)->widget()->windowTitle().toAscii().data()); + printf("bringing '%s' to front instead of closed arranger window\n",(*lit)->widget()->windowTitle().toLatin1().data()); bringToFront((*lit)->widget()); - break; + break; } - + } //--------------------------------------------------------- @@ -1692,8 +1771,42 @@ if(transport->isVisible() != flag) transport->setVisible(flag); if(viewTransportAction->isChecked() != flag) - viewTransportAction->setChecked(flag); - } + viewTransportAction->setChecked(flag); +} + +float MusE::getCPULoad() +{ + struct rusage ru; + struct timespec curSysTime; + if(clock_gettime(CLOCK_REALTIME, &curSysTime) != 0) + { + return 0.0f; + } + //float fLoad = 0.0f; + if(getrusage(RUSAGE_SELF, &ru) != 0) + { + return 0.0f; + } + long msSysElapsed = (curSysTime.tv_nsec / 1000000L) + curSysTime.tv_sec * 1000L; + msSysElapsed -= (lastSysTime.tv_nsec / 1000000L) + lastSysTime.tv_sec * 1000L; + long msCpuElasped = (ru.ru_utime.tv_usec / 1000L) + ru.ru_utime.tv_sec * 1000L; + msCpuElasped -= (lastCpuTime.tv_usec / 1000L) + lastCpuTime.tv_sec * 1000L; + if(msSysElapsed > 0) + { + fAvrCpuLoad += (float)((double)msCpuElasped / (double)msSysElapsed); + avrCpuLoadCounter++; + } + lastCpuTime = ru.ru_utime; + lastSysTime = curSysTime; + if(avrCpuLoadCounter > 10) + { + fCurCpuLoad = (fAvrCpuLoad / (float)avrCpuLoadCounter) * 100.0f; + fAvrCpuLoad = 0.0f; + avrCpuLoadCounter = 0; + } + + return fCurCpuLoad; +} //--------------------------------------------------------- // saveAs @@ -1724,7 +1837,7 @@ QMessageBox::warning(this,"Path error","Can't create project path", QMessageBox::Ok); return false; } - + bool ok = false; if (!name.isEmpty()) { QString tempOldProj = MusEGlobal::museProject; @@ -1732,7 +1845,7 @@ ok = save(name, true, writeTopwinState); if (ok) { project.setFile(name); - setWindowTitle(tr("MusE: Song: %1").arg(MusEGui::projectTitleFromFilename(name))); + setWindowTitle(projectTitle(project.absoluteFilePath())); addProject(name); } else @@ -1765,7 +1878,7 @@ void MusE::startEditor(MusECore::Track* t) { switch (t->type()) { - case MusECore::Track::MIDI: startPianoroll(); break; + case MusECore::Track::MIDI: startPianoroll(); break; case MusECore::Track::DRUM: startDrumEditor(); break; case MusECore::Track::NEW_DRUM: startDrumEditor(); break; case MusECore::Track::WAVE: startWaveEditor(); break; @@ -1795,45 +1908,45 @@ void MusE::openInScoreEdit_oneStaffPerTrack(QWidget* dest) { - openInScoreEdit((MusEGui::ScoreEdit*)dest, false); + openInScoreEdit((MusEGui::ScoreEdit*)dest, false); } void MusE::openInScoreEdit_allInOne(QWidget* dest) { - openInScoreEdit((MusEGui::ScoreEdit*)dest, true); + openInScoreEdit((MusEGui::ScoreEdit*)dest, true); } void MusE::openInScoreEdit(MusEGui::ScoreEdit* destination, bool allInOne) { - MusECore::PartList* pl = getMidiPartsToEdit(); - if (pl == 0) - return; - openInScoreEdit(destination, pl, allInOne); + MusECore::PartList* pl = getMidiPartsToEdit(); + if (pl == 0) + return; + openInScoreEdit(destination, pl, allInOne); } void MusE::openInScoreEdit(MusEGui::ScoreEdit* destination, MusECore::PartList* pl, bool allInOne) { - if (destination==NULL) // if no destination given, create a new one - { + if (destination==NULL) // if no destination given, create a new one + { destination = new MusEGui::ScoreEdit(this, 0, _arranger->cursorValue()); toplevels.push_back(destination); destination->show(); connect(destination, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(destination, SIGNAL(name_changed()), arrangerView, SLOT(scoreNamingChanged())); //connect(muse, SIGNAL(configChanged()), destination, SLOT(config_changed())); - //commented out by flo, because the ScoreEditor connects to all + //commented out by flo, because the ScoreEditor connects to all //relevant signals on his own - + arrangerView->updateScoreMenus(); updateWindowMenu(); } - + destination->add_parts(pl, allInOne); } void MusE::startScoreQuickly() { - openInScoreEdit_oneStaffPerTrack(NULL); + openInScoreEdit_oneStaffPerTrack(NULL); } //--------------------------------------------------------- @@ -1850,9 +1963,9 @@ void MusE::startPianoroll(MusECore::PartList* pl, bool showDefaultCtrls) { - + MusEGui::PianoRoll* pianoroll = new MusEGui::PianoRoll(pl, this, 0, _arranger->cursorValue()); - if(showDefaultCtrls) + if(showDefaultCtrls) pianoroll->addCtrl(); toplevels.push_back(pianoroll); pianoroll->show(); @@ -1925,7 +2038,7 @@ void MusE::startDrumEditor(MusECore::PartList* pl, bool showDefaultCtrls) { MusEGui::DrumEdit* drumEditor = new MusEGui::DrumEdit(pl, this, 0, _arranger->cursorValue()); - if(showDefaultCtrls) + if(showDefaultCtrls) drumEditor->addCtrl(); toplevels.push_back(drumEditor); drumEditor->show(); @@ -1993,17 +2106,30 @@ return; } + // All tips are separated by an empty line. Lines starting with # are ignored + QString tipMessage = ""; while (!file.atEnd()) { - dyk.tipList.append(file.readLine()); + QString line = file.readLine(); + + if (!line.simplified().isEmpty() && line.at(0) != QChar('#')) + tipMessage.append(line); + + if (!tipMessage.isEmpty() && line.simplified().isEmpty()) { + dyk.tipList.append(tipMessage); + tipMessage=""; + } + } + if (!tipMessage.isEmpty()) { + dyk.tipList.append(tipMessage); } - //dyk.tipList.append(tr("To get started with MusE why don't you visit the tutorials at
    http://muse-sequencer.org/index.php/Support")); - //dyk.tipList.append(tr("MusE can act as a realtime audio mixer if you connect it to jack!")); + std::random_shuffle(dyk.tipList.begin(),dyk.tipList.end()); dyk.show(); if( dyk.exec()) { if (dyk.dontShowCheckBox->isChecked()) { MusEGlobal::config.showDidYouKnow=false; + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); // save settings } } @@ -2035,22 +2161,18 @@ //--------------------------------------------------------- void MusE::openRecentMenu() - { - openRecent->clear(); - for (int i = 0; i < PROJECT_LIST_LEN; ++i) { - if (projectList[i] == 0) - break; - QByteArray ba = projectList[i]->toLatin1(); - const char* path = ba.constData(); - const char* p = strrchr(path, '/'); - if (p == 0) - p = path; - else - ++p; - QAction *act = openRecent->addAction(QString(p)); - act->setData(i); - } - } +{ + openRecent->clear(); + for (int i = 0; i < projectRecentList.size(); ++i) + { + if (!QFileInfo(projectRecentList[i]).exists()) + continue; + + QString fileName = QFileInfo(projectRecentList[i]).fileName(); + QAction *act = openRecent->addAction(fileName); + act->setData(i); + } +} //--------------------------------------------------------- // selectProject @@ -2061,15 +2183,15 @@ if (!act) return; int id = act->data().toInt(); - if (!(id < PROJECT_LIST_LEN)) + if (id > projectRecentList.size()-1) { printf("THIS SHOULD NEVER HAPPEN: id(%i) < PROJECT_LIST_LEN(%i) in MusE::selectProject!\n",id, PROJECT_LIST_LEN); return; } - QString* name = projectList[id]; - if (name == 0) + QString name = projectRecentList[id]; + if (name == "") return; - loadProjectFile(*name, false, true); + loadProjectFile(name, false, true); } //--------------------------------------------------------- @@ -2080,7 +2202,9 @@ { for (MusEGui::iToplevel i = toplevels.begin(); i != toplevels.end(); ++i) { if (*i == tl) { - + + tl->storeInitialState(); + if (tl == activeTopWin) { activeTopWin=NULL; @@ -2092,28 +2216,28 @@ if ((*lit)->isVisible() && (*lit)->widget() != tl) { if (MusEGlobal::debugMsg) - printf("bringing '%s' to front instead of closed window\n",(*lit)->widget()->windowTitle().toAscii().data()); + printf("bringing '%s' to front instead of closed window\n",(*lit)->widget()->windowTitle().toLatin1().data()); bringToFront((*lit)->widget()); - break; + break; } } - + if (tl == currentMenuSharingTopwin) setCurrentMenuSharingTopwin(NULL); - - + + bool mustUpdateScoreMenus=false; switch(tl->type()) { case MusEGui::TopWin::MARKER: case MusEGui::TopWin::ARRANGER: break; case MusEGui::TopWin::CLIPLIST: - viewCliplistAction->setChecked(false); + viewCliplistAction->setChecked(false); if (currentMenuSharingTopwin == clipListEdit) setCurrentMenuSharingTopwin(NULL); - updateWindowMenu(); + updateWindowMenu(); return; // the following editors can exist in more than @@ -2127,11 +2251,11 @@ break; case MusEGui::TopWin::SCORE: mustUpdateScoreMenus=true; - + case MusEGui::TopWin::TOPLEVELTYPE_LAST_ENTRY: //to avoid a warning break; } - toplevels.erase(i); + toplevels.erase(i); if (mustUpdateScoreMenus) arrangerView->updateScoreMenus(); updateWindowMenu(); @@ -2150,6 +2274,12 @@ if (key == MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_METRO].key) { MusEGlobal::song->setClick(!MusEGlobal::song->click()); } + else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_RESTART].key) { + MusEGlobal::song->restartRecording(); + } + else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_RESTART_MULTI].key) { + MusEGlobal::song->restartRecording(false); + } else if (key == MusEGui::shortcuts[MusEGui::SHRT_PLAY_TOGGLE].key) { if (MusEGlobal::audio->isPlaying()) MusEGlobal::song->setStop(true); @@ -2172,18 +2302,18 @@ else if (key == MusEGui::shortcuts[MusEGui::SHRT_PLAY_SONG].key ) { MusEGlobal::song->setPlay(true); } - + // Normally each editor window handles these, to inc by the editor's raster snap value. // But users were asking for a global version - "they don't work when I'm in mixer or transport". // Since no editor claimed the key event, we don't know a specific editor's snap setting, // so adopt a policy where the arranger is the 'main' raster reference, I guess... else if (key == MusEGui::shortcuts[MusEGui::SHRT_POS_DEC].key) { int spos = MusEGlobal::song->cpos(); - if(spos > 0) + if(spos > 0) { spos -= 1; // Nudge by -1, then snap down with raster1. spos = AL::sigmap.raster1(spos, MusEGlobal::song->arrangerRaster()); - } + } if(spos < 0) spos = 0; MusECore::Pos p(spos,true); @@ -2209,7 +2339,7 @@ MusEGlobal::song->setPos(0, p, true, true, true); return; } - + else if (key == MusEGui::shortcuts[MusEGui::SHRT_GOTO_LEFT].key) { if (!MusEGlobal::song->record()) MusEGlobal::song->setPos(0, MusEGlobal::song->lPos()); @@ -2254,13 +2384,16 @@ else if (key == MusEGui::shortcuts[MusEGui::SHRT_CONFIG_SHORTCUTS].key) { configShortCuts(); } + else if (key == MusEGui::shortcuts[MusEGui::SHRT_PART_NORMALIZE].key) { + MusEGlobal::song->normalizeWaveParts(); + } else { if (MusEGlobal::debugMsg) printf("unknown kbAccel 0x%x\n", key); } } -#if 0 +#if 0 //--------------------------------------------------------- // catchSignal // only for debugging @@ -2271,7 +2404,7 @@ static void catchSignal(int sig) { if (MusEGlobal::debugMsg) - fprintf(stderr, "MusE: signal %d catched\n", sig); + fprintf(stderr, "MusE: signal %d caught\n", sig); if (sig == SIGSEGV) { fprintf(stderr, "MusE: segmentation fault\n"); abort(); @@ -2386,6 +2519,12 @@ delete midiTransformerDialog; midiTransformerDialog = 0; } + if(routeDialog) + { + delete routeDialog; + routeDialog = 0; + } + } //--------------------------------------------------------- @@ -2396,7 +2535,7 @@ { if (!appearance) // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). - appearance = new MusEGui::Appearance(_arranger); + appearance = new MusEGui::Appearance(_arranger, this); appearance->resetValues(); if(appearance->isVisible()) { appearance->raise(); @@ -2407,67 +2546,20 @@ } //--------------------------------------------------------- -// loadTheme -//--------------------------------------------------------- - -void MusE::loadTheme(const QString& s) - { - QStringList sl = QStyleFactory::keys(); - if (s.isEmpty() || sl.indexOf(s) == -1) { - if(MusEGlobal::debugMsg) - printf("Set style does not exist, setting default.\n"); - qApp->setStyle(Appearance::defaultStyle); - qApp->style()->setObjectName(Appearance::defaultStyle); - } - else if (qApp->style()->objectName() != s) - { - qApp->setStyle(s); - qApp->style()->setObjectName(s); - } - } - -//--------------------------------------------------------- -// loadStyleSheetFile -//--------------------------------------------------------- - -void MusE::loadStyleSheetFile(const QString& s) -{ - if(s.isEmpty()) - { - qApp->setStyleSheet(s); - return; - } - - QFile cf(s); - if (cf.open(QIODevice::ReadOnly)) { - QByteArray ss = cf.readAll(); - QString sheet(QString::fromUtf8(ss.data())); - qApp->setStyleSheet(sheet); - cf.close(); - } - else - printf("loading style sheet <%s> failed\n", qPrintable(s)); -} - -//--------------------------------------------------------- -// configChanged +// changeConfig // - called whenever configuration has changed // - when configuration has changed by user, call with // writeFlag=true to save configuration in ~/.MusE +// simple=true Don't bother with theme, style, +// and font etc. updates, just emit the configChanged signal. //--------------------------------------------------------- void MusE::changeConfig(bool writeFlag) { if (writeFlag) writeGlobalConfiguration(); - - loadTheme(MusEGlobal::config.style); - QApplication::setFont(MusEGlobal::config.fonts[0]); - if(!MusEGlobal::config.styleSheetFile.isEmpty()) - loadStyleSheetFile(MusEGlobal::config.styleSheetFile); - - emit configChanged(); updateConfiguration(); + emit configChanged(); } //--------------------------------------------------------- @@ -2477,16 +2569,23 @@ void MusE::configMetronome() { if (!metronomeConfig) + { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). metronomeConfig = new MusEGui::MetronomeConfig; + metronomeConfig->show(); + return; + } if(metronomeConfig->isVisible()) { metronomeConfig->raise(); metronomeConfig->activateWindow(); } else + { + metronomeConfig->updateValues(); metronomeConfig->show(); } + } //--------------------------------------------------------- @@ -2515,6 +2614,7 @@ void MusE::configShortCutsSaveConfig() { + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. changeConfig(true); } @@ -2526,9 +2626,9 @@ { if(MusEGlobal::audio->bounce()) return; - + MusEGlobal::song->bounceOutput = 0; - + if(MusEGlobal::song->waves()->empty()) { QMessageBox::critical(this, @@ -2537,7 +2637,7 @@ ); return; } - + MusECore::OutputList* ol = MusEGlobal::song->outputs(); if(ol->empty()) { @@ -2547,22 +2647,22 @@ ); return; } - + if(checkRegionNotNull()) return; - + MusECore::AudioOutput* out = 0; // If only one output, pick it, else pick the first selected. if(ol->size() == 1) out = ol->front(); else { - for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) + for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) { - MusECore::AudioOutput* o = *iao; - if(o->selected()) + MusECore::AudioOutput* o = *iao; + if(o->selected()) { - if(out) + if(out) { out = 0; break; @@ -2570,7 +2670,7 @@ out = o; } } - if(!out) + if(!out) { QMessageBox::critical(this, tr("MusE: Bounce to Track"), @@ -2579,11 +2679,11 @@ return; } } - + // search target track MusECore::TrackList* tl = MusEGlobal::song->tracks(); MusECore::WaveTrack* track = 0; - + for (MusECore::iTrack it = tl->begin(); it != tl->end(); ++it) { MusECore::Track* t = *it; if (t->selected()) { @@ -2592,18 +2692,18 @@ break; } if(t->type() == MusECore::Track::WAVE) - { + { if(track) { track = 0; break; } track = (MusECore::WaveTrack*)t; - } - - } + } + + } } - + if (track == 0) { if(ol->size() == 1) { QMessageBox::critical(this, @@ -2612,14 +2712,14 @@ ); return; } - else + else { QMessageBox::critical(this, tr("MusE: Bounce to Track"), tr("Select one target wave track,\nand one audio output track") ); return; - } + } } MusEGlobal::song->setPos(0,MusEGlobal::song->lPos(),0,true,true); @@ -2643,7 +2743,7 @@ MusEGlobal::song->bounceOutput = 0; if(!ao) { - MusECore::OutputList* ol = MusEGlobal::song->outputs(); + MusECore::OutputList* ol = MusEGlobal::song->outputs(); if(ol->empty()) { QMessageBox::critical(this, @@ -2657,12 +2757,12 @@ ao = ol->front(); else { - for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) + for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) { - MusECore::AudioOutput* o = *iao; - if(o->selected()) + MusECore::AudioOutput* o = *iao; + if(o->selected()) { - if(ao) + if(ao) { ao = 0; break; @@ -2679,14 +2779,14 @@ } } } - + if (checkRegionNotNull()) return; - + MusECore::SndFile* sf = MusECore::getSndFile(0, this); if (sf == 0) return; - + MusEGlobal::song->setPos(0,MusEGlobal::song->lPos(),0,true,true); MusEGlobal::song->bounceOutput = ao; ao->setRecFile(sf); @@ -2740,12 +2840,12 @@ { /* save file */ QString ss = QString(lash_event_get_string(event)) + QString("/lash-project-muse.med"); - int ok = save (ss.toAscii(), false, true); + int ok = save (ss.toLatin1(), false, true); if (ok) { - project.setFile(ss.toAscii()); + project.setFile(ss.toLatin1()); setWindowTitle(tr("MusE: Song: %1").arg(MusEGui::projectTitleFromFilename(project.absoluteFilePath()))); - addProject(ss.toAscii()); - MusEGlobal::museProject = QFileInfo(ss.toAscii()).absolutePath(); + addProject(ss.toLatin1()); + MusEGlobal::museProject = QFileInfo(ss.toLatin1()).absolutePath(); QDir::setCurrent(MusEGlobal::museProject); } lash_send_event (lash_client, event); @@ -2756,7 +2856,7 @@ { /* load file */ QString sr = QString(lash_event_get_string(event)) + QString("/lash-project-muse.med"); - loadProjectFile(sr.toAscii(), false, true); + loadProjectFile(sr.toLatin1(), false, true); lash_send_event (lash_client, event); } break; @@ -2834,22 +2934,22 @@ case MusEGui::TopWin::MASTER: case MusEGui::TopWin::WAVE: case MusEGui::TopWin::LMASTER: - { + { if(tl->isVisible()) // Don't keep trying to close, only if visible. - { + { if(!tl->close()) - printf("MusE::clearSong TopWin did not close!\n"); + printf("MusE::clearSong TopWin did not close!\n"); goto again; - } + } } case MusEGui::TopWin::TOPLEVELTYPE_LAST_ENTRY: //to avoid a warning break; } } - microSleep(100000); + microSleep(100000); _arranger->songIsClearing(); MusEGlobal::song->clear(true, clear_all); - microSleep(100000); + microSleep(100000); return false; } @@ -2857,7 +2957,7 @@ // startEditInstrument //--------------------------------------------------------- -void MusE::startEditInstrument(const QString& find_instrument, EditInstrument::TabType show_tab) +void MusE::startEditInstrument(const QString& find_instrument, EditInstrumentTabType show_tab) { if(editInstrument == 0) { @@ -2885,14 +2985,14 @@ void MusE::switchMixerAutomation() { - // Could be intensive, try idling instead of a single message. + // Could be intensive, try idling instead of a single message. MusEGlobal::audio->msgIdle(true); - + MusEGlobal::automation = ! MusEGlobal::automation; // Clear all pressed and touched and rec event lists. MusEGlobal::song->clearRecAutomation(true); - - // If going to OFF mode, need to update current 'manual' values from the automation values at this time... + + // If going to OFF mode, need to update current 'manual' values from the automation values at this time... if(!MusEGlobal::automation) { MusECore::TrackList* tracks = MusEGlobal::song->tracks(); @@ -2904,9 +3004,9 @@ track->controller()->updateCurValues(MusEGlobal::audio->curFramePos()); } } - + MusEGlobal::audio->msgIdle(false); - + autoMixerAction->setChecked(MusEGlobal::automation); } @@ -2919,20 +3019,20 @@ QMessageBox::StandardButton b = QMessageBox::warning(this, appName, tr("This will clear all automation data on\n all audio tracks!\nProceed?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); - + if(b != QMessageBox::Ok) return; - - // Could be intensive, try idling instead of a single message. + + // Could be intensive, try idling instead of a single message. MusEGlobal::audio->msgIdle(true); - + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) { if ((*i)->isMidiTrack()) continue; static_cast(*i)->controller()->clearAllAutomation(); } - + MusEGlobal::audio->msgIdle(false); } @@ -2946,30 +3046,30 @@ tr("This takes an automation snapshot of\n all controllers on all audio tracks,\n" " at the current position.\nProceed?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); - + if(b != QMessageBox::Ok) return; - - // Could be intensive, try idling instead of a single message. + + // Could be intensive, try idling instead of a single message. MusEGlobal::audio->msgIdle(true); - + int frame = MusEGlobal::audio->curFramePos(); MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) { if ((*i)->isMidiTrack()) continue; - MusECore::AudioTrack* track = static_cast(*i); + MusECore::AudioTrack* track = static_cast(*i); MusECore::CtrlListList* cll = track->controller(); - // Need to update current 'manual' values from the automation values at this time. + // Need to update current 'manual' values from the automation values at this time. if(track->automationType() != AUTO_OFF) // && track->automationType() != AUTO_WRITE) cll->updateCurValues(frame); - + for (MusECore::iCtrlList icl = cll->begin(); icl != cll->end(); ++icl) { double val = icl->second->curVal(); icl->second->add(frame, val); } } - + MusEGlobal::audio->msgIdle(false); } @@ -2991,11 +3091,13 @@ fileImportPartAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_IMPORT_PART].key); fileImportWaveAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_IMPORT_AUDIO].key); quitAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_QUIT].key); - + //menu_file->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_LOAD_TEMPLATE].key, menu_ids[CMD_LOAD_TEMPLATE]); // Not used. - MusEGlobal::undoAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_UNDO].key); - MusEGlobal::redoAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_REDO].key); + if(MusEGlobal::undoAction) + MusEGlobal::undoAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_UNDO].key); + if(MusEGlobal::redoAction) + MusEGlobal::redoAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_REDO].key); //editSongInfoAction has no acceleration @@ -3007,7 +3109,7 @@ //viewCliplistAction has no acceleration viewMarkerAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_MARKER].key); - + // midiEditInstAction does not have acceleration midiResetInstAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_RESET].key); midiInitInstActions->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_INIT].key); @@ -3040,10 +3142,10 @@ dontFollowAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FOLLOW_NO].key); followPageAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FOLLOW_JUMP].key); followCtsAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FOLLOW_CONTINUOUS].key); - + helpManualAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_HELP].key); fullscreenAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FULLSCREEN].key); - + //arrangerView->updateMusEGui::Shortcuts(); //commented out by flo: is done via signal } @@ -3093,7 +3195,7 @@ void MusE::showMixer1(bool on) { if (on && mixer1 == 0) { - mixer1 = new MusEGui::AudioMixerApp(this, &(MusEGlobal::config.mixer1)); + mixer1 = new MusEGui::AudioMixerApp(NULL, &(MusEGlobal::config.mixer1)); connect(mixer1, SIGNAL(closed()), SLOT(mixer1Closed())); mixer1->resize(MusEGlobal::config.mixer1.geometry.size()); mixer1->move(MusEGlobal::config.mixer1.geometry.topLeft()); @@ -3110,7 +3212,7 @@ void MusE::showMixer2(bool on) { if (on && mixer2 == 0) { - mixer2 = new MusEGui::AudioMixerApp(this, &(MusEGlobal::config.mixer2)); + mixer2 = new MusEGui::AudioMixerApp(NULL, &(MusEGlobal::config.mixer2)); connect(mixer2, SIGNAL(closed()), SLOT(mixer2Closed())); mixer2->resize(MusEGlobal::config.mixer2.geometry.size()); mixer2->move(MusEGlobal::config.mixer2.geometry.topLeft()); @@ -3168,7 +3270,7 @@ //--------------------------------------------------------- void MusE::execDeliveredScript(int id) { - MusEGlobal::song->executeScript(MusEGlobal::song->getScriptPath(id, true).toLatin1().constData(), MusEGlobal::song->getSelectedMidiParts(), 0, false); // TODO: get quant from arranger + MusEGlobal::song->executeScript(this, MusEGlobal::song->getScriptPath(id, true).toLatin1().constData(), MusEGlobal::song->getSelectedMidiParts(), 0, false); // TODO: get quant from arranger } //--------------------------------------------------------- @@ -3176,7 +3278,7 @@ //--------------------------------------------------------- void MusE::execUserScript(int id) { - MusEGlobal::song->executeScript(MusEGlobal::song->getScriptPath(id, false).toLatin1().constData(), MusEGlobal::song->getSelectedMidiParts(), 0, false); // TODO: get quant from arranger + MusEGlobal::song->executeScript(this, MusEGlobal::song->getScriptPath(id, false).toLatin1().constData(), MusEGlobal::song->getSelectedMidiParts(), 0, false); // TODO: get quant from arranger } //--------------------------------------------------------- @@ -3188,16 +3290,16 @@ unused.exec(); } -void MusE::focusChanged(QWidget* old, QWidget* now) +void MusE::focusChanged(QWidget* old, QWidget* now) { if(MusEGlobal::heavyDebugMsg) { - printf("\n"); - printf("focusChanged: old:%p now:%p activeWindow:%p\n", old, now, qApp->activeWindow()); - if(old) - printf(" old type: %s\n", typeid(*old).name()); - if(now) - printf(" now type: %s\n", typeid(*now).name()); + printf("\n"); + printf("focusChanged: old:%p now:%p activeWindow:%p\n", old, now, qApp->activeWindow()); + if(old) + printf(" old type: %s\n", typeid(*old).name()); + if(now) + printf(" now type: %s\n", typeid(*now).name()); if (dynamic_cast(now)!=0) { QWidget* tmp=dynamic_cast(now)->widget(); @@ -3206,15 +3308,18 @@ else printf(" subwin contains NULL\n"); } - if(qApp->activeWindow()) - printf(" activeWindow type: %s\n", typeid(*qApp->activeWindow()).name()); - printf("\n"); + if(qApp->activeWindow()) + { + const char *strTid = typeid(qApp->activeWindow()).name(); + printf(" activeWindow type: %s\n", strTid); + } + printf("\n"); } - + // NOTE: FYI: This is what is required if, for 'Smart Focus', we try simply calling clearFocus from each relevant control - // upon Return/Enter/Escape or whatever. + // upon Return/Enter/Escape or whatever. // It's nice to be able to do that, but this was crash-prone and I don't like it. Instead each relevant control connects - // signals to slots in the editors which set focus on the canvases AND activate their top windows. + // signals to slots in the editors which set focus on the canvases AND activate their top windows. // Who knows, this code might be needed in some way. Informational, please keep. Tim. // /* @@ -3227,12 +3332,12 @@ if(mw && mw->widget()->focusProxy()) // Did we set a focus proxy on the window? //mw->widget()->setFocus(); // Give focus to the window so proxy gets it. mw->widget()->focusProxy()->setFocus(); // Give focus directly to the proxy. - } + } } else if(!now) { - QWidget* aw = qApp->activeWindow(); + QWidget* aw = qApp->activeWindow(); if(aw) { if(aw == this) // Active top-level window is MusE? @@ -3243,29 +3348,37 @@ if(mw && mw->widget()->focusProxy()) // Did we set a focus proxy on the window? //mw->widget()->setFocus(); // Give focus to the window so proxy gets it. mw->widget()->focusProxy()->setFocus(); // Give focus directly to the proxy. - } + } } else // Active top-level window is outside the MusE mdi window. { if(aw->focusProxy()) // Did we set a focus proxy on the window? { - //aw->setFocus(); // Give focus to the window so proxy gets it. + //aw->setFocus(); // Give focus to the window so proxy gets it. aw->focusProxy()->setFocus(); // Give focus directly to the proxy. if(!aw->focusProxy()->isActiveWindow()) aw->focusProxy()->activateWindow(); } } - } + } } */ - + QWidget* ptr=now; if (activeTopWin) + { + if(MusEGlobal::heavyDebugMsg) + printf(" activeTopWin: %s\n", typeid(*activeTopWin).name()); activeTopWin->storeInitialState(); - + } + if (currentMenuSharingTopwin && (currentMenuSharingTopwin!=activeTopWin)) + { + if(MusEGlobal::heavyDebugMsg) + printf(" currentMenuSharingTopwin: %s\n", typeid(*currentMenuSharingTopwin).name()); currentMenuSharingTopwin->storeInitialState(); + } // if the activated widget is a QMdiSubWindow containing some TopWin if ( (dynamic_cast(ptr)!=0) && @@ -3286,18 +3399,18 @@ while (ptr) { - if (MusEGlobal::heavyDebugMsg) + if (MusEGlobal::heavyDebugMsg) printf("focusChanged: at widget %p with type %s\n",ptr, typeid(*ptr).name()); - + if ( (dynamic_cast(ptr)!=0) || // *ptr is a TopWin or a derived class (ptr==this) ) // the main window is selected break; ptr=dynamic_cast(ptr->parent()); //in the unlikely case that ptr is a QObject, this returns NULL, which stops the loop } - + MusEGui::TopWin* win=dynamic_cast(ptr); // ptr is either NULL, this or the pointer to a TopWin - + // if the main win or some deleting topwin is selected, // don't treat that as "none", but also don't handle it if (ptr!=this && (!win || !win->deleting()) ) @@ -3314,8 +3427,8 @@ void MusE::activeTopWinChangedSlot(MusEGui::TopWin* win) { - if (MusEGlobal::debugMsg) printf("ACTIVE TOPWIN CHANGED to '%s' (%p)\n", win ? win->windowTitle().toAscii().data() : "", win); - + if (MusEGlobal::debugMsg) printf("ACTIVE TOPWIN CHANGED to '%s' (%p)\n", win ? win->windowTitle().toLatin1().data() : "", win); + if ( (win && (win->isMdiWin()==false) && win->sharesToolsAndMenu()) && ( (mdiArea->currentSubWindow() != NULL) && (mdiArea->currentSubWindow()->isVisible()==true) ) ) { @@ -3328,7 +3441,7 @@ // simply focus anything in the main window except the mdi area. menuBar()->setFocus(Qt::MenuBarFocusReason); } - + if (win && (win->sharesToolsAndMenu())) setCurrentMenuSharingTopwin(win); } @@ -3339,47 +3452,115 @@ { if (win && (win->sharesToolsAndMenu()==false)) { - printf("WARNING: THIS SHOULD NEVER HAPPEN: MusE::setCurrentMenuSharingTopwin() called with a win which does not share (%s)! ignoring...\n", win->windowTitle().toAscii().data()); + printf("WARNING: THIS SHOULD NEVER HAPPEN: MusE::setCurrentMenuSharingTopwin() called with a win which does not share (%s)! ignoring...\n", win->windowTitle().toLatin1().data()); return; } - + if (win!=currentMenuSharingTopwin) { MusEGui::TopWin* previousMenuSharingTopwin = currentMenuSharingTopwin; currentMenuSharingTopwin = NULL; + + if (MusEGlobal::debugMsg) printf("MENU SHARING TOPWIN CHANGED to '%s' (%p)\n", win ? win->windowTitle().toLatin1().data() : "", win); + - if (MusEGlobal::debugMsg) printf("MENU SHARING TOPWIN CHANGED to '%s' (%p)\n", win ? win->windowTitle().toAscii().data() : "", win); + list add_toolbars; + if(win) + add_toolbars = win->toolbars(); // empty our toolbars if (previousMenuSharingTopwin) { - for (list::iterator it = foreignToolbars.begin(); it!=foreignToolbars.end(); it++) - if (*it) + list add_foreign_toolbars; + for (list::iterator it = foreignToolbars.begin(); it!=foreignToolbars.end(); ++it) + { + QToolBar* tb = *it; + if(tb) { - if (MusEGlobal::heavyDebugMsg) printf(" removing sharer's toolbar '%s'\n", (*it)->windowTitle().toAscii().data()); - removeToolBar(*it); // this does not delete *it, which is good - (*it)->setParent(NULL); + // Check for existing toolbar with same object name, and replace it. + bool found = false; + for(list::iterator i_atb = add_toolbars.begin(); i_atb!=add_toolbars.end(); ++i_atb) + { + QToolBar* atb = *i_atb; + if(atb) + { + if(tb->objectName() == atb->objectName()) + { + //tb->hide(); + + if(MusEGlobal::heavyDebugMsg) + printf(" inserting toolbar '%s'\n", atb->windowTitle().toLatin1().data()); + + found = true; + insertToolBar(tb, atb); + add_foreign_toolbars.push_back(atb); + add_toolbars.remove(atb); + atb->show(); + break; + } + } + } + + // Remove any toolbar break that may exist before the toolbar - unless there + // is a replacement is to be made, in which case leave the break intact. + if(!found && toolBarBreak(tb)) + { + if(MusEGlobal::heavyDebugMsg) + fprintf(stderr, " removing break before sharer's toolbar '%s'\n", tb->windowTitle().toLatin1().data()); + removeToolBarBreak(tb); + } + + + if(MusEGlobal::heavyDebugMsg) + printf(" removing sharer's toolbar '%s'\n", tb->windowTitle().toLatin1().data()); + removeToolBar(tb); // this does not delete *it, which is good + tb->setParent(NULL); } - - foreignToolbars.clear(); + } + + foreignToolbars = add_foreign_toolbars; + } else { - for (list::iterator it = optionalToolbars.begin(); it!=optionalToolbars.end(); it++) - if (*it) + for (list::iterator it = optionalToolbars.begin(); it!=optionalToolbars.end(); ++it) + { + QToolBar* tb = *it; + if(tb) { - if (MusEGlobal::heavyDebugMsg) printf(" removing optional toolbar '%s'\n", (*it)->windowTitle().toAscii().data()); - removeToolBar(*it); // this does not delete *it, which is good - (*it)->setParent(NULL); + // Check for existing toolbar with same object name, and replace it. + for(list::iterator i_atb = add_toolbars.begin(); i_atb!=add_toolbars.end(); ++i_atb) + { + QToolBar* atb = *i_atb; + if(atb) + { + if(tb->objectName() == atb->objectName()) + { + //tb->hide(); + + if(MusEGlobal::heavyDebugMsg) + printf(" inserting toolbar '%s'\n", atb->windowTitle().toLatin1().data()); + + insertToolBar(tb, atb); + foreignToolbars.push_back(atb); + add_toolbars.remove(atb); + atb->show(); + break; + } + } + } + + if (MusEGlobal::heavyDebugMsg) + printf(" removing optional toolbar '%s'\n", tb->windowTitle().toLatin1().data()); + removeToolBar(tb); // this does not delete *it, which is good + tb->setParent(NULL); } + } } - + //empty our menu menuBar()->clear(); - - - - + for (list::iterator it = leadingMenus.begin(); it!=leadingMenus.end(); it++) menuBar()->addMenu(*it); @@ -3388,19 +3569,16 @@ const QList& actions=win->menuBar()->actions(); for (QList::const_iterator it=actions.begin(); it!=actions.end(); it++) { - if (MusEGlobal::heavyDebugMsg) printf(" adding menu entry '%s'\n", (*it)->text().toAscii().data()); - + if (MusEGlobal::heavyDebugMsg) printf(" adding menu entry '%s'\n", (*it)->text().toLatin1().data()); + menuBar()->addAction(*it); } - - - const list& toolbars=win->toolbars(); - for (list::const_iterator it=toolbars.begin(); it!=toolbars.end(); it++) + for (list::const_iterator it=add_toolbars.begin(); it!=add_toolbars.end(); ++it) if (*it) { - if (MusEGlobal::heavyDebugMsg) printf(" adding toolbar '%s'\n", (*it)->windowTitle().toAscii().data()); - + if (MusEGlobal::heavyDebugMsg) printf(" adding toolbar '%s'\n", (*it)->windowTitle().toLatin1().data()); + addToolBar(*it); foreignToolbars.push_back(*it); (*it)->show(); @@ -3408,7 +3586,7 @@ else { if (MusEGlobal::heavyDebugMsg) printf(" adding toolbar break\n"); - + addToolBarBreak(); foreignToolbars.push_back(NULL); } @@ -3416,10 +3594,10 @@ for (list::iterator it = trailingMenus.begin(); it!=trailingMenus.end(); it++) menuBar()->addMenu(*it); - + currentMenuSharingTopwin=win; - + if (win) win->restoreMainwinState(); //restore toolbar positions in main window } @@ -3453,7 +3631,7 @@ { if (topwin==NULL) return; - + if (topwin == waitingForTopwin) { if (waitingForTopwin->deleting()) @@ -3480,18 +3658,18 @@ { bool sep; bool there_are_subwins=false; - + menuWindows->clear(); // frees memory automatically - + menuWindows->addAction(windowsCascadeAction); menuWindows->addAction(windowsTileAction); menuWindows->addAction(windowsRowsAction); menuWindows->addAction(windowsColumnsAction); - + sep=false; for (MusEGui::iToplevel it=toplevels.begin(); it!=toplevels.end(); it++) if (((*it)->isVisible() || (*it)->isVisibleTo(this)) && (*it)->isMdiWin()) - // the isVisibleTo check is neccessary because isVisible returns false if a + // the isVisibleTo check is necessary because isVisible returns false if a // MdiSubWin is actually visible, but the muse main window is hidden for some reason { if (!sep) @@ -3500,9 +3678,9 @@ sep=true; } QAction* temp=menuWindows->addAction((*it)->windowTitle()); - connect(temp, SIGNAL(activated()), windowsMapper, SLOT(map())); + connect(temp, SIGNAL(triggered()), windowsMapper, SLOT(map())); windowsMapper->setMapping(temp, static_cast(*it)); - + there_are_subwins=true; } @@ -3516,21 +3694,26 @@ sep=true; } QAction* temp=menuWindows->addAction((*it)->windowTitle()); - connect(temp, SIGNAL(activated()), windowsMapper, SLOT(map())); + connect(temp, SIGNAL(triggered()), windowsMapper, SLOT(map())); windowsMapper->setMapping(temp, static_cast(*it)); } - + windowsCascadeAction->setEnabled(there_are_subwins); windowsTileAction->setEnabled(there_are_subwins); windowsRowsAction->setEnabled(there_are_subwins); windowsColumnsAction->setEnabled(there_are_subwins); } +void MusE::resetXrunsCounter() +{ + MusEGlobal::audio->resetXruns(); +} + void MusE::bringToFront(QWidget* widget) { MusEGui::TopWin* win=dynamic_cast(widget); if (!win) return; - + if (win->isMdiWin()) { win->show(); @@ -3541,7 +3724,7 @@ win->activateWindow(); win->raise(); } - + activeTopWin=win; emit activeTopWinChanged(win); } @@ -3560,19 +3743,19 @@ { QList wins = mdiarea->subWindowList(); list result; - + // always put the arranger at the top of the list, if visible - + for (QList::iterator it=wins.begin(); it!=wins.end(); it++) if ((*it)->isVisible() && ((*it)->isMinimized()==false)) if (dynamic_cast((*it)->widget())->type()==MusEGui::TopWin::ARRANGER) result.push_back(*it); - + for (QList::iterator it=wins.begin(); it!=wins.end(); it++) if ((*it)->isVisible() && ((*it)->isMinimized()==false)) if (dynamic_cast((*it)->widget())->type()!=MusEGui::TopWin::ARRANGER) result.push_back(*it); - + return result; } @@ -3580,7 +3763,7 @@ { list wins=get_all_visible_subwins(mdiArea); int n=wins.size(); - + if (n==0) return; //else if (n==1) @@ -3592,19 +3775,19 @@ int x_add = (*wins.begin())->frameGeometry().width() - (*wins.begin())->geometry().width(); int y_add = (*wins.begin())->frameGeometry().height() - (*wins.begin())->geometry().height(); int width_per_win = width/n; - + if (x_add >= width_per_win) { printf("ERROR: tried to arrange subwins in columns, but there's too few space.\n"); return; } - + int i=0; for (list::iterator it=wins.begin(); it!=wins.end(); it++, i++) { int left = (float) width*i/n; int right = (float) width*(i+1.0)/n; - + (*it)->move(left,0); (*it)->resize(right-left-x_add, height-y_add); } @@ -3615,7 +3798,7 @@ { list wins=get_all_visible_subwins(mdiArea); int n=wins.size(); - + if (n==0) return; //else if (n==1) @@ -3627,30 +3810,30 @@ int x_add = (*wins.begin())->frameGeometry().width() - (*wins.begin())->geometry().width(); int y_add = (*wins.begin())->frameGeometry().height() - (*wins.begin())->geometry().height(); int height_per_win = height/n; - + if (y_add >= height_per_win) { printf("ERROR: tried to arrange subwins in rows, but there's too few space.\n"); return; } - + int i=0; for (list::iterator it=wins.begin(); it!=wins.end(); it++, i++) { int top = (float) height*i/n; int bottom = (float) height*(i+1.0)/n; - + (*it)->move(0,top); (*it)->resize(width-x_add, bottom-top-y_add); } - } + } } void MusE::tileSubWindows() { list wins=get_all_visible_subwins(mdiArea); int n=wins.size(); - + if (n==0) return; //else if (n==1) @@ -3660,20 +3843,20 @@ int nx,ny; nx=ceil(sqrt(n)); ny=ceil((double)n/nx); - + int width = mdiArea->width(); int height = mdiArea->height(); int x_add = (*wins.begin())->frameGeometry().width() - (*wins.begin())->geometry().width(); int y_add = (*wins.begin())->frameGeometry().height() - (*wins.begin())->geometry().height(); int height_per_win = height/ny; int width_per_win = height/nx; - + if ((x_add >= width_per_win) || (y_add >= height_per_win)) { printf("ERROR: tried to tile subwins, but there's too few space.\n"); return; } - + int i=0, j=0; for (list::iterator it=wins.begin(); it!=wins.end(); it++, i++) { @@ -3682,31 +3865,59 @@ i=0; j++; } - + int top = (float) height*j/ny; int bottom = (float) height*(j+1.0)/ny; int left = (float) width*i/nx; int right = (float) width*(i+1.0)/nx; - + (*it)->move(left,top); (*it)->resize(right-left-x_add, bottom-top-y_add); } } } +QString MusE::projectTitle(QString name) +{ + return tr("MusE: Song: ") + MusEGui::projectTitleFromFilename(name); +} + QString MusE::projectTitle() const -{ +{ return MusEGui::projectTitleFromFilename(project.fileName()); } -QString MusE::projectPath() const -{ - return MusEGui::projectPathFromFilename(project.absoluteFilePath()); +QString MusE::projectPath() const +{ + return MusEGui::projectPathFromFilename(project.absoluteFilePath()); } QString MusE::projectExtension() const { - return MusEGui::projectExtensionFromFilename(project.fileName()); + return MusEGui::projectExtensionFromFilename(project.fileName()); +} + +void MusE::saveTimerSlot() +{ + if (MusEGlobal::config.autoSave == false || + MusEGlobal::museProject == MusEGlobal::museProjectInitPath || + MusEGlobal::song->dirty == false) + { + //printf("conditions not met, ignore %d %d\n", MusEGlobal::config.autoSave, MusEGlobal::song->dirty); + return; + } + saveIncrement++; + if (saveIncrement > 4) { + // printf("five minutes passed %d %d\n", MusEGlobal::config.autoSave, MusEGlobal::song->dirty); + // time to see if we are allowed to save, if so. Do + if (MusEGlobal::audio->isPlaying() == false) { + printf("Performing autosave\n"); + save(project.filePath(), false, writeTopwinState); + } else + { + //printf("isPlaying, can't save\n"); + } + } } } //namespace MusEGui diff -Nru muse-2.1.2/muse/appearance.cpp muse-3.0.2+ds1/muse/appearance.cpp --- muse-2.1.2/muse/appearance.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/appearance.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: appearance.cpp,v 1.11.2.5 2009/11/14 03:37:48 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,6 +22,9 @@ // //========================================================= +#include +#include + #include #include #include @@ -33,6 +37,10 @@ #include #include #include +#include +#include +#include +#include #include "icons.h" #include "appearance.h" @@ -47,9 +55,11 @@ #include "conf.h" #include "gconfig.h" +// For debugging output: Uncomment the fprintf section. +#define DEBUG_APPEARANCE(dev, format, args...) // fprintf(dev, format, ##args); + namespace MusEGui { -QString Appearance::defaultStyle=""; int BG_ITEM_HEIGHT = 30; class BgPreviewWidget : public QWidget { @@ -94,27 +104,6 @@ }; //--------------------------------------------------------- -// IdListViewItem -//--------------------------------------------------------- - -class IdListViewItem : public QTreeWidgetItem { - int _id; - - public: - IdListViewItem(int id, QTreeWidgetItem* parent, QString s) - : QTreeWidgetItem(parent, QStringList(s)) - { - _id = id; - } - IdListViewItem(int id, QTreeWidget* parent, QString s) - : QTreeWidgetItem(parent, QStringList(s)) - { - _id = id; - } - int id() const { return _id; } - }; - -//--------------------------------------------------------- // Appearance //--------------------------------------------------------- @@ -122,10 +111,24 @@ : QDialog(parent, Qt::Window) { setupUi(this); + + itemList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(itemList, SIGNAL(customContextMenuRequested(QPoint)), SLOT(colorListCustomContextMenuReq(QPoint))); + arr = a; color = 0; + _colorDialog = 0; +// defaultConfig = new MusEGlobal::GlobalConfigValues; config = new MusEGlobal::GlobalConfigValues; + backupConfig = new MusEGlobal::GlobalConfigValues; + _configChangedTimer = new QTimer(this); + _configChangedTimer->setObjectName("configChangedTimer"); + _configChangedTimer->setTimerType(Qt::CoarseTimer); + _configChangedTimer->setSingleShot(true); + _configChangedTimer->setInterval(500); + connect(_configChangedTimer, SIGNAL(timeout()), SLOT(configChangeTimeOut())); + lastSelectedColorItem = 0; lastSelectedBgItem = 0; @@ -175,7 +178,7 @@ id = new IdListViewItem(0, aid, "PartColors"); for(int i = 0; i < NUM_PARTCOLORS; ++i) - new IdListViewItem(0x400 + i, id, MusEGlobal::config.partColorNames[i]); + new IdListViewItem(0x600 + i, id, MusEGlobal::config.partColorNames[i]); new IdListViewItem(0x41c, aid, "part canvas background"); new IdListViewItem(0x41f, aid, "Ruler background"); @@ -199,6 +202,7 @@ new IdListViewItem(0x419, id, "synth background"); new IdListViewItem(0x41a, id, "selected track background"); new IdListViewItem(0x41b, id, "selected track foreground"); + new IdListViewItem(0x42b, id, "section dividers"); // 0x41c - 0x420 is already used (see above) id = new IdListViewItem(0, itemList, "BigTime"); new IdListViewItem(0x100, id, "background"); @@ -210,6 +214,9 @@ new IdListViewItem(0x423, id, "controller graph background"); new IdListViewItem(0x421, id, "background"); new IdListViewItem(0x422, id, "drum list"); + new IdListViewItem(0x429, id, "raster beat"); + new IdListViewItem(0x42a, id, "raster bar"); + id = new IdListViewItem(0, itemList, "Wave Editor"); new IdListViewItem(0x300, id, "Background"); @@ -223,16 +230,36 @@ new IdListViewItem(0x500, id, "background"); new IdListViewItem(0x501, id, "midi label"); new IdListViewItem(0x502, id, "drum label"); - new IdListViewItem(0x509, id, "new drum label"); - new IdListViewItem(0x503, id, "wave label"); - new IdListViewItem(0x504, id, "audio output label"); - new IdListViewItem(0x505, id, "audio input label"); - new IdListViewItem(0x506, id, "group label"); - new IdListViewItem(0x507, id, "aux label"); - new IdListViewItem(0x508, id, "synth label"); - // 0x509 is already used (between 502 and 503) + new IdListViewItem(0x503, id, "new drum label"); + new IdListViewItem(0x504, id, "wave label"); + new IdListViewItem(0x505, id, "audio output label"); + new IdListViewItem(0x506, id, "audio input label"); + new IdListViewItem(0x507, id, "group label"); + new IdListViewItem(0x508, id, "aux label"); + new IdListViewItem(0x509, id, "synth label"); + + new IdListViewItem(0x50a, id, "Slider bar default"); + new IdListViewItem(0x50b, id, "Slider default"); + new IdListViewItem(0x50c, id, "Pan slider"); + new IdListViewItem(0x50d, id, "Gain slider"); + new IdListViewItem(0x50e, id, "Aux slider"); + new IdListViewItem(0x50f, id, "Audio volume"); + new IdListViewItem(0x510, id, "Midi volume"); + new IdListViewItem(0x511, id, "Audio controller default"); + new IdListViewItem(0x512, id, "Audio property default"); + new IdListViewItem(0x513, id, "Midi controller default"); + new IdListViewItem(0x514, id, "Midi property default"); + new IdListViewItem(0x515, id, "Midi patch readout"); + new IdListViewItem(0x516, id, "Audio meter primary"); + new IdListViewItem(0x517, id, "Midi meter primary"); + new IdListViewItem(0x518, id, "Rack item background"); colorNameLineEdit->setEnabled(false); + + connect(loadColorsButton, SIGNAL(clicked(bool)), SLOT(loadColors())); + connect(saveColorsButton, SIGNAL(clicked(bool)), SLOT(saveColors())); + connect(pickColorButton, SIGNAL(clicked(bool)), SLOT(chooseColorClicked())); + connect(colorwidget, SIGNAL(clicked()), SLOT(chooseColorClicked())); connect(colorNameLineEdit, SIGNAL(editingFinished()), SLOT(colorNameEditFinished())); connect(itemList, SIGNAL(itemSelectionChanged()), SLOT(colorItemSelectionChanged())); @@ -296,8 +323,8 @@ connect(fontBrowse5, SIGNAL(clicked()), SLOT(browseFont5())); connect(fontBrowse6, SIGNAL(clicked()), SLOT(browseFont6())); - connect(applyButton, SIGNAL(clicked()), SLOT(apply())); - connect(okButton, SIGNAL(clicked()), SLOT(ok())); + connect(applyButton, SIGNAL(clicked()), SLOT(applyClicked())); + connect(okButton, SIGNAL(clicked()), SLOT(okClicked())); connect(cancelButton, SIGNAL(clicked()), SLOT(cancel())); connect(addBgButton, SIGNAL(clicked()), SLOT(addBackground())); connect(removeBgButton, SIGNAL(clicked()), SLOT(removeBackground())); @@ -305,8 +332,155 @@ connect(partShowevents, SIGNAL(toggled(bool)), eventButtonGroup, SLOT(setEnabled(bool))); //updateColor(); + + } + +Appearance::~Appearance() + { + delete config; + delete backupConfig; } +QColor* Appearance::globalConfigColorFromId(int id) const +{ + if(id == 0) + return 0; + + if(id >= 0x600 && id < (0x600 + NUM_PARTCOLORS)) + return &MusEGlobal::config.partColors[id & 0xff]; + else + { + switch(id) + { + case 0x100: return &MusEGlobal::config.bigTimeBackgroundColor; break; + case 0x101: return &MusEGlobal::config.bigTimeForegroundColor; break; + case 0x200: return &MusEGlobal::config.transportHandleColor; break; + case 0x300: return &MusEGlobal::config.waveEditBackgroundColor; break; + case 0x301: return &MusEGlobal::config.wavePeakColor; break; + case 0x302: return &MusEGlobal::config.waveRmsColor; break; + case 0x303: return &MusEGlobal::config.wavePeakColorSelected; break; + case 0x304: return &MusEGlobal::config.waveRmsColorSelected; break; + case 0x305: return &MusEGlobal::config.waveNonselectedPart; break; + + case 0x411: return &MusEGlobal::config.trackBg; break; + case 0x412: return &MusEGlobal::config.midiTrackBg; break; + case 0x413: return &MusEGlobal::config.drumTrackBg; break; + case 0x41e: return &MusEGlobal::config.newDrumTrackBg;break; + case 0x414: return &MusEGlobal::config.waveTrackBg; break; + case 0x415: return &MusEGlobal::config.outputTrackBg; break; + case 0x416: return &MusEGlobal::config.inputTrackBg; break; + case 0x417: return &MusEGlobal::config.groupTrackBg; break; + case 0x418: return &MusEGlobal::config.auxTrackBg; break; + case 0x419: return &MusEGlobal::config.synthTrackBg; break; + case 0x41a: return &MusEGlobal::config.selectTrackBg; break; + case 0x41b: return &MusEGlobal::config.selectTrackFg; break; + + case 0x41c: return &MusEGlobal::config.partCanvasBg; break; + case 0x41d: return &MusEGlobal::config.ctrlGraphFg; break; + + // 0x41e is already used (between 413 and 414) + + case 0x41f: return &MusEGlobal::config.rulerBg; break; + case 0x420: return &MusEGlobal::config.rulerFg; break; + case 0x421: return &MusEGlobal::config.midiCanvasBg; break; + case 0x422: return &MusEGlobal::config.drumListBg; break; + case 0x423: return &MusEGlobal::config.midiControllerViewBg; break; + case 0x424: return &MusEGlobal::config.rulerCurrent; break; + case 0x425: return &MusEGlobal::config.partWaveColorPeak; break; + case 0x426: return &MusEGlobal::config.partWaveColorRms; break; + case 0x427: return &MusEGlobal::config.partMidiDarkEventColor; break; + case 0x428: return &MusEGlobal::config.partMidiLightEventColor; break; + case 0x429: return &MusEGlobal::config.midiCanvasBeatColor; break; + case 0x42a: return &MusEGlobal::config.midiCanvasBarColor; break; + case 0x42b: return &MusEGlobal::config.trackSectionDividerColor; break; + + + + case 0x500: return &MusEGlobal::config.mixerBg; break; + case 0x501: return &MusEGlobal::config.midiTrackLabelBg; break; + case 0x502: return &MusEGlobal::config.drumTrackLabelBg; break; + case 0x503: return &MusEGlobal::config.newDrumTrackLabelBg;break; + case 0x504: return &MusEGlobal::config.waveTrackLabelBg; break; + case 0x505: return &MusEGlobal::config.outputTrackLabelBg; break; + case 0x506: return &MusEGlobal::config.inputTrackLabelBg; break; + case 0x507: return &MusEGlobal::config.groupTrackLabelBg; break; + case 0x508: return &MusEGlobal::config.auxTrackLabelBg; break; + case 0x509: return &MusEGlobal::config.synthTrackLabelBg; break; + + case 0x50a: return &MusEGlobal::config.sliderBarDefaultColor; break; + case 0x50b: return &MusEGlobal::config.sliderDefaultColor; break; + case 0x50c: return &MusEGlobal::config.panSliderColor; break; + case 0x50d: return &MusEGlobal::config.gainSliderColor; break; + case 0x50e: return &MusEGlobal::config.auxSliderColor; break; + case 0x50f: return &MusEGlobal::config.audioVolumeSliderColor; break; + case 0x510: return &MusEGlobal::config.midiVolumeSliderColor; break; + case 0x511: return &MusEGlobal::config.audioControllerSliderDefaultColor; break; + case 0x512: return &MusEGlobal::config.audioPropertySliderDefaultColor; break; + case 0x513: return &MusEGlobal::config.midiControllerSliderDefaultColor; break; + case 0x514: return &MusEGlobal::config.midiPropertySliderDefaultColor; break; + case 0x515: return &MusEGlobal::config.midiPatchReadoutColor; break; + case 0x516: return &MusEGlobal::config.audioMeterPrimaryColor; break; + case 0x517: return &MusEGlobal::config.midiMeterPrimaryColor; break; + case 0x518: return &MusEGlobal::config.rackItemBackgroundColor; break; + + default: + return 0; + break; + } + } + return 0; +} + +long int Appearance::configOffsetFromColorId(int id) const +{ + QColor* c = globalConfigColorFromId(id); + if(!c) + return -1; + + // Memory pointer HACK. + return ((const char*)c) - ((const char*)&MusEGlobal::config); +} + +QColor* Appearance::workingConfigColorFromId(int id) const +{ + long int itemOffset = configOffsetFromColorId(id); + if(itemOffset == -1) + return 0; + return (QColor*)(((const char*)config) + itemOffset); +} + +QColor* Appearance::backupConfigColorFromId(int id) const +{ + long int itemOffset = configOffsetFromColorId(id); + if(itemOffset == -1) + return 0; + return (QColor*)(((const char*)backupConfig) + itemOffset); +} + +//--------------------------------------------------------- +// setConfigurationColors +//--------------------------------------------------------- + +void Appearance::setConfigurationColors() +{ + palette0->setStyleSheet(QString("background-color: ") + config->palette[0].name()); + palette1->setStyleSheet(QString("background-color: ") + config->palette[1].name()); + palette2->setStyleSheet(QString("background-color: ") + config->palette[2].name()); + palette3->setStyleSheet(QString("background-color: ") + config->palette[3].name()); + palette4->setStyleSheet(QString("background-color: ") + config->palette[4].name()); + palette5->setStyleSheet(QString("background-color: ") + config->palette[5].name()); + palette6->setStyleSheet(QString("background-color: ") + config->palette[6].name()); + palette7->setStyleSheet(QString("background-color: ") + config->palette[7].name()); + palette8->setStyleSheet(QString("background-color: ") + config->palette[8].name()); + palette9->setStyleSheet(QString("background-color: ") + config->palette[9].name()); + palette10->setStyleSheet(QString("background-color: ") + config->palette[10].name()); + palette11->setStyleSheet(QString("background-color: ") + config->palette[11].name()); + palette12->setStyleSheet(QString("background-color: ") + config->palette[12].name()); + palette13->setStyleSheet(QString("background-color: ") + config->palette[13].name()); + palette14->setStyleSheet(QString("background-color: ") + config->palette[14].name()); + palette15->setStyleSheet(QString("background-color: ") + config->palette[15].name()); +} + //--------------------------------------------------------- // resetValues //--------------------------------------------------------- @@ -314,31 +488,20 @@ void Appearance::resetValues() { *config = MusEGlobal::config; // init with global config values + *backupConfig = MusEGlobal::config; // init with global config values styleSheetPath->setText(config->styleSheetFile); updateFonts(); - QPalette pal; - - palette0->setStyleSheet(QString("background-color: ") + config->palette[0].name()); - palette1->setStyleSheet(QString("background-color: ") + config->palette[1].name()); - palette2->setStyleSheet(QString("background-color: ") + config->palette[2].name()); - palette3->setStyleSheet(QString("background-color: ") + config->palette[3].name()); - palette4->setStyleSheet(QString("background-color: ") + config->palette[4].name()); - palette5->setStyleSheet(QString("background-color: ") + config->palette[5].name()); - palette6->setStyleSheet(QString("background-color: ") + config->palette[6].name()); - palette7->setStyleSheet(QString("background-color: ") + config->palette[7].name()); - palette8->setStyleSheet(QString("background-color: ") + config->palette[8].name()); - palette9->setStyleSheet(QString("background-color: ") + config->palette[9].name()); - palette10->setStyleSheet(QString("background-color: ") + config->palette[10].name()); - palette11->setStyleSheet(QString("background-color: ") + config->palette[11].name()); - palette12->setStyleSheet(QString("background-color: ") + config->palette[12].name()); - palette13->setStyleSheet(QString("background-color: ") + config->palette[13].name()); - palette14->setStyleSheet(QString("background-color: ") + config->palette[14].name()); - palette15->setStyleSheet(QString("background-color: ") + config->palette[15].name()); + setConfigurationColors(); global_bg->takeChildren(); user_bg->takeChildren(); + if (config->waveDrawing == MusEGlobal::WaveRmsPeak) + radioButtonDrawRmsPeak->setChecked(true); + else + radioButtonDrawOutline->setChecked(true); + QDir bgdir = MusEGlobal::museGlobalShare + "/wallpapers/"; QStringList filters; filters << "*.jpg" << "*.jpeg" << "*.png" << "*.gif"; @@ -412,9 +575,25 @@ globalAlphaSlider->blockSignals(false); globalAlphaVal->blockSignals(false); - updateColor(); + maxAliasedPointSize->blockSignals(true); + maxAliasedPointSize->setValue(config->maxAliasedPointSize); + maxAliasedPointSize->blockSignals(false); - } + // Grab all the colours. + updateColorItems(); + + updateColor(); +} + +QString &Appearance::getSetDefaultStyle(const QString *newStyle) +{ + static QString defaultStyle = ""; + if(newStyle != NULL) + { + defaultStyle = *newStyle; + } + return defaultStyle; +} //--------------------------------------------------------- @@ -441,15 +620,6 @@ } //--------------------------------------------------------- -// Appearance -//--------------------------------------------------------- - -Appearance::~Appearance() - { - delete config; - } - -//--------------------------------------------------------- // updateFonts //--------------------------------------------------------- @@ -502,8 +672,9 @@ return; } if(QMessageBox::question(MusEGlobal::muse, QString("Muse"), - tr("Do you really want to reset colors to theme default?"), tr("&Ok"), tr("&Cancel"), - QString::null, 0, 1 ) == 1) + tr("Do you really want to reset colors to theme default?"), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) + != QMessageBox::Ok) { return; } @@ -514,33 +685,41 @@ QString themeDir = MusEGlobal::museGlobalShare + "/themes/"; backgroundTree->reset(); + + QString configPath = themeDir + currentTheme; if (QFile::exists(themeDir + QFileInfo(currentTheme).baseName()+ ".qss")) { styleSheetPath->setText(themeDir + QFileInfo(currentTheme).baseName()+ ".qss"); MusEGlobal::config.styleSheetFile = styleSheetPath->text(); + if (MusEGlobal::debugMsg) + printf("Setting config.styleSheetFile to %s\n", config->styleSheetFile.toLatin1().data()); + + MusECore::readConfiguration(configPath.toLatin1().constData()); + // We want the simple version, don't set the style or stylesheet yet. + MusEGlobal::muse->changeConfig(true); } else { - styleSheetPath->setText("arg"); - MusEGlobal::muse->loadStyleSheetFile(""); MusEGlobal::config.styleSheetFile = ""; + // We want the simple version, don't set the style or stylesheet yet. + MusEGlobal::muse->changeConfig(true); + MusECore::readConfiguration(configPath.toLatin1().constData()); } - QString configPath = themeDir + currentTheme; - MusECore::readConfiguration(configPath.toLatin1().constData()); - colorSchemeComboBox->setCurrentIndex(0); - MusEGlobal::muse->changeConfig(true); + hide(); - close(); + // If required, ask user to restart the application for proper results. + checkClose(); } + //--------------------------------------------------------- // apply //--------------------------------------------------------- -void Appearance::apply() - { -// -// +bool Appearance::apply() +{ + bool restart_required = false; + int showPartEvent = 0; int showPartType = 0; @@ -581,7 +760,11 @@ for (int i = 0; i < user_bg->childCount(); ++i) config->canvasCustomBgList << user_bg->child(i)->data(0, Qt::UserRole).toString(); - config->styleSheetFile = styleSheetPath->text(); + if(config->styleSheetFile != styleSheetPath->text()) + { + restart_required = true; + config->styleSheetFile = styleSheetPath->text(); + } config->fonts[0].setFamily(fontName0->text()); @@ -620,18 +803,69 @@ config->fonts[6].setItalic(italic6->isChecked()); config->fonts[6].setBold(bold6->isChecked()); - config->style = themeComboBox->currentIndex() == 0 ? QString() : themeComboBox->currentText(); + if(config->style != (themeComboBox->currentIndex() == 0 ? QString() : themeComboBox->currentText())) + { + restart_required = true; + config->style = themeComboBox->currentIndex() == 0 ? QString() : themeComboBox->currentText(); + } + // setting up a new theme might change the fontsize, so re-read fontSize0->setValue(QApplication::font().pointSize()); - config->canvasShowGrid = arrGrid->isChecked(); - config->globalAlphaBlend = globalAlphaVal->value(); - // set colors... + config->maxAliasedPointSize = maxAliasedPointSize->value(); + + if (radioButtonDrawOutline->isChecked()) + config->waveDrawing = MusEGlobal::WaveOutLine; + else + config->waveDrawing = MusEGlobal::WaveRmsPeak; + MusEGlobal::config = *config; + *backupConfig = *config; + updateColorItems(); + // We want the simple version, don't set the style or stylesheet yet. MusEGlobal::muse->changeConfig(true); - } + raise(); + + return restart_required; +} + +//--------------------------------------------------------- +// checkClose +//--------------------------------------------------------- + +bool Appearance::checkClose() +{ + if(QMessageBox::warning(MusEGlobal::muse, QString("Muse"), + tr("Style was changed.\nThe program must be restarted for changes to take place.\nRestart now?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) + == QMessageBox::Yes) + { + MusEGlobal::muse->setRestartingApp(true); + if(MusEGlobal::muse->close()) + return true; + } + + // We want the non-simple version, set the style and stylesheet, and don't save - it's already been done. + // And force the style. + MusEGlobal::muse->changeConfig(false); +// MusEGlobal::muse->updateThemeAndStyle(true); + + MusEGlobal::muse->setRestartingApp(false); // Cancel any restart. Also cleared in muse->closeEvent(). + return false; +} + +//--------------------------------------------------------- +// applyClicked +//--------------------------------------------------------- + +void Appearance::applyClicked() +{ + if(apply()) + // If required, ask user to restart the application for proper results. + checkClose(); +} //--------------------------------------------------------- // colorNameEditFinished @@ -650,20 +884,359 @@ QString etxt = colorNameLineEdit->text(); QString txt = item->text(0); // We only support part color names, for now. - if(id >= 0x400 && id < (0x400 + NUM_PARTCOLORS)) + if(id >= 0x600 && id < (0x600 + NUM_PARTCOLORS)) config->partColorNames[id & 0xff] = etxt; if(etxt != txt) item->setText(0, etxt); } +void Appearance::loadColors() +{ + if(!MusEGlobal::muse->loadConfigurationColors(this)) + { + DEBUG_APPEARANCE(stderr, "Appearance::loadColors failed\n"); + return; + } + resetValues(); +} + +void Appearance::saveColors() +{ + MusEGlobal::muse->saveConfigurationColors(this); +} + +bool Appearance::isColorDirty(IdListViewItem* item) const +{ + if(!item) + return false; + int id = item->id(); + if(id == 0) + return false; + + QColor* p_gc = globalConfigColorFromId(id); + if(!p_gc) + return false; + + QColor* p_bkc = backupConfigColorFromId(id); + if(!p_bkc) + return false; + + const QColor& gc = *p_gc; + const QColor& bkc = *p_bkc; + + return gc != bkc; +} + +bool Appearance::isColorsDirty() const +{ + QTreeWidgetItemIterator it(itemList); + while(*it) + { + if(isColorDirty((IdListViewItem*)*it)) + return true; + ++it; + } + return false; +} + +void Appearance::setColorItemDirty() +{ + IdListViewItem* item = (IdListViewItem*)itemList->selectedItems()[0]; + if(!item) + return; + setColorItemDirty(item); +} + +void Appearance::setColorItemDirty(IdListViewItem* item) +{ + if(!item) + return; + int id = item->id(); + //if(id == 0 || !color) + if(id == 0) + return; + + QColor* p_gc = globalConfigColorFromId(id); + if(!p_gc) + return; + + QColor* p_bkc = backupConfigColorFromId(id); + if(!p_bkc) + return; + + const QColor& gc = *p_gc; + const QColor& bkc = *p_bkc; + + QFont fnt = item->font(0); + //fnt.setBold(bkc != gc); + fnt.setWeight(bkc != gc ? QFont::Black : QFont::Normal); + fnt.setItalic(bkc != gc); + item->setFont(0, fnt); + item->setData(0, Qt::DecorationRole, gc); +} + +void Appearance::updateColorItems() +{ + QTreeWidgetItemIterator it(itemList); + while(*it) + { + setColorItemDirty((IdListViewItem*)*it); + ++it; + } +} + +void Appearance::colorListCustomContextMenuReq(const QPoint& p) +{ + DEBUG_APPEARANCE(stderr, "Appearance::colorListCustomContextMenuReq\n"); + + IdListViewItem* item = static_cast(itemList->itemAt(p)); + bool itemDirty = item && isColorDirty(item); + + QMenu* pup = new QMenu(this); + QAction* act = pup->addAction(tr("Revert changes")); + act->setData(0x100); + act->setEnabled(itemDirty); + act = pup->addAction(tr("Revert all...")); + act->setData(0x101); + act->setEnabled(isColorsDirty()); + act = pup->exec(itemList->mapToGlobal(p)); + if(!act) + { + delete pup; + return; + } + + const int res = act->data().toInt(); + delete pup; + + switch(res) + { + case 0x100: + { + if(item && isColorDirty(item)) + { + resetColorItem(item); + updateColor(); + if(color && _colorDialog) + { + _colorDialog->blockSignals(true); + _colorDialog->setCurrentColor(*color); + _colorDialog->blockSignals(false); + } + // Notify the rest of the app, without the heavy stuff. + MusEGlobal::muse->changeConfig(false); // No write, and simple mode. + } + } + break; + + case 0x101: + { + if(QMessageBox::question(this, QString("Muse"), + tr("Do you really want to reset all colors?"), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Ok) != QMessageBox::Ok) + return; + resetAllColorItems(); + + updateColor(); + if(color && _colorDialog) + { + _colorDialog->blockSignals(true); + _colorDialog->setCurrentColor(*color); + _colorDialog->blockSignals(false); + } + //changeGlobalColor(); + // Notify the rest of the app, without the heavy stuff. + MusEGlobal::muse->changeConfig(false); // No write, and simple mode. + } + break; + } +} + +void Appearance::resetColorItem(IdListViewItem* item) +{ + if(!item) + return; + int id = item->id(); + if(id == 0) + return; + + QColor* p_bkc = backupConfigColorFromId(id); + if(!p_bkc) + return; + + QColor* p_gc = globalConfigColorFromId(id); + if(!p_gc) + return; + + QColor* p_wkc = workingConfigColorFromId(id); + if(!p_wkc) + return; + + const QColor& bkc = *p_bkc; + QColor& gc = *p_gc; + QColor& wkc = *p_wkc; + + gc = bkc; + wkc = bkc; + + QFont fnt = item->font(0); + fnt.setWeight(QFont::Normal); + fnt.setItalic(false); + item->setFont(0, fnt); + item->setData(0, Qt::DecorationRole, gc); +} + +void Appearance::resetAllColorItems() +{ + QTreeWidgetItemIterator it(itemList); + while(*it) + { + resetColorItem((IdListViewItem*)*it); + ++it; + } +} + +void Appearance::chooseColorClicked() +{ + if(!color) + return; + if(!_colorDialog) + { + DEBUG_APPEARANCE(stderr, "Appearance::chooseColorClicked creating new dialog\n"); + _colorDialog = new QColorDialog(this); + _colorDialog->setOption(QColorDialog::NoButtons, true); + connect(_colorDialog, SIGNAL(currentColorChanged(QColor)), SLOT(colorDialogCurrentChanged(QColor))); + connect(_colorDialog, SIGNAL(finished(int)), SLOT(colorDialogFinished(int))); + } + _colorDialog->setCurrentColor(*color); + + IdListViewItem* item = (IdListViewItem*)itemList->selectedItems()[0]; + if(item) + setColorDialogWindowText(item->text(0)); + else + setColorDialogWindowText(); + + _colorDialog->show(); + _colorDialog->raise(); +} + +void Appearance::changeGlobalColor() +{ + if(!color) + return; + + // Memory pointer HACK to get to the global config color. + unsigned long int itemOffset = ((const char*)color) - ((const char*)config); + const char* configOffset = ((const char*)&MusEGlobal::config) + itemOffset; + + // Change the actual global config item, 'live'. + QColor& ccol = *((QColor*)configOffset); + if(ccol != *color) + { + ccol = *color; + // Notify the rest of the app, without the heavy stuff. + MusEGlobal::muse->changeConfig(false); // No write, and simple mode. + } + + setColorItemDirty(); +} + +void Appearance::changeColor(const QColor& c) +{ +// if(!color) +// return; + + if(color && *color != c) + { + *color = c; + //updateColor(); + } + + _configChangedTimer->start(); // Restart +} + +void Appearance::colorDialogCurrentChanged(const QColor& c) +{ + changeColor(c); + //updateColor(); +} + +void Appearance::colorDialogFinished(int /*result*/) +{ + DEBUG_APPEARANCE(stderr, "Appearance::colorDialogFinished result:%d\n", result); + if(_configChangedTimer->isActive()) + _configChangedTimer->stop(); + + if(_colorDialog) + { + _colorDialog->deleteLater(); + _colorDialog = 0; + } +} + +void Appearance::configChangeTimeOut() +{ + updateColor(); + if(color && _colorDialog) + { + _colorDialog->blockSignals(true); + _colorDialog->setCurrentColor(*color); + _colorDialog->blockSignals(false); + } + changeGlobalColor(); +} + //--------------------------------------------------------- -// ok +// doCancel //--------------------------------------------------------- -void Appearance::ok() +void Appearance::doCancel() +{ + MusEGlobal::muse->arranger()->getCanvas()->setBg(QPixmap(config->canvasBgPixmap)); + MusEGlobal::config = *backupConfig; + // Save settings. Use non-simple version - set style and stylesheet. + MusEGlobal::muse->changeConfig(true); // Restore everything possible. +// MusEGlobal::muse->updateThemeAndStyle(true); +} + +//--------------------------------------------------------- +// closeEvent +//--------------------------------------------------------- + +void Appearance::closeEvent(QCloseEvent* e) +{ + DEBUG_APPEARANCE(stderr, "Appearance::closeEvent\n"); + doCancel(); + + if(_colorDialog) + { + _colorDialog->deleteLater(); + _colorDialog = 0; + } + e->accept(); + QDialog::closeEvent(e); +} + +//--------------------------------------------------------- +// okClicked +//--------------------------------------------------------- + +void Appearance::okClicked() { - apply(); - close(); + DEBUG_APPEARANCE(stderr, "Appearance::ok\n"); + if(_colorDialog) + { + _colorDialog->deleteLater(); + _colorDialog = 0; + } + //close(); + hide(); + + if(apply()) + // If required, ask user to restart the application for proper results. + checkClose(); + } //--------------------------------------------------------- @@ -672,8 +1245,15 @@ void Appearance::cancel() { - MusEGlobal::muse->arranger()->getCanvas()->setBg(QPixmap(config->canvasBgPixmap)); - close(); + DEBUG_APPEARANCE(stderr, "Appearance::cancel\n"); + doCancel(); + if(_colorDialog) + { + _colorDialog->deleteLater(); + _colorDialog = 0; + } + //close(); + hide(); } //--------------------------------------------------------- @@ -728,6 +1308,26 @@ } //--------------------------------------------------------- +// setColorDialogWindowText +//--------------------------------------------------------- + +void Appearance::setColorDialogWindowText(const QString& colorName) +{ + if(!_colorDialog) + return; + + if(colorName.isEmpty()) + _colorDialog->setWindowTitle(tr("No current color item")); + else + { + const QString title = tr("Select Color: %1").arg(colorName); + _colorDialog->blockSignals(true); + _colorDialog->setWindowTitle(title); + _colorDialog->blockSignals(false); + } +} + +//--------------------------------------------------------- // selectionChanged //--------------------------------------------------------- @@ -735,97 +1335,47 @@ { IdListViewItem* item = (IdListViewItem*)itemList->selectedItems()[0]; lastSelectedColorItem = 0; - QString txt = item->text(0); + if(!item) + { + colorNameLineEdit->setEnabled(false); + setColorDialogWindowText(); + updateColor(); + return; + } + int id = item->id(); - if (id == 0) { - color = 0; - lastSelectedColorItem = 0; - colorNameLineEdit->setEnabled(false); - return; - } - bool enle = false; - switch(id) { - case 0x400: // "Default" - case 0x401: // "Refrain" - case 0x402: // "Bridge" - case 0x403: // "Intro" - case 0x404: // "Coda" - case 0x405: // "Chorus" - case 0x406: // "Solo" - case 0x407: // "Brass" - case 0x408: // "Percussion" - case 0x409: // "Drums" - case 0x40a: // "Guitar" - case 0x40b: // "Bass" - case 0x40c: // "Flute" - case 0x40d: // "Strings - case 0x40e: // "Keyboard - case 0x40f: // "Piano - case 0x410: // "Saxophon - lastSelectedColorItem = item; - color = &config->partColors[id & 0xff]; - enle = true; - break; - case 0x100: color = &config->bigTimeBackgroundColor; break; - case 0x101: color = &config->bigTimeForegroundColor; break; - case 0x200: color = &config->transportHandleColor; break; - case 0x300: color = &config->waveEditBackgroundColor; break; - case 0x301: color = &config->wavePeakColor; break; - case 0x302: color = &config->waveRmsColor; break; - case 0x303: color = &config->wavePeakColorSelected; break; - case 0x304: color = &config->waveRmsColorSelected; break; - case 0x305: color = &config->waveNonselectedPart; break; - - case 0x411: color = &config->trackBg; break; - case 0x412: color = &config->midiTrackBg; break; - case 0x413: color = &config->drumTrackBg; break; - case 0x41e: color = &config->newDrumTrackBg;break; - case 0x414: color = &config->waveTrackBg; break; - case 0x415: color = &config->outputTrackBg; break; - case 0x416: color = &config->inputTrackBg; break; - case 0x417: color = &config->groupTrackBg; break; - case 0x418: color = &config->auxTrackBg; break; - case 0x419: color = &config->synthTrackBg; break; - case 0x41a: color = &config->selectTrackBg; break; - case 0x41b: color = &config->selectTrackFg; break; - case 0x41c: color = &config->partCanvasBg; break; - case 0x41d: color = &config->ctrlGraphFg; break; - - // 0x41e is already used (between 413 and 414) - - case 0x41f: color = &config->rulerBg; break; - case 0x420: color = &config->rulerFg; break; - case 0x421: color = &config->midiCanvasBg; break; - case 0x422: color = &config->drumListBg; break; - case 0x423: color = &config->midiControllerViewBg; break; - case 0x424: color = &config->rulerCurrent; break; - case 0x425: color = &config->partWaveColorPeak; break; - case 0x426: color = &config->partWaveColorRms; break; - case 0x427: color = &config->partMidiDarkEventColor; break; - case 0x428: color = &config->partMidiLightEventColor; break; - - case 0x500: color = &config->mixerBg; break; - case 0x501: color = &config->midiTrackLabelBg; break; - case 0x502: color = &config->drumTrackLabelBg; break; - case 0x509: color = &config->newDrumTrackLabelBg;break; - case 0x503: color = &config->waveTrackLabelBg; break; - case 0x504: color = &config->outputTrackLabelBg; break; - case 0x505: color = &config->inputTrackLabelBg; break; - case 0x506: color = &config->groupTrackLabelBg; break; - case 0x507: color = &config->auxTrackLabelBg; break; - case 0x508: color = &config->synthTrackLabelBg; break; - // 0x509 is already used (between 502 and 503) + + color = workingConfigColorFromId(id); + if(!color) + { + lastSelectedColorItem = 0; + colorNameLineEdit->setEnabled(false); + setColorDialogWindowText(); + updateColor(); + return; + } - default: - color = 0; - break; - } + bool enle = false; + if(id >= 0x600 && id < (0x600 + NUM_PARTCOLORS)) + { + lastSelectedColorItem = item; + enle = true; + } + colorNameLineEdit->setEnabled(enle); QString s; if(enle) s = config->partColorNames[id & 0xff]; colorNameLineEdit->setText(s); updateColor(); + + if(_colorDialog) + { + _colorDialog->blockSignals(true); + _colorDialog->setCurrentColor(*color); + setColorDialogWindowText(item->text(0)); + _colorDialog->blockSignals(false); + } } void Appearance::updateColor() @@ -843,6 +1393,9 @@ hval->setEnabled(color); sval->setEnabled(color); vval->setEnabled(color); + colorwidget->setEnabled(color); + colorwidget->setEnabled(color); + pickColorButton->setEnabled(color); if (color == 0) return; QPalette pal; @@ -917,6 +1470,7 @@ color->setRgb(val, g, b); } updateColor(); + _configChangedTimer->start(); // Restart } void Appearance::gsliderChanged(int val) @@ -927,6 +1481,7 @@ color->setRgb(r, val, b); } updateColor(); + _configChangedTimer->start(); // Restart } void Appearance::bsliderChanged(int val) @@ -937,6 +1492,7 @@ color->setRgb(r, g, val); } updateColor(); + _configChangedTimer->start(); // Restart } void Appearance::hsliderChanged(int val) @@ -947,6 +1503,7 @@ color->setHsv(val, s, v); } updateColor(); + _configChangedTimer->start(); // Restart } void Appearance::ssliderChanged(int val) @@ -957,6 +1514,7 @@ color->setHsv(h, val, v); } updateColor(); + _configChangedTimer->start(); // Restart } void Appearance::vsliderChanged(int val) @@ -967,6 +1525,7 @@ color->setHsv(h, s, val); } updateColor(); + _configChangedTimer->start(); // Restart } //--------------------------------------------------------- @@ -975,8 +1534,8 @@ void Appearance::addToPaletteClicked() { - if (!color) - return; + QColor new_c = color ? *color : colorwidget->color(); + QAbstractButton* button = (QAbstractButton*)aPalette->checkedButton(); // ddskrjo int r, g, b; @@ -1000,8 +1559,8 @@ } if (button) { int id = aPalette->id(button); - config->palette[id] = *color; - button->setStyleSheet(QString("background-color: ") + color->name()); + config->palette[id] = new_c; + button->setStyleSheet(QString("background-color: ") + new_c.name()); button->update(); //?? } } @@ -1023,6 +1582,7 @@ return; // interpret palette slot as empty *color = c; updateColor(); + _configChangedTimer->start(); // Restart } } diff -Nru muse-2.1.2/muse/appearance.h muse-3.0.2+ds1/muse/appearance.h --- muse-2.1.2/muse/appearance.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/appearance.h 2017-12-04 21:01:18.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: ./muse/appearance.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -27,9 +28,12 @@ class QColor; class QDialog; +class QTimer; +class QColorDialog; +class QCloseEvent; namespace MusEGlobal { - class GlobalConfigValues; + struct GlobalConfigValues; } namespace MusEGui { @@ -37,30 +41,84 @@ class MusE; //--------------------------------------------------------- +// IdListViewItem +//--------------------------------------------------------- + +class IdListViewItem : public QTreeWidgetItem { + private: + int _id; + + public: + + public: + IdListViewItem(int id, QTreeWidgetItem* parent, QString s) + : QTreeWidgetItem(parent, QStringList(s)) + { + _id = id; + } + IdListViewItem(int id, QTreeWidget* parent, QString s) + : QTreeWidgetItem(parent, QStringList(s)) + { + _id = id; + } + int id() const { return _id; } + }; + +//--------------------------------------------------------- // Appearance Dialog //--------------------------------------------------------- class Appearance : public QDialog, public Ui::AppearanceDialogBase { Q_OBJECT - + + protected: + virtual void closeEvent(QCloseEvent*); + private: + QTimer* _configChangedTimer; Arranger* arr; QColor* color; + MusEGlobal::GlobalConfigValues* backupConfig; MusEGlobal::GlobalConfigValues* config; QButtonGroup* aPalette; QTreeWidgetItem* user_bg; QTreeWidgetItem* global_bg; QTreeWidgetItem* lastSelectedBgItem; - QTreeWidgetItem* lastSelectedColorItem; + QTreeWidgetItem* lastSelectedColorItem; + QColorDialog* _colorDialog; + + QColor* globalConfigColorFromId(int id) const; + long int configOffsetFromColorId(int id) const; + QColor* backupConfigColorFromId(int id) const; + QColor* workingConfigColorFromId(int id) const; + bool isColorDirty(IdListViewItem* item) const; + bool isColorsDirty() const; + // Sets current (last) item dirty. + void setColorItemDirty(); + // Sets an item dirty. + void setColorItemDirty(IdListViewItem* item); + // Update the dirty states of all items. + void updateColorItems(); + void resetColorItem(IdListViewItem* item); + void resetAllColorItems(); void updateFonts(); void updateColor(); + void changeColor(const QColor& c); + void changeGlobalColor(); + void setConfigurationColors(); + void setColorDialogWindowText(const QString& colorName = QString()); + void doCancel(); + // Returns true if restart required (style or stylesheet changed). + bool apply(); + // Ask to close and if so, tell the main window to close the app and return true. + bool checkClose(); private slots: - void apply(); - void ok(); + void applyClicked(); + void okClicked(); void changeTheme(); void cancel(); void addBackground(); @@ -89,12 +147,19 @@ void paletteClicked(int); void bgSelectionChanged(QTreeWidgetItem*); void colorNameEditFinished(); + void loadColors(); + void saveColors(); + void chooseColorClicked(); + void colorDialogCurrentChanged(const QColor&); + void colorDialogFinished(int result); + void configChangeTimeOut(); + void colorListCustomContextMenuReq(const QPoint&); public: Appearance(Arranger*, QWidget* parent=0); ~Appearance(); void resetValues(); - static QString defaultStyle; + static QString& getSetDefaultStyle(const QString *newStyle = NULL); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/app.h muse-3.0.2+ds1/muse/app.h --- muse-2.1.2/muse/app.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/app.h 2018-01-06 20:31:35.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: app.h,v 1.34.2.14 2009/11/16 11:29:33 lunar_shuttle Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,11 +26,12 @@ #define __APP_H__ #include "config.h" +#include "globaldefs.h" #include "cobject.h" -#include #include #include +#include class QCloseEvent; class QMainWindow; @@ -60,7 +62,6 @@ class Xml; } - namespace MusEGui { class Appearance; class Arranger; @@ -72,7 +73,6 @@ class EditInstrument; class EditToolBar; class GlobalSettingsConfig; -class GlobalSettingsConfig; class MRConfig; class MarkerView; class MetronomeConfig; @@ -89,6 +89,8 @@ class TopWin; class Transport; class VisibleTracks; +class RouteDialog; +class CpuToolbar; #define MENU_ADD_SYNTH_ID_BASE 0x8000 @@ -164,12 +166,14 @@ QAction *dontFollowAction, *followPageAction, *followCtsAction; // Help Menu Actions - QAction *helpManualAction, *helpHomepageAction, *helpReportAction, *helpAboutAction; + QAction *helpManualAction, *helpHomepageAction, *helpReportAction, *helpAboutAction, *helpDidYouKnow; QString appName; QFileInfo project; QToolBar *tools; + CpuToolbar* cpuLoadToolbar; + // when adding a toolbar to the main window, remember adding it to // either the requiredToolbars or optionalToolbars list! @@ -200,6 +204,8 @@ Appearance* appearance; AudioMixerApp* mixer1; AudioMixerApp* mixer2; + RouteDialog* routeDialog; + // NOTE: For deleting parentless dialogs and widgets, please add them to deleteParentlessDialogs(). void deleteParentlessDialogs(); Arranger* _arranger; @@ -211,6 +217,8 @@ QMenu* openRecent; bool writeTopwinState; + // Set to restart MusE (almost) from scratch before calling close(). + bool _isRestartingApp; bool readMidi(FILE*); void read(MusECore::Xml& xml, bool doReadMidiPorts, bool isTemplate); @@ -236,16 +244,25 @@ void writeGlobalConfiguration(int level, MusECore::Xml&) const; void writeConfiguration(int level, MusECore::Xml&) const; void updateConfiguration(); + QString projectTitle(QString name); QSignalMapper *midiPluginSignalMapper; QSignalMapper *followSignalMapper; QSignalMapper *windowsMapper; + QTimer *saveTimer; + QTimer *blinkTimer; + QTimer *messagePollTimer; + int saveIncrement; signals: void configChanged(); void activeTopWinChanged(MusEGui::TopWin*); private slots: + void heartBeat(); + void blinkTimerSlot(); + void saveTimerSlot(); + void messagePollTimerSlot(); void loadProject(); bool save(); void configGlobalSettings(); @@ -318,6 +335,7 @@ void arrangeSubWindowsRows(); void arrangeSubWindowsColumns(); void tileSubWindows(); + void setDirty(); public slots: bool saveAs(); @@ -326,18 +344,17 @@ void loadProjectFile(const QString&); void loadProjectFile(const QString&, bool songTemplate, bool doReadMidiPorts); void toplevelDeleting(MusEGui::TopWin* tl); - void loadTheme(const QString&); - void loadStyleSheetFile(const QString&); bool seqRestart(); void loadTemplate(); void showBigtime(bool); void showMixer1(bool); void showMixer2(bool); + void startRouteDialog(); void showMarker(bool); void showArranger(bool); void importMidi(const QString &file); void showDidYouKnowDialog(); - void startEditInstrument(const QString& find_instrument = QString(), EditInstrument::TabType show_tab = EditInstrument::Patches); + void startEditInstrument(const QString& find_instrument = QString(), EditInstrumentTabType show_tab = EditInstrumentPatches); void configMidiPorts(); void startEditor(MusECore::PartList*, int); @@ -367,18 +384,40 @@ void updateWindowMenu(); + void resetXrunsCounter(); + + private: + timeval lastCpuTime; + timespec lastSysTime; + float fAvrCpuLoad; + int avrCpuLoadCounter; + float fCurCpuLoad; public: MusE(); + void populateAddTrack(); + void loadDefaultSong(int argc, char** argv); + bool loadConfigurationColors(QWidget* parent = 0); + bool saveConfigurationColors(QWidget* parent = 0); + // Whether to restart MusE (almost) from scratch when calling close(). + bool restartingApp() const { return _isRestartingApp;} + // Set to restart MusE (almost) from scratch before calling close(). + void setRestartingApp(bool v) { _isRestartingApp = v;} Arranger* arranger() const { return _arranger; } ArrangerView* getArrangerView() const { return arrangerView; } QRect configGeometryMain; QProgressDialog *progress; bool importMidi(const QString name, bool merge); void kbAccel(int); + + // writeFlag: Write to configuration file. void changeConfig(bool writeFlag); + void seqStop(); - bool seqStart(); + bool seqStart(); + // Starts the ALSA midi portion of the sequencer. audioDevice must be valid. + // Returns true if successful or already running. +// bool seqStartMidi(); void setHeartBeat(); void importController(int, MusECore::MidiPort*, int); QString projectName() { return project.fileName(); } @@ -396,6 +435,8 @@ const ToplevelList* getToplevels() { return &toplevels; } TopWin* getCurrentMenuSharingTopwin() { return currentMenuSharingTopwin; } + + float getCPULoad(); #ifdef HAVE_LASH void lash_idle_cb (); diff -Nru muse-2.1.2/muse/arranger/alayout.cpp muse-3.0.2+ds1/muse/arranger/alayout.cpp --- muse-2.1.2/muse/arranger/alayout.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/alayout.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -21,134 +21,111 @@ //========================================================= #include "alayout.h" -#include "arranger.h" - -#include +#include "widget_stack.h" +#include "scrollbar.h" +#include "splitter.h" + +#include +#include +#include +#include namespace MusEGui { -//--------------------------------------------------------- -// wadd -//--------------------------------------------------------- - -void TLLayout::wadd(int idx, QWidget* w) - { - li[idx] = new QWidgetItem(w); - if (idx == 0) - stack = (WidgetStack*)w; - if (idx == 1) - sb = (QScrollBar*)w; - addItem(li[idx]); - } - -#if 0 // DELETETHIS 36 -//--------------------------------------------------------- -// TLLayoutIterator -//--------------------------------------------------------- - -class TLLayoutIterator - { - int idx; - QList list; - - public: - TLLayoutIterator(QList l) : idx(0), list(l) {} - QLayoutItem *current() { return idx < int(list->count()) ? list->at(idx) : 0; } - QLayoutItem *next() { idx++; return current(); } - QLayoutItem *takeCurrent() { return list->take( idx ); } - }; - -//--------------------------------------------------------- -// iterator -//--------------------------------------------------------- - -QLayoutIterator TLLayout::iterator() - { - return QLayoutIterator(0); - } - -void TLLayout::addItem(QLayoutItem *item) - { - ilist.append(item); - } - -TLLayout::~TLLayout() - { - deleteAllItems(); - } - -#endif - -//--------------------------------------------------------- -// setGeometry -// perform geometry management for tracklist: -// -// 0 1 2 -// +-----------+--------+---------+ -// | Trackinfo | scroll | header 2| -// | | bar +---------+ y1 -// | ^ | | ^ | -// | | | | -// | 0 | 1 | 3 | -// +-----------+--------+---------+ y2 -// | hline 4 | -// +----------+-------------------+ y3 -// | button 5 | | -// +----------+-------------------+ -//--------------------------------------------------------- +TLLayout::TLLayout(QWidget *parent, WidgetStack* stack, ScrollBar* sb, Splitter* splitter) + : QLayout(parent), _stack(stack), _sb(sb), _splitter(splitter) +{ + _inSetGeometry = false; + setContentsMargins(0, 0, 0, 0); + setSpacing(0); + _stackLi = new QWidgetItem(_stack); + _sbLi = new QWidgetItem(_sb); +} void TLLayout::setGeometry(const QRect &rect) { + if(_inSetGeometry) + return; + int w = rect.width(); int h = rect.height(); QSize s0; - if (stack->visibleWidget()) { - s0 = stack->visibleWidget()->minimumSizeHint(); - if (!s0.isValid()) // widget has no geometry management - s0 = stack->visibleWidget()->size(); - } + QWidget* widget = _stack->visibleWidget(); + if(widget) { + s0 = widget->minimumSizeHint(); + if (!s0.isValid()) // widget has no geometry management + s0 = widget->size(); + } else - s0 = stack->minimumSizeHint(); - - QSize s1 = li[1]->sizeHint(); - QSize s2 = li[2]->sizeHint(); - //QSize s3 = li[3]->sizeHint(); DELETETHIS huh? - QSize s4 = li[4]->sizeHint(); - QSize s5 = li[5]->sizeHint(); - - int y1 = 30; // fixed header height - int ah = h - s5.height() - s4.height() - y1; // list height - int aw = w - s1.width() - s0.width(); // list width - - int y2 = ah + s2.height(); - int y3 = y2 + s4.height(); - int x1 = s0.width(); - int x2 = x1 + s1.width(); + s0 = _stack->minimumSizeHint(); - li[0]->setGeometry(QRect(0, 0, s0.width(), y2)); + int y2 = h; - QWidget* widget = stack->visibleWidget(); int range = s0.height() - y2; if (range < 0) range = 0; - + //fprintf(stderr, "TrackInfoLayout::setGeometry h:%d s0 height:%d range:%d y2:%d\n", + // h, s0.height(), range, y2); if (range) - sb->setMaximum(range); + _sb->setMaximum(range); + + const bool vis = range != 0; + if(_sb->isVisible() != vis) + { + if(_sb->isVisible()) + { + const int sbw = _sb->width(); + _sb->setVisible(false); + if(_splitter) + { + _inSetGeometry = true; + _splitter->setPosition(1, w - sbw); + _inSetGeometry = false; + } + _stackLi->setGeometry(QRect(0, 0, w - sbw, y2)); + if(widget) + { + QSize r(w - sbw, y2 < s0.height() ? s0.height() : y2); + //fprintf(stderr, "TrackInfoLayout::setGeometry hide sb: widget w:%d\n", + // r.width()); + widget->setGeometry(0, -_sb->value(), r.width(), r.height()); + } + } + else + { + _sb->setVisible(true); + if(_splitter) + { + _inSetGeometry = true; + _splitter->setPosition(1, w + _sb->width()); + _inSetGeometry = false; + } + _stackLi->setGeometry(QRect(0, 0, w, y2)); + if(widget) + { + QSize r(w, y2 < s0.height() ? s0.height() : y2); + //fprintf(stderr, "TrackInfoLayout::setGeometry show sb: widget w:%d\n", + // r.width()); + widget->setGeometry(0, -_sb->value(), r.width(), r.height()); + } + } + } + else + { + const int ww = _sb->isVisible() ? w - _sb->width() : w; + _stackLi->setGeometry(QRect(0, 0, ww, y2)); + if(widget) + { + QSize r(ww, y2 < s0.height() ? s0.height() : y2); + //fprintf(stderr, "TrackInfoLayout::setGeometry not show/hide sb: widget w:%d\n", + // r.width()); + widget->setGeometry(0, -_sb->value(), r.width(), r.height()); + } + } + if(widget) + _sbLi->setGeometry(QRect(widget->width(), 0, _sbLi->sizeHint().width(), y2)); - if (widget) { - QSize r(s0.width(), y2 < s0.height() ? s0.height() : y2); // p4.0.11 Tim - widget->setGeometry(0, 0, r.width(), r.height()); - } - - li[1]->setGeometry(QRect(x1, 0, s1.width(), y2)); - li[2]->setGeometry(QRect(x2, 0, aw, s2.height())); - li[3]->setGeometry(QRect(x2, y1, aw, ah)); - li[4]->setGeometry(QRect(0, y2, w, s4.height())); - li[5]->setGeometry(QRect(3, y3, s5.width(), s5.height())); - - // Fix for non-appearing scrollbar. Yes, we must allow the recursive call, but try it here, not above. p4.0.44 Tim - sb->setVisible(range != 0); } //--------------------------------------------------------- @@ -160,14 +137,11 @@ return QSize(150, 100); } -//--------------------------------------------------------- -// minimumSize -//--------------------------------------------------------- - QSize TLLayout::minimumSize() const { - int w = stack->minimumSizeHint().width(); - w += li[1]->sizeHint().width(); + int w = _stack->minimumSizeHint().width(); + if(_sb->isVisible()) + w += _sbLi->sizeHint().width(); return QSize(w, 50); } @@ -182,28 +156,45 @@ } //--------------------------------------------------------- +// itemAt +//--------------------------------------------------------- + +QLayoutItem* TLLayout::itemAt(int i) const +{ + switch(i) + { + case 0: return _stackLi; break; + case 1: return _sbLi; break; + //case 2: return _buttonLi; break; + } + return 0; +} + +//--------------------------------------------------------- // takeAt //--------------------------------------------------------- QLayoutItem* TLLayout::takeAt(int i) - { - if (i >= 0 && i < ilist.size()) - return ilist.takeAt(i); - else - return 0; - } - +{ + switch(i) + { + case 0: return _stackLi; break; + case 1: return _sbLi; break; + } + return 0; +} + //--------------------------------------------------------- // clear //--------------------------------------------------------- void TLLayout::clear() - { - QLayoutItem* child; - while ((child = takeAt(0)) != 0) { - delete child->widget(); - delete child; - } - } +{ + delete _stackLi; + _stackLi = 0; + + delete _sbLi; + _sbLi = 0; +} } // namespace MusEGui diff -Nru muse-2.1.2/muse/arranger/alayout.h muse-3.0.2+ds1/muse/arranger/alayout.h --- muse-2.1.2/muse/arranger/alayout.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/alayout.h 2017-12-04 21:01:18.000000000 +0000 @@ -24,14 +24,16 @@ #define __ALAYOUT_H__ #include -#include +#include class QLayoutItem; -class QScrollBar; +class QWidget; namespace MusEGui { class WidgetStack; +class ScrollBar; +class Splitter; //--------------------------------------------------------- // TLLayout @@ -43,28 +45,31 @@ Q_OBJECT bool _inSetGeometry; - QList ilist; - QLayoutItem* li[6]; - QScrollBar* sb; - WidgetStack* stack; + WidgetStack* _stack; + ScrollBar* _sb; + Splitter* _splitter; // This is not actually in the layout, but used and/or adjusted anyway. + + QLayoutItem* _stackLi; + QLayoutItem* _sbLi; + public: - TLLayout(QWidget *parent) : QLayout(parent) { _inSetGeometry = false; setContentsMargins(0, 0, 0, 0); setSpacing(-1); } + static const int numItems = 2; + TLLayout(QWidget *parent, WidgetStack* stack, ScrollBar* sb, Splitter* splitter); ~TLLayout() { clear(); } - void addItem(QLayoutItem *item) { ilist.append(item); } + void addItem(QLayoutItem*) { } // Do nothing, it's a custom layout. virtual Qt::Orientations expandingDirections() const { return 0; } virtual bool hasHeightForWidth() const { return false; } - virtual int count() const { return ilist.size(); } + virtual int count() const { return numItems; } void clear(); - void wadd(int idx, QWidget* w); virtual QSize sizeHint() const; virtual QSize minimumSize() const; virtual QSize maximumSize() const; virtual void setGeometry(const QRect &rect); - virtual QLayoutItem* itemAt(int i) const { return ilist.value(i);} + virtual QLayoutItem* itemAt(int) const; virtual QLayoutItem* takeAt(int); }; diff -Nru muse-2.1.2/muse/arranger/arranger.cpp muse-3.0.2+ds1/muse/arranger/arranger.cpp --- muse-2.1.2/muse/arranger/arranger.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/arranger.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: arranger.cpp,v 1.33.2.21 2009/11/17 22:08:22 terminator356 Exp $ // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -48,12 +49,12 @@ #include "app.h" #include "mtscale.h" #include "scrollscale.h" +#include "scrollbar.h" #include "pcanvas.h" #include "poslabel.h" #include "xml.h" #include "splitter.h" #include "lcombo.h" -#include "mtrackinfo.h" #include "midiport.h" #include "mididev.h" #include "utils.h" @@ -62,7 +63,8 @@ #include "icons.h" #include "header.h" #include "utils.h" -#include "alayout.h" +#include "widget_stack.h" +#include "trackinfo_layout.h" #include "audio.h" #include "event.h" #include "midiseq.h" @@ -70,8 +72,14 @@ #include "mpevent.h" #include "gconfig.h" #include "mixer/astrip.h" +#include "mixer/mstrip.h" #include "spinbox.h" #include "shortcuts.h" +#include "ttoolbutton.h" + +#ifdef _USE_TRACKINFO_ALT +#include "mtrackinfo.h" +#endif namespace MusEGui { @@ -160,21 +168,13 @@ return temp; } - - -void ScrollBar::redirectedWheelEvent(QWheelEvent* e) -{ - if(isVisible()) - wheelEvent(e); -} - - //--------------------------------------------------------- // Arranger::setHeaderToolTips //--------------------------------------------------------- void Arranger::setHeaderToolTips() { + header->setToolTip(COL_INPUT_MONITOR, tr("Enable input monitor")); header->setToolTip(COL_RECORD, tr("Enable Recording")); header->setToolTip(COL_MUTE, tr("Mute/Off Indicator")); header->setToolTip(COL_SOLO, tr("Solo Indicator")); @@ -195,7 +195,10 @@ void Arranger::setHeaderWhatsThis() { - header->setWhatsThis(COL_RECORD, tr("Enable recording. Click to toggle.")); + header->setWhatsThis(COL_INPUT_MONITOR, tr("Enable input monitor. Click to toggle.\nPasses input through to output for monitoring.\n" + "See also Settings: Automatically Monitor On Record Arm.")); + header->setWhatsThis(COL_RECORD, tr("Enable recording. Click to toggle.\n" + "See also Settings: Automatically Monitor On Record Arm.")); header->setWhatsThis(COL_MUTE, tr("Mute indicator. Click to toggle.\nRight-click to toggle track on/off.\nMute is designed for rapid, repeated action.\nOn/Off is not!")); header->setWhatsThis(COL_SOLO, tr("Solo indicator. Click to toggle.\nConnected tracks are also 'phantom' soloed,\n indicated by a dark square.")); header->setWhatsThis(COL_CLASS, tr("Track type. Right-click to change\n midi and drum track types.")); @@ -218,6 +221,9 @@ _raster = 0; // measure selected = 0; showTrackinfoFlag = true; +#ifdef _USE_TRACKINFO_ALT + showTrackinfoAltFlag = false; +#endif cursVal = INT_MAX; @@ -230,6 +236,13 @@ // create toolbar in toplevel widget //--------------------------------------------------- + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + parent->addToolBarBreak(); QToolBar* toolbar = parent->addToolBar(tr("Arranger")); toolbar->setObjectName("ArrangerToolbar"); @@ -251,7 +264,7 @@ for (int i = 0; i < 6; i++) _rasterCombo->insertItem(i, tr(gArrangerRasterStrings[i]), gArrangerRasterTable[i]); _rasterCombo->setCurrentIndex(1); - // Set the audio record part snapping. Set to 0 (bar), the same as this combo box intial raster. + // Set the audio record part snapping. Set to 0 (bar), the same as this combo box initial raster. MusEGlobal::song->setArrangerRaster(0); toolbar->addWidget(_rasterCombo); connect(_rasterCombo, SIGNAL(activated(int)), SLOT(rasterChanged(int))); @@ -334,10 +347,17 @@ split->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); box->addWidget(split, 1000); + + trackInfoWidget = new TrackInfoWidget(split); + split->setStretchFactor(split->indexOf(trackInfoWidget), 0); + QSizePolicy tipolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + tipolicy.setHorizontalStretch(0); + tipolicy.setVerticalStretch(100); + trackInfoWidget->setSizePolicy(tipolicy); + tracklist = new QWidget(split); - split->setStretchFactor(split->indexOf(tracklist), 0); - QSizePolicy tpolicy = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + QSizePolicy tpolicy = QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); tpolicy.setHorizontalStretch(0); tpolicy.setVerticalStretch(100); tracklist->setSizePolicy(tpolicy); @@ -353,63 +373,88 @@ // Track Info //--------------------------------------------------- - infoScroll = new ScrollBar(Qt::Vertical, tracklist); - infoScroll->setObjectName("infoScrollBar"); - //genTrackInfo(tracklist); // Moved below - - // Track-Info Button - ib = new QToolButton(tracklist); - ib->setText(tr("TrackInfo")); - ib->setCheckable(true); - ib->setChecked(showTrackinfoFlag); - ib->setFocusPolicy(Qt::NoFocus); - connect(ib, SIGNAL(toggled(bool)), SLOT(showTrackInfo(bool))); + trackInfoButton = new CompactToolButton(this); + trackInfoButton->setContentsMargins(0, 0, 0, 0); + trackInfoButton->setText(tr("TrackInfo")); + trackInfoButton->setCheckable(true); + trackInfoButton->setChecked(showTrackinfoFlag); + trackInfoButton->setFocusPolicy(Qt::NoFocus); + trackInfoButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + connect(trackInfoButton, SIGNAL(toggled(bool)), SLOT(showTrackInfo(bool))); + +#ifdef _USE_TRACKINFO_ALT + trackInfoAltButton = new CompactToolButton(this); + trackInfoAltButton->setContentsMargins(0, 0, 0, 0); + trackInfoAltButton->setText(tr("Alt")); + trackInfoAltButton->setCheckable(true); + trackInfoAltButton->setChecked(showTrackinfoAltFlag); + trackInfoAltButton->setFocusPolicy(Qt::NoFocus); + trackInfoAltButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + connect(trackInfoAltButton, SIGNAL(toggled(bool)), SLOT(showTrackInfoAlt(bool))); +#endif + genTrackInfo(trackInfoWidget); + // set up the header header = new Header(tracklist, "header"); - header->setFixedHeight(30); + header->setFixedHeight(31); - QFontMetrics fm1(header->font()); - int fw = 8; + QFontMetrics fm1 = header->fontMetrics(); + int fw = 11; - header->setColumnLabel(tr("R"), COL_RECORD, fm1.width('R')+fw); - header->setColumnLabel(tr("M"), COL_MUTE, fm1.width('M')+fw); - header->setColumnLabel(tr("S"), COL_SOLO, fm1.width('S')+fw); - header->setColumnLabel(tr("C"), COL_CLASS, fm1.width('C')+fw); + header->setColumnLabel(tr("I"), COL_INPUT_MONITOR); + header->setColumnLabel(tr("R"), COL_RECORD); + header->setColumnLabel(tr("M"), COL_MUTE); + header->setColumnLabel(tr("S"), COL_SOLO); + header->setColumnLabel(tr("C"), COL_CLASS); header->setColumnLabel(tr("Track"), COL_NAME, 100); header->setColumnLabel(tr("Port"), COL_OPORT, 60); - header->setColumnLabel(tr("Ch"), COL_OCHANNEL, 30); - header->setColumnLabel(tr("T"), COL_TIMELOCK, fm1.width('T')+fw); + header->setColumnLabel(tr("Ch"), COL_OCHANNEL); + header->setColumnLabel(tr("T"), COL_TIMELOCK); header->setColumnLabel(tr("Automation"), COL_AUTOMATION, 75); header->setColumnLabel(tr("Clef"), COL_CLEF, 75); for (unsigned i=0;isetColumnLabel(custom_columns[i].name, COL_CUSTOM_MIDICTRL_OFFSET+i, MAX(fm1.width(custom_columns[i].name)+fw, 30)); - header->setResizeMode(COL_RECORD, QHeaderView::Fixed); - header->setResizeMode(COL_MUTE, QHeaderView::Fixed); - header->setResizeMode(COL_SOLO, QHeaderView::Fixed); - header->setResizeMode(COL_CLASS, QHeaderView::Fixed); - header->setResizeMode(COL_NAME, QHeaderView::Interactive); - header->setResizeMode(COL_OPORT, QHeaderView::Interactive); - header->setResizeMode(COL_OCHANNEL, QHeaderView::Fixed); - header->setResizeMode(COL_TIMELOCK, QHeaderView::Fixed); - header->setResizeMode(COL_AUTOMATION, QHeaderView::Interactive); - header->setResizeMode(COL_CLEF, QHeaderView::Interactive); + + header->resizeSection(COL_INPUT_MONITOR, fm1.width(header->columnLabel(COL_INPUT_MONITOR)) + fw); + header->resizeSection(COL_RECORD, fm1.width(header->columnLabel(COL_RECORD)) + fw); + header->resizeSection(COL_MUTE, fm1.width(header->columnLabel(COL_MUTE)) + fw); + header->resizeSection(COL_SOLO, fm1.width(header->columnLabel(COL_SOLO)) + fw); + header->resizeSection(COL_CLASS, fm1.width(header->columnLabel(COL_CLASS)) + fw); + header->resizeSection(COL_OCHANNEL, fm1.width(header->columnLabel(COL_OCHANNEL)) + fw); + header->resizeSection(COL_TIMELOCK, fm1.width(header->columnLabel(COL_TIMELOCK)) + fw); + + header->setSectionResizeMode(COL_INPUT_MONITOR, QHeaderView::Fixed); + header->setSectionResizeMode(COL_RECORD, QHeaderView::Fixed); + header->setSectionResizeMode(COL_MUTE, QHeaderView::Fixed); + header->setSectionResizeMode(COL_SOLO, QHeaderView::Fixed); + header->setSectionResizeMode(COL_CLASS, QHeaderView::Fixed); + header->setSectionResizeMode(COL_NAME, QHeaderView::Interactive); + header->setSectionResizeMode(COL_OPORT, QHeaderView::Interactive); + header->setSectionResizeMode(COL_OCHANNEL, QHeaderView::Fixed); + header->setSectionResizeMode(COL_TIMELOCK, QHeaderView::Fixed); + header->setSectionResizeMode(COL_AUTOMATION, QHeaderView::Interactive); + header->setSectionResizeMode(COL_CLEF, QHeaderView::Interactive); for (unsigned i=0;isetResizeMode(COL_CUSTOM_MIDICTRL_OFFSET+i, QHeaderView::Interactive); + header->setSectionResizeMode(COL_CUSTOM_MIDICTRL_OFFSET+i, QHeaderView::Interactive); + + // 04/18/17 Time lock remains unused. Disabled until a use is found. + // Plans were to use it (or not) when time stretching / pitch shifting work is done. + header->setSectionHidden(COL_TIMELOCK, true); setHeaderToolTips(); setHeaderWhatsThis(); - header->setMovable (true); + header->setSectionsMovable (true); header->restoreState(header_state); - list = new TList(header, tracklist, "tracklist"); + + tlistLayout = new QVBoxLayout(tracklist); + tlistLayout->setContentsMargins(0, 0, 0, 0); + tlistLayout->setSpacing(0); + tlistLayout->addWidget(header); + tlistLayout->addWidget(list); - // Do this now that the list is available. - genTrackInfo(tracklist); - - connect(list, SIGNAL(selectionChanged(MusECore::Track*)), SLOT(trackSelectionChanged())); - connect(list, SIGNAL(selectionChanged(MusECore::Track*)), midiTrackInfo, SLOT(setTrack(MusECore::Track*))); connect(header, SIGNAL(sectionResized(int,int,int)), list, SLOT(redraw())); connect(header, SIGNAL(sectionMoved(int,int,int)), list, SLOT(redraw())); @@ -426,24 +471,26 @@ // | ib | | 3 // +-----+------------------------+ - connect(infoScroll, SIGNAL(valueChanged(int)), SLOT(trackInfoScroll(int))); - tgrid = new TLLayout(tracklist); // layout manager for this - tgrid->wadd(0, trackInfo); - tgrid->wadd(1, infoScroll); - tgrid->wadd(2, header); - tgrid->wadd(3, list); - tgrid->wadd(4, MusECore::hLine(tracklist)); - tgrid->wadd(5, ib); - //--------------------------------------------------- // Editor //--------------------------------------------------- int offset = AL::sigmap.ticksMeasure(0); - hscroll = new ScrollScale(-2000, -5, xscale, MusEGlobal::song->len(), Qt::Horizontal, editor, -offset); + hscroll = new ScrollScale(-2000, -5, xscale, MusEGlobal::song->len(), Qt::Horizontal, this, -offset); hscroll->setFocusPolicy(Qt::NoFocus); - ib->setFixedHeight(hscroll->sizeHint().height()); - + hscroll->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + +#ifdef _USE_TRACKINFO_ALT + bottomHLayout = new ArrangerHScrollLayout(0, trackInfoButton, trackInfoAltButton, hscroll, editor); +#else + // No Alt panel here. + bottomHLayout = new ArrangerHScrollLayout(0, trackInfoButton, 0, hscroll, editor); +#endif + + box->addLayout(bottomHLayout); + bottomHLayout->setContentsMargins(0, 0, 0, 0); + bottomHLayout->setSpacing(0); + vscroll = new QScrollBar(editor); vscroll->setMinimum(0); vscroll->setMaximum(20*20); @@ -454,11 +501,7 @@ list->setScroll(vscroll); - QList vallist; - vallist.append(tgrid->maximumSize().width()); - split->setSizes(vallist); - - QGridLayout* egrid = new QGridLayout(editor); + egrid = new ArrangerCanvasLayout(editor, bottomHLayout); egrid->setColumnStretch(0, 50); egrid->setRowStretch(2, 50); egrid->setContentsMargins(0, 0, 0, 0); @@ -479,6 +522,11 @@ connect(list, SIGNAL(keyPressExt(QKeyEvent*)), canvas, SLOT(redirKeypress(QKeyEvent*))); connect(canvas, SIGNAL(selectTrackAbove()), list, SLOT(selectTrackAbove())); connect(canvas, SIGNAL(selectTrackBelow()), list, SLOT(selectTrackBelow())); + connect(canvas, SIGNAL(editTrackNameSig()), list, SLOT(editTrackNameSlot())); + + connect(canvas, SIGNAL(muteSelectedTracks()), list, SLOT(muteSelectedTracksSlot())); + connect(canvas, SIGNAL(soloSelectedTracks()), list, SLOT(soloSelectedTracksSlot())); + connect(canvas, SIGNAL(horizontalZoom(bool, const QPoint&)), SLOT(horizontalZoom(bool, const QPoint&))); connect(canvas, SIGNAL(horizontalZoom(int, const QPoint&)), SLOT(horizontalZoom(int, const QPoint&))); connect(lenEntry, SIGNAL(returnPressed()), SLOT(focusCanvas())); @@ -487,19 +535,22 @@ connect(globalPitchSpinBox, SIGNAL(escapePressed()), SLOT(focusCanvas())); connect(globalTempoSpinBox, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(globalTempoSpinBox, SIGNAL(escapePressed()), SLOT(focusCanvas())); +#ifdef _USE_TRACKINFO_ALT connect(midiTrackInfo, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(midiTrackInfo, SIGNAL(escapePressed()), SLOT(focusCanvas())); - +#endif + //connect(this, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); connect(list, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); - connect(trackInfo, SIGNAL(redirectWheelEvent(QWheelEvent*)), infoScroll, SLOT(redirectedWheelEvent(QWheelEvent*))); +#ifdef _USE_TRACKINFO_ALT + //connect(trackInfo, SIGNAL(redirectWheelEvent(QWheelEvent*)), infoScroll, SLOT(redirectedWheelEvent(QWheelEvent*))); +#endif egrid->addWidget(time, 0, 0, 1, 2); egrid->addWidget(MusECore::hLine(editor), 1, 0, 1, 2); egrid->addWidget(canvas, 2, 0); egrid->addWidget(vscroll, 2, 1); - egrid->addWidget(hscroll, 3, 0, Qt::AlignBottom); connect(vscroll, SIGNAL(valueChanged(int)), canvas, SLOT(setYPos(int))); connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); @@ -513,13 +564,15 @@ connect(canvas, SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned))); connect(time, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); + connect(list, SIGNAL(verticalScrollSetYpos(int)), vscroll, SLOT(setValue(int))); + connect(canvas, SIGNAL(tracklistChanged()), list, SLOT(tracklistChanged())); connect(canvas, SIGNAL(dclickPart(MusECore::Track*)), SIGNAL(editPart(MusECore::Track*))); connect(canvas, SIGNAL(startEditor(MusECore::PartList*,int)), SIGNAL(startEditor(MusECore::PartList*, int))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); connect(canvas, SIGNAL(followEvent(int)), hscroll, SLOT(setOffset(int))); - connect(canvas, SIGNAL(selectionChanged()), SIGNAL(selectionChanged())); + connect(canvas, SIGNAL(dropSongFile(const QString&)), SIGNAL(dropSongFile(const QString&))); connect(canvas, SIGNAL(dropMidiFile(const QString&)), SIGNAL(dropMidiFile(const QString&))); @@ -527,39 +580,35 @@ connect(MusEGlobal::song, SIGNAL(controllerChanged(MusECore::Track*, int)), SLOT(controllerChanged(MusECore::Track*, int))); configChanged(); // set configuration values +#ifdef _USE_TRACKINFO_ALT if(canvas->part()) midiTrackInfo->setTrack(canvas->part()->track()); +#endif showTrackInfo(showTrackinfoFlag); + setDefaultSplitterSizes(); + // Take care of some tabbies! - setTabOrder(tempo200, trackInfo); - setTabOrder(trackInfo, infoScroll); - setTabOrder(infoScroll, list); +// setTabOrder(tempo200, trackInfo); +// setTabOrder(trackInfo, infoScroll); +// setTabOrder(infoScroll, list); setTabOrder(list, canvas); //setTabOrder(canvas, ib); //setTabOrder(ib, hscroll); } - -// DELETETHIS 20 //--------------------------------------------------------- -// updateHScrollRange +// setDefaultSplitterSizes //--------------------------------------------------------- -//void Arranger::updateHScrollRange() -//{ -// int s = 0, e = MusEGlobal::song->len(); - // Show one more measure. -// e += AL::sigmap.ticksMeasure(e); - // Show another quarter measure due to imprecise drawing at canvas end point. -// e += AL::sigmap.ticksMeasure(e) / 4; - // Compensate for the fixed vscroll width. -// e += canvas->rmapxDev(-vscroll->width()); -// int s1, e1; -// hscroll->range(&s1, &e1); -// if(s != s1 || e != e1) -// hscroll->setRange(s, e); -//} +void Arranger::setDefaultSplitterSizes() +{ + QList vallist; + vallist.append(trackInfoWidget->minimumSize().width()); + vallist.append(tlistLayout->sizeHint().width()); + vallist.append(300); + split->setSizes(vallist); +} //--------------------------------------------------------- // setTime @@ -596,6 +645,25 @@ } //--------------------------------------------------------- +// setHeaderSizes +//--------------------------------------------------------- + +void Arranger::setHeaderSizes() +{ + QFontMetrics fm1(header->font()); + int fw = 11; + header->resizeSection(COL_INPUT_MONITOR, fm1.width(header->columnLabel(COL_INPUT_MONITOR)) + fw); + header->resizeSection(COL_RECORD, fm1.width(header->columnLabel(COL_RECORD)) + fw); + header->resizeSection(COL_MUTE, fm1.width(header->columnLabel(COL_MUTE)) + fw); + header->resizeSection(COL_SOLO, fm1.width(header->columnLabel(COL_SOLO)) + fw); + header->resizeSection(COL_CLASS, fm1.width(header->columnLabel(COL_CLASS)) + fw); + header->resizeSection(COL_OCHANNEL, fm1.width(header->columnLabel(COL_OCHANNEL)) + fw); + header->resizeSection(COL_TIMELOCK, fm1.width(header->columnLabel(COL_TIMELOCK)) + fw); + for (unsigned i=0;iresizeSection(COL_CUSTOM_MIDICTRL_OFFSET+i, MAX(fm1.width(custom_columns[i].name)+fw, 30)); +} + +//--------------------------------------------------------- // configChanged //--------------------------------------------------------- @@ -608,6 +676,7 @@ else { canvas->setBg(QPixmap(MusEGlobal::config.canvasBgPixmap)); } + setHeaderSizes(); } //--------------------------------------------------------- @@ -632,17 +701,98 @@ int newLen = AL::sigmap.bar2tick(n, 0, 0); MusEGlobal::song->setLen(newLen); } + //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void Arranger::songChanged(MusECore::SongChangedFlags_t type) { - // Is it simply a midi controller value adjustment? Forget it. - if(type != SC_MIDI_CONTROLLER) - { - // TEST p4.0.36 Try these, may need more/less. +#ifdef _USE_TRACKINFO_ALT + // We must catch this first and be sure to update the strips. + if(type & SC_TRACK_REMOVED) + { + { + AudioStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if(w) + { + MusECore::Track* t = w->getTrack(); + if(t) + { + if(!MusEGlobal::song->trackExists(t)) + { + delete w; + trackInfoWidget->addWidget(0, 2); + selected = 0; + switchInfo(0); + } + } + } + } + + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(3)); + if(w) + { + MusECore::Track* t = w->getTrack(); + if(t) + { + if(!MusEGlobal::song->trackExists(t)) + { + delete w; + trackInfoWidget->addWidget(0, 3); + selected = 0; + switchInfo(0); + } + } + } + } + } +#else + // We must catch this first and be sure to update the strips. + if(type & SC_TRACK_REMOVED) + { + { + AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); + if(w) + { + MusECore::Track* t = w->getTrack(); + if(t) + { + if(!MusEGlobal::song->trackExists(t)) + { + delete w; + trackInfoWidget->addWidget(0, 1); + selected = 0; + switchInfo(0); + } + } + } + } + + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if(w) + { + MusECore::Track* t = w->getTrack(); + if(t) + { + if(!MusEGlobal::song->trackExists(t)) + { + delete w; + trackInfoWidget->addWidget(0, 2); + selected = 0; + switchInfo(0); + } + } + } + } + } +#endif + + // Try these, may need more/less. if(type & ( SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | + SC_TRACK_MOVED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED)) { unsigned endTick = MusEGlobal::song->len(); @@ -661,51 +811,37 @@ lenEntry->blockSignals(false); } - if(type & (SC_SELECTION | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED)) + if(type & (SC_TRACK_SELECTION | SC_TRACK_INSERTED | SC_TRACK_REMOVED | + SC_TRACK_MOVED | + SC_TRACK_MODIFIED | SC_TRACK_RESIZED)) trackSelectionChanged(); - // 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 | + // Keep this light, partsChanged is a heavy move! Try these, may need more. Maybe sig. Requires tempo. + if(type & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | + SC_TRACK_MOVED | SC_TRACK_RESIZED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | - SC_SIG | SC_TEMPO | SC_MASTER)) // Maybe sig. Requires tempo. + SC_SIG | SC_TEMPO | SC_MASTER)) canvas->partsChanged(); if (type & SC_SIG) time->redraw(); if (type & SC_TEMPO) setGlobalTempo(MusEGlobal::tempomap.globalTempo()); - - if(type & SC_TRACK_REMOVED) - { - AudioStrip* w = (AudioStrip*)(trackInfo->getWidget(2)); - if(w) - { - MusECore::Track* t = w->getTrack(); - if(t) - { - MusECore::TrackList* tl = MusEGlobal::song->tracks(); - MusECore::iTrack it = tl->find(t); - if(it == tl->end()) - { - delete w; - trackInfo->addWidget(0, 2); - //trackInfo->insertWidget(2, 0); - selected = 0; - } - } - } - } - - // TEST p4.0.36 Try this DELETETHIS and below and even more below - if(type & ( //SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | - SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | - SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED)) //| - //SC_SIG | SC_TEMPO)) // Maybe sig. and tempo. No, moved above. + + // Try these: + if(type & (SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | + SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED | + SC_CLIP_MODIFIED)) canvas->redraw(); - } - - updateTrackInfo(type); + // We must marshall song changed instead of connecting to the strip's song changed + // otherwise it crashes when loading another song because track is no longer valid + // and the strip's songChanged() seems to be called before Arranger songChanged() + // gets called and has a chance to stop the crash. + // Also, calling updateTrackInfo() from here is too heavy, it destroys and recreates + // the strips each time no matter what the flags are ! + //updateTrackInfo(type); + trackInfoSongChange(type); } //--------------------------------------------------------- @@ -714,14 +850,7 @@ void Arranger::trackSelectionChanged() { - MusECore::TrackList* tracks = MusEGlobal::song->tracks(); - MusECore::Track* track = 0; - for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) { - if ((*t)->selected()) { - track = *t; - break; - } - } + MusECore::Track* track = MusEGlobal::song->selectedTrack(); if (track == selected) return; selected = track; @@ -736,11 +865,11 @@ { xml.tag(level++, "arranger"); xml.intTag(level, "raster", _raster); - xml.intTag(level, "info", ib->isChecked()); + xml.intTag(level, "info", trackInfoButton->isChecked()); split->writeStatus(level, xml); - xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "xmag", hscroll->mag()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ypos", vscroll->value()); xml.etag(level, "arranger"); } @@ -768,7 +897,14 @@ return; case MusECore::Xml::TagStart: if (tag == "tlist_header") - header_state = QByteArray::fromHex(xml.parse1().toAscii()); + { + // We can only restore the header state with version-compatible data. + // If columns were altered, 'alien' loaded data will not fit! + if(xml.isVersionEqualToLatest()) + header_state = QByteArray::fromHex(xml.parse1().toLatin1()); + else + xml.parse1(); + } else if (tag == "custom_columns") readCustomColumns(xml); else @@ -802,14 +938,13 @@ rast = xml.parseInt(); else if (tag == "info") showTrackinfoFlag = xml.parseInt(); - else if (tag == split->objectName()) + else if (tag == split->objectName()) { split->readStatus(xml); + } else if (tag == "xmag") hscroll->setMag(xml.parseInt()); - else if (tag == "xpos") { - int hpos = xml.parseInt(); - hscroll->setPos(hpos); - } + else if (tag == "xpos") + hscroll->setPos(xml.parseInt()); else if (tag == "ypos") vscroll->setValue(xml.parseInt()); else @@ -817,7 +952,7 @@ break; case MusECore::Xml::TagEnd: if (tag == "arranger") { - ib->setChecked(showTrackinfoFlag); + trackInfoButton->setChecked(showTrackinfoFlag); if(rast != -1) setRasterVal(rast); return; @@ -936,7 +1071,6 @@ void Arranger::globalTempoChanged(int val) { MusEGlobal::audio->msgSetGlobalTempo(val); - MusEGlobal::song->tempoChanged(); } //--------------------------------------------------------- @@ -945,7 +1079,7 @@ void Arranger::setTempo50() { - setGlobalTempo(50); + MusEGlobal::audio->msgSetGlobalTempo(50); } //--------------------------------------------------------- @@ -954,7 +1088,7 @@ void Arranger::setTempo100() { - setGlobalTempo(100); + MusEGlobal::audio->msgSetGlobalTempo(100); } //--------------------------------------------------------- @@ -963,7 +1097,7 @@ void Arranger::setTempo200() { - setGlobalTempo(200); + MusEGlobal::audio->msgSetGlobalTempo(200); } //--------------------------------------------------------- @@ -973,7 +1107,11 @@ void Arranger::setGlobalTempo(int val) { if(val != globalTempoSpinBox->value()) + { + globalTempoSpinBox->blockSignals(true); globalTempoSpinBox->setValue(val); + globalTempoSpinBox->blockSignals(false); + } } //--------------------------------------------------------- @@ -985,121 +1123,45 @@ } //--------------------------------------------------------- -// trackInfoScroll -//--------------------------------------------------------- - -void Arranger::trackInfoScroll(int y) - { - if (trackInfo->visibleWidget()) - trackInfo->visibleWidget()->move(0, -y); - } - -//--------------------------------------------------------- -// WidgetStack -//--------------------------------------------------------- - -WidgetStack::WidgetStack(QWidget* parent, const char* name) - : QWidget(parent) - { - setObjectName(name); - top = -1; - } - -//--------------------------------------------------------- -// raiseWidget -//--------------------------------------------------------- - -void WidgetStack::raiseWidget(int idx) - { - if (top != -1) { - if (stack[top]) - stack[top]->hide(); - } - top = idx; - if (idx == -1) - return; - int n = stack.size(); - if (idx >= n) - return; - if (stack[idx]) - stack[idx]->show(); - } - -//--------------------------------------------------------- -// addWidget +// clear //--------------------------------------------------------- -void WidgetStack::addWidget(QWidget* w, unsigned int n) +void Arranger::clear() { - if (w) - w->hide(); - if (stack.size() <= n ) - stack.push_back(w); - else - stack[n] = w; - } - -QWidget* WidgetStack::getWidget(unsigned int n) +#ifdef _USE_TRACKINFO_ALT { - if (stack.size() <= n ) - return 0; - return stack[n]; + AudioStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if (w) + delete w; + trackInfoWidget->addWidget(0, 2); } - -//--------------------------------------------------------- -// visibleWidget -//--------------------------------------------------------- - -QWidget* WidgetStack::visibleWidget() const + { - if (top != -1) - return stack[top]; - return 0; + MidiStrip* w = static_cast(trackInfoWidget->getWidget(3)); + if (w) + delete w; + trackInfoWidget->addWidget(0, 3); } - -//--------------------------------------------------------- -// minimumSizeHint -//--------------------------------------------------------- - -QSize WidgetStack::minimumSizeHint() const + + selected = 0; + midiTrackInfo->setTrack(0); +#else { - if (top == -1) - return (QSize(0, 0)); - - QSize s(0,0); - for (unsigned int i = 0; i < stack.size(); ++i) { - if (stack[i]) { - QSize ss = stack[i]->minimumSizeHint(); - if (!ss.isValid()) - ss = stack[i]->minimumSize(); - s = s.expandedTo(ss); - } - } - - return s; + AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); + if (w) + delete w; + trackInfoWidget->addWidget(0, 1); } - -//--------------------------------------------------------- -// wheelEvent -//--------------------------------------------------------- - -void WidgetStack::wheelEvent(QWheelEvent* ev) + { - emit redirectWheelEvent(ev); + MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if (w) + delete w; + trackInfoWidget->addWidget(0, 2); } - -//--------------------------------------------------------- -// clear -//--------------------------------------------------------- - -void Arranger::clear() - { - AudioStrip* w = (AudioStrip*)(trackInfo->getWidget(2)); - if (w) - delete w; - trackInfo->addWidget(0, 2); + selected = 0; - midiTrackInfo->setTrack(0); +#endif } //void Arranger::wheelEvent(QWheelEvent* ev) @@ -1119,19 +1181,28 @@ void Arranger::showTrackInfo(bool flag) { showTrackinfoFlag = flag; - trackInfo->setVisible(flag); - infoScroll->setVisible(flag); + trackInfoWidget->setVisible(flag); updateTrackInfo(-1); } +#ifdef _USE_TRACKINFO_ALT //--------------------------------------------------------- -// genTrackInfo +// showTrackInfoAlt //--------------------------------------------------------- -void Arranger::genTrackInfo(QWidget* parent) +void Arranger::showTrackInfoAlt(bool flag) { - trackInfo = new WidgetStack(parent, "trackInfoStack"); + showTrackinfoAltFlag = flag; + updateTrackInfo(-1); + } +#endif +//--------------------------------------------------------- +// genTrackInfo +//--------------------------------------------------------- + +void Arranger::genTrackInfo(TrackInfoWidget* trackInfo) + { noTrackInfo = new QWidget(trackInfo); noTrackInfo->setAutoFillBackground(true); QPixmap *noInfoPix = new QPixmap(160, 1000); @@ -1146,18 +1217,25 @@ noTrackInfo->setGeometry(0, 0, 65, 200); noTrackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding)); +#ifdef _USE_TRACKINFO_ALT midiTrackInfo = new MidiTrackInfo(trackInfo); - trackInfo->addWidget(noTrackInfo, 0); + trackInfo->addWidget(noTrackInfo, 0); trackInfo->addWidget(midiTrackInfo, 1); - trackInfo->addWidget(0, 2); + trackInfo->addWidget(0, 2); // AudioStrip placeholder. + trackInfo->addWidget(0, 3); // MidiStrip placeholder. +#else + trackInfo->addWidget(noTrackInfo, 0); + trackInfo->addWidget(0, 1); // AudioStrip placeholder. + trackInfo->addWidget(0, 2); // MidiStrip placeholder. +#endif } //--------------------------------------------------------- // updateTrackInfo //--------------------------------------------------------- -void Arranger::updateTrackInfo(MusECore::SongChangedFlags_t flags) +void Arranger::updateTrackInfo(MusECore::SongChangedFlags_t /*flags*/) { if (!showTrackinfoFlag) { switchInfo(-1); @@ -1168,17 +1246,30 @@ return; } if (selected->isMidiTrack()) { - switchInfo(1); - // If a different part was selected - if(midiTrackInfo->track() != selected) - // Set a new track and do a complete update. - midiTrackInfo->setTrack(selected); - else - // Otherwise just regular update with specific flags. - midiTrackInfo->updateTrackInfo(flags); +#ifdef _USE_TRACKINFO_ALT + if(showTrackinfoAltFlag) + { + switchInfo(1); + // If a different part was selected + if(midiTrackInfo->track() != selected) + // Set a new track and do a complete update. + midiTrackInfo->setTrack(selected); + else + // Otherwise just regular update with specific flags. + midiTrackInfo->updateTrackInfo(flags); + } + else + switchInfo(3); +#else + switchInfo(2); +#endif } else { +#ifdef _USE_TRACKINFO_ALT switchInfo(2); +#else + switchInfo(1); +#endif } } @@ -1186,45 +1277,252 @@ // switchInfo //--------------------------------------------------------- +#ifdef _USE_TRACKINFO_ALT void Arranger::switchInfo(int n) { if (n == 2) { - AudioStrip* w = (AudioStrip*)(trackInfo->getWidget(2)); + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(3)); + if (w) + { + //fprintf(stderr, "Arranger::switchInfo audio strip: deleting midi strip\n"); + delete w; + //w->deleteLater(); + trackInfoWidget->addWidget(0, 3); + } + } + { + AudioStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if (w == 0 || selected != w->getTrack()) { + if (w) + { + //fprintf(stderr, "Arranger::switchInfo deleting strip\n"); + delete w; + //w->deleteLater(); + } + w = new AudioStrip(trackInfoWidget, static_cast(selected)); + // Broadcast changes to other selected tracks. + w->setBroadcastChanges(true); + + // Set focus yielding to the canvas. + if(MusEGlobal::config.smartFocus) + { + w->setFocusYieldWidget(canvas); + //w->setFocusPolicy(Qt::WheelFocus); + } + + // We must marshall song changed instead of connecting to the strip's song changed + // otherwise it crashes when loading another song because track is no longer valid + // and the strip's songChanged() seems to be called before Arranger songChanged() + // gets called and has a chance to stop the crash. + //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), w, SLOT(songChanged(MusECore::SongChangedFlags_t))); + + connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged())); + w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); + trackInfoWidget->addWidget(w, 2); + w->show(); + //setTabOrder(midiTrackInfo, w); + } + } + } + + else if (n == 3) { + { + AudioStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if (w) + { + //fprintf(stderr, "Arranger::switchInfo midi strip: deleting audio strip\n"); + delete w; + //w->deleteLater(); + trackInfoWidget->addWidget(0, 2); + } + } + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(3)); if (w == 0 || selected != w->getTrack()) { if (w) + { + //fprintf(stderr, "Arranger::switchInfo deleting strip\n"); delete w; - w = new AudioStrip(trackInfo, (MusECore::AudioTrack*)selected); - //w->setFocusPolicy(Qt::TabFocus); // p4.0.9 - connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), w, SLOT(songChanged(MusECore::SongChangedFlags_t))); + //w->deleteLater(); + } + w = new MidiStrip(trackInfoWidget, static_cast(selected)); + // Broadcast changes to other selected tracks. + w->setBroadcastChanges(true); + // Set focus yielding to the arranger canvas. + if(MusEGlobal::config.smartFocus) + { + w->setFocusYieldWidget(canvas); + //w->setFocusPolicy(Qt::WheelFocus); + } + + // No. See above. + //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), w, SLOT(songChanged(MusECore::SongChangedFlags_t))); + connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged())); - w->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); - trackInfo->addWidget(w, 2); + w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); + trackInfoWidget->addWidget(w, 3); w->show(); - //setTabOrder(midiTrackInfo, w); // p4.0.9 - tgrid->activate(); - tgrid->update(); // muse-2 Qt4 + //setTabOrder(midiTrackInfo, w); } + } + } + + if (trackInfoWidget->curIdx() == n) + return; + trackInfoWidget->raiseWidget(n); + } +#else +void Arranger::switchInfo(int n) + { + if (n == 1) { + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if (w) + { + //fprintf(stderr, "Arranger::switchInfo audio strip: deleting midi strip\n"); + delete w; + //w->deleteLater(); + trackInfoWidget->addWidget(0, 2); } - if (trackInfo->curIdx() == n) + } + { + AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); + if (w == 0 || selected != w->getTrack()) { + if (w) + { + //fprintf(stderr, "Arranger::switchInfo deleting strip\n"); + delete w; + //w->deleteLater(); + } + w = new AudioStrip(trackInfoWidget, static_cast(selected)); + // Broadcast changes to other selected tracks. + w->setBroadcastChanges(true); + + // Set focus yielding to the canvas. + if(MusEGlobal::config.smartFocus) + { + w->setFocusYieldWidget(canvas); + //w->setFocusPolicy(Qt::WheelFocus); + } + + // We must marshall song changed instead of connecting to the strip's song changed + // otherwise it crashes when loading another song because track is no longer valid + // and the strip's songChanged() seems to be called before Arranger songChanged() + // gets called and has a chance to stop the crash. + //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), w, SLOT(songChanged(MusECore::SongChangedFlags_t))); + + connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged())); + w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); + trackInfoWidget->addWidget(w, 1); + w->show(); + //setTabOrder(midiTrackInfo, w); + } + } + } + + else if (n == 2) { + { + AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); + if (w) + { + //fprintf(stderr, "Arranger::switchInfo midi strip: deleting audio strip\n"); + delete w; + //w->deleteLater(); + trackInfoWidget->addWidget(0, 1); + } + } + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if (w == 0 || selected != w->getTrack()) { + if (w) + { + //fprintf(stderr, "Arranger::switchInfo deleting strip\n"); + delete w; + //w->deleteLater(); + } + w = new MidiStrip(trackInfoWidget, static_cast(selected)); + // Broadcast changes to other selected tracks. + w->setBroadcastChanges(true); + // Set focus yielding to the arranger canvas. + if(MusEGlobal::config.smartFocus) + { + w->setFocusYieldWidget(canvas); + //w->setFocusPolicy(Qt::WheelFocus); + } + + // No. See above. + //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), w, SLOT(songChanged(MusECore::SongChangedFlags_t))); + + connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged())); + w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); + trackInfoWidget->addWidget(w, 2); + w->show(); + //setTabOrder(midiTrackInfo, w); + } + } + } + + if (trackInfoWidget->curIdx() == n) return; - trackInfo->raiseWidget(n); - tgrid->activate(); - tgrid->update(); // muse-2 Qt4 + trackInfoWidget->raiseWidget(n); } +#endif + +//--------------------------------------------------------- +// trackInfoSongChange +//--------------------------------------------------------- + +#ifdef _USE_TRACKINFO_ALT +void Arranger::trackInfoSongChange(MusECore::SongChangedFlags_t flags) +{ + if(!selected || !showTrackinfoFlag) + return; -/* DELETETHIS 12 -QSize WidgetStack::minimumSize() const -{ - printf("WidgetStack::minimumSize\n"); - return minimumSizeHint(); + // Only update what is showing. + if(selected->isMidiTrack()) + { + if(showTrackinfoAltFlag) + { + MidiTrackInfo* w = static_cast(trackInfoWidget->getWidget(1)); + if(w) + w->songChanged(flags); + } + else + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(3)); + if(w) + w->songChanged(flags); + } + } + else + { + AudioStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if(w) + w->songChanged(flags); + } } +#else +void Arranger::trackInfoSongChange(MusECore::SongChangedFlags_t flags) +{ + if(!selected || !showTrackinfoFlag) + return; -int WidgetStack::minimumHeight() const -{ - printf("WidgetStack::minimumHeight\n"); - return minimumSizeHint().height(); + // Only update what is showing. + if(selected->isMidiTrack()) + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); + if(w) + w->songChanged(flags); + } + else + { + AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); + if(w) + w->songChanged(flags); + } } -*/ +#endif void Arranger::keyPressEvent(QKeyEvent* event) { @@ -1278,4 +1576,5 @@ hscroll->setMag(hscroll->mag() + mag, cp.x()); } + } // namespace MusEGui diff -Nru muse-2.1.2/muse/arranger/arranger.h muse-3.0.2+ds1/muse/arranger/arranger.h --- muse-2.1.2/muse/arranger/arranger.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/arranger.h 2017-12-04 21:01:18.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: arranger.h,v 1.17.2.15 2009/11/14 03:37:48 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -26,23 +27,27 @@ #include #include -#include -#include - #include "type_defs.h" #include "midieditor.h" #include "pcanvas.h" #include "trackautomationview.h" +// Whether to show an additional 'Alt' button beside +// the trackinfo button, to show the old midi trackinfo panel. +// TODO: TO BE REMOVED. The midi trackinfo panel is obsolete and disabled now. +//#define _USE_TRACKINFO_ALT 1; + class QAction; class QCheckBox; class QMainWindow; class QMenu; -class QToolButton; class QWheelEvent; class QKeyEvent; class QPoint; class QComboBox; +class QScrollBar; +class QVBoxLayout; +class QHBoxLayout; namespace MusECore { class Track; @@ -55,58 +60,18 @@ class Header; class LabelCombo; class MTScale; -class MidiTrackInfo; class PosLabel; class ScrollScale; class SpinBox; class Splitter; -class TLLayout; +class TrackInfoWidget; class TList; -class WidgetStack; - - -//--------------------------------------------------------- -// WidgetStack -//--------------------------------------------------------- - -class WidgetStack : public QWidget { - Q_OBJECT - std::vector stack; - int top; - - protected: - virtual void wheelEvent(QWheelEvent* e); - - signals: - void redirectWheelEvent(QWheelEvent*); - - public: - WidgetStack(QWidget* parent, const char* name = 0); - void raiseWidget(int idx); - void addWidget(QWidget* w, unsigned int idx); - QWidget* getWidget(unsigned int idx); - QWidget* visibleWidget() const; - int curIdx() const { return top; } - virtual QSize minimumSizeHint() const; - }; - -//--------------------------------------------------------- -// ScrollBar -//--------------------------------------------------------- - -class ScrollBar : public QScrollBar { - Q_OBJECT - - public slots: - void redirectedWheelEvent(QWheelEvent*); - - protected: - virtual void resizeEvent(QResizeEvent* e) { setPageStep(e->size().height()); } - - public: - ScrollBar(Qt::Orientation orientation, QWidget * parent = 0 ) : QScrollBar(orientation, parent) {}; -}; - +class ArrangerCanvasLayout; +class ArrangerHScrollLayout; +class CompactToolButton; +#ifdef _USE_TRACKINFO_ALT +class MidiTrackInfo; +#endif //--------------------------------------------------------- // Arranger @@ -124,22 +89,31 @@ PartCanvas* canvas; ScrollScale* hscroll; QScrollBar* vscroll; + QVBoxLayout* tlistLayout; + ArrangerCanvasLayout* egrid; + ArrangerHScrollLayout* bottomHLayout; TList* list; Header* header; MTScale* time; SpinBox* lenEntry; bool showTrackinfoFlag; - WidgetStack* trackInfo; - ScrollBar* infoScroll; +#ifdef _USE_TRACKINFO_ALT + bool showTrackinfoAltFlag; +#endif + TrackInfoWidget* trackInfoWidget; +#ifdef _USE_TRACKINFO_ALT MidiTrackInfo* midiTrackInfo; +#endif AudioStrip* waveTrackInfo; QWidget* noTrackInfo; QWidget* tracklist; - TLLayout* tgrid; MusECore::Track* selected; - QToolButton* ib; + CompactToolButton* trackInfoButton; +#ifdef _USE_TRACKINFO_ALT + CompactToolButton* trackInfoAltButton; +#endif int trackInfoType; Splitter* split; int songType; @@ -148,19 +122,23 @@ SpinBox* globalPitchSpinBox; unsigned cursVal; - void genTrackInfo(QWidget* parent); + void genTrackInfo(TrackInfoWidget*); void genMidiTrackInfo(); void genWaveTrackInfo(); void switchInfo(int); + void trackInfoSongChange(MusECore::SongChangedFlags_t flags); void setHeaderToolTips(); void setHeaderWhatsThis(); + void setHeaderSizes(); private slots: void rasterChanged(int); void songlenChanged(int); void showTrackInfo(bool); +#ifdef _USE_TRACKINFO_ALT + void showTrackInfoAlt(bool); +#endif void trackSelectionChanged(); - void trackInfoScroll(int); void songChanged(MusECore::SongChangedFlags_t); void setTime(unsigned); void globalPitchChanged(int); @@ -174,7 +152,6 @@ signals: void editPart(MusECore::Track*); - void selectionChanged(); void dropSongFile(const QString&); void dropMidiFile(const QString&); void startEditor(MusECore::PartList*, int); @@ -230,17 +207,21 @@ MusECore::Track* curTrack() const { return selected; } void cmd(int); - bool isSingleSelection() { return canvas->isSingleSelection(); } - int selectionSize() { return canvas->selectionSize(); } + bool isSingleSelection() const { return canvas->isSingleSelection(); } + int selectionSize() const { return canvas->selectionSize(); } + bool itemsAreSelected() const { return canvas->itemsAreSelected(); } void setGlobalTempo(int); void clear(); void songIsClearing() { canvas->songIsClearing(); } + void setDefaultSplitterSizes(); unsigned cursorValue() { return cursVal; } ArrangerView* parentWin() const { return _parentWin; } bool setRasterVal(int); + + TList *getTrackList() { return list; } }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/arranger/arrangerview.cpp muse-3.0.2+ds1/muse/arranger/arrangerview.cpp --- muse-2.1.2/muse/arranger/arrangerview.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/arrangerview.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -67,6 +67,7 @@ #include "visibletracks.h" #include "xml.h" #include "arrangercolumns.h" +#include "tlist.h" namespace MusEGui { @@ -88,21 +89,27 @@ scoreAllInOneMapper = new QSignalMapper(this); editSignalMapper = new QSignalMapper(this); - QShortcut* sc = new QShortcut(shortcuts[SHRT_DELETE].key, this); - sc->setContext(Qt::WindowShortcut); - connect(sc, SIGNAL(activated()), editSignalMapper, SLOT(map())); - editSignalMapper->setMapping(sc, CMD_DELETE); // Toolbars --------------------------------------------------------- + + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + + addToolBarBreak(); + editTools = new EditToolBar(this, arrangerTools); addToolBar(editTools); + // Make sure name doesn't conflict with other TopWin edit toolbar object names. editTools->setObjectName("arrangerTools"); + // Already has an object name. visTracks = new VisibleTracks(this); addToolBar(visTracks); - - connect(editTools, SIGNAL(toolChanged(int)), arranger, SLOT(setTool(int))); connect(visTracks, SIGNAL(visibilityChanged()), MusEGlobal::song, SLOT(update()) ); connect(arranger, SIGNAL(editPart(MusECore::Track*)), MusEGlobal::muse, SLOT(startEditor(MusECore::Track*))); @@ -112,7 +119,6 @@ connect(arranger, SIGNAL(toolChanged(int)), editTools, SLOT(set(int))); connect(MusEGlobal::muse, SIGNAL(configChanged()), arranger, SLOT(configChanged())); connect(arranger, SIGNAL(setUsedTool(int)), editTools, SLOT(set(int))); - connect(arranger, SIGNAL(selectionChanged()), SLOT(selectionChanged())); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), this, SLOT(songChanged(MusECore::SongChangedFlags_t))); @@ -121,6 +127,7 @@ //-------- Edit Actions + editDeleteAction = new QAction(QIcon(*deleteIcon), tr("D&elete"), this); editCutAction = new QAction(QIcon(*editcutIconSet), tr("C&ut"), this); editCopyAction = new QAction(QIcon(*editcopyIconSet), tr("&Copy"), this); editCopyRangeAction = new QAction(QIcon(*editcopyIconSet), tr("Copy in range"), this); @@ -194,6 +201,7 @@ menuEdit->addActions(MusEGlobal::undoRedo->actions()); menuEdit->addSeparator(); + menuEdit->addAction(editDeleteAction); menuEdit->addAction(editCutAction); menuEdit->addAction(editCopyAction); menuEdit->addAction(editCopyRangeAction); @@ -279,6 +287,7 @@ //-------- Edit connections + connect(editDeleteAction, SIGNAL(triggered()), editSignalMapper, SLOT(map())); connect(editCutAction, SIGNAL(triggered()), editSignalMapper, SLOT(map())); connect(editCopyAction, SIGNAL(triggered()), editSignalMapper, SLOT(map())); connect(editCopyRangeAction, SIGNAL(triggered()), editSignalMapper, SLOT(map())); @@ -302,6 +311,7 @@ connect(editOutsideLoopAction, SIGNAL(triggered()), editSignalMapper, SLOT(map())); connect(editAllPartsAction, SIGNAL(triggered()), editSignalMapper, SLOT(map())); + editSignalMapper->setMapping(editDeleteAction, CMD_DELETE); editSignalMapper->setMapping(editCutAction, CMD_CUT); editSignalMapper->setMapping(editCopyAction, CMD_COPY); editSignalMapper->setMapping(editCopyRangeAction, CMD_COPY_RANGE); @@ -325,28 +335,28 @@ connect(editSignalMapper, SIGNAL(mapped(int)), this, SLOT(cmd(int))); - connect(startPianoEditAction, SIGNAL(activated()), MusEGlobal::muse, SLOT(startPianoroll())); - connect(startScoreEditAction, SIGNAL(activated()), MusEGlobal::muse, SLOT(startScoreQuickly())); - connect(startDrumEditAction, SIGNAL(activated()), MusEGlobal::muse, SLOT(startDrumEditor())); - connect(startListEditAction, SIGNAL(activated()), MusEGlobal::muse, SLOT(startListEditor())); - connect(startWaveEditAction, SIGNAL(activated()), MusEGlobal::muse, SLOT(startWaveEditor())); + connect(startPianoEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startPianoroll())); + connect(startScoreEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startScoreQuickly())); + connect(startDrumEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startDrumEditor())); + connect(startListEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startListEditor())); + connect(startWaveEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startWaveEditor())); connect(scoreOneStaffPerTrackMapper, SIGNAL(mapped(QWidget*)), MusEGlobal::muse, SLOT(openInScoreEdit_oneStaffPerTrack(QWidget*))); connect(scoreAllInOneMapper, SIGNAL(mapped(QWidget*)), MusEGlobal::muse, SLOT(openInScoreEdit_allInOne(QWidget*))); - connect(masterGraphicAction, SIGNAL(activated()), MusEGlobal::muse, SLOT(startMasterEditor())); - connect(masterListAction, SIGNAL(activated()), MusEGlobal::muse, SLOT(startLMasterEditor())); + connect(masterGraphicAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startMasterEditor())); + connect(masterListAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startLMasterEditor())); - connect(midiTransformerAction, SIGNAL(activated()), MusEGlobal::muse, SLOT(startMidiTransformer())); + connect(midiTransformerAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startMidiTransformer())); //-------- Structure connections - connect(strGlobalCutAction, SIGNAL(activated()), SLOT(globalCut())); - connect(strGlobalInsertAction, SIGNAL(activated()), SLOT(globalInsert())); - connect(strGlobalSplitAction, SIGNAL(activated()), SLOT(globalSplit())); - connect(strGlobalCutSelAction, SIGNAL(activated()), SLOT(globalCutSel())); - connect(strGlobalInsertSelAction, SIGNAL(activated()), SLOT(globalInsertSel())); - connect(strGlobalSplitSelAction, SIGNAL(activated()), SLOT(globalSplitSel())); + connect(strGlobalCutAction, SIGNAL(triggered()), SLOT(globalCut())); + connect(strGlobalInsertAction, SIGNAL(triggered()), SLOT(globalInsert())); + connect(strGlobalSplitAction, SIGNAL(triggered()), SLOT(globalSplit())); + connect(strGlobalCutSelAction, SIGNAL(triggered()), SLOT(globalCutSel())); + connect(strGlobalInsertSelAction, SIGNAL(triggered()), SLOT(globalInsertSel())); + connect(strGlobalSplitSelAction, SIGNAL(triggered()), SLOT(globalSplitSel())); @@ -358,15 +368,6 @@ connect(cb, SIGNAL(selectionChanged()), SLOT(clipboardChanged())); finalizeInit(); - - // work around for probable QT/WM interaction bug. - // for certain window managers, e.g xfce, this window is - // is displayed although not specifically set to show(); - // bug: 2811156 Softsynth GUI unclosable with XFCE4 (and a few others) - // Nov 21, 2012 Hey this causes the thing not to open at all, EVER, on Lubuntu and some others! - // And we had a request to remove this from a knowledgable tester. REMOVE Tim. - ///show(); - ///hide(); } ArrangerView::~ArrangerView() @@ -396,6 +397,11 @@ SC_CONFIG | SC_DRUMMAP)) visTracks->updateVisibleTracksButtons(); + + if(type & (SC_TRACK_SELECTION | SC_PART_SELECTION | + SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | + SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED)) + selectionChanged(); } @@ -486,6 +492,11 @@ void ArrangerView::cmd(int cmd) { + // Don't process if user is dragging or has clicked on the parts. + // Causes problems/crashes later in Canvas::viewMouseMoveEvent and viewMouseReleaseEvent. + if(arranger && arranger->getCanvas() && arranger->getCanvas()->getCurrentDrag()) + return; + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); int l = MusEGlobal::song->lpos(); int r = MusEGlobal::song->rpos(); @@ -519,26 +530,21 @@ arranger->cmd(Arranger::CMD_INSERT_EMPTYMEAS); break; case CMD_DELETE: - if (!MusEGlobal::song->msgRemoveParts()) //automatically does undo if neccessary and returns true then + if (!MusECore::delete_selected_parts()) { QMessageBox::StandardButton btn = QMessageBox::warning( this,tr("Remove track(s)"),tr("Are you sure you want to remove this track(s)?"), QMessageBox::Ok|QMessageBox::Cancel, QMessageBox::Ok); if (btn == QMessageBox::Cancel) - break; //msgRemoveParts() returned false -> no parts to remove? - MusEGlobal::song->startUndo(); - MusEGlobal::audio->msgRemoveTracks(); //TODO FINDME this could still be speeded up! - MusEGlobal::song->endUndo(SC_TRACK_REMOVED); + break; + MusEGlobal::audio->msgRemoveTracks(); } break; case CMD_DELETE_TRACK: // from menu { - MusEGlobal::song->startUndo(); MusEGlobal::audio->msgRemoveTracks(); - MusEGlobal::song->endUndo(SC_TRACK_REMOVED); - MusEGlobal::audio->msgUpdateSoloStates(); } break; @@ -628,13 +634,13 @@ action=new QAction(tr("New"), this); - connect(action, SIGNAL(activated()), scoreOneStaffPerTrackMapper, SLOT(map())); + connect(action, SIGNAL(triggered()), scoreOneStaffPerTrackMapper, SLOT(map())); scoreOneStaffPerTrackMapper->setMapping(action, (QWidget*)NULL); scoreOneStaffPerTrackSubsubmenu->addAction(action); action=new QAction(tr("New"), this); //the above action may NOT be reused! - connect(action, SIGNAL(activated()), scoreAllInOneMapper, SLOT(map())); + connect(action, SIGNAL(triggered()), scoreAllInOneMapper, SLOT(map())); scoreAllInOneMapper->setMapping(action, (QWidget*)NULL); scoreAllInOneSubsubmenu->addAction(action); @@ -646,13 +652,13 @@ ScoreEdit* score = dynamic_cast(*it); action=new QAction(score->get_name(), this); - connect(action, SIGNAL(activated()), scoreOneStaffPerTrackMapper, SLOT(map())); + connect(action, SIGNAL(triggered()), scoreOneStaffPerTrackMapper, SLOT(map())); scoreOneStaffPerTrackMapper->setMapping(action, (QWidget*)score); scoreOneStaffPerTrackSubsubmenu->addAction(action); action=new QAction(score->get_name(), this); //the above action may NOT be reused! - connect(action, SIGNAL(activated()), scoreAllInOneMapper, SLOT(map())); + connect(action, SIGNAL(triggered()), scoreAllInOneMapper, SLOT(map())); scoreAllInOneMapper->setMapping(action, (QWidget*)score); scoreAllInOneSubsubmenu->addAction(action); } @@ -675,14 +681,17 @@ QActionGroup *grp = MusEGui::populateAddTrack(addTrack, true, true); connect(addTrack, SIGNAL(triggered(QAction *)), SLOT(addNewTrack(QAction *))); - trackMidiAction = grp->actions()[0]; - trackDrumAction = grp->actions()[1]; - trackNewStyleDrumAction = grp->actions()[2]; - trackWaveAction = grp->actions()[3]; - trackAOutputAction = grp->actions()[4]; - trackAGroupAction = grp->actions()[5]; - trackAInputAction = grp->actions()[6]; - trackAAuxAction = grp->actions()[7]; + int idx = 0; + trackMidiAction = grp->actions()[idx++]; + trackDrumAction = grp->actions()[idx++]; + trackWaveAction = grp->actions()[idx++]; + trackAOutputAction = grp->actions()[idx++]; + trackAGroupAction = grp->actions()[idx++]; + trackAInputAction = grp->actions()[idx++]; + trackAAuxAction = grp->actions()[idx++]; + + + arranger->getTrackList()->populateAddTrack(); } void ArrangerView::addNewTrack(QAction* action) @@ -692,6 +701,7 @@ void ArrangerView::updateShortcuts() { + editDeleteAction->setShortcut(shortcuts[SHRT_DELETE].key); editCutAction->setShortcut(shortcuts[SHRT_CUT].key); editCopyAction->setShortcut(shortcuts[SHRT_COPY].key); editCopyRangeAction->setShortcut(shortcuts[SHRT_COPY_RANGE].key); @@ -701,12 +711,12 @@ editPasteCloneToTrackAction->setShortcut(shortcuts[SHRT_PASTE_CLONE_TO_TRACK].key); editPasteDialogAction->setShortcut(shortcuts[SHRT_PASTE_DIALOG].key); editInsertEMAction->setShortcut(shortcuts[SHRT_INSERTMEAS].key); + editDuplicateSelTrackAction->setShortcut(shortcuts[SHRT_DUPLICATE_TRACK].key); //editDeleteSelectedAction has no acceleration trackMidiAction->setShortcut(shortcuts[SHRT_ADD_MIDI_TRACK].key); trackDrumAction->setShortcut(shortcuts[SHRT_ADD_DRUM_TRACK].key); - trackNewStyleDrumAction->setShortcut(shortcuts[SHRT_ADD_NEW_STYLE_DRUM_TRACK].key); trackWaveAction->setShortcut(shortcuts[SHRT_ADD_WAVE_TRACK].key); trackAOutputAction->setShortcut(shortcuts[SHRT_ADD_AUDIO_OUTPUT].key); trackAGroupAction->setShortcut(shortcuts[SHRT_ADD_AUDIO_GROUP].key); @@ -755,14 +765,24 @@ //--------------------------------------------------------- // selectionChanged +// NOTE: This is received upon EITHER a part or track selection change from the Arranger. //--------------------------------------------------------- void ArrangerView::selectionChanged() { - //bool flag = arranger->isSingleSelection(); // -- Hmm, why only single? - bool flag = arranger->selectionSize() > 0; // -- Test OK cut and copy. For muse2. Tim. - editCutAction->setEnabled(flag); - editCopyAction->setEnabled(flag); + bool pflag = arranger->itemsAreSelected(); + bool tflag = MusECore::tracks_are_selected(); + + editDeleteAction->setEnabled(tflag || pflag); + + editDeleteSelectedAction->setEnabled(tflag); + editDuplicateSelTrackAction->setEnabled(tflag); + + editCutAction->setEnabled(pflag); + editCopyAction->setEnabled(pflag); + editShrinkPartsAction->setEnabled(pflag); + editExpandPartsAction->setEnabled(pflag); + editCleanPartsAction->setEnabled(pflag); } diff -Nru muse-2.1.2/muse/arranger/arrangerview.h muse-3.0.2+ds1/muse/arranger/arrangerview.h --- muse-2.1.2/muse/arranger/arrangerview.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/arrangerview.h 2017-12-04 21:01:18.000000000 +0000 @@ -92,9 +92,10 @@ QAction *strGlobalCutAction, *strGlobalInsertAction, *strGlobalSplitAction; QAction *strGlobalCutSelAction, *strGlobalInsertSelAction, *strGlobalSplitSelAction; - QAction *trackMidiAction, *trackDrumAction, *trackNewStyleDrumAction, *trackWaveAction, *trackAOutputAction, *trackAGroupAction; + QAction *trackMidiAction, *trackDrumAction, + *trackWaveAction, *trackAOutputAction, *trackAGroupAction; QAction *trackAInputAction, *trackAAuxAction; - QAction *editCutAction, *editCopyAction, *editCopyRangeAction; + QAction *editDeleteAction,*editCutAction, *editCopyAction, *editCopyRangeAction; QAction *editPasteAction, *editPasteCloneAction, *editPasteToTrackAction, *editPasteCloneToTrackAction, *editPasteDialogAction; QAction *editInsertEMAction, *editPasteC2TAction, *editDeleteSelectedAction, *editSelectAllAction, *editDeselectAllAction; QAction *editDuplicateSelTrackAction; @@ -139,7 +140,7 @@ void scoreNamingChanged(); void updateScoreMenus(); void clipboardChanged(); - void selectionChanged(); + void selectionChanged(); // NOTE: This is received upon EITHER a part or track selection change from the Arranger. void updateShortcuts(); void updateVisibleTracksButtons(); virtual void focusCanvas() { arranger->focusCanvas(); } diff -Nru muse-2.1.2/muse/arranger/CMakeLists.txt muse-3.0.2+ds1/muse/arranger/CMakeLists.txt --- muse-2.1.2/muse/arranger/CMakeLists.txt 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/CMakeLists.txt 2018-01-14 18:26:04.000000000 +0000 @@ -24,8 +24,7 @@ # # Expand Qt macros # -QT4_WRAP_CPP (arranger_mocs - alayout.h +QT5_WRAP_CPP (arranger_mocs arranger.h arrangerview.h pcanvas.h @@ -36,10 +35,10 @@ # List of source files to compile # file (GLOB arranger_source_files - alayout.cpp arranger.cpp arrangerview.cpp pcanvas.cpp + structure.cpp tlist.cpp ) diff -Nru muse-2.1.2/muse/arranger/pcanvas.cpp muse-3.0.2+ds1/muse/arranger/pcanvas.cpp --- muse-2.1.2/muse/arranger/pcanvas.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/pcanvas.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -26,9 +26,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include "fastlog.h" #include "widgets/tools.h" @@ -64,15 +66,26 @@ #include "utils.h" #include "dialogs.h" #include "widgets/pastedialog.h" +#include "undo.h" +#include "muse_math.h" + +using MusECore::Undo; +using MusECore::UndoOp; #define ABS(x) (abs(x)) +#define FABS(x) (fabs(x)) + #define EDITING_FINISHED_TIMEOUT 50 /* in milliseconds */ using std::set; namespace MusEGui { +const int PartCanvas::_automationPointDetectDist = 4; +const int PartCanvas::_automationPointWidthUnsel = 2; +const int PartCanvas::_automationPointWidthSel = 3; + //--------------------------------------------------------- // NPart //--------------------------------------------------------- @@ -81,9 +94,9 @@ { leftBorderTouches = false; rightBorderTouches = false; - + _serial=e->sn(); - + int y = track()->y(); setPos(QPoint(e->tick(), y)); setBBox(QRect(e->tick(), y, e->lenTick(), track()->height())); @@ -104,6 +117,8 @@ lineEditor = 0; editMode = false; + supportsResizeToTheLeft = true; + tracks = MusEGlobal::song->tracks(); setMouseTracking(true); drag = DRAG_OFF; @@ -111,6 +126,7 @@ automation.currentCtrlValid = false; automation.controllerState = doNothing; automation.moveController = false; + automation.breakUndoCombo = false; partsChanged(); } @@ -187,17 +203,17 @@ { lineEditor->hide(); if (editMode) { - //this check is neccessary, because it returnPressed may be called + //this check is necessary, because it returnPressed may be called //twice. the second call would cause a crash, however! - MusECore::Part* oldPart = editPart->part(); - MusECore::Part* newPart = oldPart->clone(); - - newPart->setName(lineEditor->text()); - // Indicate do undo, and do port controller values but not clone parts. - MusEGlobal::audio->msgChangePart(oldPart, newPart, true, true, false); - + MusECore::Part* part = editPart->part(); + // Indicate do undo, and do port controller values but not clone parts. + + Undo operations; + operations.push_back(UndoOp(UndoOp::ModifyPartName,part, part->name(), lineEditor->text())); + MusEGlobal::song->applyOperationGroup(operations); + editMode = false; - + editingFinishedTime.start(); } } @@ -236,7 +252,7 @@ emit dclickPart(((NPart*)(curItem))->track()); } } - + // double click creates new part between left and // right mark @@ -296,13 +312,13 @@ //--------------------------------------------------------- void PartCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dtype, bool rasterize) -{ +{ MusECore::Undo operations; - - for(iCItem ici = items.begin(); ici != items.end(); ++ici) + + for(iCItem ici = items.begin(); ici != items.end(); ++ici) { CItem* ci = ici->second; - + int x = ci->pos().x(); int y = ci->pos().y(); int nx = x + dx; @@ -311,124 +327,84 @@ if(rasterize) newpos = raster(newpos); selectItem(ci, true); - + bool result=moveItem(operations, ci, newpos, dtype); if (result) - ci->move(newpos); - + ci->move(newpos); + if(moving.size() == 1) { itemReleased(curItem, newpos); } if(dtype == MOVE_COPY || dtype == MOVE_CLONE) selectItem(ci, false); } - + MusEGlobal::song->applyOperationGroup(operations); partsChanged(); } - + //--------------------------------------------------------- // moveItem // return false, if copy/move not allowed //--------------------------------------------------------- bool PartCanvas::moveItem(MusECore::Undo& operations, CItem* item, const QPoint& newpos, DragType t) - { - NPart* npart = (NPart*) item; - MusECore::Part* spart = npart->part(); - MusECore::Track* track = npart->track(); - MusECore::Track* dtrack=NULL; - unsigned dtick = newpos.x(); - unsigned ntrack = y2pitch(item->mp().y()); - MusECore::Track::TrackType type = track->type(); - if (tracks->index(track) == ntrack && (dtick == spart->tick())) { - return false; - } - if (ntrack >= tracks->size()) { - ntrack = tracks->size(); - if (MusEGlobal::debugMsg) - printf("PartCanvas::moveItem - add new track\n"); - dtrack = MusEGlobal::song->addTrack(operations, type); // Add at end of list. - - if (type == MusECore::Track::WAVE) { - MusECore::WaveTrack* st = (MusECore::WaveTrack*) track; - MusECore::WaveTrack* dt = (MusECore::WaveTrack*) dtrack; - dt->setChannels(st->channels()); - } - emit tracklistChanged(); - } - else - { - dtrack = tracks->index(ntrack); - if (dtrack->type() != type) { - QMessageBox::critical(this, QString("MusE"), - tr("Cannot copy/move/clone to different Track-Type")); - return false; - } - } - - MusECore::Part* dpart; - bool clone = (t == MOVE_CLONE || (t == MOVE_COPY && spart->events()->arefCount() > 1)); - - if(t == MOVE_MOVE) - { - // This doesn't increment aref count, and doesn't chain clones. - // It also gives the new part a new serial number, but it is - // overwritten with the old one by Song::changePart(), from Audio::msgChangePart() below. - dpart = spart->clone(); - dpart->setTrack(dtrack); - } - else - // This increments aref count if cloned, and chains clones. - // It also gives the new part a new serial number. - dpart = dtrack->newPart(spart, clone); - - dpart->setTick(dtick); - - if(t == MOVE_MOVE) - item->setPart(dpart); - if (t == MOVE_COPY && !clone) { - // Copy Events - MusECore::EventList* se = spart->events(); - MusECore::EventList* de = dpart->events(); - for (MusECore::iEvent i = se->begin(); i != se->end(); ++i) { - MusECore::Event oldEvent = i->second; - MusECore::Event ev = oldEvent.clone(); - de->add(ev); - } - } +{ + NPart* npart = (NPart*) item; + MusECore::Part* spart = npart->part(); + MusECore::Track* track = npart->track(); + MusECore::Track* dtrack=NULL; + unsigned dtick = newpos.x(); // FIXME TODO make subtick-compatible! + unsigned ntrack = y2pitch(item->mp().y()); + MusECore::Track::TrackType type = track->type(); + if (tracks->index(track) == ntrack && (dtick == spart->tick())) { + return false; + } + if (ntrack >= tracks->size()) { + ntrack = tracks->size(); + if (MusEGlobal::debugMsg) + printf("PartCanvas::moveItem - add new track\n"); + dtrack = MusEGlobal::song->addTrack(type); // Add at end of list. Creates additional Undo entry. + + if (type == MusECore::Track::WAVE) { + MusECore::WaveTrack* st = (MusECore::WaveTrack*) track; + MusECore::WaveTrack* dt = (MusECore::WaveTrack*) dtrack; + dt->setChannels(st->channels()); + } + emit tracklistChanged(); + } + else + { + dtrack = tracks->index(ntrack); + if (dtrack->type() != type) { + QMessageBox::critical(this, QString("MusE"), + tr("Cannot copy/move/clone to different Track-Type")); + return false; + } + } - if (t == MOVE_COPY || t == MOVE_CLONE) { - dpart->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it - // so we must decrement it first :/ - // These will not increment ref count, and will not chain clones... - // TODO DELETETHIS: is the above comment still correct (by flo93)? i doubt it! - operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart,dpart)); - } - else if (t == MOVE_MOVE) { - // In all cases found ev lists were same. So this is redundant - Redo incs then decs the same list. - // But just in case we ever have two different lists... - dpart->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it - // so we must decrement it first :/ - spart->events()->incARef(1); // the later MusEGlobal::song->applyOperationGroup() will decrement it - // so we must increment it first :/ - dpart->setSelected(spart->selected()); - // These will increment ref count if not a clone, and will chain clones... - // TODO DELETETHIS: is the above comment still correct (by flo93)? i doubt it! - operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyPart,spart, dpart, true, false)); - - spart->setSelected(false); - } - // else // will never happen -> operations will never be empty - - if (MusEGlobal::song->len() < (dpart->lenTick() + dpart->tick())) - operations.push_back( MusECore::UndoOp(MusECore::UndoOp::ModifySongLen, - dpart->lenTick() + dpart->tick(), - MusEGlobal::song->len() ) ); - - return true; - } + + if(t == MOVE_MOVE) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::MovePart, spart, spart->posValue(), dtick, MusECore::Pos::TICKS, track, dtrack)); + else + { + MusECore::Part* dpart; + bool clone = (t == MOVE_CLONE || (t == MOVE_COPY && spart->hasClones())); + + // Gives the new part a new serial number. + if (clone) + dpart = spart->createNewClone(); + else + dpart = spart->duplicate(); + + dpart->setTick(dtick); + dpart->setTrack(dtrack); + + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart,dpart)); + } + return true; +} //--------------------------------------------------------- // raster @@ -461,8 +437,8 @@ { int sn = -1; if (curItem) sn=static_cast(curItem)->serial(); - curItem=NULL; - + curItem=NULL; + items.clearDelete(); for (MusECore::ciTrack t = tracks->begin(); t != tracks->end(); ++t) { if ((*t)->isVisible()) //ignore parts from hidden tracks @@ -472,16 +448,16 @@ MusECore::Part* part = i->second; NPart* np = new NPart(part); items.add(np); - + if (np->serial() == sn) curItem=np; - + if (i->second->selected()) selectItem(np, true); - - // Check for touching borders. p4.0.29 + + // Check for touching borders. MusECore::Part* pp; - for(MusECore::ciPart ii = pl->begin(); ii != pl->end(); ++ii) + for(MusECore::ciPart ii = pl->begin(); ii != pl->end(); ++ii) { pp = ii->second; if(pp == part) // Ignore this part @@ -492,7 +468,7 @@ np->leftBorderTouches = true; if(pp->tick() == part->endTick()) np->rightBorderTouches = true; - } + } } } } @@ -504,35 +480,60 @@ //--------------------------------------------------------- void PartCanvas::updateSelection() - { +{ + Undo operations; + bool changed=false; for (iCItem i = items.begin(); i != items.end(); ++i) { NPart* part = (NPart*)(i->second); - part->part()->setSelected(i->second->isSelected()); + operations.push_back(UndoOp(UndoOp::SelectPart, part->part(), i->second->isSelected(), part->part()->selected())); + if (i->second->isSelected() != part->part()->selected()) + changed=true; } - emit selectionChanged(); - redraw(); + + if (changed) + { + MusEGlobal::song->applyOperationGroup(operations); + redraw(); } + // TODO FIXME: this must be emitted always, because CItem is broken by design: + // CItems hold an Event smart-pointer which allows write access. + // This means, that items can (and will!) be selected bypassing the + // UndoOp::SelectEvent message! FIX THAT! (flo93) + emit selectionChanged(); +} + //--------------------------------------------------------- // resizeItem //--------------------------------------------------------- void PartCanvas::resizeItem(CItem* i, bool noSnap, bool ctrl) - { - MusECore::Track* t = ((NPart*)(i))->track(); - MusECore::Part* p = ((NPart*)(i))->part(); +{ + MusECore::Track* t = ((NPart*)(i))->track(); + MusECore::Part* p = ((NPart*)(i))->part(); - int pos = p->tick() + i->width(); - int snappedpos = pos; - if (!noSnap) { - snappedpos = AL::sigmap.raster(pos, *_raster); - } - unsigned int newwidth = snappedpos - p->tick(); - if (newwidth == 0) - newwidth = AL::sigmap.rasterStep(p->tick(), *_raster); + int pos = p->tick() + i->width(); + int snappedpos = pos; + if (!noSnap) { + snappedpos = AL::sigmap.raster(pos, *_raster); + } + unsigned int newwidth = snappedpos - p->tick(); + if (newwidth == 0) + newwidth = AL::sigmap.rasterStep(p->tick(), *_raster); + + bool doMove = false; + int newPos = 0; + if((i->mp() != i->pos()) && (resizeDirection == RESIZE_TO_THE_LEFT)) + { + doMove = true; + newPos = i->mp().x(); + if (newPos < 0) + newPos = 0; + } + MusEGlobal::song->cmdResizePart(t, p, newwidth, doMove, newPos, !ctrl); - MusEGlobal::song->cmdResizePart(t, p, newwidth, !ctrl); - } + +} //--------------------------------------------------------- // newItem @@ -555,7 +556,7 @@ MusECore::Track* track = tracks->index(trackIndex); if(!track) return 0; - + MusECore::Part* pa = 0; NPart* np = 0; switch(track->type()) { @@ -606,7 +607,7 @@ if(!noSnap) x = AL::sigmap.raster1(x, *_raster); p->setTick(x); - + unsigned trackIndex = y2pitch(i->y()); unsigned int tsize = tracks->size(); if (trackIndex >= tsize) @@ -651,7 +652,7 @@ } } } - + int len = i->width(); if (!noSnap) len = AL::sigmap.raster(len, *_raster); @@ -680,12 +681,11 @@ void PartCanvas::splitItem(CItem* item, const QPoint& pt) { NPart* np = (NPart*) item; - MusECore::Track* t = np->track(); MusECore::Part* p = np->part(); int x = pt.x(); if (x < 0) x = 0; - MusEGlobal::song->cmdSplitPart(t, p, AL::sigmap.raster(x, *_raster)); + split_part(p,AL::sigmap.raster(x, *_raster)); } //--------------------------------------------------------- @@ -695,9 +695,7 @@ void PartCanvas::glueItem(CItem* item) { NPart* np = (NPart*) item; - MusECore::Track* t = np->track(); - MusECore::Part* p = np->part(); - MusEGlobal::song->cmdGluePart(t, p); + merge_with_next_part(np->part()); } //--------------------------------------------------------- @@ -712,7 +710,7 @@ QMenu* partPopup = new QMenu(this); partPopup->addAction(new MenuTitleItem(tr("Part:"), partPopup)); - + QAction *act_cut = partPopup->addAction(*editcutIconSet, tr("C&ut")); act_cut->setData(4); act_cut->setShortcut(Qt::CTRL+Qt::Key_X); @@ -722,18 +720,18 @@ act_copy->setShortcut(Qt::CTRL+Qt::Key_C); partPopup->addSeparator(); - int rc = npart->part()->events()->arefCount(); + int rc = npart->part()->nClones(); QString st = QString(tr("s&elect ")); if(rc > 1) st += (QString().setNum(rc) + QString(" ")); st += QString(tr("clones")); QAction *act_select = partPopup->addAction(st); act_select->setData(18); - + partPopup->addSeparator(); QAction *act_rename = partPopup->addAction(tr("rename")); act_rename->setData(0); - + QMenu* colorPopup = partPopup->addMenu(tr("color")); // part color selection @@ -779,6 +777,9 @@ act_wexport->setData(16); QAction *act_wfinfo = partPopup->addAction(tr("file info")); act_wfinfo->setData(17); + QAction *act_wfnorm = partPopup->addAction(tr("Normalize")); + act_wfnorm->setData(19); + act_wfnorm->setShortcut(Qt::CTRL+Qt::Key_N); } break; case MusECore::Track::AUDIO_OUTPUT: @@ -803,158 +804,155 @@ //--------------------------------------------------------- void PartCanvas::itemPopup(CItem* item, int n, const QPoint& pt) - { - if(n >= TOOLS_ID_BASE) - { - canvasPopup(n); - return; +{ + if(n >= TOOLS_ID_BASE) + { + canvasPopup(n); + return; + } + + MusECore::PartList* pl = new MusECore::PartList; + NPart* npart = (NPart*)(item); + pl->add(npart->part()); + switch(n) { + case 0: // rename + { + editPart = npart; + QRect r = map(curItem->bbox()); + if (lineEditor == 0) { + lineEditor = new QLineEdit(this); + lineEditor->setFrame(true); + connect(lineEditor, SIGNAL(editingFinished()),SLOT(returnPressed())); + } + lineEditor->setText(editPart->name()); + lineEditor->setFocus(); + lineEditor->show(); + lineEditor->setGeometry(r); + editMode = true; + } + break; + case 1: // delete + deleteItem(item); + break; + case 2: // split + splitItem(item, pt); + break; + case 3: // glue + glueItem(item); + break; + case 4: + copy(pl); + MusEGlobal::audio->msgRemovePart(npart->part()); + break; + case 5: + copy(pl); + break; + case 6: + MusECore::merge_selected_parts(); + break; + + case 14: // wave edit + emit startEditor(pl, 4); + return; + case 15: // declone + { + MusECore::Part* spart = npart->part(); + MusECore::Part* dpart = spart->duplicate(); // dpart will not be member of any clone chain! + + Undo operations; + operations.push_back(UndoOp(UndoOp::DeletePart, spart)); + operations.push_back(UndoOp(UndoOp::AddPart, dpart)); + MusEGlobal::song->applyOperationGroup(operations); + break; + } + case 16: // Export to file + { + const MusECore::Part* part = item->part(); + bool popenFlag = false; + QString fn = getSaveFileName(QString(""), MusEGlobal::part_file_save_pattern, this, tr("MusE: save part")); + if (!fn.isEmpty()) { + FILE* fp = fileOpen(this, fn, ".mpt", "w", popenFlag, false, false); + if (fp) { + MusECore::Xml tmpXml = MusECore::Xml(fp); + // Write the part. Indicate that it's a copy operation - to add special markers, + // and force full wave paths. + part->write(0, tmpXml, true, true); + fclose(fp); + } } - - MusECore::PartList* pl = new MusECore::PartList; - NPart* npart = (NPart*)(item); - pl->add(npart->part()); - switch(n) { - case 0: // rename - { - editPart = npart; - QRect r = map(curItem->bbox()); - if (lineEditor == 0) { - lineEditor = new QLineEdit(this); - lineEditor->setFrame(true); - connect(lineEditor, SIGNAL(editingFinished()),SLOT(returnPressed())); - } - lineEditor->setText(editPart->name()); - lineEditor->setFocus(); - lineEditor->show(); - lineEditor->setGeometry(r); - editMode = true; - } - break; - case 1: // delete - deleteItem(item); - break; - case 2: // split - splitItem(item, pt); - break; - case 3: // glue - glueItem(item); - break; - case 4: - copy(pl); - MusEGlobal::audio->msgRemovePart(npart->part()); - break; - case 5: - copy(pl); - break; - case 6: - MusECore::merge_selected_parts(); - break; + break; + } - case 14: // wave edit - emit startEditor(pl, 4); - return; - case 15: // declone - { - MusECore::Part* spart = npart->part(); - MusECore::Track* track = npart->track(); - MusECore::Part* dpart = track->newPart(spart, false); - - MusECore::EventList* se = spart->events(); - MusECore::EventList* de = dpart->events(); - for (MusECore::iEvent i = se->begin(); i != se->end(); ++i) { - MusECore::Event oldEvent = i->second; - MusECore::Event ev = oldEvent.clone(); - de->add(ev); - } - // Indicate undo, and do port controller values but not clone parts. - // changed by flo93: removed start and endUndo, instead changed first bool to true - MusEGlobal::audio->msgChangePart(spart, dpart, true, true, false); - break; - } - case 16: // Export to file - { - const MusECore::Part* part = item->part(); - bool popenFlag = false; - QString fn = getSaveFileName(QString(""), MusEGlobal::part_file_save_pattern, this, tr("MusE: save part")); - if (!fn.isEmpty()) { - FILE* fp = fileOpen(this, fn, ".mpt", "w", popenFlag, false, false); - if (fp) { - MusECore::Xml tmpXml = MusECore::Xml(fp); - // Write the part. Indicate that it's a copy operation - to add special markers, - // and force full wave paths. - part->write(0, tmpXml, true, true); - fclose(fp); - } - } - break; - } - - case 17: // File info - { - MusECore::Part* p = item->part(); - MusECore::EventList* el = p->events(); - QString str = tr("Part name: %1\nFiles:").arg(p->name()); - for (MusECore::iEvent e = el->begin(); e != el->end(); ++e) - { - MusECore::Event event = e->second; - MusECore::SndFileR f = event.sndFile(); - if (f.isNull()) - continue; - str.append(QString("\n@") + QString().setNum(event.tick()) + QString(" len:") + - QString().setNum(event.lenTick()) + QString(" ") + f.path()); - } - QMessageBox::information(this, "File info", str, "Ok", 0); - break; - } - case 18: // Select clones - { - MusECore::Part* part = item->part(); - - // Traverse and process the clone chain ring until we arrive at the same part again. - // The loop is a safety net. - MusECore::Part* p = part; - int j = part->cevents()->arefCount(); - if(j > 0) - { - for(int i = 0; i < j; ++i) - { - p->setSelected(true); - p = p->nextClone(); - if(p == part) - break; - } - MusEGlobal::song->update(SC_SELECTION); - } - - break; - } - case 20 ... NUM_PARTCOLORS+20: - { - curColorIndex = n - 20; - bool selfound = false; - //Loop through all parts and set color on selected: - for (iCItem i = items.begin(); i != items.end(); i++) { - if (i->second->isSelected()) { - selfound = true; - i->second->part()->setColorIndex(curColorIndex); - } - } - - // If no items selected, use the one clicked on. - if(!selfound) - item->part()->setColorIndex(curColorIndex); - - MusEGlobal::song->update(SC_PART_MODIFIED); - redraw(); - break; - } - default: - printf("unknown action %d\n", n); - break; - } - delete pl; + case 17: // File info + { + MusECore::Part* p = item->part(); + QString str = tr("Part name: %1\nFiles:").arg(p->name()); + for (MusECore::ciEvent e = p->events().begin(); e != p->events().end(); ++e) + { + MusECore::Event event = e->second; + if(event.empty()) + continue; + MusECore::SndFileR f = event.sndFile(); + if (f.isNull()) + continue; + str.append(QString("\n@") + QString().setNum(event.tick()) + QString(" len:") + + QString().setNum(event.lenTick()) + QString(" ") + f.path()); + } + QMessageBox::information(this, "File info", str, "Ok", 0); + break; + } + case 18: // Select clones + { + MusECore::Part* part = item->part(); + + // Traverse and process the clone chain ring until we arrive at the same part again. + // The loop is a safety net. + MusECore::Part* p = part; + + Undo operations; + if(part->hasClones()) + { + operations.push_back(UndoOp(UndoOp::SelectPart, p, true, p->selected())); + for(MusECore::Part* it = p->nextClone(); it!=p; it=it->nextClone()) + operations.push_back(UndoOp(UndoOp::SelectPart, it, true, it->selected())); + + MusEGlobal::song->applyOperationGroup(operations); + } + + break; + } + case 19: // Normalize + { + MusEGlobal::song->normalizeWaveParts(item->part()); + break; + } + case 20 ... NUM_PARTCOLORS+20: + { + curColorIndex = n - 20; + bool selfound = false; + //Loop through all parts and set color on selected: + for (iCItem i = items.begin(); i != items.end(); i++) { + if (i->second->isSelected()) { + selfound = true; + i->second->part()->setColorIndex(curColorIndex); + } } + // If no items selected, use the one clicked on. + if(!selfound) + item->part()->setColorIndex(curColorIndex); + + MusEGlobal::song->update(SC_PART_MODIFIED); + redraw(); + break; + } + default: + printf("unknown action %d\n", n); + break; + } + delete pl; +} + //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- @@ -970,7 +968,7 @@ switch (_tool) { default: - break; + break; case PointerTool: case PencilTool: if (item && button == Qt::LeftButton) @@ -996,9 +994,9 @@ case AutomationTool: if (button == Qt::RightButton || button == Qt::MidButton) { - + bool do_delete = false; - + if (button == Qt::MidButton) // mid-click do_delete=true; else // right-click @@ -1022,14 +1020,25 @@ delete automationMenu; } if (do_delete && automation.currentTrack) { + Undo operations; foreach(int frame, automation.currentCtrlFrameList) - MusEGlobal::audio->msgEraseACEvent((MusECore::AudioTrack*)automation.currentTrack, - automation.currentCtrlList->id(), frame); + operations.push_back(UndoOp(UndoOp::DeleteAudioCtrlVal, automation.currentTrack, automation.currentCtrlList->id(), frame)); + if(!operations.empty()) + { + MusEGlobal::song->applyOperationGroup(operations); + // User probably would like to hear results so make sure controller is enabled. + ((MusECore::AudioTrack*)automation.currentTrack)->enableController(automation.currentCtrlList->id(), true); + } } } else { if (automation.controllerState != doNothing) - automation.moveController=true; + { + automation.moveController = true; + // Upon any next operations list execution, break any undo combining. + automation.breakUndoCombo = true; + newAutomationVertex(pt); + } } return false; break; @@ -1050,6 +1059,7 @@ automation.currentCtrlValid = false; automation.currentTrack=0; automation.currentCtrlList=0; + //automation.breakUndoCombo = false; // Don't touch this here. } //--------------------------------------------------------- @@ -1062,9 +1072,15 @@ if (x < 0) x = 0; - if (_tool == AutomationTool) - processAutomationMovements(event->pos(), event->modifiers() & Qt::ShiftModifier); + if (_tool == AutomationTool) { + event->accept(); + bool slowMotion = event->modifiers() & Qt::ShiftModifier; + processAutomationMovements(event->pos(), slowMotion); + emit timeChanged(AL::sigmap.raster(x, *_raster)); + return; + } + event->ignore(); emit timeChanged(AL::sigmap.raster(x, *_raster)); } @@ -1092,22 +1108,12 @@ void PartCanvas::keyPress(QKeyEvent* event) { int key = event->key(); -// DELETETHIS 10 -// if (_tool == AutomationTool) { // can't get the cursor pos to work right, skipping for now -// // clear all the automation parameters -// automation.moveController=false; -// automation.controllerState = doNothing; -// automation.currentCtrl=0; -// automation.currentTrack=0; -// automation.currentCtrlList=0; -// -// processAutomationMovements(mapDev(QCursor::pos()),event->key()& Qt::Key_Control); -// } + if (editMode) { // this will probably never happen, as edit mode has been set // to "false" some usec ago by returnPressed, called by editingFinished. - if ( key == Qt::Key_Return || key == Qt::Key_Enter ) + if ( key == Qt::Key_Return || key == Qt::Key_Enter ) { return; } @@ -1135,17 +1141,17 @@ if (key == shortcuts[SHRT_DELETE].key) { if (getCurrentDrag()) return; - - MusEGlobal::song->msgRemoveParts(); + + MusECore::delete_selected_parts(); return; } else if (key == shortcuts[SHRT_POS_DEC].key) { int spos = pos[0]; - if(spos > 0) + if(spos > 0) { spos -= 1; // Nudge by -1, then snap down with raster1. spos = AL::sigmap.raster1(spos, *_raster); - } + } if(spos < 0) spos = 0; MusECore::Pos p(spos,true); @@ -1155,7 +1161,7 @@ else if (key == shortcuts[SHRT_POS_INC].key) { int spos = AL::sigmap.raster2(pos[0] + 1, *_raster); // Nudge by +1, then snap up with raster2. MusECore::Pos p(spos,true); - MusEGlobal::song->setPos(0, p, true, true, true); + MusEGlobal::song->setPos(0, p, true, true, true); return; } else if (key == shortcuts[SHRT_POS_DEC_NOSNAP].key) { @@ -1215,7 +1221,18 @@ emit selectTrackBelow(); return; } - + else if (key == shortcuts[SHRT_EDIT_TRACK_NAME].key) { + emit editTrackNameSig(); + return; + } + else if (key == shortcuts[SHRT_MUTE_CURRENT_TRACKS].key) { + emit muteSelectedTracks(); + return; + } + else if (key == shortcuts[SHRT_SOLO_CURRENT_TRACKS].key) { + emit soloSelectedTracks(); + return; + } // Shortcuts that require selected parts from here if (!curItem) { if (items.size()==0) { @@ -1257,12 +1274,16 @@ } } - int left_tick = leftmost->part()->tick(); - int right_tick = rightmost->part()->tick() + rightmost->part()->lenTick(); - MusECore::Pos p1(left_tick, true); - MusECore::Pos p2(right_tick, true); - MusEGlobal::song->setPos(1, p1); - MusEGlobal::song->setPos(2, p2); + if(leftmost && rightmost) + { + int left_tick = leftmost->part()->tick(); + int right_tick = rightmost->part()->tick() + rightmost->part()->lenTick(); + MusECore::Pos p1(left_tick, true); + MusECore::Pos p2(right_tick, true); + MusEGlobal::song->setPos(1, p1); + MusEGlobal::song->setPos(2, p2); + } + return; } @@ -1286,7 +1307,7 @@ { afterthis = true; continue; - } + } if(afterthis) { newItem = i->second; @@ -1471,7 +1492,7 @@ int newx = mx + rmapx(newItem->width()) - width(); emit horizontalScroll( (newx > mx ? mx - 10 : newx + 10) - rmapx(xorg) ); } - + if (newItem->y() < mapyDev(0)) { int my = rmapy(newItem->y()); int newy = my + rmapy(newItem->height()) - height(); @@ -1480,7 +1501,7 @@ else if (newItem->y() + newItem->height() > mapyDev(height())) { emit verticalScroll( rmapy(newItem->y() + newItem->height() - yorg) - height() + 10); } - + redraw(); } } @@ -1490,13 +1511,11 @@ // draws a part //--------------------------------------------------------- -#if 0 // DELETETHIS 430 WHOA! void PartCanvas::drawItem(QPainter& p, const CItem* item, const QRect& rect) { int from = rect.x(); int to = from + rect.width(); - //printf("from %d to %d\n", from,to); MusECore::Part* part = ((NPart*)item)->part(); int pTick = part->tick(); from -= pTick; @@ -1504,602 +1523,181 @@ if(from < 0) from = 0; if((unsigned int)to > part->lenTick()) - to = part->lenTick(); + to = part->lenTick(); + + QBrush brush; + + QRect r = item->bbox(); + // Compensation required for two pixel wide border. FIXME Prefer to do this after the map, but r is needed below. + r.moveTop(r.y() + rmapyDev(1)); + //QRect rr = p.transform().mapRect(r); // Gives inconsistent positions. Source shows wrong operation for our needs. + QRect rr = map(r); // Use our own map instead. + + QRect mr = map(rect); // Item bounding box x is in tick coordinates, same as rectangle. - if(item->bbox().intersect(rect).isNull()) - { - //printf("PartCanvas::drawItem rectangle is null\n"); + if((rr & mr).isNull()) return; - } - - QRect r = item->bbox(); - bool clone = part->events()->arefCount() > 1; - QBrush brush; - - //QRect rr = map(r); - //QRect rr = p.transform().mapRect(r); - //printf("PartCanvas::drawItem called map rx:%d rw:%d rrx:%d rrw:%d\n", r.x(), r.width(), rr.x(), rr.width()); - //printf("PartCanvas::drawItem called map rx:%d rw:%d\n", r.x(), r.width()); - //p.save(); - //p.setWorldMatrixEnabled(false); - + + p.setWorldMatrixEnabled(false); + // NOTE: Optimization: For each item, hasHiddenEvents() is called once in Canvas::draw(), and we use cachedHasHiddenEvents(). // Not used for now. - //int het = part->cachedHasHiddenEvents(); + //int het = part->cachedHasHiddenEvents(); DELETETHIS or FIXME or whatever? int het = part->hasHiddenEvents(); - - int y_1 = rmapyDev(1); // Hack, try to replace. - double xs_0 = r.x(); - double xe_0 = xs_0 + r.width(); - double xs_m0 = rmapx_f(xs_0); - double xe_m0 = rmapx_f(xe_0); - double xs_1 = rmapxDev_f(xs_m0 + 1.0); + + int xs_0 = rr.x(); + int xe_0 = xs_0 + rr.width(); + int xs_1 = xs_0 + 1; if(xs_1 > xe_0) xs_1 = xe_0; - double xs_2 = rmapxDev_f(xs_m0 + 2.0); + int xs_2 = xs_0 + 2; if(xs_2 > xe_0) xs_2 = xe_0; - double xs_6 = rmapxDev_f(xs_m0 + 6.0); - if(xs_6 > xe_0) - xs_6 = xe_0; - - double xe_1 = rmapxDev_f(xe_m0 - 1.0); + int xs_j = xs_0 + 8; + if(xs_j > xe_0) + xs_j = xe_0; + + int xe_1 = xe_0 - 1; if(xe_1 < xs_0) xe_1 = xs_0; - double xe_2 = rmapxDev_f(xe_m0 - 2.0); + int xe_2 = xe_0 - 2; if(xe_2 < xs_0) xe_2 = xs_0; - double xe_6 = rmapxDev_f(xe_m0 - 6.0); - if(xe_6 < xs_0) - xe_6 = xs_0; - - double ys_0 = r.y(); - double ye_0 = ys_0 + r.height(); - double ys_m0 = rmapy_f(ys_0); - double ye_m0 = rmapy_f(ye_0); - double ys_1 = rmapyDev_f(ys_m0 + 1.0); + int xe_j = xe_0 - 8; + if(xe_j < xs_0) + xe_j = xs_0; + + int ys_0 = rr.y(); + int ye_0 = ys_0 + rr.height(); + int ys_1 = ys_0 + 1; if(ys_1 > ye_0) ys_1 = ye_0; - double ys_2 = rmapyDev_f(ys_m0 + 2.0); + int ys_2 = ys_0 + 2; if(ys_2 > ye_0) ys_2 = ye_0; - - double ye_1 = rmapyDev_f(ye_m0 - 1.0); + int ys_3 = ys_0 + 3; + if(ys_3 > ye_0) + ys_3 = ye_0; + + int ye_1 = ye_0 - 1; if(ye_1 < ys_0) ye_1 = ys_0; - double ye_2 = rmapyDev_f(ye_m0 - 2.0); + int ye_2 = ye_0 - 2; if(ye_2 < ys_0) ye_2 = ys_0; - + + int mrxs_0 = mr.x(); + int mrxe_0 = mrxs_0 + mr.width(); + bool lbt = ((NPart*)item)->leftBorderTouches; + bool rbt = ((NPart*)item)->rightBorderTouches; + int lbx = lbt?xs_1:xs_0; + int rbx = rbt?xe_1:xe_0; + int lbx_c = lbx < mrxs_0 ? mrxs_0 : lbx; + int rbx_c = rbx > mrxe_0 ? mrxe_0 : rbx; + int cidx = part->colorIndex(); - if (item->isMoving()) + if (item->isMoving()) { QColor c(Qt::gray); c.setAlpha(MusEGlobal::config.globalAlphaBlend); - QLinearGradient gradient(r.topLeft(), r.bottomLeft()); + QLinearGradient gradient(rr.topLeft(), rr.bottomLeft()); gradient.setColorAt(0, c); gradient.setColorAt(1, c.darker()); brush = QBrush(gradient); } - else - if (part->selected()) + else + if (part->selected()) { QColor c(Qt::black); c.setAlpha(MusEGlobal::config.globalAlphaBlend); - QLinearGradient gradient(r.topLeft(), r.bottomLeft()); + QLinearGradient gradient(rr.topLeft(), rr.bottomLeft()); // Use a colour only about 20% lighter than black, rather than the 50% we use in MusECore::gGradientFromQColor // and is used in darker()/lighter(), so that it is distinguished a bit better from grey non-part tracks. - //c.setRgba(64, 64, 64, c.alpha()); + //c.setRgba(64, 64, 64, c.alpha()); gradient.setColorAt(0, QColor(51, 51, 51, MusEGlobal::config.globalAlphaBlend)); gradient.setColorAt(1, c); brush = QBrush(gradient); } else - if (part->mute()) + if (part->mute()) { QColor c(Qt::white); c.setAlpha(MusEGlobal::config.globalAlphaBlend); - QLinearGradient gradient(r.topLeft(), r.bottomLeft()); + QLinearGradient gradient(rr.topLeft(), rr.bottomLeft()); gradient.setColorAt(0, c); gradient.setColorAt(1, c.darker()); brush = QBrush(gradient); - - // Not good. Aliasing missing lines happening at different mags. - // And it's too much. If notes + automation is displayed (by removing 'return' below), cross hatch interferes. - // TODO: Maybe try to draw a nice gradient SINGLE cross (or STAR) from corner to corner instead. - // Then remove the 'return' below and let it draw the name and notes. - //brush.setStyle(Qt::DiagCrossPattern); - //p.fillRect(rf, brush); } else { QColor c(MusEGlobal::config.partColors[cidx]); c.setAlpha(MusEGlobal::config.globalAlphaBlend); - brush = QBrush(MusECore::gGradientFromQColor(c, r.topLeft(), r.bottomLeft())); - } - - double h = r.height(); - double s = h / 4.0; - double y0 = r.y(); - double y1 = y0 + s; - double y2 = y0 + s * 2.0; - double y3 = y0 + s * 3.0; - double y4 = y0 + h; - - QPointF points[16]; + brush = QBrush(MusECore::gGradientFromQColor(c, rr.topLeft(), rr.bottomLeft())); + } + + int h = rr.height(); + double s = double(h) / 4.0; + int y0 = ys_0; + int y2 = y0 + lrint(s * 2.0); + int y4 = y0 + h; + + QPoint points[8]; int pts; - - // + // Fill the part rectangles, accounting for hidden events by using 'jagged' edges... - // - + p.setBrush(brush); p.setPen(Qt::NoPen); + if(het) { + // TODO: Make this part respect the requested drawing rectangle (rr & mr), for speed ! + pts = 0; if(het == (MusECore::Part::LeftEventsHidden | MusECore::Part::RightEventsHidden)) { - points[pts++] = QPointF(xs_0, y0); - points[pts++] = QPointF(xe_0, y0); - points[pts++] = QPointF(xe_6, y1); - points[pts++] = QPointF(xe_0, y2); - points[pts++] = QPointF(xe_6, y3); - points[pts++] = QPointF(xe_0, y4); - points[pts++] = QPointF(xs_0, y4); - points[pts++] = QPointF(xs_6, y3); - points[pts++] = QPointF(xs_0, y2); - points[pts++] = QPointF(xs_6, y1); + points[pts++] = QPoint(xs_0, y0); + points[pts++] = QPoint(xe_0, y0); + points[pts++] = QPoint(xe_j, y2); + points[pts++] = QPoint(xe_0, y4); + points[pts++] = QPoint(xs_0, y4); + points[pts++] = QPoint(xs_j, y2); + p.drawConvexPolygon(points, pts); // Help says may be faster on some platforms (X11). } else if(het == MusECore::Part::LeftEventsHidden) { - points[pts++] = QPointF(xs_0, y0); - points[pts++] = QPointF(xe_0, y0); - points[pts++] = QPointF(xe_0, y4); - points[pts++] = QPointF(xs_0, y4); - points[pts++] = QPointF(xs_6, y3); - points[pts++] = QPointF(xs_0, y2); - points[pts++] = QPointF(xs_6, y1); - p.drawConvexPolygon(points, pts); + points[pts++] = QPoint(xs_0, y0); + points[pts++] = QPoint(xe_0, y0); + points[pts++] = QPoint(xe_0, y4); + points[pts++] = QPoint(xs_0, y4); + points[pts++] = QPoint(xs_j, y2); + + p.drawConvexPolygon(points, pts); } else if(het == MusECore::Part::RightEventsHidden) { - points[pts++] = QPointF(xs_0, y0); - points[pts++] = QPointF(xe_0, y0); - - points[pts++] = QPointF(xe_6, y1); - points[pts++] = QPointF(xe_0, y2); - points[pts++] = QPointF(xe_6, y3); - points[pts++] = QPointF(xe_0, y4); - points[pts++] = QPointF(xs_0, y4); - p.drawConvexPolygon(points, pts); + points[pts++] = QPoint(xs_0, y0); + points[pts++] = QPoint(xe_0, y0); + points[pts++] = QPoint(xe_j, y2); + points[pts++] = QPoint(xe_0, y4); + points[pts++] = QPoint(xs_0, y4); + + p.drawConvexPolygon(points, pts); } - - // - // Draw remaining 'hidden events' decorations with 'jagged' edges... - // - - int part_r, part_g, part_b, brightness, color_brightness; - MusEGlobal::config.partColors[cidx].getRgb(&part_r, &part_g, &part_b); - brightness = part_r*29 + part_g*59 + part_b*12; - //if ((brightness < 12000 || part->selected()) && !part->mute() && !item->isMoving()) - // color_brightness=223; // too dark: use lighter color - //else - // color_brightness=32; // otherwise use dark color - if ((brightness >= 12000 && !part->selected())) - color_brightness=32; // too light: use dark color - else - color_brightness=223; // too dark: use lighter color - QColor c(color_brightness,color_brightness,color_brightness, MusEGlobal::config.globalAlphaBlend); - p.setBrush(QBrush(MusECore::gGradientFromQColor(c, r.topLeft(), r.bottomLeft()))); - //p.setBrush(QBrush(c)); - if(het & MusECore::Part::RightEventsHidden) - { - pts = 0; - points[pts++] = QPointF(xe_0, y0); - points[pts++] = QPointF(xe_0, y4); - points[pts++] = QPointF(xe_6, y3); - points[pts++] = QPointF(xe_0, y2); - points[pts++] = QPointF(xe_6, y1); - p.drawConvexPolygon(points, pts); - } - if(het & MusECore::Part::LeftEventsHidden) - { - pts = 0; - points[pts++] = QPointF(xs_0, y0); - points[pts++] = QPointF(xs_6, y1); - points[pts++] = QPointF(xs_0, y2); - points[pts++] = QPointF(xs_6, y3); - points[pts++] = QPointF(xs_0, y4); - p.drawConvexPolygon(points, pts); - } - } - else - { - p.fillRect(r, brush); - } - - MusECore::MidiPart* mp = 0; - MusECore::WavePart* wp = 0; - MusECore::Track::TrackType type = part->track()->type(); - if (type == MusECore::Track::WAVE) { - wp =(MusECore::WavePart*)part; - } - else { - mp = (MusECore::MidiPart*)part; - } - - if (wp) - drawWavePart(p, rect, wp, r); - else if (mp) - { - drawMidiPart(p, rect, mp->events(), (MusECore::MidiTrack*)part->track(), mp, r, mp->tick(), from, to); - } - - #if 0 - // - // Now draw the borders... - // Works great but requires clones be drawn with the highest priority on top of all other parts, in Canvas::draw. - // - - QPen pen(part->selected() ? MusEGlobal::config.partColors[i] : Qt::black, 2.0, clone ? Qt::DotLine : Qt::SolidLine); - pen.setCosmetic(true); - p.setPen(pen); - p.setBrush(Qt::NoBrush); - p.drawRect(r); - - #else - // - // Now draw the borders, using custom segments... - // - - // FIXME NOTE: For 1-pixel wide lines, setting pen style to anything other than solid didn't work out well. - // Much too screwy - the single-width lines kept getting shifted one pixel over intermittently. - // I tried EVERYTHING to make sure x is proper but the painter keeps shifting them over. - // Meanwhile the fills are correct. Seems painter doesn't like line patterns, whether stock or custom. - // Therefore I was forced to manually draw the left and right segments. - // It works. Which seems to be more proof that painter is handling line patterns and pen widths badly... - // DO NOT ERASE COMMENTED CODE BELOW for now, in case it can be fixed. Tim. p4.0.29 - - p.setBrush(Qt::NoBrush); - - QColor pc((part->mute() || item->isMoving())? Qt::white : MusEGlobal::config.partColors[cidx]); - QPen penSelect1H(pc); - QPen penSelect2H(pc, 2.0); - QPen penSelect1V(pc); - QPen penSelect2V(pc, 2.0); - penSelect1H.setCosmetic(true); - penSelect2H.setCosmetic(true); - penSelect1V.setCosmetic(true); - penSelect2V.setCosmetic(true); - - pc = Qt::black; - QPen penNormal1H(pc); - QPen penNormal2H(pc, 2.0); - QPen penNormal1V(pc); - QPen penNormal2V(pc, 2.0); - penNormal1H.setCosmetic(true); - penNormal2H.setCosmetic(true); - penNormal1V.setCosmetic(true); - penNormal2V.setCosmetic(true); - - QVector customDashPattern; - if(clone) - { - customDashPattern << 4.0 << 8.0; - penSelect1H.setDashPattern(customDashPattern); - penNormal1H.setDashPattern(customDashPattern); - //penSelect1V.setDashPattern(customDashPattern); - //penNormal1V.setDashPattern(customDashPattern); - customDashPattern.clear(); - customDashPattern << 2.0 << 4.0; - penSelect2H.setDashPattern(customDashPattern); - penNormal2H.setDashPattern(customDashPattern); - //penSelect2V.setDashPattern(customDashPattern); - //penNormal2V.setDashPattern(customDashPattern); - } - - pc = Qt::white; - QPen penHidden1(pc); - QPen penHidden2(pc, 2.0); - penHidden2.setCosmetic(true); - //customDashPattern.clear(); - //customDashPattern << 2.0 << 10.0; - //penHidden1.setDashPattern(customDashPattern); - //customDashPattern.clear(); - //customDashPattern << 1.0 << 5.0; - //penHidden2.setDashPattern(customDashPattern); - - bool lbt = ((NPart*)item)->leftBorderTouches; - bool rbt = ((NPart*)item)->rightBorderTouches; - - QLineF l1( lbt?xs_1:xs_0, ys_0, rbt?xe_1:xe_0, ys_0); // Top - //QLineF l2(rbt?xe_1:xe_0, r.y() + (rbt?y_1:y_2) - 1, rbt?xe_1:xe_0, r.y() + r.height() - 1); // Right - QLineF l3( lbt?xs_1:xs_0, ye_0, rbt?xe_1:xe_0, ye_0); // Bottom - //QLineF l4(r.x(), r.y() + (lbt?y_1:y_2), r.x(), r.y() + r.height() - (lbt?y_1:y_2)); // Left - - if(het & MusECore::Part::RightEventsHidden) - p.setPen(((NPart*)item)->rightBorderTouches ? penHidden1 : penHidden2); - else - { - if(((NPart*)item)->rightBorderTouches) - p.setPen(part->selected() ? penSelect1V : penNormal1V); - else - p.setPen(part->selected() ? penSelect2V : penNormal2V); - } - //p.drawLine(l2); // Right line - - double xx = rbt?xe_1:xe_0; - if(clone) - { - double yinc = 7.0 * y_1; - for(double yy = (rbt?ys_1:ys_2); yy < ye_2; yy += yinc) - { - double yi = (rbt?3.0:2.0) * y_1; - if(yy + yi > ye_2) - yi = ye_2 - yy; - p.drawLine(QPointF(xx, yy), QPointF(xx, yy + yi)); // Right dashed line - } - } - else - p.drawLine(QPointF(xx, rbt?ys_1:ys_2), QPointF(xx, rbt?ye_1:ye_2)); // Right line - - if(het & MusECore::Part::LeftEventsHidden) - p.setPen(((NPart*)item)->leftBorderTouches ? penHidden1 : penHidden2); - else - { - if(((NPart*)item)->leftBorderTouches) - p.setPen(part->selected() ? penSelect1V : penNormal1V); - else - p.setPen(part->selected() ? penSelect2V : penNormal2V); - } - //p.drawLine(l4); // Left line - - xx = xs_0; - if(clone) - { - double yinc = 7.0 * y_1; - for(double yy = (lbt?ys_1:ys_2); yy < ye_2; yy += yinc) - { - double yi = (lbt?3.0:2.0) * y_1; - if(yy + yi > ye_2) - yi = ye_2 - yy; - p.drawLine(QPointF(xx, yy), QPointF(xx, yy + yi)); // Left dashed line - } - } - else - p.drawLine(QPointF(xx, lbt?ys_1:ys_2), QPointF(xx, lbt?ye_1:ye_2)); // Left line - - p.setPen(part->selected() ? penSelect2H : penNormal2H); - p.drawLine(l1); // Top line - p.drawLine(l3); // Bottom line - - #endif - - //p.restore(); - - if (MusEGlobal::config.canvasShowPartType & 1) { // show names - // draw name - // FN: Set text color depending on part color (black / white) - int part_r, part_g, part_b, brightness; - // Since we'll draw the text on the bottom (to accommodate drum 'slivers'), - // get the lowest colour in the gradient used to draw the part. - QRect rr = map(r); - rr.setX(rr.x() + 3); - MusECore::gGradientFromQColor(MusEGlobal::config.partColors[cidx], rr.topLeft(), rr.bottomLeft()).stops().last().second.getRgb(&part_r, &part_g, &part_b); - brightness = part_r*29 + part_g*59 + part_b*12; - //bool rev = (brightness < 12000 || part->selected()) && !part->mute() && !item->isMoving(); - bool rev = brightness >= 12000 && !part->selected(); - p.save(); - p.setFont(MusEGlobal::config.fonts[4]); - p.setWorldMatrixEnabled(false); - if (rev) - p.setPen(Qt::white); - else - p.setPen(Qt::black); - p.drawText(rr.translated(1, 1), Qt::AlignBottom|Qt::AlignLeft, part->name()); - if (rev) - p.setPen(Qt::black); - else - p.setPen(Qt::white); - p.drawText(rr, Qt::AlignBottom|Qt::AlignLeft, part->name()); - p.restore(); - } - } -#endif - -void PartCanvas::drawItem(QPainter& p, const CItem* item, const QRect& rect) - { - int from = rect.x(); - int to = from + rect.width(); - MusECore::Part* part = ((NPart*)item)->part(); - int pTick = part->tick(); - from -= pTick; - to -= pTick; - if(from < 0) - from = 0; - if((unsigned int)to > part->lenTick()) - to = part->lenTick(); - - bool clone = part->events()->arefCount() > 1; - QBrush brush; - - QRect r = item->bbox(); - // Compensation required for two pixel wide border. FIXME Prefer to do this after the map, but r is needed below. - r.moveTop(r.y() + rmapyDev(1)); - //QRect rr = p.transform().mapRect(r); // Gives inconsistent positions. Source shows wrong operation for our needs. - QRect rr = map(r); // Use our own map instead. - - QRect mr = map(rect); - - // Item bounding box x is in tick coordinates, same as rectangle. - if((rr & mr).isNull()) - return; - - p.setWorldMatrixEnabled(false); - - // NOTE: Optimization: For each item, hasHiddenEvents() is called once in Canvas::draw(), and we use cachedHasHiddenEvents(). - // Not used for now. - //int het = part->cachedHasHiddenEvents(); DELETETHIS or FIXME or whatever? - int het = part->hasHiddenEvents(); - - int xs_0 = rr.x(); - int xe_0 = xs_0 + rr.width(); - int xs_1 = xs_0 + 1; - if(xs_1 > xe_0) - xs_1 = xe_0; - int xs_2 = xs_0 + 2; - if(xs_2 > xe_0) - xs_2 = xe_0; - int xs_j = xs_0 + 8; - if(xs_j > xe_0) - xs_j = xe_0; - - int xe_1 = xe_0 - 1; - if(xe_1 < xs_0) - xe_1 = xs_0; - int xe_2 = xe_0 - 2; - if(xe_2 < xs_0) - xe_2 = xs_0; - int xe_j = xe_0 - 8; - if(xe_j < xs_0) - xe_j = xs_0; - - int ys_0 = rr.y(); - int ye_0 = ys_0 + rr.height(); - int ys_1 = ys_0 + 1; - if(ys_1 > ye_0) - ys_1 = ye_0; - int ys_2 = ys_0 + 2; - if(ys_2 > ye_0) - ys_2 = ye_0; - int ys_3 = ys_0 + 3; - if(ys_3 > ye_0) - ys_3 = ye_0; - - int ye_1 = ye_0 - 1; - if(ye_1 < ys_0) - ye_1 = ys_0; - int ye_2 = ye_0 - 2; - if(ye_2 < ys_0) - ye_2 = ys_0; - - int mrxs_0 = mr.x(); - int mrxe_0 = mrxs_0 + mr.width(); - bool lbt = ((NPart*)item)->leftBorderTouches; - bool rbt = ((NPart*)item)->rightBorderTouches; - int lbx = lbt?xs_1:xs_0; - int rbx = rbt?xe_1:xe_0; - int lbx_c = lbx < mrxs_0 ? mrxs_0 : lbx; - int rbx_c = rbx > mrxe_0 ? mrxe_0 : rbx; - - int cidx = part->colorIndex(); - if (item->isMoving()) - { - QColor c(Qt::gray); - c.setAlpha(MusEGlobal::config.globalAlphaBlend); - QLinearGradient gradient(rr.topLeft(), rr.bottomLeft()); - gradient.setColorAt(0, c); - gradient.setColorAt(1, c.darker()); - brush = QBrush(gradient); - } - else - if (part->selected()) - { - QColor c(Qt::black); - c.setAlpha(MusEGlobal::config.globalAlphaBlend); - QLinearGradient gradient(rr.topLeft(), rr.bottomLeft()); - // Use a colour only about 20% lighter than black, rather than the 50% we use in MusECore::gGradientFromQColor - // and is used in darker()/lighter(), so that it is distinguished a bit better from grey non-part tracks. - //c.setRgba(64, 64, 64, c.alpha()); - gradient.setColorAt(0, QColor(51, 51, 51, MusEGlobal::config.globalAlphaBlend)); - gradient.setColorAt(1, c); - brush = QBrush(gradient); - } - else - if (part->mute()) - { - QColor c(Qt::white); - c.setAlpha(MusEGlobal::config.globalAlphaBlend); - QLinearGradient gradient(rr.topLeft(), rr.bottomLeft()); - gradient.setColorAt(0, c); - gradient.setColorAt(1, c.darker()); - brush = QBrush(gradient); - } - else - { - QColor c(MusEGlobal::config.partColors[cidx]); - c.setAlpha(MusEGlobal::config.globalAlphaBlend); - brush = QBrush(MusECore::gGradientFromQColor(c, rr.topLeft(), rr.bottomLeft())); - } - - int h = rr.height(); - double s = double(h) / 4.0; - int y0 = ys_0; - int y2 = y0 + lrint(s * 2.0); - int y4 = y0 + h; - - QPoint points[8]; - int pts; - - // Fill the part rectangles, accounting for hidden events by using 'jagged' edges... - - p.setBrush(brush); - p.setPen(Qt::NoPen); - - if(het) - { - // TODO: Make this part respect the requested drawing rectangle (rr & mr), for speed ! - - pts = 0; - if(het == (MusECore::Part::LeftEventsHidden | MusECore::Part::RightEventsHidden)) - { - points[pts++] = QPoint(xs_0, y0); - points[pts++] = QPoint(xe_0, y0); - points[pts++] = QPoint(xe_j, y2); - points[pts++] = QPoint(xe_0, y4); - points[pts++] = QPoint(xs_0, y4); - points[pts++] = QPoint(xs_j, y2); - - p.drawConvexPolygon(points, pts); // Help says may be faster on some platforms (X11). - } - else - if(het == MusECore::Part::LeftEventsHidden) - { - points[pts++] = QPoint(xs_0, y0); - points[pts++] = QPoint(xe_0, y0); - points[pts++] = QPoint(xe_0, y4); - points[pts++] = QPoint(xs_0, y4); - points[pts++] = QPoint(xs_j, y2); - - p.drawConvexPolygon(points, pts); - } - else - if(het == MusECore::Part::RightEventsHidden) - { - points[pts++] = QPoint(xs_0, y0); - points[pts++] = QPoint(xe_0, y0); - points[pts++] = QPoint(xe_j, y2); - points[pts++] = QPoint(xe_0, y4); - points[pts++] = QPoint(xs_0, y4); - - p.drawConvexPolygon(points, pts); - } - // Draw remaining 'hidden events' decorations with 'jagged' edges... - + int part_r, part_g, part_b, brightness, color_brightness; MusEGlobal::config.partColors[cidx].getRgb(&part_r, &part_g, &part_b); brightness = part_r*29 + part_g*59 + part_b*12; if ((brightness >= 12000 && !part->selected())) - color_brightness=96; //0; // too light: use dark color + color_brightness=96; //0; // too light: use dark color else - color_brightness=180; //255; // too dark: use lighter color + color_brightness=180; //255; // too dark: use lighter color QColor c(color_brightness,color_brightness,color_brightness, MusEGlobal::config.globalAlphaBlend); p.setBrush(QBrush(MusECore::gGradientFromQColor(c, rr.topLeft(), rr.bottomLeft()))); if(het & MusECore::Part::RightEventsHidden) @@ -2108,8 +1706,8 @@ points[pts++] = QPoint(xe_0, y0); points[pts++] = QPoint(xe_0, y4); points[pts++] = QPoint(xe_j, y2); - - p.drawConvexPolygon(points, pts); + + p.drawConvexPolygon(points, pts); } if(het & MusECore::Part::LeftEventsHidden) { @@ -2117,52 +1715,47 @@ points[pts++] = QPoint(xs_0, y0); points[pts++] = QPoint(xs_j, y2); points[pts++] = QPoint(xs_0, y4); - - p.drawConvexPolygon(points, pts); + + p.drawConvexPolygon(points, pts); } - } + } else { p.fillRect(rr & mr, brush); // Respect the requested drawing rectangle. Gives speed boost! } - + // Draw a pattern brush on muted parts... if(part->mute()) { p.setPen(Qt::NoPen); brush.setStyle(Qt::Dense7Pattern); - - p.fillRect(rr & mr, brush); // Respect the requested drawing rectangle. Gives speed boost! - } - + + p.fillRect(rr & mr, brush); // Respect the requested drawing rectangle. Gives speed boost! + } + p.setWorldMatrixEnabled(true); - + MusECore::MidiPart* mp = 0; MusECore::WavePart* wp = 0; MusECore::Track::TrackType type = part->track()->type(); - if (type == MusECore::Track::WAVE) { + if (type == MusECore::Track::WAVE) wp =(MusECore::WavePart*)part; - } - else { + else mp = (MusECore::MidiPart*)part; - } if (wp) drawWavePart(p, rect, wp, r); else if (mp) - { - drawMidiPart(p, rect, mp->events(), (MusECore::MidiTrack*)part->track(), mp, r, mp->tick(), from, to); - } + drawMidiPart(p, rect, mp, r, from, to); p.setWorldMatrixEnabled(false); - - #if 1 // DELETETHIS remove wrapping #if + // // Now draw the borders, using custom segments... // - + p.setBrush(Qt::NoBrush); - + QColor pc((part->mute() || item->isMoving())? Qt::white : MusEGlobal::config.partColors[cidx]); QPen penSelect1H(pc); QPen penSelect2H(pc, 2.0); @@ -2172,7 +1765,7 @@ penSelect2H.setCosmetic(true); penSelect1V.setCosmetic(true); penSelect2V.setCosmetic(true); - + pc = Qt::black; QPen penNormal1H(pc); QPen penNormal2H(pc, 2.0); @@ -2182,29 +1775,29 @@ penNormal2H.setCosmetic(true); penNormal1V.setCosmetic(true); penNormal2V.setCosmetic(true); - + QVector customDashPattern; - if(clone) + if(part->hasClones()) { customDashPattern << 4.0 << 6.0; penSelect1H.setDashPattern(customDashPattern); penNormal1H.setDashPattern(customDashPattern); - penSelect1V.setDashPattern(customDashPattern); - penNormal1V.setDashPattern(customDashPattern); - penSelect1V.setDashOffset(2.0); + penSelect1V.setDashPattern(customDashPattern); + penNormal1V.setDashPattern(customDashPattern); + penSelect1V.setDashOffset(2.0); penNormal1V.setDashOffset(2.0); - //penHidden1.setDashPattern(customDashPattern); + //penHidden1.setDashPattern(customDashPattern); customDashPattern.clear(); customDashPattern << 2.0 << 3.0; penSelect2H.setDashPattern(customDashPattern); penNormal2H.setDashPattern(customDashPattern); - penSelect2V.setDashPattern(customDashPattern); - penNormal2V.setDashPattern(customDashPattern); - penSelect2V.setDashOffset(1.0); - penNormal2V.setDashOffset(1.0); - //penHidden2.setDashPattern(customDashPattern); - - // FIXME: Some shifting still going on. Values likely not quite right here. + penSelect2V.setDashPattern(customDashPattern); + penNormal2V.setDashPattern(customDashPattern); + penSelect2V.setDashOffset(1.0); + penNormal2V.setDashOffset(1.0); + //penHidden2.setDashPattern(customDashPattern); + + // FIXME: Some shifting still going on. Values likely not quite right here. int xdiff = mrxs_0 - lbx; if(xdiff > 0) { @@ -2215,13 +1808,13 @@ penSelect2H.setDashOffset(doff); penNormal2H.setDashOffset(doff); } - } - + } + if(((NPart*)item)->rightBorderTouches) p.setPen(part->selected() ? penSelect1V : penNormal1V); else p.setPen(part->selected() ? penSelect2V : penNormal2V); - + if(rbx >= mrxs_0 && rbx <= mrxe_0) // Respect the requested drawing rectangle. Gives speed boost! { QLine l2(rbx, ys_0, rbx, ye_0); // Right @@ -2232,23 +1825,21 @@ p.setPen(part->selected() ? penSelect1V : penNormal1V); else p.setPen(part->selected() ? penSelect2V : penNormal2V); - + if(xs_0 >= mrxs_0 && xs_0 <= mrxe_0) { QLine l4(xs_0, ys_0, xs_0, ye_0); // Left p.drawLine(l4); // Left line } - + p.setPen(part->selected() ? penSelect2H : penNormal2H); - + // Respect the requested drawing rectangle. Gives speed boost! QLine l1(lbx_c, ys_0, rbx_c, ys_0); p.drawLine(l1); // Top line QLine l3(lbx_c, ye_0, rbx_c, ye_0); p.drawLine(l3); // Bottom line - - #endif - + if (MusEGlobal::config.canvasShowPartType & 1) { // show names // draw name // FN: Set text color depending on part color (black / white) @@ -2259,21 +1850,20 @@ tr.setX(tr.x() + 3); MusECore::gGradientFromQColor(MusEGlobal::config.partColors[cidx], tr.topLeft(), tr.bottomLeft()).stops().last().second.getRgb(&part_r, &part_g, &part_b); brightness = part_r*29 + part_g*59 + part_b*12; - //bool rev = (brightness < 12000 || part->selected()) && !part->mute() && !item->isMoving(); DELETETHIS bool rev = brightness >= 12000 && !part->selected(); p.setFont(MusEGlobal::config.fonts[4]); if (rev) - p.setPen(Qt::white); + p.setPen(Qt::white); else - p.setPen(Qt::black); + p.setPen(Qt::black); p.drawText(tr.translated(1, 1), Qt::AlignBottom|Qt::AlignLeft, part->name()); if (rev) - p.setPen(Qt::black); + p.setPen(Qt::black); else - p.setPen(Qt::white); + p.setPen(Qt::white); p.drawText(tr, Qt::AlignBottom|Qt::AlignLeft, part->name()); } - + p.setWorldMatrixEnabled(true); } @@ -2286,7 +1876,7 @@ { p.setPen( Qt::black); MusECore::Part* part = ((NPart*)item)->part(); - QColor c(part->mute() ? Qt::white : MusEGlobal::config.partColors[part->colorIndex()]); + QColor c(part->mute() ? Qt::white : MusEGlobal::config.partColors[part->colorIndex()]); c.setAlpha(128); // Fix this regardless of config.globalAlphaBlend setting. Should be OK. p.setBrush(c); MusECore::TrackList* tl = MusEGlobal::song->tracks(); @@ -2312,12 +1902,17 @@ // pr - part rectangle //--------------------------------------------------------- -void PartCanvas::drawMidiPart(QPainter& p, const QRect&, MusECore::EventList* events, MusECore::MidiTrack *mt, MusECore::MidiPart *pt, const QRect& r, int pTick, int from, int to) +void PartCanvas::drawMidiPart(QPainter& p, const QRect& rect, MusECore::MidiPart* midipart, const QRect& r, int from, int to) +{ + drawMidiPart(p, rect, midipart->events(), midipart->track(), midipart, r, midipart->tick(), from, to); +} + +void PartCanvas::drawMidiPart(QPainter& p, const QRect&, const MusECore::EventList& events, MusECore::MidiTrack *mt, MusECore::MidiPart *pt, const QRect& r, int pTick, int from, int to) { int color_brightness; QColor eventColor; - - if(pt) + + if(pt) { int part_r, part_g, part_b, brightness; MusEGlobal::config.partColors[pt->colorIndex()].getRgb(&part_r, &part_g, &part_b); @@ -2335,15 +1930,15 @@ eventColor=QColor(80,80,80); color_brightness=80; } - + if (MusEGlobal::config.canvasShowPartType & 2) { // show events p.setPen(eventColor); // Do not allow this, causes segfault. if(from <= to) { - MusECore::iEvent ito(events->lower_bound(to)); + MusECore::ciEvent ito(events.lower_bound(to)); - for (MusECore::iEvent i = events->lower_bound(from); i != ito; ++i) { + for (MusECore::ciEvent i = events.lower_bound(from); i != ito; ++i) { MusECore::EventType type = i->second.type(); int a = i->second.dataA() | 0xff; if ( @@ -2368,20 +1963,20 @@ else { // show Cakewalk Style using std::map; using std::pair; - - MusECore::iEvent ito(events->lower_bound(to)); + + MusECore::ciEvent ito(events.lower_bound(to)); bool isdrum = (mt->type() == MusECore::Track::DRUM || mt->type() == MusECore::Track::NEW_DRUM); // draw controllers ------------------------------------------ p.setPen(QColor(192,192,color_brightness/2)); - for (MusECore::iEvent i = events->begin(); i != ito; ++i) { // PITCH BEND + for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { // PITCH BEND int t = i->first + pTick; MusECore::EventType type = i->second.type(); if (type == MusECore::Controller) { int ctrl_type=i->second.dataA(); int val=i->second.dataB(); - + int th = int(mt->height() * 0.75); // only draw on three quarters int hoffset = (mt->height() - th ) / 2; // offset from bottom @@ -2391,14 +1986,14 @@ } p.setPen(QColor(192,color_brightness/2,color_brightness/2)); - for (MusECore::iEvent i = events->begin(); i != ito; ++i) { // PAN + for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { // PAN int t = i->first + pTick; MusECore::EventType type = i->second.type(); if (type == MusECore::Controller) { int ctrl_type=i->second.dataA(); int val=i->second.dataB(); - + int th = int(mt->height() * 0.75); // only draw on three quarters int hoffset = (mt->height() - th ) / 2; // offset from bottom @@ -2408,14 +2003,14 @@ } p.setPen(QColor(color_brightness/2,192,color_brightness/2)); - for (MusECore::iEvent i = events->begin(); i != ito; ++i) { // VOLUME + for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { // VOLUME int t = i->first + pTick; MusECore::EventType type = i->second.type(); if (type == MusECore::Controller) { int ctrl_type=i->second.dataA(); int val=i->second.dataB(); - + int th = int(mt->height() * 0.75); // only draw on three quarters int hoffset = (mt->height() - th ) / 2; // offset from bottom @@ -2425,13 +2020,13 @@ } p.setPen(QColor(0,0,255)); - for (MusECore::iEvent i = events->begin(); i != ito; ++i) { // PROGRAM CHANGE + for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { // PROGRAM CHANGE int t = i->first + pTick; MusECore::EventType type = i->second.type(); if (type == MusECore::Controller) { int ctrl_type=i->second.dataA(); - + int th = int(mt->height() * 0.75); // only draw on three quarters int hoffset = (mt->height() - th ) / 2; // offset from bottom @@ -2449,10 +2044,10 @@ int lowest_pitch=127; int highest_pitch=0; map y_mapper; - + if (MusEGlobal::config.canvasShowPartType & 4) //y-stretch? { - for (MusECore::iEvent i = events->begin(); i != events->end(); ++i) + for (MusECore::ciEvent i = events.begin(); i != events.end(); ++i) { if (i->second.type()==MusECore::Note) { @@ -2469,7 +2064,7 @@ } } } - + if (isdrum) { int cnt=0; @@ -2481,23 +2076,23 @@ lowest_pitch=0; highest_pitch=cnt-1; } - + if (lowest_pitch==highest_pitch) { lowest_pitch--; highest_pitch++; } - + if (MusEGlobal::heavyDebugMsg) { if (!isdrum) - printf("DEBUG: arranger: cakewalk enabled, y-stretching from %i to %i. eventlist=%p\n",lowest_pitch, highest_pitch, events); + printf("DEBUG: arranger: cakewalk enabled, y-stretching from %i to %i.\n",lowest_pitch, highest_pitch); else { printf("DEBUG: arranger: cakewalk enabled, y-stretching drums: ");; for (map::iterator it=y_mapper.begin(); it!=y_mapper.end(); it++) printf("%i ", it->first); - printf("; eventlist=%p\n",events); + printf("\n"); } } } @@ -2507,14 +2102,14 @@ highest_pitch=127; if (isdrum) - for (int cnt=0;cnt<127;cnt++) + for (int cnt=0;cnt<128;cnt++) y_mapper[cnt]=cnt; - + if (MusEGlobal::heavyDebugMsg) printf("DEBUG: arranger: cakewalk enabled, y-stretch disabled\n"); } p.setPen(eventColor); - for (MusECore::iEvent i = events->begin(); i != ito; ++i) { + for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { int t = i->first + pTick; int te = t + i->second.lenTick(); @@ -2523,7 +2118,7 @@ if (te >= (to + pTick)) te = lrint(rmapxDev_f(rmapx_f(to + pTick) - 1.0)); - + MusECore::EventType type = i->second.type(); if (type == MusECore::Note) { int pitch = i->second.pitch(); @@ -2534,13 +2129,111 @@ y = hoffset + r.y() + th - (pitch-lowest_pitch)*th/(highest_pitch-lowest_pitch); else y = hoffset + r.y() + y_mapper[pitch]*th/(highest_pitch-lowest_pitch); - + p.drawLine(t, y, te, y); } } } } +void PartCanvas::drawWaveSndFile(QPainter &p, MusECore::SndFileR &f, int samplePos, unsigned rootFrame, unsigned startFrame, unsigned lengthFrames, int startY, int startX, int endX, int rectHeight) +{ + int h = rectHeight >> 1; + int x1 = startX; + int x2 = endX; + if (f.isNull()) + return; + unsigned channels = f.channels(); + if (channels == 0) { + printf("drawWavePart: channels==0! %s\n", f.name().toLatin1().constData()); + return; + } + + int xScale; + int pos; + int tickstep = rmapxDev(1); + int postick = MusEGlobal::tempomap.frame2tick(rootFrame + startFrame); + int eventx = mapx(postick); + int drawoffset; + if((x1 - eventx) < 0) { + drawoffset = 0; + } + else { + drawoffset = rmapxDev(x1 - eventx); + } + postick += drawoffset; + pos = samplePos + MusEGlobal::tempomap.tick2frame(postick) - rootFrame - startFrame; + + int i; + if(x1 < eventx) + i = eventx; + else + i = x1; + int ex = mapx(MusEGlobal::tempomap.frame2tick(rootFrame + startFrame + lengthFrames)); + if(ex > x2) + ex = x2; + if (h < 20) { + // combine multi channels into one waveform + int y = startY + h; + int cc = rectHeight % 2 ? 0 : 1; + for (; i < ex; i++) { + MusECore::SampleV sa[channels]; + xScale = MusEGlobal::tempomap.deltaTick2frame(postick, postick + tickstep); + f.read(sa, xScale, pos, true, false); + postick += tickstep; + pos += xScale; + int peak = 0; + int rms = 0; + for (unsigned k = 0; k < channels; ++k) { + if (sa[k].peak > peak) + peak = sa[k].peak; + rms += sa[k].rms; + } + rms /= channels; + peak = (peak * (rectHeight-2)) >> 9; + rms = (rms * (rectHeight-2)) >> 9; + int outer = peak; + int inner = peak -1; //-1 < 0 ? 0 : peak -1; + p.setPen(MusEGlobal::config.partWaveColorPeak); + p.drawLine(i, y - outer - cc, i, y + outer); + p.setPen(MusEGlobal::config.partWaveColorRms); + if (MusEGlobal::config.waveDrawing == MusEGlobal::WaveRmsPeak) + p.drawLine(i, y - rms - cc, i, y + rms); + else // WaveOutLine + p.drawLine(i, y - inner - cc, i, y + inner); + } + } + else { + // multi channel display + int hm = rectHeight / (channels * 2); + int cc = rectHeight % (channels * 2) ? 0 : 1; + for (; i < ex; i++) { + int y = startY + hm; + MusECore::SampleV sa[channels]; + xScale = MusEGlobal::tempomap.deltaTick2frame(postick, postick + tickstep); + f.read(sa, xScale, pos, true, false); + postick += tickstep; + pos += xScale; + for (unsigned k = 0; k < channels; ++k) { + int peak = (sa[k].peak * (hm - 1)) >> 8; + int rms = (sa[k].rms * (hm - 1)) >> 8; + int outer = peak; + int inner = peak -1; //-1 < 0 ? 0 : peak -1; + p.setPen(MusEGlobal::config.partWaveColorPeak); + p.drawLine(i, y - outer - cc , i, y + outer); + p.setPen(MusEGlobal::config.partWaveColorRms); + if (MusEGlobal::config.waveDrawing == MusEGlobal::WaveRmsPeak) + p.drawLine(i, y - rms - cc, i, y + rms); + else // WaveOutLine + p.drawLine(i, y - inner - cc, i, y + inner); + + y += 2 * hm; + } + } + } + +} + //--------------------------------------------------------- // drawWavePart // bb - bounding box of paint area @@ -2565,91 +2258,16 @@ if (x2 > width()) x2 = width(); int hh = pr.height(); - int h = hh/2; - int y = pr.y() + h; + //int h = hh/2; + int startY = pr.y(); + + for (MusECore::ciEvent e = wp->events().begin(); e != wp->events().end(); ++e) { - MusECore::EventList* el = wp->events(); - for (MusECore::iEvent e = el->begin(); e != el->end(); ++e) { - int cc = hh % 2 ? 0 : 1; MusECore::Event event = e->second; - MusECore::SndFileR f = event.sndFile(); - if (f.isNull()) - continue; - unsigned channels = f.channels(); - if (channels == 0) { - printf("drawWavePart: channels==0! %s\n", f.name().toLatin1().constData()); - continue; - } + MusECore::SndFileR f = event.sndFile(); + drawWaveSndFile(p, f, event.spos(), wp->frame(), event.frame(), event.lenFrame(), startY, x1, x2, hh); + - int xScale; - int pos; - int tickstep = rmapxDev(1); - int postick = MusEGlobal::tempomap.frame2tick(wp->frame() + event.frame()); - int eventx = mapx(postick); - int drawoffset; - if((x1 - eventx) < 0) - drawoffset = 0; - else - drawoffset = rmapxDev(x1 - eventx); - postick += drawoffset; - pos = event.spos() + MusEGlobal::tempomap.tick2frame(postick) - wp->frame() - event.frame(); - - int i; - if(x1 < eventx) - i = eventx; - else - i = x1; - int ex = mapx(MusEGlobal::tempomap.frame2tick(wp->frame() + event.frame() + event.lenFrame())); - if(ex > x2) - ex = x2; - if (h < 20) { - // combine multi channels into one waveform - - for (; i < ex; i++) { - MusECore::SampleV sa[channels]; - xScale = MusEGlobal::tempomap.deltaTick2frame(postick, postick + tickstep); - f.read(sa, xScale, pos); - postick += tickstep; - pos += xScale; - int peak = 0; - int rms = 0; - for (unsigned k = 0; k < channels; ++k) { - if (sa[k].peak > peak) - peak = sa[k].peak; - rms += sa[k].rms; - } - rms /= channels; - peak = (peak * (hh-2)) >> 9; - rms = (rms * (hh-2)) >> 9; - p.setPen(MusEGlobal::config.partWaveColorPeak); - p.drawLine(i, y - peak - cc, i, y + peak); - p.setPen(MusEGlobal::config.partWaveColorRms); - p.drawLine(i, y - rms - cc, i, y + rms); - } - } - else { - // multi channel display - int hm = hh / (channels * 2); - int cc = hh % (channels * 2) ? 0 : 1; - for (; i < ex; i++) { - y = pr.y() + hm; - MusECore::SampleV sa[channels]; - xScale = MusEGlobal::tempomap.deltaTick2frame(postick, postick + tickstep); - f.read(sa, xScale, pos); - postick += tickstep; - pos += xScale; - for (unsigned k = 0; k < channels; ++k) { - int peak = (sa[k].peak * (hm - 1)) >> 8; - int rms = (sa[k].rms * (hm - 1)) >> 8; - p.setPen(MusEGlobal::config.partWaveColorPeak); - p.drawLine(i, y - peak - cc, i, y + peak); - p.setPen(MusEGlobal::config.partWaveColorRms); - p.drawLine(i, y - rms - cc, i, y + rms); - - y += 2 * hm; - } - } - } } p.restore(); } @@ -2670,9 +2288,9 @@ case CMD_CUT_PART: { copy(&pl); - + MusECore::Undo operations; - + for (iCItem i = items.begin(); i != items.end(); ++i) { if (i->second->isSelected()) { NPart* p = (NPart*)(i->second); @@ -2680,9 +2298,9 @@ operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeletePart, part)); } } - + MusEGlobal::song->applyOperationGroup(operations); - + break; } case CMD_COPY_PART: @@ -2708,7 +2326,7 @@ unsigned temp_begin = AL::sigmap.raster1(MusEGlobal::song->vcpos(),0); unsigned temp_end = AL::sigmap.raster2(temp_begin + MusECore::get_paste_len(), 0); paste_dialog->raster = temp_end - temp_begin; - + if (paste_dialog->exec()) { paste_mode_t paste_mode; @@ -2723,7 +2341,7 @@ paste(paste_dialog->clone, paste_mode, paste_dialog->all_in_one_track, paste_dialog->number, paste_dialog->raster); } - + break; } case CMD_INSERT_EMPTYMEAS: @@ -2746,7 +2364,7 @@ MusECore::PartList result_pl; unsigned int lpos = MusEGlobal::song->lpos(); unsigned int rpos = MusEGlobal::song->rpos(); - + if (pl_->empty()) { for (iCItem i = items.begin(); i != items.end(); ++i) @@ -2758,48 +2376,42 @@ } else { - for(MusECore::ciPart p = pl_->begin(); p != pl_->end(); ++p) + for(MusECore::ciPart p = pl_->begin(); p != pl_->end(); ++p) if ( (p->second->track()->isMidiTrack()) || (p->second->track()->type() == MusECore::Track::WAVE) ) pl.add(p->second); } - + if (!pl.empty() && (rpos>lpos)) { - for(MusECore::ciPart p = pl.begin(); p != pl.end(); ++p) + for(MusECore::ciPart p = pl.begin(); p != pl.end(); ++p) { MusECore::Part* part=p->second; - MusECore::Track* track=part->track(); - if ((part->tick() < rpos) && (part->endTick() > lpos)) //is the part in the range? { if ((lpos > part->tick()) && (lpos < part->endTick())) { MusECore::Part* p1; MusECore::Part* p2; - - track->splitPart(part, lpos, p1, p2); - p1->events()->incARef(-1); - p2->events()->incARef(-1); - + + part->splitPart(lpos, p1, p2); + part=p2; } - + if ((rpos > part->tick()) && (rpos < part->endTick())) { MusECore::Part* p1; MusECore::Part* p2; - - track->splitPart(part, rpos, p1, p2); - p1->events()->incARef(-1); - p2->events()->incARef(-1); + + part->splitPart(rpos, p1, p2); part=p1; } - + result_pl.add(part); } } - + copy(&result_pl); } } @@ -2810,7 +2422,7 @@ return; bool wave = false; bool midi = false; - for(MusECore::ciPart p = pl->begin(); p != pl->end(); ++p) + for(MusECore::ciPart p = pl->begin(); p != pl->end(); ++p) { if(p->second->track()->isMidiTrack()) midi = true; @@ -2818,11 +2430,11 @@ if(p->second->track()->type() == MusECore::Track::WAVE) wave = true; if(midi && wave) - break; + break; } if(!(midi || wave)) return; - + //--------------------------------------------------- // write parts as XML into tmp file //--------------------------------------------------- @@ -2837,13 +2449,13 @@ // Clear the copy clone list. MusEGlobal::cloneList.clear(); - + int level = 0; int tick = 0; for (MusECore::ciPart p = pl->begin(); p != pl->end(); ++p) { // Indicate this is a copy operation. Also force full wave paths. p->second->write(level, xml, true, true); - + int endTick = p->second->endTick(); if (endTick > tick) tick = endTick; @@ -2864,7 +2476,7 @@ MusECore::Undo PartCanvas::pasteAt(const QString& pt, MusECore::Track* track, unsigned int pos, bool clone, bool toTrack, int* finalPosPtr, set* affected_tracks) { MusECore::Undo operations; - + QByteArray ba = pt.toLatin1(); const char* ptxt = ba.constData(); MusECore::Xml xml(ptxt); @@ -2884,10 +2496,10 @@ end = true; break; case MusECore::Xml::TagStart: - if (tag == "part") { + if (tag == "part") { // Read the part. MusECore::Part* p = 0; - p = readXmlPart(xml, track, clone, toTrack); + p = MusECore::Part::readFromXml(xml, track, clone, toTrack); // If it could not be created... if(!p) @@ -2895,14 +2507,11 @@ // Increment the number of parts not done and break. ++notDone; break; - } + } - p->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it - // so we must decrement it first :/ - // Increment the number of parts done. ++done; - + if (firstPart) { firstPart=false; posOffset=pos-p->tick(); @@ -2928,8 +2537,8 @@ if(end) break; } - - + + if(notDone) { int tot = notDone + done; @@ -2937,7 +2546,7 @@ (tot > 1 ? tr("%n part(s) out of %1 could not be pasted.\nLikely the selected track is the wrong type.","",notDone).arg(tot) : tr("%n part(s) could not be pasted.\nLikely the selected track is the wrong type.","",notDone))); } - + if (finalPosPtr) *finalPosPtr=finalPos; return operations; } @@ -2951,10 +2560,10 @@ void PartCanvas::paste(bool clone, paste_mode_t paste_mode, bool to_single_track, int amount, int raster) { MusECore::Track* track = 0; - + // If we want to paste to a selected track... if (to_single_track) - { + { MusECore::TrackList* tl = MusEGlobal::song->tracks(); for (MusECore::ciTrack i = tl->begin(); i != tl->end(); ++i) { if ((*i)->selected()) { @@ -2976,45 +2585,45 @@ QClipboard* cb = QApplication::clipboard(); const QMimeData* md = cb->mimeData(QClipboard::Clipboard); - + QString pfx("text/"); QString mdpl("x-muse-midipartlist"); - QString wvpl("x-muse-wavepartlist"); + QString wvpl("x-muse-wavepartlist"); QString mxpl("x-muse-mixedpartlist"); QString txt; - + if(md->hasFormat(pfx + mdpl)) { // If we want to paste to a selected track... - if(to_single_track && !track->isMidiTrack()) + if(to_single_track && !track->isMidiTrack()) { QMessageBox::critical(this, QString("MusE"), tr("Can only paste to midi/drum track")); return; } - txt = cb->text(mdpl, QClipboard::Clipboard); + txt = cb->text(mdpl, QClipboard::Clipboard); } else if(md->hasFormat(pfx + wvpl)) { // If we want to paste to a selected track... - if(to_single_track && track->type() != MusECore::Track::WAVE) + if(to_single_track && track->type() != MusECore::Track::WAVE) { QMessageBox::critical(this, QString("MusE"), tr("Can only paste to wave track")); return; } - txt = cb->text(wvpl, QClipboard::Clipboard); - } + txt = cb->text(wvpl, QClipboard::Clipboard); + } else if(md->hasFormat(pfx + mxpl)) { // If we want to paste to a selected track... - if(to_single_track && !track->isMidiTrack() && track->type() != MusECore::Track::WAVE) + if(to_single_track && !track->isMidiTrack() && track->type() != MusECore::Track::WAVE) { QMessageBox::critical(this, QString("MusE"), tr("Can only paste to midi or wave track")); return; } - txt = cb->text(mxpl, QClipboard::Clipboard); + txt = cb->text(mxpl, QClipboard::Clipboard); } else { @@ -3022,7 +2631,7 @@ tr("Cannot paste: wrong data type")); return; } - + if (!txt.isEmpty()) { int endPos=0; @@ -3030,32 +2639,32 @@ set affected_tracks; deselectAll(); - + MusECore::Undo operations; for (int i=0;isetPos(0, p); - + if (paste_mode != PASTEMODE_MIX) { int offset; if (amount==1) offset = endPos-startPos; else offset = amount*raster; - + MusECore::Undo temp; if (paste_mode==PASTEMODE_MOVESOME) temp=MusECore::movePartsTotheRight(startPos, offset, false, &affected_tracks); else temp=MusECore::movePartsTotheRight(startPos, offset); - + operations.insert(operations.end(), temp.begin(), temp.end()); } - + MusEGlobal::song->applyOperationGroup(operations); } @@ -3100,23 +2709,23 @@ char* fbuf = (char*)mmap(0, n, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(tmp), 0); fbuf[n] = 0; - + QByteArray data(fbuf); QMimeData* md = new QMimeData(); - + md->setData("text/x-muse-partlist", data); - - // "MusECore::Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. - // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can + + // "MusECore::Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. + // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can // clean up after the drag and drop operation has been completed. " QDrag* drag = new QDrag(this); drag->setMimeData(md); - + if (t == MOVE_COPY || t == MOVE_CLONE) drag->exec(Qt::CopyAction); else drag->exec(Qt::MoveAction); - + munmap(fbuf, n); fclose(tmp); } @@ -3139,38 +2748,38 @@ if (MusEGlobal::debugMsg) printf("void PartCanvas::viewDropEvent(QDropEvent* event)\n"); if (event->source() == this) { - printf("local DROP\n"); + printf("local DROP\n"); //event->ignore(); // TODO CHECK Tim. return; } int type = 0; // 0 = unknown, 1 = partlist, 2 = uri-list QString text; - - if(event->mimeData()->hasFormat("text/partlist")) + + if(event->mimeData()->hasFormat("text/partlist")) type = 1; - else - if(event->mimeData()->hasUrls()) + else + if(event->mimeData()->hasUrls()) type = 2; - else + else { if(MusEGlobal::debugMsg && event->mimeData()->formats().size() != 0) printf("Drop with unknown format. First format:<%s>\n", event->mimeData()->formats()[0].toLatin1().constData()); //event->ignore(); // TODO CHECK Tim. - return; + return; } - + // Make a backup of the current clone list, to retain any 'copy' items, // so that pasting works properly after. MusECore::CloneList copyCloneList = MusEGlobal::cloneList; // Clear the clone list to prevent any dangerous associations with // current non-original parts. MusEGlobal::cloneList.clear(); - - if (type == 1) + + if (type == 1) { printf("type1\n"); text = QString(event->mimeData()->data("text/partlist")); - + int x = AL::sigmap.raster(event->pos().x(), *_raster); if (x < 0) x = 0; @@ -3185,7 +2794,7 @@ MusEGlobal::song->applyOperationGroup(temp); } } - else if (type == 2) + else if (type == 2) { unsigned trackNo = y2pitch(event->pos().y()); MusECore::Track* track = 0; @@ -3199,26 +2808,22 @@ foreach(QUrl url, event->mimeData()->urls()) { text = url.path(); - - if (text.endsWith(".wav",Qt::CaseInsensitive) || - text.endsWith(".ogg",Qt::CaseInsensitive) || - text.endsWith(".flac",Qt::CaseInsensitive) || + + if (text.endsWith(".wav",Qt::CaseInsensitive) || + text.endsWith(".ogg",Qt::CaseInsensitive) || + text.endsWith(".flac",Qt::CaseInsensitive) || text.endsWith(".mpt", Qt::CaseInsensitive) ) { if (!track) { // we need to create a track for this drop if (text.endsWith(".mpt", Qt::CaseInsensitive)) { - MusECore::Undo operations; - track = MusEGlobal::song->addTrack(operations, MusECore::Track::MIDI); // Add at end of list. - MusEGlobal::song->applyOperationGroup(operations); + track = MusEGlobal::song->addTrack(MusECore::Track::MIDI); // Add at end of list. } else { - MusECore::Undo operations; - track = MusEGlobal::song->addTrack(operations, MusECore::Track::WAVE); // Add at end of list. - MusEGlobal::song->applyOperationGroup(operations); + track = MusEGlobal::song->addTrack(MusECore::Track::WAVE); // Add at end of list. } } if (track->type() == MusECore::Track::WAVE && - (text.endsWith(".wav", Qt::CaseInsensitive) || + (text.endsWith(".wav", Qt::CaseInsensitive) || text.endsWith(".ogg", Qt::CaseInsensitive) || (text.endsWith(".flac", Qt::CaseInsensitive)) )) { @@ -3236,7 +2841,7 @@ { emit dropSongFile(text); break; // we only support ONE drop of this kind - } + } else if(text.endsWith(".mid",Qt::CaseInsensitive)) { emit dropMidiFile(text); @@ -3248,7 +2853,7 @@ track=0; } } - + // Restore backup of the clone list, to retain any 'copy' items, // so that pasting works properly after. MusEGlobal::cloneList.clear(); @@ -3262,18 +2867,16 @@ void PartCanvas::drawCanvas(QPainter& p, const QRect& rect) { int x = rect.x(); - //int y = rect.y(); int w = rect.width(); - //int h = rect.height(); - - // Changed to draw in device coordinate space instead of virtual, transformed space. Tim. p4.0.30 - + + // Changed to draw in device coordinate space instead of virtual, transformed space. Tim. + //QRect mr = p.transform().mapRect(rect); // Gives inconsistent positions. Source shows wrong operation for our needs. QRect mr = map(rect); // Use our own map instead. - + p.save(); p.setWorldMatrixEnabled(false); - + int mx = mr.x(); int my = mr.y(); int mw = mr.width(); @@ -3337,11 +2940,11 @@ //p.drawLine(xt+r*t, y, xt+r*t, y+h); xx = mapx(xt + r * t); p.drawLine(xx, my, xx, my+mh); - } + } } } } - + //-------------------------------- // horizontal lines //-------------------------------- @@ -3359,27 +2962,27 @@ if (MusEGlobal::config.canvasShowGrid && (track->isMidiTrack() || track->type() == MusECore::Track::WAVE)) // Tim. { p.setPen(baseColor.dark(130)); - p.drawLine(mx, yy + th, mx + mw, yy + th); + p.drawLine(mx, yy + th, mx + mw, yy + th); } - - // The update rectangle (rect and mr etc) is clipped at x<0 and y<0 in View::pdraw(). - // The 'corrupt drawing' bug of drawAudioTrack was actually because the recently added gradient + + // The update rectangle (rect and mr etc) is clipped at x<0 and y<0 in View::pdraw(). + // The 'corrupt drawing' bug of drawAudioTrack was actually because the recently added gradient // used the update rectangle, so the gradient was also being clipped at 0,0. - // One could remove that limiter, but no, that is the correct way. So instead let's construct a - // 'pseudo bounding box' (half update rectangle, half bounding box), un-clipped. The gradient needs this! + // One could remove that limiter, but no, that is the correct way. So instead let's construct a + // 'pseudo bounding box' (half update rectangle, half bounding box), un-clipped. The gradient needs this! // - // Here is a different situation than PartCanvas::drawItem which uses un-clipped part bounding boxes and - // does NOT depend on the update rectangle (except to check intersection). That's why this issue - // does not show up there. Should probably try to make that routine more efficient, just like here. Tim. p4.0.30 + // Here is a different situation than PartCanvas::drawItem which uses un-clipped part bounding boxes and + // does NOT depend on the update rectangle (except to check intersection). That's why this issue + // does not show up there. Should probably try to make that routine more efficient, just like here. Tim. QRect r(mx, yy, mw, th); { if (!track->isMidiTrack() && (track->type() != MusECore::Track::WAVE)) { drawAudioTrack(p, mr, r, (MusECore::AudioTrack*)track); - } + } } yy += th; } - + p.restore(); } @@ -3389,17 +2992,17 @@ void PartCanvas::drawTopItem(QPainter& p, const QRect& rect) { QRect mr = map(rect); - + int mx = mr.x(); int my = mr.y(); int mw = mr.width(); int mh = mr.height(); - - QColor baseColor(MusEGlobal::config.partCanvasBg.light(104)); + + //QColor baseColor(MusEGlobal::config.partCanvasBg.light(104)); p.save(); p.setWorldMatrixEnabled(false); - + MusECore::TrackList* tl = MusEGlobal::song->tracks(); int yoff = -rmapy(yorg) - ypos; int yy = yoff; @@ -3413,19 +3016,25 @@ continue; if (!track->isMidiTrack()) { // draw automation QRect r(mx, yy, mw, th); - if(r.intersects(mr)) + if(r.intersects(mr)) { drawAutomation(p, r, (MusECore::AudioTrack*)track); - } + drawAutomationPoints(p, r, (MusECore::AudioTrack*)track); + drawAutomationText(p, r, (MusECore::AudioTrack*)track); + } } yy += th; } - unsigned int startPos = MusEGlobal::extSyncFlag.value() ? MusEGlobal::audio->getStartExternalRecTick() : 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); - int width = mapx(MusEGlobal::song->cpos()) - mapx(startPos); + if(startx < 0) + startx = 0; + int width = mapx(MusEGlobal::song->cpos()) - startx; + + if (MusEGlobal::song->cpos() < startPos) { p.restore(); @@ -3438,6 +3047,7 @@ // write recording while it happens to get feedback // should be enhanced with solution that draws waveform also + // ^^ done int yPos = yoff; if (MusEGlobal::song->record() && MusEGlobal::audio->isPlaying()) { for (MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { @@ -3446,17 +3056,34 @@ if (!th) continue; if (track->recordFlag()) { - QPen pen(Qt::black, 0, Qt::SolidLine); - p.setPen(pen); - QColor c(MusEGlobal::config.partColors[0]); - c.setAlpha(MusEGlobal::config.globalAlphaBlend); - QLinearGradient gradient(QPoint(startx,yPos), QPoint(startx,yPos+th)); - gradient.setColorAt(0, c); - gradient.setColorAt(1, c.darker()); - QBrush cc(gradient); - p.setBrush(cc); + QPen pen(Qt::black, 0, Qt::SolidLine); + p.setPen(pen); + QColor c(MusEGlobal::config.partColors[0]); + c.setAlpha(MusEGlobal::config.globalAlphaBlend); + QLinearGradient gradient(QPoint(startx,yPos), QPoint(startx,yPos+th)); + gradient.setColorAt(0, c); + gradient.setColorAt(1, c.darker()); + QBrush cc(gradient); + p.setBrush(cc); + + //fprintf(stderr, "startx = %d\n", startx); + p.drawRect(startx,yPos, width, th); + + if(track->type() == MusECore::Track::WAVE){ + if(MusEGlobal::config.liveWaveUpdate){ + MusECore::SndFileR fp = ((MusECore::AudioTrack *)track)->recFile(); + if(!fp.isNull()){ + unsigned int _startFrame = MusEGlobal::tempomap.tick2frame(startPos); + unsigned int _endFrame = MusEGlobal::song->cPos().frame(); + unsigned int _lengthFrame = _endFrame - _startFrame; + if(_startFrame <= _endFrame) + { + drawWaveSndFile(p, fp, 0, _startFrame, 0, _lengthFrame, yPos, startx, startx + width, th); + } + } + } - p.drawRect(startx,yPos, width, th); + } } yPos+=th; } @@ -3473,10 +3100,9 @@ MusECore::MidiTrack *mt = (MusECore::MidiTrack*)track; QRect partRect(startPos,yPos, MusEGlobal::song->cpos()-startPos, track->height()); // probably the wrong rect MusECore::EventList myEventList; - MusECore::MPEventList *el = mt->mpevents(); - if (el->size()) { + if (mt->mpevents.size()) { - for (MusECore::ciMPEvent i = el->begin(); i != el->end(); ++i) { + for (MusECore::ciMPEvent i = mt->mpevents.begin(); i != mt->mpevents.end(); ++i) { MusECore::MidiPlayEvent pe = *i; if (pe.isNote() && !pe.isNoteOff()) { @@ -3499,7 +3125,7 @@ } } } - drawMidiPart(p, rect, &myEventList, mt, 0, partRect,startPos,0,MusEGlobal::song->cpos()-startPos); + drawMidiPart(p, rect, myEventList, mt, 0, partRect,startPos,0,MusEGlobal::song->cpos()-startPos); } yPos+=track->height(); } @@ -3512,7 +3138,7 @@ //--------------------------------------------------------- void PartCanvas::drawAudioTrack(QPainter& p, const QRect& r, const QRect& bbox, MusECore::AudioTrack* /*t*/) { - QRect mr = r & bbox; + QRect mr = r & bbox; if(mr.isNull()) return; int mx = mr.x(); @@ -3523,7 +3149,7 @@ int mey = bbox.y(); //int mew = bbox.width(); int meh = bbox.height(); - + p.setPen(Qt::black); QColor c(Qt::gray); c.setAlpha(MusEGlobal::config.globalAlphaBlend); @@ -3531,16 +3157,8 @@ gradient.setColorAt(0, c); gradient.setColorAt(1, c.darker()); QBrush brush(gradient); - p.fillRect(mr, brush); // p4.0.30 ... - - // DELETETHIS 6 - //int xx = -rmapx(xorg) - xpos; - //printf("PartCanvas::drawAudioTrack x:%d y:%d w:%d h:%d th:%d xx:%d\n", r.x(), r.y(), r.width(), r.height(), t->height(), xx); - //if(r.x() <= xx) - // p.drawLine(r.x(), r.y(), r.x(), r.y() + r.height()); // The left edge - //p.drawLine(r.x(), r.y(), r.x() + r.width(), r.y()); // The top edge - //p.drawLine(r.x(), r.y() + r.height(), r.x() + r.width(), r.y() + r.height()); // The bottom edge - + p.fillRect(mr, brush); + if(mex >= mx && mex <= mx + mw) p.drawLine(mex, my, mex, my + mh - 1); // The left edge //if(mex + mew >= mx && mex + mew <= mx + mw) DELETETHIS 2 @@ -3557,17 +3175,11 @@ void PartCanvas::drawAutomation(QPainter& p, const QRect& rr, MusECore::AudioTrack *t) { - //QRect rr = p.worldMatrix().mapRect(r); - //p.save(); - //p.resetTransform(); + const int bottom = rr.bottom() - 2; + const int height = bottom - rr.top() - 2; // limit height - int bottom = rr.bottom() - 2; - int height = bottom - rr.top() - 2; // limit height - - //printf("PartCanvas::drawAutomation x:%d y:%d w:%d h:%d height:%d\n", rr.x(), rr.y(), rr.width(), rr.height(), height); - p.setBrush(Qt::NoBrush); - + MusECore::CtrlListList* cll = t->controller(); for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { @@ -3587,10 +3199,11 @@ int ypixel = oldY; double min, max; cl->range(&min,&max); - bool discrete = cl->mode() == MusECore::CtrlList::DISCRETE; - QPen pen1(cl->color(), 0); - QPen pen2(cl->color(), 2); - pen2.setCosmetic(true); + bool discrete = cl->mode() == MusECore::CtrlList::DISCRETE; + QColor line_color(cl->color()); + line_color.setAlpha(MusEGlobal::config.globalAlphaBlend); + QPen pen1(line_color, 0); + QString txt; // Store first value for later double yfirst; @@ -3614,106 +3227,295 @@ { for (; ic !=cl->end(); ++ic) { - double y = ic->second.val; + double y = ic->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { y = logToVal(y, min, max); // represent volume between 0 and 1 if (y < 0) y = 0.0; } - else + else y = (y-min)/(max-min); // we need to set curVal between 0 and 1 - + ypixel = bottom - rmapy_f(y) * height; xpixel = mapx(MusEGlobal::tempomap.frame2tick(ic->second.frame)); - + if (oldY==-1) oldY = ypixel; - p.setPen(pen1); - if(discrete) + // Ideally we would like to cut the lines right at the rectangle boundaries. + // But they might not be drawn exactly the same as the full line would. + // So we'll also accept anything that started outside the boundaries. + // A small acceptable speed hit relatively speaking - but far far better than drawing all. + if(oldX <= rr.right() && xpixel >= rr.left() && oldY <= rr.bottom() && ypixel >= rr.top()) { - p.drawLine(oldX, oldY, xpixel, oldY); - p.drawLine(xpixel, oldY, xpixel, ypixel); + p.setPen(pen1); + if(discrete) + { + p.drawLine(oldX, oldY, xpixel, oldY); + p.drawLine(xpixel, oldY, xpixel, ypixel); + } + else + p.drawLine(oldX, oldY, xpixel, ypixel); } - else - p.drawLine(oldX, oldY, xpixel, ypixel); - + if (xpixel > rr.right()) break; - // draw a square around the point - pen2.setColor((automation.currentCtrlValid && automation.currentCtrlList == cl && - automation.currentCtrlFrameList.contains(ic->second.frame)) ? - Qt::white : cl->color()); - - p.setPen(pen2); - p.drawRect(xpixel-2, ypixel-2, 5, 5); oldX = xpixel; oldY = ypixel; - if (automation.currentCtrlValid && automation.currentCtrlList == cl && - automation.currentCtrlFrameList.contains(ic->second.frame) && - automation.currentCtrlFrameList.size() == 1) { - double val = ic->second.val; - QRect textRect = rr; - textRect.setX(xpixel + 20); - textRect.setY(ypixel); - if (cl->valueType() == MusECore::VAL_LOG) { - val = MusECore::fast_log10(ic->second.val) * 20.0; - } - p.drawText(textRect, QString("Param: %1, Value: %2").arg(cl->name()).arg(val)); - } } } - p.setPen(pen1); - //int xTextPos = mapx(0) > rmapx(0) ? mapx(0) + 5 : rmapx(0) + 5; // follow window..(doesn't work very well) - int xTextPos = mapx(0) + 5; - p.drawText(xTextPos,yfirst,100,height-2,0,cl->name()); - if (xpixel <= rr.right()) { p.setPen(pen1); p.drawLine(xpixel, ypixel, rr.right(), ypixel); - } + } } } //--------------------------------------------------------- -// checkIfOnLine -// check if our point is on the line defined by -// by first/last X/Y +// drawAutomationPoints //--------------------------------------------------------- -bool checkIfOnLine(double mouseX, double mouseY, double firstX, double lastX, double firstY, double lastY, int circumference) +void PartCanvas::drawAutomationPoints(QPainter& p, const QRect& rr, MusECore::AudioTrack *t) { - if (lastX==firstX) - return (ABS(mouseX-lastX) < circumference); - else if (mouseX < firstX || mouseX > lastX+circumference) // (*) - return false; - else + const int bottom = rr.bottom() - 2; + const int height = bottom - rr.top() - 2; // limit height + const int mx0 = mapx(0); + + const int pw2 = _automationPointWidthUnsel / 2; + const int pws2 = _automationPointWidthSel / 2; + MusECore::CtrlListList* cll = t->controller(); + + // Draw unselected vertices first. + for(MusECore::ciCtrlList icll = cll->begin(); icll != cll->end(); ++icll) + { + MusECore::CtrlList *cl = icll->second; + if(cl->dontShow() || !cl->isVisible()) + continue; + if(rr.right() < mx0) + { + //p.restore(); + return; + } + + double min, max; + cl->range(&min,&max); + const QColor line_col(cl->color()); + const QColor vtx_col1(line_col.red() ^ 255, line_col.green() ^ 255, line_col.blue() ^ 255); + QColor vtx_col2(cl->color()); + vtx_col2.setAlpha(160); + // If we happen to be using 1 pixel, use an inverted colour. Else use the line colour but slightly transparent. + const QColor& vtx_col = (_automationPointWidthUnsel == 1) ? vtx_col1 : vtx_col2; + QPen pen(vtx_col, 0); + p.setPen(pen); + + for(MusECore::ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) + { + const int frame = ic->second.frame; + if(automation.currentCtrlValid && automation.currentCtrlList == cl && automation.currentCtrlFrameList.contains(frame)) + continue; + const int xpixel = mapx(MusEGlobal::tempomap.frame2tick(frame)); + if(xpixel > rr.right()) + break; + + double y = ic->second.val; + if (cl->valueType() == MusECore::VAL_LOG ) { + y = logToVal(y, min, max); // represent volume between 0 and 1 + if(y < 0) y = 0.0; + } + else + y = (y-min)/(max-min); // we need to set curVal between 0 and 1 + + const int ypixel = bottom - rmapy_f(y) * height; + if(((xpixel + pw2 >= rr.left()) && (xpixel - pw2 <= rr.right())) && + ((ypixel + pw2 >= rr.top()) && (ypixel - pw2 <= rr.bottom()))) + //p.fillRect(xpixel - pw2, ypixel - pw2, _automationPointWidthUnsel, _automationPointWidthUnsel, vtx_col); + // For some reason this is drawing one extra pixel width and height. ??? + p.drawRect(xpixel - pw2, ypixel - pw2, _automationPointWidthUnsel, _automationPointWidthUnsel); + } + } + + // Now draw selected vertices, so that they always appear on top. + for(MusECore::ciCtrlList icll = cll->begin(); icll != cll->end(); ++icll) { - 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)*/ + MusECore::CtrlList *cl = icll->second; + if(cl->dontShow() || !cl->isVisible()) + continue; + if(rr.right() < mx0) + { + //p.restore(); + return; + } + + double min, max; + cl->range(&min,&max); + const QColor line_col(cl->color()); + const QColor vtx_col(line_col.red() ^ 255, line_col.green() ^ 255, line_col.blue() ^ 255); + + for(MusECore::ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) + { + const int frame = ic->second.frame; + if(!automation.currentCtrlValid || automation.currentCtrlList != cl || !automation.currentCtrlFrameList.contains(frame)) + continue; + const int xpixel = mapx(MusEGlobal::tempomap.frame2tick(frame)); + if(xpixel > rr.right()) + break; + + double y = ic->second.val; + if (cl->valueType() == MusECore::VAL_LOG ) { + y = logToVal(y, min, max); // represent volume between 0 and 1 + if (y < 0) y = 0.0; + } + else + y = (y-min)/(max-min); // we need to set curVal between 0 and 1 + + const int ypixel = bottom - rmapy_f(y) * height; + if(((xpixel + pws2 >= rr.left()) && (xpixel - pws2 <= rr.right())) && + ((ypixel + pws2 >= rr.top()) && (ypixel - pws2 <= rr.bottom()))) + p.fillRect(xpixel - pws2, ypixel - pws2, _automationPointWidthSel, _automationPointWidthSel, Qt::white); + } + } +} + +//--------------------------------------------------------- +// drawAutomationText +//--------------------------------------------------------- + +void PartCanvas::drawAutomationText(QPainter& p, const QRect& rr, MusECore::AudioTrack *t) +{ + const int bottom = rr.bottom() - 2; + const int height = bottom - rr.top() - 2; // limit height + + p.setBrush(Qt::NoBrush); + p.setFont(font()); + + MusECore::CtrlListList* cll = t->controller(); + for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) + { + MusECore::CtrlList *cl = icll->second; + if (cl->dontShow() || !cl->isVisible()) + continue; + MusECore::iCtrl ic=cl->begin(); + int oldX = mapx(0); + if(rr.right() < oldX) + { + //p.restore(); + return; + } + + int xpixel = 0; + int ypixel = 0; + double min, max; + cl->range(&min,&max); + const QPen pen1(cl->color(), 0); + const QColor line_col = cl->color(); + QColor txt_bk((line_col.red() + line_col.green() + line_col.blue()) / 3 >= 128 ? Qt::black : Qt::white); + txt_bk.setAlpha(150); + QString txt; + + // Store first value for later + double yfirst; + { + if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume + yfirst = logToVal(cl->curVal(), min, max); // represent volume between 0 and 1 + if (yfirst < 0) yfirst = 0.0; + } + else { + yfirst = (cl->curVal() - min)/(max-min); // we need to set curVal between 0 and 1 + } + yfirst = bottom - rmapy_f(yfirst) * height; + } + + p.setPen(pen1); + + for (; ic !=cl->end(); ++ic) + { + double y = ic->second.val; + if (cl->valueType() == MusECore::VAL_LOG ) { + y = logToVal(y, min, max); // represent volume between 0 and 1 + if (y < 0) y = 0.0; + } + else + y = (y-min)/(max-min); // we need to set curVal between 0 and 1 + + ypixel = bottom - rmapy_f(y) * height; + xpixel = mapx(MusEGlobal::tempomap.frame2tick(ic->second.frame)); + + if (xpixel > rr.right()) + break; + + if(xpixel + 20 <= rr.right() && ypixel <= rr.bottom()) + //if(!automation.currentTextRect.isNull() && + // automation.currentTextRect.left() <= rr.right() && + // automation.currentTextRect.top() <= rr.bottom()) + { + if (automation.currentCtrlValid && automation.currentCtrlList == cl && + automation.currentCtrlFrameList.contains(ic->second.frame) && + automation.currentCtrlFrameList.size() == 1) { + QRect textRect = p.fontMetrics().boundingRect(automation.currentText).adjusted(-4, -2, 4, 2); + textRect.moveLeft(xpixel + 20); + textRect.moveTop(ypixel); + if(textRect.right() >= rr.left() && textRect.bottom() >= rr.top()) + { + p.fillRect(textRect, txt_bk); + p.drawText(textRect, Qt::AlignCenter, automation.currentText); + } + } + } + } + + //const int xTextPos = mapx(0) > rmapx(0) ? mapx(0) + 5 : rmapx(0) + 5; // follow window..(doesn't work very well) + const int xTextPos = mapx(0) + 5; + if(xTextPos <= rr.right() && yfirst <= rr.bottom()) + { + QRect textRect = fontMetrics().boundingRect(cl->name()); + textRect.moveLeft(xTextPos); + textRect.moveTop(yfirst); + if(textRect.right() >= rr.left() && textRect.bottom() >= rr.top()) + p.drawText(textRect, cl->name()); + } + } +} + +//--------------------------------------------------------- +// distanceSqToSegment +// Returns the distance, squared, of a point to a line segment. +//--------------------------------------------------------- + +int distanceSqToSegment(double pointX, double pointY, double x1, double y1, double x2, double y2) +{ + double diffX = x2 - x1; + double diffY = y2 - y1; + + if((diffX == 0) && (diffY == 0)) + { + diffX = pointX - x1; + diffY = pointY - y1; + return diffX * diffX + diffY * diffY; + } + + const double t = ((pointX - x1) * diffX + (pointY - y1) * diffY) / (diffX * diffX + diffY * diffY); + + if (t < 0.0) + { + //point is nearest to the first point i.e x1 and y1 + diffX = pointX - x1; + diffY = pointY - y1; + } + else if (t > 1.0) + { + //point is nearest to the end point i.e x2 and y2 + diffX = pointX - x2; + diffY = pointY - y2; + } + else + { + //if perpendicular line intersect the line segment. + diffX = pointX - (x1 + t * diffX); + diffY = pointY - (y1 + t * diffY); + } + + return diffX * diffX + diffY * diffY; } //--------------------------------------------------------- @@ -3738,116 +3540,220 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, bool /*NOTaddNewCtrl*/) { - if (t->isMidiTrack()) + if (t->isMidiTrack()) + return; + + int mouseY; + const int trackY = t->y(); + const int trackH = t->height(); + + { int y = pointer.y(); + if(y < trackY || y >= (trackY + trackH)) return; + mouseY = mapy(y); } - int mouseY; - int trackY = t->y(); - int trackH = t->height(); - - { int y = pointer.y(); - if(y < trackY || y >= (trackY + trackH)) - return; - mouseY = mapy(y); } - - int mouseX = mapx(pointer.x()); - int circumference = 10; - - MusECore::CtrlListList* cll = ((MusECore::AudioTrack*) t)->controller(); - for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) + const int mouseX = mapx(pointer.x()); + + int closest_point_radius2 = PartCanvas::_automationPointDetectDist * PartCanvas::_automationPointDetectDist; + int closest_point_frame = 0; + double closest_point_value = 0.0; + //int closest_point_x = 0; + //int closest_point_y = 0; + MusECore::CtrlList* closest_point_cl = NULL; + + int closest_line_dist2 = PartCanvas::_automationPointDetectDist * PartCanvas::_automationPointDetectDist; + MusECore::CtrlList* closest_line_cl = NULL; + + MusECore::CtrlListList* cll = ((MusECore::AudioTrack*) t)->controller(); + for(MusECore::ciCtrlList icll = cll->begin(); icll != cll->end(); ++icll) + { + MusECore::CtrlList *cl = icll->second; + if(cl->dontShow() || !cl->isVisible()) + continue; + MusECore::ciCtrl ic=cl->begin(); + + int eventOldX = mapx(0); + int eventX = eventOldX; + int eventOldY = -1; + int eventY = eventOldY; + double min, max; + cl->range(&min,&max); + bool discrete = cl->mode() == MusECore::CtrlList::DISCRETE; + + // First check that there IS automation for this controller, ic == cl->end means no automation + if(ic == cl->end()) + { + double y; + if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume + y = logToVal(cl->curVal(), min, max); // represent volume between 0 and 1 + if (y < 0) y = 0.0; + } + else + y = (cl->curVal() - min)/(max-min); // we need to set curVal between 0 and 1 + eventY = eventOldY = mapy(trackY+trackH-1 - 2 - y * trackH); + } + else // we have automation, loop through it + { + for (; ic!=cl->end(); ++ic) { - MusECore::CtrlList *cl = icll->second; - if (cl->dontShow() || !cl->isVisible()) { - continue; + double y = ic->second.val; + if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume + y = logToVal(y, min, max); // represent volume between 0 and 1 + if (y < 0) y = 0; } - MusECore::iCtrl ic=cl->begin(); + else + y = (y-min)/(max-min); // we need to set curVal between 0 and 1 + + eventY = mapy(trackY + trackH - 2 - y * trackH); + eventX = mapx(MusEGlobal::tempomap.frame2tick(ic->second.frame)); - int eventOldX = mapx(0); - int eventX = eventOldX; - int eventOldY = -1; - int eventY = eventOldY; - double min, max; - cl->range(&min,&max); - bool discrete = cl->mode() == MusECore::CtrlList::DISCRETE; + if (eventOldY==-1) eventOldY = eventY; - // First check that there IS automation for this controller, ic == cl->end means no automation - if (ic == cl->end()) + if(pointer.x() > 0 && pointer.y() > 0) { - double y; - if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume - y = logToVal(cl->curVal(), min, max); // represent volume between 0 and 1 - if (y < 0) y = 0.0; - } - else - y = (cl->curVal() - min)/(max-min); // we need to set curVal between 0 and 1 - eventY = eventOldY = mapy(trackY+trackH-1 - 2 - y * trackH); - } - else // we have automation, loop through it - { - for (; ic!=cl->end(); ic++) + const int dx = mouseX - eventX; + const int dy = mouseY - eventY; + const int r2 = dx * dx + dy * dy; + if(r2 < closest_point_radius2) { - double y = ic->second.val; - if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume - y = logToVal(y, min, max); // represent volume between 0 and 1 - if (y < 0) y = 0; - } - else - y = (y-min)/(max-min); // we need to set curVal between 0 and 1 - - eventY = mapy(trackY + trackH - 2 - y * trackH); - eventX = mapx(MusEGlobal::tempomap.frame2tick(ic->second.frame)); - - if (eventOldY==-1) eventOldY = eventY; - - //if (addNewCtrl) { - bool onLine = checkIfOnLine(mouseX, mouseY, eventOldX,eventX, eventOldY, discrete? eventOldY:eventY, circumference); - bool onPoint = false; - if ( pointer.x() > 0 && pointer.y() > 0) - onPoint = checkIfNearPoint(mouseX, mouseY, eventX, eventY, circumference); - - eventOldX = eventX; - eventOldY = eventY; - - if (onLine) { - if (!onPoint) { - QWidget::setCursor(Qt::CrossCursor); - automation.currentCtrlValid = false; - automation.controllerState = addNewController; - }else { - QWidget::setCursor(Qt::OpenHandCursor); - automation.currentCtrlFrameList.clear(); - automation.currentCtrlFrameList.append(ic->second.frame); - automation.currentCtrlValid = true; - automation.controllerState = movingController; - } - automation.currentCtrlList = cl; - automation.currentTrack = t; - update(); - return; - } - } - } - - // we are now after the last existing controller - // 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 && ABS(mouseY-eventY) < circumference) { - QWidget::setCursor(Qt::CrossCursor); - automation.controllerState = addNewController; - automation.currentCtrlList = cl; - automation.currentTrack = t; - automation.currentCtrlValid = false; - return; + closest_point_radius2 = r2; + closest_point_frame = ic->second.frame; + closest_point_value = ic->second.val; + //closest_point_x = eventX; + //closest_point_y = eventY; + closest_point_cl = cl; + } + } + + const int ldist2 = distanceSqToSegment(mouseX, mouseY, eventOldX, eventOldY, eventX, discrete ? eventOldY : eventY); + if(ldist2 < closest_line_dist2) + { + closest_line_dist2 = ldist2; + closest_line_cl = cl; } + + eventOldX = eventX; + eventOldY = eventY; } - // if there are no hits we default to clearing all the data - automation.controllerState = doNothing; - automation.currentCtrlValid = false; - automation.currentCtrlList = 0; - automation.currentTrack = 0; - automation.currentCtrlFrameList.clear(); - setCursor(); + } + + if(mouseX >= eventX) + { + const int d2 = (mouseY-eventY) * (mouseY-eventY); + if(d2 < closest_line_dist2) + { + closest_line_dist2 = d2; + closest_line_cl = cl; + } + } + } + + // Is the mouse close to a vertex? Since we currently don't use the addNewCtrl accel key, vertices take priority over lines. + if(closest_point_cl) + { + QWidget::setCursor(Qt::PointingHandCursor); + automation.currentCtrlFrameList.clear(); + automation.currentCtrlFrameList.append(closest_point_frame); + automation.currentCtrlValid = true; + automation.controllerState = movingController; + automation.currentCtrlList = closest_point_cl; + automation.currentTrack = t; + + // Store the text. + if(closest_point_cl->valueType() == MusECore::VAL_LOG) + //closest_point_value = MusECore::fast_log10(closest_point_value) * 20.0; + closest_point_value = muse_val2dbr(closest_point_value); // Here we can use the slower but accurate function. + automation.currentText = QString("Param:%1 Value:%2").arg(closest_point_cl->name()).arg(closest_point_value); + +// FIXME These are attempts to update only the necessary rectangles. No time for it ATM, not much choice but to do full update. +#if 0 + // Be sure to erase (redraw) the old rectangles. + int erase_ypixel = bottom - rmapy_f(y) * height; + int erase_xpixel = mapx(MusEGlobal::tempomap.frame2tick(ic->second.frame)); + if(!automation.currentTextRect.isNull()) + update(automation.currentTextRect); + if(!automation.currentVertexRect.isNull()) + update(automation.currentVertexRect); + // Store the text and its rectangle. + double value = closest_point_value; + double min, max; + closest_point_cl->range(&min,&max); + if(closest_point_cl->valueType() == MusECore::VAL_LOG) + //closest_point_value = MusECore::fast_log10(closest_point_value) * 20.0; + value = muse_val2dbr(value); // Here we can use the slower but accurate function. + automation.currentText = QString("Param:%1 Frame:%2 Value:%3").arg(closest_point_cl->name()).arg(closest_point_frame).arg(value); +// automation.currentTextRect = fontMetrics().boundingRect(automation.currentText).adjusted(-4, -2, 4, 2); + automation.currentTextRect = fontMetrics().boundingRect(automation.currentText).adjusted(0, 0, 4, 4); +// automation.currentTextRect.moveLeft(closest_point_x + 20); +// automation.currentTextRect.moveTop(closest_point_y); +// if (inLog < min) inLog = min; // Should not happen +// if (inLog > max) inLog = max; +// double linMin = 20.0*MusECore::fast_log10(min); +// double linMax = 20.0*MusECore::fast_log10(max); +// double linVal = 20.0*MusECore::fast_log10(inLog); + const double linMin = muse_val2dbr(min); + const double linMax = muse_val2dbr(max); + //const double n_value = (value - linMin) / (linMax - linMin); // Normalize + automation.currentTick = MusEGlobal::tempomap.frame2tick(closest_point_frame); + automation.currentYNorm = (value - linMin) / (linMax - linMin); // Normalize + // Store the selected vertex rectangle. Use the selected size, which can be different than the unselected size. + automation.currentVertexRect = QRect(closest_point_x - PartCanvas::_automationPointWidthSel / 2, + closest_point_y - PartCanvas::_automationPointWidthSel / 2, + PartCanvas::_automationPointWidthSel, + PartCanvas::_automationPointWidthSel); + // Now fill the text's new rectangle. + update(automation.currentTextRect); + // And fill the vertex's new rectangle. + update(automation.currentVertexRect); +#else + //update(); + controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); +#endif + return; + } + + // Is the mouse close to a line? + if(closest_line_cl) + { + QWidget::setCursor(Qt::CrossCursor); + automation.currentCtrlValid = false; + automation.controllerState = addNewController; + automation.currentCtrlList = closest_line_cl; + automation.currentTrack = t; +#if 0 + // Be sure to erase (refill) the old rectangles. + if(!automation.currentTextRect.isNull()) + update(automation.currentTextRect); + if(!automation.currentVertexRect.isNull()) + update(automation.currentVertexRect); +#else + //update(); + controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); +#endif + return; + } + + if(automation.currentCtrlValid && automation.currentTrack && automation.currentCtrlList) + controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); + + // if there are no hits we default to clearing all the data + automation.controllerState = doNothing; + automation.currentCtrlValid = false; + automation.currentCtrlList = 0; + automation.currentTrack = 0; + automation.currentCtrlFrameList.clear(); +#if 0 + // Be sure to erase (refill) the old rectangles. + if(!automation.currentTextRect.isNull()) + update(automation.currentTextRect); + automation.currentTextRect = QRect(); + if(!automation.currentVertexRect.isNull()) + update(automation.currentVertexRect); + automation.currentVertexRect = QRect(); +#else + //update(); +#endif + setCursor(); } void PartCanvas::controllerChanged(MusECore::Track* t, int) @@ -3855,109 +3761,195 @@ redraw((QRect(0, mapy(t->y()), width(), rmapy(t->height())))); // TODO Check this - correct? } -void PartCanvas::processAutomationMovements(QPoint pos, bool addPoint) +void PartCanvas::processAutomationMovements(QPoint inPos, bool slowMotion) { - if (_tool == AutomationTool) { + if (_tool != AutomationTool) + return; - if (!automation.moveController) { // currently nothing going lets's check for some action. - MusECore::Track * t = y2Track(pos.y()); - if (t) { - checkAutomation(t, pos, addPoint); - } - return; + if (!automation.moveController) { // currently nothing going lets's check for some action. + MusECore::Track * t = y2Track(inPos.y()); + if (t) { + checkAutomation(t, inPos, false); } + automation.startMovePoint = inPos; + return; + } - // automation.moveController is set, lets rock. - int prevFrame = 0; - int nextFrame = -1; + if(automation.controllerState != movingController) + { + automation.startMovePoint = inPos; + return; + } - if (automation.controllerState == addNewController) - { - int frame = MusEGlobal::tempomap.tick2frame(pos.x()); - // FIXME Inefficient to add with wait here, then remove and add with wait again below. Tim. - MusEGlobal::audio->msgAddACEvent((MusECore::AudioTrack*)automation.currentTrack, automation.currentCtrlList->id(), frame, 1.0 /*dummy value */); - - MusECore::iCtrl ic=automation.currentCtrlList->begin(); - for (; ic !=automation.currentCtrlList->end(); ++ic) { - MusECore::CtrlVal &cv = ic->second; - if (cv.frame == frame) { - automation.currentCtrlFrameList.clear(); - automation.currentCtrlFrameList.append(cv.frame); - automation.currentCtrlValid = true; - automation.controllerState = movingController; - break; - } - } - } + Undo operations; + + int deltaX = inPos.x() - automation.startMovePoint.x(); + int deltaY = inPos.y() - automation.startMovePoint.y(); + if (slowMotion) + { + deltaX /= 3; + deltaY /= 3; + } + const QPoint pos(automation.startMovePoint.x() + deltaX, automation.startMovePoint.y() + deltaY); + + const int posy=mapy(pos.y()); + const int tracky = mapy(automation.currentTrack->y()); + const int trackHeight = automation.currentTrack->height(); + + const int mouseY = trackHeight - (posy - tracky)-2; + const double yfraction = ((double)mouseY)/automation.currentTrack->height(); + + double min, max; + automation.currentCtrlList->range(&min,&max); + double cvval; + if (automation.currentCtrlList->valueType() == MusECore::VAL_LOG ) { // use db scale for volume + cvval = valToLog(yfraction, min, max); + if (cvval< min) cvval=min; + if (cvval>max) cvval=max; + } + else { + // we need to set val between 0 and 1 (unless integer) + cvval = yfraction * (max-min) + min; + // 'Snap' to integer or boolean + if (automation.currentCtrlList->mode() == MusECore::CtrlList::DISCRETE) + cvval = rint(cvval + 0.1); // LADSPA docs say add a slight bias to avoid rounding errors. Try this. + if (cvval< min) cvval=min; + if (cvval>max) cvval=max; + } + + // Store the text. + automation.currentText = QString("Param:%1 Value:%2").arg(automation.currentCtrlList->name()).arg(cvval); + + const int fl_sz = automation.currentCtrlFrameList.size(); + for(int i = 0; i < fl_sz; ++i) + { + const int old_frame = automation.currentCtrlFrameList.at(i); + const int old_tick = MusEGlobal::tempomap.frame2tick(old_frame); + const int new_tick = old_tick + deltaX; + const int delta_frame = MusEGlobal::tempomap.deltaTick2frame(old_tick, new_tick); - // get previous and next frame position to give x bounds for this event. - MusECore::iCtrl ic=automation.currentCtrlList->begin(); - MusECore::iCtrl iprev = ic; - for (; ic !=automation.currentCtrlList->end(); ++ic) + MusECore::ciCtrl iold = automation.currentCtrlList->find(old_frame); + if(iold != automation.currentCtrlList->end()) { - MusECore::CtrlVal &cv = ic->second; - if (automation.currentCtrlFrameList.contains(cv.frame)) - { - //currFrame = cv.frame; - break; - } - prevFrame = cv.frame; - iprev = ic; - } - - MusECore::iCtrl icc = ic; - - if ( ++ic != automation.currentCtrlList->end()) { - MusECore::CtrlVal &cv = ic->second; - nextFrame = cv.frame; - } - - // A perfectly straight vertical line (two points with same frame) is impossible: - // there is only one value at t, and the next value at t+1, and so on. - // Also these are maps, not multimaps. p4.0.32 Tim. - int newFrame = MusEGlobal::tempomap.tick2frame(pos.x()); - - if (newFrame <= prevFrame) - newFrame=prevFrame + (icc == automation.currentCtrlList->begin() ? 0: 1); // Only first item is allowed to go to zero x. - if (nextFrame!=-1 && newFrame >= nextFrame) newFrame=nextFrame-1; - - int posy=mapy(pos.y()); - int tracky = mapy(automation.currentTrack->y()); - int trackHeight = automation.currentTrack->height(); + const double old_value = iold->second.val; - int mouseY = trackHeight - (posy - tracky)-2; - double yfraction = ((double)mouseY)/automation.currentTrack->height(); + // The minimum frame that this selected frame can be moved to is the previous + // UNSELECTED vertex frame, PLUS the number of items from here to that vertex... + int min_prev_frame = 0; + MusECore::ciCtrl iprev = iold; + int prev_frame_offset = 0; + while(iprev != automation.currentCtrlList->begin()) + { + --iprev; + ++prev_frame_offset; + // Stop when we find the previous unselected frame. + if(!automation.currentCtrlFrameList.contains(iprev->second.frame)) + { + min_prev_frame = iprev->second.frame + prev_frame_offset; + break; + } + } - double min, max; - automation.currentCtrlList->range(&min,&max); - double cvval; - if (automation.currentCtrlList->valueType() == MusECore::VAL_LOG ) { // use db scale for volume - //printf("log conversion val=%f min=%f max=%f\n", yfraction, min, max); - cvval = valToLog(yfraction, min, max); - if (cvval< min) cvval=min; - if (cvval>max) cvval=max; + // The maximum frame that this selected frame can be moved to is the next + // UNSELECTED vertex frame, MINUS the number of items from here to that vertex... + int max_next_frame = -1; + MusECore::ciCtrl inext = iold; + ++inext; + int next_frame_offset = 1; // Yes, that's 1. + while(inext != automation.currentCtrlList->end()) + { + // Stop when we find the next unselected frame. + if(!automation.currentCtrlFrameList.contains(inext->second.frame)) + { + max_next_frame = inext->second.frame - next_frame_offset; + break; + } + ++inext; + ++next_frame_offset; + } + + int new_frame = old_frame + delta_frame; + if(new_frame < min_prev_frame) + new_frame = min_prev_frame; + if(max_next_frame != -1 && new_frame > max_next_frame) + new_frame = max_next_frame; + + //if(old_frame != new_frame) + //{ + automation.currentCtrlFrameList.replace(i, new_frame); + operations.push_back(UndoOp(UndoOp::ModifyAudioCtrlVal, automation.currentTrack, automation.currentCtrlList->id(), old_frame, new_frame, old_value, cvval)); + //} } - else { - // we need to set val between 0 and 1 (unless integer) - cvval = yfraction * (max-min) + min; - // 'Snap' to integer or boolean - if (automation.currentCtrlList->mode() == MusECore::CtrlList::DISCRETE) - cvval = rint(cvval + 0.1); // LADSPA docs say add a slight bias to avoid rounding errors. Try this. + } + + automation.startMovePoint = inPos; + if(!operations.empty()) + { + operations.combobreaker = automation.breakUndoCombo; + automation.breakUndoCombo = false; // Reset. + + MusEGlobal::song->applyOperationGroup(operations); + // User probably would like to hear results so make sure controller is enabled. + ((MusECore::AudioTrack*)automation.currentTrack)->enableController(automation.currentCtrlList->id(), true); + controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); + } +} + +void PartCanvas::newAutomationVertex(QPoint pos) +{ + if (_tool != AutomationTool || automation.controllerState != addNewController) + return; + + Undo operations; + + const int posy=mapy(pos.y()); + const int tracky = mapy(automation.currentTrack->y()); + const int trackHeight = automation.currentTrack->height(); + + const int mouseY = trackHeight - (posy - tracky)-2; + const double yfraction = ((double)mouseY)/automation.currentTrack->height(); + + double min, max; + automation.currentCtrlList->range(&min,&max); + double cvval; + if (automation.currentCtrlList->valueType() == MusECore::VAL_LOG ) { // use db scale for volume + cvval = valToLog(yfraction, min, max); if (cvval< min) cvval=min; if (cvval>max) cvval=max; - } - - automation.currentCtrlFrameList.clear(); - automation.currentCtrlFrameList.append(newFrame); - automation.currentCtrlValid = true; - - if(icc != automation.currentCtrlList->end()) - MusEGlobal::audio->msgChangeACEvent((MusECore::AudioTrack*)automation.currentTrack, automation.currentCtrlList->id(), icc->second.frame, newFrame, cvval); - else - MusEGlobal::audio->msgAddACEvent((MusECore::AudioTrack*)automation.currentTrack, automation.currentCtrlList->id(), newFrame, cvval); } + else { + // we need to set val between 0 and 1 (unless integer) + cvval = yfraction * (max-min) + min; + // 'Snap' to integer or boolean + if (automation.currentCtrlList->mode() == MusECore::CtrlList::DISCRETE) + cvval = rint(cvval + 0.1); // LADSPA docs say add a slight bias to avoid rounding errors. Try this. + if (cvval< min) cvval=min; + if (cvval>max) cvval=max; + } + + // Store the text. + automation.currentText = QString("Param:%1 Value:%2").arg(automation.currentCtrlList->name()).arg(cvval); + + const int frame = MusEGlobal::tempomap.tick2frame(pos.x()); + operations.push_back(UndoOp(UndoOp::AddAudioCtrlVal, automation.currentTrack, automation.currentCtrlList->id(), frame, cvval)); + automation.currentCtrlFrameList.clear(); + automation.currentCtrlFrameList.append(frame); + automation.currentCtrlValid = true; + automation.controllerState = movingController; + + automation.startMovePoint = pos; + if(!operations.empty()) + { + operations.combobreaker = automation.breakUndoCombo; + automation.breakUndoCombo = false; // Reset. + + MusEGlobal::song->applyOperationGroup(operations); + // User probably would like to hear results so make sure controller is enabled. + ((MusECore::AudioTrack*)automation.currentTrack)->enableController(automation.currentCtrlList->id(), true); + controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); + } } //--------------------------------------------------------- @@ -4017,7 +4009,7 @@ dx = 0; moveCanvasItems(moving, dp, dx, dragtype, rasterize); - + moving.clear(); updateSelection(); redraw(); diff -Nru muse-2.1.2/muse/arranger/pcanvas.h muse-3.0.2+ds1/muse/arranger/pcanvas.h --- muse-2.1.2/muse/arranger/pcanvas.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/pcanvas.h 2017-12-04 21:01:18.000000000 +0000 @@ -42,11 +42,11 @@ class QMenu; namespace MusECore { -class CtrlVal; +struct CtrlVal; class Xml; } -#define beats 4 +//#define beats 4 namespace MusEGui { @@ -77,12 +77,19 @@ enum ControllerVals { doNothing, movingController, addNewController }; struct AutomationObject { + QPoint startMovePoint; QList currentCtrlFrameList; bool currentCtrlValid; MusECore::CtrlList *currentCtrlList; MusECore::Track *currentTrack; bool moveController; ControllerVals controllerState; + QString currentText; + bool breakUndoCombo; + //QRect currentTextRect; + //QRect currentVertexRect; + //int currentTick; + //int currentYNorm; }; //--------------------------------------------------------- @@ -100,6 +107,10 @@ int curColorIndex; bool editMode; + static const int _automationPointDetectDist; + static const int _automationPointWidthUnsel; + static const int _automationPointWidthSel; + QTime editingFinishedTime; AutomationObject automation; @@ -142,17 +153,22 @@ enum paste_mode_t { PASTEMODE_MIX, PASTEMODE_MOVEALL, PASTEMODE_MOVESOME }; void paste(bool clone = false, paste_mode_t paste_mode = PASTEMODE_MIX, bool to_single_track=false, int amount=1, int raster=1536); MusECore::Undo pasteAt(const QString&, MusECore::Track*, unsigned int, bool clone = false, bool toTrack = true, int* finalPosPtr = NULL, std::set* affected_tracks = NULL); + void drawWaveSndFile(QPainter &p, MusECore::SndFileR &f, int samplePos, unsigned rootFrame, unsigned startFrame, unsigned lengthFrames, int startY, int startX, int endX, int rectHeight); void drawWavePart(QPainter&, const QRect&, MusECore::WavePart*, const QRect&); - void drawMidiPart(QPainter&, const QRect& rect, MusECore::EventList* events, MusECore::MidiTrack*mt, MusECore::MidiPart*pt, const QRect& r, int pTick, int from, int to); + void drawMidiPart(QPainter&, const QRect& rect, const MusECore::EventList& events, MusECore::MidiTrack* mt, MusECore::MidiPart* pt, const QRect& r, int pTick, int from, int to); + void drawMidiPart(QPainter&, const QRect& rect, MusECore::MidiPart* midipart, const QRect& r, int from, int to); MusECore::Track* y2Track(int) const; void drawAudioTrack(QPainter& p, const QRect& r, const QRect& bbox, MusECore::AudioTrack* track); void drawAutomation(QPainter& p, const QRect& r, MusECore::AudioTrack* track); + void drawAutomationPoints(QPainter& p, const QRect& r, MusECore::AudioTrack* track); + void drawAutomationText(QPainter& p, const QRect& r, MusECore::AudioTrack* track); void drawTopItem(QPainter& p, const QRect& rect); void checkAutomation(MusECore::Track * t, const QPoint& pointer, bool addNewCtrl); - void processAutomationMovements(QPoint pos, bool addPoint); + void processAutomationMovements(QPoint pos, bool slowMotion); double logToVal(double inLog, double min, double max); double valToLog(double inV, double min, double max); + void newAutomationVertex(QPoint inPos); protected: virtual void drawCanvas(QPainter&, const QRect&); @@ -169,6 +185,9 @@ void trackChanged(MusECore::Track*); void selectTrackAbove(); void selectTrackBelow(); + void editTrackNameSig(); + void muteSelectedTracks(); + void soloSelectedTracks(); void startEditor(MusECore::PartList*, int); diff -Nru muse-2.1.2/muse/arranger/structure.cpp muse-3.0.2+ds1/muse/arranger/structure.cpp --- muse-2.1.2/muse/arranger/structure.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/structure.cpp 2018-01-14 18:26:04.000000000 +0000 @@ -0,0 +1,341 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: structure.cpp,v 1.113.2.68 2009/12/21 14:51:51 spamatica Exp $ +// +// (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Robert Jonsson (rj@spamatica.se) +// (C) Copyright 2013 Florian Jung (flo93@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 +#include "app.h" +#include "track.h" +#include "song.h" +#include "tempo.h" +#include "al/sig.h" +#include "keyevent.h" +#include "audio.h" +#include "marker/marker.h" +#include "structure.h" +#include "globals.h" + +#include +using std::set; + +namespace MusECore { + +//--------------------------------------------------------- +// adjustGlobalLists +// helper that adjusts tempo, sig, key and marker +// lists everything from startPos is adjusted +// 'diff' number of ticks. +//--------------------------------------------------------- + +void adjustGlobalLists(Undo& operations, int startPos, int diff) +{ + const TempoList* t = &MusEGlobal::tempomap; + const AL::SigList* s = &AL::sigmap; + const KeyList* k = &MusEGlobal::keymap; + + criTEvent it = t->rbegin(); + AL::criSigEvent is = s->rbegin(); + criKeyEvent ik = k->rbegin(); + + // key + for (; ik != k->rend(); ik++) { + const KeyEvent &ev = (KeyEvent)ik->second; + int tick = ev.tick; + int key = ev.key; + if (tick < startPos ) + break; + +// REMOVE Tim. global cut. Changed. +// if (tick > startPos && tick +diff < startPos ) { // remove + if (tick >= startPos && tick < startPos + diff) { // remove + operations.push_back(UndoOp(UndoOp::DeleteKey, tick, key)); + } + else { + operations.push_back(UndoOp(UndoOp::DeleteKey,tick, key)); +// REMOVE Tim. global cut. Changed. +// operations.push_back(UndoOp(UndoOp::AddKey,tick+diff, key)); + operations.push_back(UndoOp(UndoOp::AddKey,tick - diff, key)); + } + } + + // tempo + for (; it != t->rend(); it++) { + const TEvent* ev = (TEvent*)it->second; + int tick = ev->tick; + int tempo = ev->tempo; + if (tick < startPos ) + break; + +// REMOVE Tim. global cut. Changed. +// if (tick > startPos && tick +diff < startPos ) { // remove + if (tick >= startPos && tick < startPos + diff) { // remove + operations.push_back(UndoOp(UndoOp::DeleteTempo,tick, tempo)); + } + else { + operations.push_back(UndoOp(UndoOp::DeleteTempo,tick, tempo)); +// REMOVE Tim. global cut. Changed. +// operations.push_back(UndoOp(UndoOp::AddTempo,tick+diff, tempo)); + operations.push_back(UndoOp(UndoOp::AddTempo,tick - diff, tempo)); + } + } + + // sig + for (; is != s->rend(); is++) { + const AL::SigEvent* ev = (AL::SigEvent*)is->second; + int tick = ev->tick; + if (tick < startPos ) + break; + + int z = ev->sig.z; + int n = ev->sig.n; +// REMOVE Tim. global cut. Changed. +// if (tick > startPos && tick +diff < startPos ) { // remove + if (tick >= startPos && tick < startPos + diff) { // remove + operations.push_back(UndoOp(UndoOp::DeleteSig,tick, z, n)); + } + else { + operations.push_back(UndoOp(UndoOp::DeleteSig,tick, z, n)); +// REMOVE Tim. global cut. Changed. +// operations.push_back(UndoOp(UndoOp::AddSig,tick+diff, z, n)); + operations.push_back(UndoOp(UndoOp::AddSig,tick - diff, z, n)); + } + } + + MarkerList *markerlist = MusEGlobal::song->marker(); + for(iMarker i = markerlist->begin(); i != markerlist->end(); ++i) + { + Marker* m = &i->second; + int tick = m->tick(); + if (tick >= startPos) + { +// REMOVE Tim. global cut. Changed. +// if (tick + diff < startPos ) { // these ticks should be removed + if (tick < startPos + diff) { // these ticks should be removed + operations.push_back(UndoOp(UndoOp::ModifyMarker, 0, m)); + } else { + Marker *newMarker = new Marker(); + *newMarker = *m; +// REMOVE Tim. global cut. Changed. +// newMarker->setTick(tick + diff); + newMarker->setTick(tick - diff); + operations.push_back(UndoOp(UndoOp::ModifyMarker, newMarker, m)); + } + } + } + +} + +//--------------------------------------------------------- +// globalCut +// - remove area between left and right locator +// - cut master track +//--------------------------------------------------------- + +void globalCut(bool onlySelectedTracks) + { + int lpos = MusEGlobal::song->lpos(); + int rpos = MusEGlobal::song->rpos(); + if ((lpos - rpos) >= 0) + return; + + Undo operations; + TrackList* tracks = MusEGlobal::song->tracks(); + + for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { + Track* track = *it; + if (track == 0 || (onlySelectedTracks && !track->selected())) + continue; + PartList* pl = track->parts(); + for (iPart p = pl->begin(); p != pl->end(); ++p) { + Part* part = p->second; + int t = part->tick(); + int l = part->lenTick(); + if (t + l <= lpos) + continue; + if ((t >= lpos) && ((t+l) <= rpos)) { + operations.push_back(UndoOp(UndoOp::DeletePart,part)); + } + else if ((t < lpos) && ((t+l) > lpos) && ((t+l) <= rpos)) { + // remove part tail + int len = lpos - t; + + if (part->nextClone()==part) // no clones + { + // cut Events + const EventList& el = part->events(); + for (ciEvent ie = el.lower_bound(len); ie != el.end(); ++ie) + operations.push_back(UndoOp(UndoOp::DeleteEvent,ie->second, part, false, false)); + } + // TODO FIXME Suspect this section may need a wee bit more rework with the events above... + operations.push_back(UndoOp(UndoOp::ModifyPartLength, part, part->lenValue(), len, Pos::TICKS)); + } + else if ((t < lpos) && ((t+l) > lpos) && ((t+l) > rpos)) { + //---------------------- + // remove part middle + //---------------------- + Part* p1; + Part* p2; + Part* p3; + part->splitPart(lpos, p1, p2); + delete p2; + part->splitPart(rpos, p2, p3); + delete p2; + p3->setTick(lpos); + + MusEGlobal::song->informAboutNewParts(part,p1,p3); + operations.push_back(UndoOp(UndoOp::DeletePart,part)); + operations.push_back(UndoOp(UndoOp::AddPart,p1)); + operations.push_back(UndoOp(UndoOp::AddPart,p3)); + } + else if ((t >= lpos) && (t < rpos) && (t+l) > rpos) { + // remove part head + + Part* p1; + Part* p2; + part->splitPart(rpos, p1, p2); + delete p1; + p2->setTick(lpos); + + MusEGlobal::song->informAboutNewParts(part,p2); + operations.push_back(UndoOp(UndoOp::DeletePart,part)); + operations.push_back(UndoOp(UndoOp::AddPart,p2)); + } + else if (t >= rpos) { + // move part to the left + int nt = part->tick(); + operations.push_back(UndoOp(UndoOp::MovePart, part, part->posValue(), nt - (rpos -lpos), Pos::TICKS )); + } + } + } +// REMOVE Tim. global cut. Changed. +// int diff = lpos - rpos; +// adjustGlobalLists(operations, lpos, diff); + int diff = lpos > rpos ? lpos - rpos : rpos - lpos; + adjustGlobalLists(operations, lpos > rpos ? rpos : lpos, diff); + + MusEGlobal::song->applyOperationGroup(operations); + } + +//--------------------------------------------------------- +// globalInsert +// - insert empty space at left locator position upto +// right locator +// - insert in master track +//--------------------------------------------------------- + +void globalInsert(bool onlySelectedTracks) +{ + Undo operations=movePartsTotheRight(MusEGlobal::song->lpos(), MusEGlobal::song->rpos()-MusEGlobal::song->lpos(), onlySelectedTracks); + MusEGlobal::song->applyOperationGroup(operations); +} + +Undo movePartsTotheRight(unsigned int startTicks, int moveTicks, bool only_selected, set* tracklist) +{ + if (moveTicks<=0) + return Undo(); + + Undo operations; + TrackList* tracks = MusEGlobal::song->tracks(); + + for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { + Track* track = *it; + if ( (track == 0) || + (only_selected && !track->selected()) || + (tracklist && tracklist->find(track)==tracklist->end()) ) + continue; + PartList* pl = track->parts(); + for (riPart p = pl->rbegin(); p != pl->rend(); ++p) { + Part* part = p->second; + unsigned t = part->tick(); + int l = part->lenTick(); + if (t + l <= startTicks) + continue; + if (startTicks > t && startTicks < (t+l)) { + // split part to insert new space + Part* p1; + Part* p2; + part->splitPart(startTicks, p1, p2); + p2->setTick(startTicks+moveTicks); + + MusEGlobal::song->informAboutNewParts(part,p1,p2); + operations.push_back(UndoOp(UndoOp::DeletePart, part)); + operations.push_back(UndoOp(UndoOp::AddPart, p1)); + operations.push_back(UndoOp(UndoOp::AddPart, p2)); + } + else if (t >= startTicks) { + operations.push_back(UndoOp(UndoOp::MovePart, part, part->posValue(), t + moveTicks, Pos::TICKS)); + } + } + } + + adjustGlobalLists(operations, startTicks, moveTicks); + + return operations; + } + + +//--------------------------------------------------------- +// globalSplit +// - split all parts at the song position pointer +//--------------------------------------------------------- + +void globalSplit(bool onlySelectedTracks) +{ + Undo operations=partSplitter(MusEGlobal::song->cpos(), onlySelectedTracks); + MusEGlobal::song->applyOperationGroup(operations); +} + +Undo partSplitter(unsigned int pos, bool onlySelectedTracks) +{ + Undo operations; + TrackList* tracks = MusEGlobal::song->tracks(); + + for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { + Track* track = *it; + if (track == 0 || (onlySelectedTracks && !track->selected())) + continue; + + PartList* pl = track->parts(); + for (iPart p = pl->begin(); p != pl->end(); ++p) { + Part* part = p->second; + unsigned int p1 = part->tick(); + unsigned int l0 = part->lenTick(); + if (pos > p1 && pos < (p1+l0)) { + Part* p1; + Part* p2; + part->splitPart(pos, p1, p2); + + MusEGlobal::song->informAboutNewParts(part, p1); + MusEGlobal::song->informAboutNewParts(part, p2); + operations.push_back(UndoOp(UndoOp::DeletePart,part)); + operations.push_back(UndoOp(UndoOp::AddPart,p1)); + operations.push_back(UndoOp(UndoOp::AddPart,p2)); + break; + } + } + } + return operations; +} + + +} // namespace MusECore diff -Nru muse-2.1.2/muse/arranger/structure.h muse-3.0.2+ds1/muse/arranger/structure.h --- muse-2.1.2/muse/arranger/structure.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/structure.h 2018-01-14 18:26:04.000000000 +0000 @@ -0,0 +1,38 @@ +//========================================================= +// MusE +// Linux Music Editor +// structure.h +// (C) Copyright 2011 Florian Jung (flo93@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 __STRUCTURE_H__ +#define __STRUCTURE_H__ + +#include "undo.h" +#include + +namespace MusECore { +Undo movePartsTotheRight(unsigned int startTick, int moveTick, bool only_selected=false, std::set* tracklist=NULL); +Undo partSplitter(unsigned int tick, bool onlySelectedTracks=false); +void adjustGlobalLists(Undo& operations, int startPos, int diff); +void globalCut(bool onlySelectedTracks=false); +void globalInsert(bool onlySelectedTracks=false); +void globalSplit(bool onlySelectedTracks=false); +} + +#endif diff -Nru muse-2.1.2/muse/arranger/tlist.cpp muse-3.0.2+ds1/muse/arranger/tlist.cpp --- muse-2.1.2/muse/arranger/tlist.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/tlist.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: tlist.cpp,v 1.31.2.31 2009/12/15 03:39:58 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -70,15 +71,23 @@ #include "midi_audio_control.h" #include "ctrl.h" #include "plugin.h" +#include "operations.h" + #ifdef DSSI_SUPPORT #include "dssihost.h" #endif +#ifdef LV2_SUPPORT +#include "lv2host.h" +#endif + +using MusECore::UndoOp; + namespace MusEGui { static const int MIN_TRACKHEIGHT = 20; -static const int WHEEL_DELTA = 120; +//static const int WHEEL_DELTA = 120; QColor collist[] = { Qt::red, Qt::yellow, Qt::blue , Qt::black, Qt::white, Qt::green }; QString colnames[] = { "Red", "Yellow", "Blue", "Black", "White", "Green"}; //--------------------------------------------------------- @@ -94,7 +103,7 @@ // This is absolutely required for speed! Otherwise painfully slow because we get // full rect paint events even on small scrolls! See help on QPainter::scroll(). setAttribute(Qt::WA_OpaquePaintEvent); - + setObjectName(name); ypos = 0; editMode = false; @@ -125,11 +134,13 @@ void TList::songChanged(MusECore::SongChangedFlags_t flags) { - if (flags & (SC_MUTE | SC_SOLO | SC_RECFLAG | SC_TRACK_INSERTED - | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_ROUTE | SC_CHANNELS | SC_MIDI_TRACK_PROP + if (flags & (SC_MUTE | SC_SOLO | SC_RECFLAG | SC_TRACK_REC_MONITOR + | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED + | SC_TRACK_MOVED + | SC_TRACK_SELECTION | SC_ROUTE | SC_CHANNELS | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED )) - redraw(); + update(); if (flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED)) adjustScrollbar(); } @@ -241,6 +252,29 @@ mask.setColorAt(0.85, mask_edge); mask.setColorAt(1, mask_edge); + // Find up to two tracks that are soloed. + MusECore::Track* solo_t_1 = 0; + MusECore::Track* solo_t_2 = 0; + { + MusECore::TrackList* tl = MusEGlobal::song->tracks(); + for(MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) + { + MusECore::Track* t = *it; + if(t->internalSolo() || t->solo()) + { + if(!solo_t_1) + solo_t_1 = t; + else if(!solo_t_2) + solo_t_2 = t; + } + // Did we find at least two tracks? Done. + if(solo_t_1 && solo_t_2) + break; + } + } + + const int header_fh = header->fontMetrics().lineSpacing(); + const int svg_sz = qMin(header_fh + 2, qMax(MIN_TRACKHEIGHT - 5, 6)); MusECore::TrackList* l = MusEGlobal::song->tracks(); int idx = 0; @@ -308,199 +342,184 @@ for (int index = 0; index < header->count(); ++index) { int section = header->logicalIndex(index); int w = header->sectionSize(section); - QRect r = p.combinedTransform().mapRect(QRect(x+2, yy, w-4, trackHeight)); + QRect r = p.transform().mapRect(QRect(x+2, yy, w-4, trackHeight)); + QRect svg_r = p.transform().mapRect( + QRect(x + w / 2 - svg_sz / 2, + yy + trackHeight / 2 - svg_sz / 2, + svg_sz, + svg_sz)); + //QRect(x+2, yy, qMin(header_fh, MIN_TRACKHEIGHT)-4, trackHeight)); - switch (section) { - case COL_RECORD: - if (track->canRecord() && !header->isSectionHidden(COL_RECORD)) { - //bool aa = p.testRenderHint(QPainter::SmoothPixmapTransform); // Antialiasing); // The rec icon currently looks very jagged. AA should help. - //p.setRenderHint(QPainter::SmoothPixmapTransform); //Antialiasing); - drawCenteredPixmap(p, - track->recordFlag() ? record_on_Icon : record_off_Icon, r); - //p.setRenderHint(QPainter::SmoothPixmapTransform, aa); //Antialiasing, aa); - } - break; - case COL_CLASS: - { - if (header->isSectionHidden(COL_CLASS)) + if(!header->isSectionHidden(section)) + { + switch (section) { + case COL_INPUT_MONITOR: + if (track->canRecordMonitor()) { + (track->recMonitor() ? monitorOnSVGIcon : monitorOffSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); + } break; - const QPixmap* pm = 0; - switch(type) { - case MusECore::Track::MIDI: - pm = addtrack_addmiditrackIcon; - break; - case MusECore::Track::NEW_DRUM: - pm = addtrack_newDrumtrackIcon; - break; - case MusECore::Track::DRUM: - pm = addtrack_drumtrackIcon; - break; - case MusECore::Track::WAVE: - pm = addtrack_wavetrackIcon; - break; - case MusECore::Track::AUDIO_OUTPUT: - pm = addtrack_audiooutputIcon; - break; - case MusECore::Track::AUDIO_INPUT: - pm = addtrack_audioinputIcon; - break; - case MusECore::Track::AUDIO_GROUP: - pm = addtrack_audiogroupIcon; - break; - case MusECore::Track::AUDIO_AUX: - pm = addtrack_auxsendIcon; - break; - case MusECore::Track::AUDIO_SOFTSYNTH: - pm = synthIcon; - break; - } - drawCenteredPixmap(p, pm, r); - } - break; - case COL_MUTE: - if (track->off()) - drawCenteredPixmap(p, offIcon, r); - else if (track->mute()) - drawCenteredPixmap(p, editmuteSIcon, r); - break; - case COL_SOLO: - if(track->solo() && track->internalSolo()) - drawCenteredPixmap(p, blacksqcheckIcon, r); - else - if(track->internalSolo()) - drawCenteredPixmap(p, blacksquareIcon, r); - else - if (track->solo()) - drawCenteredPixmap(p, checkSquareIcon, r); - break; - case COL_TIMELOCK: - if (track->isMidiTrack() - && track->locked()) { - drawCenteredPixmap(p, lockIcon, r); - } - break; - case COL_NAME: - if (track->type() == MusECore::Track::AUDIO_AUX) { - p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, ((MusECore::AudioAux *)track)->auxName()); - } else { - p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, track->name()); - } - break; - case COL_OCHANNEL: - { - QString s; - int n; - // Default to track port if -1 and track channel if -1. - if (track->isMidiTrack()) { - n = ((MusECore::MidiTrack*)track)->outChannel() + 1; - } - else { - // show number of ports - n = ((MusECore::WaveTrack*)track)->channels(); - } - s.setNum(n); - p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, s); - } - break; - case COL_OPORT: - { - QString s; - if (track->isMidiTrack()) { - int outport = ((MusECore::MidiTrack*)track)->outPort(); - s.sprintf("%d:%s", outport+1, MusEGlobal::midiPorts[outport].portname().toLatin1().constData()); - } - else - if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH) - { - MusECore::MidiDevice* md = dynamic_cast(track); - if(md) + case COL_RECORD: + if (track->canRecord()) { + (track->recordFlag() ? recArmOnSVGIcon : recArmOffSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); + } + break; + case COL_CLASS: { - int outport = md->midiPort(); - if((outport >= 0) && (outport < MIDI_PORTS)) - s.sprintf("%d:%s", outport+1, MusEGlobal::midiPorts[outport].portname().toLatin1().constData()); - else - s = tr(""); - } - } - - p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); - } - break; - case COL_AUTOMATION: - { - QString s="-"; + if(const QPixmap* pm = MusECore::Track::trackTypeIcon(type)) + drawCenteredPixmap(p, pm, r); + } + break; + case COL_MUTE: + if (track->off()) + trackOffSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); + else + { + if(!track->internalSolo() && !track->solo() && + ((solo_t_1 && solo_t_1 != track) || (solo_t_2 && solo_t_2 != track))) + (track->mute() ? muteAndProxyOnSVGIcon : muteProxyOnSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); + else if(track->mute()) + muteOnSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); + } + break; + case COL_SOLO: + if(track->solo() && track->internalSolo()) + soloAndProxyOnSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); + else + if(track->internalSolo()) + soloProxyOnAloneSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); + else + if (track->solo()) + soloOnAloneSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); + break; + case COL_TIMELOCK: + if (track->isMidiTrack() + && track->locked()) { + drawCenteredPixmap(p, lockIcon, r); + } + break; + case COL_NAME: + if (track->type() == MusECore::Track::AUDIO_AUX) { + p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, ((MusECore::AudioAux *)track)->auxName()); + } else { + p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, track->name()); + } + break; + case COL_OCHANNEL: + { + QString s; + int n; + // Default to track port if -1 and track channel if -1. + if (track->isMidiTrack()) { + n = ((MusECore::MidiTrack*)track)->outChannel() + 1; + } + else { + // show number of ports + n = ((MusECore::WaveTrack*)track)->channels(); + } + s.setNum(n); + p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, s); + } + break; + case COL_OPORT: + { + QString s; + if (track->isMidiTrack()) { + int outport = ((MusECore::MidiTrack*)track)->outPort(); + s = QString("%1:%2").arg(outport+1).arg(MusEGlobal::midiPorts[outport].portname()); + } + else + if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH) + { + MusECore::MidiDevice* md = dynamic_cast(track); + if(md) + { + int outport = md->midiPort(); + if((outport >= 0) && (outport < MIDI_PORTS)) + s = QString("%1:%2").arg(outport+1).arg(MusEGlobal::midiPorts[outport].portname()); + else + s = tr(""); + } + } - if (!track->isMidiTrack()) { - MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)track)->controller(); - int countAll=0, countVisible=0; - for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { - MusECore::CtrlList *cl = icll->second; - if (!cl->dontShow()) - countAll++; - if (cl->isVisible()) - countVisible++; - } - s.sprintf(" %d(%d) %s",countVisible, countAll, tr("visible").toAscii().data()); - } + p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); + } + break; + case COL_AUTOMATION: + { + QString s="-"; + + if (!track->isMidiTrack()) { + MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)track)->controller(); + int countAll=0, countVisible=0; + for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { + MusECore::CtrlList *cl = icll->second; + if (!cl->dontShow()) + countAll++; + if (cl->isVisible()) + countVisible++; + } + s = QString(" %1(%2) %3").arg(countVisible).arg(countAll).arg(tr("visible")); + } - p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); - } - break; - case COL_CLEF: - if (track->isMidiTrack() && track->type() == MusECore::Track::MIDI) { // no drum tracks! - QString s = tr("no clef"); - if (((MusECore::MidiTrack*)track)->getClef() == trebleClef) - s=tr("Treble"); - else if (((MusECore::MidiTrack*)track)->getClef() == bassClef) - s=tr("Bass"); - else if (((MusECore::MidiTrack*)track)->getClef() == grandStaff) - s=tr("Grand"); p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); - } - break; - default: - if (section>=COL_CUSTOM_MIDICTRL_OFFSET) - { - if (track->isMidiTrack()) + } + break; + case COL_CLEF: + if (track->isMidiTrack() && track->type() == MusECore::Track::MIDI) { // no drum tracks! + QString s = tr("no clef"); + if (((MusECore::MidiTrack*)track)->getClef() == trebleClef) + s=tr("Treble"); + else if (((MusECore::MidiTrack*)track)->getClef() == bassClef) + s=tr("Bass"); + else if (((MusECore::MidiTrack*)track)->getClef() == grandStaff) + s=tr("Grand"); + p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); + } + break; + default: + if (section>=COL_CUSTOM_MIDICTRL_OFFSET) { - int col_ctrl_no=Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].ctrl; - MusECore::MidiTrack* mt=dynamic_cast(track); - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; - MusECore::MidiController* mctl = mp->midiController(col_ctrl_no); - int val; - if (Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].affected_pos == - Arranger::custom_col_t::AFFECT_BEGIN) - val=mt->getControllerChangeAtTick(0,col_ctrl_no,MusECore::CTRL_VAL_UNKNOWN); - else - { - val=mp->hwCtrlState(mt->outChannel(), col_ctrl_no); - old_ctrl_hw_states[mt][section]=val; - } - - if (val!=MusECore::CTRL_VAL_UNKNOWN) - val-=mctl->bias(); - - if (col_ctrl_no!=MusECore::CTRL_PROGRAM) - { - p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, - (val!=MusECore::CTRL_VAL_UNKNOWN)?QString::number(val):tr("off")); - } - else + if (track->isMidiTrack()) { - MusECore::MidiInstrument* instr = mp->instrument(); - QString name; + int col_ctrl_no=Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].ctrl; + MusECore::MidiTrack* mt=dynamic_cast(track); + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; + MusECore::MidiController* mctl = mp->midiController(col_ctrl_no); + int val; + if (Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].affected_pos == + Arranger::custom_col_t::AFFECT_BEGIN) + val=mt->getControllerChangeAtTick(0,col_ctrl_no,MusECore::CTRL_VAL_UNKNOWN); + else + { + val=mp->hwCtrlState(mt->outChannel(), col_ctrl_no); + old_ctrl_hw_states[mt][section]=val; + } + if (val!=MusECore::CTRL_VAL_UNKNOWN) - name = instr->getPatchName(mt->outChannel(), val, mt->isDrumTrack()); + val-=mctl->bias(); + + if (col_ctrl_no!=MusECore::CTRL_PROGRAM) + { + p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, + (val!=MusECore::CTRL_VAL_UNKNOWN)?QString::number(val):tr("off")); + } else - name = tr(""); - - p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, name); + { + MusECore::MidiInstrument* instr = mp->instrument(); + QString name; + if (val!=MusECore::CTRL_VAL_UNKNOWN) + name = instr->getPatchName(mt->outChannel(), val, mt->isDrumTrack(), true); // Include default. + else + name = tr(""); + + p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, name); + } } } - } - break; - } + break; + } + } x += header->sectionSize(section); } p.setPen(Qt::gray); @@ -521,7 +540,7 @@ int n = header->count(); int xpos = 0; - p.setPen(Qt::gray); + p.setPen(MusEGlobal::config.trackSectionDividerColor); for (int index = 0; index < n; index++) { int section = header->logicalIndex(index); xpos += header->sectionSize(section); @@ -584,28 +603,25 @@ MusECore::TrackList* tl = MusEGlobal::song->tracks(); for (MusECore::iTrack i = tl->begin(); i != tl->end(); ++i) { if ((*i)->name() == editor->text()) { + editTrack = 0; + editor->blockSignals(true); + editor->hide(); + editor->blockSignals(false); QMessageBox::critical(this, tr("MusE: bad trackname"), tr("please choose a unique track name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); - editTrack = 0; - editor->blockSignals(true); - editor->hide(); - editor->blockSignals(false); setFocus(); return; } } - MusEGlobal::song->startUndo(); - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackName, + MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackName, editTrack, - editTrack->name().toLatin1().constData(), - editor->text().toLatin1().constData())); - editTrack->setName(editor->text()); - MusEGlobal::song->endUndo(-1); //uagh, why "-1", why no proper flags? + editTrack->name(), + editor->text())); } } @@ -627,58 +643,9 @@ { if(editTrack) { - if(editTrack->isMidiTrack()) - { - MusECore::MidiTrack* mt = dynamic_cast(editTrack); - // Default to track port if -1 and track channel if -1. - if (mt) - { - int channel = chan_edit->value() - 1; - if(channel >= MIDI_CHANNELS) - channel = MIDI_CHANNELS - 1; - if(channel < 0) - channel = 0; - if(channel != mt->outChannel()) - { - MusEGlobal::song->startUndo(); - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, - editTrack, - mt->outChannel(), - channel)); - MusEGlobal::audio->msgIdle(true); - mt->setOutChanAndUpdate(channel); - MusEGlobal::audio->msgIdle(false); - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->endUndo(SC_MIDI_TRACK_PROP); - } - } - } - else - { - if(editTrack->type() != MusECore::Track::AUDIO_SOFTSYNTH) - { - MusECore::AudioTrack* at = dynamic_cast(editTrack); - if(at) - { - int n = chan_edit->value(); - if(n > MAX_CHANNELS) - n = MAX_CHANNELS; - else if (n < 1) - n = 1; - if(n != at->channels()) - { - MusEGlobal::song->startUndo(); - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, - editTrack, - at->channels(), - n)); - MusEGlobal::audio->msgSetChannels(at, n); - MusEGlobal::song->endUndo(SC_CHANNELS); - } - } - } - } - + // Default to track port if -1 and track channel if -1. + const int channel = chan_edit->value() - (editTrack->isMidiTrack() ? 1 : 0); // Subtract 1 from midi channel display. + setTrackChannel(editTrack, false, channel, 0); editTrack = 0; } @@ -723,7 +690,7 @@ { if (p->second->tick()==0) { - for (MusECore::iEvent ev=p->second->events()->begin(); ev!=p->second->events()->end(); ev++) + for (MusECore::ciEvent ev=p->second->events().begin(); ev!=p->second->events().end(); ev++) { if (ev->second.tick()!=0) break; else if (ev->second.type()==MusECore::Controller && ev->second.dataA()==ctrl_num) @@ -784,16 +751,172 @@ return 0; } +void TList::muteSelectedTracksSlot() +{ + bool stateDefined=false; + bool setTo; + MusECore::PendingOperationList operations; + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) + { + if ((*t)->selected()){ + if (!stateDefined) + { + setTo = !(*t)->isMute(); + stateDefined = true; + } + operations.add(MusECore::PendingOperationItem((*t), setTo, MusECore::PendingOperationItem::SetTrackMute)); + } + } + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + update(); +} + +void TList::soloSelectedTracksSlot() +{ + bool stateDefined=false; + bool setTo; + MusECore::PendingOperationList operations; + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) + { + if ((*t)->selected()){ + if (!stateDefined) + { + setTo = !(*t)->soloMode(); + stateDefined = true; + } + operations.add(MusECore::PendingOperationItem((*t), setTo, MusECore::PendingOperationItem::SetTrackSolo)); + } + } + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + update(); +} + +void TList::editTrackNameSlot() +{ + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + if (tracks->countSelected() == 1) { + for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) + if ((*t)->selected()){ + editTrackName(*t); + break; + } + } +} + +void TList::editTrackName(MusECore::Track *t) +{ + int colx = header->sectionPosition(COL_NAME); + int colw = header->sectionSize(COL_NAME); + int coly = t->y() - ypos; + int colh = t->height(); + editTrack = t; + if (editor == 0) { + editor = new QLineEdit(this); + editor->setFrame(false); + connect(editor, SIGNAL(editingFinished()), SLOT(returnPressed())); + } + editor->setText(editTrack->name()); + editor->selectAll(); + editor->setGeometry(colx, coly, colw, colh); + editMode = true; + editor->show(); + editor->setFocus(); +} + +void TList::setTrackChannel(MusECore::Track *track, bool isDelta, int channel, int delta, bool doAllTracks) +{ + MusECore::Undo operations; + if(track->isMidiTrack()) + { + MusECore::MidiTrack* mt = static_cast(track); + + if(!doAllTracks && !track->selected()) + { + if(isDelta) + channel = mt->outChannel() + delta; + if(channel >= MIDI_CHANNELS) channel = MIDI_CHANNELS - 1; + if(channel < 0) channel = 0; + if(channel != mt->outChannel()) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, mt, mt->outChannel(), channel)); + } + else + { + MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); + for(MusECore::iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::MidiTrack* t = *it; + if(isDelta) + channel = t->outChannel() + delta; + if(channel >= MIDI_CHANNELS) channel = MIDI_CHANNELS - 1; + else if(channel < 0) channel = 0; + if(channel != t->outChannel() && (doAllTracks || t->selected())) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, t, t->outChannel(), channel)); + } + } + + if(!operations.empty()) + MusEGlobal::song->applyOperationGroup(operations); + } + else + { + if(track->type() != MusECore::Track::AUDIO_SOFTSYNTH) + { + if(channel > MAX_CHANNELS) + channel = MAX_CHANNELS; + else if(channel < 1) + channel = 1; + + if(!doAllTracks && !track->selected()) + { + if(isDelta) + channel = track->channels() + delta; + if(channel > MAX_CHANNELS) channel = MAX_CHANNELS; + else if(channel < 1) channel = 1; + if(channel != track->channels()) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, track, track->channels(), channel)); + } + else + { + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + if((*it)->isMidiTrack()) + continue; + MusECore::Track* t = *it; + if(isDelta) + channel = t->channels() + delta; + if(channel > MAX_CHANNELS) channel = MAX_CHANNELS; + else if(channel < 1) channel = 1; + if(channel != t->channels() && (doAllTracks || t->selected())) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, t, t->channels(), channel)); + } + } + + if(!operations.empty()) + MusEGlobal::song->applyOperationGroup(operations); + } + } +} + //--------------------------------------------------------- // viewMouseDoubleClickEvent //--------------------------------------------------------- void TList::mouseDoubleClickEvent(QMouseEvent* ev) { + if((editor && (editor->isVisible() || editor->hasFocus())) || + (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) || + (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus()))) + { + ev->accept(); + return; + } + int button = ev->button(); - //bool ctrl = ((QInputEvent*)ev)->modifiers() & Qt::ControlModifier; if(button != Qt::LeftButton) { - mousePressEvent(ev); + ev->accept(); return; } @@ -801,55 +924,55 @@ int section = header->logicalIndexAt(x); if (section == -1) { - mousePressEvent(ev); + ev->accept(); return; } MusECore::Track* t = y2Track(ev->y() + ypos); + if(t == NULL) + { + ev->accept(); + return; + } - if (t) { - int colx = header->sectionPosition(section); - int colw = header->sectionSize(section); - int coly = t->y() - ypos; - int colh = t->height(); + int colx = header->sectionPosition(section); + int colw = header->sectionSize(section); + int coly = t->y() - ypos; + int colh = t->height(); + if (t) { if (section == COL_NAME) { - editTrack = t; - if (editor == 0) { - editor = new QLineEdit(this); - editor->setFrame(false); - connect(editor, SIGNAL(editingFinished()), SLOT(returnPressed())); - } - editor->setText(editTrack->name()); - editor->selectAll(); - editor->setGeometry(colx, coly, colw, colh); - editMode = true; - editor->show(); - } + editTrackName(t); + } + else if (section == COL_OPORT) { + if (t->type() == MusECore::Track::AUDIO_SOFTSYNTH) { + MusECore::SynthI* synth = static_cast(t); + if (synth->hasNativeGui()) { + synth->showNativeGui(!synth->nativeGuiVisible()); + } + else if (synth->hasGui()) { + synth->showGui(!synth->guiVisible()); + } + } + } else if (section == COL_OCHANNEL) { - // Enabled for audio tracks. And synth channels cannot be changed ATM. + // Enabled for audio tracks. And synth channels cannot be changed ATM. // Default to track port if -1 and track channel if -1. if(t->type() == MusECore::Track::AUDIO_SOFTSYNTH) { - mousePressEvent(ev); + ev->accept(); return; } // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. - if(chan_edit && chan_edit->hasFocus()) - { - ev->accept(); - return; - } - else - { editTrack=t; - if (chan_edit==0) { + if (!chan_edit) + { chan_edit=new QSpinBox(this); chan_edit->setFrame(false); chan_edit->setMinimum(1); connect(chan_edit, SIGNAL(editingFinished()), SLOT(chanValueFinished())); - } + } if (t->isMidiTrack()) { chan_edit->setMaximum(MIDI_CHANNELS); @@ -867,8 +990,6 @@ editMode = true; chan_edit->show(); chan_edit->setFocus(); - ev->accept(); - } } else if (section >= COL_CUSTOM_MIDICTRL_OFFSET) { @@ -907,232 +1028,10 @@ ctrl_edit->show(); ctrl_edit->setFocus(); } - // else: the CTRL_PROGRAM popup is displayed with a single click - ev->accept(); } } - else - mousePressEvent(ev); - } - } - -//--------------------------------------------------------- -// portsPopupMenu -//--------------------------------------------------------- -void TList::portsPopupMenu(MusECore::Track* t, int x, int y) - { - switch(t->type()) { - case MusECore::Track::MIDI: - case MusECore::Track::DRUM: - case MusECore::Track::NEW_DRUM: - case MusECore::Track::AUDIO_SOFTSYNTH: - { - MusECore::MidiTrack* track = (MusECore::MidiTrack*)t; - - MusECore::MidiDevice* md = 0; - int potential_new_port_no=-1; - int port = -1; - if(t->type() == MusECore::Track::AUDIO_SOFTSYNTH) - { - md = dynamic_cast(t); - if(md) - port = md->midiPort(); - } - else - port = track->outPort(); - - QMenu* p = MusECore::midiPortsPopup(this, port); - - if (t->isMidiTrack()) - { - // extend that menu a bit - - - // find first free port number - // do not permit numbers already used in other tracks! - // except if it's only used in this track. - int no; - for (no=0;notracks()->begin(); it!=MusEGlobal::song->tracks()->end(); it++) - { - MusECore::MidiTrack* mt=dynamic_cast(*it); - if (mt && mt!=t && mt->outPort()==no) - break; - } - if (it == MusEGlobal::song->tracks()->end()) - break; - } - - if (no==MIDI_PORTS) - { - delete p; - printf("THIS IS VERY UNLIKELY TO HAPPEN: no free midi ports! you have used all %i!\n",MIDI_PORTS); - break; - } - - - potential_new_port_no=no; - typedef std::map asmap; - typedef std::map::iterator imap; - - asmap mapALSA; - asmap mapJACK; - - int aix = 0x10000000; - int jix = 0x20000000; - for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) - { - if((*i)->deviceType() == MusECore::MidiDevice::ALSA_MIDI) - { - // don't add devices which are used somewhere - int j; - for (j=0;j (std::string((*i)->name().toLatin1().constData()), aix) ); - - ++aix; - } - else if((*i)->deviceType() == MusECore::MidiDevice::JACK_MIDI) - { - // don't add devices which are used somewhere - int j; - for (j=0;j (std::string((*i)->name().toLatin1().constData()), jix) ); - ++jix; - } - } - - if (!mapALSA.empty() || !mapJACK.empty()) - { - QMenu* pup = p->addMenu(tr("Unused Devices")); - QAction* act; - - - if (!mapALSA.empty()) - { - pup->addAction(new MusEGui::MenuTitleItem("ALSA:", pup)); - - for(imap i = mapALSA.begin(); i != mapALSA.end(); ++i) - { - int idx = i->second; - QString s(i->first.c_str()); - MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::ALSA_MIDI); - if(md) - { - if(md->deviceType() != MusECore::MidiDevice::ALSA_MIDI) - continue; - - act = pup->addAction(md->name()); - act->setData(idx); - } - } - } - - if (!mapALSA.empty() && !mapJACK.empty()) - pup->addSeparator(); - - if (!mapJACK.empty()) - { - pup->addAction(new MusEGui::MenuTitleItem("JACK:", pup)); - - for(imap i = mapJACK.begin(); i != mapJACK.end(); ++i) - { - int idx = i->second; - QString s(i->first.c_str()); - MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::JACK_MIDI); - if(md) - { - if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) - continue; - - act = pup->addAction(md->name()); - act->setData(idx); - } - } - } - } - } - - - QAction* act = p->exec(mapToGlobal(QPoint(x, y)), 0); - if(!act) - { - delete p; - break; - } - - QString acttext=act->text(); - int n = act->data().toInt(); - delete p; - - if(n < 0) // Invalid item. - break; - - if(n == MIDI_PORTS) // Show port config dialog. - { - MusEGlobal::muse->configMidiPorts(); - break; - } - else if (n & 0x30000000) - { - int typ; - if (n & 0x10000000) - typ = MusECore::MidiDevice::ALSA_MIDI; - else - typ = MusECore::MidiDevice::JACK_MIDI; - - MusECore::MidiDevice* sdev = MusEGlobal::midiDevices.find(acttext, typ); - - MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[potential_new_port_no], sdev); - n=potential_new_port_no; - - MusEGlobal::song->update(); - } - - // Default to track port if -1 and track channel if -1. No need anymore to ask to change all items - if (t->type() == MusECore::Track::AUDIO_SOFTSYNTH) - { - if(md != 0) - { - // Idling is already handled in msgSetMidiDevice. - //MusEGlobal::audio->msgIdle(true); - - // Compiler complains if simple cast from Track to MusECore::SynthI... - MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[n], (MusEGlobal::midiPorts[n].device() == md) ? 0 : md); - MusEGlobal::muse->changeConfig(true); // save configuration file - - MusEGlobal::song->update(); - } - } - else - { - MusEGlobal::audio->msgIdle(true); - track->setOutPortAndUpdate(n); - MusEGlobal::audio->msgIdle(false); - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); - } - - // Prompt and send init sequences. - MusEGlobal::audio->msgInitMidiDevices(false); - } - break; - - case MusECore::Track::WAVE: - case MusECore::Track::AUDIO_OUTPUT: - case MusECore::Track::AUDIO_INPUT: - case MusECore::Track::AUDIO_GROUP: - case MusECore::Track::AUDIO_AUX: //TODO - break; } + ev->accept(); } //--------------------------------------------------------- @@ -1141,11 +1040,12 @@ void TList::oportPropertyPopupMenu(MusECore::Track* t, int x, int y) { + if(t->type() == MusECore::Track::AUDIO_SOFTSYNTH) { MusECore::SynthI* synth = static_cast(t); - - QMenu* p = new QMenu; + PopupMenu *mSubPresets = new PopupMenu(tr("Presets")); + PopupMenu* p = new PopupMenu; QAction* gact = p->addAction(tr("show gui")); gact->setCheckable(true); gact->setEnabled(synth->hasGui()); @@ -1155,6 +1055,20 @@ nact->setCheckable(true); nact->setEnabled(synth->hasNativeGui()); nact->setChecked(synth->nativeGuiVisible()); + +#ifdef LV2_SUPPORT + //show presets submenu for lv2 synths + if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::LV2_SYNTH) + { + p->addMenu(mSubPresets); + static_cast(synth->sif())->populatePresetsMenu(mSubPresets); + } + else + { + delete mSubPresets; + mSubPresets = NULL; + } +#endif // If it has a gui but we don't have OSC, disable the action. #ifndef OSC_SUPPORT @@ -1176,6 +1090,17 @@ bool show = !synth->nativeGuiVisible(); synth->showNativeGui(show); } +#ifdef LV2_SUPPORT + else if (mSubPresets != NULL && ract != NULL) { + QWidget *mwidget = ract->parentWidget(); + if (mwidget != NULL) { + if(mSubPresets == dynamic_cast(mwidget)) { + static_cast(synth->sif())->applyPreset(ract->data().value()); + } + } + + } +#endif delete p; return; } @@ -1186,7 +1111,8 @@ int oPort = ((MusECore::MidiTrack*)t)->outPort(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[oPort]; - QMenu* p = new QMenu; + PopupMenu* p = new PopupMenu; + PopupMenu *mSubPresets = new PopupMenu(tr("Presets")); QAction* gact = p->addAction(tr("show gui")); gact->setCheckable(true); gact->setEnabled(port->hasGui()); @@ -1198,9 +1124,9 @@ nact->setChecked(port->nativeGuiVisible()); // If it has a gui but we don't have OSC, disable the action. - #ifndef OSC_SUPPORT - #ifdef DSSI_SUPPORT MusECore::MidiDevice* dev = port->device(); + #ifndef OSC_SUPPORT + #ifdef DSSI_SUPPORT if(dev && dev->isSynti()) { MusECore::SynthI* synth = static_cast(dev); @@ -1213,15 +1139,42 @@ #endif #endif +#ifdef LV2_SUPPORT + if(dev && dev->isSynti()) + { + MusECore::SynthI* synth = static_cast(dev); + //show presets submenu for lv2 synths + if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::LV2_SYNTH) + { + p->addMenu(mSubPresets); + static_cast(synth->sif())->populatePresetsMenu(mSubPresets); + } + else + { + delete mSubPresets; + mSubPresets = NULL; + } + } +#endif QAction* ract = p->exec(mapToGlobal(QPoint(x, y)), 0); if (ract == gact) { - bool show = !port->guiVisible(); - port->instrument()->showGui(show); + port->showGui(!port->guiVisible()); } else if (ract == nact) { - bool show = !port->nativeGuiVisible(); - port->instrument()->showNativeGui(show); + port->showNativeGui(!port->nativeGuiVisible()); } +#ifdef LV2_SUPPORT + else if (mSubPresets != NULL && ract != NULL) { + QWidget *mwidget = ract->parentWidget(); + if (mwidget != NULL && dev && dev->isSynti()) { + MusECore::SynthI* synth = static_cast(dev); + if(mSubPresets == dynamic_cast(mwidget)) { + static_cast(synth->sif())->applyPreset(ract->data().value()); + } + } + + } +#endif delete p; } @@ -1240,69 +1193,51 @@ //--------------------------------------------------------- void TList::keyPressEvent(QKeyEvent* e) +{ + if ( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) + { + e->accept(); + return; + } + + if (editMode) + { + if ( e->key() == Qt::Key_Escape ) + { + if(editor && editor->isVisible()) { - if ( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) - { - e->accept(); - return; - } - - if (editMode) - { - if ( e->key() == Qt::Key_Escape ) - { - if(editor && editor->isVisible()) - { - editor->blockSignals(true); - editor->hide(); - editor->blockSignals(false); - } - if(chan_edit && chan_edit->isVisible()) - { - chan_edit->blockSignals(true); - chan_edit->hide(); - chan_edit->blockSignals(false); - } - if(ctrl_edit && ctrl_edit->isVisible()) - { - ctrl_edit->blockSignals(true); - ctrl_edit->hide(); - ctrl_edit->blockSignals(false); - } - editTrack = 0; - editMode = false; - setFocus(); - return; - } - return; - } - else if (!editJustFinished) + editor->blockSignals(true); + editor->hide(); + editor->blockSignals(false); + } + if(chan_edit && chan_edit->isVisible()) { - emit keyPressExt(e); //redirect keypress events to main app. don't call this when confirming an editor + chan_edit->blockSignals(true); + chan_edit->hide(); + chan_edit->blockSignals(false); } - else - editJustFinished=false; - - - // Works OK (if focusing allowed). But instead we won't allow focus. Part canvas has Ctrl+up/down which moves selected track only. - /* - int key = e->key(); - switch (key) { - case Qt::Key_Up: - moveSelection(-1); - return; - case Qt::Key_Down: - moveSelection(1); - return; - default: - break; - } */ - - // keyPressExt are sent to part canvas, where they are ignored *only* if necessary. - //e->ignore(); - - emit keyPressExt(e); //redirect keypress events to main app + if(ctrl_edit && ctrl_edit->isVisible()) + { + ctrl_edit->blockSignals(true); + ctrl_edit->hide(); + ctrl_edit->blockSignals(false); } + editTrack = 0; + editMode = false; + setFocus(); + return; + } + return; + } + else if (!editJustFinished) + { + emit keyPressExt(e); //redirect keypress events to main app. don't call this when confirming an editor + } + else + editJustFinished=false; + + emit keyPressExt(e); //redirect keypress events to main app +} //--------------------------------------------------------- // moveSelection @@ -1312,11 +1247,7 @@ { MusECore::TrackList* tracks = MusEGlobal::song->tracks(); - // check for single selection - int nselect = 0; - for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) - if ((*t)->selected()) - ++nselect; + int nselect = tracks->countSelected(); if (nselect != 1) return; MusECore::Track* selTrack = 0; @@ -1358,6 +1289,16 @@ (*s)->setSelected(false); selTrack->setSelected(true); + // if selected track is outside of view, enforce scrolling + if (selTrack->y() > this->height()+ypos-20) + { + emit verticalScrollSetYpos(ypos+selTrack->height()); + } + else if (selTrack->y() < ypos) + { + emit verticalScrollSetYpos(selTrack->y()); + } + // rec enable track if expected MusECore::TrackList recd = getRecEnabledTracks(); if (recd.size() == 1 && MusEGlobal::config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection @@ -1373,7 +1314,7 @@ } } if(selTrack) - emit selectionChanged(selTrack); + MusEGlobal::song->update(SC_TRACK_SELECTION); } MusECore::TrackList TList::getRecEnabledTracks() @@ -1616,51 +1557,43 @@ //--------------------------------------------------------- void TList::mousePressEvent(QMouseEvent* ev) { - int x = ev->x(); - int y = ev->y(); - int button = ev->button(); - bool ctrl = ((QInputEvent*)ev)->modifiers() & Qt::ControlModifier; + if((editor && (editor->isVisible() || editor->hasFocus())) || + (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) || + (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus()))) + { + ev->accept(); + return; + } + + const int x = ev->x(); + const int y = ev->y(); + const int button = ev->button(); + const bool ctrl = ((QInputEvent*)ev)->modifiers() & Qt::ControlModifier; + const bool shift = ((QInputEvent*)ev)->modifiers() & Qt::ShiftModifier; MusECore::Track* t = y2Track(y + ypos); - // FIXME Observed: Ancient bug: Track Info doesn't change if selecting multiple tracks in reverse order. - // Will need to be fixed if/when adding 'multiple track global editing'. - TrackColumn col = TrackColumn(header->logicalIndexAt(x)); if (t == 0) { if (button == Qt::RightButton) { - QMenu* p = new QMenu; - MusEGui::populateAddTrack(p); - - // Show the menu - QAction* act = p->exec(ev->globalPos(), 0); - // Valid click? - if(act) - { - t = MusEGlobal::song->addNewTrack(act); // Add at end of list. - if(t && t->isVisible()) - { - MusEGlobal::song->deselectTracks(); - t->setSelected(true); + // Show the menu + QAction* act = addTrackMenu->exec(ev->globalPos(), 0); - ///emit selectionChanged(); - emit selectionChanged(t); - adjustScrollbar(); - } + // Valid click? + if(act) + { + t = MusEGlobal::song->addNewTrack(act); // Add at end of list. + if(t && t->isVisible()) + { + MusEGlobal::song->selectAllTracks(false); + t->setSelected(true); + MusEGlobal::song->update(SC_TRACK_SELECTION); + adjustScrollbar(); } + } - // Just delete p, and all its children will go too, right? - //delete synp; - delete p; } - /*else if (button == Qt::LeftButton) { DELETETHIS - if (!ctrl) - { - MusEGlobal::song->deselectTracks(); - emit selectionChanged(0); - } - }*/ return; } @@ -1809,44 +1742,95 @@ break; } + case COL_INPUT_MONITOR: + { + if(!t->canRecordMonitor()) + { + mode = START_DRAG; // Allow a track drag to start. + break; + } + + const bool val = !(t->recMonitor()); + if (button == Qt::LeftButton) + { + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(t, val, MusECore::PendingOperationItem::SetTrackRecMonitor)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } + else if (button == Qt::RightButton) + { + // enable or disable ALL tracks of this type + // This is a major operation not easily manually undoable. Let's make it undoable. + MusECore::Undo operations; + MusECore::TrackList* all_tl = MusEGlobal::song->tracks(); + foreach (MusECore::Track *other_t, *all_tl) + { + if(other_t->type() != t->type()) + continue; + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackRecMonitor, other_t, val)); + } + if(!operations.empty()) + { + MusEGlobal::song->applyOperationGroup(operations); + // Not required if undoable. + //MusEGlobal::song->update(SC_TRACK_REC_MONITOR); + } + } + } + break; + case COL_RECORD: { - mode = START_DRAG; - + if(!t->canRecord()) + { + mode = START_DRAG; // Allow a track drag to start. + break; + } + bool val = !(t->recordFlag()); if (button == Qt::LeftButton) { - if (!t->isMidiTrack()) { - if (t->type() == MusECore::Track::AUDIO_OUTPUT) { - if (val && t->recordFlag() == false) { - MusEGlobal::muse->bounceToFile((MusECore::AudioOutput*)t); - } - MusEGlobal::audio->msgSetRecord((MusECore::AudioOutput*)t, val); - if (!((MusECore::AudioOutput*)t)->recFile()) - val = false; - else - return; - } - MusEGlobal::song->setRecordFlag(t, val); + if (t->type() == MusECore::Track::AUDIO_OUTPUT) + { + if (val && !t->recordFlag()) + { + MusEGlobal::muse->bounceToFile((MusECore::AudioOutput*)t); + break; } - else - MusEGlobal::song->setRecordFlag(t, val); - } else if (button == Qt::RightButton) { + } + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + if(!t->setRecordFlag1(val)) + break; + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(t, val, MusECore::PendingOperationItem::SetTrackRecord)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } + else if (button == Qt::RightButton) + { // enable or disable ALL tracks of this type + // This is a major operation not easily manually undoable. Let's make it undoable. + MusECore::Undo operations; if (!t->isMidiTrack()) { if (t->type() == MusECore::Track::AUDIO_OUTPUT) { return; } MusECore::WaveTrackList* wtl = MusEGlobal::song->waves(); foreach (MusECore::WaveTrack *wt, *wtl) { - MusEGlobal::song->setRecordFlag(wt, val); + MusEGlobal::song->setRecordFlag(wt, val, &operations); } } else { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); foreach (MusECore::MidiTrack *mt, *mtl) { - MusEGlobal::song->setRecordFlag(mt, val); + MusEGlobal::song->setRecordFlag(mt, val, &operations); } } + if(!operations.empty()) + { + MusEGlobal::song->applyOperationGroup(operations); + // Not required if undoable. + //MusEGlobal::song->update(SC_RECFLAG | SC_TRACK_REC_MONITOR); + } } } break; @@ -1854,51 +1838,115 @@ mode = START_DRAG; break; case COL_CLASS: + { + bool allSelected=false; + if (t->selected() && tracks->countSelected() > 1) // toggle all selected tracks + allSelected=true; + if (t->isMidiTrack()) - classesPopupMenu(t, x, t->y() - ypos); + classesPopupMenu(t, x, t->y() - ypos, allSelected); break; + } case COL_OPORT: - // Changed by Tim. p3.3.9 DELETETHIS 15 - // Reverted. + { + bool allClassPorts=false; + if (ctrl || shift) + allClassPorts=true; if (button == Qt::LeftButton) - portsPopupMenu(t, x, t->y() - ypos); + MusEGui::midiPortsPopupMenu(t, x, t->y() - ypos, allClassPorts, this); else if (button == Qt::RightButton) oportPropertyPopupMenu(t, x, t->y() - ypos); - //if(((button == QMouseEvent::LeftButton) && (t->type() == MusECore::Track::AUDIO_SOFTSYNTH)) || (button == QMouseEvent::RightButton)) - // oportPropertyPopupMenu(t, x, t->y() - ypos); - //else - //if(button == QMouseEvent::LeftButton) - // portsPopupMenu(t, x, t->y() - ypos); - - //MusEGlobal::audio->msgUpdateSoloStates(); // p4.0.14 - //MusEGlobal::song->update(SC_ROUTE); // break; - + } case COL_MUTE: - mode = START_DRAG; - // p3.3.29 - if ((button == Qt::RightButton) || (((QInputEvent*)ev)->modifiers() & Qt::ShiftModifier)) - t->setOff(!t->off()); - else + { + bool turnOff = (button == Qt::RightButton) || shift; + + if((t->selected() && tracks->countSelected() > 1) || ctrl) { - if (t->off()) - t->setOff(false); + // These are major operations not easily manually undoable. Let's make them undoable. + MusECore::Undo operations; + if (t->selected() && tracks->countSelected() > 1) // toggle all selected tracks + { + for (MusECore::iTrack myt = tracks->begin(); myt != tracks->end(); ++myt) { + if ((*myt)->selected() && (*myt)->type() != MusECore::Track::AUDIO_OUTPUT) + toggleMute(operations, *myt, turnOff); + } + } + else if (ctrl) // toggle ALL tracks + { + for (MusECore::iTrack myt = tracks->begin(); myt != tracks->end(); ++myt) { + if ((*myt)->type() != MusECore::Track::AUDIO_OUTPUT) + toggleMute(operations, *myt, turnOff); + } + } + if(!operations.empty()) + { + MusEGlobal::song->applyOperationGroup(operations); + // Not required if undoable. + //MusEGlobal::song->update(SC_MUTE); + } + } + else { // toggle the clicked track + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + if(turnOff) { + operations.add(MusECore::PendingOperationItem(t, !t->off(), MusECore::PendingOperationItem::SetTrackOff)); + } else - t->setMute(!t->mute()); - } - MusEGlobal::song->update(SC_MUTE); + { + if(t->off()) + operations.add(MusECore::PendingOperationItem(t, false, MusECore::PendingOperationItem::SetTrackOff)); + else + operations.add(MusECore::PendingOperationItem(t, !t->mute(), MusECore::PendingOperationItem::SetTrackMute)); + } + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } + break; + } case COL_SOLO: - mode = START_DRAG; - MusEGlobal::audio->msgSetSolo(t, !t->solo()); - MusEGlobal::song->update(SC_SOLO); + { + if((t->selected() && tracks->countSelected() > 1) || ctrl) + { + // These are major operations not easily manually undoable. Let's make them undoable. + MusECore::Undo operations; + if (t->selected() && tracks->countSelected() > 1) // toggle all selected tracks + { + for (MusECore::iTrack myt = tracks->begin(); myt != tracks->end(); ++myt) { + if ((*myt)->selected() && (*myt)->type() != MusECore::Track::AUDIO_OUTPUT) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackSolo, *myt, !(*myt)->solo())); + } + } + else if (ctrl) // toggle ALL tracks + { + for (MusECore::iTrack myt = tracks->begin(); myt != tracks->end(); ++myt) { + if ((*myt)->type() != MusECore::Track::AUDIO_OUTPUT) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackSolo, *myt, !(*myt)->solo())); + } + } + if(!operations.empty()) + { + MusEGlobal::song->applyOperationGroup(operations); + // Not required if undoable. + //MusEGlobal::song->update(SC_SOLO); + } + } + else // toggle the clicked track + { + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(t, !t->solo(), MusECore::PendingOperationItem::SetTrackSolo)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } + } break; case COL_NAME: mode = START_DRAG; if (button == Qt::LeftButton) { if (!ctrl) { - MusEGlobal::song->deselectTracks(); + MusEGlobal::song->selectAllTracks(false); t->setSelected(true); // rec enable track if expected @@ -1912,50 +1960,57 @@ t->setSelected(!t->selected()); if (editTrack && editTrack != t) returnPressed(); - emit selectionChanged(t->selected() ? t : 0); - } + MusEGlobal::song->update(SC_TRACK_SELECTION); + } else if (button == Qt::RightButton) { mode = NORMAL; QMenu* p = new QMenu; // Leave room for normal track IDs - base these at AUDIO_SOFTSYNTH. + int selCnt = MusEGlobal::song->countSelectedTracks(); p->addAction(QIcon(*automation_clear_dataIcon), tr("Delete Track"))->setData(1001); + if(selCnt > 1){ + p->addAction(QIcon(*edit_track_delIcon), tr("Delete Selected Tracks"))->setData(1003); + } p->addAction(QIcon(*track_commentIcon), tr("Track Comment"))->setData(1002); p->addSeparator(); if (t->type()==MusECore::Track::NEW_DRUM) { QAction* tmp; - p->addAction(tr("Save track's drumlist"))->setData(1010); - p->addAction(tr("Save track's drumlist differences to initial state"))->setData(1011); - p->addAction(tr("Load track's drumlist"))->setData(1012); + tmp=p->addAction(tr("Save track's drumlist")); + tmp->setData(1010); + tmp->setEnabled(!static_cast(t)->workingDrumMap()->empty()); + tmp=p->addAction(tr("Load track's drumlist")); + tmp->setData(1012); tmp=p->addAction(tr("Reset track's drumlist")); tmp->setData(1013); - tmp->setEnabled(!((MusECore::MidiTrack*)t)->drummap_tied_to_patch()); + tmp->setEnabled(!static_cast(t)->workingDrumMap()->empty()); tmp=p->addAction(tr("Reset track's drumlist-ordering")); tmp->setData(1016); tmp->setEnabled(!((MusECore::MidiTrack*)t)->drummap_ordering_tied_to_patch()); - p->addAction(tr("Copy track's drumlist to all selected tracks"))->setData(1014); - p->addAction(tr("Copy track's drumlist's differences to all selected tracks"))->setData(1015); + tmp=p->addAction(tr("Copy track's drumlist to all selected tracks")); + tmp->setData(1014); + tmp->setEnabled(!static_cast(t)->workingDrumMap()->empty()); // 1016 is occupied. p->addSeparator(); } - - QMenu* pnew = new QMenu(p); - pnew->setTitle(tr("Insert Track")); - pnew->setIcon(QIcon(*edit_track_addIcon)); - MusEGui::populateAddTrack(pnew); - p->addMenu(pnew); + addTrackMenu->setTitle(tr("Insert Track")); + addTrackMenu->setIcon(QIcon(*edit_track_addIcon)); + p->addMenu(addTrackMenu); QAction* act = p->exec(ev->globalPos(), 0); if (act) { + //fprintf(stderr, "TList::mousePressEvent act:%p\n", act); int n = act->data().toInt(); - if(n >= 1000) + if(n >= 1000 && n < MENU_ADD_SYNTH_ID_BASE) { + //fprintf(stderr, " n:%d\n", n); switch (n) { case 1001: // delete track - MusEGlobal::song->removeTrack0(t); - MusEGlobal::audio->msgUpdateSoloStates(); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(t), t)); + break; + case 1003: // delete track(s) + MusEGlobal::audio->msgRemoveTracks(); break; - case 1002: // show track comment { TrackComment* tc = new TrackComment(t, 0); @@ -1967,10 +2022,6 @@ saveTrackDrummap((MusECore::MidiTrack*)t, true); break; - case 1011: - saveTrackDrummap((MusECore::MidiTrack*)t, false); - break; - case 1012: loadTrackDrummap((MusECore::MidiTrack*)t); break; @@ -1980,8 +2031,17 @@ tr("Reset the track's drum map with instrument defaults?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok) { - ((MusECore::MidiTrack*)t)->set_drummap_tied_to_patch(true); - MusEGlobal::song->update(SC_DRUMMAP); + // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the + // original lists will be deleted, in the operation following. + MusECore::PendingOperationList operations; + // Completely blank replacement list. + MusECore::WorkingDrumMapPatchList* new_wdmpl = new MusECore::WorkingDrumMapPatchList(); + MusECore::DrumMapTrackPatchReplaceOperation* dmop = new MusECore::DrumMapTrackPatchReplaceOperation; + dmop->_isInstrumentMod = false; // Not instrument operation. + dmop->_workingItemPatchList = new_wdmpl; + dmop->_track = static_cast(t); + operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); } break; @@ -1999,10 +2059,6 @@ copyTrackDrummap((MusECore::MidiTrack*)t, true); break; - case 1015: - copyTrackDrummap((MusECore::MidiTrack*)t, false); - break; - default: printf("action %d\n", n); break; @@ -2011,11 +2067,12 @@ else { t = MusEGlobal::song->addNewTrack(act, t); // Let addNewTrack handle it. Insert before clicked-on track 't'. + //fprintf(stderr, " addNewTrack: track:%p\n", t); if(t) { - MusEGlobal::song->deselectTracks(); + MusEGlobal::song->selectAllTracks(false); t->setSelected(true); - emit selectionChanged(t); + MusEGlobal::song->update(SC_TRACK_SELECTION); adjustScrollbar(); } } @@ -2025,57 +2082,27 @@ break; case COL_TIMELOCK: - mode = START_DRAG; - t->setLocked(!t->locked()); + if(!t->isMidiTrack()) + { + mode = START_DRAG; // Allow a track drag to start. + break; + } + t->setLocked(!t->locked()); break; case COL_OCHANNEL: { - mode = START_DRAG; // or not? (flo) int delta = 0; if (button == Qt::RightButton) delta = 1; else if (button == Qt::MidButton) delta = -1; - if (t->isMidiTrack()) - { - MusECore::MidiTrack* mt = dynamic_cast(t); - if (mt == 0) - break; - int channel = mt->outChannel(); - channel += delta; - if(channel >= MIDI_CHANNELS) - channel = MIDI_CHANNELS - 1; - if(channel < 0) - channel = 0; - if (channel != mt->outChannel()) - { - MusEGlobal::audio->msgIdle(true); - mt->setOutChanAndUpdate(channel); - MusEGlobal::audio->msgIdle(false); - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); - } - } else { - if(t->type() != MusECore::Track::AUDIO_SOFTSYNTH) - { - MusECore::AudioTrack* at = dynamic_cast(t); - if (at == 0) - break; - - int n = t->channels() + delta; - if (n > MAX_CHANNELS) - n = MAX_CHANNELS; - else if (n < 1) - n = 1; - if (n != t->channels()) { - MusEGlobal::audio->msgSetChannels(at, n); - MusEGlobal::song->update(SC_CHANNELS); - } - } - } + mode = START_DRAG; // Allow a track drag to start. + break; + } + setTrackChannel(t, true, 0, delta, ctrl || shift); } break; @@ -2140,7 +2167,7 @@ { if (p->second->tick()==0) { - for (MusECore::iEvent ev=p->second->events()->begin(); ev!=p->second->events()->end(); ev++) + for (MusECore::ciEvent ev=p->second->events().begin(); ev!=p->second->events().end(); ev++) { if (ev->second.tick()!=0) break; else if (ev->second.type()==MusECore::Controller && ev->second.dataA()==ctrl_num) @@ -2195,6 +2222,20 @@ redraw(); } +void TList::toggleMute(MusECore::Undo& operations, MusECore::Track *t, bool turnOff) +{ + if (turnOff) { + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackOff, t, !t->off())); + } + else + { + if (t->off()) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackOff, t, false)); + else + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackMute, t, !t->mute())); + } +} + void TList::loadTrackDrummap(MusECore::MidiTrack* t, const char* fn_) { QString fn; @@ -2215,53 +2256,80 @@ FILE* f = MusEGui::fileOpen(this, fn, QString(".map"), "r", popenFlag, true); if (f == 0) { - printf("ERROR: TList::loadTrackDrummap() could not open file %s!\n", fn.toAscii().data()); + printf("ERROR: TList::loadTrackDrummap() could not open file %s!\n", fn.toLatin1().data()); return; } MusECore::Xml xml(f); - int mode = 0; + loadTrackDrummapFromXML(t, xml); + + if (popenFlag) + pclose(f); + else + fclose(f); + + MusEGlobal::song->update(SC_DRUMMAP); +} + +void TList::loadTrackDrummapFromXML(MusECore::MidiTrack *t, MusECore::Xml &xml) +{ + MusECore::PendingOperationList operations; + MusECore::WorkingDrumMapPatchList* wdmpl = 0; + for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: + if(wdmpl) + delete wdmpl; return; case MusECore::Xml::TagStart: - if (mode == 0 && tag == "muse") - mode = 1; - else if (mode == 1 && tag == "our_drummap") { - t->readOurDrumMap(xml, tag, true); - mode = 0; - } - else if (mode == 1 && tag == "drummap") { // compatibility mode, read old drummaps - QMessageBox::information(this, tr("Drummap"), tr("This drummap was created with a previous version of MusE,\nit is being read but the format has changed slightly so some\nadjustments may be necessary.")); - t->readOurDrumMap(xml, tag, true, true); - mode = 0; - } - else + if (tag == "muse") + { + } + else if (tag == "our_drummap" || // OBSOLETE. Support old files. + tag == "drummap" || // OBSOLETE. Support old files. + tag == "drumMapPatch") + { + if(!wdmpl) + wdmpl = new MusECore::WorkingDrumMapPatchList(); + // false = Do not fill in unused items. + wdmpl->read(xml, false); + } + + else xml.unknown("TList::loadTrackDrummap"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: - if (!mode && tag == "muse") - goto ende; + if (tag == "muse") + { + if(wdmpl) + { + // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the + // original lists will be deleted, in the operation following. + MusECore::DrumMapTrackPatchReplaceOperation* dmop = new MusECore::DrumMapTrackPatchReplaceOperation; + dmop->_isInstrumentMod = false; // Not instrument operation. + dmop->_workingItemPatchList = wdmpl; + dmop->_track = t; + + operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } + goto ende; + } default: break; } } ende: - if (popenFlag) - pclose(f); - else - fclose(f); - - MusEGlobal::song->update(SC_DRUMMAP); + return; } -void TList::saveTrackDrummap(MusECore::MidiTrack* t, bool full, const char* fn_) +void TList::saveTrackDrummap(MusECore::MidiTrack* t, bool /*full*/, const char* fn_) { QString fn; if (fn_==NULL) @@ -2269,61 +2337,67 @@ this, tr("MusE: Store Track's Drum Map")); else fn = QString(fn_); - + if (fn.isEmpty()) return; - + bool popenFlag; FILE* f = MusEGui::fileOpen(this, fn, QString(".map"), "w", popenFlag, false, true); if (f == 0) return; - + MusECore::Xml xml(f); xml.header(); xml.tag(0, "muse version=\"1.0\""); - t->writeOurDrumMap(1, xml, full); + + t->workingDrumMap()->write(1, xml); + xml.tag(0, "/muse"); if (popenFlag) pclose(f); else - fclose(f); + fclose(f); } -void TList::copyTrackDrummap(MusECore::MidiTrack* t, bool full) +void TList::copyTrackDrummap(MusECore::MidiTrack* t, bool /*full*/) { - char* tmp1 = tmpnam(NULL); - char tmp2[1000]; - strcpy(tmp2, tmp1); - strcat(tmp2, ".map"); - if (MusEGlobal::debugMsg) - printf("in TList::copyTrackDrummap(); filename is %s\n",tmp2); - - saveTrackDrummap(t, full, tmp2); - - for (MusECore::iTrack it = MusEGlobal::song->tracks()->begin(); it!=MusEGlobal::song->tracks()->end(); it++) - if ((*it)->selected() && (*it)->type()==MusECore::Track::NEW_DRUM) - { - if (MusEGlobal::debugMsg) - printf(" processing track...\n"); + MusECore::PendingOperationList operations; + MusECore::WorkingDrumMapPatchList* new_wdmpl; - loadTrackDrummap((MusECore::MidiTrack*)(*it), tmp2); - } - - remove(tmp2); + MusECore::WorkingDrumMapPatchList* wdmpl = t->workingDrumMap(); + MusECore::MidiTrack* mt; + for(MusECore::iMidiTrack it = MusEGlobal::song->midis()->begin(); it != MusEGlobal::song->midis()->end(); ++it) + { + mt = *it; + if(mt == t || !mt->selected() || mt->type() != MusECore::Track::NEW_DRUM) + continue; + + // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the + // original lists will be deleted, in the operation following. + new_wdmpl = new MusECore::WorkingDrumMapPatchList(); + *new_wdmpl = *wdmpl; + MusECore::DrumMapTrackPatchReplaceOperation* dmop = new MusECore::DrumMapTrackPatchReplaceOperation; + dmop->_isInstrumentMod = false; // Not instrument operation. + dmop->_workingItemPatchList = new_wdmpl; + dmop->_track = mt; + operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList)); + } + + if(!operations.empty()) + MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // selectTrack //--------------------------------------------------------- -void TList::selectTrack(MusECore::Track* tr) +void TList::selectTrack(MusECore::Track* tr, bool /*deselect*/) { - MusEGlobal::song->deselectTracks(); + MusEGlobal::song->selectAllTracks(false); if (tr) { tr->setSelected(true); - // rec enable track if expected MusECore::TrackList recd = getRecEnabledTracks(); if (recd.size() == 1 && MusEGlobal::config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection @@ -2332,8 +2406,9 @@ } } - redraw(); - emit selectionChanged(tr); + // SC_TRACK_SELECTION will cause update anyway, no harm ... + update(); + MusEGlobal::song->update(SC_TRACK_SELECTION); } //--------------------------------------------------------- @@ -2357,6 +2432,14 @@ void TList::mouseMoveEvent(QMouseEvent* ev) { + if((editor && (editor->isVisible() || editor->hasFocus())) || + (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) || + (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus()))) + { + ev->accept(); + return; + } + if ((((QInputEvent*)ev)->modifiers() | ev->buttons()) == 0) { int y = ev->y(); int ty = -ypos; @@ -2425,7 +2508,8 @@ if (h < MIN_TRACKHEIGHT) h = MIN_TRACKHEIGHT; t->setHeight(h); - MusEGlobal::song->update(SC_TRACK_MODIFIED); + update(); + MusEGlobal::song->update(SC_TRACK_RESIZED); } } } @@ -2439,6 +2523,14 @@ void TList::mouseReleaseEvent(QMouseEvent* ev) { + if((editor && (editor->isVisible() || editor->hasFocus())) || + (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) || + (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus()))) + { + ev->accept(); + return; + } + if (mode == DRAG) { MusECore::Track* t = y2Track(ev->y() + ypos); if (t) { @@ -2461,7 +2553,8 @@ // loop through all tracks and set the levels for all tracks for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) { MusECore::AudioTrack *trk = (MusECore::AudioTrack*)*t; - if (trk->hasAuxSend()) + + if (!trk->isMidiTrack() && trk->hasAuxSend()) { std::vector oldAuxValue; for (unsigned i = 0 ; i < auxCopy.size(); i++) @@ -2500,185 +2593,6 @@ void TList::wheelEvent(QWheelEvent* ev) { emit redirectWheelEvent(ev); - -// REMOVE Tim. Hate to just kill all this, so remove later if all tests OK. -// int x = ev->x(); -// int y = ev->y(); -// MusECore::Track* t = y2Track(y + ypos); -// if (t == 0) { -// emit redirectWheelEvent(ev); -// return; -// } -// -// TrackColumn col = TrackColumn(header->logicalIndexAt(x)); -// int delta = ev->delta() / WHEEL_DELTA; -// ev->accept(); -// -// switch (col) { -// case COL_RECORD: -// case COL_NONE: -// case COL_CLASS: -// case COL_NAME: -// case COL_AUTOMATION: -// break; -// case COL_MUTE: -// // p3.3.29 -// if (((QInputEvent*)ev)->modifiers() & Qt::ShiftModifier) -// t->setOff(!t->off()); -// else -// { -// if (t->off()) -// t->setOff(false); -// else -// t->setMute(!t->mute()); -// } -// MusEGlobal::song->update(SC_MUTE); -// break; -// -// case COL_SOLO: -// MusEGlobal::audio->msgSetSolo(t, !t->solo()); -// MusEGlobal::song->update(SC_SOLO); -// break; -// -// case COL_TIMELOCK: -// t->setLocked(!t->locked()); -// break; -// -// case COL_OPORT: -// if (t->isMidiTrack()) { -// MusECore::MidiTrack* mt = (MusECore::MidiTrack*)t; -// int port = mt->outPort() + delta; -// -// if (port >= MIDI_PORTS) -// port = MIDI_PORTS-1; -// else if (port < 0) -// port = 0; -// if (port != ((MusECore::MidiTrack*)t)->outPort()) { -// MusEGlobal::audio->msgIdle(true); -// mt->setOutPortAndUpdate(port); -// MusEGlobal::audio->msgIdle(false); -// -// MusEGlobal::audio->msgUpdateSoloStates(); // p4.0.14 -// MusEGlobal::song->update(SC_MIDI_TRACK_PROP); // p4.0.17 -// } -// } -// break; -// -// case COL_OCHANNEL: -// if (t->isMidiTrack()) { -// MusECore::MidiTrack* mt = (MusECore::MidiTrack*)t; -// if (mt && mt->type() == MusECore::Track::DRUM) -// break; -// -// int channel = mt->outChannel() + delta; -// -// if (channel >= MIDI_CHANNELS) -// channel = MIDI_CHANNELS-1; -// else if (channel < 0) -// channel = 0; -// if (channel != ((MusECore::MidiTrack*)t)->outChannel()) { -// MusEGlobal::audio->msgIdle(true); -// mt->setOutChanAndUpdate(channel); -// MusEGlobal::audio->msgIdle(false); -// -// // may result in adding/removing mixer strip: -// //MusEGlobal::song->update(-1); -// MusEGlobal::audio->msgUpdateSoloStates(); // p4.0.14 -// MusEGlobal::song->update(SC_MIDI_TRACK_PROP); -// } -// } -// else { -// int n = t->channels() + delta; -// if (n > MAX_CHANNELS) -// n = MAX_CHANNELS; -// else if (n < 1) -// n = 1; -// if (n != t->channels()) { -// MusEGlobal::audio->msgSetChannels((MusECore::AudioTrack*)t, n); -// MusEGlobal::song->update(SC_CHANNELS); -// } -// } -// break; -// default: -// if (col>=COL_CUSTOM_MIDICTRL_OFFSET) -// { -// mode = START_DRAG; -// -// if (t->isMidiTrack()) -// { -// MusECore::MidiTrack* mt = dynamic_cast(t); -// if (mt == 0) -// break; -// -// int ctrl_num = Arranger::custom_columns[col-COL_CUSTOM_MIDICTRL_OFFSET].ctrl; -// -// MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; -// MusECore::MidiController* mctl = mp->midiController(ctrl_num); -// -// int minval=mctl->minVal()+mctl->bias(); -// int maxval=mctl->maxVal()+mctl->bias(); -// -// int val = mt->getControllerChangeAtTick(0,ctrl_num); -// int oldval=val; -// -// if (ctrl_num!=MusECore::CTRL_PROGRAM) -// { -// val += delta; -// if(val > maxval) -// val = maxval; -// if(val < minval-1) // "-1" because of "off" -// val = minval-1; -// } -// else -// { -// MusECore::MidiInstrument* instr = mp->instrument(); -// if (delta>0) val=instr->getNextPatch(mt->outChannel(), val, false); -// else if (delta<0) val=instr->getPrevPatch(mt->outChannel(), val, false); -// } -// -// if (val != oldval) -// { -// if (val!=minval-1) -// { -// int at_tick; -// if (Arranger::custom_columns[col-COL_CUSTOM_MIDICTRL_OFFSET].affected_pos == -// Arranger::custom_col_t::AFFECT_BEGIN) -// at_tick=0; -// else -// at_tick=MusEGlobal::song->cpos(); -// -// record_controller_change_and_maybe_send(at_tick, ctrl_num, val, mt); -// } -// else -// { -// MusECore::Undo operations; -// for (MusECore::iPart p = mt->parts()->begin(); p!=mt->parts()->end(); p++) -// { -// if (p->second->tick()==0) -// { -// for (MusECore::iEvent ev=p->second->events()->begin(); ev!=p->second->events()->end(); ev++) -// { -// if (ev->second.tick()!=0) break; -// else if (ev->second.type()==MusECore::Controller && ev->second.dataA()==ctrl_num) -// { -// using MusECore::UndoOp; -// operations.push_back(UndoOp(UndoOp::DeleteEvent, ev->second, p->second, false, false)); -// break; -// } -// } -// } -// } -// MusEGlobal::song->applyOperationGroup(operations); -// } -// } -// } -// } -// else -// mode = START_DRAG; -// -// break; -// } - } @@ -2698,107 +2612,64 @@ // classesPopupMenu //--------------------------------------------------------- -void TList::classesPopupMenu(MusECore::Track* t, int x, int y) - { - QMenu p; - p.clear(); - p.addAction(QIcon(*addtrack_addmiditrackIcon), tr("Midi"))->setData(MusECore::Track::MIDI); - p.addAction(QIcon(*addtrack_drumtrackIcon), tr("Drum"))->setData(MusECore::Track::DRUM); - p.addAction(QIcon(*addtrack_newDrumtrackIcon), tr("New style drum"))->setData(MusECore::Track::NEW_DRUM); - QAction* act = p.exec(mapToGlobal(QPoint(x, y)), 0); - - if (!act) - return; +void TList::classesPopupMenu(MusECore::Track* tIn, int x, int y, bool allSelected) +{ + QMenu p; + p.clear(); + p.addAction(QIcon(*addtrack_addmiditrackIcon), tr("Midi"))->setData(MusECore::Track::MIDI); + p.addAction(QIcon(*addtrack_newDrumtrackIcon), tr("Drum"))->setData(MusECore::Track::NEW_DRUM); + QAction* act = p.exec(mapToGlobal(QPoint(x, y)), 0); - int n = act->data().toInt(); - if ((MusECore::Track::TrackType(n) == MusECore::Track::MIDI || MusECore::Track::TrackType(n) == MusECore::Track::NEW_DRUM) && t->type() == MusECore::Track::DRUM) { - // - // Drum -> Midi - // - MusEGlobal::audio->msgIdle(true); - MusECore::PartList* pl = t->parts(); - MusECore::MidiTrack* m = (MusECore::MidiTrack*) t; - for (MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) { - MusECore::EventList* el = ip->second->events(); - for (MusECore::iEvent ie = el->begin(); ie != el->end(); ++ie) { - MusECore::Event ev = ie->second; - if(ev.type() == MusECore::Note) - { - int pitch = ev.pitch(); - pitch = MusEGlobal::drumMap[pitch].enote; - ev.setPitch(pitch); - } - else - if(ev.type() == MusECore::Controller) - { - int ctl = ev.dataA(); - // Is it a drum controller event, according to the track port's instrument? - MusECore::MidiController *mc = MusEGlobal::midiPorts[m->outPort()].drumController(ctl); - if(mc) - // Change the controller event's index into the drum map to an instrument note. - ev.setA((ctl & ~0xff) | MusEGlobal::drumMap[ctl & 0x7f].enote); - } - } - } - t->setType(MusECore::Track::TrackType(n)); - MusEGlobal::audio->msgIdle(false); - MusEGlobal::song->update(SC_EVENT_MODIFIED); - } - else if (MusECore::Track::TrackType(n) == MusECore::Track::DRUM && (t->type() == MusECore::Track::MIDI || t->type() == MusECore::Track::NEW_DRUM)) { - // - // Midi -> Drum - // + if (!act) + return; - // Default to track port if -1 and track channel if -1. No need anymore to ask to change all items. + int n = act->data().toInt(); - MusEGlobal::audio->msgIdle(true); + if (!allSelected) { + changeTrackToType(tIn,MusECore::Track::TrackType(n)); + } + else + { + MusECore::TrackList *tracks = MusEGlobal::song->tracks(); + for (MusECore::iTrack myt = tracks->begin(); myt != tracks->end(); ++myt) + { + if ((*myt)->selected() && (*myt)->isMidiTrack()) + changeTrackToType(*myt,MusECore::Track::TrackType(n)); - // Delete all port controller events. - MusEGlobal::song->changeAllPortDrumCtrlEvents(false); - - MusECore::PartList* pl = t->parts(); - MusECore::MidiTrack* m = (MusECore::MidiTrack*) t; - for (MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) { - MusECore::EventList* el = ip->second->events(); - for (MusECore::iEvent ie = el->begin(); ie != el->end(); ++ie) { - MusECore::Event ev = ie->second; - if (ev.type() == MusECore::Note) - { - int pitch = ev.pitch(); - pitch = MusEGlobal::drumInmap[pitch]; - ev.setPitch(pitch); - } - else - { - if(ev.type() == MusECore::Controller) - { - int ctl = ev.dataA(); - // Is it a drum controller event, according to the track port's instrument? - MusECore::MidiController *mc = MusEGlobal::midiPorts[m->outPort()].drumController(ctl); - if(mc) - // Change the controller event's instrument note to an index into the drum map. - ev.setA((ctl & ~0xff) | MusEGlobal::drumInmap[ctl & 0x7f]); - } - } - } - } + } // track for-loop + } +} - t->setType(MusECore::Track::DRUM); - - // Add all port controller events. - MusEGlobal::song->changeAllPortDrumCtrlEvents(true); - - MusEGlobal::audio->msgIdle(false); - MusEGlobal::song->update(SC_EVENT_MODIFIED); - } - else // MIDI -> NEW_DRUM or vice versa. added by flo. - { - MusEGlobal::audio->msgIdle(true); - t->setType(MusECore::Track::TrackType(n)); - MusEGlobal::audio->msgIdle(false); - MusEGlobal::song->update(SC_TRACK_MODIFIED); - } - } +void TList::changeTrackToType(MusECore::Track *t, MusECore::Track::TrackType trackType) +{ + if ((trackType == MusECore::Track::MIDI || trackType == MusECore::Track::NEW_DRUM) && t->type() == MusECore::Track::DRUM) + { + // + // Drum -> Midi + // + MusEGlobal::audio->msgIdle(true); + static_cast(t)->convertToType(trackType); + MusEGlobal::audio->msgIdle(false); + MusEGlobal::song->update(SC_EVENT_MODIFIED); + } + else if (trackType == MusECore::Track::DRUM && (t->type() == MusECore::Track::MIDI || t->type() == MusECore::Track::NEW_DRUM)) + { + // + // Midi -> Drum + // + MusEGlobal::audio->msgIdle(true); + static_cast(t)->convertToType(trackType); + MusEGlobal::audio->msgIdle(false); + MusEGlobal::song->update(SC_EVENT_MODIFIED); + } + else // MIDI -> NEW_DRUM or vice versa. added by flo. + { + MusEGlobal::audio->msgIdle(true); + t->setType(trackType); + MusEGlobal::audio->msgIdle(false); + MusEGlobal::song->update(SC_TRACK_MODIFIED); + } +} void TList::instrPopupActivated(QAction* act) { @@ -2818,4 +2689,11 @@ redraw(); } +void TList::populateAddTrack() +{ + addTrackMenu = new QMenu; + MusEGui::populateAddTrack(addTrackMenu); +} + } // namespace MusEGui + diff -Nru muse-2.1.2/muse/arranger/tlist.h muse-3.0.2+ds1/muse/arranger/tlist.h --- muse-2.1.2/muse/arranger/tlist.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/arranger/tlist.h 2018-01-06 20:31:35.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: tlist.h,v 1.8.2.5 2008/01/19 13:33:46 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -36,10 +37,13 @@ class QPaintEvent; class QScrollBar; class QWheelEvent; +class QMenu; namespace MusECore { class Track; class Xml; +//class PendingOperationList; +class Undo; } namespace MusEGui { @@ -48,7 +52,8 @@ class ScrollScale; enum TrackColumn { - COL_RECORD = 0, + COL_INPUT_MONITOR = 0, + COL_RECORD, COL_MUTE, COL_SOLO, COL_CLASS, @@ -78,6 +83,7 @@ QPixmap bgPixmap; // background Pixmap bool resizeFlag; // true if resize cursor is shown + QMenu* addTrackMenu; Header* header; QScrollBar* _scroll; QLineEdit* editor; @@ -88,6 +94,7 @@ MusECore::Track* editTrack; MusECore::Track* editAutomation; + int startY; int curY; int sTrack; @@ -104,17 +111,21 @@ virtual void keyPressEvent(QKeyEvent* e); virtual void wheelEvent(QWheelEvent* e); - void portsPopupMenu(MusECore::Track*, int, int); void oportPropertyPopupMenu(MusECore::Track*, int x, int y); void moveSelection(int n); void adjustScrollbar(); void paint(const QRect& r); void redraw(const QRect& r); MusECore::Track* y2Track(int) const; - void classesPopupMenu(MusECore::Track*, int x, int y); + void classesPopupMenu(MusECore::Track*, int x, int y, bool allSelected); MusECore::TrackList getRecEnabledTracks(); void setHeaderToolTips(); PopupMenu* colorMenu(QColor c, int id, QWidget* parent); + void toggleMute(MusECore::Undo& operations, MusECore::Track *t, bool turnOff); + void changeTrackToType(MusECore::Track *t, MusECore::Track::TrackType trackType); + void editTrackName(MusECore::Track *t); + void setTrackChannel(MusECore::Track *t, bool isDelta, int channel, int delta, bool doAllTracks = false); + protected: bool event(QEvent *); @@ -128,27 +139,32 @@ void changeAutomation(QAction*); void changeAutomationColor(QAction*); void loadTrackDrummap(MusECore::MidiTrack*, const char* filename=NULL); + void loadTrackDrummapFromXML(MusECore::MidiTrack*t, MusECore::Xml &xml); void saveTrackDrummap(MusECore::MidiTrack*, bool full, const char* filename=NULL); void copyTrackDrummap(MusECore::MidiTrack*, bool full); signals: - void selectionChanged(MusECore::Track*); void keyPressExt(QKeyEvent*); void redirectWheelEvent(QWheelEvent*); + void verticalScrollSetYpos(int ypos); public slots: void tracklistChanged(); void setYPos(int); void redraw(); - void selectTrack(MusECore::Track*); + void selectTrack(MusECore::Track*, bool deselect=true); void selectTrackAbove(); void selectTrackBelow(); + void editTrackNameSlot(); + void muteSelectedTracksSlot(); + void soloSelectedTracksSlot(); void setHeader(Header*); public: TList(Header*, QWidget* parent, const char* name); void setScroll(QScrollBar* s) { _scroll = s; } MusECore::Track* track() const { return editTrack; } + void populateAddTrack(); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/audio.cpp muse-3.0.2+ds1/muse/audio.cpp --- muse-2.1.2/muse/audio.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/audio.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -23,13 +23,16 @@ //========================================================= #include +#include #include +#include #include "app.h" #include "song.h" #include "node.h" #include "audiodev.h" #include "mididev.h" +#include "midiport.h" #include "alsamidi.h" #include "synth.h" #include "audioprefetch.h" @@ -44,6 +47,9 @@ #include "gconfig.h" #include "pos.h" #include "ticksynth.h" +//#include "operations.h" +#include "undo.h" +#include "globals.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. @@ -59,15 +65,18 @@ #include "jackaudio.h" #endif +// Undefine if and when multiple output routes are added to midi tracks. +#define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + + namespace MusEGlobal { -MusECore::Audio* audio; -MusECore::AudioDevice* audioDevice; // current audio device in use +MusECore::Audio* audio = NULL; +MusECore::AudioDevice* audioDevice = NULL; // current audio device in use extern unsigned int volatile midiExtSyncTicks; } namespace MusECore { - - + void initAudio() { MusEGlobal::audio = new Audio(); @@ -76,36 +85,19 @@ extern double curTime(); const char* seqMsgList[] = { - "SEQM_ADD_TRACK", "SEQM_REMOVE_TRACK", //"SEQM_CHANGE_TRACK", DELETETHIS - "SEQM_MOVE_TRACK", - "SEQM_ADD_PART", "SEQM_REMOVE_PART", "SEQM_CHANGE_PART", - "SEQM_ADD_EVENT", "SEQM_REMOVE_EVENT", "SEQM_CHANGE_EVENT", - "SEQM_ADD_TEMPO", "SEQM_SET_TEMPO", "SEQM_REMOVE_TEMPO", "SEQM_ADD_SIG", "SEQM_REMOVE_SIG", - "SEQM_SET_GLOBAL_TEMPO", - "SEQM_UNDO", "SEQM_REDO", + "SEQM_REVERT_OPERATION_GROUP", "SEQM_EXECUTE_OPERATION_GROUP", + "SEQM_EXECUTE_PENDING_OPERATIONS", "SEQM_RESET_DEVICES", "SEQM_INIT_DEVICES", "SEQM_PANIC", "SEQM_MIDI_LOCAL_OFF", - "SEQM_SET_MIDI_DEVICE", "SEQM_PLAY_MIDI_EVENT", "SEQM_SET_HW_CTRL_STATE", "SEQM_SET_HW_CTRL_STATES", - "SEQM_SET_TRACK_OUT_PORT", - "SEQM_SET_TRACK_OUT_CHAN", "SEQM_SET_TRACK_AUTO_TYPE", - "SEQM_REMAP_PORT_DRUM_CTL_EVS", - "SEQM_CHANGE_ALL_PORT_DRUM_CTL_EVS", - "SEQM_SCAN_ALSA_MIDI_PORTS", "SEQM_SET_AUX", "SEQM_UPDATE_SOLO_STATES", - //"MIDI_SHOW_INSTR_GUI", DELETETHIS - //"MIDI_SHOW_INSTR_NATIVE_GUI", DELETETHIS - "AUDIO_RECORD", "AUDIO_ROUTEADD", "AUDIO_ROUTEREMOVE", "AUDIO_REMOVEROUTES", - //"AUDIO_VOL", "AUDIO_PAN", DELETETHIS "AUDIO_ADDPLUGIN", - "AUDIO_SET_SEG_SIZE", "AUDIO_SET_PREFADER", "AUDIO_SET_CHANNELS", - //"AUDIO_SET_PLUGIN_CTRL_VAL", DELETETHIS "AUDIO_SWAP_CONTROLLER_IDX", "AUDIO_CLEAR_CONTROLLER_EVENTS", "AUDIO_SEEK_PREV_AC_EVENT", @@ -114,10 +106,11 @@ "AUDIO_ERASE_RANGE_AC_EVENTS", "AUDIO_ADD_AC_EVENT", "AUDIO_CHANGE_AC_EVENT", - "AUDIO_SET_SOLO", "AUDIO_SET_SEND_METRONOME", + "AUDIO_SET_SEND_METRONOME", "AUDIO_START_MIDI_LEARN", "MS_PROCESS", "MS_STOP", "MS_SET_RTC", "MS_UPDATE_POLL_FD", - "SEQM_IDLE", "SEQM_SEEK" + "SEQM_IDLE", "SEQM_SEEK", + "AUDIO_WAIT" }; const char* audioStates[] = { @@ -129,6 +122,8 @@ // Audio //--------------------------------------------------------- +const int Audio::_extClockHistoryCapacity = 8192; + Audio::Audio() { _running = false; @@ -138,6 +133,7 @@ _bounce = false; _loopFrame = 0; _loopCount = 0; + m_Xruns = 0; _pos.setType(Pos::FRAMES); _pos.setFrame(0); @@ -151,10 +147,11 @@ clickno = 0; clicksMeasure = 0; ticksBeat = 0; + _extClockHistory = new ExtMidiClock[_extClockHistoryCapacity]; + _extClockHistorySize = 0; syncTime = 0.0; syncFrame = 0; - frameOffset = 0; state = STOP; msg = 0; @@ -164,9 +161,6 @@ startExternalRecTick = 0; endExternalRecTick = 0; - _audioMonitor = 0; - _audioMaster = 0; - //--------------------------------------------------- // establish pipes/sockets //--------------------------------------------------- @@ -188,12 +182,14 @@ } sigFd = filedes[1]; sigFdr = filedes[0]; - - // Moved to MusE::MusE - //QSocketNotifier* ss = new QSocketNotifier(filedes[0], QSocketNotifier::Read); - //MusEGlobal::song->connect(ss, SIGNAL(activated(int)), MusEGlobal::song, SLOT(seqSignal(int))); } +Audio::~Audio() +{ + if(_extClockHistory) + delete[] _extClockHistory; +} + //--------------------------------------------------------- // start // start audio processing @@ -212,7 +208,7 @@ if(initJackAudio() == false) { InputList* itl = MusEGlobal::song->inputs(); for (iAudioInput i = itl->begin(); i != itl->end(); ++i) { - if (MusEGlobal::debugMsg) printf("reconnecting input %s\n", (*i)->name().toAscii().data()); + if (MusEGlobal::debugMsg) fprintf(stderr, "reconnecting input %s\n", (*i)->name().toLatin1().data()); for (int x=0; x < (*i)->channels();x++) (*i)->setJackPort(x,0); (*i)->setName((*i)->name()); // restore jack connection @@ -220,21 +216,26 @@ OutputList* otl = MusEGlobal::song->outputs(); for (iAudioOutput i = otl->begin(); i != otl->end(); ++i) { - if (MusEGlobal::debugMsg) printf("reconnecting output %s\n", (*i)->name().toAscii().data()); + if (MusEGlobal::debugMsg) fprintf(stderr, "reconnecting output %s\n", (*i)->name().toLatin1().data()); for (int x=0; x < (*i)->channels();x++) (*i)->setJackPort(x,0); - if (MusEGlobal::debugMsg) printf("name=%s\n",(*i)->name().toAscii().data()); + if (MusEGlobal::debugMsg) fprintf(stderr, "name=%s\n",(*i)->name().toLatin1().data()); (*i)->setName((*i)->name()); // restore jack connection } } else { - printf("Failed to init audio!\n"); + fprintf(stderr, "Failed to init audio!\n"); return false; } } _running = true; // Set before we start to avoid error messages in process. - MusEGlobal::audioDevice->start(MusEGlobal::realTimePriority); + if(!MusEGlobal::audioDevice->start(MusEGlobal::realTimePriority)) + { + fprintf(stderr, "Failed to start audio!\n"); + _running = false; + return false; + } // shall we really stop JACK transport and locate to // saved position? @@ -265,7 +266,7 @@ bool Audio::sync(int jackState, unsigned frame) { - //printf("Audio::sync: state:%d jackState:%d\n", state, jackState); + //fprintf(stderr, "Audio::sync() begin: state:%d jackState:%d frame:%u pos frame:%u\n", state, jackState, frame, _pos.frame()); bool done = true; if (state == LOOP1) @@ -279,6 +280,7 @@ if (state != START_PLAY) { Pos p(frame, false); + //fprintf(stderr, " state != START_PLAY, calling seek...\n"); seek(p); if (!_freewheel) done = MusEGlobal::audioPrefetch->seekDone(); @@ -288,17 +290,37 @@ else { if (frame != _pos.frame()) { // seek during seek + //fprintf(stderr, " state == START_PLAY, calling seek...\n"); seek(Pos(frame, false)); } done = MusEGlobal::audioPrefetch->seekDone(); } } - //printf("Audio::sync: done:%d\n", done); + //fprintf(stderr, "Audio::sync() end: state:%d pos frame:%u\n", state, _pos.frame()); return done; } //--------------------------------------------------------- +// reSyncAudio +// To be called from audio thread only. +//--------------------------------------------------------- + +void Audio::reSyncAudio() +{ + if (isPlaying()) + { + if (!MusEGlobal::checkAudioDevice()) return; +#ifdef _AUDIO_USE_TRUE_FRAME_ + _previousPos = _pos; +#endif + _pos.setTick(curTickPos); + syncFrame = MusEGlobal::audioDevice->framesAtCycleStart(); + syncTime = curTime(); + } +} + +//--------------------------------------------------------- // setFreewheel //--------------------------------------------------------- @@ -314,7 +336,7 @@ void Audio::shutdown() { _running = false; - printf("Audio::shutdown()\n"); + fprintf(stderr, "Audio::shutdown()\n"); write(sigFd, "S", 1); } @@ -330,7 +352,7 @@ if (msg) { processMsg(msg); int sn = msg->serialNo; - msg = 0; // dont process again + msg = 0; // don't process again int rv = write(fromThreadFdw, &sn, sizeof(int)); if (rv != sizeof(int)) { fprintf(stderr, "audio: write(%d) pipe failed: %s\n", @@ -346,6 +368,12 @@ return; } + // + // resync with audio interface + // + syncFrame = MusEGlobal::audioDevice->framesAtCycleStart(); + syncTime = curTime(); + int jackState = MusEGlobal::audioDevice->getState(); //if(MusEGlobal::debugMsg) @@ -353,6 +381,7 @@ if (state == START_PLAY && jackState == PLAY) { _loopCount = 0; + MusEGlobal::song->reenableTouchedControllers(); startRolling(); if (_bounce) write(sigFd, "f", 1); @@ -376,6 +405,7 @@ } else if (state == STOP && jackState == PLAY) { _loopCount = 0; + MusEGlobal::song->reenableTouchedControllers(); startRolling(); } else if (state == LOOP1 && jackState == PLAY) @@ -384,10 +414,10 @@ ; // sync cycle } else if (state != jackState) - printf("JACK: state transition %s -> %s ?\n", + fprintf(stderr, "JACK: state transition %s -> %s ?\n", audioStates[state], audioStates[jackState]); - // printf("p %s %s %d\n", audioStates[jackState], audioStates[state], _pos.frame()); + // fprintf(stderr, "p %s %s %d\n", audioStates[jackState], audioStates[state], _pos.frame()); // // clear aux send buffers @@ -407,10 +437,50 @@ #ifdef _JACK_TIMEBASE_DRIVES_MIDI_ bool use_jack_timebase = false; #endif + + for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) + { + MidiDevice* md = (*id); + const int port = md->midiPort(); + + // Gather midi input from devices that need collecting, such as Jack midi. + md->collectMidiEvents(); + + // Process the selected device's external clock history fifo ring buffer. + // Quickly transfer the items to a list for easier processing later. + // It is possible for the timestamps to be out of order. Deal with it. + // Sort all the timestamps. Do not miss a clock, better that it is at least + // included in the count. + if(port >= 0 && port < MIDI_PORTS && port == MusEGlobal::config.curMidiSyncInPort) + { + // False = don't use the size snapshot. + const int clk_fifo_sz = md->extClockHistory()->getSize(false); + if(clk_fifo_sz != 0) + { + for(int i = 0; i < clk_fifo_sz; ++i) + { + if(_extClockHistorySize >= _extClockHistoryCapacity) + { + fprintf(stderr, "Audio::process: _extClockHistory overrun!\n"); + break; + } + _extClockHistory[_extClockHistorySize] = md->extClockHistory()->get(); + ++_extClockHistorySize; + } + } + } + else + // Otherwise flush and discard the device's unused ring buffer data. + md->extClockHistory()->clearRead(); + } + + //if(MusEGlobal::extSyncFlag.value() && (MusEGlobal::midiSyncContainer.isRunning() || isPlaying())) + // fprintf(stderr, "extSyncFlag:%d externalPlayState:%d isPlaying:%d\n", + // MusEGlobal::extSyncFlag.value(), MusEGlobal::midiSyncContainer.externalPlayState(), isPlaying()); if (isPlaying()) { if (!freewheel()) - MusEGlobal::audioPrefetch->msgTick(); + MusEGlobal::audioPrefetch->msgTick(isRecording(), true); if (_bounce && _pos >= MusEGlobal::song->rPos()) { _bounce = false; @@ -442,7 +512,7 @@ || MusEGlobal::song->loop())) { if(MusEGlobal::debugMsg) - printf("Audio::process curTickPos >= MusEGlobal::song->len\n"); + fprintf(stderr, "Audio::process curTickPos >= MusEGlobal::song->len\n"); MusEGlobal::audioDevice->stopTransport(); return; @@ -462,31 +532,24 @@ n = 0; state = LOOP1; _loopFrame = lpos - n; - - // clear sustain - for (int i = 0; i < MIDI_PORTS; ++i) { - MidiPort* mp = &MusEGlobal::midiPorts[i]; - for (int ch = 0; ch < MIDI_CHANNELS; ++ch) { - if (mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { - if (mp->device()!=NULL) { - MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); - // may cause problems, called from audio thread - mp->device()->putEvent(ev); - } - } - } - } - Pos lp(_loopFrame, false); + // Seek the transport. Note that temporary clearing of sustain + // controllers is done by the seek handlers and then startRolling(). MusEGlobal::audioDevice->seekTransport(lp); } } if(MusEGlobal::extSyncFlag.value()) // p3.3.25 { - nextTickPos = curTickPos + MusEGlobal::midiExtSyncTicks; - // Probably not good - interfere with midi thread. - MusEGlobal::midiExtSyncTicks = 0; + // Advance the tick position by the number of clock events times the division. + const int div = MusEGlobal::config.division / 24; + int tcks = 0; + for(int i = 0; i < _extClockHistorySize; ++i) + { + if(_extClockHistory[i].isPlaying()) + ++tcks; + } + nextTickPos = curTickPos + tcks * div; } else { @@ -505,12 +568,6 @@ } } } - // - // resync with audio interface - // - syncFrame = MusEGlobal::audioDevice->framePos(); - syncTime = curTime(); - frameOffset = syncFrame - samplePos; process1(samplePos, offset, frames); for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) @@ -525,6 +582,13 @@ // set curTickPos (above) from the reported current tick. curTickPos = nextTickPos; } + + // If external sync has started but the transport has not started yet, + // don't reset the clock history yet, just let it pile up until the transport starts. + // It's because curTickPos does not advance yet until transport is running, so we + // can't rely on curTickPos as a base just yet... + if(!MusEGlobal::extSyncFlag.value() || !MusEGlobal::midiSyncContainer.isPlaying() || isPlaying()) + _extClockHistorySize = 0; } //--------------------------------------------------------- @@ -533,9 +597,8 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) { - if (MusEGlobal::midiSeqRunning) { - processMidi(); - } + processMidi(); + // // process not connected tracks // to animate meter display @@ -567,24 +630,24 @@ ((AudioTrack*)metronome)->preProcessAlways(); // Process Aux tracks first. - for(ciTrack it = tl->begin(); it != tl->end(); ++it) + for(ciTrack it = tl->begin(); it != tl->end(); ++it) { if((*it)->isMidiTrack()) continue; track = (AudioTrack*)(*it); if(!track->processed() && track->type() == Track::AUDIO_AUX) { - //printf("Audio::process1 Do aux: track:%s\n", track->name().toLatin1().constData()); DELETETHIS + //fprintf(stderr, "Audio::process1 Do aux: track:%s\n", track->name().toLatin1().constData()); DELETETHIS channels = track->channels(); // Just a dummy buffer. float* buffer[channels]; float data[frames * channels]; for (int i = 0; i < channels; ++i) buffer[i] = data + i * frames; - //printf("Audio::process1 calling track->copyData for track:%s\n", track->name().toLatin1()); DELETETHIS - track->copyData(samplePos, channels, -1, -1, frames, buffer); + //fprintf(stderr, "Audio::process1 calling track->copyData for track:%s\n", track->name().toLatin1()); DELETETHIS + track->copyData(samplePos, -1, channels, channels, -1, -1, frames, buffer); } - } + } OutputList* ol = MusEGlobal::song->outputs(); for (ciAudioOutput i = ol->begin(); i != ol->end(); ++i) @@ -603,15 +666,15 @@ track = (AudioTrack*)(*it); if(!track->processed() && (track->type() != Track::AUDIO_OUTPUT)) { - //printf("Audio::process1 track:%s\n", track->name().toLatin1().constData()); DELETETHIS + //fprintf(stderr, "Audio::process1 track:%s\n", track->name().toLatin1().constData()); DELETETHIS channels = track->channels(); // Just a dummy buffer. float* buffer[channels]; float data[frames * channels]; for (int i = 0; i < channels; ++i) buffer[i] = data + i * frames; - //printf("Audio::process1 calling track->copyData for track:%s\n", track->name().toLatin1()); DELETETHIS - track->copyData(samplePos, channels, -1, -1, frames, buffer); + //fprintf(stderr, "Audio::process1 calling track->copyData for track:%s\n", track->name().toLatin1()); DELETETHIS + track->copyData(samplePos, -1, channels, channels, -1, -1, frames, buffer); } } } @@ -623,9 +686,6 @@ void Audio::processMsg(AudioMsg* msg) { switch(msg->id) { - case AUDIO_RECORD: - msg->snode->setRecordFlag2(msg->ival); - break; case AUDIO_ROUTEADD: addRoute(msg->sroute, msg->droute); break; @@ -671,9 +731,6 @@ case AUDIO_CHANGE_AC_EVENT: msg->snode->changeACEvent(msg->ival, msg->a, msg->b, msg->dval); break; - case AUDIO_SET_SOLO: - msg->track->setSolo((bool)msg->ival); - break; case AUDIO_SET_SEND_METRONOME: msg->snode->setSendMetronome((bool)msg->ival); @@ -685,18 +742,6 @@ MusEGlobal::midiLearnChan = -1; MusEGlobal::midiLearnCtrl = -1; break; - - case AUDIO_SET_SEG_SIZE: - MusEGlobal::segmentSize = msg->ival; - MusEGlobal::sampleRate = msg->iival; -#if 0 //TODO or DELETETHIS ? - audioOutput.MusEGlobal::segmentSizeChanged(); - for (int i = 0; i < mixerGroups; ++i) - audioGroups[i].MusEGlobal::segmentSizeChanged(); - for (iSynthI ii = synthiInstances.begin(); ii != synthiInstances.end();++ii) - (*ii)->MusEGlobal::segmentSizeChanged(); -#endif - break; case SEQM_RESET_DEVICES: for (int i = 0; i < MIDI_PORTS; ++i) @@ -716,8 +761,16 @@ break; case SEQM_PLAY_MIDI_EVENT: { - MidiPlayEvent* ev = (MidiPlayEvent*)(msg->p1); - MusEGlobal::midiPorts[ev->port()].sendEvent(*ev); + const MidiPlayEvent ev = *((MidiPlayEvent*)(msg->p1)); + const int port = ev.port(); + if(port < 0 || port >= MIDI_PORTS) + break; + + // This is the audio thread. Just set directly. + MusEGlobal::midiPorts[port].setHwCtrlState(ev); + // Send to the device. + if(MidiDevice* md = MusEGlobal::midiPorts[port].device()) + md->putEvent(ev, MidiDevice::NotLate); // Record?? } break; @@ -733,46 +786,6 @@ port->setHwCtrlStates(msg->a, msg->b, msg->c, msg->ival); } break; - case SEQM_SCAN_ALSA_MIDI_PORTS: - alsaScanMidiPorts(); - break; - //DELETETHIS 6 - //case MIDI_SHOW_INSTR_GUI: - // MusEGlobal::midiSeq->msgUpdatePollFd(); - // break; - //case MIDI_SHOW_INSTR_NATIVE_GUI: - // MusEGlobal::midiSeq->msgUpdatePollFd(); - // break; - case SEQM_ADD_TEMPO: - case SEQM_REMOVE_TEMPO: - case SEQM_SET_GLOBAL_TEMPO: - case SEQM_SET_TEMPO: - 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(); - syncTime = curTime(); - frameOffset = syncFrame - samplePos; - } - break; - // DELETETHIS 6 - //case SEQM_ADD_TRACK: - //case SEQM_REMOVE_TRACK: - //case SEQM_CHANGE_TRACK: - //case SEQM_ADD_PART: - //case SEQM_REMOVE_PART: - //case SEQM_CHANGE_PART: - case SEQM_SET_TRACK_OUT_CHAN: - case SEQM_SET_TRACK_OUT_PORT: - case SEQM_REMAP_PORT_DRUM_CTL_EVS: - case SEQM_CHANGE_ALL_PORT_DRUM_CTL_EVS: - MusEGlobal::midiSeq->sendMsg(msg); - break; case SEQM_SET_TRACK_AUTO_TYPE: msg->track->setAutomationType(AutomationType(msg->ival)); @@ -780,7 +793,12 @@ case SEQM_IDLE: idle = msg->a; - MusEGlobal::midiSeq->sendMsg(msg); + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->sendMsg(msg); + break; + + case AUDIO_WAIT: + // Do nothing. break; default: @@ -793,25 +811,25 @@ // seek // - called before start play // - initiated from gui +// To be called from audio thread only. //--------------------------------------------------------- void Audio::seek(const Pos& p) { if (_pos == p) { if(MusEGlobal::debugMsg) - printf("Audio::seek already there\n"); + fprintf(stderr, "Audio::seek already at frame:%u\n", p.frame()); return; } if (MusEGlobal::heavyDebugMsg) - printf("Audio::seek frame:%d\n", p.frame()); + fprintf(stderr, "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(); + syncFrame = MusEGlobal::audioDevice->framesAtCycleStart(); #ifdef _JACK_TIMEBASE_DRIVES_MIDI_ unsigned curr_jt_tick; @@ -826,21 +844,18 @@ #endif curTickPos = _pos.tick(); -// ALSA support -#if 1 - MusEGlobal::midiSeq->msgSeek(); // handle stuck notes and set controller for new position -#else - if (curTickPos == 0 && !MusEGlobal::song->record()) // Moved here from MidiSeq::processStop() - MusEGlobal::audio->initDevices(); - for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) - (*i)->handleSeek(); -#endif + // + // Handle stuck notes and set controllers for new position: + // + + seekMidi(); if (state != LOOP2 && !freewheel()) { // We need to force prefetch to update, to ensure the most recent data. // Things can happen to a part before play is pressed - such as part muting, // part moving etc. Without a force, the wrong data was being played. Tim 08/17/08 + // This does not wait. MusEGlobal::audioPrefetch->msgSeek(_pos.frame(), true); } @@ -876,7 +891,7 @@ void Audio::startRolling() { if (MusEGlobal::debugMsg) - printf("startRolling - loopCount=%d, _pos=%d\n", _loopCount, _pos.tick()); + fprintf(stderr, "startRolling - loopCount=%d, _pos=%d\n", _loopCount, _pos.tick()); if(_loopCount == 0) { startRecordPos = _pos; @@ -884,12 +899,9 @@ } if (MusEGlobal::song->record()) { recording = true; - TrackList* tracks = MusEGlobal::song->tracks(); - for (iTrack i = tracks->begin(); i != tracks->end(); ++i) { - if ((*i)->isMidiTrack()) - continue; - if ((*i)->type() == Track::WAVE) - ((WaveTrack*)(*i))->resetMeter(); + WaveTrackList* tracks = MusEGlobal::song->waves(); + for (iWaveTrack i = tracks->begin(); i != tracks->end(); ++i) { + (*i)->resetMeter(); } } state = PLAY; @@ -919,26 +931,29 @@ } } } - - if (MusEGlobal::precountEnableFlag - && MusEGlobal::song->click() - && !MusEGlobal::extSyncFlag.value() - && MusEGlobal::song->record()) { - printf("state = PRECOUNT!\n"); - state = PRECOUNT; - int z, n; - if (MusEGlobal::precountFromMastertrackFlag) - AL::sigmap.timesig(curTickPos, z, n); - else { - z = MusEGlobal::precountSigZ; - n = MusEGlobal::precountSigN; - } - clickno = z * MusEGlobal::preMeasures; - clicksMeasure = z; - ticksBeat = (MusEGlobal::config.division * 4)/n; - } - else { + /// dennis: commented check for pre-count. Something seems to be + /// missing here because the state is not set to PLAY so that the + /// sequencer doesn't start rolling in record mode. +// if (MusEGlobal::precountEnableFlag +// && MusEGlobal::song->click() +// && !MusEGlobal::extSyncFlag.value() +// && MusEGlobal::song->record()) { +// fprintf(stderr, "state = PRECOUNT!\n"); +// state = PRECOUNT; +// int z, n; +// if (MusEGlobal::precountFromMastertrackFlag) +// AL::sigmap.timesig(curTickPos, z, n); +// else { +// z = MusEGlobal::precountSigZ; +// n = MusEGlobal::precountSigN; +// } +// clickno = z * MusEGlobal::preMeasures; +// clicksMeasure = z; +// ticksBeat = (MusEGlobal::config.division * 4)/n; + +// } +// else { // // compute next midi metronome click position // @@ -948,17 +963,17 @@ if (tick) beat += 1; midiClick = AL::sigmap.bar2tick(bar, beat, 0); - } +// } - // reenable sustain + // re-enable sustain for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; + if(!mp->device()) + continue; for (int ch = 0; ch < MIDI_CHANNELS; ++ch) { if (mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { - if(mp->device() != NULL) { - MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 127); - mp->device()->putEvent(ev); - } + const MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 127); + mp->device()->putEvent(ev, MidiDevice::NotLate); } } } @@ -971,26 +986,46 @@ void Audio::stopRolling() { if (MusEGlobal::debugMsg) - printf("Audio::stopRolling state %s\n", audioStates[state]); + fprintf(stderr, "Audio::stopRolling state %s\n", audioStates[state]); state = STOP; - -// ALSA support -#if 1 - MusEGlobal::midiSeq->msgStop(); -#else - MusEGlobal::midiSeq->setExternalPlayState(false); // not playing Moved here from MidiSeq::processStop() - for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) + + // + // Clear midi device notes and stop stuck notes: + // + + // Clear the special sync play state (separate from audio play state). + MusEGlobal::midiSyncContainer.setExternalPlayState(ExtMidiClock::ExternStopped); // Not playing. Moved here from MidiSeq::processStop() + + // Stop the ALSA devices... + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->msgStop(); // FIXME: This waits! + + // Stop any non-ALSA devices... + for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { MidiDevice* md = *id; - md->handleStop(); + const MidiDevice::MidiDeviceType type = md->deviceType(); + // Only for non-ALSA devices. + switch(type) + { + case MidiDevice::ALSA_MIDI: + break; + + case MidiDevice::JACK_MIDI: + case MidiDevice::SYNTH_MIDI: + md->handleStop(); + break; + } } -#endif + + // There may be disk read/write fifo buffers waiting to be emptied. Send one last tick to the disk thread. + if(!freewheel()) + MusEGlobal::audioPrefetch->msgTick(recording, false); // This does not wait. WaveTrackList* tracks = MusEGlobal::song->waves(); for (iWaveTrack i = tracks->begin(); i != tracks->end(); ++i) { - WaveTrack* track = *i; - track->resetMeter(); + (*i)->resetMeter(); } recording = false; endRecordPos = _pos; @@ -1003,48 +1038,41 @@ // execution environment: gui thread //--------------------------------------------------------- -void Audio::recordStop() +void Audio::recordStop(bool restart, Undo* ops) { MusEGlobal::song->processMasterRec(); if (MusEGlobal::debugMsg) - printf("recordStop - startRecordPos=%d\n", MusEGlobal::extSyncFlag.value() ? startExternalRecTick : startRecordPos.tick()); - - MusEGlobal::audio->msgIdle(true); // gain access to all data structures + fprintf(stderr, "recordStop - startRecordPos=%d\n", MusEGlobal::extSyncFlag.value() ? startExternalRecTick : startRecordPos.tick()); - MusEGlobal::song->startUndo(); + Undo loc_ops; + Undo& operations = ops ? (*ops) : loc_ops; + WaveTrackList* wl = MusEGlobal::song->waves(); 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); - // 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). - // Regardless, we are done with the pointer itself. Set to zero so - // MusEGlobal::song->setRecordFlag knows about it... - - track->setRecFile(0); // flush out the old file - MusEGlobal::song->setRecordFlag(track, false); + MusEGlobal::song->cmdAddRecordedWave(track, startRecordPos, restart ? _pos : endRecordPos, operations); + if(!restart) + operations.push_back(UndoOp(UndoOp::SetTrackRecord, track, false, true)); // True = non-undoable. } } MidiTrackList* ml = MusEGlobal::song->midis(); for (iMidiTrack it = ml->begin(); it != ml->end(); ++it) { MidiTrack* mt = *it; - MPEventList* mpel = mt->mpevents(); - EventList* el = mt->events(); //--------------------------------------------------- // resolve NoteOff events, Controller etc. //--------------------------------------------------- // Do SysexMeta. Do loops. - buildMidiEventList(el, mpel, mt, MusEGlobal::config.division, true, true); - MusEGlobal::song->cmdAddRecordedEvents(mt, el, - MusEGlobal::extSyncFlag.value() ? startExternalRecTick : startRecordPos.tick()); - el->clear(); - mpel->clear(); + buildMidiEventList(&mt->events, mt->mpevents, mt, MusEGlobal::config.division, true, true); + MusEGlobal::song->cmdAddRecordedEvents(mt, mt->events, + MusEGlobal::extSyncFlag.value() ? startExternalRecTick : startRecordPos.tick(), + operations); + mt->events.clear(); // ** Driver should not be touching this right now. + mt->mpevents.clear(); // ** Driver should not be touching this right now. } // @@ -1059,19 +1087,33 @@ { MusEGlobal::song->bounceOutput = 0; ao->setRecFile(NULL); // if necessary, this automatically deletes _recFile - ao->setRecordFlag1(false); - msgSetRecord(ao, false); + operations.push_back(UndoOp(UndoOp::SetTrackRecord, ao, false, true)); // True = non-undoable. } } - - MusEGlobal::audio->msgIdle(false); - MusEGlobal::song->endUndo(0); - MusEGlobal::song->setRecord(false); + + // Operate on a local list if none was given. + if(!ops) + MusEGlobal::song->applyOperationGroup(loc_ops); + + if(!restart) + MusEGlobal::song->setRecord(false); } //--------------------------------------------------------- +// framesAtCycleStart +// Frame count at the start of current cycle. +// This is meant to be called from inside process thread only. +//--------------------------------------------------------- + +unsigned Audio::framesAtCycleStart() const +{ + return MusEGlobal::audioDevice->framesAtCycleStart(); +} + +//--------------------------------------------------------- // framesSinceCycleStart // Estimated frames since the last process cycle began +// This can be called from outside process thread. //--------------------------------------------------------- unsigned Audio::framesSinceCycleStart() const @@ -1081,11 +1123,16 @@ if(f >= MusEGlobal::segmentSize) f = MusEGlobal::segmentSize - 1; return f; + + // REMOVE Tim. Or keep? (During midi_engine_fixes.) + // Can't use this since for the Jack driver, jack_frames_since_cycle_start is designed to be called ONLY from inside process. + // return MusEGlobal::audioDevice->framesSinceCycleStart(); } //--------------------------------------------------------- // curFramePos() // Current play position frame. Estimated to single-frame resolution while in play mode. +// This can be called from outside process thread. //--------------------------------------------------------- unsigned Audio::curFramePos() const @@ -1099,26 +1146,49 @@ // Estimated to single-frame resolution. // This is an always-increasing number. Good for timestamps, and // handling them during process when referenced to syncFrame. +// This is meant to be called from threads other than the process thread. //--------------------------------------------------------- unsigned int Audio::curFrame() const { - //return lrint((curTime() - syncTime) * MusEGlobal::sampleRate) + syncFrame; - return framesSinceCycleStart() + syncFrame; + return MusEGlobal::audioDevice->framePos(); + + // REMOVE Tim. Or keep? (During midi_engine_fixes.) + // Can't use this since for the Jack driver, jack_frames_since_cycle_start is designed to be called ONLY from inside process. + //return framesAtCycleStart() + framesSinceCycleStart(); } //--------------------------------------------------------- -// timestamp -// Estimated to single-frame resolution. -// This is an always-increasing number in play mode, but in stop mode -// it is circular (about the cur pos, width = segment size). +// midiQueueTimeStamp +// Converts ticks to frames, and adds a forward frame offset, for the +// purpose of scheduling a midi event to play in the near future. +// If external midi clock sync is off, it uses the tempo map as usual. +// If external sync is on, it uses the clock history list - see the +// CAUTION for extClockHistoryTick2Frame(): There must be at least +// one valid clock in the history list, and don't pass a tick +// greater than or equal to the next tick. //--------------------------------------------------------- -int Audio::timestamp() const - { - int t = curFrame() - frameOffset; - return t; - } +unsigned int Audio::midiQueueTimeStamp(unsigned int tick) const +{ + unsigned int frame = 0; + if(MusEGlobal::extSyncFlag.value()) + { + unsigned int cur_tick = tickPos(); + if(tick < cur_tick) + tick = cur_tick; + frame = MusEGlobal::audio->extClockHistoryTick2Frame(tick - cur_tick) + MusEGlobal::segmentSize; + } + else + { + const unsigned int fr = MusEGlobal::tempomap.tick2frame(tick); + const unsigned int pos_fr = pos().frame(); + frame = (fr < pos_fr) ? 0 : fr - pos_fr; + frame += syncFrame; + } + + return frame; +} //--------------------------------------------------------- // sendMsgToGui diff -Nru muse-2.1.2/muse/audio.h muse-3.0.2+ds1/muse/audio.h --- muse-2.1.2/muse/audio.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/audio.h 2018-01-26 21:59:38.000000000 +0000 @@ -25,6 +25,7 @@ #ifndef __AUDIO_H__ #define __AUDIO_H__ +#include "type_defs.h" #include "thread.h" #include "pos.h" #include "mpevent.h" @@ -52,6 +53,9 @@ class PluginI; class SynthI; class Track; +class Undo; +class PendingOperationList; +class ExtMidiClock; //--------------------------------------------------------- // AudioMsgId @@ -60,37 +64,19 @@ //--------------------------------------------------------- enum { - SEQM_ADD_TRACK, SEQM_REMOVE_TRACK, //SEQM_CHANGE_TRACK, DELETETHIS - SEQM_MOVE_TRACK, - SEQM_ADD_PART, SEQM_REMOVE_PART, SEQM_CHANGE_PART, - SEQM_ADD_EVENT, SEQM_REMOVE_EVENT, SEQM_CHANGE_EVENT, - SEQM_ADD_TEMPO, SEQM_SET_TEMPO, SEQM_REMOVE_TEMPO, SEQM_ADD_SIG, SEQM_REMOVE_SIG, - SEQM_ADD_KEY, SEQM_REMOVE_KEY, - SEQM_SET_GLOBAL_TEMPO, - SEQM_UNDO, SEQM_REDO, + SEQM_REVERT_OPERATION_GROUP, SEQM_EXECUTE_OPERATION_GROUP, + SEQM_EXECUTE_PENDING_OPERATIONS, SEQM_RESET_DEVICES, SEQM_INIT_DEVICES, SEQM_PANIC, SEQM_MIDI_LOCAL_OFF, - SEQM_SET_MIDI_DEVICE, SEQM_PLAY_MIDI_EVENT, SEQM_SET_HW_CTRL_STATE, SEQM_SET_HW_CTRL_STATES, - SEQM_SET_TRACK_OUT_PORT, - SEQM_SET_TRACK_OUT_CHAN, SEQM_SET_TRACK_AUTO_TYPE, - SEQM_REMAP_PORT_DRUM_CTL_EVS, - SEQM_CHANGE_ALL_PORT_DRUM_CTL_EVS, - SEQM_SCAN_ALSA_MIDI_PORTS, SEQM_SET_AUX, SEQM_UPDATE_SOLO_STATES, - //MIDI_SHOW_INSTR_GUI, DELETETHIS - //MIDI_SHOW_INSTR_NATIVE_GUI, DELETETHIS - AUDIO_RECORD, AUDIO_ROUTEADD, AUDIO_ROUTEREMOVE, AUDIO_REMOVEROUTES, - //AUDIO_VOL, AUDIO_PAN, DELETETHIS AUDIO_ADDPLUGIN, - AUDIO_SET_SEG_SIZE, AUDIO_SET_PREFADER, AUDIO_SET_CHANNELS, - //AUDIO_SET_PLUGIN_CTRL_VAL, DELETETHIS AUDIO_SWAP_CONTROLLER_IDX, AUDIO_CLEAR_CONTROLLER_EVENTS, AUDIO_SEEK_PREV_AC_EVENT, @@ -99,10 +85,11 @@ AUDIO_ERASE_RANGE_AC_EVENTS, AUDIO_ADD_AC_EVENT, AUDIO_CHANGE_AC_EVENT, - AUDIO_SET_SOLO, AUDIO_SET_SEND_METRONOME, + AUDIO_SET_SEND_METRONOME, AUDIO_START_MIDI_LEARN, MS_PROCESS, MS_STOP, MS_SET_RTC, MS_UPDATE_POLL_FD, SEQM_IDLE, SEQM_SEEK, + AUDIO_WAIT // Do nothing. Just wait for an audio cycle to pass. }; extern const char* seqMsgList[]; // for debug @@ -132,10 +119,10 @@ char port, channel, ctrl; int a, b, c; Pos pos; + Undo* operations; + PendingOperationList* pendingOps; }; -class AudioOutput; - //--------------------------------------------------------- // Audio //--------------------------------------------------------- @@ -158,9 +145,26 @@ #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 + + // Holds a brief temporary array of sorted FRAMES of clock history, filled from the external clock history fifo. + ExtMidiClock *_extClockHistory; + // Holds the total capacity of the clock history list. + static const int _extClockHistoryCapacity; + // Holds the current size of the temporary clock history array. + int _extClockHistorySize; + // Convert tick to frame using the external clock history list. + // The function takes a tick relative to zero (ie. relative to the first event in a processing batch). + // The returned clock frames occurred during the previous audio cycle(s), so you may want to shift + // the frames forward by one audio segment size for scheduling purposes. + // CAUTION: There must be at least one valid clock in the history, otherwise it returns zero. + // Don't feed this a tick greater than or equal to the next tick, it will simply return the + // very last frame, which is not very useful since that will just bunch the events + // together at the last frame. + unsigned int extClockHistoryTick2Frame(unsigned int tick) const; + unsigned int extClockHistoryFrame2Tick(unsigned int frame) const; //metronome values unsigned midiClick; @@ -170,7 +174,6 @@ double syncTime; // wall clock at last sync point unsigned syncFrame; // corresponding frame no. to syncTime - int frameOffset; // offset to free running hw frame counter State state; @@ -185,11 +188,12 @@ Pos endRecordPos; unsigned startExternalRecTick; unsigned endExternalRecTick; - - AudioOutput* _audioMaster; - AudioOutput* _audioMonitor; + long m_Xruns; + + // Can be called by any thread. void sendLocalOff(); + bool filterEvent(const MidiPlayEvent* event, int type, bool thru); void startRolling(); @@ -200,10 +204,12 @@ void process1(unsigned samplePos, unsigned offset, unsigned samples); void collectEvents(MidiTrack*, unsigned int startTick, unsigned int endTick); + + void seekMidi(); public: Audio(); - virtual ~Audio() { } + virtual ~Audio(); // Access to message pipe (like from gui namespace), otherwise audio would need to depend on gui. int getFromThreadFdw() { return sigFd; } @@ -211,10 +217,14 @@ void process(unsigned frames); bool sync(int state, unsigned frame); + // Called whenever the audio needs to re-sync, such as after any tempo changes. + // To be called from audio thread only. + void reSyncAudio(); void shutdown(); void writeTick(); // transport: + // To be called from audio thread only. bool start(); void stop(bool); void seek(const Pos& pos); @@ -224,6 +234,7 @@ bool isRecording() const { return state == PLAY && recording; } void setRunning(bool val) { _running = val; } bool isRunning() const { return _running; } + bool isIdle() const { return idle; } //----------------------------------------- // message interface @@ -231,58 +242,51 @@ void msgSeek(const Pos&); void msgPlay(bool val); + // For starting the transport in external sync mode. + // Starts the transport immediately, bypassing waiting for transport sync, + // although sync is still handled. + void msgExternalPlay(bool val, bool doRewind = false); + + void msgExecuteOperationGroup(Undo&); // calls exe1, then calls exe2 in audio context, then calls exe3 + void msgRevertOperationGroup(Undo&); // similar. + // Bypass the Undo system and directly execute the pending operations. + // Do a song update with accumulated flags and extra_flags, if doUpdate is true. + void msgExecutePendingOperations(PendingOperationList& operations, bool doUpdate = false, SongChangedFlags_t extraFlags = 0); - void msgRemoveTrack(Track*, bool u = true); void msgRemoveTracks(); - //void msgChangeTrack(Track* oldTrack, Track* newTrack, bool u = true); DELETETHIS - void msgMoveTrack(int idx1, int dx2, bool u = true); - void msgAddPart(Part*, bool u = true); - void msgRemovePart(Part*, bool u = true); - void msgChangePart(Part* oldPart, Part* newPart, bool u = true, bool doCtrls = true, bool doClones = false); - void msgAddEvent(Event&, Part*, bool u = true, bool doCtrls = true, bool doClones = false); - void msgDeleteEvent(Event&, Part*, bool u = true, bool doCtrls = true, bool doClones = false); - void msgChangeEvent(Event&, Event&, Part*, bool u = true, bool doCtrls = true, bool doClones = false); - void msgScanAlsaMidiPorts(); - void msgAddTempo(int tick, int tempo, bool doUndoFlag = true); - void msgSetTempo(int tick, int tempo, bool doUndoFlag = true); - void msgUpdateSoloStates(); + void msgRemoveTrack(Track*, bool u = true); // only does applyOperation + void msgMoveTrack(int idx1, int dx2, bool u = true); // only does applyOperation + void msgAddPart(Part*, bool u = true); // only does applyOperation + void msgRemovePart(Part*, bool u = true); // only does applyOperation + void msgAddEvent(Event&, Part*, bool u = true, bool doCtrls = true, bool doClones = false); // only does applyOperation + void msgDeleteEvent(Event&, Part*, bool u = true, bool doCtrls = true, bool doClones = false); // only does applyOperation + void msgChangeEvent(Event&, Event&, Part*, bool u = true, bool doCtrls = true, bool doClones = false); // only does applyOperation + void msgAddTempo(int tick, int tempo, bool doUndoFlag = true); // only does applyOperation + void msgSetTempo(int tick, int tempo, bool doUndoFlag = true); // FIXME FINDMICHJETZT TODO! + void msgDeleteTempo(int tick, int tempo, bool doUndoFlag = true); // only does applyOperation + void msgUpdateSoloStates(); // TODO and below void msgSetAux(AudioTrack*, int, double); - void msgSetGlobalTempo(int val); - void msgDeleteTempo(int tick, int tempo, bool doUndoFlag = true); + void msgSetGlobalTempo(int val, bool doUndoFlag = true); void msgAddSig(int tick, int z, int n, bool doUndoFlag = true); void msgRemoveSig(int tick, int z, int n, bool doUndoFlag = true); void msgAddKey(int tick, int key, bool doUndoFlag = true); void msgRemoveKey(int tick, int key, bool doUndoFlag = true); - //void msgShowInstrumentGui(MidiInstrument*, bool); DELETETHIS - //void msgShowInstrumentNativeGui(MidiInstrument*, bool); DELETETHIS void msgPanic(); void sendMsg(AudioMsg*); bool sendMessage(AudioMsg* m, bool doUndo); void msgRemoveRoute(Route, Route); - void msgRemoveRoute1(Route, Route); - void msgRemoveRoutes(Route, Route); - void msgRemoveRoutes1(Route, Route); + void msgRemoveRoute1(Route, Route); void msgAddRoute(Route, Route); void msgAddRoute1(Route, Route); void msgAddPlugin(AudioTrack*, int idx, PluginI* plugin); - void msgSetMute(AudioTrack*, bool val); - //void msgSetVolume(AudioTrack*, double val); DELETETHIS - //void msgSetPan(AudioTrack*, double val); DELETETHIS - void msgAddSynthI(SynthI* synth); - void msgRemoveSynthI(SynthI* synth); - void msgSetSegSize(int, int); void msgSetPrefader(AudioTrack*, int); void msgSetChannels(AudioTrack*, int); - void msgSetOff(AudioTrack*, bool); - void msgSetRecord(AudioTrack*, bool); - void msgUndo(); - void msgRedo(); void msgLocalOff(); void msgInitMidiDevices(bool force = true); void msgResetMidiDevices(); void msgIdle(bool); + void msgAudioWait(); void msgBounce(); - //void msgSetPluginCtrlVal(AudioTrack*, int /*param*/, double /*val*/); DELETETHIS void msgSwapControllerIDX(AudioTrack*, int, int); void msgClearControllerEvents(AudioTrack*, int); void msgSeekPrevACEvent(AudioTrack*, int); @@ -291,19 +295,13 @@ void msgEraseRangeACEvents(AudioTrack*, int, int, int); void msgAddACEvent(AudioTrack*, int, int, double); void msgChangeACEvent(AudioTrack* node, int acid, int frame, int newFrame, double val); - void msgSetSolo(Track*, bool); void msgSetHwCtrlState(MidiPort*, int, int, int); void msgSetHwCtrlStates(MidiPort*, int, int, int, int); - void msgSetTrackOutChannel(MidiTrack*, int); - void msgSetTrackOutPort(MidiTrack*, int); void msgSetTrackAutomationType(Track*, int); - void msgRemapPortDrumCtlEvents(int, int, int, int); - void msgChangeAllPortDrumCtrlEvents(bool, bool); void msgSetSendMetronome(AudioTrack*, bool); void msgStartMidiLearn(); - void msgPlayMidiEvent(const MidiPlayEvent* event); - void rescanAlsaPorts(); + void msgSetMidiDevice(MidiPort* port, MidiDevice* device); void midiPortsChanged(); @@ -320,24 +318,43 @@ unsigned tickPos() const { return curTickPos; } unsigned nextTick() const { return nextTickPos; } - int timestamp() const; void processMidi(); - unsigned framesSinceCycleStart() const; + // Extrapolates current play frame on syncTime/syncFrame + // Estimated to single-frame resolution. + // This is an always-increasing number. Good for timestamps, and + // handling them during process when referenced to syncFrame. + // This is meant to be called from threads other than the process thread. unsigned curFrame() const; - unsigned curSyncFrame() const { return syncFrame; } + // Current play position frame. Estimated to single-frame resolution while in play mode. + // This can be called from outside process thread. unsigned curFramePos() const; - void recordStop(); + // This is meant to be called from inside process thread only. + unsigned framesAtCycleStart() const; + // Same as framesAtCycleStart(), but can be called from outside process thread. + unsigned curSyncFrame() const { return syncFrame; } + // This can be called from outside process thread. + unsigned framesSinceCycleStart() const; + // Converts ticks to frames, and adds a forward frame offset, for the + // purpose of scheduling a midi event to play in the near future. + // If external midi clock sync is off, it uses the tempo map as usual. + // If external sync is on, it uses the clock history list - see the + // CAUTION for extClockHistoryTick2Frame(): There must be at least + // one valid clock in the history list, and don't pass a tick + // greater than or equal to the next tick. + unsigned int midiQueueTimeStamp(unsigned int tick) const; + + void recordStop(bool restart = false, Undo* operations = NULL); bool freewheel() const { return _freewheel; } void setFreewheel(bool val); - int getFrameOffset() const { return frameOffset; } void initDevices(bool force = true); - AudioOutput* audioMaster() const { return _audioMaster; } - AudioOutput* audioMonitor() const { return _audioMonitor; } - void setMaster(AudioOutput* track) { _audioMaster = track; } - void setMonitor(AudioOutput* track) { _audioMonitor = track; } void sendMsgToGui(char c); bool bounce() const { return _bounce; } + + long getXruns() { return m_Xruns; } + void resetXruns() { m_Xruns = 0; } + void incXruns() { m_Xruns++; } + }; extern int processAudio(unsigned long, void*); diff -Nru muse-2.1.2/muse/audioprefetch.cpp muse-3.0.2+ds1/muse/audioprefetch.cpp --- muse-2.1.2/muse/audioprefetch.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/audioprefetch.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -55,6 +55,8 @@ struct PrefetchMsg : public ThreadMsg { int pos; + bool _isPlayTick; + bool _isRecTick; }; //--------------------------------------------------------- @@ -83,7 +85,7 @@ // start //--------------------------------------------------------- -void AudioPrefetch::start(int priority) +void AudioPrefetch::start(int priority, void *) { clearPollFd(); addPollFd(toThreadFdr, POLLIN, MusECore::readMsgP, this, 0); @@ -107,24 +109,35 @@ const PrefetchMsg* msg = (PrefetchMsg*)m; switch(msg->id) { case PREFETCH_TICK: - if (MusEGlobal::audio->isRecording()) + if(msg->_isRecTick) // Was the tick generated when audio record was on? + { + #ifdef AUDIOPREFETCH_DEBUG + fprintf(stderr, "AudioPrefetch::processMsg1: PREFETCH_TICK: isRecTick\n"); + #endif MusEGlobal::audio->writeTick(); + } // Indicate do not seek file before each read. - prefetch(false); + if(msg->_isPlayTick) // Was the tick generated when audio playback was on? + { + #ifdef AUDIOPREFETCH_DEBUG + fprintf(stderr, "AudioPrefetch::processMsg1: PREFETCH_TICK: isPlayTick\n"); + #endif + prefetch(false); + } seekPos = ~0; // invalidate cached last seek position break; case PREFETCH_SEEK: #ifdef AUDIOPREFETCH_DEBUG - printf("AudioPrefetch::processMsg1 PREFETCH_SEEK msg->pos:%d\n", msg->pos); + fprintf(stderr, "AudioPrefetch::processMsg1 PREFETCH_SEEK msg->pos:%d\n", msg->pos); #endif // process seek in background seek(msg->pos); break; default: - printf("AudioPrefetch::processMsg1: unknown message\n"); + fprintf(stderr, "AudioPrefetch::processMsg1: unknown message\n"); } } @@ -132,13 +145,15 @@ // msgTick //--------------------------------------------------------- -void AudioPrefetch::msgTick() +void AudioPrefetch::msgTick(bool isRecTick, bool isPlayTick) { PrefetchMsg msg; msg.id = PREFETCH_TICK; - msg.pos = 0; // seems to be unused, was uninitalized. + msg.pos = 0; // seems to be unused, was uninitialized. + msg._isRecTick = isRecTick; + msg._isPlayTick = isPlayTick; while (sendMsg1(&msg, sizeof(msg))) { - printf("AudioPrefetch::msgTick(): send failed!\n"); + fprintf(stderr, "AudioPrefetch::msgTick(): send failed!\n"); } } @@ -155,14 +170,14 @@ ++seekCount; #ifdef AUDIOPREFETCH_DEBUG - printf("AudioPrefetch::msgSeek samplePos:%u force:%d seekCount:%d\n", samplePos, force, seekCount); + fprintf(stderr, "AudioPrefetch::msgSeek samplePos:%u force:%d seekCount:%d\n", samplePos, force, seekCount); #endif PrefetchMsg msg; msg.id = PREFETCH_SEEK; msg.pos = samplePos; while (sendMsg1(&msg, sizeof(msg))) { - printf("AudioPrefetch::msgSeek::sleep(1)\n"); + fprintf(stderr, "AudioPrefetch::msgSeek::sleep(1)\n"); sleep(1); } } @@ -174,7 +189,7 @@ void AudioPrefetch::prefetch(bool doSeek) { if (writePos == ~0U) { - printf("AudioPrefetch::prefetch: invalid write position\n"); + fprintf(stderr, "AudioPrefetch::prefetch: invalid write position\n"); return; } if (MusEGlobal::song->loop() && !MusEGlobal::audio->bounce() && !MusEGlobal::extSyncFlag.value()) { @@ -200,7 +215,8 @@ if (track->prefetchFifo()->getWriteBuffer(ch, MusEGlobal::segmentSize, bp, writePos)) continue; - track->fetchData(writePos, MusEGlobal::segmentSize, bp, doSeek); + // True = do overwrite. + track->fetchData(writePos, MusEGlobal::segmentSize, bp, doSeek, true); } writePos += MusEGlobal::segmentSize; @@ -213,7 +229,7 @@ void AudioPrefetch::seek(unsigned seekTo) { #ifdef AUDIOPREFETCH_DEBUG - printf("AudioPrefetch::seek to:%u seekCount:%d\n", seekTo, seekCount); + fprintf(stderr, "AudioPrefetch::seek to:%u seekCount:%d\n", seekTo, seekCount); #endif // Speedup: More than one seek message pending? diff -Nru muse-2.1.2/muse/audioprefetch.h muse-3.0.2+ds1/muse/audioprefetch.h --- muse-2.1.2/muse/audioprefetch.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/audioprefetch.h 2017-12-04 21:01:18.000000000 +0000 @@ -46,9 +46,9 @@ AudioPrefetch(const char* name); ~AudioPrefetch(); - virtual void start(int); + virtual void start(int, void* pty = NULL); - void msgTick(); + void msgTick(bool isRecTick, bool isPlayTick); void msgSeek(unsigned samplePos, bool force=false); bool seekDone() const { return seekCount == 0; } diff -Nru muse-2.1.2/muse/audiotrack.cpp muse-3.0.2+ds1/muse/audiotrack.cpp --- muse-2.1.2/muse/audiotrack.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/audiotrack.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: audiotrack.cpp,v 1.14.2.21 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2013 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 @@ -43,17 +44,18 @@ #include "app.h" #include "controlfifo.h" #include "fastlog.h" +#include "gconfig.h" namespace MusECore { -bool AudioAux::_isVisible=false; -bool AudioInput::_isVisible=false; -bool AudioOutput::_isVisible=false; -bool AudioGroup::_isVisible =false; +bool AudioAux::_isVisible=true; +bool AudioInput::_isVisible=true; +bool AudioOutput::_isVisible=true; +bool AudioGroup::_isVisible =true; bool WaveTrack::_isVisible=true; // DELETETHIS 40. this caching stuff seems to be not used any more -// By T356. For caching jack in/out routing names BEFORE file save. +// By T356. For caching jack in/out routing names BEFORE file save. // Jack often shuts down during file save, causing the routes to be lost in the file. // cacheJackRouteNames() is ONLY called from MusE::save() in app.cpp // Update: Not required any more because the real problem was Jack RT priority, which has been fixed. @@ -67,33 +69,172 @@ { jackRouteNameCache.clear(); const InputList* il = MusEGlobal::song->inputs(); - for(ciAudioInput iai = il->begin(); iai != il->end(); ++iai) + for(ciAudioInput iai = il->begin(); iai != il->end(); ++iai) { const RouteList* rl = (*iai)->inRoutes(); if(!rl->empty()) { jackRouteNameMap rm = jackRouteNameMap(); - for(ciRoute r = rl->begin(); r != rl->end(); ++r) + for(ciRoute r = rl->begin(); r != rl->end(); ++r) rm.insert(std::pair(r->channel, r->name())); jackRouteNameCache.insert(std::pair(*iai, rm)); - } + } } const OutputList* ol = MusEGlobal::song->outputs(); - for(ciAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) + for(ciAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) { const RouteList* rl = (*iao)->outRoutes(); if(!rl->empty()) { jackRouteNameMap rm = jackRouteNameMap(); - for(ciRoute r = rl->begin(); r != rl->end(); ++r) + for(ciRoute r = rl->begin(); r != rl->end(); ++r) rm.insert(std::pair(r->channel, r->name())); jackRouteNameCache.insert(std::pair(*iao, rm)); - } + } } } */ //--------------------------------------------------------- +// init_buffers +//--------------------------------------------------------- + +void AudioTrack::initBuffers() +{ + int chans = _totalOutChannels; + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + if(chans < MAX_CHANNELS) + chans = MAX_CHANNELS; + if(!outBuffers) + { + outBuffers = new float*[chans]; + for(int i = 0; i < chans; ++i) + { + int rv = posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + } + } + for(int i = 0; i < chans; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + outBuffers[i][q] = MusEGlobal::denormalBias; + } + else + memset(outBuffers[i], 0, sizeof(float) * MusEGlobal::segmentSize); + } + + if(!outBuffersExtraMix) + { + outBuffersExtraMix = new float*[MAX_CHANNELS]; + for(int i = 0; i < MAX_CHANNELS; ++i) + { + int rv = posix_memalign((void**)&outBuffersExtraMix[i], 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign outBuffersMonoMix returned error:%d. Aborting!\n", rv); + abort(); + } + } + } + for(int i = 0; i < MAX_CHANNELS; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + outBuffersExtraMix[i][q] = MusEGlobal::denormalBias; + } + else + memset(outBuffersExtraMix[i], 0, sizeof(float) * MusEGlobal::segmentSize); + } + + if(!_dataBuffers) + { + _dataBuffers = new float*[_totalOutChannels]; + for(int i = 0; i < _totalOutChannels; ++i) + { + int rv = posix_memalign((void**)&_dataBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign _dataBuffers returned error:%d. Aborting!\n", rv); + abort(); + } + } + } + for(int i = 0; i < _totalOutChannels; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _dataBuffers[i][q] = MusEGlobal::denormalBias; + } + else + memset(_dataBuffers[i], 0, sizeof(float) * MusEGlobal::segmentSize); + } + + if(!audioInSilenceBuf) + { + int rv = posix_memalign((void**)&audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + audioInSilenceBuf[q] = MusEGlobal::denormalBias; + } + else + memset(audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + } + + if(!audioOutDummyBuf) + { + int rv = posix_memalign((void**)&audioOutDummyBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + audioOutDummyBuf[q] = MusEGlobal::denormalBias; + } + else + memset(audioOutDummyBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + } + + if(!_controls && _controlPorts != 0) + { + _controls = new Port[_controlPorts]; + ciCtrlList icl = _controller.begin(); + for(unsigned long k = 0; k < _controlPorts; ++k) + { + double val = 0.0; + if(icl != _controller.end()) + { + // Since the list is sorted by id, if no match is found just let k catch up to the id. + if((unsigned long)icl->second->id() == k) + { + val = icl->second->getDefault(); + ++icl; + } + } + _controls[k].idx = k; + _controls[k].dval = val; + _controls[k].enCtrl = true; + } + } +} + +//--------------------------------------------------------- // AudioTrack //--------------------------------------------------------- @@ -106,55 +247,69 @@ _prefader = false; _efxPipe = new Pipeline(); recFileNumber = 1; - //_recFile = 0; //unneeded, _recFile's ctor does this _channels = 0; _automationType = AUTO_OFF; setChannels(2); + addController(new CtrlList(AC_VOLUME,"Volume",0.001,3.163 /* roughly 10 db */, VAL_LOG)); addController(new CtrlList(AC_PAN, "Pan", -1.0, 1.0, VAL_LINEAR)); - addController(new CtrlList(AC_MUTE,"Mute",0.0,1.0, VAL_LINEAR, true /*dont show in arranger */)); - - // for a lot of considerations and failures, see revision 1402 or earlier (flo) + addController(new CtrlList(AC_MUTE,"Mute",0.0,1.0, VAL_LINEAR, true /*don't show in arranger */)); + _controlPorts = 3; + + _curVolume = 0.0; + _curVol1 = 0.0; + _curVol2 = 0.0; + + _controls = 0; + outBuffers = 0; + outBuffersExtraMix = 0; + audioInSilenceBuf = 0; + audioOutDummyBuf = 0; + _dataBuffers = 0; + _totalOutChannels = MAX_CHANNELS; - outBuffers = new float*[_totalOutChannels]; - for (int i = 0; i < _totalOutChannels; ++i) - { - int rv = posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); - if(rv != 0) - { - fprintf(stderr, "ERROR: AudioTrack ctor: posix_memalign returned error:%d. Aborting!\n", rv); - abort(); - } - } - + // This is only set by multi-channel syntis... _totalInChannels = 0; - - bufferPos = INT_MAX; - + + initBuffers(); + setVolume(1.0); _gain = 1.0; } AudioTrack::AudioTrack(const AudioTrack& t, int flags) - : Track(t, flags) + : Track(t, flags) { _processed = false; _haveData = false; _efxPipe = new Pipeline(); // Start off with a new pipeline. recFileNumber = 1; + addController(new CtrlList(AC_VOLUME,"Volume",0.001,3.163 /* roughly 10 db */, VAL_LOG)); + addController(new CtrlList(AC_PAN, "Pan", -1.0, 1.0, VAL_LINEAR)); + addController(new CtrlList(AC_MUTE,"Mute",0.0,1.0, VAL_LINEAR, true /*don't show in arranger */)); + _controlPorts = 3; + + _curVolume = 0.0; + _curVol1 = 0.0; + _curVol2 = 0.0; + // Don't allocate outBuffers here. Let internal_assign() call setTotalOutChannels to set them up. + _controls = 0; outBuffers = 0; + outBuffersExtraMix = 0; + audioInSilenceBuf = 0; + audioOutDummyBuf = 0; + _dataBuffers = 0; + _totalOutChannels = 0; + // This is only set by multi-channel syntis... _totalInChannels = 0; - - bufferPos = INT_MAX; - + _recFile = NULL; - - _gain = t._gain; + internal_assign(t, flags | ASSIGN_PROPERTIES); } @@ -162,66 +317,181 @@ { if(t.isMidiTrack()) return; - + const AudioTrack& at = (const AudioTrack&)t; - + if(flags & ASSIGN_PROPERTIES) { _sendMetronome = at._sendMetronome; _prefader = at._prefader; _auxSend = at._auxSend; _automationType = at._automationType; - + _gain = at._gain; + if(!(flags & ASSIGN_STD_CTRLS)) { - _controller.clearDelete(); - for(ciCtrlList icl = at._controller.begin(); icl != at._controller.end(); ++icl) + // Copy the standard controller block... + ciCtrlList icl = at._controller.begin(); + ciCtrlList icl_this = _controller.begin(); + ciCtrlList icl_end = at._controller.lower_bound(AC_PLUGIN_CTL_BASE); + ciCtrlList icl_this_end = _controller.lower_bound(AC_PLUGIN_CTL_BASE); + int id, id_this; + CtrlList* cl, *cl_this; + while(icl != icl_end && icl_this != icl_this_end) { - CtrlList* cl = icl->second; - // Copy all built-in controllers (id below AC_PLUGIN_CTL_BASE), but not plugin controllers. - if(cl->id() >= AC_PLUGIN_CTL_BASE) - continue; - CtrlList* new_cl = new CtrlList(); - new_cl->assign(*cl, CtrlList::ASSIGN_PROPERTIES); // Don't copy values. - addController(new_cl); + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties but not values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES); + ++icl; + ++icl_this; + } + } + + // Copy the special synth controller block... + const int synth_id = (int)genACnum(MAX_PLUGINS, 0); // The beginning of the special synth controller block. + const int synth_id_end = synth_id + AC_PLUGIN_CTL_BASE; // The end of the special block. + icl = at._controller.lower_bound(synth_id); + icl_this = _controller.lower_bound(synth_id); + icl_end = at._controller.lower_bound(synth_id_end); + icl_this_end = _controller.lower_bound(synth_id_end); + while(icl != icl_end && icl_this != icl_this_end) + { + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties but not values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES); + ++icl; + ++icl_this; + } } } - - // This will set up or reallocate the outBuffers. + + // This will set up or reallocate the outBuffers. _controlPorts must be valid by now. setTotalOutChannels(at._totalOutChannels); - + // This is only set by multi-channel syntis... setTotalInChannels(at._totalInChannels); - + + // FIXME: setChannels also called in setTotalOutChannels above, causing redundant efxpipe setChannels. setChannels(at.channels()); // Set track channels (max 2). - } - + + unsigned long cp = _controlPorts; + if(at._controlPorts < cp) + cp = at._controlPorts; + for(unsigned long k = 0; k < cp; ++k) + _controls[k] = at._controls[k]; // Assign the structures. + } + if(flags & ASSIGN_PLUGINS) { delete _efxPipe; - _efxPipe = new Pipeline(*(at._efxPipe)); // Make copies of the plugins. - } - + _efxPipe = new Pipeline(*(at._efxPipe), this); // Make copies of the plugins. + } + if(flags & (ASSIGN_STD_CTRLS | ASSIGN_PLUGIN_CTRLS)) { - _controller.clearDelete(); - for(ciCtrlList icl = at._controller.begin(); icl != at._controller.end(); ++icl) + const int synth_id = (int)genACnum(MAX_PLUGINS, 0); // The beginning of the special synth controller block. + const int synth_id_end = synth_id + AC_PLUGIN_CTL_BASE; // The end of the special block. + ciCtrlList icl, icl_end, icl_this, icl_this_end; + int id, id_this; + CtrlList* cl, *cl_this; + + if(flags & ASSIGN_STD_CTRLS) + { + // Copy the standard controller block... + icl = at._controller.begin(); + icl_this = _controller.begin(); + icl_end = at._controller.lower_bound(AC_PLUGIN_CTL_BASE); + icl_this_end = _controller.lower_bound(AC_PLUGIN_CTL_BASE); + while(icl != icl_end && icl_this != icl_this_end) + { + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties and values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); + ++icl; + ++icl_this; + } + } + + // Copy the special synth controller block... + icl = at._controller.lower_bound(synth_id); + icl_this = _controller.lower_bound(synth_id); + icl_end = at._controller.lower_bound(synth_id_end); + icl_this_end = _controller.lower_bound(synth_id_end); + while(icl != icl_end && icl_this != icl_this_end) + { + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties and values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); + ++icl; + ++icl_this; + } + } + } + + if(flags & ASSIGN_PLUGIN_CTRLS) { - CtrlList* cl = icl->second; - // Discern between built-in controllers (id below AC_PLUGIN_CTL_BASE), and plugin controllers. - if(cl->id() >= AC_PLUGIN_CTL_BASE) + // Copy all plugin controller blocks... + icl = at._controller.lower_bound(AC_PLUGIN_CTL_BASE); + icl_this = _controller.lower_bound(AC_PLUGIN_CTL_BASE); + icl_end = at._controller.lower_bound(synth_id); + icl_this_end = _controller.lower_bound(synth_id); + while(icl != icl_end && icl_this != icl_this_end) { - if(!(flags & ASSIGN_PLUGIN_CTRLS)) - continue; + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties and values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); + ++icl; + ++icl_this; + } } - else if(!(flags & ASSIGN_STD_CTRLS)) - continue; - - CtrlList* new_cl = new CtrlList(*cl); // Let copy constructor handle the rest. Copy values. - addController(new_cl); } } - + if(flags & ASSIGN_ROUTES) { for(ciRoute ir = at._inRoutes.begin(); ir != at._inRoutes.end(); ++ir) @@ -232,7 +502,7 @@ // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. _inRoutes.push_back(*ir); } - + for(ciRoute ir = at._outRoutes.begin(); ir != at._outRoutes.end(); ++ir) { // Defer all Jack routes to Audio Input and Output copy constructors or assign ! @@ -241,7 +511,7 @@ // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. _outRoutes.push_back(*ir); } - } + } else if(flags & ASSIGN_DEFAULT_ROUTES) { // @@ -254,19 +524,20 @@ case Track::WAVE: case Track::AUDIO_AUX: // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. - _outRoutes.push_back(Route(ao, -1)); + _outRoutes.push_back(Route(ao)); break; // It should actually never get here now, but just in case. case Track::AUDIO_SOFTSYNTH: // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. - _outRoutes.push_back(Route(ao, 0, channels())); + // Add an Omni route. + _outRoutes.push_back(Route(ao)); break; default: break; } } - } -} + } +} void AudioTrack::assign(const Track& t, int flags) { @@ -277,21 +548,50 @@ AudioTrack::~AudioTrack() { delete _efxPipe; - + + if(audioInSilenceBuf) + free(audioInSilenceBuf); + + if(audioOutDummyBuf) + free(audioOutDummyBuf); + + if(_dataBuffers) + { + for(int i = 0; i < _totalOutChannels; ++i) + { + if(_dataBuffers[i]) + free(_dataBuffers[i]); + } + delete[] _dataBuffers; + } + + if(outBuffersExtraMix) + { + for(int i = 0; i < MAX_CHANNELS; ++i) + { + if(outBuffersExtraMix[i]) + free(outBuffersExtraMix[i]); + } + delete[] outBuffersExtraMix; + } + int chans = _totalOutChannels; - // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. if(chans < MAX_CHANNELS) chans = MAX_CHANNELS; if(outBuffers) { - for(int i = 0; i < chans; ++i) + for(int i = 0; i < chans; ++i) { if(outBuffers[i]) free(outBuffers[i]); } delete[] outBuffers; } - + + if(_controls) + delete[] _controls; + _controller.clearDelete(); } @@ -331,16 +631,16 @@ void AudioTrack::addPlugin(PluginI* plugin, int idx) { - if (plugin == 0) + if (plugin == 0) { PluginI* oldPlugin = (*_efxPipe)[idx]; - if (oldPlugin) + if (oldPlugin) { oldPlugin->setID(-1); oldPlugin->setTrack(0); - + int controller = oldPlugin->parameters(); - for (int i = 0; i < controller; ++i) + for (int i = 0; i < controller; ++i) { int id = genACnum(idx, i); removeController(id); @@ -348,13 +648,22 @@ } } efxPipe()->insert(plugin, idx); - if (plugin) + setupPlugin(plugin, idx); +} + +//--------------------------------------------------------- +// setupPlugin +//--------------------------------------------------------- + +void AudioTrack::setupPlugin(PluginI* plugin, int idx) +{ + if (plugin) { plugin->setID(idx); plugin->setTrack(this); - + int controller = plugin->parameters(); - for (int i = 0; i < controller; ++i) + for (int i = 0; i < controller; ++i) { int id = genACnum(idx, i); const char* name = plugin->paramName(i); @@ -385,6 +694,17 @@ } //--------------------------------------------------------- +// addAuxSendOperation +//--------------------------------------------------------- + +void AudioTrack::addAuxSendOperation(int n, PendingOperationList& ops) + { + int nn = _auxSend.size(); + for (int i = nn; i < n; ++i) + ops.add(PendingOperationItem(&_auxSend, 0.0, PendingOperationItem::AddAuxSendValue)); + } + +//--------------------------------------------------------- // addController //--------------------------------------------------------- @@ -400,7 +720,7 @@ void AudioTrack::removeController(int id) { AudioMidiCtrlStructMap amcs; - _controller.midiControls()->find_audio_ctrl_structs(id, &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); @@ -419,18 +739,18 @@ { 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; CtrlVal cv(0, 0.0); - - for(ciCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl) + + for(ciCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl) { cl = icl->second; i = cl->id() & AC_PLUGIN_CTL_ID_MASK; @@ -448,7 +768,7 @@ newcl->setDefault(cl->getDefault()); newcl->setColor(cl->color()); newcl->setVisible(cl->isVisible()); - for(iCtrl ic = cl->begin(); ic != cl->end(); ++ic) + for(iCtrl ic = cl->begin(); ic != cl->end(); ++ic) { cv = ic->second; newcl->insert(std::pair(cv.frame, cv)); @@ -458,22 +778,22 @@ else { newcl = new CtrlList(); - *newcl = *cl; + *newcl = *cl; tmpcll.insert(std::pair(newcl->id(), newcl)); - } + } } - - for(iCtrlList ci = _controller.begin(); ci != _controller.end(); ++ci) + + for(iCtrlList ci = _controller.begin(); ci != _controller.end(); ++ci) delete (*ci).second; - + _controller.clear(); - - for(ciCtrlList icl = tmpcll.begin(); icl != tmpcll.end(); ++icl) + + for(ciCtrlList icl = tmpcll.begin(); icl != tmpcll.end(); ++icl) { newcl = icl->second; _controller.insert(std::pair(newcl->id(), newcl)); - } - + } + // Remap midi to audio controls... MidiAudioCtrlMap* macm = _controller.midiControls(); for(iMidiAudioCtrlMap imacm = macm->begin(); imacm != macm->end(); ++imacm) @@ -495,11 +815,11 @@ // setAutomationType //--------------------------------------------------------- -void AudioTrack::setAutomationType(AutomationType t) -{ +void AudioTrack::setAutomationType(AutomationType t) +{ // Clear pressed and touched and rec event list. clearRecAutomation(true); - + // Now set the type. _automationType = t; } @@ -508,92 +828,102 @@ // processAutomationEvents //--------------------------------------------------------- -void AudioTrack::processAutomationEvents() -{ - if (_automationType != AUTO_TOUCH && _automationType != AUTO_WRITE) - return; - - for (iCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl) +void AudioTrack::processAutomationEvents(Undo* operations) +{ + if(_automationType != AUTO_TOUCH && _automationType != AUTO_WRITE) + return; + + // Use either the supplied operations list or a local one. + Undo ops; + Undo& opsr = operations ? (*operations) : ops; + + for(ciCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl) { CtrlList* cl = icl->second; + CtrlList& clr = *icl->second; int id = cl->id(); - + + // Were there any recorded events for this controller? + bool do_it = false; + for(ciCtrlRec icr = _recEvents.begin(); icr != _recEvents.end(); ++icr) + { + if(icr->id == id) + { + do_it = true; + break; + } + } + if(!do_it) + continue; + + // The Undo system will take 'ownership' of these and delete them at the appropriate time. + CtrlList* erased_list_items = new CtrlList(clr, CtrlList::ASSIGN_PROPERTIES); + CtrlList* added_list_items = new CtrlList(clr, CtrlList::ASSIGN_PROPERTIES); + // Remove old events from record region. - if (_automationType == AUTO_WRITE) + if(_automationType == AUTO_WRITE) { int start = MusEGlobal::audio->getStartRecordPos().frame(); int end = MusEGlobal::audio->getEndRecordPos().frame(); iCtrl s = cl->lower_bound(start); iCtrl e = cl->lower_bound(end); - - // Erase old events only if there were recorded events. - for(iCtrlRec icr = _recEvents.begin(); icr != _recEvents.end(); ++icr) - { - if(icr->id == id) // && icr->type == ARVT_VAL && icr->frame >= s->frame && icr->frame <= e->frame) - { - cl->erase(s, e); - break; - } - } + erased_list_items->insert(s, e); } - else + else { // type AUTO_TOUCH - for (iCtrlRec icr = _recEvents.begin(); icr != _recEvents.end(); ++icr) + for(ciCtrlRec icr = _recEvents.begin(); icr != _recEvents.end(); ++icr) { // Don't bother looking for start, it's OK, just take the first one. // Needed for mousewheel and paging etc. - if (icr->id == id) + if(icr->id != id) + continue; + + int start = icr->frame; + + if(icr == _recEvents.end()) + { + int end = MusEGlobal::audio->getEndRecordPos().frame(); + iCtrl s = cl->lower_bound(start); + iCtrl e = cl->lower_bound(end); + erased_list_items->insert(s, e); + break; + } + + ciCtrlRec icrlast = icr; + ++icr; + for(; ; ++icr) { - int start = icr->frame; - if(icr == _recEvents.end()) { - int end = MusEGlobal::audio->getEndRecordPos().frame(); + int end = icrlast->frame; iCtrl s = cl->lower_bound(start); iCtrl e = cl->lower_bound(end); - cl->erase(s, e); + erased_list_items->insert(s, e); break; } - - iCtrlRec icrlast = icr; - ++icr; - for(; ; ++icr) + + if(icr->id == id && icr->type == ARVT_STOP) { - if(icr == _recEvents.end()) - { - int end = icrlast->frame; - iCtrl s = cl->lower_bound(start); - iCtrl e = cl->lower_bound(end); - cl->erase(s, e); - break; - } - - if(icr->id == id && icr->type == ARVT_STOP) - { - int end = icr->frame; - - iCtrl s = cl->lower_bound(start); - iCtrl e = cl->lower_bound(end); - - cl->erase(s, e); - - break; - } - - if(icr->id == id) - icrlast = icr; + int end = icr->frame; + iCtrl s = cl->lower_bound(start); + iCtrl e = cl->lower_bound(end); + erased_list_items->insert(s, e); + break; } - if (icr == _recEvents.end()) - break; + + if(icr->id == id) + icrlast = icr; } + if(icr == _recEvents.end()) + break; } } - + // Extract all recorded events for controller "id" - // from CtrlRecList and put into cl. - for (iCtrlRec icr = _recEvents.begin(); icr != _recEvents.end(); ++icr) + // from CtrlRecList and put into new_list. + for(ciCtrlRec icr = _recEvents.begin(); icr != _recEvents.end(); ++icr) { - if (icr->id == id) + if(icr->id == id) { // Must optimize these types otherwise multiple vertices appear on flat straight lines in the graphs. CtrlValueType vtype = cl->valueType(); @@ -604,28 +934,39 @@ --icl_prev; if(icl_prev->second.val == icr->val) continue; - } + } // Now add the value. - cl->add(icr->frame, icr->val); + added_list_items->add(icr->frame, icr->val); } } + + if(erased_list_items->empty() && added_list_items->empty()) + { + delete erased_list_items; + delete added_list_items; + } + else + opsr.push_back(UndoOp(UndoOp::ModifyAudioCtrlValList, &_controller, erased_list_items, added_list_items)); } - + // Done with the recorded automation event list. Clear it. _recEvents.clear(); + + if(!operations) + MusEGlobal::song->applyOperationGroup(ops); } //--------------------------------------------------------- // setControllerMode //--------------------------------------------------------- -void AudioTrack::setControllerMode(int ctlID, CtrlList::Mode m) +void AudioTrack::setControllerMode(int ctlID, CtrlList::Mode m) { ciCtrlList cl = _controller.find(ctlID); if(cl == _controller.end()) return; - - cl->second->setMode(m); + + cl->second->setMode(m); } //--------------------------------------------------------- @@ -637,10 +978,10 @@ ciCtrlList icl = _controller.find(id); if(icl == _controller.end()) return; - + CtrlList* cl = icl->second; cl->clear(); - return; + return; } //--------------------------------------------------------- @@ -650,18 +991,19 @@ void AudioTrack::seekPrevACEvent(int id) { ciCtrlList icl = _controller.find(id); - if(icl == _controller.end()) + if(icl == _controller.end()){ return; - + } + CtrlList* cl = icl->second; if(cl->empty()) return; - - iCtrl s = cl->lower_bound(MusEGlobal::audio->pos().frame()); + + iCtrl s = cl->lower_bound(MusEGlobal::audio->pos().frame()); if(s != cl->begin()) --s; - - MusEGlobal::song->setPos(Song::CPOS, Pos(s->second.frame, false), false, true, false); + + MusEGlobal::song->setPos(Song::CPOS, Pos(s->second.frame, false), false, true, false); return; } @@ -672,22 +1014,23 @@ void AudioTrack::seekNextACEvent(int id) { ciCtrlList icl = _controller.find(id); - if(icl == _controller.end()) + if(icl == _controller.end()) { return; - + } + CtrlList* cl = icl->second; if(cl->empty()) return; - - iCtrl s = cl->upper_bound(MusEGlobal::audio->pos().frame()); - + + iCtrl s = cl->upper_bound(MusEGlobal::audio->pos().frame()); + if(s == cl->end()) { --s; } - - MusEGlobal::song->setPos(Song::CPOS, Pos(s->second.frame, false), false, true, false); - return; + + MusEGlobal::song->setPos(Song::CPOS, Pos(s->second.frame, false), false, true, false); + return; } //--------------------------------------------------------- @@ -697,17 +1040,19 @@ void AudioTrack::eraseACEvent(int id, int frame) { ciCtrlList icl = _controller.find(id); - if(icl == _controller.end()) + if(icl == _controller.end()) { return; - + } + + CtrlList* cl = icl->second; if(cl->empty()) return; - + iCtrl s = cl->find(frame); if(s != cl->end()) cl->erase(s); - return; + return; } //--------------------------------------------------------- @@ -717,17 +1062,18 @@ void AudioTrack::eraseRangeACEvents(int id, int frame1, int frame2) { ciCtrlList icl = _controller.find(id); - if(icl == _controller.end()) + if(icl == _controller.end()) { return; - + } + CtrlList* cl = icl->second; if(cl->empty()) return; - + iCtrl s = cl->lower_bound(frame1); iCtrl e = cl->lower_bound(frame2); cl->erase(s, e); - return; + return; } //--------------------------------------------------------- @@ -737,14 +1083,15 @@ void AudioTrack::addACEvent(int id, int frame, double val) { ciCtrlList icl = _controller.find(id); - if(icl == _controller.end()) + if(icl == _controller.end()) { return; - + } + CtrlList* cl = icl->second; - + // Add will replace if found. cl->add(frame, val); - return; + return; } //--------------------------------------------------------- @@ -757,20 +1104,31 @@ if(icl == _controller.end()) return; CtrlList* cl = icl->second; - iCtrl ic = cl->find(frame); + iCtrl ic = cl->find(frame); if(ic != cl->end()) cl->erase(ic); cl->insert(std::pair (newframe, CtrlVal(newframe, newval))); } //--------------------------------------------------------- +// latency +//--------------------------------------------------------- + +float AudioTrack::latency(int /*channel*/) +{ + if(!_efxPipe) + return 0.0; + return _efxPipe->latency(); +} + +//--------------------------------------------------------- // volume //--------------------------------------------------------- double AudioTrack::volume() const { - return _controller.value(AC_VOLUME, MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); + return _controller.value(AC_VOLUME, MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || automationType() == AUTO_OFF || !_controls[AC_VOLUME].enCtrl); } //--------------------------------------------------------- @@ -794,8 +1152,8 @@ double AudioTrack::pan() const { - return _controller.value(AC_PAN, MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl); + return _controller.value(AC_PAN, MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || automationType() == AUTO_OFF || !_controls[AC_PAN].enCtrl); } //--------------------------------------------------------- @@ -836,26 +1194,17 @@ double AudioTrack::pluginCtrlVal(int ctlID) const { - bool en_1 = true, en_2 = true; - if(ctlID < AC_PLUGIN_CTL_BASE) + bool en = 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; - } + if((unsigned long)ctlID < _controlPorts) + en = _controls[ctlID].enCtrl; } else { if(ctlID < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special synth controller block. { - _efxPipe->controllersEnabled(ctlID, &en_1, &en_2); + en = _efxPipe->controllerEnabled(ctlID); } else { @@ -866,15 +1215,14 @@ if(sif) { int in_ctrl_idx = ctlID & AC_PLUGIN_CTL_ID_MASK; - en_1 = sif->controllerEnabled(in_ctrl_idx); - en_2 = sif->controllerEnabled2(in_ctrl_idx); + en = sif->controllerEnabled(in_ctrl_idx); } } } - } - - return _controller.value(ctlID, MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || automationType() == AUTO_OFF || !en_1 || !en_2); + } + + return _controller.value(ctlID, MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || automationType() == AUTO_OFF || !en); } //--------------------------------------------------------- @@ -884,25 +1232,35 @@ void AudioTrack::setPluginCtrlVal(int param, double val) { iCtrlList cl = _controller.find(param); - if (cl == _controller.end()) + if (cl == _controller.end()) return; - + cl->second->setCurVal(val); } - + //--------------------------------------------------------- // addScheduledControlEvent // returns true if event cannot be delivered //--------------------------------------------------------- -bool AudioTrack::addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame) +bool AudioTrack::addScheduledControlEvent(int track_ctrl_id, double 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. + if(track_ctrl_id < AC_PLUGIN_CTL_BASE) { - iCtrlList icl = _controller.find(track_ctrl_id); - if(icl == _controller.end()) + // Send these controllers directly to the track's own FIFO. + ControlEvent ce; + ce.unique = false; + ce.fromGui = false; + ce.idx = track_ctrl_id; + ce.value = val; + // Time-stamp the event. 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, "AudioTrack::addScheduledControlEvent: fifo overflow: in control number:%d\n", track_ctrl_id); return true; - icl->second->setCurVal(val); + } return false; } else @@ -922,26 +1280,23 @@ } } } - } + } return true; } //--------------------------------------------------------- // enableController -// Enable or disable gui controls. -// Used during automation recording to inhibit gui controls +// 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) +void AudioTrack::enableController(int track_ctrl_id, bool en) { - if(track_ctrl_id < AC_PLUGIN_CTL_BASE) + 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); + if((unsigned long)track_ctrl_id < _controlPorts) + _controls[track_ctrl_id].enCtrl = en; } else { @@ -960,35 +1315,26 @@ } } } - } + } } //--------------------------------------------------------- -// controllersEnabled +// controllerEnabled //--------------------------------------------------------- -void AudioTrack::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) const +bool AudioTrack::controllerEnabled(int track_ctrl_id) const { - bool en_1 = true, en_2 = true; - if(track_ctrl_id < AC_PLUGIN_CTL_BASE) + 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; - } + if((unsigned long)track_ctrl_id < _controlPorts) + return _controls[track_ctrl_id].enCtrl; + return false; } else { if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special synth controller block. { - _efxPipe->controllersEnabled(track_ctrl_id, &en_1, &en_2); + return _efxPipe->controllerEnabled(track_ctrl_id); } else { @@ -999,39 +1345,66 @@ if(sif) { int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; - en_1 = sif->controllerEnabled(in_ctrl_idx); - en_2 = sif->controllerEnabled2(in_ctrl_idx); + return sif->controllerEnabled(in_ctrl_idx); } } } - } - - if(en1) - *en1 = en_1; - if(en2) - *en2 = en_2; + } + return false; } +//--------------------------------------------------------- +// enableAllControllers +// Enable all track and plugin controllers, and synth controllers if applicable. +//--------------------------------------------------------- + +void AudioTrack::enableAllControllers() +{ + // Enable track controllers: + for(unsigned long i = 0; i < _controlPorts; ++i) + _controls[i].enCtrl = true; + + // Enable plugin controllers: + Pipeline *pl = efxPipe(); + PluginI *p; + for(iPluginI i = pl->begin(); i != pl->end(); ++i) + { + p = *i; + if(!p) + continue; + p->enableAllControllers(true); + } + + // Enable synth controllers: + if(type() == AUDIO_SOFTSYNTH) + { + const SynthI* synth = static_cast(this); + SynthIF* sif = synth->sif(); + if(sif) + sif->enableAllControllers(true); + } +} + void AudioTrack::recordAutomation(int n, double v) { if(!MusEGlobal::automation) return; if(MusEGlobal::audio->isPlaying()) - _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); - else + _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); + else { if(automationType() == AUTO_WRITE) - _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); - else + _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); + else if(automationType() == AUTO_TOUCH) // In touch mode and not playing. Send directly to controller list. { iCtrlList cl = _controller.find(n); - if (cl == _controller.end()) + if (cl == _controller.end()) return; // Add will replace if found. - cl->second->add(MusEGlobal::audio->curFramePos(), v); - } + cl->second->add(MusEGlobal::audio->curFramePos(), v); + } } } @@ -1046,7 +1419,7 @@ else if(automationType() == AUTO_WRITE) _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); - } + } else { if(automationType() == AUTO_TOUCH) @@ -1054,15 +1427,15 @@ { // FIXME: Unsafe? Should sync by sending a message, but that'll really slow it down with large audio bufs. iCtrlList cl = _controller.find(n); - if (cl == _controller.end()) + if (cl == _controller.end()) return; // Add will replace if found. - cl->second->add(MusEGlobal::audio->curFramePos(), v); - } - else + cl->second->add(MusEGlobal::audio->curFramePos(), v); + } + else if(automationType() == AUTO_WRITE) - _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); - } + _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); + } } void AudioTrack::stopAutoRecord(int n, double v) @@ -1074,9 +1447,9 @@ if(automationType() == AUTO_TOUCH) { MusEGlobal::audio->msgAddACEvent(this, n, MusEGlobal::audio->curFramePos(), v); - _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_STOP)); - } - } + _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_STOP)); + } + } } //--------------------------------------------------------- @@ -1089,19 +1462,19 @@ xml.intTag(level, "prefader", prefader()); xml.intTag(level, "sendMetronome", sendMetronome()); xml.intTag(level, "automation", int(automationType())); - xml.floatTag(level, "gain", _gain); + xml.doubleTag(level, "gain", _gain); if (hasAuxSend()) { int naux = MusEGlobal::song->auxs()->size(); for (int idx = 0; idx < naux; ++idx) { QString s("%2\n"); // Aux fix from Remon, thanks. - xml.nput(level, s.arg(idx).arg(_auxSend[idx]).toAscii().constData()); + xml.nput(level, s.arg(idx).arg(_auxSend[idx]).toLatin1().constData()); } } for (ciPluginI ip = _efxPipe->begin(); ip != _efxPipe->end(); ++ip) { if (*ip) (*ip)->writeConfiguration(level, xml); } - _controller.write(level, xml); + _controller.write(level, xml); } //--------------------------------------------------------- @@ -1146,12 +1519,12 @@ bool AudioTrack::readProperties(Xml& xml, const QString& tag) { - if (tag == "plugin") + if (tag == "plugin") { int rackpos; - for(rackpos = 0; rackpos < PipelineDepth; ++rackpos) + for(rackpos = 0; rackpos < PipelineDepth; ++rackpos) { - if(!(*_efxPipe)[rackpos]) + if(!(*_efxPipe)[rackpos]) break; } if(rackpos < PipelineDepth) @@ -1159,9 +1532,9 @@ PluginI* pi = new PluginI(); pi->setTrack(this); pi->setID(rackpos); - if(pi->readConfiguration(xml, false)) + if(pi->readConfiguration(xml, false)) delete pi; - else + else (*_efxPipe)[rackpos] = pi; } else @@ -1174,38 +1547,38 @@ else if (tag == "sendMetronome") _sendMetronome = xml.parseInt(); else if (tag == "gain") - _gain = xml.parseFloat(); + _gain = xml.parseDouble(); else if (tag == "automation") setAutomationType(AutomationType(xml.parseInt())); else if (tag == "controller") { CtrlList* l = new CtrlList(); l->read(xml); - // Since (until now) muse wrote a 'zero' for plugin controller current value + // Since (until now) muse wrote a 'zero' for plugin controller current value // in the XML file, we can't use that value, now that plugin automation is added. // We must take the value from the plugin control value. - // Otherwise we break all existing .med files with plugins, because the gui + // Otherwise we break all existing .med files with plugins, because the gui // controls would all be set to zero. - // But we will allow for the (unintended, useless) possibility of a controller + // But we will allow for the (unintended, useless) possibility of a controller // with no matching plugin control. - const PluginIBase* p = 0; + const PluginIBase* p = 0; bool ctlfound = false; - unsigned m = l->id() & AC_PLUGIN_CTL_ID_MASK; + unsigned m = l->id() & AC_PLUGIN_CTL_ID_MASK; int n = (l->id() >> AC_PLUGIN_CTL_BASE_POW) - 1; if(n >= 0 && n < PipelineDepth) p = (*_efxPipe)[n]; // Support a special block for synth controllers. - else if(n == MAX_PLUGINS && type() == AUDIO_SOFTSYNTH) + else if(n == MAX_PLUGINS && type() == AUDIO_SOFTSYNTH) { const SynthI* synti = static_cast < SynthI* > (this); const SynthIF* sif = synti->sif(); if(sif) p = static_cast < const PluginIBase* > (sif); } - + if(p && m < p->parameters()) ctlfound = true; - + iCtrlList icl = _controller.find(l->id()); if (icl == _controller.end()) _controller.add(l); @@ -1213,7 +1586,7 @@ CtrlList* d = icl->second; for (iCtrl i = l->begin(); i != l->end(); ++i) d->insert(std::pair (i->first, i->second)); - + if(!ctlfound) d->setCurVal(l->curVal()); d->setColor(l->color()); @@ -1222,15 +1595,15 @@ delete l; l = d; } - + if(ctlfound) { l->setCurVal(p->param(m)); - l->setValueType(p->ctrlValueType(m)); - l->setMode(p->ctrlMode(m)); - } + l->setValueType(p->ctrlValueType(m)); + l->setMode(p->ctrlMode(m)); + } } - else if (tag == "midiMapper") + else if (tag == "midiMapper") _controller.midiControls()->read(xml); else return Track::readProperties(xml, tag); @@ -1250,10 +1623,10 @@ PluginI* p = (*_efxPipe)[idx]; if(!p) continue; - + if(p->isShowNativeGuiPending()) p->showNativeGui(true); - } + } } //--------------------------------------------------------- @@ -1268,31 +1641,31 @@ iCtrlList icl = _controller.lower_bound((idx + 1) * AC_PLUGIN_CTL_BASE); if(icl == _controller.end() || ((icl->second->id() >> AC_PLUGIN_CTL_BASE_POW) - 1) != idx) continue; - + // We found some controllers with that index. Now iterate the plugin rack... for(int i = idx; i >= 0; i--) { PluginI* p = (*_efxPipe)[i]; if(!p) continue; - + // We found a plugin at a rack position. If the rack position is not the same as the controller index... if(i != idx) { (*_efxPipe)[i] = 0; (*_efxPipe)[idx] = p; - } + } p->setID(idx); - + // It is now safe to update the controllers. p->updateControllers(); - + break; } } - + // No matter of the outcome of the above - rack position is not too critical - - // making sure that each control has a controller is important. Otherwise they + // making sure that each control has a controller is important. Otherwise they // are stuck at zero can't be adjusted. // Muse med files created before the automation patches (before 0.9pre1) may have broken // controller sections, so this will allow more tolerance of them. @@ -1301,38 +1674,38 @@ PluginI* p = (*_efxPipe)[idx]; if(!p) continue; - + if(p->id() != idx) p->setID(idx); - + int j = p->parameters(); - - for(int i = 0; i < j; i++) + + for(int i = 0; i < j; i++) { int id = genACnum(idx, i); CtrlList* l = 0; - + ciCtrlList icl = _controller.find(id); if(icl == _controller.end()) { l = new CtrlList(id); addController(l); - } - else + } + else l = icl->second; - - // Force all of these now, even though they may have already been set. With a pre- + + // Force all of these now, even though they may have already been set. With a pre- // 0.9pre1 med file with broken controller sections they may not be set correct. float min, max; p->range(i, &min, &max); l->setRange(min, max); l->setName(QString(p->paramName(i))); l->setValueType(p->ctrlValueType(i)); - l->setMode(p->ctrlMode(i)); + l->setMode(p->ctrlMode(i)); l->setCurVal(p->param(i)); - } + } } - + // FIXME: The loop is a safe way to delete while iterating lists. bool loop; do @@ -1345,22 +1718,22 @@ // Ignore volume, pan, mute etc. if(id < AC_PLUGIN_CTL_BASE) continue; - - unsigned param = id & AC_PLUGIN_CTL_ID_MASK; + + unsigned param = id & AC_PLUGIN_CTL_ID_MASK; int idx = (id >> AC_PLUGIN_CTL_BASE_POW) - 1; - + const PluginIBase* p = 0; if(idx >= 0 && idx < PipelineDepth) p = (*_efxPipe)[idx]; // Support a special block for synth controllers. - else if(idx == MAX_PLUGINS && type() == AUDIO_SOFTSYNTH) + else if(idx == MAX_PLUGINS && type() == AUDIO_SOFTSYNTH) { const SynthI* synti = static_cast < const SynthI* > (this); SynthIF* sif = synti->sif(); if(sif) p = static_cast < const PluginIBase* > (sif); } - + // If there's no plugin at that rack position, or the param is out of range of // the number of controls in the plugin, then it's a stray controller. Delete it. // Future: Leave room for possible bypass controller at AC_PLUGIN_CTL_ID_MASK -1. @@ -1368,15 +1741,15 @@ if(!p || (param >= p->parameters())) { _controller.erase(id); - + loop = true; break; } - } - } + } + } while (loop); - - + + // DELETETHIS 40 i DO trust the below. some container's erase functions // return an iterator to the next, so sometimes you need it=erase(it) // instead of erase(it++). @@ -1387,12 +1760,12 @@ // CAUTION: the below isn't quite up-to-date! first recheck. // this "not-being-up-to-date" is another reason for NOT keeping such // comments! - + // FIXME: Although this tested OK, and is the 'official' way to erase while iterating, - // I don't trust it. I'm weary of this method. The technique didn't work + // I don't trust it. I'm weary of this method. The technique didn't work // in Audio::msgRemoveTracks(), see comments there. /* - + // Now delete any stray controllers which don't belong to anything. for(iCtrlList icl = _controller.begin(); icl != _controller.end(); ) { @@ -1403,8 +1776,8 @@ { ++icl; continue; - } - + } + int param = id & AC_PLUGIN_CTL_ID_MASK; int idx = (id >> AC_PLUGIN_CTL_BASE_POW) - 1; PluginI* p = (*_efxPipe)[idx]; @@ -1415,11 +1788,19 @@ if(!p || (param >= p->parameters())) _controller.erase(icl++); else - ++icl; - } + ++icl; + } */ } +RouteCapabilitiesStruct AudioTrack::routeCapabilities() const +{ + RouteCapabilitiesStruct s; + s._trackChannels._inChannels = s._trackChannels._outChannels = totalProcessBuffers(); + s._trackChannels._inRoutable = s._trackChannels._outRoutable = (s._trackChannels._inChannels != 0); + return s; +} + //--------------------------------------------------------- // AudioInput //--------------------------------------------------------- @@ -1440,9 +1821,9 @@ jackPorts[i] = 0; // Register ports. - if(MusEGlobal::checkAudioDevice()) - { - for (int i = 0; i < channels(); ++i) + if(MusEGlobal::checkAudioDevice()) + { + for (int i = 0; i < channels(); ++i) { char buffer[128]; snprintf(buffer, 128, "%s-%d", _name.toLatin1().constData(), i); @@ -1456,15 +1837,15 @@ { AudioTrack::assign(t, flags); internal_assign(t, flags); -} +} void AudioInput::internal_assign(const Track& t, int flags) { if(t.type() != AUDIO_INPUT) return; - + const AudioInput& at = (const AudioInput&)t; - + if(flags & ASSIGN_ROUTES) { for(ciRoute ir = at._inRoutes.begin(); ir != at._inRoutes.end(); ++ir) @@ -1472,11 +1853,11 @@ // Defer all Jack routes to these copy constructors or assign ! if(ir->type != Route::JACK_ROUTE) continue; - // FIXME Must use msgAddRoute instead of _inRoutes.push_back, because it connects to Jack. + // FIXME Must use msgAddRoute instead of _inRoutes.push_back, because it connects to Jack. // The track is still fresh has not been added to track lists yet. Will cause audio processing problems ? - MusEGlobal::audio->msgAddRoute(*ir, Route(this, ir->channel, ir->channels)); + MusEGlobal::audio->msgAddRoute(*ir, Route(this, ir->channel, ir->channels)); } - } + } } //--------------------------------------------------------- @@ -1486,12 +1867,29 @@ AudioInput::~AudioInput() { if (!MusEGlobal::checkAudioDevice()) return; - for (int i = 0; i < _channels; ++i) + for (int i = 0; i < _channels; ++i) if(jackPorts[i]) MusEGlobal::audioDevice->unregisterPort(jackPorts[i]); } //--------------------------------------------------------- +// latency +//--------------------------------------------------------- + +float AudioInput::latency(int channel) +{ + float l = AudioTrack::latency(channel); + + if(!MusEGlobal::checkAudioDevice()) + return l; + + void* jackPort = jackPorts[channel]; + if(jackPort) + l += MusEGlobal::audioDevice->portLatency(jackPort, true); + return l; +} + +//--------------------------------------------------------- // write //--------------------------------------------------------- @@ -1533,6 +1931,19 @@ } } +RouteCapabilitiesStruct AudioInput::routeCapabilities() const +{ + RouteCapabilitiesStruct s = AudioTrack::routeCapabilities(); + + // Support Midi Track to Audio Input Track routes (for soloing chain). + s._trackChannels._inRoutable = true; + s._trackChannels._inChannels = 0; + + s._jackChannels._inRoutable = false; + s._jackChannels._inChannels = totalProcessBuffers(); + return s; +} + //--------------------------------------------------------- // AudioOutput //--------------------------------------------------------- @@ -1550,11 +1961,11 @@ for (int i = 0; i < MAX_CHANNELS; ++i) jackPorts[i] = 0; _nframes = 0; - - // Register ports. - if(MusEGlobal::checkAudioDevice()) + + // Register ports. + if(MusEGlobal::checkAudioDevice()) { - for (int i = 0; i < channels(); ++i) + for (int i = 0; i < channels(); ++i) { char buffer[128]; snprintf(buffer, 128, "%s-%d", _name.toLatin1().constData(), i); @@ -1568,15 +1979,15 @@ { AudioTrack::assign(t, flags); internal_assign(t, flags); -} +} void AudioOutput::internal_assign(const Track& t, int flags) { if(t.type() != AUDIO_OUTPUT) return; - + const AudioOutput& at = (const AudioOutput&)t; - + if(flags & ASSIGN_ROUTES) { for(ciRoute ir = at._outRoutes.begin(); ir != at._outRoutes.end(); ++ir) @@ -1584,13 +1995,13 @@ // Defer all Jack routes to these copy constructors or assign ! if(ir->type != Route::JACK_ROUTE) continue; - // FIXME Must use msgAddRoute instead of _outRoutes.push_back, because it connects to Jack. + // FIXME Must use msgAddRoute instead of _outRoutes.push_back, because it connects to Jack. // The track is still fresh has not been added to track lists yet. Will cause audio processing problems ? - MusEGlobal::audio->msgAddRoute(Route(this, ir->channel, ir->channels), *ir); + MusEGlobal::audio->msgAddRoute(Route(this, ir->channel, ir->channels), *ir); } - } + } } - + //--------------------------------------------------------- // ~AudioOutput //--------------------------------------------------------- @@ -1645,6 +2056,19 @@ } } +RouteCapabilitiesStruct AudioOutput::routeCapabilities() const +{ + RouteCapabilitiesStruct s = AudioTrack::routeCapabilities(); + + // Support Midi Track to Audio Input Track routes (for soloing chain). + s._trackChannels._outRoutable = true; + s._trackChannels._outChannels = 0; + + s._jackChannels._outRoutable = false; + s._jackChannels._outChannels = totalProcessBuffers(); + return s; +} + //--------------------------------------------------------- // write //--------------------------------------------------------- @@ -1680,7 +2104,7 @@ { mapRackPluginsToControllers(); return; - } + } default: break; } @@ -1705,13 +2129,12 @@ //--------------------------------------------------------- int getNextAuxIndex() { - printf("getNextAuxIndex!\n"); int curAux=0; AuxList * al = MusEGlobal::song->auxs(); for (MusECore::iAudioAux i = al->begin(); i != al->end(); ++i) { MusECore::AudioAux* ax = *i; - printf("ax index %d\n", ax->index()); + printf("aux index %d\n", ax->index()); if (ax->index() > curAux) { printf("found new index! %d\n", ax->index()); @@ -1739,6 +2162,13 @@ fprintf(stderr, "ERROR: AudioAux ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } else buffer[i] = 0; @@ -1759,6 +2189,13 @@ fprintf(stderr, "ERROR: AudioAux ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } else buffer[i] = 0; @@ -1802,7 +2239,7 @@ { mapRackPluginsToControllers(); return; - } + } default: break; } @@ -1815,15 +2252,15 @@ bool AudioAux::getData(unsigned pos, int ch, unsigned samples, float** data) { - // Make sure all the aux-supporting tracks are processed first so aux data is gathered. + // Make sure all the aux-supporting tracks are processed first so aux data is gathered. TrackList* tl = MusEGlobal::song->tracks(); AudioTrack* track; - for(ciTrack it = tl->begin(); it != tl->end(); ++it) + for(ciTrack it = tl->begin(); it != tl->end(); ++it) { if((*it)->isMidiTrack()) continue; track = (AudioTrack*)(*it); - // If there are any Aux route paths to the track, defer processing until the second main track processing pass. + // If there are any Aux route paths to the track, defer processing until the second main track processing pass. if(!track->processed() && track->hasAuxSend() && !track->auxRefCount()) { int chans = track->channels(); @@ -1832,11 +2269,11 @@ float buff_data[samples * chans]; for (int i = 0; i < chans; ++i) buff[i] = buff_data + i * samples; - - track->copyData(pos, chans, -1, -1, samples, buff); + + track->copyData(pos, -1, chans, chans, -1, -1, samples, buff); } - } - + } + for (int i = 0; i < ch; ++i) data[i] = buffer[i % channels()]; return true; @@ -1848,7 +2285,7 @@ void AudioAux::setChannels(int n) { - if(n > channels()) + if(n > channels()) { for(int i = channels(); i < n; ++i) { @@ -1858,11 +2295,18 @@ fprintf(stderr, "ERROR: AudioAux::setChannels: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } } - else if(n < channels()) + else if(n < channels()) { - for(int i = n; i < channels(); ++i) + for(int i = n; i < channels(); ++i) { if(buffer[i]) free(buffer[i]); @@ -1887,6 +2331,8 @@ bool AudioTrack::setRecordFlag1(bool f) { + if(!canRecord()) + return false; if (f == _recordFlag) return true; if (f) { @@ -1902,12 +2348,12 @@ // this file has not been processed and can be // deleted // We should only arrive here if going from a 'record-armed' state - // to a non record-armed state. Because otherwise after actually - // recording, the _recFile pointer is made into an event, + // to a non record-armed state. Because otherwise after actually + // recording, the _recFile pointer is made into an event, // then _recFile is made zero before this function is called. QString s = _recFile->path(); setRecFile(NULL); - + remove(s.toLatin1().constData()); if(MusEGlobal::debugMsg) printf("AudioNode::setRecordFlag1: remove file %s if it exists\n", s.toLatin1().constData()); @@ -1927,31 +2373,31 @@ bool AudioTrack::prepareRecording() { if(MusEGlobal::debugMsg) - printf("prepareRecording for track %s\n", _name.toLatin1().constData()); + printf("prepareRecording for track %s\n", name().toLatin1().constData()); if (_recFile.isNull()) { // // create soundfile for recording // - char buffer[128]; + const QString fbase = QString("%1/").arg(MusEGlobal::museProject) + + QObject::tr("TRACK") + + QString("_%1_").arg(name().simplified().replace(" ","_")) + + QObject::tr("TAKE"); QFile fil; for (;;++recFileNumber) { - sprintf(buffer, "%s/TRACK_%s_TAKE_%d.wav", - MusEGlobal::museProject.toLocal8Bit().constData(), - name().simplified().replace(" ","_").toLocal8Bit().constData(), - recFileNumber); - fil.setFileName(QString(buffer)); + fil.setFileName(fbase + QString("_%1.wav").arg(recFileNumber)); if (!fil.exists()) break; } - _recFile = new MusECore::SndFile(QString(buffer)); + _recFile = new MusECore::SndFile(fil.fileName()); + _recFile->setFormat( SF_FORMAT_WAV | SF_FORMAT_FLOAT, _channels, MusEGlobal::sampleRate); } if (MusEGlobal::debugMsg) - printf("AudioNode::setRecordFlag1: init internal file %s\n", _recFile->path().toLatin1().constData()); + printf("AudioTrack::prepareRecording: init internal file %s\n", _recFile->path().toLatin1().constData()); if(_recFile->openWrite()) { @@ -1960,7 +2406,7 @@ return false; } - return true; + return true; } double AudioTrack::auxSend(int idx) const { diff -Nru muse-2.1.2/muse/cliplist/cliplist.cpp muse-3.0.2+ds1/muse/cliplist/cliplist.cpp --- muse-2.1.2/muse/cliplist/cliplist.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/cliplist/cliplist.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -38,7 +38,7 @@ namespace MusEGui { extern int mtcType; -enum { COL_NAME=0, COL_REFS, COL_SAMPLERATE, COL_LEN }; +enum { COL_NAME=0, COL_REFS, COL_SAMPLERATE, COL_LEN, COL_DATA, COL_STATUS }; //--------------------------------------------------------- // ClipItem @@ -61,6 +61,7 @@ setText(COL_REFS, QString().setNum(_wf.getRefCount())); setText(COL_SAMPLERATE, QString().setNum(_wf.samplerate())); setText(COL_LEN, QString().setNum(_wf.samples())); + setText(COL_STATUS, _wf.isOpen() ? QObject::tr("Open") : QObject::tr("Closed")); } //--------------------------------------------------------- @@ -137,6 +138,13 @@ settingsMenu->addAction(shareAction); settingsMenu->addAction(fullscreenAction); + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + QFontMetrics fm(editor->view->font()); int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth,0, this); // ddskrjo 0 int w = 2 + fm.width('9') * 9 + fm.width(':') * 3 + fw * 4; @@ -187,11 +195,8 @@ void ClipListEdit::songChanged(MusECore::SongChangedFlags_t type) { - // Is it simply a midi controller value adjustment? Forget it. - if(type == SC_MIDI_CONTROLLER) - return; - - updateList(); + if(type & (SC_CLIP_MODIFIED | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED)) + updateList(); } //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/cliplist/CMakeLists.txt muse-3.0.2+ds1/muse/cliplist/CMakeLists.txt --- muse-2.1.2/muse/cliplist/CMakeLists.txt 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/cliplist/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP (cliplist_mocs +QT5_WRAP_CPP (cliplist_mocs cliplist.h ) diff -Nru muse-2.1.2/muse/CMakeLists.txt muse-3.0.2+ds1/muse/CMakeLists.txt --- muse-2.1.2/muse/CMakeLists.txt 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -27,9 +27,9 @@ ${CMAKE_CURRENT_BINARY_DIR} ) -set (SubDirs - arranger - cliplist +set (SubDirs + arranger + cliplist ctrl driver instruments @@ -38,10 +38,19 @@ master midiedit mixer - mplugins + mplugins waveedit widgets ) +if(LV2_SUPPORT) + if(LV2_USE_SUPPLIED_VERSION) + set ( SubDirs ${SubDirs} lv2Support ) + endif(LV2_USE_SUPPLIED_VERSION) + + if(HAVE_GTK2) + set ( SubDirs ${SubDirs} lv2Gtk2Support ) + endif(HAVE_GTK2) +endif(LV2_SUPPORT) if (PYTHON_SUPPORT) set ( REM_CPP_FLAGS "-DENABLE_PYTHON" ) @@ -54,27 +63,33 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( muse_moc_headers - app.h +QT5_WRAP_CPP ( muse_moc_headers + app.h appearance.h - cobject.h - conf.h - confmport.h - midieditor.h - miditransform.h - plugin.h - song.h - transport.h - trackdrummapupdater.h - value.h - steprec.h + cobject.h + conf.h + confmport.h + midieditor.h + miditransform.h + plugin.h + song.h + transport.h + value.h + steprec.h + lv2host.h + vst_native.h + ) + +QT5_WRAP_CPP ( wavepreview_moc_headers + wavepreview.h ) ## ## Resource files ## -QT4_ADD_RESOURCES (muse_qrc_files +QT5_ADD_RESOURCES (muse_qrc_files muse.qrc + ../resources.qrc ) file (GLOB core_source_files @@ -90,59 +105,56 @@ controlfifo.cpp ctrl.cpp dialogs.cpp - dssihost.cpp + dssihost.cpp + lv2host.cpp event.cpp - eventlist.cpp - exportmidi.cpp + eventlist.cpp + exportmidi.cpp functions.cpp - gconfig.cpp + gconfig.cpp globals.cpp - help.cpp + help.cpp helper.cpp - importmidi.cpp + importmidi.cpp key.cpp keyevent.cpp - memory.cpp midi.cpp midictrl.cpp - mididev.cpp + mididev.cpp midieditor.cpp - midievent.cpp - midifile.cpp + midievent.cpp + midifile.cpp midiport.cpp midiseq.cpp miditransform.cpp - mpevent.cpp mtc.cpp node.cpp + operations.cpp osc.cpp part.cpp plugin.cpp pos.cpp route.cpp - seqmsg.cpp - shortcuts.cpp + seqmsg.cpp + shortcuts.cpp sig.cpp song.cpp - songfile.cpp + songfile.cpp stringparam.cpp - structure.cpp sync.cpp - synth.cpp + synth.cpp tempo.cpp thread.cpp - ticksynth.cpp + ticksynth.cpp track.cpp - trackdrummapupdater.cpp transport.cpp undo.cpp value.cpp vst.cpp vst_native.cpp wave.cpp - waveevent.cpp - wavetrack.cpp - xml.cpp + waveevent.cpp + wavetrack.cpp steprec.cpp ) file (GLOB main_source_files @@ -152,16 +164,59 @@ icons.cpp ) +file (GLOB sysex_helper_source_files + sysex_helper.cpp + ) +file (GLOB memory_source_files + memory.cpp + ) +file (GLOB evdata_source_files + evdata.cpp + ) +file (GLOB mpevent_source_files + mpevent.cpp + ) +file (GLOB event_source_files + event.cpp + ) +file (GLOB wavepreview_source_files + wavepreview.cpp + ) +file (GLOB xml_source_files + xml.cpp + ) + ## ## Define target ## + +add_library ( sysex_helper_module SHARED + ${sysex_helper_source_files} + ) +add_library ( memory_module SHARED + ${memory_source_files} + ) +add_library ( evdata_module SHARED + ${evdata_source_files} + ) +add_library ( mpevent_module SHARED + ${mpevent_source_files} + ) +add_library ( wavepreview_module SHARED + ${wavepreview_moc_headers} + ${wavepreview_source_files} + ) +add_library ( xml_module SHARED + ${xml_source_files} + ) + add_library ( core ${MODULES_BUILD} - ${muse_qrc_files} ${muse_moc_headers} ${core_source_files} ) add_executable ( muse ${main_source_files} + ${muse_qrc_files} ) add_library ( icons ${MODULES_BUILD} ${icons_source_files} @@ -188,6 +243,7 @@ remote waveedit widgets + lv2Support ) # Make sure to include the uic generated headers paths. @@ -198,10 +254,10 @@ ${CMAKE_CURRENT_BINARY_DIR}/liste ${CMAKE_CURRENT_BINARY_DIR}/midiedit ${CMAKE_CURRENT_BINARY_DIR}/mixer - ${CMAKE_CURRENT_BINARY_DIR}/mplugins + ${CMAKE_CURRENT_BINARY_DIR}/mplugins ${CMAKE_CURRENT_BINARY_DIR}/remote ${CMAKE_CURRENT_BINARY_DIR}/waveedit - ${CMAKE_CURRENT_BINARY_DIR}/widgets + ${CMAKE_CURRENT_BINARY_DIR}/widgets ${CMAKE_CURRENT_BINARY_DIR}/widgets/function_dialogs ) @@ -212,7 +268,8 @@ # PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h" # PROPERTIES COMPILE_FLAGS "-Imidiedit -Iarranger -Iliste -Iwidgets -Imixer -Idriver -Iwaveedit -Implugins -Iinstruments -Iremote ${REM_CPP_FLAGS} -include ${PROJECT_BINARY_DIR}/all.h " # PROPERTIES COMPILE_FLAGS "${REM_CPP_FLAGS} -DINSTPREFIX='\"${CMAKE_INSTALL_PREFIX}\"' -DINSTLIBDIR='\"${LIBRARY_OUTPUT_DIRECTORY}\"' -include ${PROJECT_BINARY_DIR}/all.h " - PROPERTIES COMPILE_FLAGS "${REM_CPP_FLAGS} -include ${PROJECT_BINARY_DIR}/all.h" + PROPERTIES COMPILE_FLAGS "${VST_SDK_QUIRK} ${REM_CPP_FLAGS} -include ${PROJECT_BINARY_DIR}/all.h" + OUTPUT_NAME muse_core ) set_target_properties( muse @@ -222,33 +279,84 @@ PROPERTIES OUTPUT_NAME muse_icons ) +set_target_properties( sysex_helper_module + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h" + OUTPUT_NAME muse_sysex_helper_module + ) +set_target_properties( memory_module + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h" + OUTPUT_NAME muse_memory_module + ) +set_target_properties( evdata_module + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h" + OUTPUT_NAME muse_evdata_module + ) +set_target_properties( mpevent_module + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h" + OUTPUT_NAME muse_mpevent_module + ) +set_target_properties( wavepreview_module + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h" + OUTPUT_NAME muse_wavepreview_module + ) +set_target_properties( xml_module + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h" + OUTPUT_NAME muse_xml_module + ) + ## ## Linkage ## + +target_link_libraries(evdata_module + memory_module + sysex_helper_module + ) +target_link_libraries(mpevent_module + evdata_module + sysex_helper_module + ) +target_link_libraries(wavepreview_module + ${QT_LIBRARIES} + ${SNDFILE_LIBRARIES} + ${SAMPLERATE_LIBRARIES} + ) +target_link_libraries(xml_module + ${QT_LIBRARIES} + ) + target_link_libraries(core - al - arranger + al + arranger awl cliplist ctrl driver + evdata_module icons instruments liste marker master + memory_module midiedit mixer + mpevent_module mplugins - synti + muse_string + synti + sysex_helper_module waveedit - widgets + wavepreview_module + widgets widgets_functiondialogs - + xml_module + ${QT_LIBRARIES} + ## Bring in the dssi alsa compatibility libraries if required + ${DSSI_ALSA_COMPAT_LIBRARIES} ${SNDFILE_LIBRARIES} ${SAMPLERATE_LIBRARIES} - ${UUID_LIBRARIES} ${REM_LIB} ${FST_LIB} dl @@ -262,24 +370,62 @@ target_link_libraries(core ${LIBLO_LIBRARIES}) endif(OSC_SUPPORT) +if(LV2_SUPPORT) + if(LV2_USE_SUPPLIED_VERSION) + message("Building and linking internal LV2 support libraries (lv2, lilv, sord etc.)") + message("") + target_link_libraries(core lv2_support) + if (PCRE_FOUND) + target_link_libraries(core ${PCRE_LIBRARIES}) + endif (PCRE_FOUND) + else(LV2_USE_SUPPLIED_VERSION) + message("Linking system LV2 support libraries (lv2, lilv, sord etc.)") + message("") + target_link_libraries(core ${LILV_LIBRARIES}) + target_link_libraries(core ${SORD_LIBRARIES}) + target_link_libraries(core ${LV2_LIBRARIES}) + endif(LV2_USE_SUPPLIED_VERSION) + + if(HAVE_GTK2) + target_link_libraries(core lv2_gtk2_support) + endif(HAVE_GTK2) + +endif(LV2_SUPPORT) + target_link_libraries(muse midiedit core - ) + ) target_link_libraries(icons ${QT_LIBRARIES} - ) + ) ## ## Install location ## -install( TARGETS muse +install( TARGETS muse RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/ ) + +install(TARGETS + sysex_helper_module + memory_module + evdata_module + mpevent_module + wavepreview_module + xml_module + DESTINATION ${MusE_MODULES_DIR} + ) + if ( ${MODULES_BUILD} STREQUAL SHARED ) - install(TARGETS core icons + install(TARGETS core icons + # sysex_helper_module + # memory_module + # evdata_module + # mpevent_module + # wavepreview_module + # xml_module DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) - diff -Nru muse-2.1.2/muse/cobject.cpp muse-3.0.2+ds1/muse/cobject.cpp --- muse-2.1.2/muse/cobject.cpp 2013-03-28 20:34:30.000000000 +0000 +++ muse-3.0.2+ds1/muse/cobject.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -29,11 +29,15 @@ #include "shortcuts.h" #include "songpos_toolbar.h" #include "sig_tempo_toolbar.h" +#include "gconfig.h" +#include "helper.h" #include #include #include #include +#include +#include using std::list; using MusEGlobal::muse; @@ -103,52 +107,74 @@ fullscreenAction->setEnabled(!isMdiWin()); if (mdisubwin) - mdisubwin->resize(_widthInit[_type], _heightInit[_type]); + { + mdisubwin->resize(_widthInit[_type], _heightInit[_type]); + if(_type == ARRANGER) + mdisubwin->setWindowState(Qt::WindowMaximized); + } else resize(_widthInit[_type], _heightInit[_type]); - + + //-------------------------------------------------- + // Toolbar + //-------------------------------------------------- + +// NOTICE: Please ensure that any tool bar object names here match the names +// assigned in the 'toolbar' creation section of MusE::MusE(), +// or any other TopWin class. +// This allows MusE::setCurrentMenuSharingTopwin() to do some magic +// to retain the original toolbar layout. If it finds an existing +// toolbar with the same object name, it /replaces/ it using insertToolBar(), +// instead of /appending/ with addToolBar(). + QToolBar* undo_tools=addToolBar(tr("Undo/Redo tools")); undo_tools->setObjectName("Undo/Redo tools"); undo_tools->addActions(MusEGlobal::undoRedo->actions()); - QToolBar* panic_toolbar = addToolBar(tr("Panic")); - panic_toolbar->setObjectName("panic"); - panic_toolbar->addAction(MusEGlobal::panicAction); - - QToolBar* metronome_toolbar = addToolBar(tr("Metronome")); - metronome_toolbar->setObjectName("metronome"); - metronome_toolbar->addAction(MusEGlobal::metronomeAction); - - QToolBar* transport_toolbar = addToolBar(tr("Transport")); - transport_toolbar->setObjectName("transport"); - transport_toolbar->addActions(MusEGlobal::transportAction->actions()); - - QToolBar* songpos_tb; - songpos_tb = addToolBar(tr("Song Position")); - songpos_tb->setObjectName("Song Position"); - songpos_tb->addWidget(new MusEGui::SongPosToolbarWidget(songpos_tb)); - songpos_tb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - songpos_tb->setContextMenuPolicy(Qt::PreventContextMenu); - - QToolBar* tempo_tb; - tempo_tb = addToolBar(tr("Tempo")); - tempo_tb->setObjectName("Tempo"); - MusEGui::TempoToolbarWidget* tw = new MusEGui::TempoToolbarWidget(tempo_tb); - tempo_tb->addWidget(tw); - - QToolBar* sig_tb; - sig_tb = addToolBar(tr("Signature")); - sig_tb->setObjectName("Signature"); - MusEGui::SigToolbarWidget* sw = new MusEGui::SigToolbarWidget(tempo_tb); - sig_tb->addWidget(sw); - - connect(tw, SIGNAL(returnPressed()), SLOT(focusCanvas())); - connect(tw, SIGNAL(escapePressed()), SLOT(focusCanvas())); - connect(sw, SIGNAL(returnPressed()), SLOT(focusCanvas())); - connect(sw, SIGNAL(escapePressed()), SLOT(focusCanvas())); -} + QToolBar* panic_toolbar = addToolBar(tr("Panic")); + panic_toolbar->setObjectName("Panic tool"); + panic_toolbar->addAction(MusEGlobal::panicAction); + + QToolBar* metronome_toolbar = addToolBar(tr("Metronome")); + metronome_toolbar->setObjectName("Metronome tool"); + metronome_toolbar->addAction(MusEGlobal::metronomeAction); + + QToolBar* songpos_tb; + songpos_tb = addToolBar(tr("Song Position")); + songpos_tb->setObjectName("Song Position tool"); + songpos_tb->addWidget(new MusEGui::SongPosToolbarWidget(songpos_tb)); + songpos_tb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + songpos_tb->setContextMenuPolicy(Qt::PreventContextMenu); + + addToolBarBreak(); + + QToolBar* transport_toolbar = addToolBar(tr("Transport")); + transport_toolbar->setObjectName("Transport tool"); + transport_toolbar->addActions(MusEGlobal::transportAction->actions()); + transport_toolbar->setIconSize(QSize(22, 22)); + + // Already has an object name. + TempoToolbar* tempo_tb = new TempoToolbar(tr("Tempo"), this); + addToolBar(tempo_tb); + + // Already has an object name. + SigToolbar* sig_tb = new SigToolbar(tr("Signature"), this); + addToolBar(sig_tb); + + connect(tempo_tb, SIGNAL(returnPressed()), SLOT(focusCanvas())); + connect(tempo_tb, SIGNAL(escapePressed()), SLOT(focusCanvas())); + connect(sig_tb, SIGNAL(returnPressed()), SLOT(focusCanvas())); + connect(sig_tb, SIGNAL(escapePressed()), SLOT(focusCanvas())); + + /* unconnect parent if window is not mdi */ + /* to make editor windows not stay on top */ + if(!isMdiWin()) + { + setParent(0); + } +} //--------------------------------------------------------- // readStatus @@ -157,6 +183,10 @@ void TopWin::readStatus(MusECore::Xml& xml) { int x=0, y=0, width=800, height=600; + bool wsMinimized = false; + bool wsMaximized = false; + bool wsFullScreen = false; + bool wsActive = false; for (;;) { @@ -176,11 +206,19 @@ width=xml.parseInt(); else if (tag == "height") height=xml.parseInt(); + else if (tag == "wsMinimized") + wsMinimized=xml.parseInt(); + else if (tag == "wsMaximized") + wsMaximized=xml.parseInt(); + else if (tag == "wsFullScreen") + wsFullScreen=xml.parseInt(); + else if (tag == "wsActive") + wsActive=xml.parseInt(); else if (tag == "toolbars") { if (!sharesToolsAndMenu()) { - if (!restoreState(QByteArray::fromHex(xml.parse1().toAscii()))) + if (!restoreState(QByteArray::fromHex(xml.parse1().toLatin1()))) { fprintf(stderr,"ERROR: couldn't restore toolbars. trying default configuration...\n"); if (!restoreState(_toolbarNonsharedInit[_type])) @@ -189,7 +227,7 @@ } else { - _savedToolbarState=QByteArray::fromHex(xml.parse1().toAscii()); + _savedToolbarState=QByteArray::fromHex(xml.parse1().toLatin1()); if (_savedToolbarState.isEmpty()) _savedToolbarState=_toolbarNonsharedInit[_type]; } @@ -209,17 +247,25 @@ case MusECore::Xml::TagEnd: if (tag == "topwin") { + const QRect geo(x, y, width, height); + QFlags wstate; + if(wsMinimized) + wstate |= Qt::WindowMinimized; + if(wsMaximized) + wstate |= Qt::WindowMaximized; + if(wsFullScreen) + wstate |= Qt::WindowFullScreen; + if(wsActive) + wstate |= Qt::WindowActive; if (mdisubwin) { - if(mdisubwin->isMaximized()) - mdisubwin->showNormal(); - mdisubwin->move(x, y); - mdisubwin->resize(width, height); + mdisubwin->setGeometry(geo); + mdisubwin->setWindowState(wstate); } else { - move(x,y); - resize(width,height); + setGeometry(geo); + setWindowState(wstate); } return; @@ -244,20 +290,37 @@ // restoring of the positions xml.intTag(level, "is_subwin", isMdiWin()); + QRect geo; + QFlags wstate; if (mdisubwin) { - xml.intTag(level, "x", mdisubwin->x()); - xml.intTag(level, "y", mdisubwin->y()); - xml.intTag(level, "width", mdisubwin->width()); - xml.intTag(level, "height", mdisubwin->height()); + wstate = mdisubwin->windowState(); + geo = mdisubwin->normalGeometry(); + // TESTED on Qt5.3: For MDI geo was invalid (0, 0, -1, -1) when window maximized. + // This may be a reported Qt bug I read about. + if(!geo.isValid()) + geo = mdisubwin->geometry(); } else { - xml.intTag(level, "x", x()); - xml.intTag(level, "y", y()); - xml.intTag(level, "width", width()); - xml.intTag(level, "height", height()); - } + wstate = windowState(); + geo = normalGeometry(); + if(!geo.isValid()) + geo = geometry(); + } + // The order of geo first then state may be important here. + xml.intTag(level, "x", geo.x()); + xml.intTag(level, "y", geo.y()); + xml.intTag(level, "width", geo.width()); + xml.intTag(level, "height", geo.height()); + if(wstate.testFlag(Qt::WindowMinimized)) + xml.intTag(level, "wsMinimized", 1); + if(wstate.testFlag(Qt::WindowMaximized)) + xml.intTag(level, "wsMaximized", 1); + if(wstate.testFlag(Qt::WindowFullScreen)) + xml.intTag(level, "wsFullScreen", 1); + if(wstate.testFlag(Qt::WindowActive)) + xml.intTag(level, "wsActive", 1); xml.intTag(level, "shares_menu", sharesToolsAndMenu()); @@ -329,6 +392,15 @@ subwin->setVisible(vis); this->QMainWindow::show(); //bypass the delegation to the subwin + // Due to bug in Oxygen and Breeze at least on *buntu 16.04 LTS and some other distros, + // force the style and stylesheet again. Otherwise the window freezes. + if(MusEGlobal::config.fixFrozenMDISubWindows) + { + if(MusEGlobal::debugMsg) + fprintf(stderr, "TopWin::setIsMdiWin Calling updateThemeAndStyle()\n"); + MusEGui::updateThemeAndStyle(true); + } + if (_sharesToolsAndMenu == _sharesWhenFree[_type]) shareToolsAndMenu(_sharesWhenSubwin[_type]); @@ -336,6 +408,10 @@ fullscreenAction->setChecked(false); subwinAction->setChecked(true); muse->updateWindowMenu(); + + if(MusEGlobal::config.fixFrozenMDISubWindows) + connect(subwin, SIGNAL(windowStateChanged(Qt::WindowStates,Qt::WindowStates)), + SLOT(windowStateChanged(Qt::WindowStates,Qt::WindowStates))); } else { @@ -404,6 +480,13 @@ } +void TopWin::addToolBarBreak(Qt::ToolBarArea area) +{ + QMainWindow::addToolBarBreak(area); + _toolbars.push_back(NULL); +} + + void TopWin::shareToolsAndMenu(bool val) { if (MusEGlobal::unityWorkaround) @@ -530,9 +613,9 @@ else if (tag == "height") _heightInit[t] = xml.parseInt(); else if (tag == "nonshared_toolbars") - _toolbarNonsharedInit[t] = QByteArray::fromHex(xml.parse1().toAscii()); + _toolbarNonsharedInit[t] = QByteArray::fromHex(xml.parse1().toLatin1()); else if (tag == "shared_toolbars") - _toolbarSharedInit[t] = QByteArray::fromHex(xml.parse1().toAscii()); + _toolbarSharedInit[t] = QByteArray::fromHex(xml.parse1().toLatin1()); else if (tag == "shares_when_free") _sharesWhenFree[t] = xml.parseInt(); else if (tag == "shares_when_subwin") @@ -563,7 +646,7 @@ if (!initInited) { printf ("WARNING: TopWin::writeConfiguration() called although the config hasn't been\n" - " initalized! writing default configuration\n"); + " initialized! writing default configuration\n"); initConfiguration(); } xml.tag(level++, "topwin"); @@ -645,6 +728,19 @@ muse->updateWindowMenu(); } +void TopWin::windowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState) +{ + // Due to bug in Oxygen and Breeze at least on *buntu 16.04 LTS and some other distros, + // force the style and stylesheet again. Otherwise the window freezes. + // Ignore the Qt::WindowActive flag. + if((oldState & (Qt::WindowNoState | Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)) != + (newState & (Qt::WindowNoState | Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen))) + { + if(MusEGlobal::debugMsg) + fprintf(stderr, "TopWin::windowStateChanged oldState:%d newState:%d Calling updateThemeAndStyle()\n", int(oldState), int(newState)); + MusEGui::updateThemeAndStyle(true); + } +} TopWin* ToplevelList::findType(TopWin::ToplevelType type) const { diff -Nru muse-2.1.2/muse/cobject.h muse-3.0.2+ds1/muse/cobject.h --- muse-2.1.2/muse/cobject.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/cobject.h 2017-12-04 21:01:18.000000000 +0000 @@ -33,6 +33,7 @@ class QMdiSubWindow; class QFocusEvent; +class QCloseEvent; class QToolBar; class QAction; @@ -80,8 +81,15 @@ bool sharesToolsAndMenu() const { return _sharesToolsAndMenu; } const std::list& toolbars() { return _toolbars; } - void addToolBar(QToolBar* toolbar); - QToolBar* addToolBar(const QString& title); + virtual void addToolBar(QToolBar* toolbar); + virtual QToolBar* addToolBar(const QString& title); + virtual void addToolBarBreak(Qt::ToolBarArea area = Qt::TopToolBarArea); + virtual void insertToolBar(QToolBar*, QToolBar*); + virtual void insertToolBarBreak(QToolBar*); + virtual void removeToolBar(QToolBar*); + virtual void removeToolBarBreak(QToolBar*); + virtual void addToolBar(Qt::ToolBarArea, QToolBar*); + void resize(int w, int h); void resize(const QSize&); @@ -96,12 +104,6 @@ std::list _toolbars; bool _initalizing; - void insertToolBar(QToolBar*, QToolBar*); - void insertToolBarBreak(QToolBar*); - void removeToolBar(QToolBar*); - void removeToolBarBreak(QToolBar*); - void addToolBar(Qt::ToolBarArea, QToolBar*); - virtual QMdiSubWindow* createMdiWrapper(); static void initConfiguration(); @@ -142,6 +144,7 @@ void storeInitialState() const; virtual void setWindowTitle (const QString&); virtual void focusCanvas() { } + virtual void windowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState); }; diff -Nru muse-2.1.2/muse/conf.cpp muse-3.0.2+ds1/muse/conf.cpp --- muse-2.1.2/muse/conf.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/conf.cpp 2018-01-22 16:43:28.000000000 +0000 @@ -21,6 +21,11 @@ // //========================================================= +#include +#include +#include +#include + #include #include #include @@ -46,8 +51,10 @@ #include "pitchedit.h" #include "midiport.h" #include "mididev.h" +#include "instruments/minstrument.h" #include "driver/audiodev.h" #include "driver/jackmidi.h" +#include "driver/alsamidi.h" #include "xml.h" #include "waveedit.h" #include "midi.h" @@ -65,6 +72,7 @@ #include "amixer.h" #include "track.h" #include "plugin.h" +#include "filedialog.h" namespace MusECore { @@ -216,6 +224,74 @@ } //--------------------------------------------------------- +// readConfigMidiDevice +//--------------------------------------------------------- + +static void readConfigMidiDevice(Xml& xml) + { + QString device; + int rwFlags = 3; + int openFlags = 1; + int type = MidiDevice::ALSA_MIDI; + + for (;;) { + Xml::Token token = xml.parse(); + if (token == Xml::Error || token == Xml::End) + break; + QString tag = xml.s1(); + switch (token) { + case Xml::TagStart: + if (tag == "name") + device = xml.parse1(); + else if (tag == "type") + type = xml.parseInt(); + else if (tag == "openFlags") + openFlags = xml.parseInt(); + else if (tag == "rwFlags") // Jack midi devs need this. + rwFlags = xml.parseInt(); + else + xml.unknown("MidiDevice"); + break; + case Xml::Attribut: + break; + case Xml::TagEnd: + if (tag == "mididevice") { + MidiDevice* dev = MusEGlobal::midiDevices.find(device, type); + + if(!dev) + { + if(type == MidiDevice::JACK_MIDI) + { + if(MusEGlobal::debugMsg) + fprintf(stderr, "readConfigMidiDevice: creating jack midi device %s with rwFlags:%d\n", device.toLatin1().constData(), rwFlags); + dev = MidiJackDevice::createJackMidiDevice(device, rwFlags); + } +#ifdef ALSA_SUPPORT + else + if(type == MidiDevice::ALSA_MIDI) + { + if(MusEGlobal::debugMsg) + fprintf(stderr, "readConfigMidiDevice: creating ALSA midi device %s with rwFlags:%d\n", device.toLatin1().constData(), rwFlags); + dev = MidiAlsaDevice::createAlsaMidiDevice(device, rwFlags); + } +#endif + } + + if(MusEGlobal::debugMsg && !dev) + fprintf(stderr, "readConfigMidiDevice: device not found %s\n", device.toLatin1().constData()); + + if (dev) { + dev->setOpenFlags(openFlags); + } + return; + } + default: + break; + } + } + } + +//--------------------------------------------------------- // readConfigMidiPort //--------------------------------------------------------- @@ -223,12 +299,12 @@ { int idx = 0; QString device; - + // Let's be bold. New users have been confused by generic midi not enabling any patches and controllers. // I had said this may cause HW problems by sending out GM sysEx when really the HW might not be GM. // But this really needs to be done, one way or another. // FIXME: TODO: Make this user-configurable! - QString instrument("GM"); + QString instrument("GM"); int rwFlags = 3; int openFlags = 1; @@ -237,7 +313,8 @@ MidiSyncInfo tmpSi; int type = MidiDevice::ALSA_MIDI; - + bool pre_mididevice_ver_found = false; + for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) @@ -261,30 +338,43 @@ if (tag == "name") device = xml.parse1(); else if (tag == "type") + { + pre_mididevice_ver_found = true; type = xml.parseInt(); + } else if (tag == "record") { // old + pre_mididevice_ver_found = true; bool f = xml.parseInt(); if (f) openFlags |= 2; } else if (tag == "openFlags") + { + pre_mididevice_ver_found = true; openFlags = xml.parseInt(); - else if (tag == "rwFlags") // Jack midi devs need this. p4.0.41 + } + else if (tag == "rwFlags") // Jack midi devs need this. + { + pre_mididevice_ver_found = true; rwFlags = xml.parseInt(); + } else if (tag == "defaultInChans") dic = xml.parseInt(); else if (tag == "defaultOutChans") doc = xml.parseInt(); else if (tag == "midiSyncInfo") tmpSi.read(xml); - else if (tag == "instrument") { + else if (tag == "instrument") { // Obsolete instrument = xml.parse1(); //MusEGlobal::midiPorts[idx].setInstrument( // Moved below // registerMidiInstrument(instrument) // ); } else if (tag == "midithru") + { + pre_mididevice_ver_found = true; xml.parseInt(); // obsolete + } //else if (tag == "channel") { // readPortChannel(xml, idx); // Moved above // } @@ -308,7 +398,7 @@ idx = 0; } - MidiDevice* dev = MusEGlobal::midiDevices.find(device); + MidiDevice* dev = MusEGlobal::midiDevices.find(device, pre_mididevice_ver_found ? type : -1); if(!dev && type == MidiDevice::JACK_MIDI) { @@ -321,8 +411,10 @@ fprintf(stderr, "readConfigMidiPort: device not found %s\n", device.toLatin1().constData()); MidiPort* mp = &MusEGlobal::midiPorts[idx]; - - mp->setInstrument(registerMidiInstrument(instrument)); + + mp->setDefaultOutChannels(0); // reset output channel to take care of the case where no default is specified + + mp->changeInstrument(registerMidiInstrument(instrument)); if(dic != -1) // p4.0.17 Leave them alone unless set by song. mp->setDefaultInChannels(dic); if(doc != -1) @@ -338,8 +430,9 @@ mp->setFoundInSongFile(true); if (dev) { - dev->setOpenFlags(openFlags); - MusEGlobal::midiSeq->msgSetMidiDevice(mp, dev); + if(pre_mididevice_ver_found) + dev->setOpenFlags(openFlags); + MusEGlobal::audio->msgSetMidiDevice(mp, dev); } return; } @@ -404,6 +497,14 @@ MusEGlobal::accent2ClickVolume = xml.parseFloat(); else if (tag == "clickSamples") MusEGlobal::clickSamples = (MusEGlobal::ClickSamples)xml.parseInt(); + else if (tag == "beatSample") + MusEGlobal::config.beatSample = xml.parse1(); + else if (tag == "measSample") + MusEGlobal::config.measSample = xml.parse1(); + else if (tag == "accent1Sample") + MusEGlobal::config.accent1Sample = xml.parse1(); + else if (tag == "accent2Sample") + MusEGlobal::config.accent2Sample = xml.parse1(); else xml.unknown("Metronome"); break; @@ -431,6 +532,8 @@ case Xml::TagStart: if (tag == "metronom") loadConfigMetronom(xml); + else if (tag == "mididevice") + readConfigMidiDevice(xml); else if (tag == "midiport") readConfigMidiPort(xml, skipMidiPorts); else if (tag == "rcStop") @@ -580,16 +683,14 @@ if(p >= 0 && p < MidiSyncInfo::TYPE_END) { MusEGlobal::syncRecFilterPreset = MidiSyncInfo::SyncRecFilterPresetType(p); - if(MusEGlobal::midiSeq) - MusEGlobal::midiSeq->setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); + MusEGlobal::midiSyncContainer.setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); } } else if (tag == "syncRecTempoValQuant") { double qv = xml.parseDouble(); MusEGlobal::syncRecTempoValQuant = qv; - if(MusEGlobal::midiSeq) - MusEGlobal::midiSeq->setRecTempoValQuant(qv); + MusEGlobal::midiSyncContainer.setRecTempoValQuant(qv); } else if (tag == "mtcoffset") { QString qs(xml.parse1()); @@ -617,8 +718,50 @@ // ---- Global config stuff begins here ---- + else if (tag == "pluginLadspaPathList") + MusEGlobal::config.pluginLadspaPathList = xml.parse1().split(":", QString::SkipEmptyParts); + else if (tag == "pluginDssiPathList") + MusEGlobal::config.pluginDssiPathList = xml.parse1().split(":", QString::SkipEmptyParts); + else if (tag == "pluginVstPathList") + MusEGlobal::config.pluginVstPathList = xml.parse1().split(":", QString::SkipEmptyParts); + else if (tag == "pluginLinuxVstPathList") + MusEGlobal::config.pluginLinuxVstPathList = xml.parse1().split(":", QString::SkipEmptyParts); + else if (tag == "pluginLv2PathList") + MusEGlobal::config.pluginLv2PathList = xml.parse1().split(":", QString::SkipEmptyParts); + + else if (tag == "preferredRouteNameOrAlias") + MusEGlobal::config.preferredRouteNameOrAlias = static_cast(xml.parseInt()); + else if (tag == "routerExpandVertically") + MusEGlobal::config.routerExpandVertically = xml.parseInt(); + else if (tag == "routerGroupingChannels") + { + MusEGlobal::config.routerGroupingChannels = xml.parseInt(); + // TODO: For now we only support maximum two channels grouping. Zero is an error. + if(MusEGlobal::config.routerGroupingChannels < 1) + MusEGlobal::config.routerGroupingChannels = 1; + if(MusEGlobal::config.routerGroupingChannels > 2) + MusEGlobal::config.routerGroupingChannels = 2; + } + else if (tag == "fixFrozenMDISubWindows") + MusEGlobal::config.fixFrozenMDISubWindows = xml.parseInt(); else if (tag == "theme") MusEGlobal::config.style = xml.parse1(); + else if (tag == "autoSave") + MusEGlobal::config.autoSave = xml.parseInt(); + else if (tag == "scrollableSubMenus") + MusEGlobal::config.scrollableSubMenus = xml.parseInt(); + else if (tag == "liveWaveUpdate") + MusEGlobal::config.liveWaveUpdate = xml.parseInt(); + else if (tag == "preferKnobsVsSliders") + MusEGlobal::config.preferKnobsVsSliders = xml.parseInt(); + else if (tag == "showControlValues") + MusEGlobal::config.showControlValues = xml.parseInt(); + else if (tag == "monitorOnRecord") + MusEGlobal::config.monitorOnRecord = xml.parseInt(); + else if (tag == "lineEditStyleHack") + MusEGlobal::config.lineEditStyleHack = xml.parseInt(); + else if (tag == "preferMidiVolumeDb") + MusEGlobal::config.preferMidiVolumeDb = xml.parseInt(); else if (tag == "styleSheetFile") MusEGlobal::config.styleSheetFile = xml.parse1(); else if (tag == "useOldStyleStopShortCut") @@ -760,7 +903,8 @@ MusEGlobal::config.selectTrackBg = readColor(xml); else if (tag == "selectTrackFg") MusEGlobal::config.selectTrackFg = readColor(xml); - + else if (tag == "trackSectionDividerColor") + MusEGlobal::config.trackSectionDividerColor = readColor(xml); else if (tag == "mixerBg") MusEGlobal::config.mixerBg = readColor(xml); else if (tag == "midiTrackLabelBg") @@ -802,7 +946,38 @@ MusEGlobal::config.auxTrackBg = readColor(xml); else if (tag == "synthTrackBg") MusEGlobal::config.synthTrackBg = readColor(xml); - + + else if (tag == "sliderBarDefaultColor") + MusEGlobal::config.sliderBarDefaultColor = readColor(xml); + else if (tag == "sliderDefaultColor2") + MusEGlobal::config.sliderDefaultColor = readColor(xml); + else if (tag == "panSliderColor2") + MusEGlobal::config.panSliderColor = readColor(xml); + else if (tag == "gainSliderColor2") + MusEGlobal::config.gainSliderColor = readColor(xml); + else if (tag == "auxSliderColor2") + MusEGlobal::config.auxSliderColor = readColor(xml); + else if (tag == "audioVolumeSliderColor2") + MusEGlobal::config.audioVolumeSliderColor = readColor(xml); + else if (tag == "midiVolumeSliderColor2") + MusEGlobal::config.midiVolumeSliderColor = readColor(xml); + else if (tag == "audioControllerSliderDefaultColor2") + MusEGlobal::config.audioControllerSliderDefaultColor = readColor(xml); + else if (tag == "audioPropertySliderDefaultColor2") + MusEGlobal::config.audioPropertySliderDefaultColor = readColor(xml); + else if (tag == "midiControllerSliderDefaultColor2") + MusEGlobal::config.midiControllerSliderDefaultColor = readColor(xml); + else if (tag == "midiPropertySliderDefaultColor2") + MusEGlobal::config.midiPropertySliderDefaultColor = readColor(xml); + else if (tag == "midiPatchReadoutColor") + MusEGlobal::config.midiPatchReadoutColor = readColor(xml); + else if (tag == "audioMeterPrimaryColor") + MusEGlobal::config.audioMeterPrimaryColor = readColor(xml); + else if (tag == "midiMeterPrimaryColor") + MusEGlobal::config.midiMeterPrimaryColor = readColor(xml); + else if (tag == "rackItemBackgroundColor") + MusEGlobal::config.rackItemBackgroundColor = readColor(xml); + else if (tag == "extendedMidi") MusEGlobal::config.extendedMidi = xml.parseInt(); else if (tag == "midiExportDivision") @@ -815,6 +990,8 @@ MusEGlobal::config.exp2ByteTimeSigs = xml.parseInt(); else if (tag == "expOptimNoteOffs") MusEGlobal::config.expOptimNoteOffs = xml.parseInt(); + else if (tag == "expRunningStatus") + MusEGlobal::config.expRunningStatus = xml.parseInt(); else if (tag == "importMidiSplitParts") MusEGlobal::config.importMidiSplitParts = xml.parseInt(); else if (tag == "importDevNameMetas") @@ -822,21 +999,15 @@ else if (tag == "importInstrNameMetas") MusEGlobal::config.importInstrNameMetas = xml.parseInt(); else if (tag == "exportPortsDevices") - { - int i = xml.parseInt(); - if(i >= MusEGlobal::EXPORT_PORTS_DEVICES_END) - i = MusEGlobal::EXPORT_PORTS_DEVICES_ALL; - MusEGlobal::config.exportPortsDevices = MusEGlobal::ExportPortsDevices_t(i); - } + MusEGlobal::config.exportPortsDevices = xml.parseInt(); else if (tag == "exportPortDeviceSMF0") MusEGlobal::config.exportPortDeviceSMF0 = xml.parseInt(); + else if (tag == "exportDrumMapOverrides") + MusEGlobal::config.exportDrumMapOverrides = xml.parseInt(); + else if (tag == "exportChannelOverridesToNewTrack") + MusEGlobal::config.exportChannelOverridesToNewTrack = xml.parseInt(); else if (tag == "exportModeInstr") - { - int i = xml.parseInt(); - if(i >= MusEGlobal::EXPORT_MODE_INSTR_END) - i = MusEGlobal::EXPORT_MODE_INSTR_ALL; - MusEGlobal::config.exportModeInstr = MusEGlobal::ExportModeInstr_t(i); - } + MusEGlobal::config.exportModeInstr = xml.parseInt(); else if (tag == "importMidiDefaultInstr") MusEGlobal::config.importMidiDefaultInstr = xml.parse1(); @@ -868,32 +1039,39 @@ MusEGlobal::config.rulerCurrent = readColor(xml); else if (tag == "waveNonselectedPart") - MusEGlobal::config.rulerCurrent = readColor(xml); + MusEGlobal::config.waveNonselectedPart = readColor(xml); else if (tag == "wavePeakColor") - MusEGlobal::config.rulerCurrent = readColor(xml); + MusEGlobal::config.wavePeakColor = readColor(xml); else if (tag == "waveRmsColor") - MusEGlobal::config.rulerCurrent = readColor(xml); + MusEGlobal::config.waveRmsColor = readColor(xml); else if (tag == "wavePeakColorSelected") - MusEGlobal::config.rulerCurrent = readColor(xml); + MusEGlobal::config.wavePeakColorSelected = readColor(xml); else if (tag == "waveRmsColorSelected") - MusEGlobal::config.rulerCurrent = readColor(xml); + MusEGlobal::config.waveRmsColorSelected = readColor(xml); else if (tag == "partWaveColorPeak") - MusEGlobal::config.rulerCurrent = readColor(xml); + MusEGlobal::config.partWaveColorPeak = readColor(xml); else if (tag == "partWaveColorRms") - MusEGlobal::config.rulerCurrent = readColor(xml); + MusEGlobal::config.partWaveColorRms = readColor(xml); else if (tag == "partMidiDarkEventColor") - MusEGlobal::config.rulerCurrent = readColor(xml); + MusEGlobal::config.partMidiDarkEventColor = readColor(xml); else if (tag == "partMidiLightEventColor") - MusEGlobal::config.rulerCurrent = readColor(xml); + MusEGlobal::config.partMidiLightEventColor = readColor(xml); else if (tag == "midiCanvasBackgroundColor") MusEGlobal::config.midiCanvasBg = readColor(xml); + else if (tag == "midiCanvasBeatColor") + MusEGlobal::config.midiCanvasBeatColor = readColor(xml); + else if (tag == "midiCanvasBarColor") + MusEGlobal::config.midiCanvasBarColor = readColor(xml); else if (tag == "midiControllerViewBackgroundColor") MusEGlobal::config.midiControllerViewBg = readColor(xml); else if (tag == "drumListBackgroundColor") MusEGlobal::config.drumListBg = readColor(xml); + else if (tag == "maxAliasedPointSize") + MusEGlobal::config.maxAliasedPointSize = xml.parseInt(); + //else if (tag == "midiSyncInfo") // readConfigMidiSyncInfo(xml); /* Obsolete. done by song's toplevel list. arrangerview also handles arranger. @@ -929,20 +1107,32 @@ MusEGui::read_function_dialog_config(xml); else if (tag == "shortcuts") MusEGui::readShortCuts(xml); + else if (tag == "enableAlsaMidiDriver") + MusEGlobal::config.enableAlsaMidiDriver = xml.parseInt(); else if (tag == "division") MusEGlobal::config.division = xml.parseInt(); else if (tag == "guiDivision") MusEGlobal::config.guiDivision = xml.parseInt(); else if (tag == "rtcTicks") MusEGlobal::config.rtcTicks = xml.parseInt(); + else if (tag == "curMidiSyncInPort") + MusEGlobal::config.curMidiSyncInPort = xml.parseInt(); else if (tag == "midiSendInit") MusEGlobal::config.midiSendInit = xml.parseInt(); else if (tag == "warnInitPending") MusEGlobal::config.warnInitPending = xml.parseInt(); else if (tag == "midiSendCtlDefaults") MusEGlobal::config.midiSendCtlDefaults = xml.parseInt(); + else if (tag == "midiSendNullParameters") + MusEGlobal::config.midiSendNullParameters = xml.parseInt(); + else if (tag == "midiOptimizeControllers") + MusEGlobal::config.midiOptimizeControllers = xml.parseInt(); else if (tag == "warnIfBadTiming") MusEGlobal::config.warnIfBadTiming = xml.parseInt(); + else if (tag == "warnOnFileVersions") + MusEGlobal::config.warnOnFileVersions = xml.parseInt(); + else if (tag == "lv2UiBehavior") + MusEGlobal::config.lv2UiBehavior = static_cast(xml.parseInt()); else if (tag == "minMeter") MusEGlobal::config.minMeter = xml.parseInt(); else if (tag == "minSlider") @@ -957,10 +1147,13 @@ MusEGlobal::config.useOutputLimiter = xml.parseInt(); else if (tag == "vstInPlace") MusEGlobal::config.vstInPlace = xml.parseInt(); - else if (tag == "dummyAudioSampleRate") - MusEGlobal::config.dummyAudioSampleRate = xml.parseInt(); - else if (tag == "dummyAudioBufSize") - MusEGlobal::config.dummyAudioBufSize = xml.parseInt(); + else if (tag == "deviceAudioSampleRate") + MusEGlobal::config.deviceAudioSampleRate = xml.parseInt(); + else if (tag == "deviceAudioBufSize") + MusEGlobal::config.deviceAudioBufSize = xml.parseInt(); + else if (tag == "deviceAudioBackend") + MusEGlobal::config.deviceAudioBackend = xml.parseInt(); + else if (tag == "minControlProcessPeriod") MusEGlobal::config.minControlProcessPeriod = xml.parseUInt(); else if (tag == "guiRefresh") @@ -972,7 +1165,7 @@ else if (tag == "startSong") MusEGlobal::config.startSong = xml.parse1(); else if (tag == "startSongLoadConfig") - MusEGlobal::config.startSongLoadConfig = xml.parseInt(); + MusEGlobal::config.startSongLoadConfig = xml.parseInt(); else if (tag == "newDrumRecordCondition") MusEGlobal::config.newDrumRecordCondition = MusECore::newDrumRecordCondition_t(xml.parseInt()); else if (tag == "projectBaseFolder") @@ -991,6 +1184,12 @@ MusEGlobal::config.addHiddenTracks = xml.parseInt(); else if (tag == "drumTrackPreference") MusEGlobal::config.drumTrackPreference = (MusEGlobal::drumTrackPreference_t) xml.parseInt(); + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + else if (tag == "drummapOverrides") + MusEGlobal::workingDrumMapInstrumentList.read(xml); +#endif + else if (tag == "unhideTracks") MusEGlobal::config.unhideTracks = xml.parseInt(); else if (tag == "smartFocus") @@ -1001,6 +1200,11 @@ MusEGlobal::config.velocityPerNote = xml.parseInt(); else if (tag == "plugin_groups") MusEGlobal::readPluginGroupConfiguration(xml); + else if (tag == "mixdownPath") + MusEGlobal::config.mixdownPath = xml.parse1(); + else if (tag == "showNoteNamesInPianoRoll") + MusEGlobal::config.showNoteNamesInPianoRoll = xml.parseInt(); + // ---- the following only skips obsolete entries ---- else if ((tag == "arranger") || (tag == "geometryPianoroll") || (tag == "geometryDrumedit")) @@ -1070,8 +1274,12 @@ bool readConfiguration(const char *configFile) { + QByteArray ba; if (configFile == NULL) - configFile = MusEGlobal::configName.toLatin1().constData(); + { + ba = MusEGlobal::configName.toLatin1(); + configFile = ba.constData(); + } printf("Config File <%s>\n", configFile); FILE* f = fopen(configFile, "r"); @@ -1111,6 +1319,13 @@ } break; case Xml::TagEnd: + if(!xml.isVersionEqualToLatest()) + { + fprintf(stderr, "\n***WARNING***\nLoaded config file version is %d.%d\nCurrent version is %d.%d\n" + "Conversions may be applied!\n\n", + xml.majorVersion(), xml.minorVersion(), + xml.latestMajorVersion(), xml.latestMinorVersion()); + } if (!skipmode && tag == "muse") { fclose(f); return false; @@ -1154,6 +1369,10 @@ xml.floatTag(level, "accent1ClickVolume", MusEGlobal::accent1ClickVolume); xml.floatTag(level, "accent2ClickVolume", MusEGlobal::accent2ClickVolume); xml.intTag(level, "clickSamples", MusEGlobal::clickSamples); + xml.strTag(level, "beatSample", MusEGlobal::config.beatSample); + xml.strTag(level, "measSample", MusEGlobal::config.measSample); + xml.strTag(level, "accent1Sample", MusEGlobal::config.accent1Sample); + xml.strTag(level, "accent2Sample", MusEGlobal::config.accent2Sample); xml.tag(level--, "/metronom"); xml.intTag(level, "rcEnable", MusEGlobal::rcEnable); @@ -1164,6 +1383,30 @@ xml.intTag(level, "rcSteprec", MusEGlobal::rcSteprecNote); if (writePortInfo) { + for(iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) + { + MidiDevice* dev = *imd; + // TODO: For now, support only jack midi devices here. ALSA devices are different. + //if(dev->deviceType() != MidiDevice::JACK_MIDI) + if(dev->deviceType() != MidiDevice::JACK_MIDI && dev->deviceType() != MidiDevice::ALSA_MIDI) + continue; + + xml.tag(level++, "mididevice"); + xml.strTag(level, "name", dev->name()); + + if(dev->deviceType() != MidiDevice::ALSA_MIDI) + xml.intTag(level, "type", dev->deviceType()); + + // Synths will not have been created yet when this is read! So, synthIs now store their own openFlags. + if(dev->openFlags() != 1) + xml.intTag(level, "openFlags", dev->openFlags()); + + if(dev->deviceType() == MidiDevice::JACK_MIDI) + xml.intTag(level, "rwFlags", dev->rwFlags()); // Need this. Jack midi devs are created by app. p4.0.41 + + xml.etag(level--, "mididevice"); + } + // // write information about all midi ports, their assigned // instruments and all managed midi controllers @@ -1208,20 +1451,11 @@ xml.intTag(level, "defaultOutChans", mport->defaultOutChannels()); if(!mport->instrument()->iname().isEmpty() && // Tim. - (mport->instrument()->iname() != "GM")) // FIXME: TODO: Make this user configurable. - //(mport->instrument()->midiType() != MT_GM)) + (mport->instrument()->iname() != "GM")) // FIXME: TODO: Make this user configurable. xml.strTag(level, "instrument", mport->instrument()->iname()); if (dev) { xml.strTag(level, "name", dev->name()); - - if(dev->deviceType() != MidiDevice::ALSA_MIDI) - xml.intTag(level, "type", dev->deviceType()); - - xml.intTag(level, "openFlags", dev->openFlags()); - - if(dev->deviceType() == MidiDevice::JACK_MIDI) - xml.intTag(level, "rwFlags", dev->rwFlags()); // Need this. Jack midi devs are created by app. p4.0.41 } mport->syncInfo().write(level, xml); // write out registered controller for all channels @@ -1260,6 +1494,96 @@ } xml.tag(level, "/sequencer"); } + + +static void writeConfigurationColors(int level, MusECore::Xml& xml, bool partColorNames = true) +{ + for (int i = 0; i < 16; ++i) { + xml.colorTag(level, QString("palette") + QString::number(i), MusEGlobal::config.palette[i]); + } + + for (int i = 0; i < NUM_PARTCOLORS; ++i) { + xml.colorTag(level, QString("partColor") + QString::number(i), MusEGlobal::config.partColors[i]); + } + + if(partColorNames) + { + for (int i = 0; i < NUM_PARTCOLORS; ++i) { + xml.strTag(level, QString("partColorName") + QString::number(i), MusEGlobal::config.partColorNames[i]); + } + } + + xml.colorTag(level, "partCanvasBg", MusEGlobal::config.partCanvasBg); + xml.colorTag(level, "trackBg", MusEGlobal::config.trackBg); + xml.colorTag(level, "selectTrackBg", MusEGlobal::config.selectTrackBg); + xml.colorTag(level, "selectTrackFg", MusEGlobal::config.selectTrackFg); + xml.colorTag(level, "trackSectionDividerColor", MusEGlobal::config.trackSectionDividerColor); + + xml.colorTag(level, "mixerBg", MusEGlobal::config.mixerBg); + xml.colorTag(level, "midiTrackLabelBg", MusEGlobal::config.midiTrackLabelBg); + xml.colorTag(level, "drumTrackLabelBg2", MusEGlobal::config.drumTrackLabelBg); + xml.colorTag(level, "newDrumTrackLabelBg2",MusEGlobal::config.newDrumTrackLabelBg); + xml.colorTag(level, "waveTrackLabelBg", MusEGlobal::config.waveTrackLabelBg); + xml.colorTag(level, "outputTrackLabelBg", MusEGlobal::config.outputTrackLabelBg); + xml.colorTag(level, "inputTrackLabelBg", MusEGlobal::config.inputTrackLabelBg); + xml.colorTag(level, "groupTrackLabelBg", MusEGlobal::config.groupTrackLabelBg); + xml.colorTag(level, "auxTrackLabelBg2", MusEGlobal::config.auxTrackLabelBg); + xml.colorTag(level, "synthTrackLabelBg", MusEGlobal::config.synthTrackLabelBg); + + xml.colorTag(level, "midiTrackBg", MusEGlobal::config.midiTrackBg); + xml.colorTag(level, "ctrlGraphFg", MusEGlobal::config.ctrlGraphFg); + xml.colorTag(level, "drumTrackBg", MusEGlobal::config.drumTrackBg); + xml.colorTag(level, "newDrumTrackBg",MusEGlobal::config.newDrumTrackBg); + xml.colorTag(level, "waveTrackBg", MusEGlobal::config.waveTrackBg); + xml.colorTag(level, "outputTrackBg", MusEGlobal::config.outputTrackBg); + xml.colorTag(level, "inputTrackBg", MusEGlobal::config.inputTrackBg); + xml.colorTag(level, "groupTrackBg", MusEGlobal::config.groupTrackBg); + xml.colorTag(level, "auxTrackBg", MusEGlobal::config.auxTrackBg); + xml.colorTag(level, "synthTrackBg", MusEGlobal::config.synthTrackBg); + + xml.colorTag(level, "sliderBarDefaultColor", MusEGlobal::config.sliderBarDefaultColor); + xml.colorTag(level, "sliderDefaultColor2", MusEGlobal::config.sliderDefaultColor); + xml.colorTag(level, "panSliderColor2", MusEGlobal::config.panSliderColor); + xml.colorTag(level, "gainSliderColor2", MusEGlobal::config.gainSliderColor); + xml.colorTag(level, "auxSliderColor2", MusEGlobal::config.auxSliderColor); + xml.colorTag(level, "audioVolumeSliderColor2", MusEGlobal::config.audioVolumeSliderColor); + xml.colorTag(level, "midiVolumeSliderColor2", MusEGlobal::config.midiVolumeSliderColor); + xml.colorTag(level, "audioControllerSliderDefaultColor2", MusEGlobal::config.audioControllerSliderDefaultColor); + xml.colorTag(level, "audioPropertySliderDefaultColor2", MusEGlobal::config.audioPropertySliderDefaultColor); + xml.colorTag(level, "midiControllerSliderDefaultColor2", MusEGlobal::config.midiControllerSliderDefaultColor); + xml.colorTag(level, "midiPropertySliderDefaultColor2", MusEGlobal::config.midiPropertySliderDefaultColor); + xml.colorTag(level, "midiPatchReadoutColor", MusEGlobal::config.midiPatchReadoutColor); + xml.colorTag(level, "audioMeterPrimaryColor", MusEGlobal::config.audioMeterPrimaryColor); + xml.colorTag(level, "midiMeterPrimaryColor", MusEGlobal::config.midiMeterPrimaryColor); + xml.colorTag(level, "rackItemBackgroundColor", MusEGlobal::config.rackItemBackgroundColor); + + xml.colorTag(level, "transportHandleColor", MusEGlobal::config.transportHandleColor); + xml.colorTag(level, "bigtimeForegroundcolor", MusEGlobal::config.bigTimeForegroundColor); + xml.colorTag(level, "bigtimeBackgroundcolor", MusEGlobal::config.bigTimeBackgroundColor); + xml.colorTag(level, "waveEditBackgroundColor", MusEGlobal::config.waveEditBackgroundColor); + xml.colorTag(level, "rulerBackgroundColor", MusEGlobal::config.rulerBg); + xml.colorTag(level, "rulerForegroundColor", MusEGlobal::config.rulerFg); + xml.colorTag(level, "rulerCurrentColor", MusEGlobal::config.rulerCurrent); + + xml.colorTag(level, "waveNonselectedPart", MusEGlobal::config.waveNonselectedPart); + xml.colorTag(level, "wavePeakColor", MusEGlobal::config.wavePeakColor); + xml.colorTag(level, "waveRmsColor", MusEGlobal::config.waveRmsColor); + xml.colorTag(level, "wavePeakColorSelected", MusEGlobal::config.wavePeakColorSelected); + xml.colorTag(level, "waveRmsColorSelected", MusEGlobal::config.waveRmsColorSelected); + + xml.colorTag(level, "partWaveColorPeak", MusEGlobal::config.partWaveColorPeak); + xml.colorTag(level, "partWaveColorRms", MusEGlobal::config.partWaveColorRms); + xml.colorTag(level, "partMidiDarkEventColor", MusEGlobal::config.partMidiDarkEventColor); + xml.colorTag(level, "partMidiLightEventColor", MusEGlobal::config.partMidiLightEventColor); + + xml.colorTag(level, "midiCanvasBackgroundColor", MusEGlobal::config.midiCanvasBg); + xml.colorTag(level, "midiCanvasBeatColor", MusEGlobal::config.midiCanvasBeatColor); + xml.colorTag(level, "midiCanvasBarColor", MusEGlobal::config.midiCanvasBarColor); + + xml.colorTag(level, "midiControllerViewBackgroundColor", MusEGlobal::config.midiControllerViewBg); + xml.colorTag(level, "drumListBackgroundColor", MusEGlobal::config.drumListBg); +} + } // namespace MusECore @@ -1279,22 +1603,97 @@ } MusECore::Xml xml(f); xml.header(); - xml.tag(0, "muse version=\"2.0\""); + xml.nput(0, "\n", xml.latestMajorVersion(), xml.latestMinorVersion()); writeGlobalConfiguration(1, xml); xml.tag(1, "/muse"); fclose(f); } +bool MusE::loadConfigurationColors(QWidget* parent) +{ + if(!parent) + parent = this; + //QString file = QFileDialog::getOpenFileName(parent, tr("Load configuration colors"), QString(), tr("MusE color configuration files *.cfc (*.cfc)")); + QString file = MusEGui::getOpenFileName(QString("themes"), MusEGlobal::colors_config_file_pattern, this, + tr("Load configuration colors"), NULL, MusEGui::MFileDialog::GLOBAL_VIEW); + + if(file.isEmpty()) + return false; + + if(QMessageBox::question(parent, QString("MusE"), + tr("Color settings will immediately be replaced with any found in the file.\nAre you sure you want to proceed?"), tr("&Ok"), tr("&Cancel"), + QString::null, 0, 1 ) == 1) + return false; + + // Read, and return if error. + if(MusECore::readConfiguration(file.toLatin1().constData())) // True if error. + { + fprintf(stderr, "MusE::loadConfigurationColors failed\n"); + return false; + } + // Notify app, and write into configuration file. + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + changeConfig(true); + return true; +} + +bool MusE::saveConfigurationColors(QWidget* parent) +{ + if(!parent) + parent = this; + QString file = MusEGui::getSaveFileName(QString("themes"), MusEGlobal::colors_config_file_pattern, this, + tr("Save configuration colors")); + + if(file.isEmpty()) + return false; + + + if(QFile::exists(file)) + { + if(QMessageBox::question(parent, QString("MusE"), + tr("File exists.\nDo you want to overwrite it?"), tr("&Ok"), tr("&Cancel"), + QString::null, 0, 1 ) == 1) + return false; + } + FILE* f = fopen(file.toLatin1().constData(), "w"); + if (f == 0) + { + fprintf(stderr, "save configuration colors to <%s> failed: %s\n", + file.toLatin1().constData(), strerror(errno)); + return false; + } + MusECore::Xml xml(f); + xml.header(); + xml.nput(0, "\n", xml.latestMajorVersion(), xml.latestMinorVersion()); + xml.tag(1, "configuration"); + MusECore::writeConfigurationColors(2, xml, false); // Don't save part colour names. + xml.etag(1, "configuration"); + xml.tag(0, "/muse"); + fclose(f); + return true; +} + void MusE::writeGlobalConfiguration(int level, MusECore::Xml& xml) const { xml.tag(level++, "configuration"); + xml.strTag(level, "pluginLadspaPathList", MusEGlobal::config.pluginLadspaPathList.join(":")); + xml.strTag(level, "pluginDssiPathList", MusEGlobal::config.pluginDssiPathList.join(":")); + xml.strTag(level, "pluginVstPathList", MusEGlobal::config.pluginVstPathList.join(":")); + xml.strTag(level, "pluginLinuxVstPathList", MusEGlobal::config.pluginLinuxVstPathList.join(":")); + xml.strTag(level, "pluginLv2PathList", MusEGlobal::config.pluginLv2PathList.join(":")); + + xml.intTag(level, "enableAlsaMidiDriver", MusEGlobal::config.enableAlsaMidiDriver); xml.intTag(level, "division", MusEGlobal::config.division); xml.intTag(level, "rtcTicks", MusEGlobal::config.rtcTicks); + xml.intTag(level, "curMidiSyncInPort", MusEGlobal::config.curMidiSyncInPort); xml.intTag(level, "midiSendInit", MusEGlobal::config.midiSendInit); xml.intTag(level, "warnInitPending", MusEGlobal::config.warnInitPending); xml.intTag(level, "midiSendCtlDefaults", MusEGlobal::config.midiSendCtlDefaults); + xml.intTag(level, "midiSendNullParameters", MusEGlobal::config.midiSendNullParameters); + xml.intTag(level, "midiOptimizeControllers", MusEGlobal::config.midiOptimizeControllers); xml.intTag(level, "warnIfBadTiming", MusEGlobal::config.warnIfBadTiming); + xml.intTag(level, "warnOnFileVersions", MusEGlobal::config.warnOnFileVersions); xml.intTag(level, "minMeter", MusEGlobal::config.minMeter); xml.doubleTag(level, "minSlider", MusEGlobal::config.minSlider); xml.intTag(level, "freewheelMode", MusEGlobal::config.freewheelMode); @@ -1302,8 +1701,12 @@ xml.intTag(level, "didYouKnow", MusEGlobal::config.showDidYouKnow); xml.intTag(level, "outputLimiter", MusEGlobal::config.useOutputLimiter); xml.intTag(level, "vstInPlace", MusEGlobal::config.vstInPlace); - xml.intTag(level, "dummyAudioBufSize", MusEGlobal::config.dummyAudioBufSize); - xml.intTag(level, "dummyAudioSampleRate", MusEGlobal::config.dummyAudioSampleRate); + + xml.intTag(level, "deviceAudioBufSize", MusEGlobal::config.deviceAudioBufSize); + xml.intTag(level, "deviceAudioSampleRate", MusEGlobal::config.deviceAudioSampleRate); + xml.intTag(level, "deviceAudioBackend", MusEGlobal::config.deviceAudioBackend); + + xml.uintTag(level, "minControlProcessPeriod", MusEGlobal::config.minControlProcessPeriod); xml.intTag(level, "guiRefresh", MusEGlobal::config.guiRefresh); @@ -1312,6 +1715,7 @@ xml.intTag(level, "guiDivision", MusEGlobal::config.guiDivision); xml.strTag(level, "copyright", MusEGlobal::config.copyright); xml.intTag(level, "smfFormat", MusEGlobal::config.smfFormat); + xml.intTag(level, "expRunningStatus", MusEGlobal::config.expRunningStatus); xml.intTag(level, "exp2ByteTimeSigs", MusEGlobal::config.exp2ByteTimeSigs); xml.intTag(level, "expOptimNoteOffs", MusEGlobal::config.expOptimNoteOffs); xml.intTag(level, "importMidiSplitParts", MusEGlobal::config.importMidiSplitParts); @@ -1319,6 +1723,8 @@ xml.intTag(level, "importInstrNameMetas", MusEGlobal::config.importInstrNameMetas); xml.intTag(level, "exportPortsDevices", MusEGlobal::config.exportPortsDevices); xml.intTag(level, "exportPortDeviceSMF0", MusEGlobal::config.exportPortDeviceSMF0); + xml.intTag(level, "exportDrumMapOverrides", MusEGlobal::config.exportDrumMapOverrides); + xml.intTag(level, "exportChannelOverridesToNewTrack", MusEGlobal::config.exportChannelOverridesToNewTrack); xml.intTag(level, "exportModeInstr", MusEGlobal::config.exportModeInstr); xml.strTag(level, "importMidiDefaultInstr", MusEGlobal::config.importMidiDefaultInstr); xml.intTag(level, "startMode", MusEGlobal::config.startMode); @@ -1337,7 +1743,13 @@ xml.intTag(level, "midiFilterCtrl3", MusEGlobal::midiFilterCtrl3); xml.intTag(level, "midiFilterCtrl4", MusEGlobal::midiFilterCtrl4); + xml.intTag(level, "preferredRouteNameOrAlias", static_cast(MusEGlobal::config.preferredRouteNameOrAlias)); + xml.intTag(level, "routerExpandVertically", MusEGlobal::config.routerExpandVertically); + xml.intTag(level, "routerGroupingChannels", MusEGlobal::config.routerGroupingChannels); + + xml.intTag(level, "fixFrozenMDISubWindows", MusEGlobal::config.fixFrozenMDISubWindows); xml.strTag(level, "theme", MusEGlobal::config.style); + xml.intTag(level, "autoSave", MusEGlobal::config.autoSave); xml.strTag(level, "styleSheetFile", MusEGlobal::config.styleSheetFile); xml.strTag(level, "externalWavEditor", MusEGlobal::config.externalWavEditor); xml.intTag(level, "useOldStyleStopShortCut", MusEGlobal::config.useOldStyleStopShortCut); @@ -1353,6 +1765,10 @@ xml.intTag(level, "addHiddenTracks", MusEGlobal::config.addHiddenTracks); xml.intTag(level, "drumTrackPreference", MusEGlobal::config.drumTrackPreference); +#ifdef _USE_INSTRUMENT_OVERRIDES_ + MusECore::midiInstruments.writeDrummapOverrides(level, xml); +#endif + xml.intTag(level, "waveTracksVisible", MusECore::WaveTrack::visible()); xml.intTag(level, "auxTracksVisible", MusECore::AudioAux::visible()); xml.intTag(level, "groupTracksVisible", MusECore::AudioGroup::visible()); @@ -1361,60 +1777,25 @@ xml.intTag(level, "outputTracksVisible", MusECore::AudioOutput::visible()); xml.intTag(level, "synthTracksVisible", MusECore::SynthI::visible()); xml.intTag(level, "trackHeight", MusEGlobal::config.trackHeight); + xml.intTag(level, "scrollableSubMenus", MusEGlobal::config.scrollableSubMenus); + xml.intTag(level, "liveWaveUpdate", MusEGlobal::config.liveWaveUpdate); + xml.intTag(level, "preferKnobsVsSliders", MusEGlobal::config.preferKnobsVsSliders); + xml.intTag(level, "showControlValues", MusEGlobal::config.showControlValues); + xml.intTag(level, "monitorOnRecord", MusEGlobal::config.monitorOnRecord); + xml.intTag(level, "lineEditStyleHack", MusEGlobal::config.lineEditStyleHack); + xml.intTag(level, "preferMidiVolumeDb", MusEGlobal::config.preferMidiVolumeDb); + xml.intTag(level, "lv2UiBehavior", static_cast(MusEGlobal::config.lv2UiBehavior)); + xml.strTag(level, "mixdownPath", MusEGlobal::config.mixdownPath); + xml.intTag(level, "showNoteNamesInPianoRoll", MusEGlobal::config.showNoteNamesInPianoRoll); for (int i = 0; i < NUM_FONTS; ++i) { - char buffer[32]; - sprintf(buffer, "font%d", i); - xml.strTag(level, buffer, MusEGlobal::config.fonts[i].toString()); + xml.strTag(level, QString("font") + QString::number(i), MusEGlobal::config.fonts[i].toString()); } xml.intTag(level, "globalAlphaBlend", MusEGlobal::config.globalAlphaBlend); - for (int i = 0; i < 16; ++i) { - char buffer[32]; - sprintf(buffer, "palette%d", i); - xml.colorTag(level, buffer, MusEGlobal::config.palette[i]); - } - - for (int i = 0; i < NUM_PARTCOLORS; ++i) { - char buffer[32]; - sprintf(buffer, "partColor%d", i); - xml.colorTag(level, buffer, MusEGlobal::config.partColors[i]); - } - - for (int i = 0; i < NUM_PARTCOLORS; ++i) { - char buffer[32]; - sprintf(buffer, "partColorName%d", i); - xml.strTag(level, buffer, MusEGlobal::config.partColorNames[i]); - } - - xml.colorTag(level, "partCanvasBg", MusEGlobal::config.partCanvasBg); - xml.colorTag(level, "trackBg", MusEGlobal::config.trackBg); - xml.colorTag(level, "selectTrackBg", MusEGlobal::config.selectTrackBg); - xml.colorTag(level, "selectTrackFg", MusEGlobal::config.selectTrackFg); + MusECore::writeConfigurationColors(level, xml); - xml.colorTag(level, "mixerBg", MusEGlobal::config.mixerBg); - xml.colorTag(level, "midiTrackLabelBg", MusEGlobal::config.midiTrackLabelBg); - xml.colorTag(level, "drumTrackLabelBg2", MusEGlobal::config.drumTrackLabelBg); - xml.colorTag(level, "newDrumTrackLabelBg2",MusEGlobal::config.newDrumTrackLabelBg); - xml.colorTag(level, "waveTrackLabelBg", MusEGlobal::config.waveTrackLabelBg); - xml.colorTag(level, "outputTrackLabelBg", MusEGlobal::config.outputTrackLabelBg); - xml.colorTag(level, "inputTrackLabelBg", MusEGlobal::config.inputTrackLabelBg); - xml.colorTag(level, "groupTrackLabelBg", MusEGlobal::config.groupTrackLabelBg); - xml.colorTag(level, "auxTrackLabelBg2", MusEGlobal::config.auxTrackLabelBg); - xml.colorTag(level, "synthTrackLabelBg", MusEGlobal::config.synthTrackLabelBg); - - xml.colorTag(level, "midiTrackBg", MusEGlobal::config.midiTrackBg); - xml.colorTag(level, "ctrlGraphFg", MusEGlobal::config.ctrlGraphFg); - xml.colorTag(level, "drumTrackBg", MusEGlobal::config.drumTrackBg); - xml.colorTag(level, "newDrumTrackBg",MusEGlobal::config.newDrumTrackBg); - xml.colorTag(level, "waveTrackBg", MusEGlobal::config.waveTrackBg); - xml.colorTag(level, "outputTrackBg", MusEGlobal::config.outputTrackBg); - xml.colorTag(level, "inputTrackBg", MusEGlobal::config.inputTrackBg); - xml.colorTag(level, "groupTrackBg", MusEGlobal::config.groupTrackBg); - xml.colorTag(level, "auxTrackBg", MusEGlobal::config.auxTrackBg); - xml.colorTag(level, "synthTrackBg", MusEGlobal::config.synthTrackBg); - xml.intTag(level, "mtctype", MusEGlobal::mtcType); xml.nput(level, "%02d:%02d:%02d:%02d:%02d\n", MusEGlobal::mtcOffset.h(), MusEGlobal::mtcOffset.m(), MusEGlobal::mtcOffset.s(), @@ -1440,29 +1821,8 @@ xml.strTag(level, "canvasBgPixmap", MusEGlobal::config.canvasBgPixmap); xml.strTag(level, "canvasCustomBgList", MusEGlobal::config.canvasCustomBgList.join(";")); - xml.colorTag(level, "transportHandleColor", MusEGlobal::config.transportHandleColor); - xml.colorTag(level, "bigtimeForegroundcolor", MusEGlobal::config.bigTimeForegroundColor); - xml.colorTag(level, "bigtimeBackgroundcolor", MusEGlobal::config.bigTimeBackgroundColor); - xml.colorTag(level, "waveEditBackgroundColor", MusEGlobal::config.waveEditBackgroundColor); - xml.colorTag(level, "rulerBackgroundColor", MusEGlobal::config.rulerBg); - xml.colorTag(level, "rulerForegroundColor", MusEGlobal::config.rulerFg); - xml.colorTag(level, "rulerCurrentColor", MusEGlobal::config.rulerCurrent); - - xml.colorTag(level, "waveNonselectedPart", MusEGlobal::config.waveNonselectedPart); - xml.colorTag(level, "wavePeakColor", MusEGlobal::config.wavePeakColor); - xml.colorTag(level, "waveRmsColor", MusEGlobal::config.waveRmsColor); - xml.colorTag(level, "wavePeakColorSelected", MusEGlobal::config.wavePeakColorSelected); - xml.colorTag(level, "waveRmsColorSelected", MusEGlobal::config.waveRmsColorSelected); - - xml.colorTag(level, "partWaveColorPeak", MusEGlobal::config.partWaveColorPeak); - xml.colorTag(level, "partWaveColorRms", MusEGlobal::config.partWaveColorRms); - xml.colorTag(level, "partMidiDarkEventColor", MusEGlobal::config.partMidiDarkEventColor); - xml.colorTag(level, "partMidiLightEventColor", MusEGlobal::config.partMidiLightEventColor); - - xml.colorTag(level, "midiCanvasBackgroundColor", MusEGlobal::config.midiCanvasBg); - xml.colorTag(level, "midiControllerViewBackgroundColor", MusEGlobal::config.midiControllerViewBg); - xml.colorTag(level, "drumListBackgroundColor", MusEGlobal::config.drumListBg); - + xml.intTag(level, "maxAliasedPointSize", MusEGlobal::config.maxAliasedPointSize); + MusEGlobal::writePluginGroupConfiguration(level, xml); writeSeqConfiguration(level, xml, false); @@ -1622,7 +1982,7 @@ { importDefaultInstr->clear(); for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) - if(!dynamic_cast(*i)) // Sorry, no synths for now. + if(!(*i)->isSynti()) // Sorry, no synths for now. importDefaultInstr->addItem((*i)->iname()); int idx = importDefaultInstr->findText(MusEGlobal::config.importMidiDefaultInstr); if(idx != -1) @@ -1642,6 +2002,7 @@ formatCombo->setCurrentIndex(MusEGlobal::config.smfFormat); extendedFormat->setChecked(MusEGlobal::config.extendedMidi); copyrightEdit->setText(MusEGlobal::config.copyright); + runningStatus->setChecked(MusEGlobal::config.expRunningStatus); optNoteOffs->setChecked(MusEGlobal::config.expOptimNoteOffs); twoByteTimeSigs->setChecked(MusEGlobal::config.exp2ByteTimeSigs); splitPartsCheckBox->setChecked(MusEGlobal::config.importMidiSplitParts); @@ -1650,34 +2011,13 @@ importDevNameMetas->setChecked(MusEGlobal::config.importDevNameMetas); importInstrNameMetas->setChecked(MusEGlobal::config.importInstrNameMetas); exportPortDeviceSMF0->setChecked(MusEGlobal::config.exportPortDeviceSMF0); - switch(MusEGlobal::config.exportPortsDevices) - { - case MusEGlobal::PORT_NUM_META: - exportPortMetas->setChecked(true); - break; - case MusEGlobal::DEVICE_NAME_META: - exportDeviceNameMetas->setChecked(true); - break; - case MusEGlobal::EXPORT_PORTS_DEVICES_ALL: - exportPortAndDeviceNameMetas->setChecked(true); - break; - default: - printf("MidiFileConfig::updateValues FIXME: Unknown exportPortsDevices type\n"); - } - switch(MusEGlobal::config.exportModeInstr) - { - case MusEGlobal::MODE_SYSEX: - exportModeSysexes->setChecked(true); - break; - case MusEGlobal::INSTRUMENT_NAME_META: - exportInstrumentNames->setChecked(true); - break; - case MusEGlobal::EXPORT_MODE_INSTR_ALL: - exportModeAndInstrName->setChecked(true); - break; - default: - printf("MidiFileConfig::updateValues FIXME: Unknown exportModeInstr type\n"); - } + drumMapOverrides->setChecked(MusEGlobal::config.exportDrumMapOverrides); + channelOverridesToNewTrack->setChecked(MusEGlobal::config.exportChannelOverridesToNewTrack); + exportPortMetas->setChecked(MusEGlobal::config.exportPortsDevices & MusEGlobal::PORT_NUM_META); + exportDeviceNameMetas->setChecked(MusEGlobal::config.exportPortsDevices & MusEGlobal::DEVICE_NAME_META); + exportModeSysexes->setChecked(MusEGlobal::config.exportModeInstr & MusEGlobal::MODE_SYSEX); + exportInstrumentNames->setChecked(MusEGlobal::config.exportModeInstr & MusEGlobal::INSTRUMENT_NAME_META); + } //--------------------------------------------------------- @@ -1698,6 +2038,7 @@ MusEGlobal::config.extendedMidi = extendedFormat->isChecked(); MusEGlobal::config.smfFormat = formatCombo->currentIndex(); MusEGlobal::config.copyright = copyrightEdit->text(); + MusEGlobal::config.expRunningStatus = runningStatus->isChecked(); MusEGlobal::config.expOptimNoteOffs = optNoteOffs->isChecked(); MusEGlobal::config.exp2ByteTimeSigs = twoByteTimeSigs->isChecked(); MusEGlobal::config.importMidiSplitParts = splitPartsCheckBox->isChecked(); @@ -1706,20 +2047,22 @@ MusEGlobal::config.importDevNameMetas = importDevNameMetas->isChecked(); MusEGlobal::config.importInstrNameMetas = importInstrNameMetas->isChecked(); MusEGlobal::config.exportPortDeviceSMF0 = exportPortDeviceSMF0->isChecked(); + MusEGlobal::config.exportDrumMapOverrides = drumMapOverrides->isChecked(); + MusEGlobal::config.exportChannelOverridesToNewTrack = channelOverridesToNewTrack->isChecked(); + + MusEGlobal::config.exportPortsDevices = 0; if(exportPortMetas->isChecked()) - MusEGlobal::config.exportPortsDevices = MusEGlobal::PORT_NUM_META; - else if(exportDeviceNameMetas->isChecked()) - MusEGlobal::config.exportPortsDevices = MusEGlobal::DEVICE_NAME_META; - else if(exportPortAndDeviceNameMetas->isChecked()) - MusEGlobal::config.exportPortsDevices = MusEGlobal::EXPORT_PORTS_DEVICES_ALL; + MusEGlobal::config.exportPortsDevices |= MusEGlobal::PORT_NUM_META; + if(exportDeviceNameMetas->isChecked()) + MusEGlobal::config.exportPortsDevices |= MusEGlobal::DEVICE_NAME_META; + MusEGlobal::config.exportModeInstr = 0; if(exportModeSysexes->isChecked()) - MusEGlobal::config.exportModeInstr = MusEGlobal::MODE_SYSEX; - else if(exportInstrumentNames->isChecked()) - MusEGlobal::config.exportModeInstr = MusEGlobal::INSTRUMENT_NAME_META; - else if(exportModeAndInstrName->isChecked()) - MusEGlobal::config.exportModeInstr = MusEGlobal::EXPORT_MODE_INSTR_ALL; + MusEGlobal::config.exportModeInstr |= MusEGlobal::MODE_SYSEX; + if(exportInstrumentNames->isChecked()) + MusEGlobal::config.exportModeInstr |= MusEGlobal::INSTRUMENT_NAME_META; + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); // write config file close(); } @@ -1759,7 +2102,9 @@ xml.intTag(level, "showGroupTracks", showGroupTracks); xml.intTag(level, "showAuxTracks", showAuxTracks); xml.intTag(level, "showSyntiTracks", showSyntiTracks); - + + xml.intTag(level, "displayOrder", displayOrder); + xml.etag(level, "Mixer"); } @@ -1799,6 +2144,12 @@ showAuxTracks = xml.parseInt(); else if (tag == "showSyntiTracks") showSyntiTracks = xml.parseInt(); + else if (tag == "displayOrder") + displayOrder = (DisplayOrder)xml.parseInt(); + else if (tag == "StripName") + stripOrder.append(xml.parse1()); + else if (tag == "StripVisible") + stripVisibility.append(xml.parseInt() == 0 ? false : true ); else xml.unknown("Mixer"); break; diff -Nru muse-2.1.2/muse/conf.h muse-3.0.2+ds1/muse/conf.h --- muse-2.1.2/muse/conf.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/conf.h 2018-01-26 21:59:38.000000000 +0000 @@ -26,6 +26,13 @@ #include "ui_configmidifilebase.h" +// REMOVE Tim. newdrums. Added. +// Adds the ability to override at instrument level. +// But it just makes things too complex for the user. +// And in a way is unnecessary and overkill, since we +// already allow modifying an instrument. +//#define _USE_INSTRUMENT_OVERRIDES_ + class QDialog; class QLineEdit; diff -Nru muse-2.1.2/muse/confmport.cpp muse-3.0.2+ds1/muse/confmport.cpp --- muse-2.1.2/muse/confmport.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/confmport.cpp 2018-01-22 16:43:28.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: confmport.cpp,v 1.9.2.10 2009/12/15 03:39:58 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -28,6 +29,7 @@ #include #include +#include #include #include #include @@ -56,6 +58,15 @@ #include "menutitleitem.h" #include "utils.h" #include "popupmenu.h" +#include "routepopup.h" +#include "operations.h" +#include "gconfig.h" + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); + +// Undefine if and when multiple output routes are added to midi tracks. +#define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ namespace MusEGlobal { extern std::vector synthis; @@ -63,9 +74,6 @@ namespace MusEGui { -enum { DEVCOL_NO = 0, DEVCOL_GUI, DEVCOL_REC, DEVCOL_PLAY, DEVCOL_INSTR, DEVCOL_NAME, - DEVCOL_INROUTES, DEVCOL_OUTROUTES, DEVCOL_DEF_IN_CHANS, DEVCOL_DEF_OUT_CHANS, DEVCOL_STATE }; - //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- @@ -112,6 +120,7 @@ int actid = act->data().toInt(); int allch = (1 << MIDI_CHANNELS) - 1; int defch = MusEGlobal::midiPorts[no].defaultInChannels(); + MusECore::PendingOperationList operations; if(actid == MIDI_CHANNELS + 1) // Apply to all tracks now. { @@ -128,12 +137,46 @@ MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { - // Remove all routes from this port to the tracks first. - MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); - if(defch) - MusEGlobal::audio->msgAddRoute(MusECore::Route(no, defch), MusECore::Route(*it, defch)); - } - MusEGlobal::song->update(SC_ROUTE); + MusECore::MidiTrack* mt = *it; + MusECore::RouteList* rl = mt->inRoutes(); + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + switch(ir->type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + // Remove all routes from this port to the tracks first. + if(ir->midiPort == no) + operations.add(MusECore::PendingOperationItem(*ir, MusECore::Route(mt, ir->channel), + MusECore::PendingOperationItem::DeleteRoute)); + break; + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; + } + + // All channels set or Omni? Use an Omni route: + if(defch == -1 || defch == allch) + operations.add(MusECore::PendingOperationItem(MusECore::Route(no), MusECore::Route(mt), + MusECore::PendingOperationItem::AddRoute)); + else + // Add individual channels: + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + const int chbit = 1 << ch; + if(defch & chbit) + operations.add(MusECore::PendingOperationItem(MusECore::Route(no, ch), MusECore::Route(mt, ch), + MusECore::PendingOperationItem::AddRoute)); + } + } + } + + if(!operations.empty()) + { + operations.add(MusECore::PendingOperationItem((MusECore::TrackList*)NULL, MusECore::PendingOperationItem::UpdateSoloStates)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); +// MusEGlobal::song->update(SC_ROUTE); + } } } } @@ -142,17 +185,26 @@ int chbits; if(actid == MIDI_CHANNELS) // Toggle all. { - chbits = (defch == allch) ? 0 : allch; - if(defpup) - for(int i = 0; i < MIDI_CHANNELS; ++i) + chbits = (defch == -1 || defch == allch) ? 0 : allch; + if(act->actionGroup()) + { + QList acts = act->actionGroup()->actions(); + const int sz = acts.size(); + for(int i = 0; i < sz; ++i) { - QAction* act = defpup->findActionFromData(i); - if(act) - act->setChecked(chbits); - } + QAction* a = acts.at(i); + if(a) + a->setChecked(chbits); + } + } } else - chbits = defch ^ (1 << actid); + { + if(defch == -1) + chbits = 0; + else + chbits = defch ^ (1 << actid); + } MusEGlobal::midiPorts[no].setDefaultInChannels(chbits); mdevView->item(item->row(), DEVCOL_DEF_IN_CHANS)->setText(MusECore::bitmap2String(chbits)); } @@ -173,10 +225,11 @@ return; int actid = act->data().toInt(); int defch = MusEGlobal::midiPorts[no].defaultOutChannels(); - // Turn on if and when multiple output routes are supported. DELETETHIS?? - #if 0 + +#ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ int allch = (1 << MIDI_CHANNELS) - 1; - #endif + MusECore::PendingOperationList operations; +#endif if(actid == MIDI_CHANNELS + 1) // Apply to all tracks now. { @@ -184,11 +237,11 @@ // Tested: Hmm, allow ports with no device since that is a valid situation. if(!MusEGlobal::song->midis()->empty()) // && MusEGlobal::midiPorts[no].device()) { - // Turn off if and when multiple output routes are supported. - #if 1 + +#ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(!defch) // No channels selected? Just return. return; - #endif +#endif int ret = QMessageBox::question(this, tr("Default output connections"), tr("Are you sure you want to apply to all existing midi tracks now?"), @@ -197,66 +250,85 @@ if(ret == QMessageBox::Ok) { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); - // Turn on if and when multiple output routes are supported. DELETETHIS?? - #if 0 - for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) - { - // Remove all routes from this port to the tracks first. - MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); - if(defch) - MusEGlobal::audio->msgAddRoute(MusECore::Route(no, defch), MusECore::Route(*it, defch)); - } - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); - #else + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ for(int ch = 0; ch < MIDI_CHANNELS; ++ch) if(defch & (1 << ch)) { + MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; 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); + changed |= (*it)->setOutPortAndUpdate(no, false); else - (*it)->setOutPortAndChannelAndUpdate(no, ch); - } + changed |= (*it)->setOutPortAndChannelAndUpdate(no, ch, false); + } MusEGlobal::audio->msgIdle(false); MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); + MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); // Stop at the first output channel found. break; } - #endif +#else + for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) + { + MusECore::MidiTrack* mt = *it; + MusECore::RouteList* rl = mt->outRoutes(); + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + switch(ir->type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + // Remove all routes from this port to the tracks first. + if(ir->midiPort == no) + operations.add(MusECore::PendingOperationItem(MusECore::Route(mt, ir->channel), *ir, + MusECore::PendingOperationItem::DeleteRoute)); + break; + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; + } + + // All channels set or Omni? Use an Omni route: + if(defch == -1 || defch == allch) + operations.add(MusECore::PendingOperationItem(MusECore::Route(mt), MusECore::Route(no), + MusECore::PendingOperationItem::AddRoute)); + else + // Add individual channels: + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + const int chbit = 1 << ch; + if(defch & chbit) + operations.add(MusECore::PendingOperationItem(MusECore::Route(mt, ch), MusECore::Route(no, ch), + MusECore::PendingOperationItem::AddRoute)); + } + } + } + + if(!operations.empty()) + { + operations.add(MusECore::PendingOperationItem((MusECore::TrackList*)NULL, MusECore::PendingOperationItem::UpdateSoloStates)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); +// MusEGlobal::song->update(SC_ROUTE); + } +#endif + } } } else { - #if 0 // Turn on if and when multiple output routes are supported. DELETETHIS?? - int chbits; - if(actid == MIDI_CHANNELS) // Toggle all. - { - chbits = (defch == allch) ? 0 : allch; - if(defpup) - for(int i = 0; i < MIDI_CHANNELS; ++i) - { - QAction* act = defpup->findActionFromData(i); - if(act) - act->setChecked(chbits); - } - } - else - chbits = defch ^ (1 << actid); - MusEGlobal::midiPorts[no].setDefaultOutChannels(chbits); - mdevView->item(item->row(), DEVCOL_DEF_OUT_CHANS)->setText(MusECore::bitmap2String(chbits)); - #else +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(actid < MIDI_CHANNELS) { int chbits = 1 << actid; // Are we toggling off? - if(chbits & defch) + //if(chbits & defch) + if(defch == -1 || chbits & defch) { // Just clear this port's default channels. MusEGlobal::midiPorts[no].setDefaultOutChannels(0); @@ -269,125 +341,90 @@ int j = mdevView->rowCount(); for(int i = 0; i < j; ++i) mdevView->item(i, DEVCOL_DEF_OUT_CHANS)->setText(MusECore::bitmap2String(i == no ? chbits : 0)); - if(defpup) - { - QAction* a; - for(int i = 0; i < MIDI_CHANNELS; ++i) - { - a = defpup->findActionFromData(i); - if(a) - a->setChecked(i == actid); - } - } + // The group is exclusive. No need to iterate manually. +// if(act->actionGroup()) +// { +// QList acts = act->actionGroup()->actions(); +// const int sz = acts.size(); +// if(sz > actid) +// { +// QAction* a = acts.at(actid); +// if(a) +// a->setChecked(true); +// } +// } } } - #endif +#else + int chbits; + if(actid == MIDI_CHANNELS) // Toggle all. + { + chbits = (defch == -1 || defch == allch) ? 0 : allch; + if(act->actionGroup()) + { + QList acts = act->actionGroup()->actions(); + const int sz = acts.size(); + for(int i = 0; i < sz; ++i) + { + QAction* a = acts.at(i); + if(a) + a->setChecked(chbits); + } + } + } + else + { + if(defch == -1) + chbits = 0; + else + chbits = defch ^ (1 << actid); + } + MusEGlobal::midiPorts[no].setDefaultOutChannels(chbits); + mdevView->item(item->row(), DEVCOL_DEF_OUT_CHANS)->setText(MusECore::bitmap2String(chbits)); +#endif + } } //--------------------------------------------------------- -// mdevViewItemRenamed +// DeviceItemRenamed //--------------------------------------------------------- -void MPConfig::mdevViewItemRenamed(QTableWidgetItem* item) +void MPConfig::DeviceItemRenamed(QTableWidgetItem* item) { - int col = item->column(); - QString s = item->text(); if(item == 0) return; + if(!item->data(DeviceRole).canConvert()) + return; + MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); + + int col = item->column(); + QTableWidgetItem* twi = item->tableWidget()->item(item->row(), INSTCOL_NAME); + if(!twi) + { + fprintf(stderr, "synthesizerConfig::DeviceItemRenamed(): row:%d INSTCOL_NAME not found\n", item->row()); + return; + } + QString new_name = twi->text(); + // Get the original name. + QString orig_name = md->name(); + if(new_name == orig_name) + return; + MusECore::iMidiDevice imd; + for(imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) { + MusECore::MidiDevice* d = *imd; + if(d == md || (*imd)->name() != new_name) + continue; + break; + } switch(col) { - // Enabled: Use editor (Not good - only responds if text changed. We need to respond always). - // Disabled: Use pop-up menu. DELETETHIS? - #if 0 - case DEVCOL_DEF_IN_CHANS: - { - QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); - int no = atoi(id.toLatin1().constData()) - 1; - if(no < 0 || no >= MIDI_PORTS) - return; - int allch = (1 << MIDI_CHANNELS) - 1; - int ch = allch & string2bitmap(s); - MusEGlobal::midiPorts[no].setDefaultInChannels(ch); - - if(!MusEGlobal::song->midis()->empty() && MusEGlobal::midiPorts[no].device()) // Only if there are tracks, and device is valid. - { - int ret = QMessageBox::question(this, tr("Default input connections"), - tr("Setting will apply to new midi tracks.\n" - "Do you want to apply to all existing midi tracks now?"), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No); - if(ret == QMessageBox::Yes) - { - MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); - for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) - { - // Remove all routes from this port to the tracks first. - MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); - if(ch) - MusEGlobal::audio->msgAddRoute(MusECore::Route(no, ch), MusECore::Route(*it, ch)); - } - } - } - MusEGlobal::song->update(); - } - break; - #endif - - // Enabled: Use editor (Not good - only responds if text changed. We need to respond always). - // Disabled: Use pop-up menu. - // Only turn on if and when multiple output routes are supported. DELETETHIS? - #if 0 - case DEVCOL_DEF_OUT_CHANS: - { - QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); - int no = atoi(id.toLatin1().constData()) - 1; - if(no < 0 || no >= MIDI_PORTS) - return; - int allch = (1 << MIDI_CHANNELS) - 1; - int ch = allch & string2bitmap(s); - MusEGlobal::midiPorts[no].setDefaultOutChannels(ch); - - if(!MusEGlobal::song->midis()->empty() && MusEGlobal::midiPorts[no].device()) // Only if there are tracks, and device is valid. - { - int ret = QMessageBox::question(this, tr("Default output connections"), - tr("Setting will apply to new midi tracks.\n" - "Do you want to apply to all existing midi tracks now?"), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No); - if(ret == QMessageBox::Yes) - { - MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); - for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) - { - // Remove all routes from the tracks to this port first. - MusEGlobal::audio->msgRemoveRoute(MusECore::Route(*it, allch), MusECore::Route(no, allch)); - if(ch) - MusEGlobal::audio->msgAddRoute(MusECore::Route(*it, ch), MusECore::Route(no, ch)); - } - } - } - MusEGlobal::song->update(); - } - break; - # endif - - case DEVCOL_NAME: + case INSTCOL_NAME: { - QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); - int no = atoi(id.toLatin1().constData()) - 1; - if(no < 0 || no >= MIDI_PORTS) - return; - - MusECore::MidiPort* port = &MusEGlobal::midiPorts[no]; - MusECore::MidiDevice* dev = port->device(); // Only Jack midi devices. - if(!dev || dev->deviceType() != MusECore::MidiDevice::JACK_MIDI) + if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) return; - if(dev->name() == s) - return; - - if(MusEGlobal::midiDevices.find(s)) + if(imd != MusEGlobal::midiDevices.end()) { QMessageBox::critical(this, tr("MusE: bad device name"), @@ -395,11 +432,15 @@ QMessageBox::Ok, Qt::NoButton, Qt::NoButton); - songChanged(-1); + instanceList->blockSignals(true); + item->setText(orig_name); + instanceList->blockSignals(false); return; } - dev->setName(s); - MusEGlobal::song->update(); + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + md->setName(new_name); + MusEGlobal::audio->msgIdle(false); + MusEGlobal::song->update(SC_CONFIG); } break; default: @@ -423,32 +464,37 @@ int n; MusECore::MidiPort* port = &MusEGlobal::midiPorts[no]; MusECore::MidiDevice* dev = port->device(); +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ int rwFlags = dev ? dev->rwFlags() : 0; int openFlags = dev ? dev->openFlags() : 0; +#endif QTableWidget* listView = item->tableWidget(); QPoint ppt = listView->visualItemRect(item).bottomLeft(); - QPoint mousepos = QCursor::pos(); int col = item->column(); ppt += QPoint(0, listView->horizontalHeader()->height()); ppt = listView->mapToGlobal(ppt); - + MusECore::PendingOperationList operations; + switch (col) { + +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ case DEVCOL_GUI: if (dev == 0) return; if (port->hasNativeGui()) { - port->instrument()->showNativeGui(!port->nativeGuiVisible()); + port->showNativeGui(!port->nativeGuiVisible()); item->setIcon(port->nativeGuiVisible() ? QIcon(*dotIcon) : QIcon(*dothIcon)); } return; + break; case DEVCOL_REC: if (dev == 0 || !(rwFlags & 2)) return; openFlags ^= 0x2; dev->setOpenFlags(openFlags); - MusEGlobal::midiSeq->msgSetMidiDevice(port, dev); // reopen device + MusEGlobal::audio->msgSetMidiDevice(port, dev); // reopen device item->setIcon(openFlags & 2 ? QIcon(*dotIcon) : QIcon(*dothIcon)); if(dev->deviceType() == MusECore::MidiDevice::JACK_MIDI) @@ -465,13 +511,14 @@ } } return; + break; case DEVCOL_PLAY: if (dev == 0 || !(rwFlags & 1)) return; openFlags ^= 0x1; dev->setOpenFlags(openFlags); - MusEGlobal::midiSeq->msgSetMidiDevice(port, dev); // reopen device + MusEGlobal::audio->msgSetMidiDevice(port, dev); // reopen device item->setIcon(openFlags & 1 ? QIcon(*dotIcon) : QIcon(*dothIcon)); if(dev->deviceType() == MusECore::MidiDevice::JACK_MIDI) @@ -488,6 +535,7 @@ } } return; + break; case DEVCOL_INROUTES: case DEVCOL_OUTROUTES: @@ -508,498 +556,399 @@ if(!(dev->openFlags() & ((col == DEVCOL_OUTROUTES) ? 1 : 2))) return; - MusECore::RouteList* rl = (col == DEVCOL_OUTROUTES) ? dev->outRoutes() : dev->inRoutes(); - QMenu* pup = 0; - int gid = 0; - std::list sl; - pup = new QMenu(this); - - _redisplay: - pup->clear(); - gid = 0; - - // Jack input ports if device is writable, and jack output ports if device is readable. - sl = (col == DEVCOL_OUTROUTES) ? MusEGlobal::audioDevice->inputPorts(true, _showAliases) : MusEGlobal::audioDevice->outputPorts(true, _showAliases); - - QAction* act; - - act = pup->addAction(tr("Show first aliases")); - act->setData(gid); - act->setCheckable(true); - act->setChecked(_showAliases == 0); - ++gid; - - act = pup->addAction(tr("Show second aliases")); - act->setData(gid); - act->setCheckable(true); - act->setChecked(_showAliases == 1); - ++gid; - - pup->addSeparator(); - for(std::list::iterator ip = sl.begin(); ip != sl.end(); ++ip) - { - act = pup->addAction(*ip); - act->setData(gid); - act->setCheckable(true); - - MusECore::Route rt(*ip, (col == DEVCOL_OUTROUTES), -1, MusECore::Route::JACK_ROUTE); - for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if (*ir == rt) - { - act->setChecked(true); - break; - } - } - ++gid; - } - - act = pup->exec(ppt); - if(act) - { - n = act->data().toInt(); - if(n == 0) // Show first aliases - { - if(_showAliases == 0) - _showAliases = -1; - else - _showAliases = 0; - goto _redisplay; // Go back - } - else - if(n == 1) // Show second aliases - { - if(_showAliases == 1) - _showAliases = -1; - else - _showAliases = 1; - goto _redisplay; // Go back - } - - QString s(act->text()); - - if(col == DEVCOL_OUTROUTES) // Writeable - { - MusECore::Route srcRoute(dev, -1); - MusECore::Route dstRoute(s, true, -1, MusECore::Route::JACK_ROUTE); - - MusECore::ciRoute iir = rl->begin(); - for(; iir != rl->end(); ++iir) - { - if(*iir == dstRoute) - break; - } - if(iir != rl->end()) - // disconnect - MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); - else - // connect - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); - } - else - { - MusECore::Route srcRoute(s, false, -1, MusECore::Route::JACK_ROUTE); - MusECore::Route dstRoute(dev, -1); - - MusECore::ciRoute iir = rl->begin(); - for(; iir != rl->end(); ++iir) - { - if(*iir == srcRoute) - break; - } - if(iir != rl->end()) - // disconnect - MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); - else - // connect - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); - } - - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); - - // FIXME: - // Routes can't be re-read until the message sent from msgAddRoute1() - // has had time to be sent and actually affected the routes. - //goto _redisplay; // Go back - } + RoutePopupMenu* pup = new RoutePopupMenu(); + pup->exec(QCursor::pos(), dev, col == DEVCOL_OUTROUTES); delete pup; } return; - + break; + +#endif // not _USE_EXTRA_INSTANCE_COLUMNS_ + case DEVCOL_DEF_IN_CHANS: { - defpup = new MusEGui::PopupMenu(this, true); - defpup->addAction(new MusEGui::MenuTitleItem("Channel", defpup)); + PopupMenu* pup = new PopupMenu(true); + pup->addAction(new MenuTitleItem("Channel", pup)); QAction* act = 0; int chbits = MusEGlobal::midiPorts[no].defaultInChannels(); + QActionGroup* ag = new QActionGroup(pup); + ag->setExclusive(false); for(int i = 0; i < MIDI_CHANNELS; ++i) { - act = defpup->addAction(QString().setNum(i + 1)); + act = ag->addAction(QString().setNum(i + 1)); act->setData(i); act->setCheckable(true); act->setChecked((1 << i) & chbits); } + pup->addActions(ag->actions()); - act = defpup->addAction(tr("Toggle all")); + act = pup->addAction(tr("Toggle all")); act->setData(MIDI_CHANNELS); - defpup->addSeparator(); - act = defpup->addAction(tr("Change all tracks now")); + pup->addSeparator(); + act = pup->addAction(tr("Change all tracks now")); act->setData(MIDI_CHANNELS + 1); // Enable only if there are tracks, and port has a device. - // Tested: Hmm, allow ports with no device since that is a valid situation. - act->setEnabled(!MusEGlobal::song->midis()->empty()); // && MusEGlobal::midiPorts[no].device()); DELETETHIS + // Allow ports with no device since that is a valid situation. + act->setEnabled(!MusEGlobal::song->midis()->empty()); - connect(defpup, SIGNAL(triggered(QAction*)), SLOT(changeDefInputRoutes(QAction*))); - // DELETETHIS 2 - //connect(defpup, SIGNAL(aboutToHide()), MusEGlobal::muse, SLOT(routingPopupMenuAboutToHide())); - //defpup->popup(QCursor::pos()); - defpup->exec(QCursor::pos()); - delete defpup; - defpup = 0; + connect(pup, SIGNAL(triggered(QAction*)), SLOT(changeDefInputRoutes(QAction*))); + pup->exec(QCursor::pos()); + delete pup; } return; + break; case DEVCOL_DEF_OUT_CHANS: { - defpup = new MusEGui::PopupMenu(this, true); - defpup->addAction(new MusEGui::MenuTitleItem("Channel", defpup)); + PopupMenu* pup = new PopupMenu(true); + pup->addAction(new MenuTitleItem("Channel", pup)); QAction* act = 0; int chbits = MusEGlobal::midiPorts[no].defaultOutChannels(); - for(int i = 0; i < MIDI_CHANNELS; ++i) + QActionGroup* ag = new QActionGroup(pup); + ag->setExclusive(true); + + act = ag->addAction("None"); + act->setData(0); + act->setCheckable(true); + act->setChecked(chbits == 0); + + for(int i = 0; i < MIDI_CHANNELS; ++i) { - act = defpup->addAction(QString().setNum(i + 1)); + act = ag->addAction(QString().setNum(i + 1)); act->setData(i); act->setCheckable(true); act->setChecked((1 << i) & chbits); } + pup->addActions(ag->actions()); - // Turn on if and when multiple output routes are supported. DELETETHIS? - #if 0 - act = defpup->addAction(tr("Toggle all")); + // Turn on if and when multiple output routes are supported. +#ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + act = pup->addAction(tr("Toggle all")); act->setData(MIDI_CHANNELS); - #endif +#endif - defpup->addSeparator(); - act = defpup->addAction(tr("Change all tracks now")); + pup->addSeparator(); + act = pup->addAction(tr("Change all tracks now")); act->setData(MIDI_CHANNELS + 1); // Enable only if there are tracks, and port has a device. - // Tested: Hmm, allow ports with no device since that is a valid situation. - act->setEnabled(!MusEGlobal::song->midis()->empty()); // && MusEGlobal::midiPorts[no].device()); + // Allow ports with no device since that is a valid situation. + act->setEnabled(!MusEGlobal::song->midis()->empty()); - connect(defpup, SIGNAL(triggered(QAction*)), SLOT(changeDefOutputRoutes(QAction*))); - defpup->exec(QCursor::pos()); - delete defpup; - defpup = 0; + connect(pup, SIGNAL(triggered(QAction*)), SLOT(changeDefOutputRoutes(QAction*))); + pup->exec(QCursor::pos()); + delete pup; } return; + break; case DEVCOL_NAME: { - // Did we click in the text area? - if((mousepos.x() - ppt.x()) > buttondownIcon->width()) - { - // Start the renaming of the cell... - QModelIndex current = item->tableWidget()->currentIndex(); - if (item->flags() & Qt::ItemIsEditable) - item->tableWidget()->edit(current.sibling(current.row(), DEVCOL_NAME)); - - return; - } - else // We clicked the 'down' button. + PopupMenu* pup = new PopupMenu(false); + QAction* act; + + // REMOVE Tim. Persistent routes. Added. Testing... + QMenu* ctxmenu = pup->contextMenu(); + act = ctxmenu->addAction(tr("Remove")); + act->setData(0); + connect(ctxmenu, SIGNAL(triggered(QAction*)), SLOT(deviceContextTriggered(QAction*))); + + // Could do it this way... + //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" input")); + //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" output")); + //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" combo")); + // ... or keep it simple and let the user click on the green lights instead. + act = pup->addAction(tr("Create Jack device")); + act->setData(0); + + typedef std::map asmap; + typedef std::map::iterator imap; + + asmap mapALSA; + asmap mapJACK; + asmap mapSYNTH; + + int aix = 0x10000000; + int jix = 0x20000000; + int six = 0x30000000; + for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { - QMenu* pup = new QMenu(this); - - QAction* act; - - // Could do it this way... - //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" input")); - //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" output")); - //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" combo")); - // ... or keep it simple and let the user click on the green lights instead. - act = pup->addAction(tr("Create Jack device")); - act->setData(0); - - typedef std::map asmap; - typedef std::map::iterator imap; - - asmap mapALSA; - asmap mapJACK; - asmap mapSYNTH; - - int aix = 0x10000000; - int jix = 0x20000000; - int six = 0x30000000; - for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + if((*i)->deviceType() == MusECore::MidiDevice::ALSA_MIDI) { - if((*i)->deviceType() == MusECore::MidiDevice::ALSA_MIDI) - { - mapALSA.insert( std::pair (std::string((*i)->name().toLatin1().constData()), aix) ); - ++aix; - } - else - if((*i)->deviceType() == MusECore::MidiDevice::JACK_MIDI) - { - mapJACK.insert( std::pair (std::string((*i)->name().toLatin1().constData()), jix) ); - ++jix; - } - else - if((*i)->deviceType() == MusECore::MidiDevice::SYNTH_MIDI) - { - mapSYNTH.insert( std::pair (std::string((*i)->name().toLatin1().constData()), six) ); - ++six; - } - else - printf("MPConfig::rbClicked unknown midi device: %s\n", (*i)->name().toLatin1().constData()); + mapALSA.insert( std::pair (std::string((*i)->name().toLatin1().constData()), aix) ); + ++aix; + } + else + if((*i)->deviceType() == MusECore::MidiDevice::JACK_MIDI) + { + mapJACK.insert( std::pair (std::string((*i)->name().toLatin1().constData()), jix) ); + ++jix; } - - if(!mapALSA.empty()) + else + if((*i)->deviceType() == MusECore::MidiDevice::SYNTH_MIDI) { - pup->addSeparator(); - pup->addAction(new MusEGui::MenuTitleItem("ALSA:", pup)); - - for(imap i = mapALSA.begin(); i != mapALSA.end(); ++i) - { - int idx = i->second; - //if(idx > sz) // Sanity check DELETETHIS 2 - // continue; - QString s(i->first.c_str()); - MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::ALSA_MIDI); - if(md) - { - //if(!dynamic_cast(md)) DELETETHIS - if(md->deviceType() != MusECore::MidiDevice::ALSA_MIDI) - continue; - - act = pup->addAction(md->name()); - act->setData(idx); - act->setCheckable(true); - act->setChecked(md == dev); - } - } + mapSYNTH.insert( std::pair (std::string((*i)->name().toLatin1().constData()), six) ); + ++six; } + else + fprintf(stderr, "MPConfig::rbClicked unknown midi device: %s\n", (*i)->name().toLatin1().constData()); + } + + if(!mapALSA.empty()) + { + pup->addSeparator(); + pup->addAction(new MusEGui::MenuTitleItem("ALSA:", pup)); - if(!mapSYNTH.empty()) + for(imap i = mapALSA.begin(); i != mapALSA.end(); ++i) { - pup->addSeparator(); - pup->addAction(new MusEGui::MenuTitleItem("SYNTH:", pup)); - - for(imap i = mapSYNTH.begin(); i != mapSYNTH.end(); ++i) + int idx = i->second; + QString s(i->first.c_str()); + MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::ALSA_MIDI); + if(md) { - int idx = i->second; - QString s(i->first.c_str()); - MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::SYNTH_MIDI); - if(md) - { - if(md->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) - continue; - - act = pup->addAction(md->name()); - act->setData(idx); - act->setCheckable(true); - act->setChecked(md == dev); - } - } + if(md->deviceType() != MusECore::MidiDevice::ALSA_MIDI) + continue; + act = pup->addAction(md->name()); + act->setData(idx); + act->setCheckable(true); + act->setChecked(md == dev); + } } + } + + if(!mapJACK.empty()) + { + pup->addSeparator(); + pup->addAction(new MusEGui::MenuTitleItem("JACK:", pup)); - if(!mapJACK.empty()) + for(imap i = mapJACK.begin(); i != mapJACK.end(); ++i) { - pup->addSeparator(); - pup->addAction(new MusEGui::MenuTitleItem("JACK:", pup)); - - for(imap i = mapJACK.begin(); i != mapJACK.end(); ++i) + int idx = i->second; + QString s(i->first.c_str()); + MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::JACK_MIDI); + if(md) { - int idx = i->second; - QString s(i->first.c_str()); - MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::JACK_MIDI); - if(md) - { - if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) - continue; - - act = pup->addAction(md->name()); - act->setData(idx); - act->setCheckable(true); - act->setChecked(md == dev); - } - } - } - - act = pup->exec(ppt); - if(!act) - { - delete pup; - return; + if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) + continue; + + act = pup->addAction(md->name()); + act->setData(idx); + act->setCheckable(true); + act->setChecked(md == dev); + } } + } + + if(!mapSYNTH.empty()) + { + pup->addSeparator(); + pup->addAction(new MusEGui::MenuTitleItem("SYNTH:", pup)); - n = act->data().toInt(); - - MusECore::MidiDevice* sdev = 0; - if(n < 0x10000000) + for(imap i = mapSYNTH.begin(); i != mapSYNTH.end(); ++i) { - delete pup; - if(n <= 2) + int idx = i->second; + QString s(i->first.c_str()); + MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::SYNTH_MIDI); + if(md) { - sdev = MusECore::MidiJackDevice::createJackMidiDevice(); + if(md->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) + continue; + + act = pup->addAction(md->name()); + act->setData(idx); + act->setCheckable(true); + act->setChecked(md == dev); + } + } + } + + act = pup->exec(ppt); + if(!act) + { + delete pup; + return; + } + + n = act->data().toInt(); + + MusECore::MidiDevice* sdev = 0; + if(n < 0x10000000) + { + if(n <= 2) + { + sdev = MusECore::MidiJackDevice::createJackMidiDevice(); - if(sdev) + if(sdev) + { + int of = 3; + switch(n) { - int of = 3; - switch(n) - { - case 0: of = 3; break; - case 1: of = 2; break; - case 2: of = 1; break; - } - sdev->setOpenFlags(of); + case 0: of = 3; break; + case 1: of = 2; break; + case 2: of = 1; break; } + sdev->setOpenFlags(of); } } - else - { - int typ; - if(n < 0x20000000) - typ = MusECore::MidiDevice::ALSA_MIDI; - else if(n < 0x30000000) - typ = MusECore::MidiDevice::JACK_MIDI; - else //if(n < 0x40000000) - typ = MusECore::MidiDevice::SYNTH_MIDI; - - sdev = MusEGlobal::midiDevices.find(act->text(), typ); - delete pup; - // Is it the current device? Reset it to . - if(sdev == dev) - sdev = 0; - } - - int allch = (1 << MIDI_CHANNELS) - 1; - MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); + } + else + { + int typ; + if(n < 0x20000000) + typ = MusECore::MidiDevice::ALSA_MIDI; + else if(n < 0x30000000) + typ = MusECore::MidiDevice::JACK_MIDI; + else //if(n < 0x40000000) + typ = MusECore::MidiDevice::SYNTH_MIDI; + + sdev = MusEGlobal::midiDevices.find(act->text(), typ); + // Is it the current device? Reset it to . + if(sdev == dev) + sdev = 0; + } - // Remove track routes to/from an existing port already using the selected device... - if(sdev) + delete pup; + + MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); + for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) + { + MusECore::MidiTrack* mt = *it; + MusECore::RouteList* rl = mt->inRoutes(); + for(MusECore::iRoute ir = rl->begin(); ir != rl->end(); ++ir) { - for(int i = 0; i < MIDI_PORTS; ++i) + switch(ir->type) { - if(MusEGlobal::midiPorts[i].device() == sdev) - { - for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) - MusEGlobal::audio->msgRemoveRoute(MusECore::Route(i, allch), MusECore::Route(*it, allch)); - - // Turn on if and when multiple output routes are supported. DELETETHIS? - #if 0 - for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) - MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); - #endif - - break; - } + case MusECore::Route::MIDI_PORT_ROUTE: + // Remove track routes from an existing port already using the selected device. + // Remove all track routes from this port. + if((sdev && ir->midiPort == sdev->midiPort()) || (ir->midiPort == no)) + operations.add(MusECore::PendingOperationItem(*ir, MusECore::Route(mt, ir->channel), + MusECore::PendingOperationItem::DeleteRoute)); + break; + + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; } } - // Remove all track routes to/from this port... - for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) - // Remove all routes from this port to the tracks. - MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); - // Turn on if and when multiple output routes are supported. DELETETHIS? - #if 0 - for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) - MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); - #endif - - MusEGlobal::midiSeq->msgSetMidiDevice(port, sdev); - MusEGlobal::muse->changeConfig(true); // save configuration file - - // Add all track routes to/from this port... - if(sdev) - { - int chbits = MusEGlobal::midiPorts[no].defaultInChannels(); - // Do not add input routes to synths. - if(!sdev->isSynti()) + rl = mt->outRoutes(); + for(MusECore::iRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + switch(ir->type) { - for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) - { - // Connect all the specified routes. - if(chbits) - MusEGlobal::audio->msgAddRoute(MusECore::Route(no, chbits), MusECore::Route(*it, chbits)); - } + case MusECore::Route::MIDI_PORT_ROUTE: + // Remove track routes to an existing port already using the selected device. + // Remove all track routes to this port. + if((sdev && ir->midiPort == sdev->midiPort()) || (ir->midiPort == no)) + operations.add(MusECore::PendingOperationItem(MusECore::Route(mt, ir->channel), *ir, + MusECore::PendingOperationItem::DeleteRoute)); + break; + + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; } -// chbits = MusEGlobal::midiPorts[no].defaultOutChannels(); - // Turn on if and when multiple output routes are supported. DELETETHIS? - #if 0 + } + } + + MusEGlobal::audio->msgSetMidiDevice(port, sdev); + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + MusEGlobal::muse->changeConfig(true); // save configuration file + + // Add all track routes to/from this port... + if(sdev) + { + const int allch = (1 << MIDI_CHANNELS) - 1; + const int i_chbits = MusEGlobal::midiPorts[no].defaultInChannels(); + const int o_chbits = MusEGlobal::midiPorts[no].defaultOutChannels(); + // Connect all the specified routes. Do not add input routes to synths. + if((i_chbits || o_chbits) && !sdev->isSynti()) + { for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { - // Connect all the specified routes. - if(chbits) - MusEGlobal::audio->msgAddRoute(MusECore::Route(no, chbits), MusECore::Route(*it, chbits)); + MusECore::MidiTrack* mt = *it; + // All channels set or Omni? Use an Omni route: + if(i_chbits == -1 || i_chbits == allch) + operations.add(MusECore::PendingOperationItem(MusECore::Route(no), MusECore::Route(mt), + MusECore::PendingOperationItem::AddRoute)); + else + // Add individual channels: + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + const int chbit = 1 << ch; + if(i_chbits & chbit) + operations.add(MusECore::PendingOperationItem(MusECore::Route(no, ch), MusECore::Route(mt, ch), + MusECore::PendingOperationItem::AddRoute)); + } + +#ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + // All channels set or Omni? Use an Omni route: + if(o_chbits == -1 || o_chbits == allch) + operations.add(MusECore::PendingOperationItem(MusECore::Route(mt), MusECore::Route(no), + MusECore::PendingOperationItem::AddRoute)); + else + // Add individual channels: + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + const int chbit = 1 << ch; + if(o_chbits & chbit) + operations.add(MusECore::PendingOperationItem(MusECore::Route(mt, ch), MusECore::Route(no, ch), + MusECore::PendingOperationItem::AddRoute)); + } +#endif + } - #else -// 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 } - - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(); - } + } } + + // Do these always, regardless of operations - the device has changed. + operations.add(MusECore::PendingOperationItem((MusECore::TrackList*)NULL, MusECore::PendingOperationItem::UpdateSoloStates)); + //if(!operations.empty()) + MusEGlobal::audio->msgExecutePendingOperations(operations, true, SC_EVERYTHING); +// MusEGlobal::song->update(); + return; + break; case DEVCOL_INSTR: { if (dev && dev->isSynti()) return; - if (instrPopup == 0) - instrPopup = new QMenu(this); - instrPopup->clear(); - for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i - != MusECore::midiInstruments.end(); ++i) - { - // By T356. - // Do not list synths. Although it is possible to assign a synth - // as an instrument to a non-synth device, we should not allow this. - // (One reason is that the 'show gui' column is then enabled, which - // makes no sense for a non-synth device). - MusECore::SynthI* si = dynamic_cast(*i); - if(!si) - instrPopup->addAction((*i)->iname()); - } + PopupMenu* pup = new PopupMenu(false); + MusECore::MidiInstrument::populateInstrPopup(pup, port->instrument(), false); - QAction* act = instrPopup->exec(ppt); - if(!act) + if(pup->actions().count() == 0) + { + delete pup; return; + } + + QAction* act = pup->exec(ppt); + if(!act) + return; + QString s = act->text(); + delete pup; + item->tableWidget()->item(item->row(), DEVCOL_INSTR)->setText(s); for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { if ((*i)->iname() == s) { - port->setInstrument(*i); + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + port->changeInstrument(*i); + MusEGlobal::audio->msgIdle(false); break; } } - MusEGlobal::song->update(); + MusEGlobal::song->update(SC_MIDI_INSTRUMENT); } return; + break; } } @@ -1011,21 +960,26 @@ { switch (col) { case DEVCOL_NO: item->setToolTip(tr("Port Number")); break; + +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ case DEVCOL_GUI: item->setToolTip(tr("Enable gui")); break; case DEVCOL_REC: item->setToolTip(tr("Enable reading")); break; case DEVCOL_PLAY: item->setToolTip(tr("Enable writing")); break; - case DEVCOL_INSTR: item->setToolTip(tr("Port instrument")); break; - case DEVCOL_NAME: item->setToolTip(tr("Midi device name. Click to edit (Jack)")); break; case DEVCOL_INROUTES: item->setToolTip(tr("Connections from Jack Midi outputs")); break; case DEVCOL_OUTROUTES: item->setToolTip(tr("Connections to Jack Midi inputs")); break; + case DEVCOL_STATE: item->setToolTip(tr("Device state")); break; +#endif + + case DEVCOL_INSTR: item->setToolTip(tr("Port instrument")); break; + case DEVCOL_NAME: item->setToolTip(tr("Midi device name. Click to edit (Jack)")); break; case DEVCOL_DEF_IN_CHANS: item->setToolTip(tr("Auto-connect these channels to new midi tracks")); break; - // Turn on if and when multiple output routes are supported. DELETETHIS? - #if 0 - case DEVCOL_DEF_OUT_CHANS: item->setToolTip(tr("Auto-connect new midi tracks to these channels")); break; - #else + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ case DEVCOL_DEF_OUT_CHANS: item->setToolTip(tr("Auto-connect new midi tracks to this channel")); break; - #endif - case DEVCOL_STATE: item->setToolTip(tr("Device state")); break; +#else + case DEVCOL_DEF_OUT_CHANS: item->setToolTip(tr("Auto-connect new midi tracks to these channels")); break; +#endif + default: return; } } @@ -1039,37 +993,97 @@ switch (col) { case DEVCOL_NO: item->setWhatsThis(tr("Port Number")); break; + +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ case DEVCOL_GUI: item->setWhatsThis(tr("Enable gui for device")); break; case DEVCOL_REC: item->setWhatsThis(tr("Enable reading from device")); break; case DEVCOL_PLAY: item->setWhatsThis(tr("Enable writing to device")); break; + case DEVCOL_INROUTES: + item->setWhatsThis(tr("Connections from Jack Midi output ports")); break; + case DEVCOL_OUTROUTES: + item->setWhatsThis(tr("Connections to Jack Midi input ports")); break; + case DEVCOL_STATE: + item->setWhatsThis(tr("State: result of opening the device")); break; +#endif + case DEVCOL_NAME: item->setWhatsThis(tr("Name of the midi device associated with" " this port number. Click to edit Jack midi name.")); break; case DEVCOL_INSTR: item->setWhatsThis(tr("Instrument connected to port")); break; - case DEVCOL_INROUTES: - item->setWhatsThis(tr("Connections from Jack Midi output ports")); break; - case DEVCOL_OUTROUTES: - item->setWhatsThis(tr("Connections to Jack Midi input ports")); break; case DEVCOL_DEF_IN_CHANS: item->setWhatsThis(tr("Auto-connect these channels, on this port, to new midi tracks.")); break; case DEVCOL_DEF_OUT_CHANS: - // Turn on if and when multiple output routes are supported. DELETETHIS? - #if 0 - item->setWhatsThis(tr("Connect new midi tracks to these channels, on this port.")); break; - #else +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ item->setWhatsThis(tr("Connect new midi tracks to this channel, on this port.")); break; - #endif - case DEVCOL_STATE: - item->setWhatsThis(tr("State: result of opening the device")); break; +#else + item->setWhatsThis(tr("Connect new midi tracks to these channels, on this port.")); break; +#endif default: break; } } +//--------------------------------------------------------- +// MPConfig::setInstToolTip +//--------------------------------------------------------- + +void MPConfig::setInstToolTip(QTableWidgetItem *item, int col) + { + switch (col) { + case INSTCOL_NAME: item->setToolTip(tr("Midi device name")); break; + case INSTCOL_TYPE: item->setToolTip(tr("Midi device type")); break; + case INSTCOL_STATE: item->setToolTip(tr("Device state")); break; + +#ifdef _USE_EXTRA_INSTANCE_COLUMNS_ + case INSTCOL_REC: item->setToolTip(tr("Enable reading")); break; + case INSTCOL_PLAY: item->setToolTip(tr("Enable writing")); break; + case INSTCOL_GUI: item->setToolTip(tr("Enable gui")); break; + case INSTCOL_INROUTES: item->setToolTip(tr("Connections from Jack Midi")); break; + case INSTCOL_OUTROUTES: item->setToolTip(tr("Connections to Jack Midi")); break; +#endif + + default: + break; + } + } + +//--------------------------------------------------------- +// MPConfig::setInstWhatsThis +//--------------------------------------------------------- + +void MPConfig::setInstWhatsThis(QTableWidgetItem *item, int col) + { + switch (col) { + case INSTCOL_NAME: item->setWhatsThis(tr("Midi device name")); break; + case INSTCOL_TYPE: item->setWhatsThis(tr("Midi device type")); break; + case INSTCOL_STATE: item->setWhatsThis(tr("Result of opening the device:\n" + "OK: Assigned to a port and in use\n" + "Closed: Unassigned to a port, or closed\n" + "R/W Error: Unable to open for read or write\n" + "Unavailable: USB midi unplugged, or external\n" + " application not running, or synth plugin\n" + " not installed etc.\n" + "(Jack Midi devices have 'unavailable ports'\n" + " in the routes columns.)\n" + "Unavailable devices or ports can be purged\n" + " with 'Remove' or with the advanced router.")); break; + +#ifdef _USE_EXTRA_INSTANCE_COLUMNS_ + case INSTCOL_REC: item->setWhatsThis(tr("Enable reading from device")); break; + case INSTCOL_PLAY: item->setWhatsThis(tr("Enable writing to device")); break; + case INSTCOL_GUI: item->setWhatsThis(tr("Enable Graphical User Interface for device")); break; + case INSTCOL_INROUTES: item->setWhatsThis(tr("Connections from Jack Midi ports")); break; + case INSTCOL_OUTROUTES: item->setWhatsThis(tr("Connections to Jack Midi ports")); break; +#endif + + default: + break; + } + } //--------------------------------------------------------- // MPConfig::addItem() @@ -1081,6 +1095,16 @@ table->setItem(row, col, item); } +//--------------------------------------------------------- +// MPConfig::addInstItem() +//--------------------------------------------------------- + +void MPConfig::addInstItem(int row, int col, QTableWidgetItem *item, QTableWidget *table) + { + setInstWhatsThis(item, col); + table->setItem(row, col, item); + } + //--------------------------------------------------------- // MPConfig @@ -1096,26 +1120,30 @@ mdevView->setRowCount(MIDI_PORTS); mdevView->verticalHeader()->hide(); - mdevView->setSelectionMode(QAbstractItemView::SingleSelection); mdevView->setShowGrid(false); - instrPopup = 0; - defpup = 0; _showAliases = 1; // 0: Show second aliases, if available. QStringList columnnames; columnnames << tr("Port") +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ << tr("GUI") << tr("I") << tr("O") +#endif + << tr("Device Name") << tr("Instrument") - << tr("Device Name") +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ << tr("In routes") << tr("Out routes") +#endif << tr("Def in ch") << tr("Def out ch") - << tr("State"); - +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ + << tr("State") +#endif + ; + mdevView->setColumnCount(columnnames.size()); mdevView->setHorizontalHeaderLabels(columnnames); for (int i = 0; i < columnnames.size(); ++i) { @@ -1124,20 +1152,42 @@ } mdevView->setFocusPolicy(Qt::NoFocus); - connect(mdevView, SIGNAL(itemPressed(QTableWidgetItem*)), - this, SLOT(rbClicked(QTableWidgetItem*))); - connect(mdevView, SIGNAL(itemChanged(QTableWidgetItem*)), - this, SLOT(mdevViewItemRenamed(QTableWidgetItem*))); - connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); + + instanceList->verticalHeader()->hide(); + instanceList->setShowGrid(false); + columnnames.clear(); + columnnames << tr("Device Name") + << tr("Type") +#ifdef _USE_EXTRA_INSTANCE_COLUMNS_ + << tr("I") + << tr("O") + << tr("GUI") + << tr("In") + << tr("Out") + << tr("State") +#endif + ; - connect(synthList, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); - connect(instanceList, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); + addALSADevice->setChecked(MusEGlobal::midiSeq != NULL); - connect(addInstance, SIGNAL(clicked()), SLOT(addInstanceClicked())); + instanceList->setColumnCount(columnnames.size()); + instanceList->setHorizontalHeaderLabels(columnnames); + for (int i = 0; i < columnnames.size(); ++i) { + setInstWhatsThis(instanceList->horizontalHeaderItem(i), i); + setInstToolTip(instanceList->horizontalHeaderItem(i), i); + } + connect(instanceList, SIGNAL(itemPressed(QTableWidgetItem*)), SLOT(deviceItemClicked(QTableWidgetItem*))); + connect(instanceList, SIGNAL(itemSelectionChanged()), SLOT(deviceSelectionChanged())); + connect(instanceList, SIGNAL(itemChanged(QTableWidgetItem*)), SLOT(DeviceItemRenamed(QTableWidgetItem*))); + connect(addJACKDevice, SIGNAL(clicked(bool)), SLOT(addJackDeviceClicked())); + connect(addALSADevice, SIGNAL(clicked(bool)), SLOT(addAlsaDeviceClicked(bool))); + connect(mdevView, SIGNAL(itemPressed(QTableWidgetItem*)), this, SLOT(rbClicked(QTableWidgetItem*))); + connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); + connect(synthList, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); + connect(addSynthDevice, SIGNAL(clicked()), SLOT(addInstanceClicked())); connect(synthList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(addInstanceClicked())); - connect(removeInstance, SIGNAL(clicked()), SLOT(removeInstanceClicked())); - connect(instanceList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(removeInstanceClicked())); - + connect(renameDevice, SIGNAL(clicked()), SLOT(renameInstanceClicked())); + connect(removeDevice, SIGNAL(clicked()), SLOT(removeInstanceClicked())); connect(applyButton, SIGNAL(clicked()), SLOT(apply())); connect(okButton, SIGNAL(clicked()), SLOT(okClicked())); @@ -1155,19 +1205,76 @@ void MPConfig::selectionChanged() { - addInstance->setEnabled(synthList->currentItem()); - removeInstance->setEnabled(instanceList->currentItem()); + addSynthDevice->setEnabled(synthList->selectedItems().isEmpty() ? false : (bool)synthList->currentItem()); } //--------------------------------------------------------- +// deviceSelectionChanged +//--------------------------------------------------------- + +void MPConfig::deviceSelectionChanged() +{ + DEBUG_PRST_ROUTES(stderr, "synthesizerConfig::deviceSelectionChanged() currentItem:%p\n", instanceList->currentItem()); + bool can_remove = false; + bool can_rename = false; + + int rowSelCount = 0; + const int sz = instanceList->rowCount(); + for(int i = 0; i < sz; ++i) + { + QTableWidgetItem* item = instanceList->item(i, INSTCOL_NAME); + if(!item || !item->data(DeviceRole).canConvert() || !item->isSelected()) + continue; + + MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); + if(!md) + continue; + + ++rowSelCount; + + switch(md->deviceType()) + { + // TODO: For now, don't allow creating/removing/renaming them until we decide on addressing strategy. + case MusECore::MidiDevice::ALSA_MIDI: + // Allow removing ('purging') an unavailable ALSA device. + if(md->isAddressUnknown()) + can_remove = true; + break; + + case MusECore::MidiDevice::JACK_MIDI: + can_remove = true; + can_rename = true; + break; + + case MusECore::MidiDevice::SYNTH_MIDI: + can_remove = true; + break; + } + + // Optimize: No need to check further. + if(can_rename && can_remove && rowSelCount >= 2) + break; + } + + // Only one rename at a time, for now... + renameDevice->setEnabled(can_rename && rowSelCount == 1); + removeDevice->setEnabled(can_remove); +} + +//--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MPConfig::songChanged(MusECore::SongChangedFlags_t flags) { - if(!(flags & (SC_CONFIG | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED))) + if(!(flags & (SC_CONFIG | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_MIDI_INSTRUMENT))) return; + addALSADevice->blockSignals(true); + addALSADevice->setChecked(MusEGlobal::midiSeq != NULL); + addALSADevice->blockSignals(false); + + // Get currently selected index... int no = -1; QTableWidgetItem* sitem = mdevView->currentItem(); @@ -1180,6 +1287,7 @@ } sitem = 0; + mdevView->blockSignals(true); mdevView->clearContents(); int defochs = 0; for (int i = MIDI_PORTS-1; i >= 0; --i) @@ -1191,30 +1299,37 @@ s.setNum(i+1); QTableWidgetItem* itemno = new QTableWidgetItem(s); addItem(i, DEVCOL_NO, itemno, mdevView); - itemno->setTextAlignment(Qt::AlignHCenter); + itemno->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); itemno->setFlags(Qt::ItemIsEnabled); + +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ QTableWidgetItem* itemstate = new QTableWidgetItem(port->state()); addItem(i, DEVCOL_STATE, itemstate, mdevView); itemstate->setFlags(Qt::ItemIsEnabled); +#endif + QTableWidgetItem* iteminstr = new QTableWidgetItem(port->instrument() ? port->instrument()->iname() : tr("")); addItem(i, DEVCOL_INSTR, iteminstr, mdevView); iteminstr->setFlags(Qt::ItemIsEnabled); + QTableWidgetItem* itemname = new QTableWidgetItem; addItem(i, DEVCOL_NAME, itemname, mdevView); itemname->setFlags(Qt::ItemIsEnabled); + +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ QTableWidgetItem* itemgui = new QTableWidgetItem; addItem(i, DEVCOL_GUI, itemgui, mdevView); - itemgui->setTextAlignment(Qt::AlignHCenter); + itemgui->setTextAlignment(Qt::AlignCenter); itemgui->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemrec = new QTableWidgetItem; addItem(i, DEVCOL_REC, itemrec, mdevView); - itemrec->setTextAlignment(Qt::AlignHCenter); + itemrec->setTextAlignment(Qt::AlignCenter); itemrec->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemplay = new QTableWidgetItem; addItem(i, DEVCOL_PLAY, itemplay, mdevView); - itemplay->setTextAlignment(Qt::AlignHCenter); + itemplay->setTextAlignment(Qt::AlignCenter); itemplay->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemout = new QTableWidgetItem; addItem(i, DEVCOL_OUTROUTES, itemout, mdevView); @@ -1222,11 +1337,13 @@ QTableWidgetItem* itemin = new QTableWidgetItem; addItem(i, DEVCOL_INROUTES, itemin, mdevView); itemin->setFlags(Qt::ItemIsEnabled); +#endif + // Ignore synth devices. Default input routes make no sense for them (right now). QTableWidgetItem* itemdefin = new QTableWidgetItem((dev && dev->isSynti()) ? QString() : MusECore::bitmap2String(port->defaultInChannels())); addItem(i, DEVCOL_DEF_IN_CHANS, itemdefin, mdevView); - // Enabled: Use editor (not good). Disabled: Use pop-up menu. DELETETHIS + // Enabled: Use editor (not good). Disabled: Use pop-up menu. #if 0 itemdefin->setFlags((dev && dev->isSynti()) ? Qt::NoItemFlags : Qt::ItemIsEditable | Qt::ItemIsEnabled); # else @@ -1239,12 +1356,7 @@ } #endif - // Turn on if and when multiple output routes are supported. DELETETHIS? - #if 0 - QTableWidgetItem* itemdefout = new QTableWidgetItem(MusECore::bitmap2String(port->defaultOutChannels())); - addItem(i, DEVCOL_DEF_OUT_CHANS, itemdefout, mdevView); - itemdefout->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled); - #else +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ QTableWidgetItem* itemdefout = new QTableWidgetItem(MusECore::bitmap2String(0)); defochs = port->defaultOutChannels(); if(defochs) @@ -1261,17 +1373,30 @@ addItem(i, DEVCOL_DEF_OUT_CHANS, itemdefout, mdevView); itemdefout->setFlags(Qt::ItemIsEnabled); itemdefout->setIcon(QIcon(*buttondownIcon)); - #endif +#else + QTableWidgetItem* itemdefout = new QTableWidgetItem(MusECore::bitmap2String(port->defaultOutChannels())); + addItem(i, DEVCOL_DEF_OUT_CHANS, itemdefout, mdevView); + itemdefout->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled); +#endif + if(dev && dev->isSynti()) //make deleted audio softsynths not show in ports dialog + { + MusECore::AudioTrack *_track = static_cast(static_cast(dev)); + MusECore::TrackList* tl = MusEGlobal::song->tracks(); + if(tl->find(_track) == tl->end()) + { + for(int __col = 0; __col < mdevView->columnCount(); ++__col) + { + mdevView->item(i, __col)->setFlags(Qt::NoItemFlags); + } + } + } mdevView->blockSignals(false); if (dev) { itemname->setText(dev->name()); - // Is it a Jack midi device? Allow renaming. - if (dev->deviceType() == MusECore::MidiDevice::JACK_MIDI) - itemname->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled); - +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ if (dev->rwFlags() & 0x2) itemrec->setIcon(dev->openFlags() & 2 ? QIcon(*dotIcon) : QIcon(*dothIcon)); else @@ -1280,24 +1405,23 @@ itemplay->setIcon( dev->openFlags() & 1 ? QIcon(*dotIcon) : QIcon(*dothIcon)); else itemplay->setIcon(QIcon(QPixmap())); +#endif } else { itemname->setText(tr("")); +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ itemgui->setIcon(QIcon(*dothIcon)); itemrec->setIcon(QIcon(QPixmap())); itemplay->setIcon(QIcon(QPixmap())); +#endif } + +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ if (port->hasNativeGui()) itemgui->setIcon(port->nativeGuiVisible() ? QIcon(*dotIcon) : QIcon(*dothIcon)); else itemgui->setIcon(QIcon(QPixmap())); - - if (!(dev && dev->isSynti())) - iteminstr->setIcon(QIcon(*buttondownIcon)); - - itemname->setIcon(QIcon(*buttondownIcon)); - - + if(dev && dev->deviceType() == MusECore::MidiDevice::JACK_MIDI) { if(dev->rwFlags() & 1) @@ -1317,13 +1441,21 @@ } } } - +#endif + + if (!(dev && dev->isSynti())) + iteminstr->setIcon(QIcon(*buttondownIcon)); + + itemname->setIcon(QIcon(*buttondownIcon)); + if(i == no) sitem = itemno; } if(sitem) mdevView->setCurrentItem(sitem); + mdevView->blockSignals(false); QString s; + synthList->blockSignals(true); synthList->clear(); for (std::vector::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) { @@ -1337,24 +1469,124 @@ item->setText(4, QString((*i)->version())); item->setText(5, QString((*i)->description())); } - instanceList->clear(); - MusECore::SynthIList* sl = MusEGlobal::song->syntis(); - for (MusECore::iSynthI si = sl->begin(); si != sl->end(); ++si) { - QTreeWidgetItem* iitem = new QTreeWidgetItem(instanceList); - iitem->setText(0, (*si)->name()); - iitem->setText(1, MusECore::synthType2String((*si)->synth()->synthType())); - if ((*si)->midiPort() == -1) - s = tr(""); + synthList->blockSignals(false); + instanceList->blockSignals(true); + instanceList->clearContents(); + instanceList->setRowCount(MusEGlobal::midiDevices.size()); + int row_cnt = 0; + for (MusECore::iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) { + MusECore::MidiDevice* md = *imd; + MusECore::SynthI* synth = 0; + if(md->isSynti()) + synth = static_cast(md); + QTableWidgetItem* iitem = new QTableWidgetItem(md->name()); + iitem->setData(DeviceRole, QVariant::fromValue(md)); + // Is it a Jack midi device? Allow renaming. + if(md->deviceType() == MusECore::MidiDevice::JACK_MIDI) + iitem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + else + iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + addInstItem(row_cnt, INSTCOL_NAME, iitem, instanceList); + iitem = new QTableWidgetItem(md->deviceTypeString()); + iitem->setData(DeviceRole, QVariant::fromValue(md)); + iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + addInstItem(row_cnt, INSTCOL_TYPE, iitem, instanceList); + +#ifdef _USE_EXTRA_INSTANCE_COLUMNS_ + iitem = new QTableWidgetItem; + iitem->setData(DeviceRole, QVariant::fromValue(md)); + iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + iitem->setTextAlignment(Qt::AlignCenter); + addInstItem(row_cnt, INSTCOL_REC, iitem, instanceList); + if(md->rwFlags() & 0x2) + iitem->setIcon(md->openFlags() & 2 ? QIcon(*dotIcon) : QIcon(*dothIcon)); + else + iitem->setIcon(QIcon(QPixmap())); + + iitem = new QTableWidgetItem; + iitem->setData(DeviceRole, QVariant::fromValue(md)); + iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + iitem->setTextAlignment(Qt::AlignCenter); + addInstItem(row_cnt, INSTCOL_PLAY, iitem, instanceList); + if(md->rwFlags() & 0x1) + iitem->setIcon(md->openFlags() & 1 ? QIcon(*dotIcon) : QIcon(*dothIcon)); + else + iitem->setIcon(QIcon(QPixmap())); + + iitem = new QTableWidgetItem; + iitem->setData(DeviceRole, QVariant::fromValue(md)); + iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + iitem->setTextAlignment(Qt::AlignCenter); + addInstItem(row_cnt, INSTCOL_GUI, iitem, instanceList); + if(synth && synth->hasNativeGui()) + iitem->setIcon(synth->nativeGuiVisible() ? QIcon(*dotIcon) : QIcon(*dothIcon)); else - s.setNum((*si)->midiPort() + 1); - iitem->setText(2, s); + iitem->setIcon(QIcon(QPixmap())); + + QTableWidgetItem* or_item = new QTableWidgetItem; + or_item->setData(DeviceRole, QVariant::fromValue(md)); + or_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + QTableWidgetItem* ir_item = new QTableWidgetItem; + ir_item->setData(DeviceRole, QVariant::fromValue(md)); + ir_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + if(md->deviceType() == MusECore::MidiDevice::JACK_MIDI) + { + if(md->rwFlags() & 1) + or_item->setIcon(QIcon(*routesMidiOutIcon)); + else + or_item->setIcon(QIcon()); + + if(md->rwFlags() & 2) + ir_item->setIcon(QIcon(*routesMidiInIcon)); + else + ir_item->setIcon(QIcon()); } + addInstItem(row_cnt, INSTCOL_OUTROUTES, or_item, instanceList); + addInstItem(row_cnt, INSTCOL_INROUTES, ir_item, instanceList); + + iitem = new QTableWidgetItem(md->state()); + iitem->setData(DeviceRole, QVariant::fromValue(md)); + iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + iitem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); + addInstItem(row_cnt, INSTCOL_STATE, iitem, instanceList); +#endif + + ++row_cnt; + } + instanceList->blockSignals(false); + + instanceList->resizeColumnToContents(INSTCOL_NAME); + instanceList->resizeColumnToContents(INSTCOL_TYPE); + instanceList->resizeColumnToContents(INSTCOL_STATE); + +#ifdef _USE_EXTRA_INSTANCE_COLUMNS_ + instanceList->resizeColumnToContents(INSTCOL_REC); + instanceList->resizeColumnToContents(INSTCOL_PLAY); + instanceList->resizeColumnToContents(INSTCOL_GUI); + instanceList->resizeColumnToContents(INSTCOL_OUTROUTES); + instanceList->resizeColumnToContents(INSTCOL_INROUTES); + instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_REC, QHeaderView::Fixed); + instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_PLAY, QHeaderView::Fixed); + instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_GUI, QHeaderView::Fixed); + instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_OUTROUTES, QHeaderView::Fixed); + instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_INROUTES, QHeaderView::Fixed); +#endif + + //instanceList->horizontalHeader()->setStretchLastSection( false ); + instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_STATE, QHeaderView::Stretch); + instanceList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); + deviceSelectionChanged(); + synthList->resizeColumnToContents(1); mdevView->resizeColumnsToContents(); - mdevView->horizontalHeader()->setResizeMode(DEVCOL_NO ,QHeaderView::Fixed); - mdevView->horizontalHeader()->setResizeMode(DEVCOL_REC ,QHeaderView::Fixed); - mdevView->horizontalHeader()->setResizeMode(DEVCOL_PLAY ,QHeaderView::Fixed); - mdevView->horizontalHeader()->setResizeMode(DEVCOL_GUI ,QHeaderView::Fixed); + mdevView->horizontalHeader()->setSectionResizeMode(DEVCOL_NO ,QHeaderView::Fixed); + +#ifndef _USE_EXTRA_INSTANCE_COLUMNS_ + mdevView->horizontalHeader()->setSectionResizeMode(DEVCOL_REC ,QHeaderView::Fixed); + mdevView->horizontalHeader()->setSectionResizeMode(DEVCOL_PLAY ,QHeaderView::Fixed); + mdevView->horizontalHeader()->setSectionResizeMode(DEVCOL_GUI ,QHeaderView::Fixed); +#endif + mdevView->horizontalHeader()->setStretchLastSection( true ); selectionChanged(); } @@ -1380,7 +1612,8 @@ MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* dev = port->device(); if (dev==0) { - MusEGlobal::midiSeq->msgSetMidiDevice(port, si); + MusEGlobal::audio->msgSetMidiDevice(port, si); + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); // save configuration file MusEGlobal::song->update(); break; @@ -1393,24 +1626,304 @@ //--------------------------------------------------------- void MPConfig::removeInstanceClicked() +{ + const int sz = instanceList->rowCount(); + if(sz == 0) + return; + + bool doupd = false; + + // Two passes: One for synths and one for all others (so far). + + // + // Others: + // + bool isIdle = false; + for(int i = 0; i < sz; ++i) + { + QTableWidgetItem* item = instanceList->item(i, INSTCOL_NAME); + if(!item || !item->data(DeviceRole).canConvert() || !item->isSelected()) + continue; + MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); + if(!md) + continue; + + switch(md->deviceType()) + { + // TODO: For now, don't allow creating/removing/renaming them until we decide on addressing strategy. + case MusECore::MidiDevice::ALSA_MIDI: + // Allow removing ('purging') an unavailable ALSA device. + if(!md->isAddressUnknown()) + break; + // Fall through. + case MusECore::MidiDevice::JACK_MIDI: + if(!isIdle) + { + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + isIdle = true; + } + if(md->midiPort() != -1) + MusEGlobal::midiPorts[md->midiPort()].setMidiDevice(0); + //MusEGlobal::midiDevices.erase(imd); + MusEGlobal::midiDevices.remove(md); + break; + + case MusECore::MidiDevice::SYNTH_MIDI: + break; + } + } + if(isIdle) + { + MusEGlobal::audio->msgIdle(false); + // Defer update until end, otherwise instanceList is wiped and + // rebuilt upon songChanged so next section won't work! + doupd = true; + } + + // + // Synths: + // + MusECore::Undo operations; + for(int i = 0; i < sz; ++i) + { + QTableWidgetItem* item = instanceList->item(i, INSTCOL_NAME); + if(!item || !item->data(DeviceRole).canConvert() || !item->isSelected()) + continue; + MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); + if(!md) + continue; + + switch(md->deviceType()) + { + case MusECore::MidiDevice::ALSA_MIDI: + case MusECore::MidiDevice::JACK_MIDI: + break; + + case MusECore::MidiDevice::SYNTH_MIDI: { - QTreeWidgetItem* item = instanceList->currentItem(); - if (item == 0) - return; - MusECore::SynthIList* sl = MusEGlobal::song->syntis(); - MusECore::iSynthI ii; - for (ii = sl->begin(); ii != sl->end(); ++ii) { - if( (*ii)->iname() == item->text(0) && - MusECore::synthType2String((*ii)->synth()->synthType()) == item->text(1) ) - break; - } - if (ii == sl->end()) { - printf("synthesizerConfig::removeInstanceClicked(): synthi not found\n"); - return; - } - MusEGlobal::audio->msgRemoveTrack(*ii); + MusECore::SynthI* s = dynamic_cast(md); + if(s) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(s), s)); } + break; + } + } + if(!operations.empty()) + MusEGlobal::song->applyOperationGroup(operations, true); + if(doupd) + MusEGlobal::song->update(SC_CONFIG); +} + +//--------------------------------------------------------- +// renameInstanceClicked +//--------------------------------------------------------- + +void MPConfig::renameInstanceClicked() +{ + QTableWidgetItem* item = instanceList->currentItem(); + if(!item) + return; + item = instanceList->item(item->row(), INSTCOL_NAME); + if(!item) + return; + // FIXME: How to know if the table is already in edit mode? The useful state() method is protected, + // and there don't appear to be any signals we can use. + if(item->flags().testFlag(Qt::ItemIsEditable) && item->flags().testFlag(Qt::ItemIsEnabled)) + instanceList->editItem(item); +} + +//--------------------------------------------------------- +// deviceItemClicked +//--------------------------------------------------------- + +void MPConfig::deviceItemClicked(QTableWidgetItem* item) +{ + if(!item) + return; + const int col = item->column(); + +#ifdef _USE_EXTRA_INSTANCE_COLUMNS_ + if(!item->data(DeviceRole).canConvert()) + return; + MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); + MusECore::SynthI* synth = 0; + if(md->isSynti()) + synth = static_cast(md); + int rwFlags = md->rwFlags(); + int openFlags = md->openFlags(); +#endif + + switch(col) + { + +#ifdef _USE_EXTRA_INSTANCE_COLUMNS_ + case INSTCOL_REC: + if(!(rwFlags & 2)) + return; + openFlags ^= 0x2; + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + md->setOpenFlags(openFlags); + if(md->midiPort() != -1) + MusEGlobal::midiPorts[md->midiPort()].setMidiDevice(md); // reopen device // FIXME: This causes jack crash with R+W Jack midi device + MusEGlobal::audio->msgIdle(false); + item->setIcon(openFlags & 2 ? QIcon(*dotIcon) : QIcon(*dothIcon)); + return; + case INSTCOL_PLAY: + if(!(rwFlags & 1)) + return; + openFlags ^= 0x1; + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + md->setOpenFlags(openFlags); + if(md->midiPort() != -1) + MusEGlobal::midiPorts[md->midiPort()].setMidiDevice(md); // reopen device FIXME: This causes jack crash with R+W Jack midi device + MusEGlobal::audio->msgIdle(false); + item->setIcon(openFlags & 1 ? QIcon(*dotIcon) : QIcon(*dothIcon)); + return; + case INSTCOL_GUI: + if(synth && synth->hasNativeGui()) + { + synth->showNativeGui(!synth->nativeGuiVisible()); + item->setIcon(synth->nativeGuiVisible() ? QIcon(*dotIcon) : QIcon(*dothIcon)); + } + return; + + case INSTCOL_INROUTES: + case INSTCOL_OUTROUTES: + { + if(!MusEGlobal::checkAudioDevice()) + return; + + if(MusEGlobal::audioDevice->deviceType() != MusECore::AudioDevice::JACK_AUDIO) // Only if Jack is running. + return; + + if(!md) + return; + + // Only Jack midi devices. + if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) + return; + + if(!(md->rwFlags() & ((col == INSTCOL_OUTROUTES) ? 1 : 2))) + return; + + RoutePopupMenu* pup = new RoutePopupMenu(); + pup->exec(QCursor::pos(), md, col == INSTCOL_OUTROUTES); + delete pup; + } + return; + +#endif + + } +} + +//--------------------------------------------------------- +// addJackDeviceClicked +//--------------------------------------------------------- + +void MPConfig::addJackDeviceClicked() +{ + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + // This automatically adds the device to the midiDevices list. + MusECore::MidiDevice* md = MusECore::MidiJackDevice::createJackMidiDevice(); + if(md) + md->setOpenFlags(3); // Start with open read + write. + MusEGlobal::audio->msgIdle(false); + if(md) + MusEGlobal::song->update(SC_CONFIG); +} + +//--------------------------------------------------------- +// addAlsaDeviceClicked +//--------------------------------------------------------- + +void MPConfig::addAlsaDeviceClicked(bool v) +{ + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + + MusEGlobal::config.enableAlsaMidiDriver = v; + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + //MusEGlobal::muse->changeConfig(true); // Save settings? No, wait till close. + + if(v) + { + // Initialize the ALSA driver. This will automatically initialize the sequencer thread if necessary. + MusECore::initMidiAlsa(); + + if(MusEGlobal::midiSeq) + { + // Now start the sequencer if necessary. Prio unused, set in start. + MusEGlobal::midiSeq->start(0); + // Update the timer poll file descriptors. + MusEGlobal::midiSeq->msgUpdatePollFd(); + } + + MusEGlobal::audio->msgIdle(false); + + // Scan for any changes in ALSA. FIXME: Note there's another another idle here ! + MusECore::alsaScanMidiPorts(); + + // Inform the rest of the app's gui. + MusEGlobal::song->update(SC_CONFIG); + } + else + { + // Exit ALSA midi. + MusECore::exitMidiAlsa(); + MusEGlobal::audio->msgIdle(false); + + // Scan for any changes in ALSA. FIXME: Note there's another another idle here ! + MusECore::alsaScanMidiPorts(); + + if(MusEGlobal::midiSeq) + { + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + + MusEGlobal::midiSeq->msgUpdatePollFd(); + MusEGlobal::midiSeq->stop(true); + + MusECore::exitMidiSequencer(); + + MusEGlobal::audio->msgIdle(false); + } + + // Inform the rest of the app's gui. + MusEGlobal::song->update(SC_CONFIG); + } +} + +//--------------------------------------------------------- +// beforeDeviceContextShow +//--------------------------------------------------------- + +void MPConfig::beforeDeviceContextShow(PopupMenu* /*menu*/, QAction* /*menuAction*/, QMenu* /*ctxMenu*/) +{ + DEBUG_PRST_ROUTES(stderr, "MPConfig::beforeDeviceContextShow\n"); +} + +//--------------------------------------------------------- +// deviceContextTriggered +//--------------------------------------------------------- + +void MPConfig::deviceContextTriggered(QAction* act) +{ + DEBUG_PRST_ROUTES(stderr, "MPConfig::deviceRemoveTriggered:%s\n", act->text().toLatin1().constData()); + if(act) + { +// TODO: Work in progress. +// +// PopupMenu* menu = 0; +// QAction* action = 0; +// QVariant var_val = 0; +// +// menu = act->data().value().menu(); +// action = act->data().value().action(); +// var_val = act->data().value().varValue(); +// +// DEBUG_PRST_ROUTES(stderr, " menu:%p action:%p var:%x md:%p\n", menu, action, var_val.toInt(), MusEGlobal::midiDevices.find(action->text()); + } +} //--------------------------------------------------------- // configMidiPorts diff -Nru muse-2.1.2/muse/confmport.h muse-3.0.2+ds1/muse/confmport.h --- muse-2.1.2/muse/confmport.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/confmport.h 2017-12-04 21:01:18.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: confmport.h,v 1.3 2004/01/25 11:20:31 wschweer Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -30,6 +31,10 @@ #include "ui_synthconfigbase.h" #include "type_defs.h" +// Temporary for testing migration of some port list columns over to the new device list. +// Make permanent later. +#define _USE_EXTRA_INSTANCE_COLUMNS_ + class QTreeWidget; class QTableWidget; class QPoint; @@ -50,26 +55,48 @@ class MPConfig : public QDialog, Ui::SynthConfigBase { Q_OBJECT - QMenu* instrPopup; - MusEGui::PopupMenu* defpup; + + enum InstanceRoles { DeviceRole = Qt::UserRole, DeviceTypeRole = Qt::UserRole + 1}; + #ifdef _USE_EXTRA_INSTANCE_COLUMNS_ + enum DeviceColumns { DEVCOL_NO = 0, DEVCOL_NAME, DEVCOL_INSTR, DEVCOL_DEF_IN_CHANS, DEVCOL_DEF_OUT_CHANS }; + #else + enum DeviceColumns { DEVCOL_NO = 0, DEVCOL_GUI, DEVCOL_REC, DEVCOL_PLAY, DEVCOL_INSTR, DEVCOL_NAME, + DEVCOL_INROUTES, DEVCOL_OUTROUTES, DEVCOL_DEF_IN_CHANS, DEVCOL_DEF_OUT_CHANS, DEVCOL_STATE }; + #endif + #ifdef _USE_EXTRA_INSTANCE_COLUMNS_ + enum InstanceColumns { INSTCOL_NAME = 0, INSTCOL_TYPE, INSTCOL_REC, INSTCOL_PLAY, INSTCOL_GUI, INSTCOL_INROUTES, INSTCOL_OUTROUTES, INSTCOL_STATE }; + #else + enum InstanceColumns { INSTCOL_NAME = 0, INSTCOL_TYPE, INSTCOL_STATE }; + #endif + + PopupMenu* defpup; int _showAliases; // -1: None. 0: First aliases. 1: Second aliases etc. void setWhatsThis(QTableWidgetItem *item, int col); void setToolTip(QTableWidgetItem *item, int col); + void setInstWhatsThis(QTableWidgetItem *item, int col); + void setInstToolTip(QTableWidgetItem *item, int col); void addItem(int row, int col, QTableWidgetItem *item, QTableWidget *table); - - + void addInstItem(int row, int col, QTableWidgetItem *item, QTableWidget *table); private slots: void rbClicked(QTableWidgetItem*); - void mdevViewItemRenamed(QTableWidgetItem*); + void DeviceItemRenamed(QTableWidgetItem*); void songChanged(MusECore::SongChangedFlags_t); void selectionChanged(); + void deviceSelectionChanged(); + void addJackDeviceClicked(); + void addAlsaDeviceClicked(bool); void addInstanceClicked(); + void renameInstanceClicked(); void removeInstanceClicked(); + //void deviceItemClicked(QTreeWidgetItem*, int); + void deviceItemClicked(QTableWidgetItem* item); void changeDefInputRoutes(QAction* act); void changeDefOutputRoutes(QAction* act); void apply(); void okClicked(); + void beforeDeviceContextShow(PopupMenu* menu, QAction* menuAction, QMenu* ctxMenu); + void deviceContextTriggered(QAction*); public slots: void closeEvent(QCloseEvent*e); diff -Nru muse-2.1.2/muse/controlfifo.h muse-3.0.2+ds1/muse/controlfifo.h --- muse-2.1.2/muse/controlfifo.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/controlfifo.h 2017-12-04 21:01:18.000000000 +0000 @@ -40,7 +40,7 @@ // Whether or not the event is from a synth or effect's own GUI. bool fromGui; unsigned long idx; - float value; + double value; unsigned long frame; }; diff -Nru muse-2.1.2/muse/ctrl/CMakeLists.txt muse-3.0.2+ds1/muse/ctrl/CMakeLists.txt --- muse-2.1.2/muse/ctrl/CMakeLists.txt 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/ctrl/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP (ctrl_mocs +QT5_WRAP_CPP (ctrl_mocs # configmidictrl.h # ctrldialog.h # definemidictrl.h diff -Nru muse-2.1.2/muse/ctrl/ctrlcanvas.cpp muse-3.0.2+ds1/muse/ctrl/ctrlcanvas.cpp --- muse-2.1.2/muse/ctrl/ctrlcanvas.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/ctrl/ctrlcanvas.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -167,6 +167,14 @@ } //--------------------------------------------------------- +// setSelected +//--------------------------------------------------------- +void CEvent::setSelected(bool v) +{ + MusEGlobal::song->selectEvent(_event, _part, v); +} + +//--------------------------------------------------------- // clearDelete //--------------------------------------------------------- @@ -460,11 +468,6 @@ if(editor->deleting()) // Ignore while while deleting to prevent crash. return; - //printf("CtrlCanvas::songChanged type:%x\n", type); - // Is it simply a midi controller value adjustment? Forget it. - if(type == SC_MIDI_CONTROLLER) - return; - if(type & SC_CONFIG) setFont(MusEGlobal::config.fonts[3]); @@ -476,13 +479,13 @@ // config window generates a type of -1, we can eliminate // some other useless calls using SC_CONFIG, which was not used // anywhere else in muse before now, except song header. - if((type & (SC_CONFIG | SC_DRUMMAP)) || ((type & (SC_PART_MODIFIED | SC_SELECTION)) && changed)) + if((type & (SC_CONFIG | SC_DRUM_SELECTION | SC_PIANO_SELECTION | SC_DRUMMAP)) || ((type & (SC_PART_MODIFIED | SC_SELECTION)) && changed)) setMidiController(_cnum); if(!curPart) return; - if(type & (SC_CONFIG | SC_DRUMMAP | SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED)) + if(type & (SC_CONFIG | SC_DRUM_SELECTION | SC_PIANO_SELECTION | SC_DRUMMAP | SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED)) updateItems(); else if(type & SC_SELECTION) updateSelections(); @@ -522,9 +525,9 @@ } MusECore::MidiTrack* mt = part->track(); - MusECore::MidiPort* mp; - int di; - int n; + MusECore::MidiPort* mp = NULL; + int di = 0; + int n = 0; if((curDrumPitch >= 0) && ((num & 0xff) == 0xff)) { @@ -538,18 +541,23 @@ mport = mt->outPort(); mp = &MusEGlobal::midiPorts[mport]; } - if(mt->type() == MusECore::Track::NEW_DRUM) + else if(mt->type() == MusECore::Track::NEW_DRUM) { - n = di; // Simple one-to-one correspondence. - mp = &MusEGlobal::midiPorts[mt->outPort()]; + n = (num & ~0xff) | mt->drummap()[curDrumPitch].anote; + // Default to track port if -1 and track channel if -1. + int mport = mt->drummap()[curDrumPitch].port; + if(mport == -1) + mport = mt->outPort(); + mp = &MusEGlobal::midiPorts[mport]; + + } else if(mt->type() == MusECore::Track::MIDI) { n = di; // Simple one-to-one correspondence. There is no 'mapping' for piano roll midi - yet. mp = &MusEGlobal::midiPorts[mt->outPort()]; } - } - + } else { di = num; @@ -608,14 +616,13 @@ if (filterTrack && part->track() != curTrack) continue; - MusECore::EventList* el = part->events(); MusECore::MidiCtrlValList* mcvl; partControllers(part, _cnum, 0, 0, 0, &mcvl); unsigned len = part->lenTick(); - for (MusECore::iEvent i = el->begin(); i != el->end(); ++i) + for (MusECore::ciEvent i = part->events().begin(); i != part->events().end(); ++i) { - MusECore::Event e = i->second; + const MusECore::Event& e = i->second; // Do not add events which are past the end of the part. if(e.tick() >= len) break; @@ -623,10 +630,17 @@ if(_cnum == MusECore::CTRL_VELOCITY && e.type() == MusECore::Note) { newev = 0; + // Zero note on vel is not allowed now. + int vel = e.velo(); + if(vel == 0) + { + fprintf(stderr, "CtrlCanvas::updateItems: Warning: Event has zero note on velocity!\n"); + vel = 1; + } if (curDrumPitch == -1 || !_perNoteVeloMode) // and NOT >=0 - items.add(newev = new CEvent(e, part, e.velo())); + items.add(newev = new CEvent(e, part, vel)); else if (e.dataA() == curDrumPitch) //same note. if curDrumPitch==-2, this never is true - items.add(newev = new CEvent(e, part, e.velo())); + items.add(newev = new CEvent(e, part, vel)); if(newev && e.selected()) selection.push_back(newev); } @@ -654,6 +668,30 @@ continue; ctl = (ctl & ~0xff) | MusEGlobal::drumMap[ctl & 0x7f].anote; } + else if(part->track() && part->track()->type() == MusECore::Track::NEW_DRUM && (_cnum & 0xff) == 0xff) + { + if(curDrumPitch < 0) + continue; + // Default to track port if -1 and track channel if -1. + int port = part->track()->drummap()[ctl & 0x7f].port; + if(port == -1) + port = part->track()->outPort(); + int chan = part->track()->drummap()[ctl & 0x7f].channel; + if(chan == -1) + chan = part->track()->outChannel(); + + int cur_port = part->track()->drummap()[curDrumPitch].port; + if(cur_port == -1) + cur_port = part->track()->outPort(); + int cur_chan = part->track()->drummap()[curDrumPitch].channel; + if(cur_chan == -1) + cur_chan = part->track()->outChannel(); + + if((port != cur_port) || (chan != cur_chan)) + continue; + ctl = (ctl & ~0xff) | part->track()->drummap()[ctl & 0x7f].anote; + } + if(ctl == _dnum) { if(mcvl && last.empty()) @@ -814,7 +852,10 @@ QPoint pos = event->pos(); QPoint dist = pos - start; - bool moving = dist.y() >= 3 || dist.y() <= 3 || dist.x() >= 3 || dist.x() <= 3; + + bool moving = dist.y() >= 3 || dist.y() <= -3 + || dist.x() >= 3 || dist.x() <= -3; + switch (drag) { case DRAG_LASSO_START: if (!moving) @@ -964,13 +1005,10 @@ if (event.empty()) continue; int x = event.tick() + curPartTick; - if (x < xx1) continue; - if (x >= xx2) break; - // Do port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, event, curPart, true, true)); } @@ -1004,7 +1042,6 @@ } else event.setB(nval); - // Do port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, event, curPart, true, true)); } @@ -1049,10 +1086,12 @@ ev->setVal(nval); if (type == MusECore::CTRL_VELOCITY) { + if(nval > 127) nval = 127; + else if(nval <= 0) nval = 1; // Zero note on vel is not allowed. + if ((event.velo() != nval)) { MusECore::Event newEvent = event.clone(); newEvent.setVelo(nval); - ev->setEvent(newEvent); // Do not do port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, curPart, false, false)); } @@ -1062,7 +1101,6 @@ if ((event.dataB() != nval)) { MusECore::Event newEvent = event.clone(); newEvent.setB(nval); - ev->setEvent(newEvent); // Do port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, curPart, true, true)); } @@ -1096,11 +1134,13 @@ MusECore::Event event = ev->event(); if (type == MusECore::CTRL_VELOCITY) { + if(newval > 127) newval = 127; + else if(newval <= 0) newval = 1; // Zero note on vel is not allowed. + if ((event.velo() != newval)) { ev->setVal(newval); MusECore::Event newEvent = event.clone(); newEvent.setVelo(newval); - ev->setEvent(newEvent); // Indicate no undo, and do not do port controller values and clone parts. MusEGlobal::audio->msgChangeEvent(event, newEvent, curPart, false, false, false); changed = true; @@ -1121,7 +1161,6 @@ if ((event.dataB() != nval)) { MusECore::Event newEvent = event.clone(); newEvent.setB(nval); - ev->setEvent(newEvent); // Indicate no undo, and do port controller values and clone parts. MusEGlobal::audio->msgChangeEvent(event, newEvent, curPart, false, true, true); changed = true; @@ -1226,9 +1265,6 @@ { MusECore::Event newEvent = event.clone(); newEvent.setB(nval); - - ev->setEvent(newEvent); - // Indicate no undo, and do port controller values and clone parts. MusEGlobal::audio->msgChangeEvent(event, newEvent, curPart, false, true, true); @@ -1332,7 +1368,7 @@ // But with the raster 'off' there were some problems not catching some items, and jagged drawing. // Possibly there are slight differences between rasterVal1() and rasterVal2() - not reciprocals of each other? // Also the loops below were getting complicated to ignore that first position. - // So I draw from the first x. (TODO The idea may work now since I wrote this - more work was done.) p4.0.18 Tim. + // So I draw from the first x. (TODO The idea may work now since I wrote this - more work was done.) Tim. int xx1 = editor->rasterVal1(x1); @@ -1634,7 +1670,8 @@ MusECore::MidiPort* mp; int cnum = _cnum; bool is_drum_ctl = (mt->type() == MusECore::Track::DRUM) && (curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff); - + bool is_newdrum_ctl = (mt->type() == MusECore::Track::NEW_DRUM) && (curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff); + if(is_drum_ctl) { // Default to track port if -1 and track channel if -1. @@ -1644,6 +1681,15 @@ mp = &MusEGlobal::midiPorts[mport]; cnum = (_cnum & ~0xff) | MusEGlobal::drumMap[curDrumPitch].anote; } + else if(is_newdrum_ctl) + { + // Default to track port if -1 and track channel if -1. + int mport = mt->drummap()[curDrumPitch].port; + if(mport == -1) + mport = mt->outPort(); + mp = &MusEGlobal::midiPorts[mport]; + cnum = (_cnum & ~0xff) | mt->drummap()[curDrumPitch].anote; + } else mp = &MusEGlobal::midiPorts[mt->outPort()]; @@ -1676,7 +1722,7 @@ continue; MusECore::Event ev = e->event(); // Draw drum controllers from another drum on top of ones from this drum. - if(is_drum_ctl && ev.type() == MusECore::Controller && ev.dataA() != _didx) + if((is_drum_ctl || is_newdrum_ctl) && ev.type() == MusECore::Controller && ev.dataA() != _didx) continue; int tick = mapx(!ev.empty() ? ev.tick() + e->part()->tick() : 0); int val = e->val(); @@ -1773,7 +1819,8 @@ MusECore::MidiPort* mp; int cnum = _cnum; bool is_drum_ctl = (mt->type() == MusECore::Track::DRUM) && (curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff); - + bool is_newdrum_ctl = (mt->type() == MusECore::Track::NEW_DRUM) && (curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff); + if(is_drum_ctl) { // Default to track port if -1 and track channel if -1. @@ -1783,6 +1830,15 @@ mp = &MusEGlobal::midiPorts[mport]; cnum = (_cnum & ~0xff) | MusEGlobal::drumMap[curDrumPitch].anote; } + else if(is_newdrum_ctl) + { + // Default to track port if -1 and track channel if -1. + int mport = mt->drummap()[curDrumPitch].port; + if(mport == -1) + mport = mt->outPort(); + mp = &MusEGlobal::midiPorts[mport]; + cnum = (_cnum & ~0xff) | mt->drummap()[curDrumPitch].anote; + } else mp = &MusEGlobal::midiPorts[mt->outPort()]; @@ -1816,9 +1872,9 @@ MusECore::Event ev = e->event(); // Draw drum controllers from another drum on top of ones from this drum. // FIXME TODO Finish this off, not correct yet. - if(drum_ctl == -1 && is_drum_ctl && ev.type() == MusECore::Controller && ev.dataA() != _didx) + if(drum_ctl == -1 && (is_drum_ctl || is_newdrum_ctl) && ev.type() == MusECore::Controller && ev.dataA() != _didx) continue; - if(drum_ctl != -1 && (!is_drum_ctl || (ev.type() == MusECore::Controller && ev.dataA() == _didx))) + if(drum_ctl != -1 && (!(is_drum_ctl || is_newdrum_ctl) || (ev.type() == MusECore::Controller && ev.dataA() == _didx))) continue; int tick = mapx(!ev.empty() ? ev.tick() + e->part()->tick() : 0); int val = e->val(); @@ -1950,7 +2006,24 @@ pdrawExtraDrumCtrlItems(p, rect, curPart, anote); } } - + else if(curPart && curPart->track() && curPart->track()->type() == MusECore::Track::NEW_DRUM && + curDrumPitch >= 0 && ((_cnum & 0xff) == 0xff)) + { + // Default to track port if -1 and track channel if -1. + int port = curPart->track()->drummap()[curDrumPitch].port; + if(port == -1) + port = curPart->track()->outPort(); + int anote = curPart->track()->drummap()[curDrumPitch].anote; + for(int i = 0; i < DRUM_MAPSIZE; ++i) + { + int iport = curPart->track()->drummap()[i].port; + if(iport == -1) + iport = curPart->track()->outPort(); + if(i != curDrumPitch && iport == port && curPart->track()->drummap()[i].anote == anote) + pdrawExtraDrumCtrlItems(p, rect, curPart, anote); + } + } + if(velo) { // Draw fg velocity items for the current part @@ -2090,6 +2163,14 @@ else curDrumPitch = -2; // this means "invalid", but not "unused" } + + // Is it a drum controller? + if((curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff)) + { + // Recompose the canvas according to the new selected pitch. + setMidiController(_cnum); + updateItems(); + } } void CtrlCanvas::curPartHasChanged(MusECore::Part*) diff -Nru muse-2.1.2/muse/ctrl/ctrlcanvas.h muse-3.0.2+ds1/muse/ctrl/ctrlcanvas.h --- muse-2.1.2/muse/ctrl/ctrlcanvas.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/ctrl/ctrlcanvas.h 2017-12-04 21:01:18.000000000 +0000 @@ -65,8 +65,8 @@ CEvent(MusECore::Event e, MusECore::MidiPart* part, int v); MusECore::Event event() const { return _event; } void setEvent(MusECore::Event& ev) { _event = ev; } - bool selected() const { return !_event.empty() && _event.selected(); } - void setSelected(bool v) { if(!_event.empty()) _event.setSelected(v); } + bool selected() const { return _event.selected(); } + void setSelected(bool v); int val() const { return _val; } void setVal(int v) { _val = v; } void setEX(int v) { ex = v; } diff -Nru muse-2.1.2/muse/ctrl/ctrlpanel.cpp muse-3.0.2+ds1/muse/ctrl/ctrlpanel.cpp --- muse-2.1.2/muse/ctrl/ctrlpanel.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/ctrl/ctrlpanel.cpp 2017-12-17 21:07:38.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // $Id: ctrlpanel.cpp,v 1.10.2.9 2009/06/14 05:24:45 terminator356 Exp $ // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012, 2017 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 @@ -33,14 +33,15 @@ #include #include #include +#include #include +#include "globaldefs.h" #include "app.h" #include "globals.h" #include "midictrl.h" #include "instruments/minstrument.h" -#include "editinstrument.h" #include "midiport.h" #include "mididev.h" #include "xml.h" @@ -52,10 +53,13 @@ #include "midiedit/drummap.h" #include "gconfig.h" #include "song.h" -#include "knob.h" -#include "doublelabel.h" -#include "midi.h" +#include "compact_knob.h" +#include "compact_slider.h" +#include "lcd_widgets.h" +#include "utils.h" + #include "audio.h" +#include "midi.h" #include "menutitleitem.h" #include "popupmenu.h" #include "helper.h" @@ -72,6 +76,16 @@ { setObjectName(name); inHeartBeat = true; + + //setFocusPolicy(Qt::NoFocus); + + _knob = 0; + _slider = 0; + _patchEdit = 0; + _veloPerNoteButton = 0; + + _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; + _showval = MusEGlobal::config.showControlValues; editor = e; ctrlcanvas = c; setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); @@ -80,17 +94,17 @@ bbox->setSpacing (0); vbox->addLayout(bbox); vbox->addStretch(); - QHBoxLayout* kbox = new QHBoxLayout; - QHBoxLayout* dbox = new QHBoxLayout; + kbox = new QHBoxLayout; vbox->addLayout(kbox); - vbox->addLayout(dbox); vbox->addStretch(); vbox->setContentsMargins(0, 0, 0, 0); bbox->setContentsMargins(0, 0, 0, 0); kbox->setContentsMargins(0, 0, 0, 0); - dbox->setContentsMargins(0, 0, 0, 0); + vbox->setSpacing (0); + kbox->setSpacing(0); selCtrl = new QPushButton(tr("S"), this); + selCtrl->setContentsMargins(0, 0, 0, 0); selCtrl->setFocusPolicy(Qt::NoFocus); selCtrl->setFont(MusEGlobal::config.fonts[3]); selCtrl->setFixedHeight(20); @@ -100,6 +114,7 @@ // destroy button QPushButton* destroy = new QPushButton(tr("X"), this); + destroy->setContentsMargins(0, 0, 0, 0); destroy->setFocusPolicy(Qt::NoFocus); destroy->setFont(MusEGlobal::config.fonts[3]); destroy->setFixedHeight(20); @@ -112,69 +127,153 @@ _track = 0; _ctrl = 0; - _val = MusECore::CTRL_VAL_UNKNOWN; _dnum = -1; - _knob = new Knob(this); - _knob->setFixedWidth(25); - _knob->setFixedHeight(25); - _knob->setToolTip(tr("manual adjust")); - _knob->setRange(0.0, 127.0, 1.0); - _knob->setValue(0.0); - _knob->setEnabled(false); - _knob->hide(); - _knob->setAltFaceColor(Qt::red); - - _dl = new DoubleLabel(-1.0, 0.0, +127.0, this); - _dl->setPrecision(0); - _dl->setToolTip(tr("ctrl-double-click on/off")); - _dl->setSpecialText(tr("off")); - _dl->setFont(MusEGlobal::config.fonts[1]); - _dl->setBackgroundRole(QPalette::Mid); - _dl->setFrame(true); - _dl->setFixedWidth(36); - _dl->setFixedHeight(15); - _dl->setEnabled(false); - _dl->hide(); - - connect(_knob, SIGNAL(sliderMoved(double,int)), SLOT(ctrlChanged(double))); - connect(_knob, SIGNAL(sliderRightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int))); - connect(_dl, SIGNAL(valueChanged(double,int)), SLOT(ctrlChanged(double))); - connect(_dl, SIGNAL(ctrlDoubleClicked(int)), SLOT(labelDoubleClicked())); - - _veloPerNoteButton = new PixmapButton(veloPerNote_OnIcon, veloPerNote_OffIcon, 2, this); // Margin = 2 - _veloPerNoteButton->setFocusPolicy(Qt::NoFocus); - _veloPerNoteButton->setCheckable(true); - _veloPerNoteButton->setToolTip(tr("all/per-note velocity mode")); - _veloPerNoteButton->setEnabled(false); - _veloPerNoteButton->hide(); - connect(_veloPerNoteButton, SIGNAL(clicked()), SLOT(velPerNoteClicked())); - bbox->addStretch(); bbox->addWidget(selCtrl); bbox->addWidget(destroy); bbox->addStretch(); - kbox->addStretch(); - kbox->addWidget(_knob); - kbox->addWidget(_veloPerNoteButton); - kbox->addStretch(); - dbox->addStretch(); - dbox->addWidget(_dl); - dbox->addStretch(); - + + buildPanel(); + setController(); + configChanged(); + connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); inHeartBeat = false; setLayout(vbox); } + +void CtrlPanel::buildPanel() +{ + if(_veloPerNoteButton) + { + delete _veloPerNoteButton; + _veloPerNoteButton = 0; + } + + if(_slider) + { + delete _slider; + _slider = 0; + } + + if(_knob) + { + delete _knob; + _knob = 0; + } + + if(_patchEdit) + { + delete _patchEdit; + _patchEdit = 0; + } + + _patchEdit = new LCDPatchEdit(this); + _patchEdit->setReadoutOrientation(LCDPatchEdit::PatchVertical); + _patchEdit->setValue(MusECore::CTRL_VAL_UNKNOWN); + _patchEdit->setFocusPolicy(Qt::NoFocus); + // Don't allow anything here, it interferes with the CompactPatchEdit which sets it's own controls' tooltips. + //control->setToolTip(d->_toolTipText); + _patchEdit->setEnabled(false); + _patchEdit->hide(); + _patchEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + _patchEdit->setContentsMargins(0, 0, 0, 0); + + _patchEdit->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + if(_patchEdit->font() != MusEGlobal::config.fonts[1]) + { + _patchEdit->setFont(MusEGlobal::config.fonts[1]); + _patchEdit->setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); + } + + connect(_patchEdit, SIGNAL(valueChanged(int,int)), SLOT(patchCtrlChanged(int))); + connect(_patchEdit, SIGNAL(rightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int))); + + kbox->addWidget(_patchEdit); + + if(_preferKnobs) + { + _knob = new CompactKnob(this, "CtrlPanelKnob", CompactKnob::Bottom); + _knob->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + _knob->setToolTip(tr("Manual adjust (Ctrl-double-click on/off)")); + //_knob->setFocusPolicy(Qt::NoFocus); + _knob->setRange(0.0, 127.0, 1.0); + _knob->setValue(0.0); + _knob->setEnabled(false); + _knob->hide(); + _knob->setHasOffMode(true); + _knob->setOff(true); + _knob->setValueDecimals(0); + _knob->setFaceColor(MusEGlobal::config.midiControllerSliderDefaultColor); + _knob->setStep(1.0); + _knob->setShowLabel(false); + _knob->setShowValue(true); + _knob->setEnableValueToolTips(false); // FIXME: Tooltip just gets in the way! + _knob->setShowValueToolTipsOnHover(false); // + + if(_knob->font() != MusEGlobal::config.fonts[1]) + { + _knob->setFont(MusEGlobal::config.fonts[1]); + _knob->setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); + } + + connect(_knob, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(ctrlChanged(double,bool,int,int))); + connect(_knob, SIGNAL(sliderRightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int))); + + kbox->addWidget(_knob); + } + else + { + _slider = new CompactSlider(this, "CtrlPanelSlider"); + _slider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + _slider->setToolTip(tr("manual adjust (Ctrl-double-click on/off)")); + //_slider->setFocusPolicy(Qt::NoFocus); + _slider->setRange(0.0, 127.0, 1.0); + _slider->setValue(0.0); + _slider->setEnabled(false); + _slider->hide(); + _slider->setHasOffMode(true); + _slider->setOff(true); + _slider->setValueDecimals(0); + _slider->setBarColor(MusEGlobal::config.sliderDefaultColor); + _slider->setStep(1.0); + _slider->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + _slider->setEnableValueToolTips(false); // FIXME: Tooltip just gets in the way! + _slider->setShowValueToolTipsOnHover(false); // + + if(_slider->font() != MusEGlobal::config.fonts[1]) + { + _slider->setFont(MusEGlobal::config.fonts[1]); + _slider->setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); + } + + connect(_slider, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(ctrlChanged(double,bool,int,int))); + connect(_slider, SIGNAL(sliderRightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int))); + + kbox->addWidget(_slider); + } + + _veloPerNoteButton = new PixmapButton(veloPerNote_OnIcon, veloPerNote_OffIcon, 2, this); // Margin = 2 + _veloPerNoteButton->setFocusPolicy(Qt::NoFocus); + _veloPerNoteButton->setCheckable(true); + _veloPerNoteButton->setToolTip(tr("all/per-note velocity mode")); + _veloPerNoteButton->setEnabled(false); + _veloPerNoteButton->hide(); + connect(_veloPerNoteButton, SIGNAL(clicked()), SLOT(velPerNoteClicked())); + + kbox->addWidget(_veloPerNoteButton); +} + //--------------------------------------------------------- // heartBeat //--------------------------------------------------------- void CtrlPanel::heartBeat() { - if(editor->deleting()) // Ignore while while deleting to prevent crash. + if(editor && editor->deleting()) // Ignore while while deleting to prevent crash. return; inHeartBeat = true; @@ -183,65 +282,210 @@ { if(_dnum != MusECore::CTRL_VELOCITY) { - int outport; - int chan; + int outport = _track->outPort(); + int chan = _track->outChannel(); int cdp = ctrlcanvas->getCurDrumPitch(); - if(_track->type() == MusECore::Track::DRUM && _ctrl->isPerNoteController() && cdp != -1) + if(_ctrl->isPerNoteController() && cdp >= 0) { - // Default to track port if -1 and track channel if -1. - outport = MusEGlobal::drumMap[cdp].port; - if(outport == -1) - outport = _track->outPort(); - chan = MusEGlobal::drumMap[cdp].channel; - if(chan == -1) - chan = _track->outChannel(); - } - else - { - outport = _track->outPort(); - chan = _track->outChannel(); - } - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport]; - - int v = mp->hwCtrlState(chan, _dnum); - if(v == MusECore::CTRL_VAL_UNKNOWN) - { - // MusEGui::DoubleLabel ignores the value if already set... - _dl->setValue(_dl->off() - 1.0); - _val = MusECore::CTRL_VAL_UNKNOWN; - v = mp->lastValidHWCtrlState(chan, _dnum); - if(v != MusECore::CTRL_VAL_UNKNOWN && ((_dnum != MusECore::CTRL_PROGRAM) || ((v & 0xff) != 0xff) )) - { - if(_dnum == MusECore::CTRL_PROGRAM) - v = (v & 0x7f) + 1; - else - // Auto bias... - v -= _ctrl->bias(); - if (double(v) != _knob->value()) - _knob->setValue(double(v)); + if(_track->type() == MusECore::Track::DRUM) + { + // Default to track port if -1 and track channel if -1. + outport = MusEGlobal::drumMap[cdp].port; + if(outport == -1) + outport = _track->outPort(); + chan = MusEGlobal::drumMap[cdp].channel; + if(chan == -1) + chan = _track->outChannel(); + } + else if(_track->type() == MusECore::Track::NEW_DRUM) + { + // Default to track port if -1 and track channel if -1. + outport = _track->drummap()[cdp].port; + if(outport == -1) + outport = _track->outPort(); + chan = _track->drummap()[cdp].channel; + if(chan == -1) + chan = _track->outChannel(); } } - else if(v != _val) + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport]; + MusECore::MidiCtrlValListList* mcvll = mp->controller(); + + MusECore::ciMidiCtrlValList imcvl = mcvll->find(chan, _dnum); + const bool enable = imcvl != mcvll->end() && !_track->off(); + +// REMOVE Tim. midi. Added. +// Although we do this for the midi strip control racks (and probably shouldn't), +// we need the control to be enabled - user would expect to be able to adjust it +// even if controller was not found - our code will create the controller. +// if(_knob) +// { +// if(_knob->isEnabled() != enable) +// _knob->setEnabled(enable); +// } +// else if(_slider) +// { +// if(_slider->isEnabled() != enable) +// _slider->setEnabled(enable); +// } + + if(enable) { - _val = v; - if(v == MusECore::CTRL_VAL_UNKNOWN || ((_dnum == MusECore::CTRL_PROGRAM) && ((v & 0xff) == 0xff) )) + MusECore::MidiCtrlValList* mcvl = imcvl->second; + + int hwVal = mcvl->hwVal(); + + if(_patchEdit && _dnum == MusECore::CTRL_PROGRAM) { - _dl->setValue(_dl->off() - 1.0); + // Special for new LCD patch edit control: Need to give both current and last values. + // Keeping a local last value with the control won't work. + _patchEdit->blockSignals(true); + _patchEdit->setLastValidPatch(mcvl->lastValidHWVal()); + _patchEdit->setLastValidBytes(mcvl->lastValidByte2(), mcvl->lastValidByte1(), mcvl->lastValidByte0()); + _patchEdit->setValue(hwVal); + _patchEdit->blockSignals(false); } else { - if(_dnum == MusECore::CTRL_PROGRAM) - v = (v & 0x7f) + 1; - else - // Auto bias... - v -= _ctrl->bias(); - - _knob->setValue(double(v)); - _dl->setValue(double(v)); - } - } - } - } + int min = 0; + int max = 127; + int bias = 0; + int initval = 0; + MusECore::MidiController* mc = mp->midiController(_dnum); + if(mc) + { + bias = mc->bias(); + min = mc->minVal(); + max = mc->maxVal(); + initval = mc->initVal(); + if(initval == MusECore::CTRL_VAL_UNKNOWN) + initval = 0; + } + + const double dmin = (double)min; + const double dmax = (double)max; + + if(_knob) + { + const double c_dmin = _knob->minValue(); + const double c_dmax = _knob->maxValue(); + if(c_dmin != min && c_dmax != max) + { + _knob->blockSignals(true); + _knob->setRange(dmin, dmax, 1.0); + _knob->blockSignals(false); + } + else if(c_dmin != min) + { + _knob->blockSignals(true); + _knob->setMinValue(min); + _knob->blockSignals(false); + } + else if(c_dmax != max) + { + _knob->blockSignals(true); + _knob->setMaxValue(max); + _knob->blockSignals(false); + } + + if(hwVal == MusECore::CTRL_VAL_UNKNOWN) + { + hwVal = mcvl->lastValidHWVal(); + if(hwVal == MusECore::CTRL_VAL_UNKNOWN) + { + hwVal = initval; + if(!_knob->isOff() || hwVal != _knob->value()) + { + _knob->blockSignals(true); + _knob->setValueState(hwVal, true); + _knob->blockSignals(false); + } + } + else + { + hwVal -= bias; + if(!_knob->isOff() || hwVal != _knob->value()) + { + _knob->blockSignals(true); + _knob->setValueState(hwVal, true); + _knob->blockSignals(false); + } + } + } + else + { + hwVal -= bias; + if(_knob->isOff() || hwVal != _knob->value()) + { + _knob->blockSignals(true); + _knob->setValueState(hwVal, false); + _knob->blockSignals(false); + } + } + } + else if(_slider) + { + const double c_dmin = _slider->minValue(); + const double c_dmax = _slider->maxValue(); + if(c_dmin != min && c_dmax != max) + { + _slider->blockSignals(true); + _slider->setRange(dmin, dmax, 1.0); + _slider->blockSignals(false); + } + else if(c_dmin != min) + { + _slider->blockSignals(true); + _slider->setMinValue(min); + _slider->blockSignals(false); + } + else if(c_dmax != max) + { + _slider->blockSignals(true); + _slider->setMaxValue(max); + _slider->blockSignals(false); + } + + if(hwVal == MusECore::CTRL_VAL_UNKNOWN) + { + hwVal = mcvl->lastValidHWVal(); + if(hwVal == MusECore::CTRL_VAL_UNKNOWN) + { + hwVal = initval; + if(!_slider->isOff() || hwVal != _slider->value()) + { + _slider->blockSignals(true); + _slider->setValueState(hwVal, true); + _slider->blockSignals(false); + } + } + else + { + hwVal -= bias; + if(!_slider->isOff() || hwVal != _slider->value()) + { + _slider->blockSignals(true); + _slider->setValueState(hwVal, true); + _slider->blockSignals(false); + } + } + } + else + { + hwVal -= bias; + if(_slider->isOff() || hwVal != _slider->value()) + { + _slider->blockSignals(true); + _slider->setValueState(hwVal, false); + _slider->blockSignals(false); + } + } + } + } + } + } + } inHeartBeat = false; } @@ -250,136 +494,76 @@ // configChanged //--------------------------------------------------------- -void CtrlPanel::configChanged() +void CtrlPanel::configChanged() { - songChanged(SC_CONFIG); + songChanged(SC_CONFIG); + + // Detect when knobs are preferred and rebuild. + if(_preferKnobs != MusEGlobal::config.preferKnobsVsSliders) + { + _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; + buildPanel(); + setController(); + } + + if(_patchEdit) + { + if(_patchEdit->font() != MusEGlobal::config.fonts[1]) + { + _patchEdit->setFont(MusEGlobal::config.fonts[1]); + _patchEdit->setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); + } + _patchEdit->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + } + + if(_knob) + { + if(_knob->font() != MusEGlobal::config.fonts[1]) + { + _knob->setFont(MusEGlobal::config.fonts[1]); + _knob->setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); + } + + // Whether to show values along with labels for certain controls. + //_knob->setShowValue(MusEGlobal::config.showControlValues); + } + + if(_slider) + { + if(_slider->font() != MusEGlobal::config.fonts[1]) + { + _slider->setFont(MusEGlobal::config.fonts[1]); + _slider->setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); + } + + _slider->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + // Whether to show values along with labels for certain controls. + //_slider->setShowValue(MusEGlobal::config.showControlValues); + } + + setControlColor(); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- -void CtrlPanel::songChanged(MusECore::SongChangedFlags_t type) +void CtrlPanel::songChanged(MusECore::SongChangedFlags_t /*flags*/) { - if(editor->deleting()) // Ignore while while deleting to prevent crash. + if(editor && editor->deleting()) // Ignore while while deleting to prevent crash. return; - - // Is it simply a midi controller value adjustment? Forget it. - if(type == SC_MIDI_CONTROLLER) - return; } -//--------------------------------------------------------- -// labelDoubleClicked -//--------------------------------------------------------- - -void CtrlPanel::labelDoubleClicked() +void CtrlPanel::patchCtrlChanged(int val) { - if(!_track || !_ctrl || _dnum == -1) - return; - - int outport; - int chan; - int cdp = ctrlcanvas->getCurDrumPitch(); - if(_track->type() == MusECore::Track::DRUM && _ctrl->isPerNoteController() && cdp != -1) - { - // Default to track port if -1 and track channel if -1. - outport = MusEGlobal::drumMap[cdp].port; - if(outport == -1) - outport = _track->outPort(); - chan = MusEGlobal::drumMap[cdp].channel; - if(chan == -1) - chan = _track->outChannel(); - } - else - { - outport = _track->outPort(); - chan = _track->outChannel(); - } - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport]; - int lastv = mp->lastValidHWCtrlState(chan, _dnum); - - int curv = mp->hwCtrlState(chan, _dnum); - -// if(_dnum == MusECore::CTRL_AFTERTOUCH) -// { -// // Auto bias... -// ival += _ctrl->bias(); -// MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_AFTERTOUCH, ival, 0); -// MusEGlobal::audio->msgPlayMidiEvent(&ev); -// } -// else - if(_dnum == MusECore::CTRL_PROGRAM) - { - if(curv == MusECore::CTRL_VAL_UNKNOWN || ((curv & 0xffffff) == 0xffffff)) - { - // If no value has ever been set yet, use the current knob value - // (or the controller's initial value?) to 'turn on' the controller. - if(lastv == MusECore::CTRL_VAL_UNKNOWN || ((lastv & 0xffffff) == 0xffffff)) - { - int kiv = lrint(_knob->value()); - --kiv; - kiv &= 0x7f; - kiv |= 0xffff00; - MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_CONTROLLER, _dnum, kiv); - MusEGlobal::audio->msgPlayMidiEvent(&ev); - } - else - { - MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_CONTROLLER, _dnum, lastv); - MusEGlobal::audio->msgPlayMidiEvent(&ev); - } - } - else - { - //if((curv & 0xffff00) == 0xffff00) DELETETHIS? - //{ - ////if(mp->hwCtrlState(chan, _dnum) != MusECore::CTRL_VAL_UNKNOWN) - MusEGlobal::audio->msgSetHwCtrlState(mp, chan, _dnum, MusECore::CTRL_VAL_UNKNOWN); - //} - //else - //{ - // MusECore::MidiPlayEvent ev(MusEGlobal::song->cpos(), outport, chan, MusECore::ME_CONTROLLER, _dnum, (curv & 0xffff00) | 0xff); - // MusEGlobal::audio->msgPlayMidiEvent(&ev); - //} - } - } - else - { - if(curv == MusECore::CTRL_VAL_UNKNOWN) - { - // If no value has ever been set yet, use the current knob value - // (or the controller's initial value?) to 'turn on' the controller. - if(lastv == MusECore::CTRL_VAL_UNKNOWN) - { - int kiv = lrint(_knob->value()); - if(kiv < _ctrl->minVal()) - kiv = _ctrl->minVal(); - if(kiv > _ctrl->maxVal()) - kiv = _ctrl->maxVal(); - kiv += _ctrl->bias(); - MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_CONTROLLER, _dnum, kiv); - MusEGlobal::audio->msgPlayMidiEvent(&ev); - } - else - { - MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_CONTROLLER, _dnum, lastv); - MusEGlobal::audio->msgPlayMidiEvent(&ev); - } - } - else - { - MusEGlobal::audio->msgSetHwCtrlState(mp, chan, _dnum, MusECore::CTRL_VAL_UNKNOWN); - } - } - MusEGlobal::song->update(SC_MIDI_CONTROLLER); + ctrlChanged(double(val), false, _dnum, 0); } //--------------------------------------------------------- // ctrlChanged //--------------------------------------------------------- -void CtrlPanel::ctrlChanged(double val) +void CtrlPanel::ctrlChanged(double val, bool off, int /*id*/, int /*scrollMode*/) { if (inHeartBeat) return; @@ -387,173 +571,260 @@ return; int ival = lrint(val); - - int outport; - int chan; + int outport = _track->outPort(); + int chan = _track->outChannel(); + if(chan < 0 || chan >= MIDI_CHANNELS || outport < 0 || outport >= MIDI_PORTS) + return; int cdp = ctrlcanvas->getCurDrumPitch(); - if(_track->type() == MusECore::Track::DRUM && _ctrl->isPerNoteController() && cdp != -1) - { - // Default to track port if -1 and track channel if -1. - outport = MusEGlobal::drumMap[cdp].port; - if(outport == -1) - outport = _track->outPort(); - chan = MusEGlobal::drumMap[cdp].channel; - if(chan == -1) - chan = _track->outChannel(); - } - else + if(_ctrl->isPerNoteController() && cdp >= 0) { - outport = _track->outPort(); - chan = _track->outChannel(); + if(_track->type() == MusECore::Track::DRUM) + { + // Default to track port if -1 and track channel if -1. + outport = MusEGlobal::drumMap[cdp].port; + if(outport == -1) + outport = _track->outPort(); + chan = MusEGlobal::drumMap[cdp].channel; + if(chan == -1) + chan = _track->outChannel(); + } + else if(_track->type() == MusECore::Track::NEW_DRUM) + { + // Default to track port if -1 and track channel if -1. + outport = _track->drummap()[cdp].port; + if(outport == -1) + outport = _track->outPort(); + chan = _track->drummap()[cdp].channel; + if(chan == -1) + chan = _track->outChannel(); + } } + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport]; - int curval = mp->hwCtrlState(chan, _dnum); - -// if(_dnum == MusECore::CTRL_AFTERTOUCH) -// { -// // Auto bias... -// ival += _ctrl->bias(); -// MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_AFTERTOUCH, ival, 0); -// MusEGlobal::audio->msgPlayMidiEvent(&ev); -// } -// else - if(_dnum == MusECore::CTRL_PROGRAM) - { - --ival; - ival &= 0x7f; - - if(curval == MusECore::CTRL_VAL_UNKNOWN) - ival |= 0xffff00; - else - ival |= (curval & 0xffff00); - MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_CONTROLLER, _dnum, ival); - MusEGlobal::audio->msgPlayMidiEvent(&ev); - } - else // Shouldn't happen, but... - if((ival < _ctrl->minVal()) || (ival > _ctrl->maxVal())) - { - if(curval != MusECore::CTRL_VAL_UNKNOWN) - MusEGlobal::audio->msgSetHwCtrlState(mp, chan, _dnum, MusECore::CTRL_VAL_UNKNOWN); - } - else - { + if(off || ival < _ctrl->minVal() || ival > _ctrl->maxVal()) + ival = MusECore::CTRL_VAL_UNKNOWN; + + if(ival != MusECore::CTRL_VAL_UNKNOWN) // Auto bias... ival += _ctrl->bias(); - - MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_CONTROLLER, _dnum, ival); - MusEGlobal::audio->msgPlayMidiEvent(&ev); - } - MusEGlobal::song->update(SC_MIDI_CONTROLLER); + + MusECore::MidiPlayEvent ev(MusEGlobal::audio->curFrame(), outport, chan, MusECore::ME_CONTROLLER, _dnum, ival); + mp->putEvent(ev); } //--------------------------------------------------------- -// setHWController +// setController //--------------------------------------------------------- -void CtrlPanel::setHWController(MusECore::MidiTrack* t, MusECore::MidiController* ctrl) -{ - inHeartBeat = true; - - _track = t; _ctrl = ctrl; - +void CtrlPanel::setController() +{ if(!_track || !_ctrl) { - _knob->setEnabled(false); - _dl->setEnabled(false); - _knob->hide(); - _dl->hide(); + if(_patchEdit) + { + _patchEdit->setEnabled(false); + _patchEdit->hide(); + } + + if(_knob) + { + _knob->setEnabled(false); + _knob->hide(); + } + + if(_slider) + { + _slider->setEnabled(false); + _slider->hide(); + } inHeartBeat = false; return; } - - MusECore::MidiPort* mp; - int ch; + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[_track->outPort()]; + int ch = _track->outChannel(); int cdp = ctrlcanvas->getCurDrumPitch(); _dnum = _ctrl->num(); - if(_track->type() == MusECore::Track::DRUM && _ctrl->isPerNoteController() && cdp != -1) + if(_ctrl->isPerNoteController() && cdp >= 0) { - _dnum = (_dnum & ~0xff) | MusEGlobal::drumMap[cdp].anote; - int mport = MusEGlobal::drumMap[cdp].port; - // Default to track port if -1 and track channel if -1. - if(mport == -1) - mport = _track->outPort(); - mp = &MusEGlobal::midiPorts[mport]; - ch = MusEGlobal::drumMap[cdp].channel; - if(ch == -1) - ch = _track->outChannel(); - } - else if((_track->type() == MusECore::Track::NEW_DRUM || _track->type() == MusECore::Track::MIDI) && _ctrl->isPerNoteController() && cdp != -1) - { - _dnum = (_dnum & ~0xff) | cdp; //FINDMICHJETZT does that work? - mp = &MusEGlobal::midiPorts[_track->outPort()]; - ch = _track->outChannel(); - } - else - { - mp = &MusEGlobal::midiPorts[_track->outPort()]; - ch = _track->outChannel(); + if(_track->type() == MusECore::Track::DRUM) + { + _dnum = (_dnum & ~0xff) | MusEGlobal::drumMap[cdp].anote; + int mport = MusEGlobal::drumMap[cdp].port; + // Default to track port if -1 and track channel if -1. + if(mport == -1) + mport = _track->outPort(); + mp = &MusEGlobal::midiPorts[mport]; + ch = MusEGlobal::drumMap[cdp].channel; + if(ch == -1) + ch = _track->outChannel(); + } + else if(_track->type() == MusECore::Track::NEW_DRUM) + { + _dnum = (_dnum & ~0xff) | _track->drummap()[cdp].anote; + int mport = _track->drummap()[cdp].port; + // Default to track port if -1 and track channel if -1. + if(mport == -1) + mport = _track->outPort(); + mp = &MusEGlobal::midiPorts[mport]; + ch = _track->drummap()[cdp].channel; + if(ch == -1) + ch = _track->outChannel(); + } + else if(_track->type() == MusECore::Track::MIDI) + { + _dnum = (_dnum & ~0xff) | cdp; //FINDMICHJETZT does that work? + } } - + if(_dnum == MusECore::CTRL_VELOCITY) { - _knob->setEnabled(false); - _dl->setEnabled(false); - _knob->hide(); - _dl->hide(); + if(_patchEdit) + { + _patchEdit->setEnabled(false); + _patchEdit->hide(); + } + + if(_knob) + { + _knob->setEnabled(false); + _knob->hide(); + } + + if(_slider) + { + _slider->setEnabled(false); + _slider->hide(); + } _veloPerNoteButton->setEnabled(true); _veloPerNoteButton->show(); } else { - _knob->setEnabled(true); - _dl->setEnabled(true); _veloPerNoteButton->setEnabled(false); _veloPerNoteButton->hide(); - double dlv; + + MusECore::MidiCtrlValListList* mcvll = mp->controller(); + int mn; int mx; int v; if(_dnum == MusECore::CTRL_PROGRAM) { - mn = 1; - mx = 128; - v = mp->hwCtrlState(ch, _dnum); - _val = v; - _knob->setRange(double(mn), double(mx), 1.0); - _dl->setRange(double(mn), double(mx)); - if(v == MusECore::CTRL_VAL_UNKNOWN || ((v & 0xffffff) == 0xffffff)) + if(_patchEdit) { - int lastv = mp->lastValidHWCtrlState(ch, _dnum); - if(lastv == MusECore::CTRL_VAL_UNKNOWN || ((lastv & 0xffffff) == 0xffffff)) + _patchEdit->setEnabled(true); + if(_knob) + { + _knob->setEnabled(false); + _knob->hide(); + } + if(_slider) + { + _slider->setEnabled(false); + _slider->hide(); + } + MusECore::ciMidiCtrlValList imcvl = mcvll->find(ch, _dnum); + if(imcvl != mcvll->end()) { - int initv = _ctrl->initVal(); - if(initv == MusECore::CTRL_VAL_UNKNOWN || ((initv & 0xffffff) == 0xffffff)) - v = 1; - else - v = (initv + 1) & 0xff; - } - else - v = (lastv + 1) & 0xff; - - if(v > 128) - v = 128; - dlv = _dl->off() - 1.0; - } + MusECore::MidiCtrlValList* mcvl = imcvl->second; + int hwVal = mcvl->hwVal(); + // Special for new LCD patch edit control: Need to give both current and last values. + // Keeping a local last value with the control won't work. + _patchEdit->blockSignals(true); + _patchEdit->setLastValidPatch(mcvl->lastValidHWVal()); + _patchEdit->setLastValidBytes(mcvl->lastValidByte2(), mcvl->lastValidByte1(), mcvl->lastValidByte0()); + _patchEdit->setValue(hwVal); + _patchEdit->blockSignals(false); + } + _patchEdit->show(); + _patchEdit->update(); + } else { - v = (v + 1) & 0xff; - if(v > 128) - v = 128; - dlv = double(v); + if(_knob) + { + _knob->setEnabled(true); + if(_slider) + { + _slider->setEnabled(false); + _slider->hide(); + } + } + else if(_slider) + _slider->setEnabled(true); + + mn = 1; + mx = 128; + v = mp->hwCtrlState(ch, _dnum); + + if(_knob) + _knob->setRange(double(mn), double(mx), 1.0); + else if(_slider) + _slider->setRange(double(mn), double(mx), 1.0); + if(v == MusECore::CTRL_VAL_UNKNOWN || ((v & 0xffffff) == 0xffffff)) + { + int lastv = mp->lastValidHWCtrlState(ch, _dnum); + if(lastv == MusECore::CTRL_VAL_UNKNOWN || ((lastv & 0xffffff) == 0xffffff)) + { + int initv = _ctrl->initVal(); + if(initv == MusECore::CTRL_VAL_UNKNOWN || ((initv & 0xffffff) == 0xffffff)) + v = 1; + else + v = (initv + 1) & 0xff; + } + else + v = (lastv + 1) & 0xff; + + if(v > 128) + v = 128; + } + else + { + v = (v + 1) & 0xff; + if(v > 128) + v = 128; + } + + if(_knob) + { + _knob->setValue(double(v)); + _knob->show(); + _knob->update(); + } + else if(_slider) + { + _slider->setValue(double(v)); + _slider->show(); + _slider->update(); + } } } else { + if(_patchEdit) + { + _patchEdit->setEnabled(false); + _patchEdit->hide(); + } + if(_knob) + { + _knob->setEnabled(true); + if(_slider) + { + _slider->setEnabled(false); + _slider->hide(); + } + } + else if(_slider) + _slider->setEnabled(true); + mn = _ctrl->minVal(); mx = _ctrl->maxVal(); v = mp->hwCtrlState(ch, _dnum); - _val = v; - _knob->setRange(double(mn), double(mx), 1.0); - _dl->setRange(double(mn), double(mx)); + if(_knob) + _knob->setRange(double(mn), double(mx), 1.0); + else if(_slider) + _slider->setRange(double(mn), double(mx), 1.0); if(v == MusECore::CTRL_VAL_UNKNOWN) { int lastv = mp->lastValidHWCtrlState(ch, _dnum); @@ -561,30 +832,87 @@ { if(_ctrl->initVal() == MusECore::CTRL_VAL_UNKNOWN) v = 0; - else + else v = _ctrl->initVal(); } - else + else v = lastv - _ctrl->bias(); - dlv = _dl->off() - 1.0; - } + } else { // Auto bias... v -= _ctrl->bias(); - dlv = double(v); + } + + if(_knob) + { + _knob->setValue(double(v)); + _knob->show(); + _knob->update(); + } + else if(_slider) + { + _slider->setValue(double(v)); + _slider->show(); + _slider->update(); } } - _knob->setValue(double(v)); - _dl->setValue(dlv); - - _knob->show(); - _dl->show(); - // Incomplete drawing sometimes. Update fixes it. - _knob->update(); - _dl->update(); } + + setControlColor(); +} + +void CtrlPanel::setControlColor() +{ + if(_dnum == -1) + return; + QColor color = MusEGlobal::config.sliderDefaultColor; + + switch(_dnum) + { + case MusECore::CTRL_PANPOT: + color = MusEGlobal::config.panSliderColor; + break; + + case MusECore::CTRL_PROGRAM: + color = MusEGlobal::config.midiPatchReadoutColor; + break; + + default: + color = MusEGlobal::config.midiControllerSliderDefaultColor; + break; + } + + if(_patchEdit) + { + _patchEdit->setReadoutColor(color); + } + + if(_knob) + { + _knob->setFaceColor(color); + } + + if(_slider) + { + _slider->setBorderColor(color); + _slider->setBarColor(MusEGlobal::config.sliderBarDefaultColor); + } +} + +//--------------------------------------------------------- +// setHWController +//--------------------------------------------------------- + +void CtrlPanel::setHWController(MusECore::MidiTrack* t, MusECore::MidiController* ctrl) +{ + inHeartBeat = true; + + _track = t; _ctrl = ctrl; + + setController(); + inHeartBeat = false; } @@ -646,7 +974,7 @@ } else if (rv == edit_ins) { // edit instrument MusECore::MidiInstrument* instr = port->instrument(); - MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrument::Controllers); + MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrumentControllers); } else { // Select a control MusECore::iMidiCtrlValList i = cll->find(channel, rv); @@ -673,7 +1001,8 @@ int cdp = ctrlcanvas->getCurDrumPitch(); int ctlnum = _ctrl->num(); - if(_track->type() == MusECore::Track::DRUM && _ctrl->isPerNoteController() && cdp != -1) + if((_track->type() == MusECore::Track::DRUM || _track->type() == MusECore::Track::NEW_DRUM) && + _ctrl->isPerNoteController() && cdp >= 0) //ctlnum = (ctlnum & ~0xff) | MusEGlobal::drumMap[cdp].enote; DELETETHIS or which of them is correct? ctlnum = (ctlnum & ~0xff) | cdp; diff -Nru muse-2.1.2/muse/ctrl/ctrlpanel.h muse-3.0.2+ds1/muse/ctrl/ctrlpanel.h --- muse-2.1.2/muse/ctrl/ctrlpanel.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/ctrl/ctrlpanel.h 2017-12-17 21:07:38.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // $Id: ctrlpanel.h,v 1.2.2.5 2009/06/10 00:34:59 terminator356 Exp $ // (C) Copyright 1999-2001 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012, 2017 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 @@ -30,6 +30,7 @@ class QPushButton; class QAction; +class QHBoxLayout; namespace MusECore { class MidiController; @@ -38,11 +39,12 @@ } namespace MusEGui { -class DoubleLabel; -class Knob; class MidiEditor; class CtrlCanvas; class PixmapButton; +class CompactKnob; +class CompactSlider; +class LCDPatchEdit; //--------------------------------------------------------- // CtrlPanel @@ -59,18 +61,29 @@ MusECore::MidiController* _ctrl; int _dnum; bool inHeartBeat; - MusEGui::Knob* _knob; - MusEGui::DoubleLabel* _dl; - int _val; + + QHBoxLayout* kbox; + CompactKnob* _knob; + CompactSlider* _slider; + LCDPatchEdit* _patchEdit; + // Current local state of knobs versus sliders preference global setting. + bool _preferKnobs; + // Current local state of show values preference global setting. + bool _showval; + PixmapButton* _veloPerNoteButton; + void buildPanel(); + void setController(); + void setControlColor(); + signals: void destroyPanel(); void controllerChanged(int); private slots: - void ctrlChanged(double val); - void labelDoubleClicked(); + void patchCtrlChanged(int val); + void ctrlChanged(double val, bool off, int id, int scrollMode); void ctrlRightClicked(const QPoint& p, int id); void ctrlPopupTriggered(QAction* act); void velPerNoteClicked(); diff -Nru muse-2.1.2/muse/ctrl.cpp muse-3.0.2+ds1/muse/ctrl.cpp --- muse-2.1.2/muse/ctrl.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/ctrl.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -6,7 +6,7 @@ // controller handling for mixer automation // // (C) Copyright 2003 Werner Schweer (ws@seh.de) -// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2013 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 @@ -227,7 +227,7 @@ .arg(chan) .arg(mctrl) .arg(actrl); - xml.tag(level++, s.toAscii().constData()); + xml.tag(level++, s.toLatin1().constData()); // TODO //const MidiAudioCtrlStruct& macs = imacs->second; @@ -359,6 +359,13 @@ initColor(id); } +CtrlList::CtrlList(const CtrlList& l, int flags) +{ + _id = l._id; + _valueType = l._valueType; + assign(l, flags | ASSIGN_PROPERTIES); +} + //--------------------------------------------------------- // assign //--------------------------------------------------------- @@ -367,14 +374,12 @@ { if(flags & ASSIGN_PROPERTIES) { - _id = l._id; _default = l._default; _curVal = l._curVal; _mode = l._mode; _name = l._name; _min = l._min; _max = l._max; - _valueType = l._valueType; _dontShow = l._dontShow; _displayColor = l._displayColor; _visible = l._visible; @@ -388,6 +393,129 @@ } //--------------------------------------------------------- +// getInterpolation +// Fills CtrlInterpolate struct for given frame. +// cur_val_only means read the current 'manual' value, not from the list even if it is not empty. +// CtrlInterpolate member eFrame can be -1 meaning no next value (wide-open, endless). +//--------------------------------------------------------- + +void CtrlList::getInterpolation(int frame, bool cur_val_only, CtrlInterpolate* interp) +{ + interp->eStop = false; // During processing, control FIFO ring buffers will set this true. + + if(cur_val_only || empty()) + { + interp->sFrame = 0; + interp->eFrame = -1; + interp->sVal = _curVal; + interp->eVal = _curVal; + interp->doInterp = false; + return; + } + 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; + interp->sFrame = 0; + interp->eFrame = -1; + interp->sVal = i->second.val; + interp->eVal = i->second.val; + interp->doInterp = false; + return; + } + else if(_mode == DISCRETE) + { + if(i == begin()) + { + interp->sFrame = 0; + interp->eFrame = i->second.frame; + interp->sVal = i->second.val; + interp->eVal = i->second.val; + interp->doInterp = false; + } + else + { + interp->eFrame = i->second.frame; + interp->eVal = i->second.val; + --i; + interp->sFrame = i->second.frame; + interp->sVal = i->second.val; + interp->doInterp = false; + } + } + else // INTERPOLATE + { + if(i == begin()) + { + interp->sFrame = 0; + interp->eFrame = i->second.frame; + interp->sVal = i->second.val; + interp->eVal = i->second.val; + interp->doInterp = false; + } + else + { + interp->eFrame = i->second.frame; + interp->eVal = i->second.val; + --i; + interp->sFrame = i->second.frame; + interp->sVal = i->second.val; + interp->doInterp = (interp->eVal != interp->sVal && interp->eFrame > interp->sFrame); + } + } +} + +//--------------------------------------------------------- +// interpolate +// Returns interpolated value at given frame, from a CtrlInterpolate struct. +// For speed, no checking is done for frame = frame2, val1 = val2 or even CtrlInterpolate::doInterp. +// Those are to be taken care of before calling this routine. See getInterpolation(). +//--------------------------------------------------------- + +double CtrlList::interpolate(int frame, const CtrlInterpolate& interp) +{ + int frame1 = interp.sFrame; + int frame2 = interp.eFrame; + double val1 = interp.sVal; + double val2 = interp.eVal; + if(frame >= frame2) // frame2 can also be -1 + { + if(_valueType == VAL_LOG) + { + const double min = exp10(MusEGlobal::config.minSlider / 20.0); // TODO Try fastexp10 + if(val2 < min) + val2 = min; + } + return val2; + } + if(frame <= frame1) + { + if(_valueType == VAL_LOG) + { + const double min = exp10(MusEGlobal::config.minSlider / 20.0); // TODO Try fastexp10 + if(val1 < min) + val1 = min; + } + return val1; + } + + if(_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 (_valueType == VAL_LOG) + val1 = exp10(val1/20.0); + return val1; +} + +//--------------------------------------------------------- // value // Returns value at frame. // cur_val_only means read the current 'manual' value, not from the list even if it is not empty. @@ -558,6 +686,15 @@ return res; } +void CtrlList::insert(iCtrl first, iCtrl last) +{ +#ifdef _CTRL_DEBUG_ + printf("CtrlList::insert3 first frame:%d last frame:%d\n", first->first, last->first); +#endif + std::map >::insert(first, last); + _guiUpdatePending = true; +} + void CtrlList::erase(iCtrl ictl) { #ifdef _CTRL_DEBUG_ @@ -818,13 +955,13 @@ 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(); + QString s= QString("controller id=\"%1\" cur=\"%2\"").arg(cl->id()).arg(cl->curVal()); s += QString(" color=\"%1\" visible=\"%2\"").arg(cl->color().name()).arg(cl->isVisible()); - xml.tag(level++, s.toAscii().constData()); + xml.tag(level++, s.toLatin1().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()); + xml.nput(level, s.arg(ic->second.frame).arg(ic->second.val).toLatin1().constData()); ++i; if (i >= 4) { xml.put(level, ""); diff -Nru muse-2.1.2/muse/ctrl.h muse-3.0.2+ds1/muse/ctrl.h --- muse-2.1.2/muse/ctrl.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/ctrl.h 2017-12-04 21:01:18.000000000 +0000 @@ -6,7 +6,7 @@ // controller for mixer automation // // (C) Copyright 2003-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2013 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 @@ -31,7 +31,12 @@ #include #include #include + +#ifdef OSC_SUPPORT #include +#else +#include +#endif #define AC_PLUGIN_CTL_BASE 0x1000 #define AC_PLUGIN_CTL_BASE_POW 12 @@ -51,6 +56,32 @@ enum CtrlRecValueType { ARVT_VAL, ARVT_START, ARVT_STOP }; //--------------------------------------------------------- +// CtrlInterpolate +// Controller interpolation values. +// For speed: Can be filled once by CtrlList::getInterpolation(), +// then passed repeatedly to CtrlList::interpolate(). +//--------------------------------------------------------- + +struct CtrlInterpolate { + int sFrame; // Starting frame. Always valid. Can be less than any first CtrlList item's frame, or zero ! + double sVal; // Value at starting frame. + int eFrame; // Ending frame. Can be -1 meaning endless. + double eVal; // Value at ending frame, or sVal if eFrame is -1. + bool eStop; // Whether to stop refreshing this struct from CtrlList upon eFrame. Control FIFO ring buffers + // set this true and replace eFrame and eVal. Upon the next run slice, if eStop is set, eval + // should be copied to sVal, eFrame to sFrame, doInterp cleared, and eFrame set to some frame or -1. + bool doInterp; // Whether to actually interpolate whenever this struct is passed to CtrlList::interpolate(). + CtrlInterpolate(int sframe = 0, int eframe = -1, double sval = 0.0, double eval = 0.0, bool end_stop = false, bool do_interpolate = false) { + sFrame = sframe; + sVal = sval; + eFrame = eframe; + eVal = eval; + eStop = end_stop; + doInterp = do_interpolate; + } + }; + +//--------------------------------------------------------- // CtrlVal // controller "event" //--------------------------------------------------------- @@ -85,6 +116,7 @@ }; typedef CtrlRecList::iterator iCtrlRec; +typedef CtrlRecList::const_iterator ciCtrlRec; //--------------------------------------------------------- // MidiAudioCtrlMap @@ -159,11 +191,13 @@ CtrlList(bool dontShow=false); CtrlList(int id, bool dontShow=false); CtrlList(int id, QString name, double min, double max, CtrlValueType v, bool dontShow=false); + CtrlList(const CtrlList& l, int flags); void assign(const CtrlList& l, int flags); void swap(CtrlList&); std::pair insert(const std::pair& p); iCtrl insert(iCtrl ic, const std::pair& p); + void insert(iCtrl first, iCtrl last); void erase(iCtrl ictl); size_type erase(int frame); void erase(iCtrl first, iCtrl last); @@ -180,6 +214,8 @@ int id() const { return _id; } QString name() const { return _name; } void setName(const QString& s) { _name = s; } + double minVal() const { return _min; } + double maxVal() const { return _max; } void setRange(double min, double max) { _min = min; _max = max; @@ -190,7 +226,9 @@ } CtrlValueType valueType() const { return _valueType; } void setValueType(CtrlValueType t) { _valueType = t; } - + void getInterpolation(int frame, bool cur_val_only, CtrlInterpolate* interp); + double interpolate(int frame, const CtrlInterpolate& interp); + double value(int frame, bool cur_val_only = false, int* nextFrame = NULL) const; void add(int frame, double value); void del(int frame); diff -Nru muse-2.1.2/muse/driver/alsamidi.cpp muse-3.0.2+ds1/muse/driver/alsamidi.cpp --- muse-2.1.2/muse/driver/alsamidi.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/alsamidi.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // $Id: alsamidi.cpp,v 1.8.2.7 2009/11/19 04:20:33 terminator356 Exp $ // (C) Copyright 2000-2001 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,32 +21,41 @@ // //========================================================= +#include "alsamidi.h" + +#ifdef ALSA_SUPPORT + #include -#include "alsamidi.h" #include "globals.h" #include "midi.h" -//#include "mididev.h" #include "../midiport.h" #include "../midiseq.h" #include "../midictrl.h" #include "../audio.h" -//#include "mpevent.h" -//#include "sync.h" +#include "minstrument.h" #include "utils.h" +#include "helper.h" #include "audiodev.h" #include "xml.h" #include "part.h" #include "gconfig.h" #include "track.h" +#include "song.h" +#include "muse_atomic.h" +#include "lock_free_buffer.h" +#include "evdata.h" #include // Enable debugging: //#define ALSA_DEBUG 1 +// For debugging output: Uncomment the fprintf section. +#define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); namespace MusECore { +muse_atomic_t atomicAlsaMidiScanPending; static int alsaSeqFdi = -1; static int alsaSeqFdo = -1; @@ -56,16 +65,61 @@ static snd_seq_addr_t announce_adr; //--------------------------------------------------------- +// createAlsaMidiDevice +// If name parameter is blank, creates a new (locally) unique one. +//--------------------------------------------------------- + +MidiDevice* MidiAlsaDevice::createAlsaMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable 3: Writable + Readable +{ + int ni = 0; + if(name.isEmpty()) + { + for( ; ni < 65536; ++ni) + { + name = QString("alsa-midi-") + QString::number(ni); + if(!MusEGlobal::midiDevices.find(name)) + break; + } + } + if(ni >= 65536) + { + fprintf(stderr, "MusE: createAlsaMidiDevice failed! Can't find an unused midi device name 'alsa-midi-[0-65535]'.\n"); + return 0; + } + + snd_seq_addr_t a; + + // From seq.h: "Special client (port) ids SND_SEQ_ADDRESS_UNKNOWN 253 = unknown source" + // Hopefully we can use that as a 'valid' marker here. We can't use zero. + a.client = SND_SEQ_ADDRESS_UNKNOWN; + a.port = SND_SEQ_ADDRESS_UNKNOWN; + + MidiAlsaDevice* dev = new MidiAlsaDevice(a, name); + + dev->setrwFlags(rwflags); + MusEGlobal::midiDevices.add(dev); + return dev; +} + + +//--------------------------------------------------------- // MidiAlsaDevice //--------------------------------------------------------- MidiAlsaDevice::MidiAlsaDevice(const snd_seq_addr_t& a, const QString& n) : MidiDevice(n) { +// _playEventFifo = new LockFreeBuffer(8192); adr = a; init(); } +MidiAlsaDevice::~MidiAlsaDevice() +{ +// if(_playEventFifo) +// delete _playEventFifo; +} + //--------------------------------------------------------- // selectWfd //--------------------------------------------------------- @@ -83,78 +137,104 @@ { _openFlags &= _rwFlags; // restrict to available bits - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca(&pinfo); - int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo); - if(rv < 0) - { - printf("MidiAlsaDevice::open Error getting port info: adr: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv)); - return QString(snd_strerror(rv)); + if(!alsaSeq) + { + _state = QString("Unavailable"); + return _state; } - snd_seq_port_subscribe_t* subs; - // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. - snd_seq_port_subscribe_alloca(&subs); + snd_seq_port_info_t *pinfo = NULL; + snd_seq_port_subscribe_t* subs = NULL; + DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Getting port info: address: %d:%d\n", adr.client, adr.port); + if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN) + { + snd_seq_port_info_alloca(&pinfo); + int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo); + if(rv < 0) + { + fprintf(stderr, "MidiAlsaDevice::open Error getting port info: address: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv)); + _state = QString(snd_strerror(rv)); + return _state; + } + DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open: address: %d:%d\n", adr.client, adr.port); + // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. + snd_seq_port_subscribe_alloca(&subs); + } + QString estr; int wer = 0; int rer = 0; - int cap = snd_seq_port_info_get_capability(pinfo); + if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN) + { + + int cap = snd_seq_port_info_get_capability(pinfo); #ifdef ALSA_DEBUG - printf("MidiAlsaDevice::open cap:%d\n", cap); + fprintf(stderr, "MidiAlsaDevice::open cap:%d\n", cap); #endif - - // subscribe for writing - if (_openFlags & 1) + + // subscribe for writing + if (_openFlags & 1) + { + if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE) + { + snd_seq_port_subscribe_set_sender(subs, &musePort); + snd_seq_port_subscribe_set_dest(subs, &adr); + DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Checking write subscription: address: %d:%d\n", adr.client, adr.port); + // Not already subscribed (or error)? Then try subscribing. + if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) + { + //int error = snd_seq_subscribe_port(alsaSeq, subs); + wer = snd_seq_subscribe_port(alsaSeq, subs); + //if (error < 0) + if(wer < 0) + //return QString("Play: ")+QString(snd_strerror(error)); + estr += (QString("Play: ") + QString(snd_strerror(wer)) + QString(" ")); + } + } + if(!wer && (cap & SND_SEQ_PORT_CAP_WRITE)) + _writeEnable = true; + } + + // subscribe for reading + if (_openFlags & 2) + { + if(cap & SND_SEQ_PORT_CAP_SUBS_READ) + { + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &adr); + DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Checking read subscription: address: %d:%d\n", adr.client, adr.port); + // Not already subscribed (or error)? Then try subscribing. + if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) + { + //int error = snd_seq_subscribe_port(alsaSeq, subs); + rer = snd_seq_subscribe_port(alsaSeq, subs); + //if (error < 0) + if(rer < 0) + //return QString("Rec: ") + QString(snd_strerror(error)); + estr += (QString("Rec: ") + QString(snd_strerror(rer))); + } + } + if(!rer && (cap & SND_SEQ_PORT_CAP_READ)) + _readEnable = true; + } + } + else { - if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE) - { - snd_seq_port_subscribe_set_sender(subs, &musePort); - snd_seq_port_subscribe_set_dest(subs, &adr); - // Not already subscribed (or error)? Then try subscribing. - if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) - { - //int error = snd_seq_subscribe_port(alsaSeq, subs); - wer = snd_seq_subscribe_port(alsaSeq, subs); - //if (error < 0) - if(wer < 0) - //return QString("Play: ")+QString(snd_strerror(error)); - estr += (QString("Play: ") + QString(snd_strerror(wer)) + QString(" ")); - } - } - if(!wer && (cap & SND_SEQ_PORT_CAP_WRITE)) - _writeEnable = true; - } - - // subscribe for reading - if (_openFlags & 2) - { - if(cap & SND_SEQ_PORT_CAP_SUBS_READ) - { - snd_seq_port_subscribe_set_dest(subs, &musePort); - snd_seq_port_subscribe_set_sender(subs, &adr); - // Not already subscribed (or error)? Then try subscribing. - if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) - { - //int error = snd_seq_subscribe_port(alsaSeq, subs); - rer = snd_seq_subscribe_port(alsaSeq, subs); - //if (error < 0) - if(rer < 0) - //return QString("Rec: ") + QString(snd_strerror(error)); - estr += (QString("Rec: ") + QString(snd_strerror(rer))); - } - } - if(!rer && (cap & SND_SEQ_PORT_CAP_READ)) - _readEnable = true; + _state = QString("Unavailable"); + return _state; } - if(wer < 0 || rer < 0) - return estr; - - return QString("OK"); + { + _state = estr; + return _state; + } + + _state = QString("OK"); + return _state; } //--------------------------------------------------------- @@ -163,90 +243,111 @@ void MidiAlsaDevice::close() { - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca(&pinfo); - int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo); - if(rv < 0) - { - printf("MidiAlsaDevice::close Error getting port info: adr: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv)); + if(!alsaSeq) + { + _state = QString("Unavailable"); return; } + snd_seq_port_info_t *pinfo; snd_seq_port_subscribe_t* subs; - // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. - snd_seq_port_subscribe_alloca(&subs); - - int wer = 0; - int rer = 0; + if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN) + { + + snd_seq_port_info_alloca(&pinfo); + int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo); + if(rv < 0) + { + fprintf(stderr, "MidiAlsaDevice::close Error getting port info: adr: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv)); + _state = QString("Error on close"); + return; + } + // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. + snd_seq_port_subscribe_alloca(&subs); + } + + if(adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN) + { + _readEnable = false; + _writeEnable = false; + _state = QString("Unavailable"); + } + else + + { + int wer = 0; + int rer = 0; - int cap = snd_seq_port_info_get_capability(pinfo); + int cap = snd_seq_port_info_get_capability(pinfo); #ifdef ALSA_DEBUG - printf("MidiAlsaDevice::close cap:%d\n", cap); + fprintf(stderr, "MidiAlsaDevice::close cap:%d\n", cap); #endif - // This function appears to be called only by MidiPort::setMidiDevice(), - // which closes then opens the device. - // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags. - // - // NOTE: Tested: The read unsubscribe works ok but not the write. - // As viewed in say, qjackctl, the connection is clearly lost, - // but strangely the events are still accepted, ie, playback notes - // are still heard etc. Tried an alsa midi device AND external fluidsynth inst. - // - // Also, jack running and with jack midi disabled, we get messages like - // MidiAlsaDevice::0x84512c0 putEvent(): midi write error: No such device - // dst 16:0 - // only sometimes (not when playing notes), but with jack midi turned on, - // we don't get the messages. With jack stopped we get the messages - // no matter if jack midi is turned on or not. Tim. - - //if (_openFlags & 1) { - //if (!(_openFlags & 1)) - { - if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE) - { - snd_seq_port_subscribe_set_sender(subs, &musePort); - snd_seq_port_subscribe_set_dest(subs, &adr); - - // Already subscribed? Then unsubscribe. - if(!snd_seq_get_port_subscription(alsaSeq, subs)) - { - wer = snd_seq_unsubscribe_port(alsaSeq, subs); - //if(!wer) - // _writeEnable = false; + // This function appears to be called only by MidiPort::setMidiDevice(), + // which closes then opens the device. + // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags. + // + // NOTE: Tested: The read unsubscribe works ok but not the write. + // As viewed in say, qjackctl, the connection is clearly lost, + // but strangely the events are still accepted, ie, playback notes + // are still heard etc. Tried an alsa midi device AND external fluidsynth inst. + // + // Also, jack running and with jack midi disabled, we get messages like + // MidiAlsaDevice::0x84512c0 putEvent(): midi write error: No such device + // dst 16:0 + // only sometimes (not when playing notes), but with jack midi turned on, + // we don't get the messages. With jack stopped we get the messages + // no matter if jack midi is turned on or not. Tim. + + //if (_openFlags & 1) { + //if (!(_openFlags & 1)) + { + if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE) + { + snd_seq_port_subscribe_set_sender(subs, &musePort); + snd_seq_port_subscribe_set_dest(subs, &adr); + + // Already subscribed? Then unsubscribe. + if(!snd_seq_get_port_subscription(alsaSeq, subs)) + { + wer = snd_seq_unsubscribe_port(alsaSeq, subs); + //if(!wer) + // _writeEnable = false; + //else + if(wer < 0) + fprintf(stderr, "MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for writing: %s\n", adr.client, adr.port, snd_strerror(wer)); + } //else - if(wer < 0) - printf("MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for writing: %s\n", adr.client, adr.port, snd_strerror(wer)); + //_writeEnable = false; } - //else - //_writeEnable = false; - } - _writeEnable = false; - } + _writeEnable = false; + } - //if (_openFlags & 2) { - //if (!(_openFlags & 2)) - { - if(cap & SND_SEQ_PORT_CAP_SUBS_READ) - { - snd_seq_port_subscribe_set_dest(subs, &musePort); - snd_seq_port_subscribe_set_sender(subs, &adr); - - // Already subscribed? Then unsubscribe. - if(!snd_seq_get_port_subscription(alsaSeq, subs)) - { - rer = snd_seq_unsubscribe_port(alsaSeq, subs); - //if(!rer) + //if (_openFlags & 2) { + //if (!(_openFlags & 2)) + { + if(cap & SND_SEQ_PORT_CAP_SUBS_READ) + { + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &adr); + + // Already subscribed? Then unsubscribe. + if(!snd_seq_get_port_subscription(alsaSeq, subs)) + { + rer = snd_seq_unsubscribe_port(alsaSeq, subs); + //if(!rer) + // _readEnable = false; + //else + if(rer < 0) + fprintf(stderr, "MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for reading: %s\n", adr.client, adr.port, snd_strerror(rer)); + } + //else // _readEnable = false; - //else - if(rer < 0) - printf("MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for reading: %s\n", adr.client, adr.port, snd_strerror(rer)); - } - //else - // _readEnable = false; - } - _readEnable = false; + } + _readEnable = false; + } + _state = QString("Closed"); } } @@ -286,174 +387,24 @@ } //--------------------------------------------------------- -// putEvent +// putAlsaEvent +// return false if event is delivered //--------------------------------------------------------- -bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) +bool MidiAlsaDevice::putAlsaEvent(snd_seq_event_t* event) { if (MusEGlobal::midiOutputTrace) { - fprintf(stderr, "MidiOut: Alsa: <%s>: ", name().toLatin1().constData()); - e.dump(); + fprintf(stderr, "ALSA MidiOut driver: <%s>: ", name().toLatin1().constData()); + dump(event); } - int chn = e.channel(); - int a = e.dataA(); - int b = e.dataB(); - - snd_seq_event_t event; - memset(&event, 0, sizeof(event)); - event.queue = SND_SEQ_QUEUE_DIRECT; - event.source = musePort; - event.dest = adr; - - switch(e.type()) { - case ME_NOTEON: - snd_seq_ev_set_noteon(&event, chn, a, b); - break; - case ME_NOTEOFF: - snd_seq_ev_set_noteoff(&event, chn, a, 0); - break; - case ME_PROGRAM: - snd_seq_ev_set_pgmchange(&event, chn, a); - break; - case ME_CONTROLLER: - { - if(a == CTRL_PROGRAM) - { - snd_seq_ev_set_pgmchange(&event, chn, b); - break; - } - else if(a == CTRL_PITCH) - { - snd_seq_ev_set_pitchbend(&event, chn, b); - break; - } - else if((a | 0xff) == CTRL_POLYAFTER) - { - snd_seq_ev_set_keypress(&event, chn, a & 0x7f, b & 0x7f); - break; - } - else if(a == CTRL_AFTERTOUCH) - { - snd_seq_ev_set_chanpress(&event, chn, b); - break; - } - } - -#if 1 - snd_seq_ev_set_controller(&event, chn, a, b); -#else - { - int a = e.dataA(); - int b = e.dataB(); - int chn = e.channel(); - if (a < CTRL_14_OFFSET) { // 7 Bit Controller - snd_seq_ev_set_controller(&event, chn, a, b); - } - else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - a = (ctrlH << 7) + ctrlL; - snd_seq_ev_set_controller(&event, chn, a, b); - event.type = SND_SEQ_EVENT_CONTROL14; - } - else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - a = (ctrlH << 7) + ctrlL; - b <<= 7; - snd_seq_ev_set_controller(&event, chn, a, b); - event.type = SND_SEQ_EVENT_REGPARAM; - } - else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - a = (ctrlH << 7) + ctrlL; - b <<= 7; - snd_seq_ev_set_controller(&event, chn, a, b); - event.type = SND_SEQ_EVENT_NONREGPARAM; - } - else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - a = (ctrlH << 7) + ctrlL; - snd_seq_ev_set_controller(&event, chn, a, b); - event.type = SND_SEQ_EVENT_REGPARAM; - } - else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - a = (ctrlH << 7) + ctrlL; - snd_seq_ev_set_controller(&event, chn, a, b); - event.type = SND_SEQ_EVENT_NONREGPARAM; - } - else { - printf("putEvent: unknown controller type 0x%x\n", a); - } - } -#endif - break; - case ME_PITCHBEND: - snd_seq_ev_set_pitchbend(&event, chn, a); - break; - case ME_POLYAFTER: - snd_seq_ev_set_keypress(&event, chn, a, b); - break; - case ME_AFTERTOUCH: - snd_seq_ev_set_chanpress(&event, chn, a); - break; - case ME_SYSEX: - { - const unsigned char* p = e.data(); - int n = e.len(); - int len = n + sizeof(event) + 2; - char buf[len]; - event.type = SND_SEQ_EVENT_SYSEX; - event.flags = SND_SEQ_EVENT_LENGTH_VARIABLE; - event.data.ext.len = n + 2; - event.data.ext.ptr = (void*)(buf + sizeof(event)); - memcpy(buf, &event, sizeof(event)); - char* pp = buf + sizeof(event); - *pp++ = 0xf0; - memcpy(pp, p, n); - pp += n; - *pp = 0xf7; - return putEvent(&event); - } - case ME_SONGPOS: - event.data.control.value = a; - event.type = SND_SEQ_EVENT_SONGPOS; - break; - case ME_CLOCK: - event.type = SND_SEQ_EVENT_CLOCK; - break; - case ME_START: - event.type = SND_SEQ_EVENT_START; - break; - case ME_CONTINUE: - event.type = SND_SEQ_EVENT_CONTINUE; - break; - case ME_STOP: - event.type = SND_SEQ_EVENT_STOP; - break; - default: - if(MusEGlobal::debugMsg) - printf("MidiAlsaDevice::putEvent(): event type %d not implemented\n", e.type()); - return true; - } - return putEvent(&event); - } - -//--------------------------------------------------------- -// putEvent -// return false if event is delivered -//--------------------------------------------------------- - -bool MidiAlsaDevice::putEvent(snd_seq_event_t* event) - { + + if(!_writeEnable || !alsaSeq || adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN) + return true; + int error; #ifdef ALSA_DEBUG - printf("MidiAlsaDevice::putEvent\n"); + fprintf(stderr, "MidiAlsaDevice::putAlsaEvent\n"); #endif do { @@ -465,407 +416,642 @@ } if (error < 0) { if (error == -12) { -// printf("?");fflush(stdout); return true; } else { - fprintf(stderr, "MidiAlsaDevice::%p putEvent(): midi write error: %s\n", + fprintf(stderr, "MidiAlsaDevice::%p putAlsaEvent(): midi write error: %s\n", this, snd_strerror(error)); fprintf(stderr, " dst %d:%d\n", adr.client, adr.port); //exit(-1); } } else - fprintf(stderr, "MidiAlsaDevice::putEvent(): midi write returns %d, expected %d: %s\n", + fprintf(stderr, "MidiAlsaDevice::putAlsaEvent(): midi write returns %d, expected %d: %s\n", error, len, snd_strerror(error)); } while (error == -12); return true; } //--------------------------------------------------------- -// processMidi -// Called from ALSA midi sequencer thread only. +// processEvent +// return false if event is delivered //--------------------------------------------------------- -void MidiAlsaDevice::processMidi() +bool MidiAlsaDevice::processEvent(const MidiPlayEvent& ev) { - //bool stop = stopPending; // Snapshots - //bool seek = seekPending; // - //seekPending = stopPending = false; - // Transfer the stuck notes FIFO to the play events list. - // FIXME It would be faster to have MidiAlsaDevice automatically add the stuck note so that only - // one FIFO would be needed. But that requires passing an extra 'tick' and 'off velocity' in - // addScheduledEvent, which felt too weird. - //while(!stuckNotesFifo.isEmpty()) - // _stuckNotes.add(stuckNotesFifo.get()); - - //int frameOffset = getFrameOffset(); - //int nextTick = MusEGlobal::audio->nextTick(); - - //bool is_playing = MusEGlobal::audio->isPlaying(); - // We're in the ALSA midi thread. audio->isPlaying() might not be true during seek right now. Include START_PLAY state... - //bool is_playing = MusEGlobal::audio->isPlaying() || MusEGlobal::audio->isStarting(); // TODO Check this. It includes LOOP1 and LOOP2 besides PLAY. - int pos = MusEGlobal::audio->tickPos(); - int port = midiPort(); - MidiPort* mp = port == -1 ? 0 : &MusEGlobal::midiPorts[port]; - bool ext_sync = MusEGlobal::extSyncFlag.value(); + if (MusEGlobal::midiOutputTrace) { + fprintf(stderr, "ALSA MidiOut pre-driver: <%s>: ", name().toLatin1().constData()); + dumpMPEvent(&ev); + } + + int chn = ev.channel(); + int a = ev.dataA(); + int b = ev.dataB(); - /* - if(mp) - { - MidiSyncInfo& si = mp->syncInfo(); - if(stop) - { - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!ext_sync) - { - // Shall we check open flags? - //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) - //if(!(dev->openFlags() & 1)) - // return; - - // Send MMC stop... - if(si.MMCOut()) - { - unsigned char msg[mmcStopMsgLen]; - memcpy(msg, mmcStopMsg, mmcStopMsgLen); - msg[1] = si.idOut(); - putMidiEvent(MidiPlayEvent(0, 0, ME_SYSEX, msg, mmcStopMsgLen)); - } - - // Send midi stop... - if(si.MRTOut()) - { - putMidiEvent(MidiPlayEvent(0, 0, 0, ME_STOP, 0, 0)); - // Added check of option send continue not start. p3.3.31 - // Hmm, is this required? Seems to make other devices unhappy. - // (Could try now that this is in MidiDevice. p4.0.22 ) - //if(!si.sendContNotStart()) - // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); - } - } - } - - if(seek) - { - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!ext_sync) - { - // Send midi stop and song position pointer... - if(si.MRTOut()) - { - // Shall we check for device write open flag to see if it's ok to send?... - //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) - //if(!(openFlags() & 1)) - // continue; - putMidiEvent(MidiPlayEvent(0, 0, 0, ME_STOP, 0, 0)); - // Hm, try sending these after stuck notes below... - //putMidiEvent(MidiPlayEvent(0, 0, 0, ME_SONGPOS, beat, 0)); - //if(is_playing) - // putMidiEvent(MidiPlayEvent(0, 0, 0, ME_CONTINUE, 0, 0)); - } - } - } - } - */ - - /* - if(stop || (seek && is_playing)) - { - // Clear all notes and handle stuck notes... - //playEventFifo.clear(); - _playEvents.clear(); - for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) - { - MidiPlayEvent ev = *i; - ev.setTime(0); - //_playEvents.add(ev); - putMidiEvent(ev); // Play immediately. - } - _stuckNotes.clear(); - } - */ - - /* - if(mp) - { - MidiSyncInfo& si = mp->syncInfo(); - // Try sending these now after stuck notes above... - if(stop || seek) - { - // Reset sustain. - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) - if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) - putMidiEvent(MidiPlayEvent(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0)); - } - if(seek) - { - // Send new song position. - if(!ext_sync && si.MRTOut()) + snd_seq_event_t event; + snd_seq_ev_clear(&event); + + event.queue = SND_SEQ_QUEUE_DIRECT; + event.source = musePort; + event.dest = adr; + + MidiInstrument::NoteOffMode nom = MidiInstrument::NoteOffAll; // Default to NoteOffAll in case of no port. + const int mport = midiPort(); + if(mport != -1) { - int beat = (pos * 4) / MusEGlobal::config.division; - putMidiEvent(MidiPlayEvent(0, 0, 0, ME_SONGPOS, beat, 0)); - } - // Send new controller values. - MidiCtrlValListList* cll = mp->controller(); - for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) - { - MidiCtrlValList* vl = ivl->second; - iMidiCtrlVal imcv = vl->iValue(pos); - if(imcv != vl->end()) { - Part* p = imcv->second.part; - // Don't send if part or track is muted or off. - if(!p || p->mute()) - continue; - Track* track = p->track(); - if(track && (track->isMute() || track->off())) - continue; - unsigned t = (unsigned)imcv->first; - // Do not add values that are outside of the part. - if(t >= p->tick() && t < (p->tick() + p->lenTick())) - // Use sendEvent to get the optimizations and limiting. But force if there's a value at this exact position. - mp->sendEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val), imcv->first == pos); - } + if(MidiInstrument* mi = MusEGlobal::midiPorts[mport].instrument()) + nom = mi->noteOffMode(); } - // Send continue. - // REMOVE Tim. This is redundant and too early - Audio::startRolling already properly sends it when sync ready. - //if(is_playing && !ext_sync && si.MRTOut()) - // putMidiEvent(MidiPlayEvent(0, 0, 0, ME_CONTINUE, 0, 0)); - } - } - */ - - //if(!(stop || (seek && is_playing))) - { - // Transfer the play events FIFO to the play events list. - //while(!playEventFifo.isEmpty()) - // _playEvents.add(playEventFifo.get()); - - /* TODO Handle these more directly than putting them into play events list. - //if(MusEGlobal::audio->isPlaying()) - { - iMPEvent k; - for (k = _stuckNotes.begin(); k != _stuckNotes.end(); ++k) { - if (k->time() >= nextTick) + switch(ev.type()) + { + case ME_NOTEON: + + if(b == 0) + { + // Handle zero-velocity note ons. Technically this is an error because internal midi paths + // are now all 'note-off' without zero-vel note ons - they're converted to note offs. + // Nothing should be setting a Note type Event's on velocity to zero. + // But just in case... If we get this warning, it means there is still code to change. + fprintf(stderr, "MidiAlsaDevice::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", + ev.time(), ev.type(), chn, a, b); + switch(nom) + { + // Instrument uses note offs. Convert to zero-vel note off. + case MidiInstrument::NoteOffAll: + if(MusEGlobal::midiOutputTrace) + fprintf(stderr, "MidiOut: Alsa: Following event will be converted to zero-velocity note off:\n"); + snd_seq_ev_set_noteoff(&event, chn, a, 0); break; - MidiPlayEvent ev(*k); - if(extsync) // p3.3.25 - ev.setTime(k->time()); - else - ev.setTime(tempomap.tick2frame(k->time()) + frameOffset); - _playEvents.add(ev); - } - _stuckNotes.erase(_stuckNotes.begin(), k); - } - */ - processStuckNotes(); - } - - if(_playEvents.empty()) - return; - - unsigned curFrame = MusEGlobal::audio->curFrame(); - - // Play all events up to current frame. - iMPEvent i = _playEvents.begin(); - for (; i != _playEvents.end(); ++i) { - if (i->time() > (ext_sync ? pos : curFrame)) // p3.3.25 Check: Should be nextTickPos? p4.0.34 - break; - if(mp){ - if (mp->sendEvent(*i, true)) // Force the event to be sent. - break; + + // Instrument uses no note offs at all. Send as-is. + case MidiInstrument::NoteOffNone: + // Instrument converts all note offs to zero-vel note ons. Send as-is. + case MidiInstrument::NoteOffConvertToZVNoteOn: + snd_seq_ev_set_noteon(&event, chn, a, b); + break; + } } - else - if(putMidiEvent(*i)) - break; + else + + snd_seq_ev_set_noteon(&event, chn, a, b); + break; + case ME_NOTEOFF: + + switch(nom) + { + // Instrument uses note offs. Send as-is. + case MidiInstrument::NoteOffAll: + snd_seq_ev_set_noteoff(&event, chn, a, b); + break; + + // Instrument uses no note offs at all. Send nothing. Eat up the event - return false. + case MidiInstrument::NoteOffNone: + return false; + + // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. + case MidiInstrument::NoteOffConvertToZVNoteOn: + if(MusEGlobal::midiOutputTrace) + fprintf(stderr, "MidiOut: Alsa: Following event will be converted to zero-velocity note on:\n"); + snd_seq_ev_set_noteon(&event, chn, a, 0); + break; + } + break; + case ME_PROGRAM: + { + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. + _curOutParamNums[chn].setPROG(a); + snd_seq_ev_set_pgmchange(&event, chn, a); + } + break; + case ME_PITCHBEND: + snd_seq_ev_set_pitchbend(&event, chn, a); + break; + case ME_POLYAFTER: + snd_seq_ev_set_keypress(&event, chn, a, b); + break; + case ME_AFTERTOUCH: + snd_seq_ev_set_chanpress(&event, chn, a); + break; + case ME_SYSEX: + { + // Probably best to reset all. + resetCurOutParamNums(); + + // Stage the event data - is it OK to proceed? + const size_t len = sysExOutProcessor()->stageEvData(ev.eventData(), ev.time()); + if(len > 0) + { + unsigned char buf[len]; + if(sysExOutProcessor()->getCurChunk(buf, MusEGlobal::sampleRate)) + { + snd_seq_ev_set_sysex(&event, len, buf); + // NOTE: Don't move this out, 'buf' would go out of scope. + return putAlsaEvent(&event); + } + } + return true; + } + break; + case ME_SONGPOS: + event.data.control.value = a; + event.type = SND_SEQ_EVENT_SONGPOS; + break; + case ME_CLOCK: + event.type = SND_SEQ_EVENT_CLOCK; + break; + case ME_START: + event.type = SND_SEQ_EVENT_START; + break; + case ME_CONTINUE: + event.type = SND_SEQ_EVENT_CONTINUE; + break; + case ME_STOP: + event.type = SND_SEQ_EVENT_STOP; + break; + case ME_CONTROLLER: + { + int a = ev.dataA(); + int b = ev.dataB(); + int chn = ev.channel(); + + if(a == CTRL_PITCH) + snd_seq_ev_set_pitchbend(&event, chn, b); + else if((a | 0xff) == CTRL_POLYAFTER) + snd_seq_ev_set_keypress(&event, chn, a & 0x7f, b & 0x7f); + else if(a == CTRL_AFTERTOUCH) + snd_seq_ev_set_chanpress(&event, chn, b); + else if(a == CTRL_PROGRAM) { + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. + int hb = (b >> 16) & 0xff; + int lb = (b >> 8) & 0xff; + int pr = b & 0xff; + _curOutParamNums[chn].setCurrentProg(pr, lb, hb); + if(hb != 0xff) + { + snd_seq_ev_set_controller(&event, chn, CTRL_HBANK, hb); + if(putAlsaEvent(&event)) + return true; + } + if(lb != 0xff) + { + snd_seq_ev_set_controller(&event, chn, CTRL_LBANK, lb); + if(putAlsaEvent(&event)) + return true; + } + if(pr != 0xff) + { + snd_seq_ev_set_pgmchange(&event, chn, pr); + if(putAlsaEvent(&event)) + return true; + } + return false; + } + +// Set this to 1 if ALSA cannot handle RPN NRPN etc. +// NOTE: Although ideally it should be 0, there are problems with +// letting ALSA do the 'composition' of the messages in putMidiEvent() - +// chiefly that ALSA does not handle 7-bit (N)RPN controllers. +// This define is kept because it is important to understand, try, and see +// the difference between the two techniques, and possibly make it work... +// Also see the corresponding define in MidiAlsaDevice::putMidiEvent(). +#if 0 + snd_seq_ev_set_controller(&event, chn, a, b); +#else + + else if (a < CTRL_14_OFFSET) { // 7 Bit Controller + if(a == CTRL_HRPN) + _curOutParamNums[chn].setRPNH(b); + else if(a == CTRL_LRPN) + _curOutParamNums[chn].setRPNL(b); + else if(a == CTRL_HNRPN) + _curOutParamNums[chn].setNRPNH(b); + else if(a == CTRL_LNRPN) + _curOutParamNums[chn].setNRPNL(b); + else if(a == CTRL_HBANK) + { + _curOutParamNums[chn].setBANKH(b); + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. + } + else if(a == CTRL_LBANK) + { + _curOutParamNums[chn].setBANKL(b); + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. + } + else if(a == CTRL_RESET_ALL_CTRL) + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. + + snd_seq_ev_set_controller(&event, chn, a, b); + } + else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; +#if 0 + int dataH = (b >> 7) & 0x7f; + int dataL = b & 0x7f; + snd_seq_ev_set_controller(&event, chn, ctrlH, dataH); + if(putAlsaEvent(&event)) + return true; + snd_seq_ev_set_controller(&event, chn, ctrlL, dataL); + return putAlsaEvent(&event); +#else + snd_seq_event_t ev; + snd_seq_ev_clear(&ev); + ev.queue = SND_SEQ_QUEUE_DIRECT; + ev.source = musePort; + ev.dest = adr; + int n = (ctrlH << 7) + ctrlL; + snd_seq_ev_set_controller(&ev, chn, n, b); + ev.type = SND_SEQ_EVENT_CONTROL14; + return putAlsaEvent(&ev); +#endif + + } + else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + int data = b & 0x7f; + if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setRPNL(ctrlL); + snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, ctrlL); + if(putAlsaEvent(&event)) + return true; + } + if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setRPNH(ctrlH); + snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, ctrlH); + if(putAlsaEvent(&event)) + return true; + } + if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setDATAH(data); + snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, data); + if(putAlsaEvent(&event)) + return true; + } + + // Select null parameters so that subsequent data controller + // events do not upset the last *RPN controller. Tim. + if(MusEGlobal::config.midiSendNullParameters) + { + _curOutParamNums[chn].setRPNH(0x7f); + snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, 0x7f); + if(putAlsaEvent(&event)) + return true; + + _curOutParamNums[chn].setRPNL(0x7f); + snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, 0x7f); + if(putAlsaEvent(&event)) + return true; + } + return false; + } + else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + int data = b & 0x7f; + if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setNRPNL(ctrlL); + snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, ctrlL); + if(putAlsaEvent(&event)) + return true; + } + if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setNRPNH(ctrlH); + snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, ctrlH); + if(putAlsaEvent(&event)) + return true; + } + if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setDATAH(data); + snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, data); + if(putAlsaEvent(&event)) + return true; + } + + if(MusEGlobal::config.midiSendNullParameters) + { + _curOutParamNums[chn].setNRPNH(0x7f); + snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, 0x7f); + if(putAlsaEvent(&event)) + return true; + + _curOutParamNums[chn].setNRPNL(0x7f); + snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, 0x7f); + if(putAlsaEvent(&event)) + return true; + } + return false; + } + else if (a < CTRL_RPN14_OFFSET) // Unaccounted for internal controller + return true; + else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; + int dataH = (b >> 7) & 0x7f; + int dataL = b & 0x7f; + if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setRPNL(ctrlL); + snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, ctrlL); + if(putAlsaEvent(&event)) + return true; + } + if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setRPNH(ctrlH); + snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, ctrlH); + if(putAlsaEvent(&event)) + return true; + } + if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setDATAH(dataH); + snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, dataH); + if(putAlsaEvent(&event)) + return true; + } + if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setDATAL(dataL); + snd_seq_ev_set_controller(&event, chn, CTRL_LDATA, dataL); + if(putAlsaEvent(&event)) + return true; + } + + if(MusEGlobal::config.midiSendNullParameters) + { + _curOutParamNums[chn].setRPNH(0x7f); + snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, 0x7f); + if(putAlsaEvent(&event)) + return true; + + _curOutParamNums[chn].setRPNL(0x7f); + snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, 0x7f); + if(putAlsaEvent(&event)) + return true; + } + return false; + } + else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller + int ctrlH = (a >> 8) & 0x7f; + int ctrlL = a & 0x7f; +#if 0 + int dataH = (b >> 7) & 0x7f; + int dataL = b & 0x7f; + if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setNRPNL(ctrlL); + snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, ctrlL); + if(putAlsaEvent(&event)) + return true; + } + if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setNRPNH(ctrlH); + snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, ctrlH); + if(putAlsaEvent(&event)) + return true; + } + if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setDATAH(dataH); + snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, dataH); + if(putAlsaEvent(&event)) + return true; + } + if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setDATAL(dataL); + snd_seq_ev_set_controller(&event, chn, CTRL_LDATA, dataL); + if(putAlsaEvent(&event)) + return true; + } + +#else + int n = (ctrlH << 7) + ctrlL; + snd_seq_ev_set_controller(&event, chn, n, b); + event.type = SND_SEQ_EVENT_NONREGPARAM; + if(putAlsaEvent(&event)) + return true; +#endif + + if(MusEGlobal::config.midiSendNullParameters) + { + _curOutParamNums[chn].setNRPNH(0x7f); + snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, 0x7f); + if(putAlsaEvent(&event)) + return true; + + _curOutParamNums[chn].setNRPNL(0x7f); + snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, 0x7f); + if(putAlsaEvent(&event)) + return true; + } + return false; + } + else { + fprintf(stderr, "MidiAlsaDevice::processEvent: unknown controller type 0x%x\n", a); + return true; + } +#endif } - _playEvents.erase(_playEvents.begin(), i); + break; // ME_CONTROLLER + + default: + if(MusEGlobal::debugMsg) + fprintf(stderr, "MidiAlsaDevice::processEvent(): event type %d not implemented\n", ev.type()); + return true; + } + + return putAlsaEvent(&event); } - -/* + //--------------------------------------------------------- -// handleStop +// processMidi +// Called from ALSA midi sequencer thread only. //--------------------------------------------------------- -void MidiAlsaDevice::handleStop() +void MidiAlsaDevice::processMidi(unsigned int curFrame) { - // If the device is not in use by a port, don't bother it. - if(_port == -1) - return; - - stopPending = true; // Trigger stop handling in processMidi. - - //--------------------------------------------------- - // send midi stop - //--------------------------------------------------- + // Get the state of the stop flag. + const bool do_stop = stopFlag(); + + //-------------------------------------------------------------------------------- + // For now we stop ALL ring buffer processing until any sysex transmission is finished. + // TODO FIXME Some realtime events ARE allowed while sending a sysex. Let that happen below... + //-------------------------------------------------------------------------------- - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!MusEGlobal::extSyncFlag.value()) + SysExOutputProcessor* sop = sysExOutProcessor(); + switch(sop->state()) { - // Shall we check open flags? - //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) - //if(!(dev->openFlags() & 1)) - // return; - - MidiSyncInfo& si = mp->syncInfo(); - if(si.MMCOut()) - mp->sendMMCStop(); + case SysExOutputProcessor::Clear: + // Proceed with normal ring buffer processing, below... + break; - if(si.MRTOut()) + case SysExOutputProcessor::Sending: { - mp->sendStop(); - // Added check of option send continue not start. Hmm, is this required? Seems to make other devices unhappy. - // (Could try now that this is in MidiDevice.) - //if(!si.sendContNotStart()) - // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); - } - } + // Current chunk is meant for a future cycle? + if(sop->curChunkFrame() > curFrame) + break; - //--------------------------------------------------- - // reset sustain - //--------------------------------------------------- - - MidiPort* mp = &MusEGlobal::midiPorts[_port]; - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) - { - if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + const size_t len = sop->curChunkSize(); + if(len > 0) + { + unsigned char buf[len]; + if(sop->getCurChunk(buf, MusEGlobal::sampleRate)) + { + snd_seq_event_t event; + snd_seq_ev_clear(&event); + event.queue = SND_SEQ_QUEUE_DIRECT; + event.source = musePort; + event.dest = adr; + snd_seq_ev_set_sysex(&event, len, buf); + putAlsaEvent(&event); + } + } + else + { + fprintf(stderr, "Error: MidiAlsaDevice::processMidi(): curChunkSize is zero while state is Sending\n"); + // Should not happen. Protection against accidental zero chunk size while sending. + // Let's just clear the thing. + sop->clear(); + } + } + break; + + case SysExOutputProcessor::Finished: { - MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); - //putMidiEvent(ev); - putEvent(ev); - // Do sendEvent to get the optimizations - send only on a change of value. - //mp->sendEvent(ev); + // Wait for the last chunk to transmit. + if(sop->curChunkFrame() > curFrame) + break; + // Now we are truly done. Clear or reset the processor, which + // sets the state to Clear. Prefer reset for speed but clear is OK, + // the EvData reference will already have been released. + //sop->reset(); + sop->clear(); } + break; } - //--------------------------------------------------- - // send midi stop - //--------------------------------------------------- + MidiPlayEvent buf_ev; -// // Don't send if external sync is on. The master, and our sync routing system will take care of that. -// if(!MusEGlobal::extSyncFlag.value()) -// { -// // Shall we check open flags? -// //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) -// //if(!(dev->openFlags() & 1)) -// // return; -// -// MidiSyncInfo& si = mp->syncInfo(); -// if(si.MMCOut()) -// mp->sendMMCStop(); -// -// if(si.MRTOut()) -// { -// // Send STOP -// mp->sendStop(); -// // Added check of option send continue not start. Hmm, is this required? Seems to make other devices unhappy. -// // (Could try now that this is in MidiDevice.) -// //if(!si.sendContNotStart()) -// // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); -// } -// } -} -*/ - -/* -//--------------------------------------------------------- -// handleSeek -//--------------------------------------------------------- - -void MidiAlsaDevice::handleSeek() -{ - // If the device is not in use by a port, don't bother it. - if(_port == -1) - return; - - seekPending = true; // Trigger seek handling in processMidi. + // Transfer the user lock-free buffer events to the user sorted multi-set. + // False = don't use the size snapshot, but update it. + const unsigned int usr_buf_sz = eventBuffers(UserBuffer)->getSize(false); + for(unsigned int i = 0; i < usr_buf_sz; ++i) + { + if(eventBuffers(UserBuffer)->get(buf_ev)) + _outUserEvents.insert(buf_ev); + } - MidiPort* mp = &MusEGlobal::midiPorts[_port]; - MidiCtrlValListList* cll = mp->controller(); - int pos = MusEGlobal::audio->tickPos(); + // Transfer the playback lock-free buffer events to the playback sorted multi-set. + const unsigned int pb_buf_sz = eventBuffers(PlaybackBuffer)->getSize(false); + for(unsigned int i = 0; i < pb_buf_sz; ++i) + { + // Are we stopping? Just remove the item. + if(do_stop) + eventBuffers(PlaybackBuffer)->remove(); + // Otherwise get the item. + else if(eventBuffers(PlaybackBuffer)->get(buf_ev)) + _outPlaybackEvents.insert(buf_ev); + } - //--------------------------------------------------- - // Send STOP - //--------------------------------------------------- - - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!MusEGlobal::extSyncFlag.value()) + // Are we stopping? + if(do_stop) { - if(mp->syncInfo().MRTOut()) - { - // Shall we check for device write open flag to see if it's ok to send?... - //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) - //if(!(openFlags() & 1)) - // continue; - mp->sendStop(); - } + // Transport has stopped, purge ALL further scheduled playback events now. + _outPlaybackEvents.clear(); + // Reset the flag. + setStopFlag(false); } - //--------------------------------------------------- - // reset sustain - //--------------------------------------------------- + iMPEvent impe_pb = _outPlaybackEvents.begin(); + iMPEvent impe_us = _outUserEvents.begin(); + bool using_pb; - MidiPort* mp = &MusEGlobal::midiPorts[_port]; - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + while(1) { - if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + if(impe_pb != _outPlaybackEvents.end() && impe_us != _outUserEvents.end()) + using_pb = *impe_pb < *impe_us; + else if(impe_pb != _outPlaybackEvents.end()) + using_pb = true; + else if(impe_us != _outUserEvents.end()) + using_pb = false; + else break; + + const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us; + + #ifdef ALSA_DEBUG + fprintf(stderr, "INFO: MidiAlsaDevice::processMidi() evTime:%u curFrame:%u\n", e.time(), curFrame); + #endif + + // Event is meant for next cycle? + if(e.time() > curFrame) { - MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); - putEvent(ev); - //putMidiEvent(ev); - // Do sendEvent to get the optimizations - send only on a change of value. - //mp->sendEvent(ev); +#ifdef ALSA_DEBUG + fprintf(stderr, " alsa play event is for future:%lu, breaking loop now\n", e.time()); +#endif + break; } - } - - //--------------------------------------------------- - // Send new controller values - //--------------------------------------------------- - - for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) - { - MidiCtrlValList* vl = ivl->second; - iMidiCtrlVal imcv = vl->iValue(pos); - if(imcv != vl->end()) + + // Is the sysex processor not in a Clear state? + // Only realtime events are allowed to be sent while a sysex is in progress. + // The delayed events list is pre-allocated with reserve(). + // According to http://en.cppreference.com/w/cpp/container/vector/clear, + // clear() shall not change the capacity. That statement references here: + // https://stackoverflow.com/questions/18467624/what-does-the-standard-say- + // about-how-calling-clear-on-a-vector-changes-the-capac/18467916#18467916 + // But http://www.cplusplus.com/reference/vector/vector/clear + // has not been updated to clarify this situation. + if(sop->state() != SysExOutputProcessor::Clear) { - Part* p = imcv->second.part; - // Don't send if part or track is muted or off. - if(!p || p->mute()) - continue; - Track* track = p->track(); - if(track && (track->isMute() || track->off())) - continue; - unsigned t = (unsigned)imcv->first; - // Do not add values that are outside of the part. - if(p && t >= p->tick() && t < (p->tick() + p->lenTick()) ) - //_playEvents.add(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); - // Use sendEvent to get the optimizations and limiting. But force if there's a value at this exact position. - mp->sendEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val), imcv->first == pos); + // Is it a realtime message? + if(e.type() >= 0xf8 && e.type() <= 0xff) + // Process it now. + processEvent(e); + else + // Store it for later. + _sysExOutDelayedEvents->push_back(e); } - } - - //--------------------------------------------------- - // Send STOP and "set song position pointer" - //--------------------------------------------------- - - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!MusEGlobal::extSyncFlag.value()) - { - if(mp->syncInfo().MRTOut()) + else { - // Shall we check for device write open flag to see if it's ok to send?... - //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) - //if(!(openFlags() & 1)) - // continue; + // Process any delayed events. + const unsigned int sz = _sysExOutDelayedEvents->size(); + for(unsigned int i = 0; i < sz; ++i) + processEvent(_sysExOutDelayedEvents->at(i)); - //mp->sendStop(); // Moved above. - int beat = (pos * 4) / MusEGlobal::config.division; - mp->sendSongpos(beat); - } + // Let's check that capacity out of curiosity... + const unsigned int cap = _sysExOutDelayedEvents->capacity(); + // Done with the delayed event list. Clear it. + _sysExOutDelayedEvents->clear(); + + // Throw up a developer warning if things are fishy. + if(_sysExOutDelayedEvents->capacity() != cap) + fprintf(stderr, "WARNING: MidiAlsaDevice::processMidi() delayed events vector " + "capacity:%u is not the same as before clear:%u\n", + (unsigned int)_sysExOutDelayedEvents->capacity(), cap); + + // If processEvent fails, although we would like to not miss events by keeping them + // until next cycle and trying again, that can lead to a large backup of events + // over a long time. So we'll just... miss them. + processEvent(e); + } + + // Successfully processed event. Remove it from FIFO. + // C++11. + if(using_pb) + impe_pb = _outPlaybackEvents.erase(impe_pb); + else + impe_us = _outUserEvents.erase(impe_us); } } -*/ //--------------------------------------------------------- // initMidiAlsa @@ -874,8 +1060,21 @@ bool initMidiAlsa() { + // The sequencer is required for ALSA midi. Initialize it if not already done. + // But do not start the sequencer thread yet... the caller may want to do that a bit later. + MusECore::initMidiSequencer(); + + if(alsaSeq) + { + DEBUG_PRST_ROUTES(stderr, "initMidiAlsa: alsaSeq already initialized, ignoring\n"); + return false; + } + + muse_atomic_init(&atomicAlsaMidiScanPending); + muse_atomic_set(&atomicAlsaMidiScanPending, 0); + if (MusEGlobal::debugMsg) - printf("initMidiAlsa\n"); + fprintf(stderr, "initMidiAlsa\n"); int error = snd_seq_open(&alsaSeq, "hw", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); if (error < 0) { fprintf(stderr, "Could not open ALSA sequencer: %s\n", @@ -892,7 +1091,7 @@ while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { const char* cname = snd_seq_client_info_get_name(cinfo); - //printf( "ALSA client name: %s\n", cname); + //fprintf(stderr, "ALSA client name: %s\n", cname); // Put Midi Through and user clients after others. Insert other unwanted clients here: // p4.0.41 if(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT || strcmp("Midi Through", cname) == 0) @@ -915,26 +1114,54 @@ continue; } snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo); - MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo))); + + const QString dev_name(snd_seq_port_info_get_name(pinfo)); + MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI); + const bool dev_found = dev; + if(dev_found) + { + DEBUG_PRST_ROUTES(stderr, "initMidiAlsa device found:%p %s\n", dev, snd_seq_port_info_get_name(pinfo)); + // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device? + dev->setAddressClient(adr.client); + dev->setAddressPort(adr.port); + // The state should be 'Unavailable', change it to 'Closed' (or 'Open' below). + //if(dev->midiPort() == -1) + dev->setState("Closed"); + } + else + dev = new MidiAlsaDevice(adr, QString(dev_name)); + //MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo))); int flags = 0; if (capability & outCap) flags |= 1; if (capability & inCap) flags |= 2; dev->setrwFlags(flags); - if (MusEGlobal::debugMsg) - printf("ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", + if (MusEGlobal::debugMsg) + fprintf(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", + snd_seq_port_info_get_name(pinfo), + adr.client, adr.port, + flags, capability); + DEBUG_PRST_ROUTES(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", snd_seq_port_info_get_name(pinfo), adr.client, adr.port, flags, capability); - MusEGlobal::midiDevices.add(dev); + if(dev_found) + { +// // The device should be closed right now. Open it if necessary, +// // which will also change the state to 'Open'. +// if(dev->midiPort() != -1) +// dev->open(); + } + else + MusEGlobal::midiDevices.add(dev); } } snd_seq_client_info_set_client(cinfo, -1); // Reset while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { const char* cname = snd_seq_client_info_get_name(cinfo); - //printf( "ALSA client name: %s\n", cname); + //fprintf(stderr, "ALSA client name: %s\n", cname); bool is_thru = (strcmp("Midi Through", cname) == 0); // Put Midi Through and user clients after others. Insert other unwanted clients here: // p4.0.41 @@ -958,7 +1185,22 @@ continue; } snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo); - MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo))); + const QString dev_name(snd_seq_port_info_get_name(pinfo)); + MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI); + const bool dev_found = dev; + if(dev_found) + { + DEBUG_PRST_ROUTES(stderr, "initMidiAlsa device found:%p %s\n", dev, snd_seq_port_info_get_name(pinfo)); + // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device? + dev->setAddressClient(adr.client); + dev->setAddressPort(adr.port); + // The state should be 'Unavailable', change it to 'Closed' (or 'Open' below). + //if(dev->midiPort() == -1) + dev->setState("Closed"); + } + else + dev = new MidiAlsaDevice(adr, dev_name); + //MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo))); int flags = 0; if (capability & outCap) flags |= 1; @@ -968,11 +1210,23 @@ if(is_thru) // Don't auto-open Midi Through. dev->setOpenFlags(0); if (MusEGlobal::debugMsg) - printf("ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", + fprintf(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", + snd_seq_port_info_get_name(pinfo), + adr.client, adr.port, + flags, capability); + DEBUG_PRST_ROUTES(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", snd_seq_port_info_get_name(pinfo), adr.client, adr.port, flags, capability); - MusEGlobal::midiDevices.add(dev); + if(dev_found) + { +// // The device should be closed right now. Open it if necessary, +// // which will also change the state to 'Open'. +// if(dev->midiPort() != -1) +// dev->open(); + } + else + MusEGlobal::midiDevices.add(dev); } } @@ -980,7 +1234,7 @@ //snd_seq_set_client_name(alsaSeq, "MusE Sequencer"); error = snd_seq_set_client_name(alsaSeq, MusEGlobal::audioDevice->clientName()); if (error < 0) { - printf("Alsa: Set client name failed: %s", snd_strerror(error)); + fprintf(stderr, "Alsa: Set client name failed: %s", snd_strerror(error)); return true; } @@ -988,7 +1242,7 @@ int co = snd_seq_poll_descriptors_count(alsaSeq, POLLOUT); if (ci > 1 || co > 1) { - printf("ALSA midi: cannot handle more than one poll fd\n"); + fprintf(stderr, "ALSA midi: cannot handle more than one poll fd\n"); abort(); } @@ -1025,9 +1279,29 @@ snd_seq_port_subscribe_set_sender(subs, &announce_adr); error = snd_seq_subscribe_port(alsaSeq, subs); if (error < 0) { - printf("Alsa: Subscribe System failed: %s", snd_strerror(error)); + fprintf(stderr, "Alsa: Subscribe System failed: %s", snd_strerror(error)); return true; } + + + // The ALSA devices should be closed right now. Open them if necessary, + // which will also change their states to 'Open'. + for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + MidiDevice* d = *i; + switch(d->deviceType()) + { + case MidiDevice::ALSA_MIDI: + if(d->midiPort() != -1) + d->open(); + break; + + case MidiDevice::JACK_MIDI: + case MidiDevice::SYNTH_MIDI: + break; + } + } + return false; } @@ -1058,7 +1332,7 @@ { error = snd_seq_unsubscribe_port(alsaSeq, subs); if(error < 0) - printf("MusE: exitMidiAlsa: Error unsubscribing alsa midi Announce port %d:%d for reading: %s\n", announce_adr.client, announce_adr.port, snd_strerror(error)); + fprintf(stderr, "MusE: exitMidiAlsa: Error unsubscribing alsa midi Announce port %d:%d for reading: %s\n", announce_adr.client, announce_adr.port, snd_strerror(error)); } error = snd_seq_delete_simple_port(alsaSeq, musePort.port); @@ -1068,7 +1342,17 @@ error = snd_seq_close(alsaSeq); if(error < 0) fprintf(stderr, "MusE: Could not close ALSA sequencer: %s\n", snd_strerror(error)); - } + + muse_atomic_destroy(&atomicAlsaMidiScanPending); + } + else + fprintf(stderr, "initMidiAlsa: alsaSeq already exited, ignoring\n"); + + alsaSeq = 0; + // Be sure to call MusEGlobal::midiSeq->msgUpdatePollFd() or midiSeq->updatePollFd() + // for this to take effect. + alsaSeqFdi = -1; + alsaSeqFdo = -1; } @@ -1079,7 +1363,7 @@ void setAlsaClientName(const char* name) { #ifdef ALSA_DEBUG - printf("setAlsaClientName: %s seq:%p\n", name, alsaSeq); + fprintf(stderr, "setAlsaClientName: %s seq:%p\n", name, alsaSeq); #endif if(!alsaSeq) @@ -1087,7 +1371,7 @@ int error = snd_seq_set_client_name(alsaSeq, name); if (error < 0) - printf("setAlsaClientName: failed: %s", snd_strerror(error)); + fprintf(stderr, "setAlsaClientName: failed: %s", snd_strerror(error)); } struct AlsaPort { @@ -1099,6 +1383,7 @@ name = strdup(s); flags = f; } + //~AlsaPort() { if(name) free(name); } }; static std::list portList; @@ -1110,13 +1395,59 @@ void alsaScanMidiPorts() { #ifdef ALSA_DEBUG - printf("alsa scan midi ports\n"); + fprintf(stderr, "alsa scan midi ports\n"); #endif + DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts\n"); + + bool idling = false; + portList.clear(); + + if(!alsaSeq) + { + // Reset this now. + muse_atomic_set(&atomicAlsaMidiScanPending, 0); + + // Check for devices to disable + for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + MidiAlsaDevice* d = dynamic_cast(*i); + if(d == 0) + continue; + + DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts stopped: disabling device:%p %s\n", + d, d->name().toLatin1().constData()); + if(!idling) + { + // Not much choice but to idle both the audio and midi threads since + // midi does not idle while audio messages are being processed. + MusEGlobal::audio->msgIdle(true); + idling = true; + } + + //operations.add(PendingOperationItem(d, SND_SEQ_ADDRESS_UNKNOWN, SND_SEQ_ADDRESS_UNKNOWN, PendingOperationItem::ModifyMidiDeviceAddress)); + d->adr.client = SND_SEQ_ADDRESS_UNKNOWN; + d->adr.port = SND_SEQ_ADDRESS_UNKNOWN; + // Close to reset some device members. + d->close(); + // Update the port's state + d->setState("Unavailable"); + if(d->midiPort() != -1) + MusEGlobal::midiPorts[d->midiPort()].setState(d->state()); + } + + if(idling) + { + MusEGlobal::audio->msgIdle(false); + // Update the GUI. + MusEGlobal::song->update(SC_CONFIG); + } + return; + } + + QString state; const int inCap = SND_SEQ_PORT_CAP_SUBS_READ; const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE; - portList.clear(); - snd_seq_client_info_t* cinfo; snd_seq_client_info_alloca(&cinfo); snd_seq_client_info_set_client(cinfo, 0); @@ -1144,65 +1475,168 @@ flags |= 1; if (capability & inCap) flags |= 2; -// printf("ALSA port add: <%s>, flags %d\n", name, flags); +// fprintf(stderr, "ALSA port add: <%s>, flags %d\n", name, flags); portList.push_back(AlsaPort(adr, name, flags)); } } + + // Reset this now. + muse_atomic_set(&atomicAlsaMidiScanPending, 0); + // // check for devices to delete // - for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end();) { + for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { MidiAlsaDevice* d = dynamic_cast(*i); - if (d == 0) { - ++i; + if (d == 0) continue; - } + std::list::iterator k = portList.begin(); for (; k != portList.end(); ++k) { if (k->adr.client == d->adr.client && k->adr.port == d->adr.port) { break; } + // Search by name if either of the client or port are 0. + if(strcmp(k->name, d->name().toLatin1().constData()) == 0 && + ((d->adr.client == SND_SEQ_ADDRESS_UNKNOWN && d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) || + (d->adr.client == SND_SEQ_ADDRESS_UNKNOWN && d->adr.port == k->adr.port) || + (d->adr.port == SND_SEQ_ADDRESS_UNKNOWN && d->adr.client == k->adr.client))) + break; } if (k == portList.end()) { - if (d->midiPort() != -1) - MusEGlobal::midiPorts[d->midiPort()].setMidiDevice(0); - iMidiDevice k = i; -// printf("erase device\n"); - ++i; - MusEGlobal::midiDevices.erase(k); + DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts nulling adr op:ModifyMidiDeviceAddress device:%p %s\n", + d, d->name().toLatin1().constData()); + if(!idling) + { + // Not much choice but to idle both the audio and midi threads since + // midi does not idle while audio messages are being processed. + MusEGlobal::audio->msgIdle(true); + idling = true; } - else { - ++i; + + //operations.add(PendingOperationItem(d, SND_SEQ_ADDRESS_UNKNOWN, SND_SEQ_ADDRESS_UNKNOWN, PendingOperationItem::ModifyMidiDeviceAddress)); + d->adr.client = SND_SEQ_ADDRESS_UNKNOWN; + d->adr.port = SND_SEQ_ADDRESS_UNKNOWN; + // Close to reset some device members. + d->close(); + // Update the port's state + d->setState("Unavailable"); + if(d->midiPort() != -1) + MusEGlobal::midiPorts[d->midiPort()].setState(d->state()); } } + + // // check for devices to add // - // TODO: Possibly auto-add them to available midi ports. p4.0.41 + // TODO: Possibly auto-add them to available midi ports. // for (std::list::iterator k = portList.begin(); k != portList.end(); ++k) { iMidiDevice i = MusEGlobal::midiDevices.begin(); -// printf("ALSA port: <%s>\n", k->name); for (;i != MusEGlobal::midiDevices.end(); ++i) { MidiAlsaDevice* d = dynamic_cast(*i); if (d == 0) continue; - if ((k->adr.client == d->adr.client) && (k->adr.port == d->adr.port)) { + DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts add: checking port:%s client:%d port:%d device:%p %s client:%d port:%d\n", + k->name, k->adr.client, k->adr.port, d, d->name().toLatin1().constData(), d->adr.client, d->adr.port); + if (k->adr.client == d->adr.client && k->adr.port == d->adr.port) break; - } + + if((d->adr.client == SND_SEQ_ADDRESS_UNKNOWN || d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) && strcmp(k->name, d->name().toLatin1().constData()) == 0) + { + if(d->adr.client != SND_SEQ_ADDRESS_UNKNOWN && d->adr.client != k->adr.client) + { + DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts: k->name:%s d->adr.client:%u != k->adr.client:%u", k->name, d->adr.client, k->adr.client); + //continue; + } + if(d->adr.port != SND_SEQ_ADDRESS_UNKNOWN && d->adr.port != k->adr.port) + { + DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts: k->name:%s d->adr.port:%u != k->adr.port:%u", k->name, d->adr.port, k->adr.port); + //continue; + } + //if(d->adr.client == SND_SEQ_ADDRESS_UNKNOWN) +// d->adr.client = k->adr.client; + //if(d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) +// d->adr.port = k->adr.port; + DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts modifying adr op:ModifyMidiDeviceAddress device:%p %s client:%d port:%d\n", + d, d->name().toLatin1().constData(), k->adr.client, k->adr.port); + + if(!idling) + { + MusEGlobal::audio->msgIdle(true); + idling = true; + } + + //operations.add(PendingOperationItem(d, k->adr.client, k->adr.port, PendingOperationItem::ModifyMidiDeviceAddress)); + // FIXME: Re-subscribe to any ports for now, need a time delay to implement unsubscribe-exit events + //operations.add(PendingOperationItem(d, k->flags, k->flags, PendingOperationItem::ModifyMidiDeviceFlags)); + d->setAddressClient(k->adr.client); + d->setAddressPort(k->adr.port); + d->setrwFlags(k->flags); + // FIXME: Re-subscribe to any ports for now, need a time delay to implement unsubscribe-exit events + d->setOpenFlags(k->flags); + if(d->midiPort() < 0) + // Keep the device closed and update the state. + d->setState("Closed"); + else + // Re-subscribe, open and update the port's state. + MusEGlobal::midiPorts[d->midiPort()].setState(d->open()); + break; + } + } if (i == MusEGlobal::midiDevices.end()) { + + if(!idling) + { + MusEGlobal::audio->msgIdle(true); + idling = true; + } + // add device - MidiAlsaDevice* dev = new MidiAlsaDevice(k->adr, - QString(k->name)); + + const QString dev_name(k->name); + MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI); + const bool dev_found = dev; + if(dev_found) + { + // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device? + dev->setAddressClient(k->adr.client); + dev->setAddressPort(k->adr.port); + } + else + dev = new MidiAlsaDevice(k->adr, dev_name); + //MidiAlsaDevice* dev = new MidiAlsaDevice(k->adr, QString(k->name)); dev->setrwFlags(k->flags); - MusEGlobal::midiDevices.add(dev); -// printf("add device\n"); + DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts op:AddMidiDevice adding:%p %s adr.client:%d adr.port:%d\n", + dev, dev->name().toLatin1().constData(), k->adr.client, k->adr.port); + + //operations.add(PendingOperationItem(MusEGlobal::midiDevices, dev, PendingOperationItem::AddMidiDevice)); + //MusEGlobal::midiDevices.addOperation(dev, operations); + if(!dev_found) + MusEGlobal::midiDevices.add(dev); + // Subscribe + //dev->open(); } } - + //if(!operations.empty()) + //{ + // if(!idling) + // MusEGlobal::audio->msgIdle(true); + // //MusEGlobal::audio->msgExecutePendingOperations(operations, true); + // // Execute directly both stages + // operations.executeNonRTStage(); + // operations.executeRTStage(); + if(idling) + { + MusEGlobal::audio->msgIdle(false); + // Update the GUI. + MusEGlobal::song->update(SC_CONFIG); + } + //} } //--------------------------------------------------------- @@ -1229,33 +1663,165 @@ void alsaProcessMidiInput() { + unsigned frame_ts = MusEGlobal::audio->curFrame(); + const double time_ts = curTime(); + + DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput()\n"); + + if(!alsaSeq) + return; + MidiRecordEvent event; snd_seq_event_t* ev; for (;;) { int rv = snd_seq_event_input(alsaSeq, &ev); -// printf("AlsaInput %d\n", rv); +// fprintf(stderr, "AlsaInput %d\n", rv); if (rv < 0) { -// printf("AlsaMidi: read error %s\n", snd_strerror(rv)); +// fprintf(stderr, "AlsaMidi: read error %s\n", snd_strerror(rv)); return; } + + if (MusEGlobal::midiInputTrace) { + switch(ev->type) + { + // Ignore some flooding events like clock. + case SND_SEQ_EVENT_CLOCK: + break; + + default: + fprintf(stderr, "ALSA MidiIn driver: "); + MidiAlsaDevice::dump(ev); + break; + } + } + switch(ev->type) { case SND_SEQ_EVENT_PORT_SUBSCRIBED: + DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_SUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n", + ev->data.connect.sender.client, ev->data.connect.sender.port, + ev->data.connect.dest.client, ev->data.connect.dest.port); +// MusEGlobal::audio->midiPortsChanged(); // signal gui +// snd_seq_free_event(ev); +// return; + if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) + { + muse_atomic_set(&atomicAlsaMidiScanPending, 1); + MusEGlobal::audio->sendMsgToGui('P'); + } + snd_seq_free_event(ev); + if(rv == 0) + return; + continue; + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: - return; + DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_UNSUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n", + ev->data.connect.sender.client, ev->data.connect.sender.port, + ev->data.connect.dest.client, ev->data.connect.dest.port); +// MusEGlobal::audio->midiPortsChanged(); // signal gui +// snd_seq_free_event(ev); +// return; + if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) + { + muse_atomic_set(&atomicAlsaMidiScanPending, 1); + MusEGlobal::audio->sendMsgToGui('P'); + } + snd_seq_free_event(ev); + if(rv == 0) + return; + continue; + case SND_SEQ_EVENT_CLIENT_START: + DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_CLIENT_START adr: %d:%d\n", + ev->data.addr.client, ev->data.addr.port); + + //alsaScanMidiPorts(); +// MusEGlobal::audio->midiPortsChanged(); // signal gui +// snd_seq_free_event(ev); +// return; + if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) + { + muse_atomic_set(&atomicAlsaMidiScanPending, 1); + MusEGlobal::audio->sendMsgToGui('P'); + } + snd_seq_free_event(ev); + if(rv == 0) + return; + continue; + case SND_SEQ_EVENT_CLIENT_EXIT: + DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_CLIENT_EXIT adr: %d:%d\n", + ev->data.addr.client, ev->data.addr.port); + //snd_seq_free_event(ev); // return; // on first start of a software synthesizer we only // get CLIENT_START event and no PORT_START, why? + //alsaScanMidiPorts(); +// MusEGlobal::audio->midiPortsChanged(); // signal gui +// snd_seq_free_event(ev); +// return; + if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) + { + muse_atomic_set(&atomicAlsaMidiScanPending, 1); + MusEGlobal::audio->sendMsgToGui('P'); + } + snd_seq_free_event(ev); + if(rv == 0) + return; + continue; + case SND_SEQ_EVENT_PORT_START: + DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_START adr: %d:%d\n", + ev->data.addr.client, ev->data.addr.port); + + //alsaScanMidiPorts(); +// MusEGlobal::audio->midiPortsChanged(); // signal gui +// snd_seq_free_event(ev); +// return; + if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) + { + muse_atomic_set(&atomicAlsaMidiScanPending, 1); + MusEGlobal::audio->sendMsgToGui('P'); + } + snd_seq_free_event(ev); + if(rv == 0) + return; + continue; + case SND_SEQ_EVENT_PORT_EXIT: - alsaScanMidiPorts(); - MusEGlobal::audio->midiPortsChanged(); // signal gui + DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_EXIT adr: %d:%d\n", + ev->data.addr.client, ev->data.addr.port); + //alsaScanMidiPorts(); +// MusEGlobal::audio->midiPortsChanged(); // signal gui +// snd_seq_free_event(ev); +// return; + if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) + { + muse_atomic_set(&atomicAlsaMidiScanPending, 1); + MusEGlobal::audio->sendMsgToGui('P'); + } snd_seq_free_event(ev); - return; + if(rv == 0) + return; + continue; + +// case SND_SEQ_EVENT_PORT_SUBSCRIBED: +// case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: +// case SND_SEQ_EVENT_CLIENT_START: +// case SND_SEQ_EVENT_CLIENT_EXIT: +// case SND_SEQ_EVENT_PORT_START: +// case SND_SEQ_EVENT_PORT_EXIT: +// if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) +// { +// muse_atomic_set(&atomicAlsaMidiScanPending, 1); +// MusEGlobal::audio->sendMsgToGui('P'); +// } +// snd_seq_free_event(ev); +// if(rv == 0) +// return; +// continue; } int curPort = -1; @@ -1264,9 +1830,11 @@ // find real source device // for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { - MidiAlsaDevice* d = dynamic_cast(*i); - if (d && d->adr.client == ev->source.client - && d->adr.port == ev->source.port) { + if((*i)->deviceType() != MidiDevice::ALSA_MIDI) + continue; + MidiAlsaDevice* d = static_cast(*i); + if(d->adr.client == ev->source.client + && d->adr.port == ev->source.port) { curPort = d->midiPort(); mdev = d; } @@ -1278,20 +1846,36 @@ ev->source.client, ev->source.port); } snd_seq_free_event(ev); - return; + //return; + if(rv == 0) + return; + continue; } - + event.setType(0); // mark as unused event.setPort(curPort); event.setB(0); + //MidiInstrument* instr = MusEGlobal::midiPorts[curPort].inputInstrument(); + switch(ev->type) { case SND_SEQ_EVENT_NOTEON: - event.setChannel(ev->data.note.channel); - event.setType(ME_NOTEON); - event.setA(ev->data.note.note); - event.setB(ev->data.note.velocity); + if(ev->data.note.velocity == 0) + { + // Convert zero-velocity note ons to note offs as per midi spec. + event.setChannel(ev->data.note.channel); + event.setType(ME_NOTEOFF); + event.setA(ev->data.note.note); + event.setB(ev->data.note.velocity); + } + else + { + event.setChannel(ev->data.note.channel); + event.setType(ME_NOTEON); + event.setA(ev->data.note.note); + event.setB(ev->data.note.velocity); + } break; case SND_SEQ_EVENT_NOTEOFF: @@ -1334,53 +1918,62 @@ break; case SND_SEQ_EVENT_CLOCK: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CLOCK, curTime()); - //mdev->syncInfo().trigMCSyncDetect(); + if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) + mdev->midiClockInput(frame_ts); break; case SND_SEQ_EVENT_START: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_START, curTime()); + #ifdef ALSA_DEBUG + if(MusEGlobal::midiInputTrace) + fprintf(stderr, "alsaProcessMidiInput: start port:%d curFrame:%u\n", curPort, frame_ts); + #endif + MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_START, time_ts); break; case SND_SEQ_EVENT_CONTINUE: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_CONTINUE, curTime()); + MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_CONTINUE, time_ts); break; case SND_SEQ_EVENT_STOP: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_STOP, curTime()); + MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_STOP, time_ts); break; case SND_SEQ_EVENT_TICK: - MusEGlobal::midiSeq->realtimeSystemInput(curPort, ME_TICK, curTime()); - //mdev->syncInfo().trigTickDetect(); + MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_TICK, time_ts); break; case SND_SEQ_EVENT_SYSEX: - // TODO: Deal with large sysex, which are broken up into chunks! - // For now, do not accept if the first byte is not SYSEX or the last byte is not EOX, - // meaning it's a chunk, possibly with more chunks to follow. - if((*((unsigned char*)ev->data.ext.ptr) != ME_SYSEX) || - (*(((unsigned char*)ev->data.ext.ptr) + ev->data.ext.len - 1) != ME_SYSEX_END)) { - printf("MusE: alsaProcessMidiInput sysex chunks not supported!\n"); - break; + EvData ed; + const unsigned char* p = (unsigned char*)ev->data.ext.ptr; + + // Process the input. Create the event data only if finished. + if(mdev->sysExInProcessor()->processInput( + &ed, p, ev->data.ext.len, frame_ts) != SysExInputProcessor::Finished) + break; + + #ifdef ALSA_DEBUG + fprintf(stderr, "alsaProcessMidiInput: SysEx: frame_ts:%u startFrame:%u\n", + frame_ts, (unsigned int)mdev->sysExInProcessor()->startFrame()); + #endif + + // Finished composing the sysex data. + // Mark the frame timestamp as the frame at which the sysex started. + frame_ts = mdev->sysExInProcessor()->startFrame(); + event.setType(ME_SYSEX); + event.setData(ed); } - - event.setTime(0); // mark as used - event.setType(ME_SYSEX); - event.setData((unsigned char*)(ev->data.ext.ptr)+1, - ev->data.ext.len-2); break; case SND_SEQ_EVENT_PORT_SUBSCRIBED: case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // write port is released break; case SND_SEQ_EVENT_SONGPOS: - MusEGlobal::midiSeq->setSongPosition(curPort, ev->data.control.value); + MusEGlobal::midiSyncContainer.setSongPosition(curPort, ev->data.control.value); break; case SND_SEQ_EVENT_SENSING: break; case SND_SEQ_EVENT_QFRAME: - MusEGlobal::midiSeq->mtcInputQuarter(curPort, ev->data.control.value); + MusEGlobal::midiSyncContainer.mtcInputQuarter(curPort, ev->data.control.value); break; // case SND_SEQ_EVENT_CLIENT_START: // case SND_SEQ_EVENT_CLIENT_EXIT: @@ -1397,14 +1990,29 @@ // case SND_SEQ_EVENT_NOTE: // case SND_SEQ_EVENT_CONTROL14: - // case SND_SEQ_EVENT_NONREGPARAM: +// case SND_SEQ_EVENT_NONREGPARAM: +// fprintf(stderr, "ALSA Midi input: NONREGPARAM ch:%u param:%u value:%d\n", +// ev->data.control.channel, +// ev->data.control.param, +// ev->data.control.value); +// event.setChannel(ev->data.control.channel); +// event.setType(ME_CONTROLLER); +// event.setA(ev->data.control.param); +// event.setB(ev->data.control.value); +// break; // case SND_SEQ_EVENT_REGPARAM: default: - printf("ALSA Midi input: type %d not handled\n", ev->type); + fprintf(stderr, "ALSA Midi input: type %d not handled\n", ev->type); break; } if(event.type()) + { + // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. + event.setTime(frame_ts); + event.setTick(MusEGlobal::lastExtMidiSyncTick); + mdev->recordEvent(event); + } snd_seq_free_event(ev); if (rv == 0) @@ -1412,4 +2020,181 @@ } } +//--------------------------------------------------------- +// dump +// static +//--------------------------------------------------------- + +void MidiAlsaDevice::dump(const snd_seq_event_t* ev) +{ + switch(ev->type) + { + case SND_SEQ_EVENT_PORT_SUBSCRIBED: + fprintf(stderr, "SND_SEQ_EVENT_PORT_SUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n", + ev->data.connect.sender.client, ev->data.connect.sender.port, + ev->data.connect.dest.client, ev->data.connect.dest.port); + break; + + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: + fprintf(stderr, "SND_SEQ_EVENT_PORT_UNSUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n", + ev->data.connect.sender.client, ev->data.connect.sender.port, + ev->data.connect.dest.client, ev->data.connect.dest.port); + break; + + case SND_SEQ_EVENT_CLIENT_START: + fprintf(stderr, "SND_SEQ_EVENT_CLIENT_START adr: %d:%d\n", + ev->data.addr.client, ev->data.addr.port); + break; + + case SND_SEQ_EVENT_CLIENT_EXIT: + fprintf(stderr, "SND_SEQ_EVENT_CLIENT_EXIT adr: %d:%d\n", + ev->data.addr.client, ev->data.addr.port); + break; + + case SND_SEQ_EVENT_PORT_START: + fprintf(stderr, "SND_SEQ_EVENT_PORT_START adr: %d:%d\n", + ev->data.addr.client, ev->data.addr.port); + break; + + case SND_SEQ_EVENT_PORT_EXIT: + fprintf(stderr, "SND_SEQ_EVENT_PORT_EXIT adr: %d:%d\n", + ev->data.addr.client, ev->data.addr.port); + break; + + case SND_SEQ_EVENT_CONTROLLER: + fprintf(stderr, "SND_SEQ_EVENT_CONTROLLER chan:%u param:%u value:%d\n", + ev->data.control.channel, ev->data.control.param, ev->data.control.value); + break; + + case SND_SEQ_EVENT_NOTE: + fprintf(stderr, "SND_SEQ_EVENT_NOTE chan:%u note:%u velocity:%u off_velocity:%u duration:%u\n", + ev->data.note.channel, ev->data.note.note, ev->data.note.velocity, ev->data.note.off_velocity, ev->data.note.duration); + break; + + case SND_SEQ_EVENT_NOTEON: + fprintf(stderr, "SND_SEQ_EVENT_NOTEON chan:%u note:%u velocity:%u\n", + ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_NOTEOFF: + fprintf(stderr, "SND_SEQ_EVENT_NOTEOFF chan:%u note:%u velocity:%u\n", + ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_KEYPRESS: + fprintf(stderr, "SND_SEQ_EVENT_KEYPRESS chan:%u note:%u velocity:%u\n", + ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_CHANPRESS: + fprintf(stderr, "SND_SEQ_EVENT_CHANPRESS chan:%u value:%d\n", + ev->data.control.channel, ev->data.control.value); + break; + + case SND_SEQ_EVENT_PGMCHANGE: + fprintf(stderr, "SND_SEQ_EVENT_PGMCHANGE chan:%u value:%d\n", + ev->data.control.channel, ev->data.control.value); + break; + + case SND_SEQ_EVENT_PITCHBEND: + fprintf(stderr, "SND_SEQ_EVENT_PITCHBEND chan:%u value:%d\n", + ev->data.control.channel, ev->data.control.value); + break; + + case SND_SEQ_EVENT_CLOCK: + fprintf(stderr, "SND_SEQ_EVENT_CLOCK\n"); + break; + + case SND_SEQ_EVENT_START: + fprintf(stderr, "SND_SEQ_EVENT_START\n"); + break; + + case SND_SEQ_EVENT_CONTINUE: + fprintf(stderr, "SND_SEQ_EVENT_CONTINUE\n"); + break; + + case SND_SEQ_EVENT_STOP: + fprintf(stderr, "SND_SEQ_EVENT_STOP\n"); + break; + + case SND_SEQ_EVENT_TICK: + fprintf(stderr, "SND_SEQ_EVENT_TICK\n"); + break; + + case SND_SEQ_EVENT_SYSEX: + fprintf(stderr, "SND_SEQ_EVENT_SYSEX len:%u data: ", ev->data.ext.len); + for(unsigned int i = 0; i < ev->data.ext.len && i < 16; ++i) + fprintf(stderr, "%0x ", ((unsigned char*)ev->data.ext.ptr)[i]); + if(ev->data.ext.len >= 16) + fprintf(stderr, "..."); + fprintf(stderr, "\n"); + break; + + case SND_SEQ_EVENT_SONGPOS: + fprintf(stderr, "SND_SEQ_EVENT_SONGPOS value:%d\n", + ev->data.control.value); + break; + + case SND_SEQ_EVENT_SENSING: + fprintf(stderr, "SND_SEQ_EVENT_SENSING\n"); + break; + + case SND_SEQ_EVENT_QFRAME: + fprintf(stderr, "SND_SEQ_EVENT_QFRAME value:%d\n", + ev->data.control.value); + break; + case SND_SEQ_EVENT_CONTROL14: + fprintf(stderr, "SND_SEQ_EVENT_CONTROL14 ch:%u param:%u value:%d\n", + ev->data.control.channel, + ev->data.control.param, + ev->data.control.value); + break; + + case SND_SEQ_EVENT_NONREGPARAM: + fprintf(stderr, "SND_SEQ_EVENT_NONREGPARAM ch:%u param:%u value:%d\n", + ev->data.control.channel, + ev->data.control.param, + ev->data.control.value); + break; + + case SND_SEQ_EVENT_REGPARAM: + fprintf(stderr, "SND_SEQ_EVENT_REGPARAM ch:%u param:%u value:%d\n", + ev->data.control.channel, + ev->data.control.param, + ev->data.control.value); + break; + + // case SND_SEQ_EVENT_CLIENT_CHANGE: + // case SND_SEQ_EVENT_PORT_CHANGE: + // case SND_SEQ_EVENT_SONGSEL: + // case SND_SEQ_EVENT_TIMESIGN: + // case SND_SEQ_EVENT_KEYSIGN: + // case SND_SEQ_EVENT_SETPOS_TICK: + // case SND_SEQ_EVENT_SETPOS_TIME: + // case SND_SEQ_EVENT_TEMPO: + // case SND_SEQ_EVENT_TUNE_REQUEST: + // case SND_SEQ_EVENT_RESET: + + default: + fprintf(stderr, "ALSA dump event: unknown type:%u\n", ev->type); + break; + } +} + } // namespace MusECore + +#else // ALSA_SUPPORT + +namespace MusECore { + +void initDSSI() {} +bool initMidiAlsa() { return false; } +void exitMidiAlsa() {} +int alsaSelectRfd() { return -1; } +int alsaSelectWfd() { return -1; } +void alsaProcessMidiInput() { } +void alsaScanMidiPorts() { } +void setAlsaClientName(const char*) { } +} + +#endif // ALSA_SUPPORT diff -Nru muse-2.1.2/muse/driver/alsamidi.h muse-3.0.2+ds1/muse/driver/alsamidi.h --- muse-2.1.2/muse/driver/alsamidi.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/alsamidi.h 2017-12-17 21:07:38.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // $Id: alsamidi.h,v 1.2 2004/01/14 09:06:43 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,14 +24,21 @@ #ifndef __ALSAMIDI_H__ #define __ALSAMIDI_H__ -#include +#include "config.h" + +#ifdef ALSA_SUPPORT + #include #include "mpevent.h" #include "mididev.h" +#endif // ALSA_SUPPORT + namespace MusECore { +#ifdef ALSA_SUPPORT + class Xml; //--------------------------------------------------------- @@ -43,39 +50,52 @@ snd_seq_addr_t adr; private: - // Special for ALSA midi device: Play event list is processed in the ALSA midi sequencer thread. - // Need this FIFO, to decouple from audio thread which adds events to the list. - //MidiFifo playEventFifo; - //MidiFifo stuckNotesFifo; + // Fifo for midi playback events sent from audio thread: + // The audio thread will gather the events in _playEvents for the + // convenience of its sorting, then dump them to this FIFO so that + // a driver or device may read it, possibly from another thread (ALSA driver). + SeqMPEventList _outPlaybackEvents; + SeqMPEventList _outUserEvents; + + // Return false if event is delivered. + bool processEvent(const MidiPlayEvent& ev); - virtual QString open(); - virtual void close(); virtual void processInput() {} virtual int selectRfd() { return -1; } virtual int selectWfd(); - bool putEvent(snd_seq_event_t*); - virtual bool putMidiEvent(const MidiPlayEvent&); - + bool putAlsaEvent(snd_seq_event_t*); + public: MidiAlsaDevice(const snd_seq_addr_t&, const QString& name); - virtual ~MidiAlsaDevice() {} + virtual ~MidiAlsaDevice(); - virtual void* inClientPort() { return (void*)&adr; } // For ALSA midi, in/out client ports are the same. - virtual void* outClientPort() { return (void*)&adr; } // That is, ALSA midi client ports can be both r/w. + static MidiDevice* createAlsaMidiDevice(QString name = "", int rwflags = 3); // 1:Writable 2: Readable 3: Writable + Readable + static void dump(const snd_seq_event_t* ev); + + virtual QString open(); + virtual void close(); + + // The meaning of the returned pointer depends on the driver. + // For Jack it returns the address of a Jack port, for ALSA it return the address of a snd_seq_addr_t. + // For ALSA midi, in/out client ports are the same. That is, ALSA midi client ports can be both r/w. + virtual void* inClientPort() { return (void*)&adr; } + virtual void* outClientPort() { return (void*)&adr; } virtual void writeRouting(int, Xml&) const; - virtual inline int deviceType() const { return ALSA_MIDI; } - // Schedule an event for playback. Returns false if event cannot be delivered. - //virtual bool addScheduledEvent(const MidiPlayEvent& ev) { return !playEventFifo.put(ev); } - // Add a stuck note. Returns false if event cannot be delivered. - //virtual bool addStuckNote(const MidiPlayEvent& ev) { return !stuckNotesFifo.put(ev); } + virtual inline MidiDeviceType deviceType() const { return ALSA_MIDI; } + // Play all events up to current frame. - virtual void processMidi(); - //virtual void handleStop(); - //virtual void handleSeek(); + virtual void processMidi(unsigned int curFrame = 0); + + virtual void setAddressClient(int client) { adr.client = client; } + virtual void setAddressPort(int port) { adr.port = port; } + // We (ab)use the ALSA value SND_SEQ_ADDRESS_UNKNOWN to mean 'unavailable' - if BOTH client and port equal it. + virtual bool isAddressUnknown() const { return adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN; } }; +#endif // ALSA_SUPPORT + extern bool initMidiAlsa(); extern void exitMidiAlsa(); extern int alsaSelectRfd(); @@ -84,6 +104,7 @@ extern void alsaScanMidiPorts(); extern void setAlsaClientName(const char*); + } // namespace MusECore #endif diff -Nru muse-2.1.2/muse/driver/alsatimer.cpp muse-3.0.2+ds1/muse/driver/alsatimer.cpp --- muse-2.1.2/muse/driver/alsatimer.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/alsatimer.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -7,6 +7,7 @@ // alsalib 1.0.7 // // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) +// (C) Copyright 2016-2017 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,27 +25,32 @@ // //========================================================= + #include "alsatimer.h" +#include "globals.h" + +#ifdef ALSA_SUPPORT + #include +#include -#define TIMER_DEBUG 0 +#define ALSA_TIMER_DEBUG 0 namespace MusECore { AlsaTimer::AlsaTimer() { - if(TIMER_DEBUG) + if(TIMER_DEBUG || ALSA_TIMER_DEBUG) fprintf(stderr,"AlsaTimer::AlsaTimer(this=%p) called\n",this); handle = NULL; id = NULL; info = NULL; params = NULL; - findBest = true; } AlsaTimer::~AlsaTimer() { - if(TIMER_DEBUG) + if(TIMER_DEBUG || ALSA_TIMER_DEBUG) fprintf(stderr,"AlsaTimer::~AlsaTimer(this=%p) called\n",this); if (handle) snd_timer_close(handle); @@ -52,159 +58,231 @@ if (info) snd_timer_info_free(info); if (params) snd_timer_params_free(params); } - - signed int AlsaTimer::initTimer() - { - if(TIMER_DEBUG) - printf("AlsaTimer::initTimer(this=%p)\n",this); - - int err; + +signed int AlsaTimer::initTimer(unsigned long desiredFrequency) +{ + if(TIMER_DEBUG || ALSA_TIMER_DEBUG) + fprintf(stderr, "AlsaTimer::initTimer(this=%p)\n",this); + + if(id || info || params) + { + fprintf(stderr, "AlsaTimer::initTimer(): called on initialised timer!\n"); + return fds->fd; + } + + snd_timer_id_malloc(&id); + snd_timer_id_set_class(id, SND_TIMER_CLASS_NONE); + snd_timer_info_malloc(&info); + snd_timer_params_malloc(¶ms); + + int best_dev = SND_TIMER_GLOBAL_SYSTEM; + int best_devclass = SND_TIMER_CLASS_GLOBAL; + int best_sclass = SND_TIMER_CLASS_NONE; + int best_card = 0; + int best_subdevice = 0; + unsigned long best_freq = 0; + int err; + + snd_timer_query_t *timer_query = NULL; + if(snd_timer_query_open(&timer_query, "hw", 0) >= 0) + { + int device = SND_TIMER_GLOBAL_SYSTEM; int devclass = SND_TIMER_CLASS_GLOBAL; int sclass = SND_TIMER_CLASS_NONE; int card = 0; - int device = SND_TIMER_GLOBAL_SYSTEM; int subdevice = 0; - int test_ids[] = { SND_TIMER_GLOBAL_SYSTEM - , SND_TIMER_GLOBAL_RTC -#ifdef SND_TIMER_GLOBAL_HPET - , SND_TIMER_GLOBAL_HPET -#endif - }; - int max_ids = sizeof(test_ids) / sizeof(int); - long best_res = LONG_MAX; - //int best_dev = -1; // SND_TIMER_GLOBAL_SYSTEM; - int best_dev = SND_TIMER_GLOBAL_SYSTEM; - int i; - - if (id || info || params) { - fprintf(stderr,"AlsaTimer::initTimer(): called on initialised timer!\n"); - return fds->fd; - } - snd_timer_id_malloc(&id); - snd_timer_info_malloc(&info); - snd_timer_params_malloc(¶ms); - - if (findBest) { - for (i = 0; i < max_ids; ++i) { - device = test_ids[i]; - sprintf(timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", devclass, sclass, card, device, subdevice); - if ((err = snd_timer_open(&handle, timername, SND_TIMER_OPEN_NONBLOCK)) < 0) { - continue; - } - if ((err = snd_timer_info(handle, info)) < 0) { - snd_timer_close(handle); - continue; - } - // select a non slave timer with the lowest resolution value - int is_slave = snd_timer_info_is_slave(info); - long res = snd_timer_info_get_resolution(info); - if ((is_slave == 0) && (best_res > res)) { - best_res = res; - best_dev = device; + + while(snd_timer_query_next_device(timer_query, id) >= 0) + { + devclass = snd_timer_id_get_class(id); + if(devclass < 0) + break; + sclass = snd_timer_id_get_sclass(id); + if(sclass < 0) + sclass = 0; + card = snd_timer_id_get_card(id); + if(card < 0) + card = 0; + device = snd_timer_id_get_device(id); + if(device < 0) + device = 0; + subdevice = snd_timer_id_get_subdevice(id); + if(subdevice < 0) + subdevice = 0; + snprintf(timername, sizeof(timername) - 1, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", devclass, sclass, card, device, subdevice); + if(snd_timer_open(&handle, timername, SND_TIMER_OPEN_NONBLOCK) >= 0) + { + if(snd_timer_info(handle, info) >= 0) + { + // Select a non slave timer with the lowest resolution value + if(snd_timer_info_is_slave(info) == 0) + { + unsigned long freq = setTimerFreq(desiredFrequency); + + if(MusEGlobal::debugMsg) + fprintf(stderr, "AlsaTimer::initTimer(): Checked timer:%s got frequency:%lu Hz\n", snd_timer_info_get_name(info), freq); + + if(freq > best_freq) + { + best_freq = freq; + best_dev = device; + best_devclass = devclass; + best_sclass = sclass; + best_card = card; + best_subdevice = subdevice; + } } - snd_timer_close(handle); } - device = best_dev; + snd_timer_close(handle); } + } + snd_timer_query_close(timer_query); + } - //if(best_dev==-1) - // return -1; // no working timer found + sprintf(timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", best_devclass, best_sclass, best_card, best_dev, best_subdevice); + if ((err = snd_timer_open(&handle, timername, SND_TIMER_OPEN_NONBLOCK))<0) { + fprintf(stderr, "AlsaTimer::initTimer(): timer open %i (%s)\n", err, snd_strerror(err)); + return -1; + } - sprintf(timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", devclass, sclass, card, device, subdevice); - if ((err = snd_timer_open(&handle, timername, SND_TIMER_OPEN_NONBLOCK))<0) { - fprintf(stderr, "AlsaTimer::initTimer(): timer open %i (%s)\n", err, snd_strerror(err)); - return -1; - } - - if ((err = snd_timer_info(handle, info)) < 0) { - fprintf(stderr, "AlsaTimer::initTimer(): timer info %i (%s)\n", err, snd_strerror(err)); - return -1; - } + if ((err = snd_timer_info(handle, info)) < 0) { + fprintf(stderr, "AlsaTimer::initTimer(): timer info %i (%s)\n", err, snd_strerror(err)); + return -1; + } - //if(debugMsg) - fprintf(stderr, "AlsaTimer::initTimer(): best available ALSA timer: %s\n", snd_timer_info_get_name(info)); + //if(debugMsg) + fprintf(stderr, "AlsaTimer::initTimer(): best available ALSA timer: %s\n", snd_timer_info_get_name(info)); - snd_timer_params_set_auto_start(params, 1); - snd_timer_params_set_ticks(params, 1); - - if ((err = snd_timer_params(handle, params)) < 0) { - fprintf(stderr, "AlsaTimer::initTimer(): timer params %i (%s)\n", err, snd_strerror(err)); - return -1; - } - - count = snd_timer_poll_descriptors_count(handle); - fds = (pollfd *)calloc(count, sizeof(pollfd)); - if (fds == NULL) { - fprintf(stderr, "AlsaTimer::initTimer(): malloc error\n"); - return -1; - } - if ((err = snd_timer_poll_descriptors(handle, fds, count)) < 0) { - fprintf(stderr, "AlsaTimer::initTimer(): snd_timer_poll_descriptors error: %s\n", snd_strerror(err)); - return -1; - } - return fds->fd; + count = snd_timer_poll_descriptors_count(handle); + fds = (pollfd *)calloc(count, sizeof(pollfd)); + if (fds == NULL) { + fprintf(stderr, "AlsaTimer::initTimer(): malloc error\n"); + return -1; } - - unsigned int AlsaTimer::setTimerResolution(unsigned int resolution) + if ((err = snd_timer_poll_descriptors(handle, fds, count)) < 0) { + fprintf(stderr, "AlsaTimer::initTimer(): snd_timer_poll_descriptors error: %s\n", snd_strerror(err)); + return -1; + } + + return fds->fd; +} + +unsigned long AlsaTimer::setTimerResolution(unsigned long resolution) { - if(TIMER_DEBUG) - printf("AlsaTimer::setTimerResolution(%d)\n",resolution); + if(TIMER_DEBUG || ALSA_TIMER_DEBUG) + fprintf(stderr, "AlsaTimer::setTimerResolution(%lu)\n",resolution); /* Resolution of an AlsaTimer is fixed - it cannot be set */ return 0; } - unsigned int AlsaTimer::setTimerFreq(unsigned int freq) - { - signed int err; - unsigned int setTick, actFreq; - - if(TIMER_DEBUG) - printf("AlsaTimer::setTimerFreq(this=%p)\n",this); +unsigned long AlsaTimer::setTimerFreq(unsigned long freq) +{ + if(freq == 0) + return 0; + + signed int err; + + if(TIMER_DEBUG || ALSA_TIMER_DEBUG) + fprintf(stderr, "AlsaTimer::setTimerFreq(this=%p)\n",this); - setTick = (1000000000 / snd_timer_info_get_resolution(info)) / freq; + const long int res = snd_timer_info_get_resolution(info); + const long int adj_res = 1000000000L / res; + long int setTick = adj_res / freq; - if (setTick == 0) { - // return, print error if freq is below 500 (timing will suffer) - if (((1000000000.0 / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params)) < 500) { - fprintf(stderr,"AlsaTimer::setTimerTicks(): requested freq %u Hz too high for timer (max is %g)\n", - freq, 1000000000.0 / snd_timer_info_get_resolution(info)); - fprintf(stderr," freq stays at %ld Hz\n", - (long int)((1000000000.0 / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params))); - } - - return (long int)((1000000000.0 / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params)); - } - actFreq = (1000000000 / snd_timer_info_get_resolution(info)) / setTick; - if (actFreq != freq) { - fprintf(stderr,"AlsaTimer::setTimerTicks(): warning: requested %u Hz, actual freq is %u Hz\n", - freq, actFreq); - } - if(TIMER_DEBUG) - printf("AlsaTimer::setTimerFreq(): Setting ticks (period) to %d ticks\n", setTick); - snd_timer_params_set_auto_start(params, 1); + if(setTick <= 0) + setTick = 1; + + snd_timer_params_set_auto_start(params, 1); + if(!snd_timer_info_is_slave(info)) + { snd_timer_params_set_ticks(params, setTick); - if ((err = snd_timer_params(handle, params)) < 0) { - fprintf(stderr, "AlsaTimer::setTimerFreq(): timer params %i (%s)\n", err, snd_strerror(err)); - return 0; + if(snd_timer_params_get_ticks(params) < 1) + snd_timer_params_set_ticks(params, 1); + } + else + snd_timer_params_set_ticks(params, 1); + + if((err = snd_timer_params(handle, params)) < 0) + { + const int num_freqs = 10; + const unsigned int freqs[num_freqs] = {32768, 16384, 8192, 4096, 2048, 1024, 1000, 500, 250, 100}; + int found_idx = -1; + if(!snd_timer_info_is_slave(info)) + { + + for(int i = 0; i < num_freqs; ++i) + { + const unsigned int f = freqs[i]; + if(f >= freq) + continue; + + long int t = adj_res / f; + if(t <= 0) + t = 1; + + snd_timer_params_set_ticks(params, t); + if(snd_timer_params_get_ticks(params) < 1) + snd_timer_params_set_ticks(params, 1); + if((err = snd_timer_params(handle, params)) == 0) + { + found_idx = i; + break; + } } + if(found_idx == -1) + { + if(MusEGlobal::debugMsg) + fprintf(stderr, "MusE: Cannot find a suitable ALSA timer frequency. Your system may need adjustment.\n"); + snd_timer_params_set_ticks(params, 1); + return 0; + } + } - return actFreq; + if(found_idx >= 0) + { + if(MusEGlobal::debugMsg) + fprintf(stderr, "MusE: Cannot set requested ALSA timer frequency (%luHz). Your system may need adjustment.\n" + " Timer frequency set to best value: %liHz\n", + freq, + 1000000000L / snd_timer_info_get_resolution(info) / snd_timer_params_get_ticks(params)); } + } + + const long int ticks = snd_timer_params_get_ticks(params); + const long int cur_freq = adj_res / ticks; + if(setTick <= 0) + { + // Return, print error if freq is below 500 (timing will suffer). + if(cur_freq < 500) + { + if(MusEGlobal::debugMsg) + { + fprintf(stderr,"AlsaTimer::setTimerFreq(): requested freq %lu Hz too high for timer (max is %ld)\n", freq, adj_res); + fprintf(stderr," freq stays at %ld Hz\n", cur_freq); + } + } + } + + if(MusEGlobal::debugMsg) + fprintf(stderr, "AlsaTimer::setTimerFreq(%lu): Using %li tick(s)\n", freq, ticks); + + return (unsigned long)cur_freq; +} - unsigned int AlsaTimer::getTimerResolution() + unsigned long AlsaTimer::getTimerResolution() { return snd_timer_info_get_resolution(info); } - unsigned int AlsaTimer::getTimerFreq() + unsigned long AlsaTimer::getTimerFreq() { - return (1000000000 / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params); + return (1000000000L / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params); } bool AlsaTimer::startTimer() { - if(TIMER_DEBUG) - printf("AlsaTimer::startTimer(this=%p): handle=%p\n",this,handle); + if(TIMER_DEBUG || ALSA_TIMER_DEBUG) + fprintf(stderr, "AlsaTimer::startTimer(this=%p): handle=%p\n",this,handle); int err; if ((err = snd_timer_start(handle)) < 0) { fprintf(stderr, "AlsaTimer::startTimer(): timer start %i (%s)\n", err, snd_strerror(err)); @@ -216,8 +294,8 @@ bool AlsaTimer::stopTimer() { int err; - if(TIMER_DEBUG) - printf("AlsaTimer::stopTimer(this=%p): handle=%p\n",this,handle); + if(TIMER_DEBUG || ALSA_TIMER_DEBUG) + fprintf(stderr, "AlsaTimer::stopTimer(this=%p): handle=%p\n",this,handle); if ((err = snd_timer_stop(handle)) < 0) { fprintf(stderr, "AlsaTimer::stopTimer(): timer stop %i (%s)\n", err, snd_strerror(err)); return false; @@ -225,15 +303,13 @@ return true; } - unsigned int AlsaTimer::getTimerTicks(bool printTicks) + unsigned long AlsaTimer::getTimerTicks(bool printTicks) { - //if(TIMER_DEBUG) - // printf("AlsaTimer::getTimerTicks\n"); snd_timer_read_t tr; tr.ticks = 0; while (snd_timer_read(handle, &tr, sizeof(tr)) == sizeof(tr)) { if (printTicks) { - printf("TIMER: resolution = %uns, ticks = %u\n", + fprintf(stderr, "TIMER: resolution = %uns, ticks = %u\n", tr.resolution, tr.ticks); } } @@ -241,3 +317,5 @@ } } // namespace MusECore + +#endif // ALSA_SUPPORT diff -Nru muse-2.1.2/muse/driver/alsatimer.h muse-3.0.2+ds1/muse/driver/alsatimer.h --- muse-2.1.2/muse/driver/alsatimer.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/alsatimer.h 2017-12-04 21:01:18.000000000 +0000 @@ -27,6 +27,10 @@ #ifndef __ALSATIMER_H__ #define __ALSATIMER_H__ +#include "config.h" + +#ifdef ALSA_SUPPORT + #include "alsa/asoundlib.h" #include "timerdev.h" @@ -43,28 +47,26 @@ snd_timer_info_t *info; snd_timer_params_t *params; struct pollfd *fds; - char timername[64]; + char timername[256]; signed int count; - unsigned int ticks; - bool findBest; public: AlsaTimer(); virtual ~AlsaTimer(); - virtual signed int initTimer(); - virtual unsigned int setTimerResolution(unsigned int resolution); - virtual unsigned int getTimerResolution(); - virtual unsigned int setTimerFreq(unsigned int freq); - virtual unsigned int getTimerFreq(); + virtual signed int initTimer(unsigned long desiredFrequency); + virtual unsigned long setTimerResolution(unsigned long resolution); + virtual unsigned long getTimerResolution(); + virtual unsigned long setTimerFreq(unsigned long freq); + virtual unsigned long getTimerFreq(); virtual bool startTimer(); virtual bool stopTimer(); - virtual unsigned int getTimerTicks(bool printTicks=false); - - void setFindBestTimer(bool b) { findBest = b; } + virtual unsigned long getTimerTicks(bool printTicks=false); }; } // namespace MusECore +#endif // ALSA_SUPPORT + #endif //__ALSATIMER_H__ diff -Nru muse-2.1.2/muse/driver/audiodev.h muse-3.0.2+ds1/muse/driver/audiodev.h --- muse-2.1.2/muse/driver/audiodev.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/audiodev.h 2018-01-06 20:31:35.000000000 +0000 @@ -25,6 +25,8 @@ #define __AUDIODEV_H__ #include +#include +#include class QString; @@ -39,21 +41,29 @@ class AudioDevice { public: - enum { DUMMY_AUDIO=0, JACK_AUDIO=1 }; // p3.3.52 + enum { DUMMY_AUDIO=0, JACK_AUDIO=1, RTAUDIO_AUDIO=2 }; + enum PortType { UnknownType=0, AudioPort=1, MidiPort=2 }; + enum PortDirection { UnknownDirection=0, InputPort=1, OutputPort=2 }; AudioDevice() {} virtual ~AudioDevice() {} - virtual int deviceType() const = 0; // p3.3.52 + virtual int deviceType() const = 0; - //virtual void start() = 0; - virtual void start(int priority) = 0; + // Returns true on success. + virtual bool start(int priority) = 0; virtual void stop () = 0; + // Estimated current frame. + // This is meant to be called from threads other than the process thread. virtual int framePos() const = 0; virtual unsigned frameTime() const = 0; virtual double systemTime() const = 0; + // These are meant to be called from inside process thread only. + virtual unsigned framesAtCycleStart() const = 0; + virtual unsigned framesSinceCycleStart() const = 0; + virtual float* getBuffer(void* port, unsigned long nframes) = 0; virtual std::list outputPorts(bool midi = false, int aliases = -1) = 0; @@ -67,14 +77,36 @@ //virtual void* registerInPort(const char* name) = 0; virtual void* registerOutPort(const char* /*name*/, bool /*midi*/) = 0; virtual void* registerInPort(const char* /*name*/, bool /*midi*/) = 0; - + + virtual float getDSP_Load() = 0; + + virtual PortType portType(void*) const = 0; + virtual PortDirection portDirection(void*) const = 0; virtual void unregisterPort(void*) = 0; - virtual void connect(void*, void*) = 0; - virtual void disconnect(void*, void*) = 0; + virtual bool connect(void* src, void* dst) = 0; + virtual bool connect(const char* src, const char* dst) = 0; + virtual bool disconnect(void* src, void* dst) = 0; + virtual bool disconnect(const char* src, const char* dst) = 0; virtual int connections(void* /*clientPort*/) = 0; + virtual bool portConnectedTo(void* our_port, const char* port) = 0; + // Returns true if the ports are connected. + virtual bool portsCanDisconnect(void* src, void* dst) const = 0; + // Returns true if the ports are found and they are connected. + virtual bool portsCanDisconnect(const char* src, const char* dst) const = 0; + // Returns true if the ports are not connected and CAN be connected. + virtual bool portsCanConnect(void* src, void* dst) const = 0; + // Returns true if the ports are found and they are not connected and CAN be connected. + virtual bool portsCanConnect(const char* src, const char* dst) const = 0; + // Returns true if the ports CAN be connected. + virtual bool portsCompatible(void* src, void* dst) const = 0; + // Returns true if the ports are found and they CAN be connected. + virtual bool portsCompatible(const char* src, const char* dst) const = 0; virtual void setPortName(void* p, const char* n) = 0; virtual void* findPort(const char* name) = 0; - virtual QString portName(void* port) = 0; + // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. + virtual char* portName(void* port, char* str, int str_size, int preferred_name_or_alias = -1) = 0; + virtual const char* canonicalPortName(void*) = 0; + virtual unsigned int portLatency(void* port, bool capture) const = 0; virtual int getState() = 0; virtual unsigned getCurFrame() const = 0; virtual bool isRealtime() = 0; @@ -86,7 +118,8 @@ virtual void setFreewheel(bool f) = 0; virtual void graphChanged() {} virtual void registrationChanged() {} - virtual int setMaster(bool f) = 0; + virtual void connectionsChanged() {} + virtual int setMaster(bool f) = 0; }; } // namespace MusECore diff -Nru muse-2.1.2/muse/driver/CMakeLists.txt muse-3.0.2+ds1/muse/driver/CMakeLists.txt --- muse-2.1.2/muse/driver/CMakeLists.txt 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/CMakeLists.txt 2018-01-06 20:31:35.000000000 +0000 @@ -32,6 +32,13 @@ jackmidi.cpp rtctimer.cpp ) +if (HAVE_RTAUDIO) + file (GLOB driver_source_files + ${driver_source_files} + rtaudio.cpp + ) +endif (HAVE_RTAUDIO) + ## ## Define target @@ -65,6 +72,7 @@ ${ALSA_LIBRARIES} ${JACK_LIBRARIES} ${QT_LIBRARIES} + ${RTAUDIO_LIBRARIES} mplugins ) diff -Nru muse-2.1.2/muse/driver/dummyaudio.cpp muse-3.0.2+ds1/muse/driver/dummyaudio.cpp --- muse-2.1.2/muse/driver/dummyaudio.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/dummyaudio.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -44,67 +44,56 @@ namespace MusECore { -class MidiPlayEvent; - //--------------------------------------------------------- // DummyAudioDevice //--------------------------------------------------------- -//static const unsigned dummyFrames = 1024; - -enum Cmd { -trSeek, -trStart, -trStop -}; - -struct Msg { - enum Cmd cmd; - int arg; -}; - - class DummyAudioDevice : public AudioDevice { pthread_t dummyThread; - // Changed by Tim. p3.3.15 - //float buffer[1024]; float* buffer; int _realTimePriority; public: - std::list cmdQueue; Audio::State state; int _framePos; + unsigned _framesAtCycleStart; + double _timeAtCycleStart; int playPos; - bool realtimeFlag; bool seekflag; DummyAudioDevice(); virtual ~DummyAudioDevice() { - // Added by Tim. p3.3.15 free(buffer); } - virtual inline int deviceType() const { return DUMMY_AUDIO; } // p3.3.52 + virtual inline int deviceType() const { return DUMMY_AUDIO; } - //virtual void start(); - virtual void start(int); + // Returns true on success. + virtual bool start(int); virtual void stop (); virtual int framePos() const { if(DEBUG_DUMMY) - printf("DummyAudioDevice::framePos %d\n", _framePos); + fprintf(stderr, "DummyAudioDevice::framePos %d\n", _framePos); return _framePos; } + // These are meant to be called from inside process thread only. + virtual unsigned framesAtCycleStart() const { return _framesAtCycleStart; } + virtual unsigned framesSinceCycleStart() const + { + unsigned f = lrint((curTime() - _timeAtCycleStart) * MusEGlobal::sampleRate); + // Safety due to inaccuracies. It cannot be after the segment, right? + if(f >= MusEGlobal::segmentSize) + f = MusEGlobal::segmentSize - 1; + return f; + } + virtual float* getBuffer(void* /*port*/, unsigned long nframes) { - // p3.3.30 - //if (nframes > dummyFrames) { - //printf("error: segment size > 1024\n"); if (nframes > MusEGlobal::segmentSize) { - printf("DummyAudioDevice::getBuffer nframes > segment size\n"); + fprintf(stderr, "DummyAudioDevice::getBuffer nframes > segment size\n"); exit(-1); } @@ -118,30 +107,44 @@ virtual const char* clientName() { return "MusE"; } - //virtual void* registerOutPort(const char*) { virtual void* registerOutPort(const char*, bool) { return (void*)1; } - //virtual void* registerInPort(const char*) { virtual void* registerInPort(const char*, bool) { return (void*)2; } + float getDSP_Load() + { + return 0.0f; + } + virtual AudioDevice::PortType portType(void*) const { return UnknownType; } + virtual AudioDevice::PortDirection portDirection(void*) const { return UnknownDirection; } virtual void unregisterPort(void*) {} - virtual void connect(void*, void*) {} - virtual void disconnect(void*, void*) {} + virtual bool connect(void* /*src*/, void* /*dst*/) { return false; } + virtual bool connect(const char* /*src*/, const char* /*dst*/) { return false; } + virtual bool disconnect(void* /*src*/, void* /*dst*/) { return false; } + virtual bool disconnect(const char* /*src*/, const char* /*dst*/) { return false; } virtual int connections(void* /*clientPort*/) { return 0; } + virtual bool portConnectedTo(void*, const char*) { return false; } + virtual bool portsCanDisconnect(void* /*src*/, void* /*dst*/) const { return false; }; + virtual bool portsCanDisconnect(const char* /*src*/, const char* /*dst*/) const { return false; } + virtual bool portsCanConnect(void* /*src*/, void* /*dst*/) const { return false; } + virtual bool portsCanConnect(const char* /*src*/, const char* /*dst*/) const { return false; } + virtual bool portsCompatible(void* /*src*/, void* /*dst*/) const { return false; } + virtual bool portsCompatible(const char* /*src*/, const char* /*dst*/) const { return false; } virtual void setPortName(void*, const char*) {} virtual void* findPort(const char*) { return 0;} - virtual QString portName(void*) { - return QString("mops"); - } + // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. + virtual char* portName(void*, char* str, int str_size, int /*preferred_name_or_alias*/ = -1) { if(str_size == 0) return 0; str[0] = '\0'; return str; } + virtual const char* canonicalPortName(void*) { return 0; } + virtual unsigned int portLatency(void* /*port*/, bool /*capture*/) const { return 0; } virtual int getState() { // if(DEBUG_DUMMY) -// printf("DummyAudioDevice::getState %d\n", state); +// fprintf(stderr, "DummyAudioDevice::getState %d\n", state); return state; } virtual unsigned getCurFrame() const { if(DEBUG_DUMMY) - printf("DummyAudioDevice::getCurFrame %d\n", _framePos); + fprintf(stderr, "DummyAudioDevice::getCurFrame %d\n", _framePos); return _framePos; } virtual unsigned frameTime() const { @@ -151,30 +154,20 @@ { struct timeval t; gettimeofday(&t, 0); - //printf("%ld %ld\n", t.tv_sec, t.tv_usec); // Note I observed values coming out of order! Causing some problems. + //fprintf(stderr, "%ld %ld\n", t.tv_sec, t.tv_usec); // Note I observed values coming out of order! Causing some problems. return (double)((double)t.tv_sec + (t.tv_usec / 1000000.0)); } - virtual bool isRealtime() { return realtimeFlag; } + virtual bool isRealtime() { return MusEGlobal::realTimeScheduling; } //virtual int realtimePriority() const { return 40; } virtual int realtimePriority() const { return _realTimePriority; } virtual void startTransport() { if(DEBUG_DUMMY) - printf("DummyAudioDevice::startTransport playPos=%d\n", playPos); -#if 0 - Msg trcmd; - trcmd.cmd = trStart; - trcmd.arg = playPos; - cmdQueue.push_front(trcmd); -#else + fprintf(stderr, "DummyAudioDevice::startTransport playPos=%d\n", playPos); state = Audio::PLAY; -#endif -/* state = Audio::START_PLAY; - audio->sync(state, playPos); - state = Audio::PLAY;*/ } virtual void stopTransport() { if(DEBUG_DUMMY) - printf("DummyAudioDevice::stopTransport, playPos=%d\n", playPos); + fprintf(stderr, "DummyAudioDevice::stopTransport, playPos=%d\n", playPos); state = Audio::STOP; } virtual int setMaster(bool) { return 1; } @@ -182,67 +175,49 @@ virtual void seekTransport(const Pos &p) { if(DEBUG_DUMMY) - printf("DummyAudioDevice::seekTransport frame=%d topos=%d\n",playPos, p.frame()); -#if 0 - Msg trcmd; - trcmd.cmd = trSeek; - trcmd.arg = p.frame(); - cmdQueue.push_front(trcmd); - playPos = p.frame(); -#else + fprintf(stderr, "DummyAudioDevice::seekTransport frame=%d topos=%d\n",playPos, p.frame()); seekflag = true; //pos = n; playPos = p.frame(); -#endif } virtual void seekTransport(unsigned pos) { if(DEBUG_DUMMY) - printf("DummyAudioDevice::seekTransport frame=%d topos=%d\n",playPos,pos); -#if 0 - Msg trcmd; - trcmd.cmd = trSeek; - trcmd.arg = pos; - cmdQueue.push_front(trcmd); - playPos = pos; -#else + fprintf(stderr, "DummyAudioDevice::seekTransport frame=%d topos=%d\n",playPos,pos); seekflag = true; //pos = n; playPos = pos; -#endif -/* - Audio::State tempState = state; - state = Audio::START_PLAY; - audio->sync(state, playPos); - state = tempState;*/ } virtual void setFreewheel(bool) {} - void setRealTime() { realtimeFlag = true; } }; DummyAudioDevice* dummyAudio = 0; -DummyAudioDevice::DummyAudioDevice() +DummyAudioDevice::DummyAudioDevice() : AudioDevice() { - // Added by Tim. p3.3.15 - // p3.3.30 - //posix_memalign((void**)&buffer, 16, sizeof(float) * dummyFrames); - - int rv = posix_memalign((void**)&buffer, 16, sizeof(float) * MusEGlobal::config.dummyAudioBufSize); + MusEGlobal::sampleRate = MusEGlobal::config.deviceAudioSampleRate; + MusEGlobal::segmentSize = MusEGlobal::config.deviceAudioBufSize; + int rv = posix_memalign((void**)&buffer, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DummyAudioDevice ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[q] = MusEGlobal::denormalBias; + } + else + memset(buffer, 0, sizeof(float) * MusEGlobal::segmentSize); + dummyThread = 0; - realtimeFlag = false; seekflag = false; state = Audio::STOP; - //startTime = curTime(); _framePos = 0; + _framesAtCycleStart = 0; + _timeAtCycleStart = 0.0; playPos = 0; - cmdQueue.clear(); } @@ -306,203 +281,45 @@ static void* dummyLoop(void* ptr) { - //unsigned int tickRate = 25; - - // p3.3.30 - //MusEGlobal::sampleRate = 25600; - MusEGlobal::sampleRate = MusEGlobal::config.dummyAudioSampleRate; - //MusEGlobal::segmentSize = dummyFrames; - MusEGlobal::segmentSize = MusEGlobal::config.dummyAudioBufSize; -#if 0 - //unsigned int tickRate = MusEGlobal::sampleRate / dummyFrames; - unsigned int tickRate = MusEGlobal::sampleRate / MusEGlobal::segmentSize; - - AlsaTimer timer; - fprintf(stderr, "Get alsa timer for dummy driver:\n"); - timer.setFindBestTimer(false); - int fd = timer.initTimer(); - if (fd==-1) { - // QMessageBox::critical( 0, /*tr*/(QString("Failed to start timer for dummy audio driver!")), - // /*tr*/(QString("No functional timer was available.\n" - // "Alsa timer not available, check if module snd_timer is available and /dev/snd/timer is available"))); - fprintf(stderr, "Failed to start timer for dummy audio driver! No functional timer was available.\n" - "Alsa timer not available, check if module snd_timer is available and /dev/snd/timer is available\n"); - pthread_exit(0); - } - - /* Depending on nature of the timer, the requested tickRate might not - * be available. The return value is the nearest available frequency, - * so use this to reset our dummpy MusEGlobal::sampleRate to keep everything - * consistent. - */ - tickRate = timer.setTimerFreq( /*250*/ tickRate ); - - // p3.3.31 - // If it didn't work, get the actual rate. - if(tickRate == 0) - tickRate = timer.getTimerFreq(); - - MusEGlobal::sampleRate = tickRate * MusEGlobal::segmentSize; - timer.startTimer(); -#endif DummyAudioDevice *drvPtr = (DummyAudioDevice *)ptr; - - ///pollfd myPollFd; - - ///myPollFd.fd = fd; - ///myPollFd.events = POLLIN; - - - /* - MusEGlobal::doSetuid(); - struct sched_param rt_param; - int rv; - memset(&rt_param, 0, sizeof(sched_param)); - int type; - rv = pthread_getschedparam(pthread_self(), &type, &rt_param); - if (rv != 0) - perror("get scheduler parameter"); - if (type != SCHED_FIFO) { - fprintf(stderr, "Driver thread not running SCHED_FIFO, trying to set...\n"); - - memset(&rt_param, 0, sizeof(sched_param)); - //rt_param.sched_priority = 1; - rt_param.sched_priority = realtimePriority(); - rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, &rt_param); - if (rv != 0) - perror("set realtime scheduler"); - memset(&rt_param, 0, sizeof(sched_param)); - rv = pthread_getschedparam(pthread_self(), &type, &rt_param); - if (rv != 0) - perror("get scheduler parameter"); - if (type == SCHED_FIFO) { - drvPtr->setRealTime(); - fprintf(stderr, "Thread succesfully set to SCHED_FIFO\n"); - } - else { - fprintf(stderr, "Unable to set thread to SCHED_FIFO\n"); - } - } - MusEGlobal::undoSetuid(); - */ - -#ifndef __APPLE__ - MusEGlobal::doSetuid(); - //if (realTimePriority) { - if (MusEGlobal::realTimeScheduling) { - // - // check if we really got realtime priviledges - // - int policy; - if ((policy = sched_getscheduler (0)) < 0) { - printf("cannot get current client scheduler for audio dummy thread: %s!\n", strerror(errno)); - } - else - { - if (policy != SCHED_FIFO) - printf("audio dummy thread _NOT_ running SCHED_FIFO\n"); - else if (MusEGlobal::debugMsg) { - struct sched_param rt_param; - memset(&rt_param, 0, sizeof(sched_param)); - int type; - int rv = pthread_getschedparam(pthread_self(), &type, &rt_param); - if (rv == -1) - perror("get scheduler parameter"); - printf("audio dummy thread running SCHED_FIFO priority %d\n", - rt_param.sched_priority); - } - } - } - MusEGlobal::undoSetuid(); -#endif -#if 0 - /* unsigned long tick = 0;*/ // prevent compiler warning: unused variable - for (;;) { - int _pollWait = 10; // ms - unsigned long count = 0; - while (count < 1 /*250/tickRate*/) // will loop until the next tick occurs - { - /*int n = */ poll(&myPollFd, 1 /* npfd */, _pollWait); - count += timer.getTimerTicks(); - // FIXME FIXME: There is a crash here (or near-lockup, a race condition?) while zipping - // the cursor around in an editor (pianoroll, master edit) while arranger is open. - while (drvPtr->cmdQueue.size()) - { - Msg &msg = drvPtr->cmdQueue.back(); - drvPtr->cmdQueue.pop_back(); - switch(msg.cmd) { - case trSeek: - { - //printf("trSeek\n"); - drvPtr->playPos = msg.arg; - Audio::State tempState = drvPtr->state; - drvPtr->state = Audio::START_PLAY; - audio->sync(drvPtr->state, msg.arg); - drvPtr->state = tempState; - } - break; - case trStart: - { - //printf("trStart\n"); - drvPtr->state = Audio::START_PLAY; - audio->sync(drvPtr->state, msg.arg); - drvPtr->state = Audio::PLAY; - } - break; - case trStop: - break; - default: - printf("dummyLoop: Unknown command!\n"); - } - } - } - audio->process(MusEGlobal::segmentSize); - int increment = MusEGlobal::segmentSize; // 1 //tickRate / MusEGlobal::sampleRate * MusEGlobal::segmentSize; - drvPtr->_framePos+=increment; - if (drvPtr->state == Audio::PLAY) - { - drvPtr->playPos+=increment; - } - } -#else // Adapted from muse_qt4_evolution. p4.0.20 for(;;) { - //if(audioState == AUDIO_RUNNING) - if(MusEGlobal::audio->isRunning()) - //MusEGlobal::audio->process(MusEGlobal::segmentSize, drvPtr->state); - MusEGlobal::audio->process(MusEGlobal::segmentSize); - //else if (audioState == AUDIO_START1) - // audioState = AUDIO_START2; - //usleep(dummyFrames*1000000/AL::sampleRate); - usleep(MusEGlobal::segmentSize*1000000/MusEGlobal::sampleRate); - //if(dummyAudio->seekflag) - if(drvPtr->seekflag) - { - //MusEGlobal::audio->sync(Audio::STOP, dummyAudio->pos); - //MusEGlobal::audio->sync(drvPtr->state, drvPtr->playPos); - MusEGlobal::audio->sync(Audio::STOP, drvPtr->playPos); - - //dummyAudio->seekflag = false; - drvPtr->seekflag = false; - } - - //if(dummyAudio->state == Audio::PLAY) - // dummyAudio->pos += dummyFrames; - drvPtr->_framePos += MusEGlobal::segmentSize; - if(drvPtr->state == Audio::PLAY) - drvPtr->playPos += MusEGlobal::segmentSize; + drvPtr->_timeAtCycleStart = curTime(); + + if(MusEGlobal::audio->isRunning()) { + + MusEGlobal::audio->process(MusEGlobal::segmentSize); + } + + usleep(MusEGlobal::segmentSize*1000000/MusEGlobal::sampleRate); + + if(drvPtr->seekflag) { + + MusEGlobal::audio->sync(Audio::STOP, drvPtr->playPos); + + drvPtr->seekflag = false; + } + + drvPtr->_framePos += MusEGlobal::segmentSize; + drvPtr->_framesAtCycleStart += MusEGlobal::segmentSize; + + if(drvPtr->state == Audio::PLAY) { + + drvPtr->playPos += MusEGlobal::segmentSize; + } } -#endif - - ///timer.stopTimer(); pthread_exit(0); } -//void DummyAudioDevice::start() -void DummyAudioDevice::start(int priority) +//--------------------------------------------------------- +// dummyLoop +// Returns true on success. +//--------------------------------------------------------- + +bool DummyAudioDevice::start(int priority) { _realTimePriority = priority; pthread_attr_t* attributes = 0; @@ -512,21 +329,21 @@ pthread_attr_init(attributes); if (pthread_attr_setschedpolicy(attributes, SCHED_FIFO)) { - printf("cannot set FIFO scheduling class for dummy RT thread\n"); + fprintf(stderr, "cannot set FIFO scheduling class for dummy RT thread\n"); } if (pthread_attr_setscope (attributes, PTHREAD_SCOPE_SYSTEM)) { - printf("Cannot set scheduling scope for dummy RT thread\n"); + fprintf(stderr, "Cannot set scheduling scope for dummy RT thread\n"); } // p4.0.16 Dummy was not running FIFO because this is needed. if (pthread_attr_setinheritsched(attributes, PTHREAD_EXPLICIT_SCHED)) { - printf("Cannot set setinheritsched for dummy RT thread\n"); + fprintf(stderr, "Cannot set setinheritsched for dummy RT thread\n"); } struct sched_param rt_param; memset(&rt_param, 0, sizeof(rt_param)); rt_param.sched_priority = priority; if (pthread_attr_setschedparam (attributes, &rt_param)) { - printf("Cannot set scheduling priority %d for dummy RT thread (%s)\n", + fprintf(stderr, "Cannot set scheduling priority %d for dummy RT thread (%s)\n", priority, strerror(errno)); } } @@ -534,10 +351,6 @@ int rv = pthread_create(&dummyThread, attributes, dummyLoop, this); if(rv) { - // p4.0.16: MusEGlobal::realTimeScheduling is unreliable. It is true even in some clearly non-RT cases. - // I cannot seem to find a reliable answer to the question of "are we RT or not". - // MusE was failing with a stock kernel because of PTHREAD_EXPLICIT_SCHED. - // So we'll just have to try again without attributes. if (MusEGlobal::realTimeScheduling && _realTimePriority > 0) rv = pthread_create(&dummyThread, NULL, dummyLoop, this); } @@ -545,11 +358,13 @@ if(rv) fprintf(stderr, "creating dummy audio thread failed: %s\n", strerror(rv)); - if (attributes) // p4.0.16 + if (attributes) { pthread_attr_destroy(attributes); free(attributes); } + + return true; } void DummyAudioDevice::stop () @@ -559,4 +374,5 @@ dummyThread = 0; } + } // namespace MusECore diff -Nru muse-2.1.2/muse/driver/jackaudio.h muse-3.0.2+ds1/muse/driver/jackaudio.h --- muse-2.1.2/muse/driver/jackaudio.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/jackaudio.h 2018-01-06 20:31:35.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: jackaudio.h,v 1.20.2.4 2009/12/20 05:00:35 terminator356 Exp $ // (C) Copyright 2002 Werner Schweer (ws@seh.de) +// (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge.net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,24 +25,36 @@ #define __JACKAUDIO_H__ #include +#include #include "audiodev.h" +#include "operations.h" + +class QString; namespace MusEGlobal { bool checkAudioDevice(); } namespace MusECore { -class MidiPlayEvent; + +enum JackCallbackEventType {PortRegister, PortUnregister, PortConnect, PortDisconnect, GraphChanged}; +struct JackCallbackEvent +{ + JackCallbackEventType type; + jack_port_id_t port_id_A; + jack_port_id_t port_id_B; + jack_port_t* port_A; + jack_port_t* port_B; +}; +typedef std::list JackCallbackEventList; +typedef std::list::iterator iJackCallbackEvent; //--------------------------------------------------------- // JackAudioDevice //--------------------------------------------------------- class JackAudioDevice : public AudioDevice { - jack_client_t* _client; - //double sampleTime; - //int samplePos; float _syncTimeout; jack_transport_state_t transportState; jack_position_t pos; @@ -52,70 +65,114 @@ volatile int _dummyPosPending; // Free-running frame counter incremented always in process. jack_nframes_t _frameCounter; + + PendingOperationList operations; + // Temporary, for processing callback event FIFO. + JackCallbackEventList jackCallbackEvents; void getJackPorts(const char** ports, std::list& name_list, bool midi, bool physical, int aliases); static int processAudio(jack_nframes_t frames, void*); + + void processGraphChanges(); + void processJackCallbackEvents(const Route& our_node, jack_port_t* our_port, RouteList* route_list, bool is_input); + void checkNewRouteConnections(jack_port_t* our_port, int channel, RouteList* route_list); + // Return 0: Neither disconnect nor unregister found + // 1: Disconnect found followed later by unregister + // 2: Disconnect found (with no unregister later) + int checkDisconnectCallback(const jack_port_t* our_port, const jack_port_t* port); + // Return 0: No port register found (or it was cancelled by a later unregister) + // 1: Port register was found. + int checkPortRegisterCallback(const jack_port_t* port); + + static int static_JackXRunCallback(void *); + public: JackAudioDevice(jack_client_t* cl, char * jack_id_string); virtual ~JackAudioDevice(); - virtual void nullify_client() { _client = 0; } - virtual inline int deviceType() const { return JACK_AUDIO; } - void scanMidiPorts(); - - //virtual void start(); - virtual void start(int); + virtual bool start(int); virtual void stop (); - virtual int framePos() const; - virtual unsigned frameTime() const { return _frameCounter; } - virtual double systemTime() const; - - virtual float* getBuffer(void* port, unsigned long nframes) { - return (float*)jack_port_get_buffer((jack_port_t*)port, nframes); - } - - virtual std::list outputPorts(bool midi = false, int aliases = -1); - virtual std::list inputPorts(bool midi = false, int aliases = -1); + // These are meant to be called from inside process thread only. + virtual unsigned framesAtCycleStart() const; + virtual unsigned framesSinceCycleStart() const; jack_client_t* jackClient() const { return _client; } virtual void registerClient(); virtual const char* clientName() { return jackRegisteredName; } + virtual void nullify_client() { _client = 0; } + float getDSP_Load(); + virtual std::list outputPorts(bool midi = false, int aliases = -1); + virtual std::list inputPorts(bool midi = false, int aliases = -1); virtual void* registerOutPort(const char* /*name*/, bool /*midi*/); virtual void* registerInPort(const char* /*name*/, bool /*midi*/); - - //virtual char* getJackName(); - virtual void unregisterPort(void*); - virtual void connect(void*, void*); - virtual void disconnect(void*, void*); + virtual AudioDevice::PortType portType(void*) const; + virtual AudioDevice::PortDirection portDirection(void*) const; + virtual bool connect(void* src, void* dst); + virtual bool connect(const char* src, const char* dst); + virtual bool disconnect(void* src, void* dst); + virtual bool disconnect(const char* src, const char* dst); virtual int connections(void* clientPort) { return jack_port_connected((jack_port_t*)clientPort); } - virtual void setPortName(void* p, const char* n) { jack_port_set_name((jack_port_t*)p, n); } + virtual bool portConnectedTo(void* our_port, const char* port) { return jack_port_connected_to((jack_port_t*)our_port, port); } + // Returns true if the ports are connected. + virtual bool portsCanDisconnect(void* src, void* dst) const; + // Returns true if the ports are found and they are connected. + virtual bool portsCanDisconnect(const char* src, const char* dst) const; + // Returns true if the ports are not connected and CAN be connected. + virtual bool portsCanConnect(void* src, void* dst) const; + // Returns true if the ports are found and they are not connected and CAN be connected. + virtual bool portsCanConnect(const char* src, const char* dst) const; + // Returns true if the ports CAN be connected. + virtual bool portsCompatible(void* src, void* dst) const; + // Returns true if the ports are found and they CAN be connected. + virtual bool portsCompatible(const char* src, const char* dst) const; + virtual void setPortName(void* p, const char* n); + // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. + virtual char* portName(void* port, char* str, int str_size, int preferred_name_or_alias = -1); + virtual const char* canonicalPortName(void* port) { if(!port) return NULL; return jack_port_name((jack_port_t*)port); } virtual void* findPort(const char* name); - virtual QString portName(void* port); + virtual unsigned int portLatency(void* port, bool capture) const; + virtual float* getBuffer(void* port, unsigned long nframes) { + return (float*)jack_port_get_buffer((jack_port_t*)port, nframes); + } + virtual int getState(); virtual unsigned int getCurFrame() const; + virtual int framePos() const; + virtual unsigned frameTime() const { return _frameCounter; } + virtual double systemTime() const; virtual bool isRealtime() { return jack_is_realtime(_client); } + // Jack does this for us. + virtual bool setRealTime() { return false; } virtual int realtimePriority() const; + virtual void startTransport(); virtual void stopTransport(); virtual void seekTransport(unsigned frame); virtual void seekTransport(const Pos &p); virtual void setFreewheel(bool f); + virtual int setMaster(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(); - - virtual int setMaster(bool f); - //static bool jackStarted; + void graphChanged(); }; +// Our own wrappers for functions we need to look up with dlsym: +typedef void(*jack_get_version_type)(int*, int*, int*, int*); +extern jack_get_version_type jack_get_version_fp; + +typedef int(*jack_port_set_name_type)(jack_port_t*, const char*); +extern jack_port_set_name_type jack_port_set_name_fp; + +typedef int(*jack_port_rename_type)(jack_client_t*, jack_port_t*, const char*); +extern jack_port_rename_type jack_port_rename_fp; + + } // namespace MusECore #endif diff -Nru muse-2.1.2/muse/driver/jack.cpp muse-3.0.2+ds1/muse/driver/jack.cpp --- muse-2.1.2/muse/driver/jack.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/jack.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // $Id: jack.cpp,v 1.30.2.17 2009/12/20 05:00:35 terminator356 Exp $ // (C) Copyright 2002 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on sourceforge.net) +// (C) Copyright 2012-2015 Tim E. Real (terminator356 on sourceforge.net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,17 +22,22 @@ //========================================================= #include "config.h" -#include -#include +#include #include #include #include #include -//#include #include #include #include +#include +#include +#include + +#include + +#include "libs/strntcpy.h" #include "audio.h" #include "globals.h" #include "song.h" @@ -43,17 +48,21 @@ #include "sync.h" #include "utils.h" #include "gconfig.h" +#include "route.h" #include "midi.h" #include "mididev.h" #include "mpevent.h" #include "jackmidi.h" - +#include "muse_atomic.h" #define JACK_DEBUG 0 -//#include "errorhandler.h" +#define JACK_CALLBACK_FIFO_SIZE 512 + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); #ifdef VST_SUPPORT #include @@ -82,10 +91,94 @@ } // namespace MusEGlobal + namespace MusECore { JackAudioDevice* jackAudio; +int jack_ver_maj = 0, jack_ver_min = 0, jack_ver_micro = 0, jack_ver_proto = 0; +muse_atomic_t atomicGraphChangedPending; +bool jack1_port_by_name_workaround = false; + +// Function pointers obtained with dlsym: +jack_get_version_type jack_get_version_fp = NULL; +jack_port_set_name_type jack_port_set_name_fp = NULL; +jack_port_rename_type jack_port_rename_fp = NULL; + + +//--------------------------------------------------------- +// JackCallbackFifo +//--------------------------------------------------------- + +class JackCallbackFifo +{ + JackCallbackEvent fifo[JACK_CALLBACK_FIFO_SIZE]; + volatile int size; + int wIndex; + int rIndex; + + public: + JackCallbackFifo() { clear(); } + bool put(const JackCallbackEvent& event); // returns true on fifo overflow + const JackCallbackEvent& get(); + const JackCallbackEvent& peek(int n = 0); + void remove(); + bool isEmpty() const { return size == 0; } + void clear() { size = 0, wIndex = 0, rIndex = 0; } + int getSize() const { return size; } +}; + +//--------------------------------------------------------- +// JackCallbackFifo +// put +// return true on fifo overflow +//--------------------------------------------------------- + +bool JackCallbackFifo::put(const JackCallbackEvent& event) + { + if (size < JACK_CALLBACK_FIFO_SIZE) { + fifo[wIndex] = event; + wIndex = (wIndex + 1) % JACK_CALLBACK_FIFO_SIZE; + ++size; + return false; + } + return true; + } + +//--------------------------------------------------------- +// get +//--------------------------------------------------------- + +const JackCallbackEvent& JackCallbackFifo::get() + { + const JackCallbackEvent& event = fifo[rIndex]; + rIndex = (rIndex + 1) % JACK_CALLBACK_FIFO_SIZE; + --size; + return event; + } + +//--------------------------------------------------------- +// peek +//--------------------------------------------------------- + +const JackCallbackEvent& JackCallbackFifo::peek(int n) + { + int idx = (rIndex + n) % JACK_CALLBACK_FIFO_SIZE; + return fifo[idx]; + } + +//--------------------------------------------------------- +// remove +//--------------------------------------------------------- + +void JackCallbackFifo::remove() + { + rIndex = (rIndex + 1) % JACK_CALLBACK_FIFO_SIZE; + --size; + } + +JackCallbackFifo jackCallbackFifo; + //--------------------------------------------------------- // checkJackClient - make sure client is valid //--------------------------------------------------------- @@ -116,10 +209,10 @@ int JackAudioDevice::processAudio(jack_nframes_t frames, void*) { - int state_pending = jackAudio->_dummyStatePending; // Snapshots. - int pos_pending = jackAudio->_dummyPosPending; // - jackAudio->_dummyStatePending = -1; // Reset. - jackAudio->_dummyPosPending = -1; // + const int state_pending = jackAudio->_dummyStatePending; // Snapshots. + const int pos_pending = jackAudio->_dummyPosPending; // + jackAudio->_dummyStatePending = -1; // Reset. + jackAudio->_dummyPosPending = -1; // jackAudio->_frameCounter += frames; MusEGlobal::segmentSize = frames; @@ -216,7 +309,7 @@ return 1; int audioState = Audio::STOP; - switch (state) { + switch (int(state)) { case JackTransportStopped: audioState = Audio::STOP; break; @@ -233,7 +326,7 @@ // FIXME: Quick and dirty hack to support both Jack-1 and Jack-2 // Really need a config check of version... case 4: - //printf("processSync JackTransportNetStarting\n"); + //fprintf(stderr, "processSync JackTransportNetStarting\n"); audioState = Audio::START_PLAY; break; @@ -332,7 +425,6 @@ static void jackError(const char *s) { - //error->logError( "JACK ERROR: %s\n", s); fprintf(stderr,"JACK ERROR: %s\n", s); } @@ -342,23 +434,41 @@ static void noJackError(const char* /* s */) { - //printf("noJackError()\n"); + //fprintf(stderr, "noJackError()\n"); } + +//--------------------------------------------------------- +// jackInfo +//--------------------------------------------------------- + +static void jackInfo(const char* s) +{ + fprintf(stderr, "JACK INFO: %s\n", s); +} + +//--------------------------------------------------------- +// noJackInfo +//--------------------------------------------------------- +static void noJackInfo(const char* /*s*/) +{ + //fprintf(stderr, "noJackInfo()\n"); +} + //--------------------------------------------------------- // JackAudioDevice //--------------------------------------------------------- JackAudioDevice::JackAudioDevice(jack_client_t* cl, char* name) : AudioDevice() - { +{ _frameCounter = 0; //JackAudioDevice::jackStarted=false; strcpy(jackRegisteredName, name); _client = cl; dummyState = Audio::STOP; dummyPos = 0; - } +} //--------------------------------------------------------- // ~JackAudioDevice @@ -385,22 +495,28 @@ //--------------------------------------------------------- int JackAudioDevice::realtimePriority() const - { - pthread_t t = jack_client_thread_id(_client); - int policy; - struct sched_param param; - memset(¶m, 0, sizeof(param)); - int rv = pthread_getschedparam(t, &policy, ¶m); - if (rv) { - perror("MusE: JackAudioDevice::realtimePriority: Error: Get jack schedule parameter"); - return 0; - } - if (policy != SCHED_FIFO) { - printf("MusE: JackAudioDevice::realtimePriority: JACK is not running realtime\n"); - return 0; - } - return param.sched_priority; - } +{ + if(!_client) + return 0; + + pthread_t t = jack_client_thread_id(_client); + if(t == 0) + return jack_client_real_time_priority(_client); + + int policy; + struct sched_param param; + memset(¶m, 0, sizeof(param)); + int rv = pthread_getschedparam(t, &policy, ¶m); + if (rv) { + perror("MusE: JackAudioDevice::realtimePriority: Error: Get jack schedule parameter"); + return 0; + } + if (policy != SCHED_FIFO) { + printf("MusE: JackAudioDevice::realtimePriority: JACK is not running realtime\n"); + return 0; + } + return param.sched_priority; +} //--------------------------------------------------------- // initJackAudio @@ -411,27 +527,49 @@ { if (JACK_DEBUG) printf("initJackAudio()\n"); + + muse_atomic_init(&atomicGraphChangedPending); + muse_atomic_set(&atomicGraphChangedPending, 0); + + jack_get_version_fp = reinterpret_cast(dlsym(RTLD_DEFAULT, "jack_get_version")); + DEBUG_PRST_ROUTES(stderr, "initJackAudio jack_get_version() address:%p \n", jack_get_version_fp); + if(jack_get_version_fp) // ATM Only in Jack-2. Dlsym'd. Check for existence first. + { + jack_get_version_fp(&jack_ver_maj, &jack_ver_min, &jack_ver_micro, &jack_ver_proto); + DEBUG_PRST_ROUTES(stderr, "initJackAudio: jack_ver_maj:%d jack_ver_min:%d jack_ver_micro:%d jack_ver_proto:%d\n", + jack_ver_maj, jack_ver_min, jack_ver_micro, jack_ver_proto); + // FIXME: ATM Jack-2 jack_get_version() returns all zeros. When it is fixed, do something with the values. + if(jack_ver_maj == 0 && jack_ver_min == 0 && jack_ver_micro == 0 && jack_ver_proto == 0) + { + fprintf(stderr, "MusE:initJackAudio: jack_get_version() returned zeros. Setting version major to 1.\n"); + jack_ver_maj = 1; + } + } + + jack_port_set_name_fp = reinterpret_cast(dlsym(RTLD_DEFAULT, "jack_port_set_name")); + DEBUG_PRST_ROUTES(stderr, "initJackAudio jack_port_set_name() address:%p \n", jack_port_set_name_fp); + if(jack_port_set_name_fp) + { + } + + jack_port_rename_fp = reinterpret_cast(dlsym(RTLD_DEFAULT, "jack_port_rename")); + DEBUG_PRST_ROUTES(stderr, "initJackAudio jack_port_rename() address:%p \n", jack_port_rename_fp); + if(jack_port_rename_fp) + { + } + if (MusEGlobal::debugMsg) { - fprintf(stderr,"initJackAudio()\n"); + fprintf(stderr, "initJackAudio(): registering error and info callbacks...\n"); jack_set_error_function(jackError); + jack_set_info_function(jackInfo); } - else + else { jack_set_error_function(noJackError); + jack_set_info_function(noJackInfo); + } + MusEGlobal::doSetuid(); - //jack_client_t* client = 0; - //int i = 0; - //char jackIdString[8]; - //for (i = 0; i < 5; ++i) { - // sprintf(jackIdString, "MusE-%d", i+1); - //client = jack_client_new(jackIdString); - // client = jack_client_open(jackIdString, JackNoStartServer, 0); - // if (client) - // break; - // } - //if (i == 5) - // return true; - int opts = JackNullOption; if(MusEGlobal::noAutoStartJack) opts |= JackNoStartServer; @@ -455,8 +593,41 @@ if (MusEGlobal::debugMsg) fprintf(stderr, "initJackAudio(): client %s opened.\n", jack_get_client_name(client)); - //jack_set_error_function(jackError); - //jackAudio = new JackAudioDevice(client, jackIdString); + + // Check if Jack-1 jack_port_by_name() workaround is required: + if(jack_ver_maj == 0) + { + sleep(1); + jack_port_t* p = jack_port_register(client, "jack1_test_port", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + if(p) + { + sleep(1); + int sz = jack_port_name_size(); + char s[sz]; + strcpy(s, jack_get_client_name(client)); + strcat(s, ":jack1_test_port"); + jack_port_t* sp = jack_port_by_name(client, s); + if(sp) + { + if(p != sp) + { + fprintf(stderr, "initJackAudio(): Enabling Jack-1 jack_port_by_name() workaround\n"); + jack1_port_by_name_workaround = true; + } + } + else + fprintf(stderr, "initJackAudio(): Jack-1 jack_port_by_name() workaround: Error on jack_port_by_name(): port not found\n"); + + if(jack_port_unregister(client, p)) + fprintf(stderr, "initJackAudio(): Jack-1 jack_port_by_name() workaround: Error on jack_port_unregister()\n"); + else + sleep(1); + } + else + fprintf(stderr, "initJackAudio(): Jack-1 jack_port_by_name() workaround: Error on jack_port_register()\n"); + + } + jackAudio = new JackAudioDevice(client, jack_get_client_name(client)); if (MusEGlobal::debugMsg) fprintf(stderr, "initJackAudio(): registering client...\n"); @@ -471,8 +642,6 @@ MusEGlobal::sampleRate = jack_get_sample_rate(client); MusEGlobal::segmentSize = jack_get_buffer_size(client); - jackAudio->scanMidiPorts(); - return false; } @@ -504,78 +673,41 @@ // registration_callback //--------------------------------------------------------- -static void registration_callback(jack_port_id_t, int, void*) +static void registration_callback(jack_port_id_t port_id, int is_register, void*) { if(MusEGlobal::debugMsg || JACK_DEBUG) - printf("JACK: registration changed\n"); - - MusEGlobal::audio->sendMsgToGui('R'); -} + printf("JACK: registration_callback\n"); -//--------------------------------------------------------- -// JackAudioDevice::registrationChanged -// this is called from song in gui context triggered -// by registration_callback() -//--------------------------------------------------------- - -void JackAudioDevice::registrationChanged() -{ - if(JACK_DEBUG) - printf("JackAudioDevice::registrationChanged()\n"); - - // Rescan. - scanMidiPorts(); - // Connect the Jack midi client ports to the device ports. - //connectJackMidiPorts(); -} + DEBUG_PRST_ROUTES(stderr, "JACK: registration_callback: port_id:%d is_register:%d\n", port_id, is_register); -//--------------------------------------------------------- -// JackAudioDevice::connectJackMidiPorts -//--------------------------------------------------------- - -void JackAudioDevice::connectJackMidiPorts() -{ - if(JACK_DEBUG) - printf("JackAudioDevice::connectJackMidiPorts()\n"); - - for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + // With Jack-1 do not use functions like jack_port_by_name and jack_port_by_id here. + // With registration the port has not been added yet, so they allocate a new + // 'external' port which is NOT the same as the port returned by jack_port_register ! + // Thereafter each call to those functions returns THAT allocated port NOT the jack_port_register one. + // [ This was a bug in Jack1 due to a missing section. A fix by Tim was submitted late 2014 and was pending. ] + JackCallbackEvent ev; + ev.type = is_register ? PortRegister : PortUnregister; + ev.port_id_A = port_id; + + jackCallbackFifo.put(ev); + + // NOTE: Jack-1 does not issue a graph order callback after a registration call. + // Jack-1 callbacks: [ port connect -> graph order -> registration ] {...} + // Jack-2 callbacks: [ port connect {...} -> registration {...} -> graph order ] {...} + if(jack_ver_maj != 1) { - //MidiJackDevice* mjd = dynamic_cast(*i); - //if(!mjd) - MidiDevice* md = *i; - if(md->deviceType() != MidiDevice::JACK_MIDI) - continue; - - //void* port = md->clientPort(); - if(md->rwFlags() & 1) + // Add a GraphChanged event. + JackCallbackEvent ev; + ev.type = GraphChanged; + jackCallbackFifo.put(ev); + // we cannot call JackAudioDevice::graphChanged() from this + // context, so we send a message to the gui thread which in turn + // calls graphChanged() + if(muse_atomic_read(&atomicGraphChangedPending) == 0) { - void* port = md->outClientPort(); - if(port) - { - RouteList* rl = md->outRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - if(r->type != Route::JACK_ROUTE) - continue; - connect(port, r->jackPort); - } - } + muse_atomic_set(&atomicGraphChangedPending, 1); + MusEGlobal::audio->sendMsgToGui('C'); } - - if(md->rwFlags() & 2) - { - void* port = md->inClientPort(); - if(port) - { - RouteList* rl = md->inRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - if(r->type != Route::JACK_ROUTE) - continue; - connect(r->jackPort, port); - } - } - } } } @@ -587,22 +719,39 @@ { if (MusEGlobal::debugMsg || JACK_DEBUG) printf("JACK: client registration changed:%s register:%d\n", name, isRegister); + DEBUG_PRST_ROUTES(stderr, "JACK: client registration changed:%s register:%d\n", name, isRegister); } //--------------------------------------------------------- // port_connect_callback //--------------------------------------------------------- -static void port_connect_callback(jack_port_id_t a, jack_port_id_t b, int isConnect, void*) - { - if (MusEGlobal::debugMsg || JACK_DEBUG) - { - //jack_port_t* ap = jack_port_by_id(_client, a); - //jack_port_t* bp = jack_port_by_id(_client, b); - //printf("JACK: port connections changed: A:%d:%s B:%d:%s isConnect:%d\n", a, jack_port_name(ap), b, jack_port_name(bp), isConnect); - printf("JACK: port connections changed: A:%d B:%d isConnect:%d\n", a, b, isConnect); - } - } +static void port_connect_callback(jack_port_id_t a, jack_port_id_t b, int isConnect, void* arg) +{ + if (MusEGlobal::debugMsg || JACK_DEBUG) + printf("JACK: port connections changed: A:%d B:%d isConnect:%d\n", a, b, isConnect); + + DEBUG_PRST_ROUTES(stderr, "JACK: port_connect_callback id a:%d id b:%d isConnect:%d\n", a, b, isConnect); + + JackCallbackEvent ev; + ev.type = isConnect ? PortConnect : PortDisconnect; + ev.port_id_A = a; + ev.port_id_B = b; + JackAudioDevice* jad = (JackAudioDevice*)arg; + jack_client_t* client = jad->jackClient(); + if(client) + { + ev.port_A = jack_port_by_id(client, a); + ev.port_B = jack_port_by_id(client, b); + } + else + { + ev.port_A = NULL; + ev.port_B = NULL; + } + + jackCallbackFifo.put(ev); +} //--------------------------------------------------------- // graph_callback @@ -612,17 +761,170 @@ static int graph_callback(void*) { - if (JACK_DEBUG) + if (MusEGlobal::debugMsg || JACK_DEBUG) printf("graph_callback()\n"); + + DEBUG_PRST_ROUTES(stderr, "JACK: graph_callback\n"); + + // Add a GraphChanged event. + JackCallbackEvent ev; + ev.type = GraphChanged; + jackCallbackFifo.put(ev); + // we cannot call JackAudioDevice::graphChanged() from this // context, so we send a message to the gui thread which in turn // calls graphChanged() - MusEGlobal::audio->sendMsgToGui('C'); - if (MusEGlobal::debugMsg) - printf("JACK: graph changed\n"); + if(muse_atomic_read(&atomicGraphChangedPending) == 0) + { + muse_atomic_set(&atomicGraphChangedPending, 1); + MusEGlobal::audio->sendMsgToGui('C'); + } return 0; } +void JackAudioDevice::processJackCallbackEvents(const Route& our_node, jack_port_t* our_port, + RouteList* route_list, bool is_input) +{ + jack_client_t* client = jackClient(); + if(!client) + return; + + jack_port_t* our_ext_port = our_port; + const char* our_port_name = our_port ? jack_port_name(our_port) : 0; + + if(our_port && our_port_name && jack1_port_by_name_workaround) + { + jack_port_t* jp = jack_port_by_name(client, our_port_name); + if(jp && jp != our_port) + { + DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::processJackCallbackEvents: changing audio input port!: channel:%d our_port:%p new port:%p\n", + our_node.channel, our_port, jp); + our_ext_port = jp; + } + } + + for(iRoute ir = route_list->begin(); ir != route_list->end(); ++ir) + { + // Check correct route type, and channel if required. + if((ir->type != Route::JACK_ROUTE) || (our_node.channel != -1 && ir->channel != our_node.channel)) + continue; + const char* route_jpname = ir->persistentJackPortName; + // FIXME: TODO: Maybe switch to get_ports + jack_port_t* jp = jack_port_by_name(client, route_jpname); + if(jp) + { + // TODO: For Jack-2 maybe alter this? Before calling jack_port_connected_to(), maybe first check if the IDs + // (hence jack ports) passed in the connect callback match here, to avoid calling jack_port_connected_to() ? + if(our_port && jack_port_connected_to(our_port, route_jpname)) + { + // The ports are connected. Keep the route node but update its jack port pointer if necessary. + const char* s = NULL; + if(jp != ir->jackPort) + { + DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports connected. Modifying route: our_port:%p old_route_jp:%p new_route_jp:%p route_persistent_name:%s\n", + our_port, ir->jackPort, jp, route_jpname); + s = route_jpname; + } + // Find a more appropriate name if necessary. + char fin_name[ROUTE_PERSISTENT_NAME_SIZE]; + portName(jp, fin_name, ROUTE_PERSISTENT_NAME_SIZE); + if(strcmp(ir->persistentJackPortName, fin_name) != 0) + { + DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports connected. Modifying route name: route_persistent_name:%s new name:%s\n", route_jpname, fin_name); + s = fin_name; + } + + if(s) + operations.add(PendingOperationItem(Route(Route::JACK_ROUTE, 0, jp, ir->channel, 0, 0, s), &(*ir), PendingOperationItem::ModifyRouteNode)); + } + else + { + if(ir->jackPort) + { + // Check whether the disconnect happened BEFORE this graphChanged() was called, + // or just now during it, or was followed by an unregister. + // Support our port == null (midi device not assigned to a midi port or I/O disabled etc.): + // If our port is null, treat this as an unregister... + const int ret = our_ext_port ? checkDisconnectCallback(our_ext_port, jp) : 1; + if(ret == 2) + { + DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports not connected, ret=DeleteRouteNode. Deleting route: our_port:%p route_jp:%p found_jp:%p route_persistent_name:%s\n", + our_port, ir->jackPort, jp, route_jpname); + // The port exists but is not connected to our port. Remove the route node. + operations.add(PendingOperationItem(route_list, ir, PendingOperationItem::DeleteRouteNode)); + } + else + if(ret == 1) + { + DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports not connected, ret=ModifyRouteNode. Modifying route: our_port:%p route_jp:%p found_jp:%p route_persistent_name:%s\n", + our_port, ir->jackPort, jp, route_jpname); + operations.add(PendingOperationItem(Route(Route::JACK_ROUTE, 0, NULL, ir->channel, 0, 0, ir->persistentJackPortName), &(*ir), PendingOperationItem::ModifyRouteNode)); + } + } + else + if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) // Don't try to connect if not running. + { + // Check whether a port registration happened BEFORE this graphChanged() was called, + // or just now during it, or was followed by an unregister. + int ret = checkPortRegisterCallback(jp); + if(ret == 1) + { + // No failed attempts at connection to the Jack port now or in the previous call to graphChanged(). + if(our_port) + { + // The port exists but is not connected to our port. Reconnect the route. + // NOTE: Jack2: graph changed callback will be called again regardless if jack_connect succeeds or fails... + DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports not connected. Reconnecting route: our_port:%p route_jp:%p found_jp:%p our_port_name:%s route_persistent_name:%s\n", + our_port, ir->jackPort, jp, our_port_name, route_jpname); + if(our_port_name) + { + int err; + if(is_input) + err = jack_connect(client, route_jpname, our_port_name); + else + err = jack_connect(client, our_port_name, route_jpname); + if(err) + { + DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports not connected. Reconnecting route: ERROR:%d our_port:%p route_jp:%p found_jp:%p our_port_name:%s route_persistent_name:%s\n", + err, our_port, ir->jackPort, jp, our_port_name, route_jpname); + } + else + { + // We have our jack port, on a supposedly active client. Update the route node's jack port pointer. + // Find a more appropriate name if necessary. + const char* s = ir->persistentJackPortName; + char fin_name[ROUTE_PERSISTENT_NAME_SIZE]; + portName(jp, fin_name, ROUTE_PERSISTENT_NAME_SIZE); + if(strcmp(ir->persistentJackPortName, fin_name) != 0) + { + DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports connected. Modifying route name: route_persistent_name:%s new name:%s\n", route_jpname, fin_name); + s = fin_name; + } + operations.add(PendingOperationItem(Route(Route::JACK_ROUTE, 0, jp, ir->channel, 0, 0, s), &(*ir), PendingOperationItem::ModifyRouteNode)); + } + } + } + } + // State unchanged. + } + } + } + else + { + // Port does not exist. Keep the route node but clear its jack port pointer if necessary. + if(ir->jackPort) + { + DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Port non-existent. Modifying route: our_port:%p route_jp:%p route_persistent_name:%s\n", + our_port, ir->jackPort, route_jpname); + operations.add(PendingOperationItem(Route(Route::JACK_ROUTE, 0, NULL, ir->channel, 0, 0, ir->persistentJackPortName), &(*ir), PendingOperationItem::ModifyRouteNode)); + } + } + } + + if(our_port) + checkNewRouteConnections(our_port, our_node.channel, route_list); +} + //--------------------------------------------------------- // JackAudioDevice::graphChanged // this is called from song in gui context triggered @@ -631,401 +933,273 @@ void JackAudioDevice::graphChanged() { - if (JACK_DEBUG) - printf("graphChanged()\n"); - if(!checkJackClient(_client)) return; - InputList* il = MusEGlobal::song->inputs(); - for (iAudioInput ii = il->begin(); ii != il->end(); ++ii) { - AudioInput* it = *ii; - int channels = it->channels(); - for (int channel = 0; channel < channels; ++channel) { - jack_port_t* port = (jack_port_t*)(it->jackPort(channel)); - if (port == 0) - continue; - const char** ports = jack_port_get_all_connections(_client, port); - RouteList* rl = it->inRoutes(); - - //--------------------------------------- - // check for disconnects - //--------------------------------------- - - bool erased; - // limit set to 20 iterations for disconnects, don't know how to make it go - // the "right" amount - for (int i = 0;i < 20;i++) { - erased = false; - for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { - if(irl->type != Route::JACK_ROUTE) - continue; - if (irl->channel != channel) - continue; - QString name = irl->name(); - QByteArray ba = name.toLatin1(); - const char* portName = ba.constData(); - //printf("portname=%s\n", portName); - bool found = false; - const char** pn = ports; - while (pn && *pn) { - if (strcmp(*pn, portName) == 0) { - found = true; - break; - } - ++pn; - } - if (!found) { - MusEGlobal::audio->msgRemoveRoute1( - //Route(portName, false, channel), - Route(portName, false, channel, Route::JACK_ROUTE), - Route(it, channel) - ); - erased = true; - break; - } - } - if (!erased) - break; - } - - //--------------------------------------- - // check for connects - //--------------------------------------- - - if (ports) { - const char** pn = ports; - while (*pn) { - bool found = false; - for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { - if(irl->type != Route::JACK_ROUTE) - continue; - if (irl->channel != channel) - continue; - QString name = irl->name(); - QByteArray ba = name.toLatin1(); - const char* portName = ba.constData(); - if (strcmp(*pn, portName) == 0) { - found = true; - break; - } - } - if (!found) { - MusEGlobal::audio->msgAddRoute1( - //Route(*pn, false, channel), - Route(*pn, false, channel, Route::JACK_ROUTE), - Route(it, channel) - ); - } - ++pn; - } - - jack_free(ports); // p4.0.29 - - ports = NULL; - } - } - } - OutputList* ol = MusEGlobal::song->outputs(); - for (iAudioOutput ii = ol->begin(); ii != ol->end(); ++ii) { - AudioOutput* it = *ii; - int channels = it->channels(); - for (int channel = 0; channel < channels; ++channel) { - jack_port_t* port = (jack_port_t*)(it->jackPort(channel)); - if (port == 0) - continue; - const char** ports = jack_port_get_all_connections(_client, port); - RouteList* rl = it->outRoutes(); - - //--------------------------------------- - // check for disconnects - //--------------------------------------- - - bool erased; - // limit set to 20 iterations for disconnects, don't know how to make it go - // the "right" amount - for (int i = 0; i < 20 ; i++) { - erased = false; - for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { - if(irl->type != Route::JACK_ROUTE) - continue; - if (irl->channel != channel) - continue; - QString name = irl->name(); - QByteArray ba = name.toLatin1(); - const char* portName = ba.constData(); - bool found = false; - const char** pn = ports; - while (pn && *pn) { - if (strcmp(*pn, portName) == 0) { - found = true; - break; - } - ++pn; - } - if (!found) { - MusEGlobal::audio->msgRemoveRoute1( - Route(it, channel), - //Route(portName, false, channel) - Route(portName, false, channel, Route::JACK_ROUTE) - ); - erased = true; - break; - } - } - if (!erased) - break; - } - - //--------------------------------------- - // check for connects - //--------------------------------------- - - if (ports) { - const char** pn = ports; - while (*pn) { - bool found = false; - for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { - if(irl->type != Route::JACK_ROUTE) - continue; - if (irl->channel != channel) - continue; - QString name = irl->name(); - QByteArray ba = name.toLatin1(); - const char* portName = ba.constData(); - if (strcmp(*pn, portName) == 0) { - found = true; - break; - } - } - if (!found) { - MusEGlobal::audio->msgAddRoute1( - Route(it, channel), - //Route(*pn, false, channel) - Route(*pn, false, channel, Route::JACK_ROUTE) - ); - } - ++pn; - } - - jack_free(ports); // p4.0.29 - - ports = NULL; - } - } - } - - for (iMidiDevice ii = MusEGlobal::midiDevices.begin(); ii != MusEGlobal::midiDevices.end(); ++ii) + if (JACK_DEBUG) + printf("graphChanged()\n"); + DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::graphChanged()\n"); + + if(!checkJackClient(_client)) + { + jackCallbackFifo.clear(); // Want this? + // Reset this now. + muse_atomic_set(&atomicGraphChangedPending, 0); + return; + } + + // For Jack-1 only: See if we need to wait, for example for a port unregister event. + // Jack "2" does not require waiting, Jack "1" does, assume any other version requires the wait (no harm). + if(MusEGlobal::audio && jack_ver_maj != 1) + { + // TODO: It may be desirable to always wait so that bunches of commands can be processed easier. + //bool do_audio_wait = false; + // This is safe because the writer only increases the size. + int cb_fifo_sz = jackCallbackFifo.getSize(); + for(int i = 0; i < cb_fifo_sz; ++i) + { + const JackCallbackEvent& jcb = jackCallbackFifo.peek(i); + if(jcb.type == PortDisconnect && (jack_port_is_mine(_client, jcb.port_A) || jack_port_is_mine(_client, jcb.port_B))) { - MidiDevice* md = *ii; - if(md->deviceType() != MidiDevice::JACK_MIDI) - continue; - - //MidiJackDevice* mjd = dynamic_cast(*ii); - //if(!mjd) - // continue; - //for (int channel = 0; channel < channels; ++channel) - //{ - - // p3.3.55 Removed - //jack_port_t* port = (jack_port_t*)md->clientPort(); - //if (port == 0) - // continue; - //const char** ports = jack_port_get_all_connections(_client, port); - - //--------------------------------------- - // outputs - //--------------------------------------- - - if(md->rwFlags() & 1) // Writable - { - jack_port_t* port = (jack_port_t*)md->outClientPort(); - if(port != 0) - { - const char** ports = jack_port_get_all_connections(_client, port); - - RouteList* rl = md->outRoutes(); - - //--------------------------------------- - // check for disconnects - //--------------------------------------- - - bool erased; - // limit set to 20 iterations for disconnects, don't know how to make it go - // the "right" amount - for (int i = 0; i < 20 ; i++) - { - erased = false; - for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { - if(irl->type != Route::JACK_ROUTE) - continue; - //if (irl->channel != channel) - // continue; - QString name = irl->name(); - //name += QString(JACK_MIDI_OUT_PORT_SUFFIX); - QByteArray ba = name.toLatin1(); - const char* portName = ba.constData(); - bool found = false; - const char** pn = ports; - while (pn && *pn) { - if (strcmp(*pn, portName) == 0) { - found = true; - break; - } - ++pn; - } - if (!found) { - MusEGlobal::audio->msgRemoveRoute1( - //Route(it, channel), - //Route(mjd), - Route(md, -1), - //Route(portName, false, channel) - //Route(portName, false, -1) - Route(portName, false, -1, Route::JACK_ROUTE) - ); - erased = true; - break; - } - } - if (!erased) - break; - } - - //--------------------------------------- - // check for connects - //--------------------------------------- - - if (ports) - { - const char** pn = ports; - while (*pn) { - bool found = false; - for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { - if(irl->type != Route::JACK_ROUTE) - continue; - //if (irl->channel != channel) - // continue; - QString name = irl->name(); - QByteArray ba = name.toLatin1(); - const char* portName = ba.constData(); - if (strcmp(*pn, portName) == 0) { - found = true; - break; - } - } - if (!found) { - MusEGlobal::audio->msgAddRoute1( - //Route(it, channel), - //Route(mjd), - Route(md, -1), - //Route(*pn, false, channel) - //Route(*pn, false, -1) - Route(*pn, false, -1, Route::JACK_ROUTE) - ); - } - ++pn; - } - - jack_free(ports); // p4.0.29 - } - } - } - - - //------------------------ - // Inputs - //------------------------ - - if(md->rwFlags() & 2) // Readable - { - jack_port_t* port = (jack_port_t*)md->inClientPort(); - if(port != 0) - { - const char** ports = jack_port_get_all_connections(_client, port); - - RouteList* rl = md->inRoutes(); - - //--------------------------------------- - // check for disconnects - //--------------------------------------- + DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::graphChanged: *** calling msgAudioWait()\n"); + MusEGlobal::audio->msgAudioWait(); // Wait until upcoming process call has finished... + break; + } + } + } + + // Reset this now. + muse_atomic_set(&atomicGraphChangedPending, 0); + + jackCallbackEvents.clear(); + // Find the last GraphChanged event, if any. + // This is safe because the writer only increases the size. + int cb_fifo_sz = jackCallbackFifo.getSize(); + if(cb_fifo_sz) + { + int last_gc_idx = cb_fifo_sz - 1; + if(jack_ver_maj == 1) + for(int i = 0; i < cb_fifo_sz; ++i) + if(jackCallbackFifo.peek(i).type == GraphChanged) + last_gc_idx = i; + // Move the events into a list for processing, including the final GraphChanged event. + // Leave any 'still in progress' ending events (without closing GraphChanged event) in the ring buffer. + //jackCallbackEvents.clear(); + for(int i = 0; i <= last_gc_idx; ++i) + jackCallbackEvents.push_back(jackCallbackFifo.get()); + } + processGraphChanges(); + + if(!operations.empty()) + { + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + operations.clear(); + } +} + +void JackAudioDevice::processGraphChanges() +{ + DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::processGraphChanges()\n"); + //--------------------------------------- + // Audio inputs: + //--------------------------------------- + + InputList* il = MusEGlobal::song->inputs(); + for(iAudioInput ii = il->begin(); ii != il->end(); ++ii) + { + AudioInput* it = *ii; + int channels = it->channels(); + for(int channel = 0; channel < channels; ++channel) + { + jack_port_t* port = (jack_port_t*)(it->jackPort(channel)); + // Support even if port == null. + processJackCallbackEvents(Route(it, channel), port, it->inRoutes(), true); + } + } + + //--------------------------------------- + // Audio outputs: + //--------------------------------------- + + OutputList* ol = MusEGlobal::song->outputs(); + for(iAudioOutput ii = ol->begin(); ii != ol->end(); ++ii) + { + AudioOutput* it = *ii; + int channels = it->channels(); + for(int channel = 0; channel < channels; ++channel) + { + jack_port_t* port = (jack_port_t*)(it->jackPort(channel)); + // Support even if port == null. + processJackCallbackEvents(Route(it, channel), port, it->outRoutes(), false); + } + } + + //--------------------------------------- + // Midi devices: + //--------------------------------------- + + for(iMidiDevice ii = MusEGlobal::midiDevices.begin(); ii != MusEGlobal::midiDevices.end(); ++ii) + { + MidiDevice* md = *ii; + if(md->deviceType() != MidiDevice::JACK_MIDI) + continue; - bool erased; - // limit set to 20 iterations for disconnects, don't know how to make it go - // the "right" amount - for (int i = 0; i < 20 ; i++) - { - erased = false; - for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { - if(irl->type != Route::JACK_ROUTE) - continue; - //if (irl->channel != channel) - // continue; - QString name = irl->name(); - QByteArray ba = name.toLatin1(); - const char* portName = ba.constData(); - bool found = false; - const char** pn = ports; - while (pn && *pn) { - if (strcmp(*pn, portName) == 0) { - found = true; - break; - } - ++pn; - } - if (!found) { - MusEGlobal::audio->msgRemoveRoute1( - //Route(portName, false, channel), - //Route(portName, false, -1), - Route(portName, false, -1, Route::JACK_ROUTE), - //Route(it, channel) - //Route(mjd) - Route(md, -1) - ); - erased = true; - break; - } - } - if (!erased) - break; - } + //--------------------------------------- + // Midi outputs: + //--------------------------------------- - //--------------------------------------- - // check for connects - //--------------------------------------- + if(md->rwFlags() & 1) // Writable + { + jack_port_t* port = (jack_port_t*)md->outClientPort(); + // Support even if port == null. + processJackCallbackEvents(Route(md, -1), port, md->outRoutes(), false); + } + + //------------------------ + // Midi inputs: + //------------------------ - if (ports) - { - const char** pn = ports; - while (*pn) { - bool found = false; - for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { - if(irl->type != Route::JACK_ROUTE) - continue; - //if (irl->channel != channel) - // continue; - QString name = irl->name(); - QByteArray ba = name.toLatin1(); - const char* portName = ba.constData(); - if (strcmp(*pn, portName) == 0) { - found = true; - break; - } - } - if (!found) { - MusEGlobal::audio->msgAddRoute1( - //Route(*pn, false, channel), - //Route(*pn, false, -1), - Route(*pn, false, -1, Route::JACK_ROUTE), - //Route(it, channel) - //Route(mjd) - Route(md, -1) - ); - } - ++pn; - } - - jack_free(ports); // p4.0.29 - } - } - } + if(md->rwFlags() & 2) // Readable + { + jack_port_t* port = (jack_port_t*)md->inClientPort(); + // Support even if port == null. + processJackCallbackEvents(Route(md, -1), port, md->inRoutes(), true); + } + } +} + +void JackAudioDevice::checkNewRouteConnections(jack_port_t* our_port, int channel, RouteList* route_list) +{ + DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::checkNewRouteConnections(): client:%p our_port:%p channel:%d route_list:%p\n", + _client, our_port, channel, route_list); + // Check for new connections... + const char** ports = jack_port_get_all_connections(_client, our_port); + if(ports) + { + const char** pn = ports; + while(*pn) + { + // Should be safe and quick search here, we know that the port name is valid. + jack_port_t* jp = jack_port_by_name(_client, *pn); + if(jp) + { + bool found = false; + for(ciRoute ir = route_list->begin(); ir != route_list->end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE || (channel != -1 && ir->channel != channel)) + continue; + + // See if any changes are pending for the route node and take them into account. + jack_port_t* op_jp = (jack_port_t*)ir->jackPort; + const char* op_ppname = ir->persistentJackPortName; + iPendingOperation ipo = operations.end(); + while(ipo != operations.begin()) + { + --ipo; + switch(ipo->_type) + { + case PendingOperationItem::DeleteRouteNode: + if(ipo->_route_list == route_list && &(*ipo->_iRoute) == &(*ir)) + { + found = true; + ipo = operations.begin(); // Breakout + } + break; + + case PendingOperationItem::ModifyRouteNode: + if(ipo->_dst_route_pointer == &(*ir)) + { + op_jp = (jack_port_t*)ipo->_src_route.jackPort; + op_ppname = ipo->_src_route.persistentJackPortName; + ipo = operations.begin(); // Breakout + } + break; + + default: + break; + } + } + if(found) + { + found = false; + continue; // Ignore the route node - it has been scheduled for deletion. + } + + // TODO: Maybe switch to get_ports + if(op_jp == jp || jack_port_by_name(_client, op_ppname) == jp) + { + found = true; + break; + } + } + if(!found) + { + Route r(Route::JACK_ROUTE, 0, jp, channel, 0, 0, NULL); + // Find a better name. + portName(jp, r.persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); + DEBUG_PRST_ROUTES(stderr, " adding route: route_jp:%p portname:%s route_persistent_name:%s\n", + jp, *pn, r.persistentJackPortName); + operations.add(PendingOperationItem(route_list, r, PendingOperationItem::AddRouteNode)); + } + } + ++pn; + } + jack_free(ports); + } +} + +int JackAudioDevice::checkDisconnectCallback(const jack_port_t* our_port, const jack_port_t* port) +{ + DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::checkDisconnectCallback(): our_port:%p port:%p\n", our_port, port); + + iJackCallbackEvent ijce = jackCallbackEvents.end(); + while(ijce != jackCallbackEvents.begin()) + { + --ijce; + if(ijce->type == PortConnect && ((ijce->port_A == our_port && ijce->port_B == port) || (ijce->port_B == our_port && ijce->port_A == port))) + return 0; + if(ijce->type == PortDisconnect) + { + jack_port_id_t id; + if(ijce->port_A == our_port && ijce->port_B == port) + id = ijce->port_id_B; + else if(ijce->port_B == our_port && ijce->port_A == port) + id = ijce->port_id_A; + else continue; + + for( ++ijce ; ijce != jackCallbackEvents.end(); ++ijce) + if(ijce->type == PortUnregister && ijce->port_id_A == id) + return 1; + return 2; + } + } + return 0; +} + +int JackAudioDevice::checkPortRegisterCallback(const jack_port_t* port) +{ + DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::checkPortRegisterCallback(): port:%p\n", port); + + iJackCallbackEvent ijce = jackCallbackEvents.end(); + while(ijce != jackCallbackEvents.begin()) + { + --ijce; + if(ijce->type == PortRegister) + { + jack_port_id_t id = ijce->port_id_A; + if(jack_port_by_id(_client, id) == port) + { + for( ++ijce ; ijce != jackCallbackEvents.end(); ++ijce) + if(ijce->type == PortUnregister && ijce->port_id_A == id) + return 0; + return 1; } + } + } + return 0; +} + +int JackAudioDevice::static_JackXRunCallback(void *) +{ + MusEGlobal::audio->incXruns(); + return 0; } //static int xrun_callback(void*) @@ -1042,6 +1216,18 @@ { if (JACK_DEBUG) printf("registerClient()\n"); + +// if (MusEGlobal::debugMsg) { +// fprintf(stderr, "JackAudioDevice::registerClient(): registering error and info callbacks...\n"); +// jack_set_error_function(jackError); +// jack_set_info_function(jackInfo); +// } +// else { +// fprintf(stderr, "JackAudioDevice::registerClient(): registering no error and no info callbacks...\n"); +// jack_set_error_function(noJackError); +// jack_set_info_function(noJackInfo); +// } + if(!checkJackClient(_client)) return; jack_set_thread_init_callback(_client, (JackThreadInitCallback) jack_thread_init, 0); @@ -1053,13 +1239,16 @@ jack_on_shutdown(_client, processShutdown, 0); jack_set_buffer_size_callback(_client, bufsize_callback, 0); jack_set_sample_rate_callback(_client, srate_callback, 0); - jack_set_port_registration_callback(_client, registration_callback, 0); + jack_set_port_registration_callback(_client, registration_callback, this); jack_set_client_registration_callback(_client, client_registration_callback, 0); - jack_set_port_connect_callback(_client, port_connect_callback, 0); - - jack_set_graph_order_callback(_client, graph_callback, 0); + jack_set_port_connect_callback(_client, port_connect_callback, this); + // Can't use this! Incompatible in jack 1/2 and unimplemented in jack1. + //jack_set_port_rename_callback(_client, port_rename_callback, 0); + jack_set_graph_order_callback(_client, graph_callback, this); // jack_set_xrun_callback(client, xrun_callback, 0); jack_set_freewheel_callback (_client, freewheel_callback, 0); + + jack_set_xrun_callback(_client, static_JackXRunCallback, this); } //--------------------------------------------------------- @@ -1068,13 +1257,13 @@ void* JackAudioDevice::registerInPort(const char* name, bool midi) { - if (JACK_DEBUG) - printf("registerInPort()\n"); - if(!checkJackClient(_client)) return NULL; + if(JACK_DEBUG) + printf("registerInPort()\n"); + if(!checkJackClient(_client) || !name || name[0] == '\0') + return NULL; const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; - //void* p = jack_port_register(_client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); void* p = jack_port_register(_client, name, type, JackPortIsInput, 0); -// printf("JACK: registerInPort: <%s> %p\n", name, p); + DEBUG_PRST_ROUTES(stderr, "JACK: registerInPort: <%s> %p\n", name, p); return p; } @@ -1084,13 +1273,13 @@ void* JackAudioDevice::registerOutPort(const char* name, bool midi) { - if (JACK_DEBUG) - printf("registerOutPort()\n"); - if(!checkJackClient(_client)) return NULL; + if(JACK_DEBUG) + printf("registerOutPort()\n"); + if(!checkJackClient(_client) || !name || name[0] == '\0') + return NULL; const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; - //void* p = jack_port_register(_client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); void* p = jack_port_register(_client, name, type, JackPortIsOutput, 0); -// printf("JACK: registerOutPort: <%s> %p\n", name, p); + DEBUG_PRST_ROUTES(stderr, "JACK: registerOutPort: <%s> %p\n", name, p); return p; } @@ -1098,22 +1287,23 @@ // connect //--------------------------------------------------------- -void JackAudioDevice::connect(void* src, void* dst) +bool JackAudioDevice::connect(void* src, void* dst) { if (JACK_DEBUG) printf("JackAudioDevice::connect()\n"); - if(!checkJackClient(_client)) return; + if(!checkJackClient(_client)) return false; const char* sn = jack_port_name((jack_port_t*) src); const char* dn = jack_port_name((jack_port_t*) dst); if (sn == 0 || dn == 0) { fprintf(stderr, "JackAudio::connect: unknown jack ports\n"); - return; + return false; } int err = jack_connect(_client, sn, dn); //if (jack_connect(_client, sn, dn)) { if (err) { fprintf(stderr, "jack connect <%s>%p - <%s>%p failed with err:%d\n", sn, src, dn, dst, err); + return false; } else if (JACK_DEBUG) @@ -1121,30 +1311,47 @@ fprintf(stderr, "jack connect <%s>%p - <%s>%p succeeded\n", sn, src, dn, dst); } + return true; +} + +bool JackAudioDevice::connect(const char* src, const char* dst) +{ + if(JACK_DEBUG) + printf("JackAudioDevice::connect()\n"); + if(!checkJackClient(_client) || !src || !dst || src[0] == '\0' || dst[0] == '\0') + return false; + int err = jack_connect(_client, src, dst); + if(err) + { + fprintf(stderr, "jack connect <%s> - <%s> failed with err:%d\n", src, dst, err); + return false; + } + return true; } //--------------------------------------------------------- // disconnect //--------------------------------------------------------- -void JackAudioDevice::disconnect(void* src, void* dst) +bool JackAudioDevice::disconnect(void* src, void* dst) { if (JACK_DEBUG) printf("JackAudioDevice::disconnect()\n"); - if(!checkJackClient(_client)) return; + if(!checkJackClient(_client)) return false; if(!src || !dst) - return; + return false; const char* sn = jack_port_name((jack_port_t*) src); const char* dn = jack_port_name((jack_port_t*) dst); if (sn == 0 || dn == 0) { fprintf(stderr, "JackAudio::disconnect: unknown jack ports\n"); - return; + return false; } int err = jack_disconnect(_client, sn, dn); //if (jack_disconnect(_client, sn, dn)) { if (err) { fprintf(stderr, "jack disconnect <%s> - <%s> failed with err:%d\n", sn, dn, err); + return false; } else if (JACK_DEBUG) @@ -1152,71 +1359,164 @@ fprintf(stderr, "jack disconnect <%s> - <%s> succeeded\n", sn, dn); } + return true; +} + +bool JackAudioDevice::disconnect(const char* src, const char* dst) +{ + if(JACK_DEBUG) + printf("JackAudioDevice::disconnect()\n"); + if(!checkJackClient(_client) || !src || !dst || src[0] == '\0' || dst[0] == '\0') + return false; + int err = jack_disconnect(_client, src, dst); + if(err) + { + fprintf(stderr, "jack disconnect <%s> - <%s> failed with err:%d\n", src, dst, err); + return false; + } + return true; +} + +//--------------------------------------------------------- +// portsCanDisconnect +//--------------------------------------------------------- + +bool JackAudioDevice::portsCanDisconnect(void* src, void* dst) const +{ + if(!_client) + return false; + if(!src || !dst) + return false; + + const char** ports = jack_port_get_all_connections(_client, (jack_port_t*)src); + if(!ports) + return false; + + bool rv = false; + for(const char** p = ports; p && *p; ++p) + { + jack_port_t* jp = jack_port_by_name(_client, *p); + if(jp == dst) + { + rv = true; + break; + } + } + jack_free(ports); + return rv; +} + +bool JackAudioDevice::portsCanDisconnect(const char* src, const char* dst) const +{ + if(!_client) + return false; + return portsCanDisconnect(jack_port_by_name(_client, src), jack_port_by_name(_client, dst)); +} + +//--------------------------------------------------------- +// portsCanConnect +//--------------------------------------------------------- + +bool JackAudioDevice::portsCanConnect(void* src, void* dst) const +{ + if(!_client) + return false; + if(!src || !dst) + return false; + const char* src_type = jack_port_type((jack_port_t*)src); + const char* dst_type = jack_port_type((jack_port_t*)dst); + if(!src_type || !dst_type || (strcmp(src_type, dst_type) != 0)) + return false; + + if(!(jack_port_flags((jack_port_t*)src) & JackPortIsOutput) || !(jack_port_flags((jack_port_t*)dst) & JackPortIsInput)) + return false; + + const char** ports = jack_port_get_all_connections(_client, (jack_port_t*)src); + if(!ports) + return true; + + bool rv = true; + for(const char** p = ports; p && *p; ++p) + { + jack_port_t* jp = jack_port_by_name(_client, *p); + if(jp == dst) + { + rv = false; + break; + } + } + + jack_free(ports); + return rv; +} + +bool JackAudioDevice::portsCanConnect(const char* src, const char* dst) const +{ + if(!_client) + return false; + return portsCanConnect(jack_port_by_name(_client, src), jack_port_by_name(_client, dst)); +} + +//--------------------------------------------------------- +// portsCompatible +//--------------------------------------------------------- + +bool JackAudioDevice::portsCompatible(void* src, void* dst) const +{ + if(!src || !dst) + return false; + const char* src_type = jack_port_type((jack_port_t*)src); + const char* dst_type = jack_port_type((jack_port_t*)dst); + if(!src_type || !dst_type || (strcmp(src_type, dst_type) != 0)) + return false; + + if(!(jack_port_flags((jack_port_t*)src) & JackPortIsOutput) || !(jack_port_flags((jack_port_t*)dst) & JackPortIsInput)) + return false; + + return true; +} + +bool JackAudioDevice::portsCompatible(const char* src, const char* dst) const +{ + if(!_client) + return false; + return portsCompatible(jack_port_by_name(_client, src), jack_port_by_name(_client, dst)); } //--------------------------------------------------------- // start +// Return true on success. //--------------------------------------------------------- -void JackAudioDevice::start(int /*priority*/) +bool JackAudioDevice::start(int /*priority*/) { if (JACK_DEBUG) printf("JackAudioDevice::start()\n"); - if(!checkJackClient(_client)) return; + if(!checkJackClient(_client)) return false; MusEGlobal::doSetuid(); + DEBUG_PRST_ROUTES (stderr, "JackAudioDevice::start(): calling jack_activate()\n"); + if (jack_activate(_client)) { MusEGlobal::undoSetuid(); fprintf (stderr, "JACK: cannot activate client\n"); exit(-1); } + + MusEGlobal::undoSetuid(); + /* connect the ports. Note: you can't do this before the client is activated, because we can't allow connections to be made to clients that aren't running. */ + MusEGlobal::song->connectAllPorts(); - InputList* il = MusEGlobal::song->inputs(); - for (iAudioInput i = il->begin(); i != il->end(); ++i) { - AudioInput* ai = *i; - int channel = ai->channels(); - for (int ch = 0; ch < channel; ++ch) { - RouteList* rl = ai->inRoutes(); - void* port = ai->jackPort(ch); - for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { - if(ir->type != Route::JACK_ROUTE) - continue; - if (ir->channel == ch) - connect(ir->jackPort, port); - } - } - } - OutputList* ol = MusEGlobal::song->outputs(); - for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) { - AudioOutput* ai = *i; - int channel = ai->channels(); - for (int ch = 0; ch < channel; ++ch) { - RouteList* rl = ai->outRoutes(); - void* port = ai->jackPort(ch); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) { - if(r->type != Route::JACK_ROUTE) - continue; - if (r->channel == ch) { - connect(port, r->jackPort); - } - } - } - } - - // Connect the Jack midi client ports to device ports. - connectJackMidiPorts(); - - MusEGlobal::undoSetuid(); - - //MUSE_DEBUG("JackAudioDevice::start()\n"); fflush(stdin); //JackAudioDevice::jackStarted=true; + + return true; } //--------------------------------------------------------- @@ -1228,6 +1528,8 @@ if (JACK_DEBUG) printf("JackAudioDevice::stop()\n"); if(!checkJackClient(_client)) return; + DEBUG_PRST_ROUTES (stderr, "JackAudioDevice::stop(): calling jack_deactivate()\n"); + if (jack_deactivate(_client)) { fprintf (stderr, "cannot deactivate client\n"); } @@ -1371,17 +1673,42 @@ return (int)n; } -#if 0 +//--------------------------------------------------------- +// framesAtCycleStart +// Frame count at the start of current cycle. +// This is meant to be called from inside process thread only. +//--------------------------------------------------------- + +unsigned JackAudioDevice::framesAtCycleStart() const +{ + if(!checkJackClient(_client)) return 0; + jack_nframes_t n = jack_last_frame_time(_client); + //if (JACK_DEBUG) + // printf("JackAudioDevice::framesAtCycleStart jack frame:%d\n", (unsigned)n); + return (unsigned)n; +} + //--------------------------------------------------------- // framesSinceCycleStart +// Estimated frames since the last process cycle began +// This is meant to be called from inside process thread only. //--------------------------------------------------------- -int JackAudioDevice::framesSinceCycleStart() const - { - jack_nframes_t n = jack_frames_since_cycle_start(client); - return (int)n; - } +unsigned JackAudioDevice::framesSinceCycleStart() const +{ + if(!checkJackClient(_client)) return 0; + jack_nframes_t n = jack_frames_since_cycle_start(_client); + //if (JACK_DEBUG) + // printf("JackAudioDevice::framesSinceCycleStart jack frame:%d\n", (unsigned)n); + + // Safety due to inaccuracies. It cannot be after the segment, right? + if(n >= MusEGlobal::segmentSize) + n = MusEGlobal::segmentSize - 1; + + return (unsigned)n; +} +#if 0 //--------------------------------------------------------- // framesDelay // TODO @@ -1402,25 +1729,13 @@ { if (JACK_DEBUG) printf("JackAudioDevice::getJackPorts()\n"); - //std::list clientList; - //if(!checkJackClient(_client)) return clientList; - //if(!checkJackClient(_client)) return; QString qname; - //const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; - //const char** ports = jack_get_ports(_client, 0, type, JackPortIsInput); - //const char** ports = jack_get_ports(_client, 0, type, jflags); - QString cname(jack_get_client_name(_client)); for (const char** p = ports; p && *p; ++p) { + // Should be safe and quick search here, we know that the port name is valid. jack_port_t* port = jack_port_by_name(_client, *p); - //int flags = jack_port_flags(port); - //if (!(flags & JackPortIsInput)) - // continue; - //char buffer[128]; - int port_flags = jack_port_flags(port); - //printf("JackAudioDevice::getJackPorts port: %s flags: %d\n", *p, port_flags); // Ignore our own client ports. if(jack_port_is_mine(_client, port)) @@ -1483,13 +1798,8 @@ else qname = QString(buffer); - //clientList.push_back(QString(buffer)); name_list.push_back(qname); } - - // jack_free(ports); // p4.0.29 - - //return clientList; } //--------------------------------------------------------- @@ -1540,21 +1850,127 @@ } //--------------------------------------------------------- +// setPortName +//--------------------------------------------------------- + +void JackAudioDevice::setPortName(void* p, const char* n) +{ + // NOTE: jack_port_set_name() is deprecated as of Jack2 = 1.9.11, and Jack1 > 0.124.1 + if(jack_port_rename_fp) + { + if(!checkJackClient(_client)) + return; + jack_port_rename_fp(_client, (jack_port_t*)p, n); + } + else if(jack_port_set_name_fp) + jack_port_set_name_fp((jack_port_t*)p, n); +} + +//--------------------------------------------------------- // portName +// Returns name of port and sets success. +// This method consults a blacklist of client names, +// such as "system:", whether to pick the name or +// one of the aliases, whichever does NOT contain +// the blacklist names. +// preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. //--------------------------------------------------------- -QString JackAudioDevice::portName(void* port) - { - //if (JACK_DEBUG) - // printf("JackAudioDevice::portName\n"); - if(!checkJackClient(_client)) return ""; - if (!port) - return ""; - - QString s(jack_port_name((jack_port_t*)port)); - //printf("Jack::portName %p %s\n", port, s.toLatin1()); - return s; - } +char* JackAudioDevice::portName(void* port, char* str, int str_size, int preferred_name_or_alias) +{ + bool A = false, B = false, C = false; + const char* p_name = jack_port_name((jack_port_t*)port); + if(p_name && p_name[0] != '\0') + { + // TODO: Make this a user editable blacklist of client names! + if((strncmp(p_name, "system:", 7) != 0 && preferred_name_or_alias == -1) || preferred_name_or_alias == 0) + return MusELib::strntcpy(str, p_name, str_size); + A = true; + } + + int nsz = jack_port_name_size(); + char a1[nsz]; + char a2[nsz]; + char* al[2]; + al[0] = &a1[0]; + al[1] = &a2[0]; + + int na = jack_port_get_aliases((jack_port_t*)port, al); + if(na >= 1 && *al[0] != '\0') + { + if((strncmp(al[0], "system:", 7) != 0 && preferred_name_or_alias == -1) || preferred_name_or_alias == 1) + return MusELib::strntcpy(str, al[0], str_size); + B = true; + } + + if(na >= 2 && *al[1] != '\0') + { + if((strncmp(al[1], "system:", 7) != 0 && preferred_name_or_alias == -1) || preferred_name_or_alias == 2) + return MusELib::strntcpy(str, al[1], str_size); + C = true; + } + + if(A) + return MusELib::strntcpy(str, p_name, str_size); + if(B) + return MusELib::strntcpy(str, al[0], str_size); + if(C) + return MusELib::strntcpy(str, al[1], str_size); + + return MusELib::strntcpy(str, p_name, str_size); // strntcpy accepts NULL source + +} + +//--------------------------------------------------------- +// portLatency +// If capture is true get the capture latency, +// otherwise get the playback latency. +//--------------------------------------------------------- + +unsigned int JackAudioDevice::portLatency(void* port, bool capture) const +{ + // TODO: Experimental code... Finish it... + + if(!checkJackClient(_client) || !port) + return 0; + + //QString s(jack_port_name((jack_port_t*)port)); + //fprintf(stderr, "Jack::portName %p %s\n", port, s.toLatin1().constData()); + + jack_latency_range_t p_range; + jack_port_get_latency_range((jack_port_t*)port, JackPlaybackLatency, &p_range); + //fprintf(stderr, "JackAudioDevice::portLatency playback min:%u max:%u\n", p_range.min, p_range.max); + //if(p_range.max != p_range.min) + //{ + // fprintf(stderr, "JackAudioDevice::portLatency min:%u != max:%u\n", p_range.min, p_range.max); + //return (p_range.max - p_range.min) / 2; + //} + + jack_latency_range_t c_range; + jack_port_get_latency_range((jack_port_t*)port, JackCaptureLatency, &c_range); + //fprintf(stderr, "JackAudioDevice::portLatency capture min:%u max:%u\n", c_range.min, c_range.max); + //if(c_range.max != c_range.min) + //{ + //fprintf(stderr, "JackAudioDevice::portLatency capture min:%u != max:%u\n", c_range.min, c_range.max); + + // TODO TEST Decide which method to use. Although, it is arbitrary. + //return (c_range.max - c_range.min) / 2; + //return c_range.max; + //} + + if(capture) + //return (c_range.max - c_range.min) / 2; + return c_range.max; + + //return (p_range.max - p_range.min) / 2; + return p_range.max; + + +// // Hm... for speed, maybe cache the values? +// jack_latency_range_t l_range; +// jack_port_get_latency_range((jack_port_t*)port, capture ? JackCaptureLatency : JackPlaybackLatency, &l_range); +// return l_range.max; +} //--------------------------------------------------------- // unregisterPort @@ -1562,13 +1978,50 @@ void JackAudioDevice::unregisterPort(void* p) { - if (JACK_DEBUG) - printf("JackAudioDevice::unregisterPort(\n"); - if(!checkJackClient(_client)) return; + if(JACK_DEBUG) + printf("JackAudioDevice::unregisterPort(%p)\n", p); + if(!checkJackClient(_client) || !p) + return; // printf("JACK: unregister Port\n"); jack_port_unregister(_client, (jack_port_t*)p); } +float AudioDevice::getDSP_Load() +{ + return 0.0f; +} + +float JackAudioDevice::getDSP_Load() +{ + return jack_cpu_load(_client); +} + +AudioDevice::PortType JackAudioDevice::portType(void* p) const +{ + if(!p) + return UnknownType; + if(const char* type = jack_port_type((jack_port_t*)p)) + { + if(strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) + return AudioPort; + if(strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) + return MidiPort; + } + return UnknownType; +} + +AudioDevice::PortDirection JackAudioDevice::portDirection(void* p) const +{ + if(!p) + return UnknownDirection; + const int flags = jack_port_flags((jack_port_t*)p); + if(flags & JackPortIsInput) + return InputPort; + if(flags & JackPortIsOutput) + return OutputPort; + return UnknownDirection; +} + //--------------------------------------------------------- // getState //--------------------------------------------------------- @@ -1593,7 +2046,7 @@ //if (JACK_DEBUG) // printf("JackAudioDevice::getState transportState:%d\n", transportState); - switch (transportState) { + switch (int(transportState)) { case JackTransportStopped: return Audio::STOP; case JackTransportLooping: @@ -1747,10 +2200,10 @@ void* JackAudioDevice::findPort(const char* name) { if (JACK_DEBUG) - printf("JackAudioDevice::findPort(\n"); - if(!checkJackClient(_client)) return NULL; + printf("JackAudioDevice::findPort(%s)\n", name); + if(!checkJackClient(_client) || !name || name[0] == '\0') + return NULL; void* p = jack_port_by_name(_client, name); -// printf("Jack::findPort <%s>, %p\n", name, p); return p; } @@ -1797,148 +2250,6 @@ } //--------------------------------------------------------- -// scanMidiPorts -//--------------------------------------------------------- - -void JackAudioDevice::scanMidiPorts() -{ - //if(MusEGlobal::debugMsg) - // printf("JackAudioDevice::scanMidiPorts:\n"); - -/* - const char* type = JACK_DEFAULT_MIDI_TYPE; - const char** ports = jack_get_ports(_client, 0, type, 0); - - std::set names; - for (const char** p = ports; p && *p; ++p) - { - jack_port_t* port = jack_port_by_name(_client, *p); - if(!port) - continue; - // Ignore our own client ports. - if(jack_port_is_mine(_client, port)) - { - if(MusEGlobal::debugMsg) - printf(" ignoring own port: %s\n", *p); - continue; - } - - int nsz = jack_port_name_size(); - char buffer[nsz]; - strncpy(buffer, *p, nsz); - // Ignore the MusE Jack port. - //if(strncmp(buffer, "MusE", 4) == 0) - // continue; - - if(MusEGlobal::debugMsg) - printf(" found port: %s ", buffer); - - // If there are aliases for this port, use the first one - much better for identifying. - //char a1[nsz]; - char a2[nsz]; - char* aliases[2]; - //aliases[0] = a1; - aliases[0] = buffer; - aliases[1] = a2; - // To disable aliases, just rem this line. - jack_port_get_aliases(port, aliases); - //int na = jack_port_get_aliases(port, aliases); - //char* namep = (na >= 1) ? aliases[0] : buffer; - //char* namep = aliases[0]; - //names.insert(std::string(*p)); - if(MusEGlobal::debugMsg) - printf("alias: %s\n", aliases[0]); - - names.insert(std::string(aliases[0])); - } - if(ports) - free(ports); - - std::list to_del; - for(iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) - { - // Only Jack midi devices. - if(dynamic_cast(*imd) == 0) - continue; - if(names.find(std::string((*imd)->name().toLatin1())) == names.end()) - to_del.push_back(*imd); - } - - for(std::list::iterator imd = to_del.begin(); imd != to_del.end(); ++imd) - { - if(MusEGlobal::debugMsg) - printf(" removing port device:%s\n", (*imd)->name().toLatin1()); - MusEGlobal::midiDevices.remove(*imd); - // This will close (and unregister) the client port. - delete (*imd); - } - - //for (const char** p = ports; p && *p; ++p) - for(std::set::iterator is = names.begin(); is != names.end(); ++is) - { - //jack_port_t* port = jack_port_by_name(_client, *p); - jack_port_t* port = jack_port_by_name(_client, is->c_str()); - if(!port) - continue; -*/ - - /* - int nsz = jack_port_name_size(); - char buffer[nsz]; - //strncpy(buffer, *p, nsz); - strncpy(buffer, is->c_str(), nsz); - // Ignore the MusE Jack port. - //if(strncmp(buffer, "MusE", 4) == 0) - // continue; - - // If there are aliases for this port, use the first one - much better for identifying. - //char a1[nsz]; - char a2[nsz]; - char* aliases[2]; - //aliases[0] = a1; - aliases[0] = buffer; - aliases[1] = a2; - // To disable aliases, just rem this line. - jack_port_get_aliases(port, aliases); - //int na = jack_port_get_aliases(port, aliases); - //char* namep = (na >= 1) ? aliases[0] : buffer; - char* namep = aliases[0]; - QString qname(namep); - */ - -/* - QString qname(is->c_str()); - - // Port already exists? - if(MusEGlobal::midiDevices.find(qname)) - continue; - - int flags = 0; - int pf = jack_port_flags(port); - // If Jack port can send data to us... - if(pf & JackPortIsOutput) - // Mark as input capable. - flags |= 2; - // If Jack port can receive data from us... - if(pf & JackPortIsInput) - // Mark as output capable. - flags |= 1; - - //JackPort jp(0, QString(buffer), flags); - //portList.append(jp); - - if(MusEGlobal::debugMsg) - printf(" adding port device:%s\n", qname.toLatin1()); - - MidiJackDevice* dev = new MidiJackDevice(0, qname); - dev->setrwFlags(flags); - MusEGlobal::midiDevices.add(dev); - } -*/ -} - - -//--------------------------------------------------------- // exitJackAudio //--------------------------------------------------------- @@ -1953,8 +2264,9 @@ printf("exitJackAudio() after delete jackAudio\n"); MusEGlobal::audioDevice = NULL; - + muse_atomic_destroy(&atomicGraphChangedPending); } + } // namespace MusECore diff -Nru muse-2.1.2/muse/driver/jackmidi.cpp muse-3.0.2+ds1/muse/driver/jackmidi.cpp --- muse-2.1.2/muse/driver/jackmidi.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/jackmidi.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -22,6 +22,7 @@ //========================================================= #include +#include #include #include @@ -39,6 +40,7 @@ #include "../midiseq.h" #include "../midictrl.h" #include "../audio.h" +#include "minstrument.h" #include "mpevent.h" //#include "sync.h" #include "audiodev.h" @@ -47,10 +49,15 @@ #include "xml.h" #include "gconfig.h" #include "track.h" +#include "route.h" +#include "helper.h" // Turn on debug messages. //#define JACK_MIDI_DEBUG +// For debugging output: Uncomment the fprintf section. +#define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); + namespace MusECore { //--------------------------------------------------------- @@ -95,8 +102,8 @@ { for( ; ni < 65536; ++ni) { - name.sprintf("jack-midi-%d", ni); - if(!MusEGlobal::midiDevices.find(name)) + name = QString("jack-midi-") + QString::number(ni); + if(!MusEGlobal::midiDevices.find(name, JACK_MIDI)) break; } } @@ -141,7 +148,17 @@ printf("MidiJackDevice::open %s\n", name().toLatin1().constData()); #endif + // Start by disabling for now. + _writeEnable = _readEnable = false; + if(!MusEGlobal::checkAudioDevice()) + { + fprintf(stderr, "MusE: MidiJackDevice::open failed: No audio device\n"); + _state = QString("Not ready"); + return _state; + } + QString s; + bool out_fail = false, in_fail = false; if(_openFlags & 1) { if(!_out_client_jackport) @@ -149,19 +166,45 @@ if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO) { s = name() + QString(JACK_MIDI_OUT_PORT_SUFFIX); - _out_client_jackport = (jack_port_t*)MusEGlobal::audioDevice->registerOutPort(s.toLatin1().constData(), true); + QByteArray ba = s.toLatin1(); + const char* cs = ba.constData(); + DEBUG_PRST_ROUTES(stderr, "MusE: MidiJackDevice::open creating output port name %s\n", cs); + _out_client_jackport = (jack_port_t*)MusEGlobal::audioDevice->registerOutPort(cs, true); if(!_out_client_jackport) { - fprintf(stderr, "MusE: MidiJackDevice::open failed creating output port name %s\n", s.toLatin1().constData()); - _openFlags &= ~1; // Remove the flag, but continue on... + fprintf(stderr, "MusE: MidiJackDevice::open failed creating output port name %s\n", cs); + _writeEnable = false; + out_fail = true; + } + else + { + _writeEnable = true; + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(_out_client_jackport); + if(our_port_name) + { + // (We just registered the port. At this point, any existing persistent routes' jackPort SHOULD be 0.) + for(iRoute ir = _outRoutes.begin(); ir != _outRoutes.end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE) + continue; + const char* route_name = ir->persistentJackPortName; + if(!ir->jackPort) + ir->jackPort = MusEGlobal::audioDevice->findPort(route_name); + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + if(ir->jackPort) + MusEGlobal::audioDevice->connect(our_port_name, route_name); + } + } } } } } else { + _writeEnable = false; if(_out_client_jackport) { + DEBUG_PRST_ROUTES(stderr, "MusE: MidiJackDevice::open unregistering output port\n"); // We want to unregister the port (which will also disconnect it), AND remove Routes, and then NULL-ify _out_client_jackport. // We could let our graph change callback (the gui thread one) remove the Routes (which it would anyway). // But that happens later (gui thread) and it needs a valid _out_client_jackport, @@ -182,10 +225,21 @@ // When I toggle the lights again (which kills, then recreates the ports here), the problem can disappear or come back again. // Also once observed a weird double connection from the port to two different Jack ports but one of // the connections should not have been there and kept toggling along with the other (like a 'ghost' connection). - MusEGlobal::audio->msgRemoveRoutes(Route(this, 0), Route()); // New function msgRemoveRoutes simply uses Routes, for their pointers. + for(iRoute ir = _outRoutes.begin(); ir != _outRoutes.end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE) + continue; + if(ir->jackPort) + { + // Before we nullify the jackPort, grab the latest valid name of the port. + MusEGlobal::audioDevice->portName(ir->jackPort, ir->persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); + ir->jackPort = 0; + } + } + MusEGlobal::audioDevice->unregisterPort(_out_client_jackport); + _out_client_jackport = NULL; } - _out_client_jackport = NULL; } if(_openFlags & 2) @@ -195,29 +249,72 @@ if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO) { s = name() + QString(JACK_MIDI_IN_PORT_SUFFIX); - _in_client_jackport = (jack_port_t*)MusEGlobal::audioDevice->registerInPort(s.toLatin1().constData(), true); + QByteArray ba = s.toLatin1(); + const char* cs = ba.constData(); + DEBUG_PRST_ROUTES(stderr, "MusE: MidiJackDevice::open creating input port name %s\n", cs); + _in_client_jackport = (jack_port_t*)MusEGlobal::audioDevice->registerInPort(cs, true); if(!_in_client_jackport) { - fprintf(stderr, "MusE: MidiJackDevice::open failed creating input port name %s\n", s.toLatin1().constData()); - _openFlags &= ~2; // Remove the flag, but continue on... + fprintf(stderr, "MusE: MidiJackDevice::open failed creating input port name %s\n", cs); + _readEnable = false; + in_fail = true; } + else + { + _readEnable = true; + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(_in_client_jackport); + if(our_port_name) + { + // (We just registered the port. At this point, any existing persistent routes' jackPort SHOULD be 0.) + for(iRoute ir = _inRoutes.begin(); ir != _inRoutes.end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE) + continue; + const char* route_name = ir->persistentJackPortName; + if(!ir->jackPort) + ir->jackPort = MusEGlobal::audioDevice->findPort(route_name); + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + if(ir->jackPort) + MusEGlobal::audioDevice->connect(route_name, our_port_name); + } + } + } } } } else { + _readEnable = false; if(_in_client_jackport) { - MusEGlobal::audio->msgRemoveRoutes(Route(), Route(this, 0)); + DEBUG_PRST_ROUTES(stderr, "MusE: MidiJackDevice::open unregistering input port\n"); + for(iRoute ir = _inRoutes.begin(); ir != _inRoutes.end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE) + continue; + if(ir->jackPort) + { + // Before we nullify the jackPort, grab the latest valid name of the port. + MusEGlobal::audioDevice->portName(ir->jackPort, ir->persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); + ir->jackPort = 0; + } + } + MusEGlobal::audioDevice->unregisterPort(_in_client_jackport); + _in_client_jackport = NULL; } - _in_client_jackport = NULL; } - _writeEnable = bool(_openFlags & 1); - _readEnable = bool(_openFlags & 2); + if(out_fail && in_fail) + _state = QString("R+W Open fail"); + else if(out_fail) + _state = QString("Write open fail"); + else if(in_fail) + _state = QString("Read open fail"); + else + _state = QString("OK"); - return QString("OK"); + return _state; } //--------------------------------------------------------- @@ -230,31 +327,67 @@ printf("MidiJackDevice::close %s\n", name().toLatin1().constData()); #endif - // TODO: I don't really want to unregister the - // Jack midi ports because then we lose the connections - // to Jack every time we click the read/write lights - // or change a port's device. p3.3.55 + DEBUG_PRST_ROUTES(stderr, "MidiJackDevice::close %s\n", name().toLatin1().constData()); + // Disable immediately. + _writeEnable = _readEnable = false; + jack_port_t* i_jp = _in_client_jackport; + jack_port_t* o_jp = _out_client_jackport; + _in_client_jackport = 0; + _out_client_jackport = 0; + + DEBUG_PRST_ROUTES(stderr, "MidiJackDevice::close nullifying route jackPorts...\n"); - /* - if(_client_jackport) + for(iRoute ir = _outRoutes.begin(); ir != _outRoutes.end(); ++ir) { - int pf = jack_port_flags(_client_jackport); - - if(pf & JackPortIsOutput) - _nextOutIdNum--; - else - if(pf & JackPortIsInput) - _nextInIdNum--; - MusEGlobal::audioDevice->unregisterPort(_client_jackport); - _client_jackport = 0; - _writeEnable = false; - _readEnable = false; - return; + if(ir->type != Route::JACK_ROUTE) + continue; + if(ir->jackPort) + { + // Before we nullify the jackPort, grab the latest valid name of the port. + if(MusEGlobal::checkAudioDevice()) + MusEGlobal::audioDevice->portName(ir->jackPort, ir->persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); + ir->jackPort = 0; + } } - */ - - _writeEnable = false; - _readEnable = false; + for(iRoute ir = _inRoutes.begin(); ir != _inRoutes.end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE) + continue; + if(ir->jackPort) + { + // Before we nullify the jackPort, grab the latest valid name of the port. + if(MusEGlobal::checkAudioDevice()) + MusEGlobal::audioDevice->portName(ir->jackPort, ir->persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); + ir->jackPort = 0; + } + } + +// if(_in_client_jackport) +// { +// if(MusEGlobal::checkAudioDevice()) +// MusEGlobal::audioDevice->unregisterPort(_in_client_jackport); +// _in_client_jackport = 0; +// } +// if(_out_client_jackport) +// { +// if(MusEGlobal::checkAudioDevice()) +// MusEGlobal::audioDevice->unregisterPort(_out_client_jackport); +// _out_client_jackport = 0; +// } + + DEBUG_PRST_ROUTES(stderr, "MidiJackDevice::close unregistering our ports...\n"); + + if(i_jp) + { + if(MusEGlobal::checkAudioDevice()) + MusEGlobal::audioDevice->unregisterPort(i_jp); + } + if(o_jp) + { + if(MusEGlobal::checkAudioDevice()) + MusEGlobal::audioDevice->unregisterPort(o_jp); + } + _state = QString("Closed"); } //--------------------------------------------------------- @@ -265,8 +398,9 @@ { // If this device is not actually in use by the song, do not write any routes. // This prevents bogus routes from being saved and propagated in the med file. - if(midiPort() == -1) - return; + // Removed. Need to let routes be saved. + //if(midiPort() == -1) + // return; QString s; if(rwFlags() & 2) // Readable @@ -310,20 +444,16 @@ } } } - + //--------------------------------------------------------- -// putEvent +// pbForwardShiftFrames //--------------------------------------------------------- -/* FIX: if we fail to transmit the event, - * we return false (indicating OK). Otherwise - * it seems muse will retry forever - */ -bool MidiJackDevice::putMidiEvent(const MidiPlayEvent& /*event*/) +unsigned int MidiJackDevice::pbForwardShiftFrames() const { - return false; + return MusEGlobal::segmentSize; } - + //--------------------------------------------------------- // recordEvent //--------------------------------------------------------- @@ -337,7 +467,7 @@ if (MusEGlobal::midiInputTrace) { fprintf(stderr, "MidiIn Jack: <%s>: ", name().toLatin1().constData()); - event.dump(); + dumpMPEvent(&event); } int typ = event.type(); @@ -359,18 +489,18 @@ && ((p[1] == 0x7f) || (idin == 0x7f) || (p[1] == idin))) { if (p[2] == 0x06) { //mmcInput(p, n); - MusEGlobal::midiSeq->mmcInput(_port, p, n); + MusEGlobal::midiSyncContainer.mmcInput(_port, p, n); return; } if (p[2] == 0x01) { //mtcInputFull(p, n); - MusEGlobal::midiSeq->mtcInputFull(_port, p, n); + MusEGlobal::midiSyncContainer.mtcInputFull(_port, p, n); return; } } else if (p[0] == 0x7e) { //nonRealtimeSystemSysex(p, n); - MusEGlobal::midiSeq->nonRealtimeSystemSysex(_port, p, n); + MusEGlobal::midiSyncContainer.nonRealtimeSystemSysex(_port, p, n); return; } } @@ -424,15 +554,19 @@ } //--------------------------------------------------------- -// midiReceived +// eventReceived //--------------------------------------------------------- void MidiJackDevice::eventReceived(jack_midi_event_t* ev) { + if(ev->size == 0) + return; + MidiRecordEvent event; event.setB(0); event.setPort(_port); - + jack_nframes_t abs_ft = 0; + // NOTE: From muse_qt4_evolution. Not done here in Muse-2 (yet). // move all events 2*MusEGlobal::segmentSize into the future to get // jitterfree playback @@ -445,53 +579,80 @@ // 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. + // position, so that by the time of the NEXT process, THOSE events have also occurred 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); + abs_ft = MusEGlobal::audio->previousPos().frame() + ev->time; #else - event.setTime(MusEGlobal::audio->pos().frame() + ev->time); + // The events arrived in the previous cycle, not this one. Adjust. + abs_ft = MusEGlobal::audio->curSyncFrame() + ev->time; + if(abs_ft >= MusEGlobal::segmentSize) + abs_ft -= MusEGlobal::segmentSize; #endif + event.setTime(abs_ft); event.setTick(MusEGlobal::lastExtMidiSyncTick); event.setChannel(*(ev->buffer) & 0xf); - int type = *(ev->buffer) & 0xf0; - int a = *(ev->buffer + 1) & 0x7f; - int b = *(ev->buffer + 2) & 0x7f; + const int type = *(ev->buffer) & 0xf0; event.setType(type); switch(type) { case ME_NOTEON: + { + if(ev->size < 3) + return; + // Convert zero-velocity note ons to note offs as per midi spec. + if(*(ev->buffer + 2) == 0) + event.setType(ME_NOTEOFF); + } + // Fall through. + case ME_NOTEOFF: case ME_CONTROLLER: case ME_POLYAFTER: - event.setA(*(ev->buffer + 1)); - event.setB(*(ev->buffer + 2)); + if(ev->size < 3) + return; + event.setA(*(ev->buffer + 1) & 0x7f); + event.setB(*(ev->buffer + 2) & 0x7f); break; case ME_PROGRAM: case ME_AFTERTOUCH: - event.setA(*(ev->buffer + 1)); + if(ev->size < 2) + return; + event.setA(*(ev->buffer + 1) & 0x7f); break; case ME_PITCHBEND: - event.setA(((b << 7) + a) - 8192); + if(ev->size < 3) + return; + event.setA(( ((*(ev->buffer + 2) & 0x7f) << 7) + + (*(ev->buffer + 1) & 0x7f) ) + - 8192); break; case ME_SYSEX: { - int type = *(ev->buffer) & 0xff; + const int type = *(ev->buffer) & 0xff; switch(type) { case ME_SYSEX: - + #ifdef JACK_MIDI_DEBUG + // ---Diagnostics---: + fprintf(stderr, "MidiJackDevice::eventReceived SYSEX len:%u data: ", (unsigned int)ev->size); + for(unsigned int i = 0; i < ev->size && i < 16; ++i) + fprintf(stderr, "%0x ", ((unsigned char*)ev->buffer)[i]); + if(ev->size >= 16) + fprintf(stderr, "..."); + fprintf(stderr, "\n"); + #endif + // TODO: Deal with large sysex, which are broken up into chunks! // For now, do not accept if the last byte is not EOX, meaning it's a chunk with more chunks to follow. if(*(((unsigned char*)ev->buffer) + ev->size - 1) != ME_SYSEX_END) { - if(MusEGlobal::debugMsg) - printf("MidiJackDevice::eventReceived sysex chunks not supported!\n"); + fprintf(stderr, "MidiJackDevice::eventReceived sysex chunks not supported!\n"); return; } @@ -501,32 +662,37 @@ break; case ME_MTC_QUARTER: if(_port != -1) - MusEGlobal::midiSeq->mtcInputQuarter(_port, *(ev->buffer + 1)); + { + MusEGlobal::midiSyncContainer.mtcInputQuarter(_port, *(ev->buffer + 1)); + } return; case ME_SONGPOS: if(_port != -1) - MusEGlobal::midiSeq->setSongPosition(_port, *(ev->buffer + 1) | (*(ev->buffer + 2) << 7 )); // LSB then MSB + { + MusEGlobal::midiSyncContainer.setSongPosition(_port, *(ev->buffer + 1) | (*(ev->buffer + 2) << 7 )); // LSB then MSB + } return; //case ME_SONGSEL: //case ME_TUNE_REQ: //case ME_SENSE: case ME_CLOCK: + { + midiClockInput(abs_ft); + return; + } + case ME_START: + { + #ifdef JACK_MIDI_DEBUG + fprintf(stderr, "MidiJackDevice::eventReceived: START port:%d time:%u\n", _port, abs_ft); + #endif + } + // FALLTHROUGH + case ME_TICK: - case ME_START: case ME_CONTINUE: case ME_STOP: { - if(MusEGlobal::audioDevice && MusEGlobal::audioDevice->deviceType() == JACK_MIDI && _port != -1) - { - MusECore::JackAudioDevice* jad = static_cast(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); - } - } + MusEGlobal::midiSyncContainer.realtimeSystemInput(_port, type, 0.0); return; } //case ME_SYSEX_END: @@ -538,12 +704,10 @@ return; } } - //return; break; default: if(MusEGlobal::debugMsg) printf("MidiJackDevice::eventReceived unknown event 0x%02x\n", type); - //printf("MidiJackDevice::eventReceived unknown event 0x%02x size:%d buf:0x%02x 0x%02x 0x%02x ...0x%02x\n", type, ev->size, *(ev->buffer), *(ev->buffer + 1), *(ev->buffer + 2), *(ev->buffer + (ev->size - 1))); return; } @@ -583,78 +747,53 @@ } //--------------------------------------------------------- -// putEvent -// return true if event cannot be delivered -//--------------------------------------------------------- - -bool MidiJackDevice::putEvent(const MidiPlayEvent& ev) -{ - if(!_writeEnable || !_out_client_jackport) - return false; - - #ifdef JACK_MIDI_DEBUG - printf("MidiJackDevice::putEvent time:%d type:%d ch:%d A:%d B:%d\n", ev.time(), ev.type(), ev.channel(), ev.dataA(), ev.dataB()); - #endif - - bool rv = eventFifo.put(ev); - if(rv) - printf("MidiJackDevice::putEvent: port overflow\n"); - - return rv; -} - -//--------------------------------------------------------- // queueEvent // return true if successful //--------------------------------------------------------- -bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) +bool MidiJackDevice::queueEvent(const MidiPlayEvent& e, void* evBuffer) { // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). // No big deal if not. Not used for now. //int port = e.port(); - //if(port >= JACK_MIDI_CHANNELS) - // return false; - - if(!_out_client_jackport) + if(!_writeEnable || !evBuffer) return false; - void* pb = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize); - - //unsigned frameCounter = ->frameTime(); - int frameOffset = MusEGlobal::audio->getFrameOffset(); - unsigned pos = MusEGlobal::audio->pos().frame(); - int ft = e.time() - frameOffset - pos; - - if (ft < 0) - ft = 0; - if (ft >= (int)MusEGlobal::segmentSize) { - printf("MidiJackDevice::queueEvent: Event time:%d out of range. offset:%d ft:%d (seg=%d)\n", e.time(), frameOffset, ft, MusEGlobal::segmentSize); - if (ft > (int)MusEGlobal::segmentSize) - ft = MusEGlobal::segmentSize - 1; + + const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); + if(e.time() != 0 && e.time() < syncFrame) + fprintf(stderr, "MidiJackDevice::queueEvent() evTime:%u < syncFrame:%u!!\n", e.time(), syncFrame); + unsigned int ft = (e.time() < syncFrame) ? 0 : e.time() - syncFrame; + if (ft >= MusEGlobal::segmentSize) { + fprintf(stderr, "MidiJackDevice::queueEvent: Event time:%d out of range. syncFrame:%d ft:%d (seg=%d)\n", + e.time(), syncFrame, ft, MusEGlobal::segmentSize); + ft = MusEGlobal::segmentSize - 1; } #ifdef JACK_MIDI_DEBUG - printf("MidiJackDevice::queueEvent pos:%d fo:%d ft:%d time:%d type:%d ch:%d A:%d B:%d\n", pos, frameOffset, ft, e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); + fprintf(stderr, "MidiJackDevice::queueEvent pos:%d syncFrame:%d ft:%d time:%d type:%d ch:%d A:%d B:%d\n", + pos, syncFrame, ft, e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); #endif if (MusEGlobal::midiOutputTrace) { fprintf(stderr, "MidiOut: Jack: <%s>: ", name().toLatin1().constData()); - e.dump(); + dumpMPEvent(&e); } switch(e.type()) { + + + case ME_CONTROLLER: case ME_NOTEON: case ME_NOTEOFF: case ME_POLYAFTER: - case ME_CONTROLLER: case ME_PITCHBEND: { #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::queueEvent note on/off polyafter controller or pitch\n"); #endif - unsigned char* p = jack_midi_event_reserve(pb, ft, 3); + unsigned char* p = jack_midi_event_reserve(evBuffer, ft, 3); if (p == 0) { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::queueEvent NOTE CTL PAT or PB: buffer overflow, stopping until next cycle\n"); @@ -674,7 +813,7 @@ printf("MidiJackDevice::queueEvent program or aftertouch\n"); #endif - unsigned char* p = jack_midi_event_reserve(pb, ft, 2); + unsigned char* p = jack_midi_event_reserve(evBuffer, ft, 2); if (p == 0) { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::queueEvent PROG or AT: buffer overflow, stopping until next cycle\n"); @@ -693,7 +832,7 @@ const unsigned char* data = e.data(); int len = e.len(); - unsigned char* p = jack_midi_event_reserve(pb, ft, len+2); + unsigned char* p = jack_midi_event_reserve(evBuffer, ft, len+2); if (p == 0) { fprintf(stderr, "MidiJackDevice::queueEvent ME_SYSEX: buffer overflow, sysex too big, event lost\n"); @@ -707,8 +846,8 @@ return true; } p[0] = 0xf0; - p[len+1] = 0xf7; memcpy(p+1, data, len); + p[len+1] = 0xf7; } break; case ME_SONGPOS: @@ -717,7 +856,7 @@ printf("MidiJackDevice::queueEvent songpos %d\n", e.dataA()); #endif - unsigned char* p = jack_midi_event_reserve(pb, ft, 3); + unsigned char* p = jack_midi_event_reserve(evBuffer, ft, 3); if (p == 0) { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::queueEvent songpos: buffer overflow, stopping until next cycle\n"); @@ -739,7 +878,7 @@ printf("MidiJackDevice::queueEvent realtime %x\n", e.type()); #endif - unsigned char* p = jack_midi_event_reserve(pb, ft, 1); + unsigned char* p = jack_midi_event_reserve(evBuffer, ft, 1); if (p == 0) { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::queueEvent realtime: buffer overflow, stopping until next cycle\n"); @@ -763,11 +902,8 @@ // return true if successful //--------------------------------------------------------- -bool MidiJackDevice::processEvent(const MidiPlayEvent& event) +bool MidiJackDevice::processEvent(const MidiPlayEvent& event, void* evBuffer) { - //int frameOffset = MusEGlobal::audio->getFrameOffset(); - //unsigned pos = MusEGlobal::audio->pos().frame(); - int chn = event.channel(); unsigned t = event.time(); int a = event.dataA(); @@ -775,120 +911,144 @@ // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). // No big deal if not. Not used for now. int port = event.port(); - - // TODO: No sub-tick playback resolution yet, with external sync. - // Just do this 'standard midi 64T timing thing' for now until we figure out more precise external timings. - // Does require relatively short audio buffers, in order to catch the resolution, but buffer <= 256 should be OK... - // Tested OK so far with 128. - //if(MusEGlobal::extSyncFlag.value()) - // p4.0.15 Or, is the event marked to be played immediately? - // Nothing to do but stamp the event to be queued for frame 0+. - if(t == 0 || MusEGlobal::extSyncFlag.value()) - t = MusEGlobal::audio->getFrameOffset() + MusEGlobal::audio->pos().frame(); - //t = frameOffset + pos; - + #ifdef JACK_MIDI_DEBUG //printf("MidiJackDevice::processEvent time:%d type:%d ch:%d A:%d B:%d\n", t, event.type(), chn, a, b); #endif - if(event.type() == ME_PROGRAM) + MidiInstrument::NoteOffMode nom = MidiInstrument::NoteOffAll; // Default to NoteOffAll in case of no port. + const int mport = midiPort(); + if(mport != -1) + { + if(MidiInstrument* mi = MusEGlobal::midiPorts[mport].instrument()) + nom = mi->noteOffMode(); + } + + // REMOVE Tim. Noteoff. Added. + if(event.type() == ME_NOTEON) { + if(b == 0) + { + // Handle zero-velocity note ons. Technically this is an error because internal midi paths + // are now all 'note-off' without zero-vel note ons - they're converted to note offs. + // Nothing should be setting a Note type Event's on velocity to zero. + // But just in case... If we get this warning, it means there is still code to change. + fprintf(stderr, "MidiJackDevice::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", t, event.type(), chn, a, b); + switch(nom) + { + // Instrument uses note offs. Convert to zero-vel note off. + case MidiInstrument::NoteOffAll: + return queueEvent(MidiPlayEvent(t, port, chn, ME_NOTEOFF, a, 0), evBuffer); + break; + + // Instrument uses no note offs at all. Send as-is. + case MidiInstrument::NoteOffNone: + // Instrument converts all note offs to zero-vel note ons. Send as-is. + case MidiInstrument::NoteOffConvertToZVNoteOn: + return queueEvent(event, evBuffer); + break; + } + } + return queueEvent(event, evBuffer); + } + else if(event.type() == ME_NOTEOFF) + { + switch(nom) + { + // Instrument uses note offs. Send as-is. + case MidiInstrument::NoteOffAll: + return queueEvent(event, evBuffer); + break; + + // Instrument uses no note offs at all. Send nothing. Eat up the event - return true. + case MidiInstrument::NoteOffNone: + return true; + + // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. + case MidiInstrument::NoteOffConvertToZVNoteOn: + return queueEvent(MidiPlayEvent(t, port, chn, ME_NOTEON, a, 0), evBuffer); + break; + } + return queueEvent(event, evBuffer); + } + + else if(event.type() == ME_PROGRAM) + { + //_curOutParamNums[chn].resetParamNums(); // Probably best to reset. // don't output program changes for GM drum channel //if (!(MusEGlobal::song->mtype() == MT_GM && chn == 9)) { - int hb = (a >> 16) & 0xff; - int lb = (a >> 8) & 0xff; - int pr = a & 0x7f; - - //printf("MidiJackDevice::processEvent ME_PROGRAM time:%d type:%d ch:%d A:%d B:%d hb:%d lb:%d pr:%d\n", - // event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); - - if (hb != 0xff) - { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb))) - return false; // p4.0.15 Inform that processing the event in general failed. Ditto all below... -/// t += 1; - } - if (lb != 0xff) - { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LBANK, lb))) - return false; -/// t += 1; - } - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, pr, 0))) + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. + _curOutParamNums[chn].setPROG(a); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, a, 0), evBuffer)) return false; - + // } } - else - if(event.type() == ME_PITCHBEND) + else if(event.type() == ME_PITCHBEND) { int v = a + 8192; //printf("MidiJackDevice::processEvent ME_PITCHBEND v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f), evBuffer)) return false; } - else - if(event.type() == ME_CONTROLLER) + else if(event.type() == ME_SYSEX) + { + resetCurOutParamNums(); // Probably best to reset all. + if(!queueEvent(event, evBuffer)) + return false; + } + else if(event.type() == ME_CONTROLLER) { // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). // No big deal if not. Not used for now. //int port = event.port(); - int nvh = 0xff; - int nvl = 0xff; - if(_port != -1) - { - int nv = MusEGlobal::midiPorts[_port].nullSendValue(); - if(nv != -1) - { - nvh = (nv >> 8) & 0xff; - nvl = nv & 0xff; - } - } - if((a | 0xff) == CTRL_POLYAFTER) { //printf("MidiJackDevice::processEvent CTRL_POLYAFTER v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_POLYAFTER, a & 0x7f, b & 0x7f))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_POLYAFTER, a & 0x7f, b & 0x7f), evBuffer)) return false; } else if(a == CTRL_AFTERTOUCH) { //printf("MidiJackDevice::processEvent CTRL_AFTERTOUCH v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_AFTERTOUCH, b & 0x7f, 0))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_AFTERTOUCH, b & 0x7f, 0), evBuffer)) return false; } else if(a == CTRL_PITCH) { int v = b + 8192; //printf("MidiJackDevice::processEvent CTRL_PITCH v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f), evBuffer)) return false; } else if (a == CTRL_PROGRAM) { + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. // don't output program changes for GM drum channel //if (!(MusEGlobal::song->mtype() == MT_GM && chn == 9)) { int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; - int pr = b & 0x7f; + int pr = b & 0xff; //printf("MidiJackDevice::processEvent CTRL_PROGRAM time:%d type:%d ch:%d A:%d B:%d hb:%d lb:%d pr:%d\n", // event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); - + + _curOutParamNums[chn].setCurrentProg(pr, lb, hb); if (hb != 0xff) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb), evBuffer)) return false; -/// t += 1; } if (lb != 0xff) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LBANK, lb))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LBANK, lb), evBuffer)) + return false; + } + if (pr != 0xff) + { + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, pr, 0), evBuffer)) return false; -/// t += 1; } - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, pr, 0))) - return false; // } } @@ -900,13 +1060,34 @@ //sysex[1] = deviceId(); TODO FIXME p4.0.15 Grab the ID from midi port sync info. sysex[4] = b & 0x7f; sysex[5] = (b >> 7) & 0x7f; - if(!queueEvent(MidiPlayEvent(t, port, ME_SYSEX, sysex, 6))) + if(!queueEvent(MidiPlayEvent(t, port, ME_SYSEX, sysex, 6), evBuffer)) return false; } else if (a < CTRL_14_OFFSET) { // 7 Bit Controller + if(a == CTRL_HRPN) + _curOutParamNums[chn].setRPNH(b); + else if(a == CTRL_LRPN) + _curOutParamNums[chn].setRPNL(b); + else if(a == CTRL_HNRPN) + _curOutParamNums[chn].setNRPNH(b); + else if(a == CTRL_LNRPN) + _curOutParamNums[chn].setNRPNL(b); + else if(a == CTRL_HBANK) + { + _curOutParamNums[chn].setBANKH(b); + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. + } + else if(a == CTRL_LBANK) + { + _curOutParamNums[chn].setBANKL(b); + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. + } + else if(a == CTRL_RESET_ALL_CTRL) + _curOutParamNums[chn].resetParamNums(); // Probably best to reset. + //queueEvent(museport, MidiPlayEvent(t, port, chn, event)); - if(!queueEvent(event)) + if(!queueEvent(event, evBuffer)) return false; } else if (a < CTRL_RPN_OFFSET) @@ -915,102 +1096,125 @@ int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlH, dataH))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlH, dataH), evBuffer)) return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlL, dataL))) + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlL, dataL), evBuffer)) return false; } else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, b))) - return false; -/// t += 1; - - //t += 3; - // Select null parameters so that subsequent data controller events do not upset the last *RPN controller. - //sendNullRPNParams(chn, false); - if(nvh != 0xff) + int data = b & 0x7f; + if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setRPNH(ctrlH); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH), evBuffer)) + return false; + } + if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f))) + _curOutParamNums[chn].setRPNL(ctrlL); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL), evBuffer)) return false; -/// t += 1; } - if(nvl != 0xff) + if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setDATAH(data); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, data), evBuffer)) + return false; + } + + // Select null parameters so that subsequent data controller events do not upset the last *RPN controller. + if(MusEGlobal::config.midiSendNullParameters) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f))) + _curOutParamNums[chn].setRPNH(0x7f); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, 0x7f), evBuffer)) + return false; + + _curOutParamNums[chn].setRPNL(0x7f); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, 0x7f), evBuffer)) return false; - } + } } - //else if (a < CTRL_RPN14_OFFSET) else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, b))) - return false; -/// t += 1; - - //t += 3; - //sendNullRPNParams(chn, true); - if(nvh != 0xff) + int data = b & 0x7f; + if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setNRPNH(ctrlH); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH), evBuffer)) + return false; + } + if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f))) + _curOutParamNums[chn].setNRPNL(ctrlL); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL), evBuffer)) return false; -/// t += 1; } - if(nvl != 0xff) + if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f))) + _curOutParamNums[chn].setDATAH(data); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, data), evBuffer)) return false; - } + } + + if(MusEGlobal::config.midiSendNullParameters) + { + _curOutParamNums[chn].setNRPNH(0x7f); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, 0x7f), evBuffer)) + return false; + + _curOutParamNums[chn].setNRPNL(0x7f); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, 0x7f), evBuffer)) + return false; + } } + else if (a < CTRL_RPN14_OFFSET) // Unaccounted for internal controller + return false; else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL))) - return false; -/// t += 1; - - //t += 4; - //sendNullRPNParams(chn, false); - if(nvh != 0xff) + if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setRPNH(ctrlH); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH), evBuffer)) + return false; + } + if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f))) + _curOutParamNums[chn].setRPNL(ctrlL); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL), evBuffer)) return false; -/// t += 1; } - if(nvl != 0xff) + if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f))) + _curOutParamNums[chn].setDATAH(dataH); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH), evBuffer)) + return false; + } + if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setDATAL(dataL); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL), evBuffer)) + return false; + } + + if(MusEGlobal::config.midiSendNullParameters) + { + _curOutParamNums[chn].setRPNH(0x7f); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, 0x7f), evBuffer)) + return false; + + _curOutParamNums[chn].setRPNL(0x7f); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, 0x7f), evBuffer)) return false; - } + } } else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller @@ -1018,32 +1222,41 @@ int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH))) - return false; -/// t += 1; - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL))) - return false; -/// t += 1; - - //t += 4; - //sendNullRPNParams(chn, true); - if(nvh != 0xff) + if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setNRPNH(ctrlH); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH), evBuffer)) + return false; + } + if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f))) + _curOutParamNums[chn].setNRPNL(ctrlL); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL), evBuffer)) return false; -/// t += 1; } - if(nvl != 0xff) + if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f))) + _curOutParamNums[chn].setDATAH(dataH); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH), evBuffer)) + return false; + } + if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers) + { + _curOutParamNums[chn].setDATAL(dataL); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL), evBuffer)) + return false; + } + + if(MusEGlobal::config.midiSendNullParameters) + { + _curOutParamNums[chn].setNRPNH(0x7f); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, 0x7f), evBuffer)) + return false; + + _curOutParamNums[chn].setNRPNL(0x7f); + if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, 0x7f), evBuffer)) return false; - } + } } else { @@ -1055,7 +1268,7 @@ else { //queueEvent(MidiPlayEvent(t, port, chn, event)); - if(!queueEvent(event)) + if(!queueEvent(event, evBuffer)) return false; } @@ -1067,18 +1280,8 @@ // Called from audio thread only. //--------------------------------------------------------- -void MidiJackDevice::processMidi() +void MidiJackDevice::processMidi(unsigned int curFrame) { - //bool stop = stopPending; // Snapshots - //bool seek = seekPending; // - //seekPending = stopPending = false; - - processStuckNotes(); - - // Don't process if the device is not assigned to a port. - //if(_port == -1) - // return; - void* port_buf = 0; if(_out_client_jackport && _writeEnable) { @@ -1086,294 +1289,78 @@ jack_midi_clear_buffer(port_buf); } - int port = midiPort(); - MidiPort* mp = port == -1 ? 0 : &MusEGlobal::midiPorts[port]; - - /* - bool is_playing = MusEGlobal::audio->isPlaying(); // TODO Check this. It includes LOOP1 and LOOP2 besides PLAY. - //bool is_playing = MusEGlobal::audio->isPlaying() || MusEGlobal::audio->isStarting(); - int pos = MusEGlobal::audio->tickPos(); - bool ext_sync = MusEGlobal::extSyncFlag.value(); - - if(mp) - { - MidiSyncInfo& si = mp->syncInfo(); - if(stop) - { - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!ext_sync) - { - // Shall we check open flags? - //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) - //if(!(dev->openFlags() & 1)) - // return; - - // Send MMC stop... - if(si.MMCOut()) - { - unsigned char msg[mmcStopMsgLen]; - memcpy(msg, mmcStopMsg, mmcStopMsgLen); - msg[1] = si.idOut(); - putEvent(MidiPlayEvent(0, 0, ME_SYSEX, msg, mmcStopMsgLen)); - } - - // Send midi stop... - if(si.MRTOut()) - { - putEvent(MidiPlayEvent(0, 0, 0, ME_STOP, 0, 0)); - // Added check of option send continue not start. p3.3.31 - // Hmm, is this required? Seems to make other devices unhappy. - // (Could try now that this is in MidiDevice. p4.0.22 ) - //if(!si.sendContNotStart()) - // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); - } - } - } - - if(seek) - { - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!ext_sync) - { - // Send midi stop and song position pointer... - if(si.MRTOut()) - { - // Shall we check for device write open flag to see if it's ok to send?... - //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) - //if(!(openFlags() & 1)) - // continue; - putEvent(MidiPlayEvent(0, 0, 0, ME_STOP, 0, 0)); - // Hm, try scheduling these for after stuck notes scheduled below... - //putEvent(MidiPlayEvent(0, 0, 0, ME_SONGPOS, beat, 0)); - //if(is_playing) - // putEvent(MidiPlayEvent(0, 0, 0, ME_CONTINUE, 0, 0)); - } - } - } - } - - if(stop || (seek && is_playing)) - { - // Clear all notes and handle stuck notes... - _playEvents.clear(); - for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) - { - MidiPlayEvent ev = *i; - ev.setTime(0); // Schedule immediately. - putEvent(ev); - } - _stuckNotes.clear(); - } + // Get the state of the stop flag. + const bool do_stop = stopFlag(); - if(mp) + MidiPlayEvent buf_ev; + + // Transfer the user lock-free buffer events to the user sorted multi-set. + // False = don't use the size snapshot, but update it. + const unsigned int usr_buf_sz = eventBuffers(UserBuffer)->getSize(false); + for(unsigned int i = 0; i < usr_buf_sz; ++i) { - MidiSyncInfo& si = mp->syncInfo(); - // Try scheduling these now for after stuck notes scheduled above... - if(stop || seek) - { - // Reset sustain. - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) - if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) - putEvent(MidiPlayEvent(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0)); - } - if(seek) - { - // Send new song position. - if(!ext_sync && si.MRTOut()) - { - int beat = (pos * 4) / MusEGlobal::config.division; - putEvent(MidiPlayEvent(0, 0, 0, ME_SONGPOS, beat, 0)); - } - // Send new controller values. - MidiCtrlValListList* cll = mp->controller(); - for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) - { - MidiCtrlValList* vl = ivl->second; - iMidiCtrlVal imcv = vl->iValue(pos); - if(imcv != vl->end()) { - Part* p = imcv->second.part; - // Don't send if part or track is muted or off. - if(!p || p->mute()) - continue; - Track* track = p->track(); - if(track && (track->isMute() || track->off())) - continue; - unsigned t = (unsigned)imcv->first; - // Do not add values that are outside of the part. - if(t >= p->tick() && t < (p->tick() + p->lenTick())) - // Use sendEvent to get the optimizations and limiting. But force if there's a value at this exact position. - mp->sendEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val), imcv->first == pos); - } - } - // Send continue. - // REMOVE Tim. This is redundant and too early - Audio::startRolling already properly sends it when sync ready. - //if(is_playing && !ext_sync && si.MRTOut()) - // putEvent(MidiPlayEvent(0, 0, 0, ME_CONTINUE, 0, 0)); - } + if(eventBuffers(UserBuffer)->get(buf_ev)) + _outUserEvents.insert(buf_ev); } - */ - while(!eventFifo.isEmpty()) + // Transfer the playback lock-free buffer events to the playback sorted multi-set. + const unsigned int pb_buf_sz = eventBuffers(PlaybackBuffer)->getSize(false); + for(unsigned int i = 0; i < pb_buf_sz; ++i) { - MidiPlayEvent e(eventFifo.peek()); - //printf("MidiJackDevice::processMidi FIFO event time:%d type:%d ch:%d A:%d B:%d\n", e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); - // Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. p4.0.15 - if(port_buf && !processEvent(e)) - return; // Give up. The Jack buffer is full. Nothing left to do. - eventFifo.remove(); // Successfully processed event. Remove it from FIFO. + // Are we stopping? Just remove the item. + if(do_stop) + eventBuffers(PlaybackBuffer)->remove(); + // Otherwise get the item. + else if(eventBuffers(PlaybackBuffer)->get(buf_ev)) + _outPlaybackEvents.insert(buf_ev); } - //if(!(stop || (seek && is_playing))) - // processStuckNotes(); - - if(_playEvents.empty()) - return; - - iMPEvent i = _playEvents.begin(); - for(; i != _playEvents.end(); ++i) + // Are we stopping? + if(do_stop) { - //printf("MidiJackDevice::processMidi playEvent time:%d type:%d ch:%d A:%d B:%d\n", i->time(), i->type(), i->channel(), i->dataA(), i->dataB()); - // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. - if(mp && !mp->sendHwCtrlState(*i, true)) // Force the event to be sent. - continue; - - // Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. p4.0.15 - if(port_buf && !processEvent(*i)) - break; + // Transport has stopped, purge ALL further scheduled playback events now. + _outPlaybackEvents.clear(); + // Reset the flag. + setStopFlag(false); } - _playEvents.erase(_playEvents.begin(), i); -} - -/* -//--------------------------------------------------------- -// handleStop -//--------------------------------------------------------- - -void MidiJackDevice::handleStop() -{ - // If the device is not in use by a port, don't bother it. - if(_port == -1) - return; - - stopPending = true; // Trigger stop handling in processMidi. -// //--------------------------------------------------- -// // reset sustain -// //--------------------------------------------------- -// -// MidiPort* mp = &MusEGlobal::midiPorts[_port]; -// for(int ch = 0; ch < MIDI_CHANNELS; ++ch) -// { -// if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) -// { -// //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); -// MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); -// putEvent(ev); -// // Do sendEvent to get the optimizations - send only on a change of value. -// //mp->sendEvent(ev); -// } -// } + iMPEvent impe_pb = _outPlaybackEvents.begin(); + iMPEvent impe_us = _outUserEvents.begin(); + bool using_pb; -// //--------------------------------------------------- -// // send midi stop -// //--------------------------------------------------- -// -// // Don't send if external sync is on. The master, and our sync routing system will take care of that. -// if(!MusEGlobal::extSyncFlag.value()) -// { -// // Shall we check open flags? -// //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) -// //if(!(dev->openFlags() & 1)) -// // return; -// -// MidiSyncInfo& si = mp->syncInfo(); -// if(si.MMCOut()) -// mp->sendMMCStop(); -// -// if(si.MRTOut()) -// { -// // Send STOP -// mp->sendStop(); -// -// // Added check of option send continue not start. p3.3.31 -// // Hmm, is this required? Seems to make other devices unhappy. -// // (Could try now that this is in MidiDevice. p4.0.22 ) -// //if(!si.sendContNotStart()) -// // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / config.division); -// } -// } -} -*/ - -/* -//--------------------------------------------------------- -// handleSeek -//--------------------------------------------------------- - -void MidiJackDevice::handleSeek() -{ - // If the device is not in use by a port, don't bother it. - if(_port == -1) - return; - - seekPending = true; // Trigger seek handling in processMidi. - - //MidiPort* mp = &MusEGlobal::midiPorts[_port]; - //MidiCtrlValListList* cll = mp->controller(); - //int pos = MusEGlobal::audio->tickPos(); - - //--------------------------------------------------- - // Send new contoller values - //--------------------------------------------------- + while(1) + { + if(impe_pb != _outPlaybackEvents.end() && impe_us != _outUserEvents.end()) + using_pb = *impe_pb < *impe_us; + else if(impe_pb != _outPlaybackEvents.end()) + using_pb = true; + else if(impe_us != _outUserEvents.end()) + using_pb = false; + else break; -// for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) -// { -// MidiCtrlValList* vl = ivl->second; -// iMidiCtrlVal imcv = vl->iValue(pos); -// if(imcv != vl->end()) -// { -// Part* p = imcv->second.part; -// //printf("MidiAlsaDevice::handleSeek _port:%d ctl:%d num:%d val:%d\n", _port, ivl->first >> 24, vl->num(), imcv->second.val); -// unsigned t = (unsigned)imcv->first; -// // Do not add values that are outside of the part. -// if(p && t >= p->tick() && t < (p->tick() + p->lenTick()) ) -// // Keep this and the section in processMidi() just in case we need to revert... -// //_playEvents.add(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); -// // Hmm, play event list for immediate playback? Try putEvent, putMidiEvent, or sendEvent (for the optimizations) instead. -// mp->sendEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); -// } -// } - - //--------------------------------------------------- - // Send STOP and "set song position pointer" - //--------------------------------------------------- + const MidiPlayEvent& ev = using_pb ? *impe_pb : *impe_us; -// // Don't send if external sync is on. The master, and our sync routing system will take care of that. p3.3.31 -// if(!MusEGlobal::extSyncFlag.value()) -// { -// if(mp->syncInfo().MRTOut()) -// { -// // Shall we check for device write open flag to see if it's ok to send?... -// // This means obey what the user has chosen for read/write in the midi port config dialog, -// // which already takes into account whether the device is writable or not. -// //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) -// //if(!(openFlags() & 1)) -// // continue; -// -// int beat = (pos * 4) / MusEGlobal::config.division; -// -// //bool isPlaying = (state == PLAY); -// bool isPlaying = MusEGlobal::audio->isPlaying(); // TODO Check this it includes LOOP1 and LOOP2 besides PLAY. p4.0.22 -// -// mp->sendStop(); -// mp->sendSongpos(beat); -// // REMOVE Tim. This is redundant and too early - Audio::startRolling already properly sends it when sync ready. -// //if(isPlaying) -// // mp->sendContinue(); -// } -// } + if(ev.time() >= (curFrame + MusEGlobal::segmentSize)) + { + #ifdef JACK_MIDI_DEBUG + fprintf(stderr, "MusE: Jack midi: putted event is for future:%lu, breaking loop now\n", ev.time() - curFrame); + #endif + break; + } + + // If processEvent fails, although we would like to not miss events by keeping them + // until next cycle and trying again, that can lead to a large backup of events + // over a long time. So we'll just... miss them. + processEvent(ev, port_buf); + + // Successfully processed event. Remove it from FIFO. + // C++11. + if(using_pb) + impe_pb = _outPlaybackEvents.erase(impe_pb); + else + impe_us = _outUserEvents.erase(impe_us); + } } -*/ //--------------------------------------------------------- // initMidiJack diff -Nru muse-2.1.2/muse/driver/jackmidi.h muse-3.0.2+ds1/muse/driver/jackmidi.h --- muse-2.1.2/muse/driver/jackmidi.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/jackmidi.h 2017-12-17 21:07:38.000000000 +0000 @@ -33,6 +33,7 @@ #include "mididev.h" #include "route.h" +#include "mpevent.h" class QString; //class MidiFifo; @@ -57,38 +58,50 @@ jack_port_t* _in_client_jackport; jack_port_t* _out_client_jackport; + MPEventList _outPlaybackEvents; + MPEventList _outUserEvents; + //RouteList _routes; virtual QString open(); virtual void close(); //bool putEvent(int*); - bool processEvent(const MidiPlayEvent&); + // Return true if successful + // evBuffer is the Jack buffer. + bool processEvent(const MidiPlayEvent&, void* evBuffer); // Port is not midi port, it is the port(s) created for MusE. - bool queueEvent(const MidiPlayEvent&); + // evBuffer is the Jack buffer. + bool queueEvent(const MidiPlayEvent&, void* evBuffer); - virtual bool putMidiEvent(const MidiPlayEvent&); + //virtual bool putMidiEvent(const MidiPlayEvent&); // REMOVE Tim. //bool sendEvent(const MidiPlayEvent&); void eventReceived(jack_midi_event_t*); + protected: + // Returns the number of frames to shift forward output event scheduling times when putting events + // into the eventFifos. + virtual unsigned int pbForwardShiftFrames() const; + public: MidiJackDevice(const QString& name); virtual ~MidiJackDevice(); static MidiDevice* createJackMidiDevice(QString name = "", int rwflags = 3); // 1:Writable 2: Readable 3: Writable + Readable - virtual inline int deviceType() const { return JACK_MIDI; } + virtual inline MidiDeviceType deviceType() const { return JACK_MIDI; } virtual void setName(const QString&); //virtual void handleStop(); //virtual void handleSeek(); - virtual void processMidi(); + virtual void processMidi(unsigned int curFrame = 0); virtual void recordEvent(MidiRecordEvent&); - virtual bool putEvent(const MidiPlayEvent&); virtual void collectMidiEvents(); + // The meaning of the returned pointer depends on the driver. + // For Jack it returns the address of a Jack port, for ALSA it return the address of a snd_seq_addr_t. virtual void* inClientPort() { return (void*) _in_client_jackport; } virtual void* outClientPort() { return (void*) _out_client_jackport; } diff -Nru muse-2.1.2/muse/driver/rtaudio.cpp muse-3.0.2+ds1/muse/driver/rtaudio.cpp --- muse-2.1.2/muse/driver/rtaudio.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/rtaudio.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -0,0 +1,639 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "audio.h" +#include "audiodev.h" +#include "globals.h" +#include "song.h" +#include "driver/alsatimer.h" +#include "pos.h" +#include "gconfig.h" +#include "utils.h" + +#define DEBUG_RTAUDIO 0 + +#define MASTER_LEFT (void*)1 +#define MASTER_RIGHT (void*)2 + +namespace MusECore { + +//--------------------------------------------------------- +// RtAudioDevice +//--------------------------------------------------------- + +struct MuseRtAudioPort { + QString name; + float* buffer; +}; + +class RtAudioDevice : public AudioDevice { + + RtAudio *dac; + + public: + Audio::State state; + int _framePos; + unsigned _framesAtCycleStart; + double _timeAtCycleStart; + int playPos; + bool seekflag; + + QList outputPortsList; + QList inputPortsList; + + RtAudioDevice(bool forceDefault); + virtual ~RtAudioDevice() + { + + while (outputPortsList.size() > 0) { + MuseRtAudioPort *port = outputPortsList.takeFirst(); + free (port->buffer); + free (port); + } + + while (inputPortsList.size() > 0) { + MuseRtAudioPort *port = inputPortsList.takeFirst(); + free (port->buffer); + free (port); + } + + } + + virtual inline int deviceType() const { return RTAUDIO_AUDIO; } + + virtual bool start(int); + + virtual void stop (); + virtual int framePos() const { + return _framePos; + } + + // These are meant to be called from inside process thread only. + virtual unsigned framesAtCycleStart() const { return _framesAtCycleStart; } + virtual unsigned framesSinceCycleStart() const + { + unsigned f = lrint((curTime() - _timeAtCycleStart) * MusEGlobal::sampleRate); + // Safety due to inaccuracies. It cannot be after the segment, right? + if(f >= MusEGlobal::segmentSize) + f = MusEGlobal::segmentSize - 1; + return f; + } + + virtual float* getBuffer(void* port, unsigned long nframes) + { + if (nframes > MusEGlobal::segmentSize) { + + fprintf(stderr, "RtAudioDevice::getBuffer nframes > segment size\n"); + + exit(-1); + } + + return ((MuseRtAudioPort*)port)->buffer; + } + + virtual std::list outputPorts(bool, int) { + std::list outlist; + + foreach (MuseRtAudioPort *port, outputPortsList) { + + outlist.push_back(port->name); + } + + return outlist; + } + + virtual std::list inputPorts(bool, int) { + std::list inlist; + + foreach (MuseRtAudioPort *port, inputPortsList) { + + inlist.push_back(port->name); + } + + return inlist; + } + + virtual void registerClient() {} + + virtual const char* clientName() { return "RtAudio"; } + + virtual void* registerOutPort(const char* name, bool) { + + fprintf(stderr, "register output port [%s] length %d char %c\n", name, int(strlen(name)), name[strlen(name)-1] ); + + foreach (MuseRtAudioPort *port, outputPortsList) { + if (port->name == name) { + fprintf(stderr, "RtAudioDevice::registerOutPort - port [%s] already exists, return existing.", name); + return port; + } + } + + MuseRtAudioPort *port = new MuseRtAudioPort(); + port->name = name; + port->buffer = new float[MusEGlobal::segmentSize]; + memset(port->buffer, 0, MusEGlobal::segmentSize * sizeof(float)); + + outputPortsList.push_back(port); + return port; + } + + virtual void* registerInPort(const char* name, bool) { + + fprintf(stderr, "register input port [%s] length %d char %c\n", name, int(strlen(name)), name[strlen(name)-1] ); + + foreach (MuseRtAudioPort *port, inputPortsList) { + if (port->name == name) { + fprintf(stderr, "RtAudioDevice::registerInPort - port [%s] already exists, return existing.", name); + return port; + } + } + + MuseRtAudioPort *port = new MuseRtAudioPort(); + port->name = name; + port->buffer = new float[MusEGlobal::segmentSize]; + memset(port->buffer, 0, MusEGlobal::segmentSize * sizeof(float)); + + inputPortsList.push_back(port); + return port; + } + + float getDSP_Load() { + return 0.0f; + } + + virtual AudioDevice::PortType portType(void*) const { return AudioPort; } + virtual AudioDevice::PortDirection portDirection(void*) const { return OutputPort; } + virtual void unregisterPort(void*) {} + virtual bool connect(void* /*src*/, void* /*dst*/) { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::connect\n"); + return false; + } + virtual bool connect(const char* /*src*/, const char* /*dst*/) { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::connect2\n"); + return false; + } + virtual bool disconnect(void* /*src*/, void* /*dst*/) { return false; } + virtual bool disconnect(const char* /*src*/, const char* /*dst*/) { return false; } + virtual int connections(void* /* clientPort */) { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::connections\n"); + return 1; // always return nonzero, for now + } + virtual bool portConnectedTo(void*, const char*) { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::portConnectedTo\n"); + return false; + } + virtual bool portsCanDisconnect(void* /*src*/, void* /*dst*/) const { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::portCanDisconnect\n"); + return false; + } + virtual bool portsCanDisconnect(const char* /*src*/, const char* /*dst*/) const { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::portCanDisconnect2\n"); + return false; + } + virtual bool portsCanConnect(void* /*src*/, void* /*dst*/) const { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::portCanConnect\n"); + return false; + } + virtual bool portsCanConnect(const char* /*src*/, const char* /*dst*/) const { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::portCanConnect\n"); + return false; + } + virtual bool portsCompatible(void* /*src*/, void* /*dst*/) const { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::portCompatible\n"); + return false; + } + virtual bool portsCompatible(const char* /*src*/, const char* /*dst*/) const { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::portCompatible2\n"); + return false; + } + virtual void setPortName(void*, const char*) { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::setPortName\n"); + + } + virtual void* findPort(const char*) { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::findPort\n"); + return 0; + } + // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. + virtual char* portName(void*, char* str, int str_size, int /*preferred_name_or_alias*/ = -1) { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::portName %s\n", str); + if(str_size == 0) { + return 0; + } + str[0] = '\0'; + return str; + } + virtual const char* canonicalPortName(void*) { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::canonicalPortName\n"); + return 0; + } + virtual unsigned int portLatency(void* /*port*/, bool /*capture*/) const { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudio::portLatency\n"); + return 0; + } + + virtual int getState() { + return state; + } + + virtual unsigned getCurFrame() const { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudioDevice::getCurFrame %d\n", _framePos); + + return _framePos; + } + + virtual unsigned frameTime() const { + return lrint(curTime() * MusEGlobal::sampleRate); + } + + virtual double systemTime() const { + struct timeval t; + gettimeofday(&t, 0); + //fprintf(stderr, "%ld %ld\n", t.tv_sec, t.tv_usec); // Note I observed values coming out of order! Causing some problems. + return (double)((double)t.tv_sec + (t.tv_usec / 1000000.0)); + } + + virtual bool isRealtime() { return MusEGlobal::realTimeScheduling; } + virtual int realtimePriority() const { return 40; } + virtual void startTransport() { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudioDevice::startTransport playPos=%d\n", playPos); + state = Audio::PLAY; + } + virtual void stopTransport() { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudioDevice::stopTransport, playPos=%d\n", playPos); + state = Audio::STOP; + } + virtual int setMaster(bool) { return 1; } + + virtual void seekTransport(const Pos &p) + { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudioDevice::seekTransport frame=%d topos=%d\n",playPos, p.frame()); + seekflag = true; + //pos = n; + playPos = p.frame(); + + } + virtual void seekTransport(unsigned pos) { + if(DEBUG_RTAUDIO) + fprintf(stderr, "RtAudioDevice::seekTransport frame=%d topos=%d\n",playPos,pos); + seekflag = true; + //pos = n; + playPos = pos; + } + virtual void setFreewheel(bool) {} +}; + +RtAudioDevice* rtAudioDevice = 0; + +RtAudioDevice::RtAudioDevice(bool forceDefault) : AudioDevice() + { + fprintf(stderr, "Init RtAudioDevice\n"); + MusEGlobal::sampleRate = MusEGlobal::config.deviceAudioSampleRate; + MusEGlobal::segmentSize = MusEGlobal::config.deviceAudioBufSize; + + seekflag = false; + state = Audio::STOP; + //startTime = curTime(); + _framePos = 0; + _framesAtCycleStart = 0; + _timeAtCycleStart = 0.0; + playPos = 0; + + RtAudio::Api api = RtAudio::UNSPECIFIED; + + switch (MusEGlobal::config.deviceAudioBackend) { + case MusEGlobal::RtAudioChoice: + api = RtAudio::UNSPECIFIED; + break; + case MusEGlobal::RtAudioAlsa: + api = RtAudio::LINUX_ALSA; + break; + case MusEGlobal::RtAudioPulse: + api = RtAudio::LINUX_PULSE; + break; + case MusEGlobal::RtAudioOss: + api = RtAudio::LINUX_OSS; + break; + //case MusEGlobal::RtAudioJack: + // api = RtAudio::UNIX_JACK; + //break; + default: + fprintf(stderr, "Error: RtAudio device selection illegal, setting up dummy audio backend!\n"); + api = RtAudio::RTAUDIO_DUMMY; + } + + if (forceDefault) { + + api = RtAudio::LINUX_PULSE; + } + + dac = new RtAudio(api); + if ( dac->getDeviceCount() < 1 ) { + + fprintf (stderr, "\nNo audio devices found!\n"); + QMessageBox::warning(NULL,"No sound device.","RtAudio did not find any audio device - run muse in midi-only mode if there is audio capable device.", QMessageBox::Ok); + } +} + + +//--------------------------------------------------------- +// exitRtAudio +//--------------------------------------------------------- + +void exitRtAudio() +{ + if(rtAudioDevice) + delete rtAudioDevice; + rtAudioDevice = NULL; + MusEGlobal::audioDevice = NULL; +} + + +//--------------------------------------------------------- +// initRtAudio +//--------------------------------------------------------- + +bool initRtAudio(bool forceDefault = false) +{ + rtAudioDevice = new RtAudioDevice(forceDefault); + MusEGlobal::audioDevice = rtAudioDevice; + return false; +} + +//--------------------------------------------------------- +// processAudio +//--------------------------------------------------------- +int processAudio( void * outputBuffer, void *inputBuffer, unsigned int nBufferFrames, + double /* streamTime */, RtAudioStreamStatus /* status */, void * /* userData */ ) +{ +// fprintf(stderr, "RtAduioDevice::processAudio %d\n", nBufferFrames); + + float *floatOutputBuffer = (float*)outputBuffer; + float *floatInputBuffer = (float*)inputBuffer; + + rtAudioDevice->_framePos += nBufferFrames; + rtAudioDevice->_framesAtCycleStart += nBufferFrames; + + if(rtAudioDevice->seekflag) + { + MusEGlobal::audio->sync(Audio::STOP, rtAudioDevice->playPos); + + rtAudioDevice->seekflag = false; + } + + if(rtAudioDevice->state == Audio::PLAY) { + + rtAudioDevice->playPos += nBufferFrames; + } + + if (MusEGlobal::audio->isRunning()) { + + MusEGlobal::audio->process((unsigned long)nBufferFrames); + } + + if (rtAudioDevice->outputPortsList.size() >= 2) { + + MuseRtAudioPort *left = rtAudioDevice->outputPortsList.at(0); + MuseRtAudioPort *right= rtAudioDevice->outputPortsList.at(1); + + // copy buffers into output + for (unsigned int i = 0; i < nBufferFrames; i++ ) { + + floatOutputBuffer[i*2] = left->buffer[i]; + floatOutputBuffer[i*2+1] = right->buffer[i]; + } + } else { + + //fprintf(stderr, "Too few ports in list, won't copy any data\n"); + } + + if (rtAudioDevice->inputPortsList.size() >= 1) { + + MuseRtAudioPort *left = rtAudioDevice->inputPortsList.at(0); + MuseRtAudioPort *right = NULL; + if (rtAudioDevice->inputPortsList.size() >= 2) { + right= rtAudioDevice->inputPortsList.at(1); + } + +// if (left->buffer[0] > 0.001) { +// fprintf(stderr, "Got non zero buffer value %f\n", left->buffer[0]); +// } + + // copy buffers into input + for (unsigned int i = 0; i < nBufferFrames; i++ ) { + + left->buffer[i] = floatInputBuffer[i*2]; + + if (right != NULL) { + right->buffer[i] = floatInputBuffer[i*2+1]; + } + } + + } else { + + //fprintf(stderr, "Too few ports in list, won't copy any data\n"); + } + + + return 0; +} + +//--------------------------------------------------------- +// start +//--------------------------------------------------------- +bool RtAudioDevice::start(int /* priority */) +{ + if (dac->isStreamRunning()) { + stop(); + } + + RtAudio::StreamParameters outParameters; + outParameters.deviceId = dac->getDefaultOutputDevice(); + outParameters.nChannels = 2; + outParameters.firstChannel = 0; + + RtAudio::StreamParameters inParameters; + inParameters.deviceId = dac->getDefaultInputDevice(); + inParameters.nChannels = 2; + inParameters.firstChannel = 0; + + unsigned int fin_sr = MusEGlobal::sampleRate; + + RtAudio::DeviceInfo in_di = dac->getDeviceInfo(inParameters.deviceId); + RtAudio::DeviceInfo out_di = dac->getDeviceInfo(outParameters.deviceId); + + if(!in_di.probed || !out_di.probed) + { + fprintf(stderr, "Error: RtAudioDevice: Could not probe device info.\n"); + } + else + { + typedef std::vector::iterator t_isr; + + std::set sr_set; + typedef std::set::iterator t_isr_set; + + if(in_di.sampleRates.empty()) + { + for(t_isr isr_o = out_di.sampleRates.begin(); isr_o != out_di.sampleRates.end(); ++isr_o) + sr_set.insert(*isr_o); + } + else + { + if(out_di.sampleRates.empty()) + { + for(t_isr isr_i = in_di.sampleRates.begin(); isr_i != in_di.sampleRates.end(); ++isr_i) + sr_set.insert(*isr_i); + } + else + { + std::vector out_sr_tmp = out_di.sampleRates; + for(t_isr isr_i = in_di.sampleRates.begin(); isr_i != in_di.sampleRates.end(); ++isr_i) + { + for(t_isr isr_o = out_sr_tmp.begin(); isr_o != out_sr_tmp.end(); ++isr_o) + { + // Since we currently just use one openStream for both input and output, + // and openStream takes only one samplerate value, then we can only openStream + // if the samplerate is supported for both input and output... I guess? + if(*isr_o == *isr_i) + { + sr_set.insert(*isr_i); + // Done with this output samplerate. Remove it to speed up as we go. + out_sr_tmp.erase(isr_o); + break; + } + } + } + } + } + + if(sr_set.find(fin_sr) == sr_set.end()) + { + unsigned int near_low = 0; + unsigned int near_high = 0; + unsigned int sr; + for(t_isr_set isr = sr_set.begin(); isr != sr_set.end(); ++isr) + { + sr = *isr; + // Include the desired samplerate. + if(sr > fin_sr) + continue; + if(sr > near_low) + near_low = sr; + } + for(t_isr_set isr = sr_set.begin(); isr != sr_set.end(); ++isr) + { + sr = *isr; + // Include the desired samplerate. + if(sr < fin_sr) + continue; + if(near_high == 0 || sr < near_high) + near_high = sr; + } + + // Prefer the closest lower rate rather than highest to be safe, I suppose. + if(near_low == 0 && near_high == 0) + { + fprintf(stderr, "Error: RtAudioDevice: Unsupported samplerate for both in/out:%d. No other samplerates found! Trying 44100 anyway...\n", + MusEGlobal::sampleRate); + fin_sr = 44100; + } + else + { + if(near_low == 0) + fin_sr = near_high; + else + fin_sr = near_low; + fprintf(stderr, "Warning: RtAudioDevice: Unsupported samplerate for both in/out:%d. Using closest:%d\n", MusEGlobal::sampleRate, fin_sr); + } + } + } + + MusEGlobal::sampleRate = fin_sr; + + double data[2]; + + try { + + dac->openStream( &outParameters, &inParameters, RTAUDIO_FLOAT32, MusEGlobal::sampleRate, &MusEGlobal::segmentSize, &processAudio, (void *)&data ); + dac->startStream(); + + } catch ( RtAudioError& e ) { + + e.printMessage(); + fprintf(stderr, "Error: RtAudioDevice: Cannot open device for streaming!.\n"); + return false; + } + + return true; +} + +void RtAudioDevice::stop () +{ + try { + + if (dac->isStreamRunning()) { + dac->stopStream(); + } + + } catch (RtAudioError& e) { + + e.printMessage(); + } + if ( dac->isStreamOpen() ) { + + dac->closeStream(); + } +} + + +} // namespace MusECore diff -Nru muse-2.1.2/muse/driver/rtctimer.cpp muse-3.0.2+ds1/muse/driver/rtctimer.cpp --- muse-2.1.2/muse/driver/rtctimer.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/rtctimer.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -35,11 +35,11 @@ #include #include #include +#include #include "rtctimer.h" #include "globals.h" -#include "gconfig.h" namespace MusECore { @@ -54,7 +54,7 @@ close(timerFd); } -signed int RtcTimer::initTimer() +signed int RtcTimer::initTimer(unsigned long desiredFrequency) { if(TIMER_DEBUG) printf("RtcTimer::initTimer()\n"); @@ -67,11 +67,10 @@ timerFd = ::open("/dev/rtc", O_RDONLY); if (timerFd == -1) { fprintf(stderr, "fatal error: open /dev/rtc failed: %s\n", strerror(errno)); - fprintf(stderr, "hint: check if 'rtc' kernel module is loaded, or used by something else\n"); MusEGlobal::undoSetuid(); return timerFd; } - if (!setTimerFreq(MusEGlobal::config.rtcTicks)) { + if (!setTimerFreq(desiredFrequency)) { // unable to set timer frequency return -1; } @@ -85,21 +84,21 @@ return timerFd; } -unsigned int RtcTimer::setTimerResolution(unsigned int resolution) +unsigned long RtcTimer::setTimerResolution(unsigned long resolution) { if(TIMER_DEBUG) - printf("RtcTimer::setTimerResolution(%d)\n",resolution); + printf("RtcTimer::setTimerResolution(%lu)\n",resolution); /* The RTC can take power-of-two frequencies from 2 to 8196 Hz. * It doesn't really have a resolution as such. */ return 0; } -unsigned int RtcTimer::setTimerFreq(unsigned int freq) +unsigned long RtcTimer::setTimerFreq(unsigned long freq) { int rc = ioctl(timerFd, RTC_IRQP_SET, freq); if (rc == -1) { - fprintf(stderr, "RtcTimer::setTimerFreq(): cannot set freq %d on /dev/rtc: %s\n", freq, + fprintf(stderr, "RtcTimer::setTimerFreq(): cannot set freq %lu on /dev/rtc: %s\n", freq, strerror(errno)); fprintf(stderr, " precise timer not available, check file permissions and allowed RTC freq (/sys/class/rtc/rtc0/max_user_freq)\n"); return 0; @@ -107,7 +106,7 @@ return freq; } -unsigned int RtcTimer::getTimerResolution() +unsigned long RtcTimer::getTimerResolution() { /* The RTC doesn't really work with a set resolution as such. * Not sure how this fits into things yet. @@ -115,9 +114,9 @@ return 0; } -unsigned int RtcTimer::getTimerFreq() +unsigned long RtcTimer::getTimerFreq() { - unsigned int freq; + unsigned long freq; int rv = ioctl(timerFd, RTC_IRQP_READ, &freq); if (rv < 0) return 0; @@ -154,7 +153,7 @@ return true; } -unsigned int RtcTimer::getTimerTicks(bool /*printTicks*/)// prevent compiler warning: unused parameter +unsigned long RtcTimer::getTimerTicks(bool /*printTicks*/)// prevent compiler warning: unused parameter { if(TIMER_DEBUG) printf("getTimerTicks()\n"); diff -Nru muse-2.1.2/muse/driver/rtctimer.h muse-3.0.2+ds1/muse/driver/rtctimer.h --- muse-2.1.2/muse/driver/rtctimer.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/rtctimer.h 2017-12-04 21:01:18.000000000 +0000 @@ -42,15 +42,15 @@ RtcTimer(); virtual ~RtcTimer(); - virtual signed int initTimer(); - virtual unsigned int setTimerResolution(unsigned int resolution); - virtual unsigned int getTimerResolution(); - virtual unsigned int setTimerFreq(unsigned int tick); - virtual unsigned int getTimerFreq(); + virtual signed int initTimer(unsigned long desiredFrequency); + virtual unsigned long setTimerResolution(unsigned long resolution); + virtual unsigned long getTimerResolution(); + virtual unsigned long setTimerFreq(unsigned long tick); + virtual unsigned long getTimerFreq(); virtual bool startTimer(); virtual bool stopTimer(); - virtual unsigned int getTimerTicks(bool printTicks=false); + virtual unsigned long getTimerTicks(bool printTicks=false); private: int timerFd; diff -Nru muse-2.1.2/muse/driver/timerdev.h muse-3.0.2+ds1/muse/driver/timerdev.h --- muse-2.1.2/muse/driver/timerdev.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/driver/timerdev.h 2017-12-04 21:01:18.000000000 +0000 @@ -27,8 +27,6 @@ #ifndef __TIMERDEV_H__ #define __TIMERDEV_H__ -#include "alsa/asoundlib.h" - #define TIMER_DEBUG 0 namespace MusECore { @@ -43,18 +41,18 @@ Timer() {}; virtual ~Timer() {}; - virtual signed int initTimer() = 0; - virtual unsigned int setTimerResolution(unsigned int resolution) = 0; - virtual unsigned int getTimerResolution() = 0; - virtual unsigned int setTimerFreq(unsigned int freq) = 0; - virtual unsigned int getTimerFreq() = 0; + virtual signed int initTimer(unsigned long desiredFrequency) = 0; + virtual unsigned long setTimerResolution(unsigned long resolution) = 0; + virtual unsigned long getTimerResolution() = 0; + virtual unsigned long setTimerFreq(unsigned long freq) = 0; + virtual unsigned long getTimerFreq() = 0; virtual bool startTimer() = 0; virtual bool stopTimer() = 0; - virtual unsigned int getTimerTicks(bool printTicks = false) = 0; + virtual unsigned long getTimerTicks(bool printTicks = false) = 0; }; } // namespace MusECore -#endif //__ALSATIMER_H__ +#endif //__TIMERDEV_H__ diff -Nru muse-2.1.2/muse/dssihost.cpp muse-3.0.2+ds1/muse/dssihost.cpp --- muse-2.1.2/muse/dssihost.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/dssihost.cpp 2017-12-17 21:07:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: dssihost.cpp,v 1.15.2.16 2009/12/15 03:39:58 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License @@ -30,7 +30,7 @@ //#define DSSI_DEBUG_PROCESS // Support vst state saving/loading with vst chunks. -//#define DSSI_VST_CHUNK_SUPPORT +//#define DSSI_VST_CHUNK_SUPPORT #include #include @@ -49,6 +49,7 @@ #include "jackaudio.h" #include "midi.h" #include "midiport.h" +#include "minstrument.h" #include "stringparam.h" #include "plugin.h" #include "controlfifo.h" @@ -61,6 +62,7 @@ #include "globaldefs.h" #include "gconfig.h" #include "popupmenu.h" +#include "lock_free_buffer.h" namespace MusECore { @@ -123,7 +125,19 @@ if(is != MusEGlobal::synthis.end()) continue; - DssiSynth* s = new DssiSynth(fi, descr); + Plugin::PluginFeatures reqfeat = Plugin::NoFeatures; + if(LADSPA_IS_INPLACE_BROKEN(descr->LADSPA_Plugin->Properties)) + reqfeat |= Plugin::NoInPlaceProcessing; + + // Hack: Special flag required for example for control processing. + bool vst = false; + if(fi.completeBaseName() == QString("dssi-vst")) + { + vst = true; + reqfeat |= Plugin::FixedBlockSize; + } + + DssiSynth* s = new DssiSynth(fi, descr, vst, reqfeat); if(MusEGlobal::debugMsg) { @@ -225,12 +239,13 @@ // Synth.version = nil (no such field in ladspa, maybe try copyright instead) //--------------------------------------------------------- -DssiSynth::DssiSynth(QFileInfo& fi, const DSSI_Descriptor* d) : // ddskrjo removed const from QFileInfo - Synth(fi, QString(d->LADSPA_Plugin->Label), QString(d->LADSPA_Plugin->Name), QString(d->LADSPA_Plugin->Maker), QString()) +DssiSynth::DssiSynth(QFileInfo& fi, const DSSI_Descriptor* d, bool isDssiVst, Plugin::PluginFeatures reqFeatures) : // ddskrjo removed const from QFileInfo + Synth(fi, QString(d->LADSPA_Plugin->Label), QString(d->LADSPA_Plugin->Name), QString(d->LADSPA_Plugin->Maker), QString(), reqFeatures) { df = 0; handle = 0; dssi = 0; + _isDssiVst = isDssiVst; _hasGui = false; const LADSPA_Descriptor* descr = d->LADSPA_Plugin; @@ -263,13 +278,9 @@ } } - _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(descr->Properties); - - // Hack: Special flag required for example for control processing. - _isDssiVst = fi.completeBaseName() == QString("dssi-vst"); // Hack: Blacklist vst plugins in-place, configurable for now. if ((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace)) - _inPlaceCapable = false; + _requiredFeatures |= Plugin::NoInPlaceProcessing; } DssiSynth::~DssiSynth() @@ -327,7 +338,6 @@ iIdx.clear(); oIdx.clear(); rpIdx.clear(); - iUsedIdx.clear(); midiCtl2PortMap.clear(); port2MidiCtlMap.clear(); @@ -349,7 +359,6 @@ { ++_inports; iIdx.push_back(k); - iUsedIdx.push_back(false); // Start out with all false. } else if (LADSPA_IS_PORT_OUTPUT(pd)) { @@ -374,12 +383,9 @@ } } - _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(descr->Properties); - // Hack: Special flag required for example for control processing. - _isDssiVst = info.completeBaseName() == QString("dssi-vst"); // Hack: Blacklist vst plugins in-place, configurable for now. if((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace)) - _inPlaceCapable = false; + _requiredFeatures |= Plugin::NoInPlaceProcessing; } } @@ -415,16 +421,15 @@ return false; } -bool DssiSynthIF::guiVisible() const - { - return _gui && _gui->isVisible(); - } - //--------------------------------------------------------- // showNativeGui //--------------------------------------------------------- -void DssiSynthIF::showNativeGui(bool v) +void DssiSynthIF::showNativeGui(bool +#if defined(OSC_SUPPORT) +v +#endif +) { #ifdef OSC_SUPPORT @@ -438,29 +443,12 @@ } //--------------------------------------------------------- -// showGui -//--------------------------------------------------------- - -void DssiSynthIF::showGui(bool v) -{ - if (v) { - if (_gui == 0) - makeGui(); - _gui->show(); - } - else { - if (_gui) - _gui->hide(); - } -} - -//--------------------------------------------------------- // receiveEvent //--------------------------------------------------------- -MusECore::MidiPlayEvent DssiSynthIF::receiveEvent() +MidiPlayEvent DssiSynthIF::receiveEvent() { - return MusECore::MidiPlayEvent(); + return MidiPlayEvent(); } //--------------------------------------------------------- @@ -473,10 +461,10 @@ printf("DssiSynthIF::init\n"); #endif - synth = s; - const DSSI_Descriptor* dssi = synth->dssi; + _synth = s; + const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; - handle = ld->instantiate(ld, MusEGlobal::sampleRate); + _handle = ld->instantiate(ld, MusEGlobal::sampleRate); #ifdef OSC_SUPPORT _oscif.oscSetSynthIF(this); @@ -484,67 +472,85 @@ queryPrograms(); - int inports = synth->_inports; + int inports = _synth->_inports; if(inports != 0) { - int rv = posix_memalign((void**)&audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + int rv = posix_memalign((void**)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DssiSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioInSilenceBuf[q] = MusEGlobal::denormalBias; + } + else + memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); - audioInBuffers = new float*[inports]; + _audioInBuffers = new float*[inports]; for(int k = 0; k < inports; ++k) { - int rv = posix_memalign((void**)&audioInBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); + int rv = posix_memalign((void**)&_audioInBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DssiSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); - ld->connect_port(handle, synth->iIdx[k], audioInBuffers[k]); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioInBuffers[k][q] = MusEGlobal::denormalBias; + } + else + memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); + ld->connect_port(_handle, _synth->iIdx[k], _audioInBuffers[k]); } } - int outports = synth->_outports; + int outports = _synth->_outports; if(outports != 0) { - audioOutBuffers = new float*[outports]; + _audioOutBuffers = new float*[outports]; for(int k = 0; k < outports; ++k) { - int rv = posix_memalign((void**)&audioOutBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); + int rv = posix_memalign((void**)&_audioOutBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DssiSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); - ld->connect_port(handle, synth->oIdx[k], audioOutBuffers[k]); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioOutBuffers[k][q] = MusEGlobal::denormalBias; + } + else + memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); + ld->connect_port(_handle, _synth->oIdx[k], _audioOutBuffers[k]); } } - int controlPorts = synth->_controlInPorts; - int controlOutPorts = synth->_controlOutPorts; + int controlPorts = _synth->_controlInPorts; + int controlOutPorts = _synth->_controlOutPorts; if(controlPorts != 0) - controls = new Port[controlPorts]; + _controls = new Port[controlPorts]; else - controls = 0; + _controls = 0; if(controlOutPorts != 0) - controlsOut = new Port[controlOutPorts]; + _controlsOut = new Port[controlOutPorts]; else - controlsOut = 0; + _controlsOut = 0; - synth->midiCtl2PortMap.clear(); - synth->port2MidiCtlMap.clear(); + _synth->midiCtl2PortMap.clear(); + _synth->port2MidiCtlMap.clear(); int cip = 0; int cop = 0; - for (unsigned long k = 0; k < synth->_portCount; ++k) + for (unsigned long k = 0; k < _synth->_portCount; ++k) { LADSPA_PortDescriptor pd = ld->PortDescriptors[k]; @@ -556,13 +562,12 @@ { if (LADSPA_IS_PORT_INPUT(pd)) { - controls[cip].idx = k; + _controls[cip].idx = k; float val; ladspaDefaultValue(ld, k, &val); - controls[cip].val = val; - controls[cip].tmpVal = val; - controls[cip].enCtrl = true; - controls[cip].en2Ctrl = true; + _controls[cip].val = val; + _controls[cip].tmpVal = val; + _controls[cip].enCtrl = true; #ifdef DSSI_DEBUG printf("DssiSynthIF::init control port:%d port idx:%lu name:%s\n", cip, k, ld->PortNames[k]); @@ -573,7 +578,7 @@ int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) - ctlnum = dssi->get_midi_controller_for_port(handle, k); + ctlnum = dssi->get_midi_controller_for_port(_handle, k); // No controller number? Try to give it a unique one... if(ctlnum == DSSI_NONE) @@ -585,7 +590,7 @@ // If CC Controller7 is chosen we must make sure to use only non-common numbers. An already limited range // of 127 now becomes narrower. See the cool document midi-controllers.txt in the DSSI source for a // nice roundup of numbers and how to choose them and how they relate to synths and DSSI synths etc. ! - ctlnum = MusECore::CTRL_NRPN14_OFFSET + 0x2000 + cip; + ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + cip; } else { @@ -610,7 +615,7 @@ printf("DssiSynthIF::init is NRPN control\n"); #endif - ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET; + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; } } @@ -620,8 +625,8 @@ #endif // We have a controller number! Insert it and the DSSI port number into both maps. - synth->midiCtl2PortMap.insert(std::pair(ctlnum, cip)); - synth->port2MidiCtlMap.insert(std::pair(cip, ctlnum)); + _synth->midiCtl2PortMap.insert(std::pair(ctlnum, cip)); + _synth->port2MidiCtlMap.insert(std::pair(cip, ctlnum)); // Support a special block for dssi synth ladspa controllers. // Put the ID at a special block after plugins (far after). @@ -630,42 +635,47 @@ float min, max; ladspaControlRange(ld, k, &min, &max); CtrlList* cl; - CtrlListList* cll = ((MusECore::AudioTrack*)synti)->controller(); + CtrlListList* cll = track()->controller(); iCtrlList icl = cll->find(id); if (icl == cll->end()) { cl = new CtrlList(id); cll->add(cl); - cl->setCurVal(controls[cip].val); + cl->setCurVal(_controls[cip].val); } else { cl = icl->second; - controls[cip].val = cl->curVal(); + _controls[cip].val = cl->curVal(); } cl->setRange(min, max); cl->setName(QString(name)); cl->setValueType(ladspaCtrlValueType(ld, k)); cl->setMode(ladspaCtrlMode(ld, k)); - ld->connect_port(handle, k, &controls[cip].val); + ld->connect_port(_handle, k, &_controls[cip].val); ++cip; } else if (LADSPA_IS_PORT_OUTPUT(pd)) { - controlsOut[cop].idx = k; - controlsOut[cop].val = 0.0; - controlsOut[cop].tmpVal = 0.0; - controlsOut[cop].enCtrl = false; - controlsOut[cop].en2Ctrl = false; + const char* pname = ld->PortNames[k]; + if(pname == QString("latency") || pname == QString("_latency")) + { + _hasLatencyOutPort = true; + _latencyOutPort = cop; + } + _controlsOut[cop].idx = k; + _controlsOut[cop].val = 0.0; + _controlsOut[cop].tmpVal = 0.0; + _controlsOut[cop].enCtrl = false; #ifdef DSSI_DEBUG printf("DssiSynthIF::init control output port:%d port idx:%lu name:%s\n", cop, k, ld->PortNames[k]); #endif // Control outs are not handled but still must be connected to something. - ld->connect_port(handle, k, &controlsOut[cop].val); + ld->connect_port(_handle, k, &_controlsOut[cop].val); ++cop; } @@ -677,7 +687,7 @@ // Set current configuration values. if(dssi->configure) { - char *rv = dssi->configure(handle, DSSI_PROJECT_DIRECTORY_KEY, + char *rv = dssi->configure(_handle, DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::museProject.toLatin1().constData()); //MusEGlobal::song->projectPath() if(rv) @@ -689,7 +699,7 @@ for(ciStringParamMap r = synti->_stringParamMap.begin(); r != synti->_stringParamMap.end(); ++r) { rv = 0; - rv = dssi->configure(handle, r->first.c_str(), r->second.c_str()); + rv = dssi->configure(_handle, r->first.c_str(), r->second.c_str()); if(rv) { fprintf(stderr, "MusE: Warning: plugin config key: %s value: %s \"%s\"\n", r->first.c_str(), r->second.c_str(), rv); @@ -698,10 +708,6 @@ } } - // Set current program. - if(dssi->select_program) - doSelectProgram(handle, synti->_curBankL, synti->_curProgram); - // // For stored initial control values, let SynthI::initInstance() take care of that via ::setParameter(). // @@ -719,13 +725,15 @@ #ifdef DSSI_DEBUG printf("DssiSynthIF::DssiSynthIF\n"); #endif - synth = 0; - handle = NULL; - controls = 0; - controlsOut = 0; - audioInBuffers = 0; - audioInSilenceBuf = 0; - audioOutBuffers = 0; + _synth = 0; + _handle = NULL; + _controls = 0; + _controlsOut = 0; + _hasLatencyOutPort = false; + _latencyOutPort = 0; + _audioInBuffers = 0; + _audioInSilenceBuf = 0; + _audioOutBuffers = 0; } //--------------------------------------------------------- @@ -742,19 +750,19 @@ _oscif.oscSetSynthIF(NULL); #endif - if(synth) + if(_synth) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIF synth:%p\n", synth); #endif - if(synth->dssi) + if(_synth->dssi) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIF synth->dssi:%p\n", synth->dssi); #endif - if(synth->dssi->LADSPA_Plugin) + if(_synth->dssi->LADSPA_Plugin) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIFsynth->dssi->LADSPA_Plugin:%p\n", synth->dssi->LADSPA_Plugin); @@ -763,9 +771,9 @@ } } - if(synth && synth->dssi && synth->dssi->LADSPA_Plugin) + if(_synth && _synth->dssi && _synth->dssi->LADSPA_Plugin) { - const DSSI_Descriptor* dssi = synth->dssi; + const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; #ifdef DSSI_DEBUG @@ -778,37 +786,37 @@ printf("DssiSynthIF::~DssiSynthIF calling cleanup function\n"); #endif - descr->cleanup(handle); + descr->cleanup(_handle); } } - if(audioInBuffers) + if(_audioInBuffers) { - for(unsigned long i = 0; i < synth->_inports; ++i) + for(unsigned long i = 0; i < _synth->_inports; ++i) { - if(audioInBuffers[i]) - free(audioInBuffers[i]); + if(_audioInBuffers[i]) + free(_audioInBuffers[i]); } - delete[] audioInBuffers; + delete[] _audioInBuffers; } - if(audioInSilenceBuf) - free(audioInSilenceBuf); + if(_audioInSilenceBuf) + free(_audioInSilenceBuf); - if(audioOutBuffers) + if(_audioOutBuffers) { - for(unsigned long i = 0; i < synth->_outports; ++i) + for(unsigned long i = 0; i < _synth->_outports; ++i) { - if(audioOutBuffers[i]) - free(audioOutBuffers[i]); + if(_audioOutBuffers[i]) + free(_audioOutBuffers[i]); } - delete[] audioOutBuffers; + delete[] _audioOutBuffers; } - if(controls) - delete[] controls; + if(_controls) + delete[] _controls; - if(controlsOut) - delete[] controlsOut; + if(_controlsOut) + delete[] _controlsOut; } int DssiSynthIF::oldMidiStateHeader(const unsigned char** data) const @@ -819,45 +827,57 @@ } //--------------------------------------------------------- +// latency +//--------------------------------------------------------- + +float DssiSynthIF::latency() +{ + if(!_hasLatencyOutPort) + return 0.0; + return _controlsOut[_latencyOutPort].val; +} + + +//--------------------------------------------------------- // getParameter //--------------------------------------------------------- -float DssiSynthIF::getParameter(unsigned long n) const +double DssiSynthIF::getParameter(unsigned long n) const { - if(n >= synth->_controlInPorts) + if(n >= _synth->_controlInPorts) { - printf("DssiSynthIF::getParameter param number %lu out of range of ports:%lu\n", n, synth->_controlInPorts); + printf("DssiSynthIF::getParameter param number %lu out of range of ports:%lu\n", n, _synth->_controlInPorts); return 0.0; } - if(!controls) + if(!_controls) return 0.0; - return controls[n].val; + return _controls[n].val; } //--------------------------------------------------------- // getParameter //--------------------------------------------------------- -float DssiSynthIF::getParameterOut(unsigned long n) const +double DssiSynthIF::getParameterOut(unsigned long n) const { - if(n >= synth->_controlOutPorts) + if(n >= _synth->_controlOutPorts) { - printf("DssiSynthIF::getParameterOut param number %lu out of range of ports:%lu\n", n, synth->_controlOutPorts); + printf("DssiSynthIF::getParameterOut param number %lu out of range of ports:%lu\n", n, _synth->_controlOutPorts); return 0.0; } - if(!controlsOut) + if(!_controlsOut) return 0.0; - return controlsOut[n].val; + return _controlsOut[n].val; } //--------------------------------------------------------- // setParameter //--------------------------------------------------------- -void DssiSynthIF::setParameter(unsigned long n, float v) +void DssiSynthIF::setParameter(unsigned long n, double v) { addScheduledControlEvent(n, v, MusEGlobal::audio->curFrame()); } @@ -869,22 +889,22 @@ void DssiSynthIF::write(int level, Xml& xml) const { #ifdef DSSI_VST_CHUNK_SUPPORT - if(synth->dssi->getCustomData) + if(_synth->dssi->getCustomData) { //--------------------------------------------- // dump current state of synth //--------------------------------------------- - printf("dumping DSSI custom data! %p\n", synth->dssi->getCustomData); + printf("dumping DSSI custom data! %p\n", _synth->dssi->getCustomData); // this is only needed and supported if // we are talking to a VST plugin at the other end. - std::string name = synth->dssi->LADSPA_Plugin->Name; + std::string name = _synth->dssi->LADSPA_Plugin->Name; if ((name.length()> 4) && name.substr(name.length() - 4) == " VST") { - printf("is vst plugin, commencing data dump, apiversion=%d!\n", synth->dssi->DSSI_API_Version); + printf("is vst plugin, commencing data dump, apiversion=%d!\n", _synth->dssi->DSSI_API_Version); unsigned long len = 0; void* p = 0; - synth->dssi->getCustomData(handle,&p, &len); + _synth->dssi->getCustomData(_handle,&p, &len); if (len) { xml.tag(level++, "midistate version=\"%d\"", SYNTH_MIDI_STATE_SAVE_VERSION); xml.nput(level++, "_controlInPorts; ++c) - { - float f = controls[c].val; - xml.floatTag(level, "param", f); - } + for(unsigned long c = 0; c < _synth->_controlInPorts; ++c) + xml.doubleTag(level, "param", _controls[c].val); } //--------------------------------------------------------- @@ -1027,102 +1044,153 @@ // Return true if event pointer filled. //-------------------------------------------------------- -bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t* event) +bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) { - const DSSI_Descriptor* dssi = synth->dssi; + const DSSI_Descriptor* dssi = _synth->dssi; int chn = e.channel(); int a = e.dataA(); int b = e.dataB(); - - int len = e.len(); - char ca[len + 2]; - - ca[0] = 0xF0; - memcpy(ca + 1, (const char*)e.data(), len); - ca[len + 1] = 0xF7; - - len += 2; #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", e.type(), chn, a, b); #endif + const MidiInstrument::NoteOffMode nom = synti->noteOffMode(); + switch(e.type()) { - case MusECore::ME_NOTEON: + case ME_NOTEON: #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_NOTEON\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_NOTEON\n"); #endif snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; - if(b) - snd_seq_ev_set_noteon(event, chn, a, b); + + if(b == 0) + { + // Handle zero-velocity note ons. Technically this is an error because internal midi paths + // are now all 'note-off' without zero-vel note ons - they're converted to note offs. + // Nothing should be setting a Note type Event's on velocity to zero. + // But just in case... If we get this warning, it means there is still code to change. + fprintf(stderr, "DssiSynthIF::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", e.time(), e.type(), chn, a, b); + switch(nom) + { + // Instrument uses note offs. Convert to zero-vel note off. + case MidiInstrument::NoteOffAll: + //if(MusEGlobal::midiOutputTrace) + // fprintf(stderr, "MidiOut: DSSI: Following event will be converted to zero-velocity note off:\n"); + snd_seq_ev_set_noteoff(event, chn, a, 0); + break; + + // Instrument uses no note offs at all. Send as-is. + case MidiInstrument::NoteOffNone: + // Instrument converts all note offs to zero-vel note ons. Send as-is. + case MidiInstrument::NoteOffConvertToZVNoteOn: + snd_seq_ev_set_noteon(event, chn, a, b); + break; + } + } else - snd_seq_ev_set_noteoff(event, chn, a, 0); + snd_seq_ev_set_noteon(event, chn, a, b); + + + break; - case MusECore::ME_NOTEOFF: + case ME_NOTEOFF: #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_NOTEOFF\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_NOTEOFF\n"); #endif snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; - snd_seq_ev_set_noteoff(event, chn, a, 0); + + switch(nom) + { + // Instrument uses note offs. Send as-is. + case MidiInstrument::NoteOffAll: + snd_seq_ev_set_noteoff(event, chn, a, b); + break; + + // Instrument uses no note offs at all. Send nothing. Eat up the event - return false. + case MidiInstrument::NoteOffNone: + return false; + + // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. + case MidiInstrument::NoteOffConvertToZVNoteOn: + //if(MusEGlobal::midiOutputTrace) + // fprintf(stderr, "MidiOut: DSSI: Following event will be converted to zero-velocity note on:\n"); + snd_seq_ev_set_noteon(event, chn, a, 0); + break; + } + break; - case MusECore::ME_PROGRAM: + // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore. + case ME_PROGRAM: { - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_PROGRAM\n"); + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_PROGRAM\n"); #endif - - int bank = (a >> 8) & 0xff; - int prog = a & 0xff; - synti->_curBankH = 0; - synti->_curBankL = bank; - synti->_curProgram = prog; - - if(dssi->select_program) - doSelectProgram(handle, bank, prog); + int hb, lb; + synti->currentProg(chn, NULL, &lb, &hb); + synti->setCurrentProg(chn, a & 0xff, lb, hb); + doSelectProgram(_handle, hb, lb, a); // Event pointer not filled. Return false. return false; - } + } break; - case MusECore::ME_CONTROLLER: + case ME_CONTROLLER: { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER\n"); #endif - if((a == 0) || (a == 32)) + // Our internal hwCtrl controllers support the 'unknown' value. + // Don't send 'unknown' values to the driver. Ignore and return no error. + if(b == CTRL_VAL_UNKNOWN) return false; - - if(a == MusECore::CTRL_PROGRAM) + + if(a == CTRL_PROGRAM) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PROGRAM\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); #endif - int bank = (b >> 8) & 0xff; - int prog = b & 0xff; - - synti->_curBankH = 0; - synti->_curBankL = bank; - synti->_curProgram = prog; - - if(dssi->select_program) - doSelectProgram(handle, bank, prog); - + int hb = (b >> 16) & 0xff; + int lb = (b >> 8) & 0xff; + int pr = b & 0xff; + synti->setCurrentProg(chn, pr, lb, hb); + doSelectProgram(_handle, hb, lb, pr); // Event pointer not filled. Return false. return false; } - if(a == MusECore::CTRL_PITCH) + if(a == CTRL_HBANK) + { + int lb, pr; + synti->currentProg(chn, &pr, &lb, NULL); + synti->setCurrentProg(chn, pr, lb, b & 0xff); + doSelectProgram(_handle, b, lb, pr); + // Event pointer not filled. Return false. + return false; + } + + if(a == CTRL_LBANK) + { + int hb, pr; + synti->currentProg(chn, &pr, NULL, &hb); + synti->setCurrentProg(chn, pr, b & 0xff, hb); + doSelectProgram(_handle, hb, b, pr); + // Event pointer not filled. Return false. + return false; + } + + if(a == CTRL_PITCH) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PITCH\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); #endif snd_seq_ev_clear(event); @@ -1132,10 +1200,10 @@ return true; } - if(a == MusECore::CTRL_AFTERTOUCH) + if(a == CTRL_AFTERTOUCH) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_AFTERTOUCH\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_AFTERTOUCH\n"); #endif snd_seq_ev_clear(event); @@ -1145,10 +1213,10 @@ return true; } - if((a | 0xff) == MusECore::CTRL_POLYAFTER) + if((a | 0xff) == CTRL_POLYAFTER) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_POLYAFTER\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_POLYAFTER\n"); #endif snd_seq_ev_clear(event); @@ -1160,14 +1228,14 @@ const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; - MusECore::ciMidiCtl2LadspaPort ip = synth->midiCtl2PortMap.find(a); + ciMidiCtl2LadspaPort ip = _synth->midiCtl2PortMap.find(a); // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)? // NOTE: There's no way to tell which of these controllers is supported by the plugin. // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port. - if(ip == synth->midiCtl2PortMap.end()) + if(ip == _synth->midiCtl2PortMap.end()) { int ctlnum = a; - if(MusECore::midiControllerType(a) != MusECore::MidiController::Controller7) + if(midiControllerType(a) != MidiController::Controller7) return false; // Event pointer not filled. Return false. else { @@ -1189,20 +1257,20 @@ } unsigned long k = ip->second; - unsigned long i = controls[k].idx; + unsigned long i = _controls[k].idx; int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) - ctlnum = dssi->get_midi_controller_for_port(handle, i); + ctlnum = dssi->get_midi_controller_for_port(_handle, i); // No midi controller for the ladspa port? Send to ladspa control. if(ctlnum == DSSI_NONE) { // Sanity check. - if(k > synth->_controlInPorts) + if(k > _synth->_controlInPorts) return false; // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. - ctlnum = k + (MusECore::CTRL_NRPN14_OFFSET + 0x2000); + ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); } else { @@ -1228,7 +1296,7 @@ else if(DSSI_IS_NRPN(ctlnum)) { - ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET; + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; #ifdef DSSI_DEBUG printf("DssiSynthIF::processEvent is NRPN ctlnum:%x(h) %d(d)\n", ctlnum, ctlnum); @@ -1244,7 +1312,7 @@ #endif // Set the ladspa port value. - controls[k].val = val; + _controls[k].val = val; // Need to update the automation value, otherwise it overwrites later with the last automation value. if(id() != -1) @@ -1255,25 +1323,25 @@ return false; } break; - case MusECore::ME_PITCHBEND: + case ME_PITCHBEND: snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_pitchbend(event, chn, a); break; - case MusECore::ME_AFTERTOUCH: + case ME_AFTERTOUCH: snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_chanpress(event, chn, a); break; - case MusECore::ME_POLYAFTER: + case ME_POLYAFTER: snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_keypress(event, chn, a & 0x7f, b & 0x7f); break; - case MusECore::ME_SYSEX: + case ME_SYSEX: { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_SYSEX\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX\n"); #endif const unsigned char* data = e.data(); @@ -1290,8 +1358,10 @@ if(dssi->setCustomData) { printf("loading chunk from sysex %s!\n", data+9); - dssi->setCustomData(handle, (unsigned char*)(data+9) /* len of str*/,e.len()-9); - } + usleep(300000); + dssi->setCustomData(_handle, (unsigned char*)(data+9) /* len of str*/,e.len()-9); + usleep(300000); + } #else printf("support for vst chunks not compiled in!\n"); #endif @@ -1311,7 +1381,7 @@ if (QString((const char*)e.data()).startsWith("PARAMSAVE")) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_SYSEX PARAMSAVE\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX PARAMSAVE\n"); #endif unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE" @@ -1362,13 +1432,24 @@ //else { // NOTE: There is a limit on the size of a sysex. Got this: - // "DssiSynthIF::processEvent midi event is MusECore::ME_SYSEX" + // "DssiSynthIF::processEvent midi event is ME_SYSEX" // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding" // That might be ALSA doing that. + + const int len = e.len(); + char buf[len + 2]; + + buf[0] = 0xF0; + memcpy(buf + 1, e.data(), len); + buf[len + 1] = 0xF7; + snd_seq_ev_clear(event); + snd_seq_ev_set_sysex(event, len + 2, buf); event->queue = SND_SEQ_QUEUE_DIRECT; - snd_seq_ev_set_sysex(event, len, - (unsigned char*)ca); + + // NOTE: Don't move this out, 'buf' would go out of scope. + // Event was filled. Return true. + return true; } } break; @@ -1385,427 +1466,437 @@ //--------------------------------------------------------- // getData +// If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- -MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MPEventList* el, MusECore::iMPEvent start_event, unsigned pos, int ports, unsigned nframes, float** buffer) +bool DssiSynthIF::getData(MidiPort* /*mp*/, unsigned pos, int ports, unsigned nframes, float** buffer) { - // We may not be using nevents all at once - this will be just the maximum. - unsigned long nevents = el->size() + synti->eventFifo.getSize(); - snd_seq_event_t events[nevents]; - - int frameOffset = MusEGlobal::audio->getFrameOffset(); - unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); - - #ifdef DSSI_DEBUG_PROCESS - fprintf(stderr, "DssiSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu nevents:%lu\n", pos, ports, nframes, syncFrame, nevents); + const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); + + #ifdef DSSI_DEBUG_PROCESS + fprintf(stderr, "DssiSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu\n", pos, ports, nframes, syncFrame); #endif - + // All ports must be connected to something! - unsigned long nop, k; - - nop = ((unsigned long) ports) > synth->_outports ? synth->_outports : ((unsigned long) ports); - - const DSSI_Descriptor* dssi = synth->dssi; + const unsigned long in_ports = _synth->inPorts(); + const unsigned long out_ports = _synth->outPorts(); + const unsigned long nop = ((unsigned long) ports) > out_ports ? out_ports : ((unsigned long) ports); + + const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; unsigned long sample = 0; - - // To remember the last retrieved value of each AudioTrack controller. - //float prev_ctrl_values[synth->_controlInPorts]; - - // NOTE Tested: Variable run-lengths worked superbly for LADSPA and DSSI synths. But DSSI-VST definitely - // does NOT like changing sample run length. It crashes the plugin and Wine (but MusE keeps running!). - // Furthermore, it resizes the shared memory (mmap, remap) upon each run length DIFFERENT from the last. + + // NOTE Tested: Variable run-lengths worked superbly for LADSPA and DSSI synths. But DSSI-VST definitely + // does NOT like changing sample run length. It crashes the plugin and Wine (but MusE keeps running!). + // Furthermore, it resizes the shared memory (mmap, remap) upon each run length DIFFERENT from the last. // And all of this done through client-server communications. It doesn't seem designed for this technique. // - // So we could support an alternate technique: A fixed control processing rate, in number of samples. + // So we could support an alternate technique: A fixed control processing rate, in number of samples. // - // Allow user to choose either a fixed rate or these 'packets' for LADSPA and DSSI plugins/synths, + // Allow user to choose either a fixed rate or these 'packets' for LADSPA and DSSI plugins/synths, // but make fixed-rate MANDATORY for DSSI-VST plugins and synths. - // + // // Or K.I.S.S - Just use fixed rates only, but allow it to be changed. I'm worried about libraries and // plugins other than DSSI-VST. What if they need the fixed-rate, too? // How to tell, and manage it all...? // But this 'packet' method sure seems to work nicely so far, so we'll throw it in... // // Must make this detectable for dssi vst synths, just like the plugins' in-place blacklist. - const bool usefixedrate = synth->_isDssiVst; // Try this. (was: true) - // TODO Make this number a global setting. - // Note for dssi-vst this MUST equal MusEGlobal::audio period. It doesn't like broken-up runs (it stutters), + const bool usefixedrate = (requiredFeatures() & Plugin::FixedBlockSize); + + // Note for dssi-vst this MUST equal MusEGlobal::audio period. It doesn't like broken-up runs (it stutters), // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length. - unsigned long fixedsize = nframes; // was: 2048 - // For now, the fixed size is clamped to the MusEGlobal::audio buffer size. // TODO: We could later add slower processing over several cycles - - // so that users can select a small MusEGlobal::audio period but a larger control period. - if(fixedsize > nframes) - fixedsize = nframes; - - unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; // Must be power of 2 ! - if(min_per > nframes) - min_per = nframes; - - #ifdef DSSI_DEBUG_PROCESS + // so that users can select a small MusEGlobal::audio period but a larger control period. + const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; + const unsigned long min_per_mask = min_per-1; // min_per must be power of 2 + + AudioTrack* atrack = track(); + const AutomationType at = atrack->automationType(); + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + const unsigned long in_ctrls = _synth->inControls(); + CtrlListList* cll = atrack->controller(); + ciCtrlList icl_first; + const int plug_id = id(); + if(plug_id != -1 && ports != 0) // Don't bother if not 'running'. + icl_first = cll->lower_bound(genACnum(plug_id, 0)); + + #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData: Handling inputs...\n"); #endif - - // Handle inputs... - if(!((MusECore::AudioTrack*)synti)->noInRoute()) - { - RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes(); - iRoute i = irl->begin(); - if(!i->track->isMidiTrack()) - { - int ch = i->channel == -1 ? 0 : i->channel; - int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; - int chs = i->channels == -1 ? 0 : i->channels; - - if((unsigned)ch < synth->_inports && (unsigned)(ch + chs) <= synth->_inports) - { - int h = remch + chs; - for(int j = remch; j < h; ++j) - synth->iUsedIdx[j] = true; - - ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &audioInBuffers[remch]); - } - } + + bool used_in_chan_array[in_ports]; // Don't bother initializing if not 'running'. - ++i; - for(; i != irl->end(); ++i) + // Don't bother if not 'running'. + if(ports != 0) + { + // Initialize the array. + for(unsigned long i = 0; i < in_ports; ++i) + used_in_chan_array[i] = false; + + if(!atrack->noInRoute()) { - if(i->track->isMidiTrack()) - continue; - - int ch = i->channel == -1 ? 0 : i->channel; - int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; - int chs = i->channels == -1 ? 0 : i->channels; - - if((unsigned)ch < synth->_inports && (unsigned)(ch + chs) <= synth->_inports) - { - bool u1 = synth->iUsedIdx[remch]; - if(chs >= 2) - { - bool u2 = synth->iUsedIdx[remch + 1]; - if(u1 && u2) - ((MusECore::AudioTrack*)i->track)->addData(pos, chs, ch, -1, nframes, &audioInBuffers[remch]); - else - if(!u1 && !u2) - ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &audioInBuffers[remch]); - else - { - if(u1) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, 1, nframes, &audioInBuffers[remch]); - else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, 1, nframes, &audioInBuffers[remch]); - - if(u2) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch + 1, 1, nframes, &audioInBuffers[remch + 1]); - else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch + 1, 1, nframes, &audioInBuffers[remch + 1]); - } - } - else - { - if(u1) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, -1, nframes, &audioInBuffers[remch]); - else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, -1, nframes, &audioInBuffers[remch]); - } - - int h = remch + chs; - for(int j = remch; j < h; ++j) - synth->iUsedIdx[j] = true; + RouteList *irl = atrack->inRoutes(); + for(ciRoute i = irl->begin(); i != irl->end(); ++i) + { + if(i->track->isMidiTrack()) + continue; + // Only this synth knows how many destination channels there are, + // while only the track knows how many source channels there are. + // So take care of the destination channels here, and let the track handle the source channels. + const int dst_ch = i->channel <= -1 ? 0 : i->channel; + if((unsigned long)dst_ch >= in_ports) + continue; + const int dst_chs = i->channels <= -1 ? in_ports : i->channels; + //const int total_ins = atrack->totalRoutableInputs(Route::TRACK_ROUTE); + const int src_ch = i->remoteChannel <= -1 ? 0 : i->remoteChannel; + const int src_chs = i->channels; + + int fin_dst_chs = dst_chs; + if((unsigned long)(dst_ch + fin_dst_chs) > in_ports) + fin_dst_chs = in_ports - dst_ch; + + static_cast(i->track)->copyData(pos, + dst_ch, dst_chs, fin_dst_chs, + src_ch, src_chs, + nframes, &_audioInBuffers[0], + false, used_in_chan_array); + const int nxt_ch = dst_ch + fin_dst_chs; + for(int ch = dst_ch; ch < nxt_ch; ++ch) + used_in_chan_array[ch] = true; } } - } + } - #ifdef DSSI_DEBUG_PROCESS + #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData: Processing automation control values...\n"); #endif - + + int cur_slice = 0; while(sample < nframes) { - unsigned long nsamp = usefixedrate ? fixedsize : nframes - sample; + unsigned long nsamp = nframes - sample; + const unsigned long slice_frame = pos + 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 + // 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) + if(ports != 0) // Don't bother if not 'running'. { - unsigned long frame = pos + sample; - AutomationType at = AUTO_OFF; - at = synti->automationType(); - bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; - AudioTrack* track = (static_cast(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) + ciCtrlList icl = icl_first; + for(unsigned long k = 0; k < in_ctrls; ++k) + { + CtrlList* cl = (cll && plug_id != -1 && icl != cll->end()) ? icl->second : NULL; + CtrlInterpolate& ci = _controls[k].interp; + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)) ) ) + { + if(cl && plug_id != -1 && (unsigned long)cl->id() == genACnum(plug_id, k)) + { + cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); + if(icl != cll->end()) + ++icl; + } + else + { + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = _controls[k].val; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; + } + } + else + { + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. + { + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + if(cl && cll && icl != cll->end()) + ++icl; + } + + if(!usefixedrate && MusEGlobal::audio->isPlaying()) { - // 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) + unsigned long samps = nsamp; + if(ci.eFrame != -1) + samps = (unsigned long)ci.eFrame - slice_frame; + + if(!ci.doInterp && samps > min_per) + { + samps &= ~min_per_mask; + if((samps & min_per_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); + + if(ci.doInterp && cl) + _controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); + else + _controls[k].val = ci.sVal; + +#ifdef DSSI_DEBUG_PROCESS + fprintf(stderr, "DssiSynthIF::getData k:%lu sample:%lu frame:%lu ci.eFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); #endif + + } } - + +#ifdef DSSI_DEBUG_PROCESS + fprintf(stderr, "DssiSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp); +#endif + bool found = false; - unsigned long frame = 0; + unsigned long frame = 0; unsigned long index = 0; - unsigned long evframe; + unsigned long evframe; // Get all control ring buffer items valid for this time period... while(!_controlFifo.isEmpty()) { - ControlEvent v = _controlFifo.peek(); - // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. + const ControlEvent& v = _controlFifo.peek(); + // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. - evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; + evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", found, evframe, frame, v.frame, v.idx, v.value, v.unique); #endif // Protection. Observed this condition. Why? Supposed to be linear timestamps. if(found && evframe < frame) { - printf("DssiSynthIF::getData *** Error: evframe:%lu < frame:%lu event: frame:%lu idx:%lu val:%f unique:%d\n", - evframe, frame, v.frame, v.idx, v.value, v.unique); + fprintf(stderr, "DssiSynthIF::getData *** Error: evframe:%lu < frame:%lu event: frame:%lu idx:%lu val:%f unique:%d\n", + evframe, frame, v.frame, v.idx, v.value, v.unique); // No choice but to ignore it. _controlFifo.remove(); // Done with the ring buffer's item. Remove it. continue; - } - - 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. + } + + 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. + if(v.idx >= in_ctrls) // Sanity check. + { + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. break; + } + found = true; frame = evframe; index = v.idx; - // Set the ladspa control port value. - controls[v.idx].val = v.value; - + + if(ports == 0) // Don't bother if not 'running'. + _controls[v.idx].val = v.value; // Might as well at least update these. + else + { + CtrlInterpolate* ci = &_controls[v.idx].interp; + // Tell it to stop the current ramp at this frame, when it does stop, set this value: + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; + } + // Need to update the automation value, otherwise it overwrites later with the last automation value. - if(id() != -1) - synti->setPluginCtrlVal(genACnum(id(), v.idx), v.value); + if(plug_id != -1) + synti->setPluginCtrlVal(genACnum(plug_id, v.idx), v.value); + + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. } - + 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; - + + if(sample + nsamp > nframes) // Safety check. + nsamp = nframes - sample; + // 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; - - nevents = 0; - // Process event list events... - for(; start_event != el->end(); ++start_event) + if(nsamp != 0) { - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::getData eventlist event time:%d pos:%u sample:%lu nsamp:%lu frameOffset:%d\n", start_event->time(), pos, sample, nsamp, frameOffset); - #endif + unsigned long nevents = 0; + // Get the state of the stop flag. + const bool do_stop = synti->stopFlag(); - if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again... + MidiPlayEvent buf_ev; + + // Transfer the user lock-free buffer events to the user sorted multi-set. + // False = don't use the size snapshot, but update it. + const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); + for(unsigned int i = 0; i < usr_buf_sz; ++i) { - #ifdef DSSI_DEBUG - fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample); - #endif - break; + if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) + synti->_outUserEvents.insert(buf_ev); } - // 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) + // Transfer the playback lock-free buffer events to the playback sorted multi-set. + const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); + for(unsigned int i = 0; i < pb_buf_sz; ++i) { - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[synti->midiPort()]; - if(start_event->type() == MusECore::ME_CONTROLLER) - { - int da = start_event->dataA(); - int db = start_event->dataB(); - db = mp->limitValToInstrCtlRange(da, db); - if(!mp->setHwCtrlState(start_event->channel(), da, db)) - continue; - } - else if(start_event->type() == MusECore::ME_PITCHBEND) - { - int da = mp->limitValToInstrCtlRange(MusECore::CTRL_PITCH, start_event->dataA()); - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PITCH, da)) - continue; - } - else if(start_event->type() == MusECore::ME_AFTERTOUCH) - { - int da = mp->limitValToInstrCtlRange(MusECore::CTRL_AFTERTOUCH, start_event->dataA()); - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_AFTERTOUCH, da)) - continue; - } - else if(start_event->type() == MusECore::ME_POLYAFTER) - { - int ctl = (MusECore::CTRL_POLYAFTER & ~0xff) | (start_event->dataA() & 0x7f); - int db = mp->limitValToInstrCtlRange(ctl, start_event->dataB()); - if(!mp->setHwCtrlState(start_event->channel(), ctl , db)) - continue; - } - else if(start_event->type() == MusECore::ME_PROGRAM) - { - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PROGRAM, start_event->dataA())) - continue; - } + // Are we stopping? Just remove the item. + if(do_stop) + synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); + // Otherwise get the item. + else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) + synti->_outPlaybackEvents.insert(buf_ev); } - - // Returns false if the event was not filled. It was handled, but some other way. - if(processEvent(*start_event, &events[nevents])) + + // Are we stopping? + if(do_stop) + { + // Transport has stopped, purge ALL further scheduled playback events now. + synti->_outPlaybackEvents.clear(); + // Reset the flag. + synti->setStopFlag(false); + } + + // Count how many events we need. + for(ciMPEvent impe = synti->_outPlaybackEvents.begin(); impe != synti->_outPlaybackEvents.end(); ++impe) + { + const MidiPlayEvent& e = *impe; + if(e.time() >= (syncFrame + sample + nsamp)) + break; + ++nevents; + } + for(ciMPEvent impe = synti->_outUserEvents.begin(); impe != synti->_outUserEvents.end(); ++impe) { - // Time-stamp the event. - int ft = start_event->time() - frameOffset - pos - sample; - if(ft < 0) - ft = 0; + const MidiPlayEvent& e = *impe; + if(e.time() >= (syncFrame + sample + nsamp)) + break; + ++nevents; + } + + snd_seq_event_t events[nevents]; + + iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); + iMPEvent impe_us = synti->_outUserEvents.begin(); + bool using_pb; + + unsigned long event_counter = 0; + while(1) + { + if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) + using_pb = *impe_pb < *impe_us; + else if(impe_pb != synti->_outPlaybackEvents.end()) + using_pb = true; + else if(impe_us != synti->_outUserEvents.end()) + using_pb = false; + else break; + + const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us; - if (ft >= int(nsamp)) + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::getData eventFifos event time:%d\n", e.time()); + #endif + + if(e.time() >= (syncFrame + sample + nsamp)) + break; + + if(ports != 0) // Don't bother if not 'running'. { - printf("DssiSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", start_event->time(), pos, frameOffset, ft, sample, nsamp); - ft = nsamp - 1; + // Returns false if the event was not filled. It was handled, but some other way. + if(processEvent(e, &events[event_counter])) + { + // Time-stamp the event. + unsigned int ft = (e.time() < syncFrame) ? 0 : e.time() - syncFrame; + ft = (ft < sample) ? 0 : ft - sample; + if (ft >= nsamp) + { + fprintf(stderr, "DssiSynthIF::getData: eventFifos event time:%d out of range. pos:%d syncFrame:%lu ft:%u sample:%lu nsamp:%lu\n", + e.time(), pos, syncFrame, ft, sample, nsamp); + ft = nsamp - 1; + } + // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. + // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h + events[event_counter].time.tick = ft; + + ++event_counter; + } } - - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::getData eventlist: ft:%d current nevents:%lu\n", ft, nevents); - #endif - - // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. - // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h - events[nevents].time.tick = ft; - ++nevents; + // Done with ring buffer's event. Remove it. + // C++11. + if(using_pb) + impe_pb = synti->_outPlaybackEvents.erase(impe_pb); + else + impe_us = synti->_outUserEvents.erase(impe_us); } - } - - // Now process putEvent events... - while(!synti->eventFifo.isEmpty()) - { - MusECore::MidiPlayEvent e = synti->eventFifo.peek(); + + if(event_counter < nevents) + nevents = event_counter; - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::getData eventFifo event time:%d\n", e.time()); + #ifdef DSSI_DEBUG_PROCESS + fprintf(stderr, "DssiSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); #endif - - if(e.time() >= (pos + sample + nsamp + frameOffset)) - break; - - synti->eventFifo.remove(); // Done with ring buffer's event. Remove it. - // Returns false if the event was not filled. It was handled, but some other way. - if(processEvent(e, &events[nevents])) - { - // Time-stamp the event. - int ft = e.time() - frameOffset - pos - sample; - if(ft < 0) - ft = 0; - if (ft >= int(nsamp)) - { - printf("DssiSynthIF::getData: eventFifo event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", e.time(), pos, frameOffset, ft, sample, nsamp); - ft = nsamp - 1; - } - // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. - // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h - events[nevents].time.tick = ft; - - ++nevents; - } - } - - #ifdef DSSI_DEBUG_PROCESS - fprintf(stderr, "DssiSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); - #endif - - k = 0; - // Connect the given buffers directly to the ports, up to a max of synth ports. - for(; k < nop; ++k) - descr->connect_port(handle, synth->oIdx[k], buffer[k] + sample); - // Connect the remaining ports to some local buffers (not used yet). - for(; k < synth->_outports; ++k) - descr->connect_port(handle, synth->oIdx[k], audioOutBuffers[k] + sample); - // Connect all inputs either to some local buffers, or a silence buffer. - for(k = 0; k < synth->_inports; ++k) - { - if(synth->iUsedIdx[k]) + + if(ports != 0) // Don't bother if not 'running'. { - synth->iUsedIdx[k] = false; // Reset - descr->connect_port(handle, synth->iIdx[k], audioInBuffers[k] + sample); + // Connect the given buffers directly to the ports, up to a max of synth ports. + for(unsigned long k = 0; k < nop; ++k) + descr->connect_port(_handle, _synth->oIdx[k], buffer[k] + sample); + // Connect the remaining ports to some local buffers (not used yet). + for(unsigned long k = nop; k < out_ports; ++k) + descr->connect_port(_handle, _synth->oIdx[k], _audioOutBuffers[k] + sample); + // Connect all inputs either to some local buffers, or a silence buffer. + for(unsigned long k = 0; k < in_ports; ++k) + { + if(used_in_chan_array[k]) + descr->connect_port(_handle, _synth->iIdx[k], _audioInBuffers[k] + sample); + else + descr->connect_port(_handle, _synth->iIdx[k], _audioInSilenceBuf + sample); + } + + // Run the synth for a period of time. This processes events and gets/fills our local buffers... + if(_synth->dssi->run_synth) + { + _synth->dssi->run_synth(_handle, nsamp, events, nevents); + } + else if (_synth->dssi->run_multiple_synths) + { + snd_seq_event_t* ev = events; + _synth->dssi->run_multiple_synths(1, &_handle, nsamp, &ev, &nevents); + } + // TIP: Until we add programs to plugins, uncomment these four checks to load dssi effects as synths, in order to have programs. + //else + //if(synth->dssi->LADSPA_Plugin->run) + //{ + // synth->dssi->LADSPA_Plugin->run(handle, nsamp); + //} } - else - { - descr->connect_port(handle, synth->iIdx[k], audioInSilenceBuf + sample); - } - } - - // Run the synth for a period of time. This processes events and gets/fills our local buffers... - if(synth->dssi->run_synth) - { - synth->dssi->run_synth(handle, nsamp, events, nevents); - } - else if (synth->dssi->run_multiple_synths) - { - snd_seq_event_t* ev = events; - synth->dssi->run_multiple_synths(1, &handle, nsamp, &ev, &nevents); + + sample += nsamp; } - // TIP: Until we add programs to plugins, uncomment these four checks to load dssi effects as synths, in order to have programs. - //else - //if(synth->dssi->LADSPA_Plugin->run) - //{ - // synth->dssi->LADSPA_Plugin->run(handle, nsamp); - //} - - sample += nsamp; - } - - return start_event; -} -//--------------------------------------------------------- -// putEvent -//--------------------------------------------------------- + ++cur_slice; // Slice is done. Moving on to any next slice now... + } -bool DssiSynthIF::putEvent(const MusECore::MidiPlayEvent& ev) - { - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::putEvent midi event time:%d chn:%d a:%d b:%d\n", ev.time(), ev.channel(), ev.dataA(), ev.dataB()); - #endif - if (MusEGlobal::midiOutputTrace) - ev.dump(); - return synti->eventFifo.put(ev); - } + return true; +} //--------------------------------------------------------- // incInstances @@ -1830,38 +1921,35 @@ iIdx.clear(); oIdx.clear(); rpIdx.clear(); - iUsedIdx.clear(); midiCtl2PortMap.clear(); port2MidiCtlMap.clear(); } } //--------------------------------------------------------- -// initGui -//--------------------------------------------------------- -bool DssiSynthIF::initGui() -{ - #ifdef OSC_SUPPORT - return _oscif.oscInitGui(); - #endif - return true; -} - -//--------------------------------------------------------- // guiHeartBeat //--------------------------------------------------------- void DssiSynthIF::guiHeartBeat() { #ifdef OSC_SUPPORT + int chn = 0; // TODO: Channel? + int hb, lb, pr; + synti->currentProg(chn, &pr, &lb, &hb); + if(hb > 127) // Map "dont care" to 0 + hb = 0; + if(lb > 127) + lb = 0; + if(pr > 127) + pr = 0; // Update the gui's program if needed. - _oscif.oscSendProgram(synti->_curProgram, synti->_curBankL); + _oscif.oscSendProgram(pr, (hb << 8) + lb); // Update the gui's controls if needed. - unsigned long ports = synth->_controlInPorts; + unsigned long ports = _synth->_controlInPorts; for(unsigned long i = 0; i < ports; ++i) - _oscif.oscSendControl(controls[i].idx, controls[i].val); + _oscif.oscSendControl(_controls[i].idx, _controls[i].val); #endif } @@ -1887,13 +1975,22 @@ } // Send current bank and program. - _oscif.oscSendProgram(synti->_curProgram, synti->_curBankL, true /*force*/); + int chn = 0; // TODO: Channel? + int hb, lb, pr; + synti->currentProg(chn, &pr, &lb, &hb); + if(hb > 127) // Map "dont care" to 0 + hb = 0; + if(lb > 127) + lb = 0; + if(pr > 127) + pr = 0; + _oscif.oscSendProgram(pr, (hb << 8) + lb, true /*force*/); // Send current control values. - unsigned long ports = synth->_controlInPorts; + unsigned long ports = _synth->_controlInPorts; for(unsigned long i = 0; i < ports; ++i) { - _oscif.oscSendControl(controls[i].idx, controls[i].val, true /*force*/); + _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) usleep(300000); @@ -1910,33 +2007,29 @@ { int ch = 0; // TODO: ?? int port = synti->midiPort(); + + // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. + int hb = bank >> 8; + int lb = bank & 0xff; + if(hb > 127 || lb > 127 || program > 127) + return 0; + hb &= 0x7f; + lb &= 0x7f; - synti->_curBankH = 0; - synti->_curBankL = bank; - synti->_curProgram = program; - - bank &= 0xff; - program &= 0xff; + synti->setCurrentProg(ch, program, lb, hb); if(port != -1) { - MusECore::MidiPlayEvent event(0, port, ch, MusECore::ME_PROGRAM, (bank << 8) + program, 0); + // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore. + const MidiPlayEvent event(0, port, ch, ME_CONTROLLER, CTRL_PROGRAM, (hb << 16) | (lb << 8) | program); #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::oscProgram midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB()); #endif - MusEGlobal::midiPorts[port].sendEvent(event); + MusEGlobal::midiPorts[port].putEvent(event); } - //synti->playMidiEvent(&event); // TODO DELETETHIS 7 hasn't changed since r462 - // - //MidiDevice* md = dynamic_cast(synti); - //if(md) - // md->putEvent(event); - // - //synti->putEvent(event); - return 0; } @@ -1950,14 +2043,14 @@ printf("DssiSynthIF::oscControl received oscControl port:%lu val:%f\n", port, value); #endif - if(port >= synth->rpIdx.size()) + if(port >= _synth->rpIdx.size()) { - fprintf(stderr, "DssiSynthIF::oscControl: port number:%lu is out of range of index list size:%zd\n", port, synth->rpIdx.size()); + fprintf(stderr, "DssiSynthIF::oscControl: port number:%lu is out of range of index list size:%zd\n", port, _synth->rpIdx.size()); return 0; } // Convert from DSSI port number to control input port index. - unsigned long cport = synth->rpIdx[port]; + unsigned long cport = _synth->rpIdx[port]; if((int)cport == -1) { @@ -1965,16 +2058,28 @@ return 0; } + // Record automation: + // Take care of this immediately, because we don't want the silly delay associated with + // processing the fifo one-at-a-time in the apply(). + // NOTE: With some vsts we don't receive control events until the user RELEASES a control. + // So the events all arrive at once when the user releases a control. + // That makes this pretty useless... But what the heck... + if(id() != -1) + { + unsigned long pid = genACnum(id(), cport); + synti->recordAutomation(pid, value); + } + // DELETETHIS????: is the below still correct? of so, then keep it of course! // p3.3.39 Set the DSSI control input port's value. // Observations: With a native DSSI synth like LessTrivialSynth, the native GUI's controls do not change the sound at all - // ie. they don't update the DSSI control port values themselves. + // ie. they don't update the DSSI control port values themselves. // Hence in response to the call to this oscControl, sent by the native GUI, it is required to that here. /// controls[cport].val = value; DELETETHIS // DSSI-VST synths however, unlike DSSI synths, DO change their OWN sound in response to their gui controls. - // AND this function is called. - // Despite the descrepency we are STILL required to update the DSSI control port values here - // because dssi-vst is WAITING FOR A RESPONSE. (A CHANGE in the control port value). + // AND this function is called. + // Despite the descrepency we are STILL required to update the DSSI control port values here + // because dssi-vst is WAITING FOR A RESPONSE. (A CHANGE in the control port value). // It will output something like "...4 events expected..." and count that number down as 4 actual control port value CHANGES // are done here in response. Normally it says "...0 events expected..." when MusE is the one doing the DSSI control changes. // @@ -1987,62 +2092,21 @@ // (Because the server simply ignores the 'expected' messages.) // // Well, at least here are the fifos. Try this ... - // DELETETHIS 20 pretty old as well - /* - OscControlFifo* cfifo = _oscif.oscFifo(cport); - if(cfifo) - { - OscControlValue cv; - //cv.idx = cport; - cv.value = value; - // Time-stamp the event. Looks like no choice but to use the (possibly slow) call to gettimeofday via timestamp(), - // because these are asynchronous events arriving from OSC. 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.) p4.0.15 Tim. - cv.frame = MusEGlobal::audio->timestamp(); - if(cfifo->put(cv)) - { - fprintf(stderr, "DssiSynthIF::oscControl: fifo overflow: in control number:%lu\n", cport); - } - } - */ - // p4.0.21 + + // Schedules a timed control change: ControlEvent ce; - ce.unique = synth->_isDssiVst; // Special for messages from vst gui to host - requires processing every message. - ce.fromGui = true; // It came form the plugin's own GUI. + ce.unique = _synth->isDssiVst(); // Special for messages from vst gui to host - requires processing every message. + ce.fromGui = true; // It came from the plugin's own GUI. ce.idx = cport; ce.value = value; - - ce.frame = MusEGlobal::audio->curFrame(); - // don't use timestamp(), because it's circular, which is making it impossible to deal + // Don't use timestamp(), because it's circular, which is making it impossible to deal // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. - - + ce.frame = MusEGlobal::audio->curFrame(); if(_controlFifo.put(ce)) - { fprintf(stderr, "DssiSynthIF::oscControl: fifo overflow: in control number:%lu\n", cport); - } - - // Record automation: - // Take care of this immediately, because we don't want the silly delay associated with - // processing the fifo one-at-a-time in the apply(). - // NOTE: With some vsts we don't receive control events until the user RELEASES a control. - // So the events all arrive at once when the user releases a control. - // That makes this pretty useless... But what the heck... - if(id() != -1) - { - unsigned long pid = genACnum(id(), cport); - AutomationType at = synti->automationType(); + + enableController(cport, false); //TODO maybe re-enable the ctrl soon? - // 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 ! - if ((at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying())) - enableController(cport, false); //TODO maybe re-enable the ctrl soon? - - synti->recordAutomation(pid, value); - } - return 0; } @@ -2052,22 +2116,52 @@ int DssiSynthIF::oscMidi(int a, int b, int c) { - if (a == MusECore::ME_NOTEOFF) { - a = MusECore::ME_NOTEON; - c = 0; + // From the DSSI RFC document: + // /midi + // "Send an arbitrary MIDI event to the plugin. Takes a four-byte MIDI string. + // This is expected to be used for note data generated from a test panel on the UI, + // for example. It should not be used for program or controller changes, sysex data, etc. + // A host should feel free to drop any values it doesn't wish to pass on. + // No guarantees are provided about timing accuracy, etc, of the MIDI communication." + + // From dssi.h: + // " A host must not attempt to switch notes off by sending + // zero-velocity NOTE_ON events. It should always send true + // NOTE_OFFs. It is the host's responsibility to remap events in + // cases where an external MIDI source has sent it zero-velocity + // NOTE_ONs." + + int type = a & 0xf0; + if (type == ME_NOTEON && c == 0) { + type = ME_NOTEOFF; + c = 64; } - int channel = 0; // TODO: ?? - int port = synti->midiPort(); + + const int channel = a & 0x0f; + + const int port = synti->midiPort(); if(port != -1) { - MusECore::MidiPlayEvent event(0, port, channel, a, b, c); + // Time-stamp the event. + MidiPlayEvent event(MusEGlobal::audio->curFrame(), port, channel, type, b, c); #ifdef DSSI_DEBUG - printf("DssiSynthIF::oscMidi midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB()); + printf("DssiSynthIF::oscMidi midi event port:%d type:%d chn:%d a:%d b:%d\n", event.port(), event.type(), event.channel(), event.dataA(), event.dataB()); #endif - MusEGlobal::midiPorts[port].sendEvent(event); + // Just in case someone decides to send controllers, sysex, or stuff + // OTHER than "test notes", contrary to the rules... + // Since this is a thread other than audio or gui, it may not be safe to + // even ask whether a controller exists, so MidiPort::putEvent or putHwCtrlEvent + // would not be safe here. Ask the gui to do it for us. + // Is it a controller message? Send the message to the gui. + //if(event.translateCtrlNum() >= 0) + MusEGlobal::song->putIpcInEvent(event); + + // Send the message to the device. + if(MidiDevice* md = MusEGlobal::midiPorts[port].device()) + md->putEvent(event, MidiDevice::Late); } return 0; @@ -2079,16 +2173,11 @@ int DssiSynthIF::oscConfigure(const char *key, const char *value) { - //"This is pretty much the simplest legal implementation of - // configure in a DSSI host. - // The host has the option to remember the set of (key,value) + // "The host has the option to remember the set of (key,value) // pairs associated with a particular instance, so that if it // wants to restore the "same" instance on another occasion it can // just call configure() on it for each of those pairs and so - // restore state without any input from a GUI. Any real-world GUI - // host will probably want to do that. This host doesn't have any - // concept of restoring an instance from one run to the next, so - // we don't bother remembering these at all." + // restore state without any input from a GUI." #ifdef DSSI_DEBUG printf("DssiSynthIF::oscConfigure synth name:%s key:%s value:%s\n", synti->name().toLatin1().constData(), key, value); @@ -2104,10 +2193,10 @@ return 0; } - if (!synth->dssi->configure) + if (!_synth->dssi->configure) return 0; - char* message = synth->dssi->configure(handle, key, value); + char* message = _synth->dssi->configure(_handle, key, value); if (message) { printf("MusE: on configure '%s' '%s', plugin '%s' returned error '%s'\n", key, value, synti->name().toLatin1().constData(), message); @@ -2141,13 +2230,20 @@ } programs.clear(); - if (!synth->dssi->get_program) + if (!_synth->dssi->get_program) return; for (int i = 0;; ++i) { - const DSSI_Program_Descriptor* pd = synth->dssi->get_program(handle, i); + const DSSI_Program_Descriptor* pd = _synth->dssi->get_program(_handle, i); if (pd == 0) break; + + // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. + if((pd->Bank >> 8) > 127 || + (pd->Bank & 0xff) > 127 || + pd->Program > 127) + continue; + DSSI_Program_Descriptor d; d.Name = strdup(pd->Name); d.Program = pd->Program; @@ -2156,9 +2252,18 @@ } } -void DssiSynthIF::doSelectProgram(LADSPA_Handle handle, int bank, int prog) +void DssiSynthIF::doSelectProgram(LADSPA_Handle handle, int bankH, int bankL, int prog) { - const DSSI_Descriptor* dssi = synth->dssi; + if(bankH > 127) // Map "dont care" to 0 + bankH = 0; + if(bankL > 127) + bankL = 0; + if(prog > 127) + prog = 0; + + const int bank = (bankH << 8) | bankL; + + 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. @@ -2167,10 +2272,10 @@ // (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) + 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); + synti->setPluginCtrlVal(genACnum(id(), k), _controls[k].val); } } } @@ -2181,15 +2286,17 @@ QString DssiSynthIF::getPatchName(int /*chan*/, int prog, bool /*drum*/) const { - unsigned program = prog & 0x7f; - int lbank = (prog >> 8) & 0xff; - int hbank = (prog >> 16) & 0xff; - - if (lbank == 0xff) + unsigned program = prog & 0xff; + unsigned lbank = (prog >> 8) & 0xff; + unsigned hbank = (prog >> 16) & 0xff; + + if (program > 127) // Map "dont care" to 0 + program = 0; + if (lbank > 127) lbank = 0; - if (hbank == 0xff) + if (hbank > 127) hbank = 0; - unsigned bank = (hbank << 8) + lbank; + const unsigned bank = (hbank << 8) + lbank; for (std::vector::const_iterator i = programs.begin(); i != programs.end(); ++i) { @@ -2213,18 +2320,22 @@ for (std::vector::const_iterator i = programs.begin(); i != programs.end(); ++i) { - int bank = i->Bank; - int prog = i->Program; - int id = (bank << 16) + prog; - + // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. + int hb = i->Bank >> 8; + int lb = i->Bank & 0xff; + if(hb > 127 || lb > 127 || i->Program > 127) + continue; + hb &= 0x7f; + lb &= 0x7f; + QAction *act = menu->addAction(QString(i->Name)); - act->setData(id); + act->setData((hb << 16) | (lb << 8) | (int)i->Program); } } -int DssiSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval) +int DssiSynthIF::getControllerInfo(int id, QString* name, int* ctrl, int* min, int* max, int* initval) { - int controlPorts = synth->_controlInPorts; + int controlPorts = _synth->_controlInPorts; if(id == controlPorts || id == controlPorts + 1) { // @@ -2232,22 +2343,22 @@ // (channel and key pressure) midi messages, so add support for them now (as controllers). // if(id == controlPorts) - *ctrl = MusECore::CTRL_POLYAFTER; + *ctrl = CTRL_POLYAFTER; else if(id == controlPorts + 1) - *ctrl = MusECore::CTRL_AFTERTOUCH; + *ctrl = CTRL_AFTERTOUCH; *min = 0; *max = 127; - *initval = MusECore::CTRL_VAL_UNKNOWN; - *name = midiCtrlName(*ctrl).toLatin1().constData(); + *initval = CTRL_VAL_UNKNOWN; + *name = midiCtrlName(*ctrl); return ++id; } else if(id >= controlPorts + 2) return 0; - const DSSI_Descriptor* dssi = synth->dssi; + const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; - unsigned long i = controls[id].idx; + unsigned long i = _controls[id].idx; #ifdef DSSI_DEBUG printf("DssiSynthIF::getControllerInfo control port:%d port idx:%lu name:%s\n", id, i, ld->PortNames[i]); @@ -2255,14 +2366,14 @@ int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) - ctlnum = dssi->get_midi_controller_for_port(handle, i); + ctlnum = dssi->get_midi_controller_for_port(_handle, i); // No controller number? Give it one. if(ctlnum == DSSI_NONE) { // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. - ctlnum = MusECore::CTRL_NRPN14_OFFSET + 0x2000 + id; + ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + id; } else { @@ -2292,38 +2403,38 @@ printf("DssiSynthIF::getControllerInfo is NRPN control\n"); #endif - ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET; + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; } } - int def = MusECore::CTRL_VAL_UNKNOWN; + int def = CTRL_VAL_UNKNOWN; if(ladspa2MidiControlValues(ld, i, ctlnum, min, max, &def)) *initval = def; else - *initval = MusECore::CTRL_VAL_UNKNOWN; + *initval = CTRL_VAL_UNKNOWN; #ifdef DSSI_DEBUG printf("DssiSynthIF::getControllerInfo passed ctlnum:%d min:%d max:%d initval:%d\n", ctlnum, *min, *max, *initval); #endif *ctrl = ctlnum; - *name = ld->PortNames[i]; + *name = QString(ld->PortNames[i]); return ++id; } int DssiSynthIF::channels() const { - return ((int)synth->_outports) > MAX_CHANNELS ? MAX_CHANNELS : ((int)synth->_outports) ; + return ((int)_synth->_outports) > MAX_CHANNELS ? MAX_CHANNELS : ((int)_synth->_outports) ; } int DssiSynthIF::totalOutChannels() const { - return synth->_outports; + return _synth->_outports; } int DssiSynthIF::totalInChannels() const { - return synth->_inports; + return _synth->_inports; } void DssiSynthIF::deactivate3() @@ -2336,42 +2447,28 @@ // Methods for PluginIBase: //-------------------------------- -//bool DssiSynthIF::on() const { return true; } // Synth is not part of a rack plugin chain. Always on. -//void DssiSynthIF::setOn(bool /*val*/) { } -unsigned long DssiSynthIF::pluginID() { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->UniqueID : 0; } +unsigned long DssiSynthIF::pluginID() { return (_synth && _synth->dssi) ? _synth->dssi->LADSPA_Plugin->UniqueID : 0; } int DssiSynthIF::id() { return MAX_PLUGINS; } // Set for special block reserved for dssi synth. p4.0.20 -QString DssiSynthIF::pluginLabel() const { return (synth && synth->dssi) ? QString(synth->dssi->LADSPA_Plugin->Label) : QString(); } -//QString DssiSynthIF::name() const { return synti->name(); } -QString DssiSynthIF::lib() const { return synth ? synth->completeBaseName() : QString(); } -QString DssiSynthIF::dirPath() const { return synth ? synth->absolutePath() : QString(); } -QString DssiSynthIF::fileName() const { return synth ? synth->fileName() : QString(); } -//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; } +QString DssiSynthIF::pluginLabel() const { return (_synth && _synth->dssi) ? QString(_synth->dssi->LADSPA_Plugin->Label) : QString(); } +QString DssiSynthIF::lib() const { return _synth ? _synth->completeBaseName() : QString(); } +QString DssiSynthIF::dirPath() const { return _synth ? _synth->absolutePath() : QString(); } +QString DssiSynthIF::fileName() const { return _synth ? _synth->fileName() : QString(); } +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::enableAllControllers(bool v) { - if(!synth) + 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; + for(unsigned long i = 0; i < _synth->_controlInPorts; ++i) + _controls[i].enCtrl = v; } void DssiSynthIF::updateControllers() { } void DssiSynthIF::activate() { - if(synth && synth->dssi && synth->dssi->LADSPA_Plugin && synth->dssi->LADSPA_Plugin->activate) + if(_synth && _synth->dssi && _synth->dssi->LADSPA_Plugin && _synth->dssi->LADSPA_Plugin->activate) //for (int i = 0; i < instances; ++i) // _plugin->activate(handle[i]); - synth->dssi->LADSPA_Plugin->activate(handle); + _synth->dssi->LADSPA_Plugin->activate(_handle); // REMOVE Tim. Or keep? From PluginI::activate(). // if (initControlValues) { @@ -2388,27 +2485,24 @@ } void DssiSynthIF::deactivate() { - if(!synth || !synth->dssi || !synth->dssi->LADSPA_Plugin ||!synth->dssi->LADSPA_Plugin->deactivate) + if(!_synth || !_synth->dssi || !_synth->dssi->LADSPA_Plugin ||!_synth->dssi->LADSPA_Plugin->deactivate) return; //for (int i = 0; i < instances; ++i) // synth->dssi->LADSPA_Plugin->deactivate(handle[i]); - synth->dssi->LADSPA_Plugin->deactivate(handle); + _synth->dssi->LADSPA_Plugin->deactivate(_handle); } -//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); } -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; } -const char* DssiSynthIF::paramOutName(unsigned long i) { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->PortNames[controlsOut[i].idx] : 0; } -LADSPA_PortRangeHint DssiSynthIF::range(unsigned long i) { return synth->dssi->LADSPA_Plugin->PortRangeHints[controls[i].idx]; } -LADSPA_PortRangeHint DssiSynthIF::rangeOut(unsigned long i) { return synth->dssi->LADSPA_Plugin->PortRangeHints[controlsOut[i].idx]; } -CtrlValueType DssiSynthIF::ctrlValueType(unsigned long i) const { return ladspaCtrlValueType(synth->dssi->LADSPA_Plugin, controls[i].idx); } -CtrlList::Mode DssiSynthIF::ctrlMode(unsigned long i) const { return ladspaCtrlMode(synth->dssi->LADSPA_Plugin, controls[i].idx); }; +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, double val) { setParameter(i, val); } +double DssiSynthIF::param(unsigned long i) const { return getParameter(i); } +double 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; } +const char* DssiSynthIF::paramOutName(unsigned long i) { return (_synth && _synth->dssi) ? _synth->dssi->LADSPA_Plugin->PortNames[_controlsOut[i].idx] : 0; } +LADSPA_PortRangeHint DssiSynthIF::range(unsigned long i) { return _synth->dssi->LADSPA_Plugin->PortRangeHints[_controls[i].idx]; } +LADSPA_PortRangeHint DssiSynthIF::rangeOut(unsigned long i) { return _synth->dssi->LADSPA_Plugin->PortRangeHints[_controlsOut[i].idx]; } +CtrlValueType DssiSynthIF::ctrlValueType(unsigned long i) const { return ladspaCtrlValueType(_synth->dssi->LADSPA_Plugin, _controls[i].idx); } +CtrlList::Mode DssiSynthIF::ctrlMode(unsigned long i) const { return ladspaCtrlMode(_synth->dssi->LADSPA_Plugin, _controls[i].idx); }; } // namespace MusECore diff -Nru muse-2.1.2/muse/dssihost.h muse-3.0.2+ds1/muse/dssihost.h --- muse-2.1.2/muse/dssihost.h 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/dssihost.h 2017-12-17 21:07:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: dssihost.h,v 1.10.2.7 2009/12/06 10:05:00 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License @@ -26,7 +26,8 @@ #include "config.h" -// Make sure this number is unique among all the MESS synths and DSSI host synth. +// Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. #define DSSI_SYNTH_UNIQUE_ID 8 // Midistate sysex initialization command. #define DSSI_INIT_DATA_CMD 1 @@ -69,7 +70,7 @@ struct _DSSI; class DssiPluginIF; -class Port; +struct Port; //--------------------------------------------------------- // DssiSynth @@ -83,17 +84,16 @@ unsigned long _portCount, _inports, _outports, _controlInPorts, _controlOutPorts; std::vector iIdx; // Audio input index to port number. std::vector oIdx; // Audio output index to port number. - std::vector iUsedIdx; // During process, tells whether an audio input port was used by any input routes. std::vector rpIdx; // Port number to control input index. Item is -1 if it's not a control input. - MusECore::MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to DSSI port numbers. - MusECore::MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps DSSI port numbers to midi controller numbers. + MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to DSSI port numbers. + MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps DSSI port numbers to midi controller numbers. bool _hasGui; - bool _inPlaceCapable; // Hack: Special flag required. bool _isDssiVst; public: - DssiSynth(QFileInfo&, const DSSI_Descriptor*); // removed const for QFileInfo + DssiSynth(QFileInfo&, const DSSI_Descriptor*, bool isDssiVst = false, + Plugin::PluginFeatures reqFeatures = Plugin::NoFeatures); // removed const for QFileInfo virtual ~DssiSynth(); virtual Type synthType() const { return DSSI_SYNTH; } @@ -105,6 +105,8 @@ unsigned long outPorts() const { return _outports; } unsigned long inControls() const { return _controlInPorts; } unsigned long outControls() const { return _controlOutPorts; } + + bool isDssiVst() const { return _isDssiVst; } const std::vector* getRpIdx() { return &rpIdx; } }; @@ -116,11 +118,13 @@ class DssiSynthIF : public SynthIF { - DssiSynth* synth; - LADSPA_Handle handle; + DssiSynth* _synth; + LADSPA_Handle _handle; - Port* controls; - Port* controlsOut; + Port* _controls; + Port* _controlsOut; + bool _hasLatencyOutPort; + unsigned long _latencyOutPort; #ifdef OSC_SUPPORT OscDssiIF _oscif; @@ -128,12 +132,12 @@ std::vector programs; void queryPrograms(); - void doSelectProgram(LADSPA_Handle handle, int bank, int prog); - bool processEvent(const MusECore::MidiPlayEvent&, snd_seq_event_t*); + void doSelectProgram(LADSPA_Handle handle, int bankH, int bankL, int prog); + bool processEvent(const MidiPlayEvent&, snd_seq_event_t*); - float** audioInBuffers; - float** audioOutBuffers; - float* audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. + float** _audioInBuffers; + float** _audioOutBuffers; + float* _audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. public: DssiSynthIF(SynthI* s); @@ -145,26 +149,20 @@ bool init(DssiSynth* s); - virtual DssiSynth* dssiSynth() { return synth; } + virtual DssiSynth* dssiSynth() { return _synth; } virtual SynthI* dssiSynthI() { return synti; } - virtual bool initGui(); virtual void guiHeartBeat(); - virtual bool guiVisible() const; - virtual void showGui(bool); virtual bool hasGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); virtual bool hasNativeGui() const { return !dssi_ui_filename().isEmpty(); } - virtual void getGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } - virtual void setGeometry(int, int, int, int) {} virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } virtual void setNativeGeometry(int, int, int, int) {} virtual void preProcessAlways(); - virtual MusECore::iMPEvent getData(MusECore::MidiPort*, MusECore::MPEventList*, MusECore::iMPEvent, unsigned pos, int ports, unsigned n, float** buffer); - virtual bool putEvent(const MusECore::MidiPlayEvent& ev); - virtual MusECore::MidiPlayEvent receiveEvent(); + virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned n, float** buffer); + virtual MidiPlayEvent receiveEvent(); virtual int eventsPending() const { return 0; } virtual int channels() const; @@ -178,10 +176,10 @@ virtual void write(int level, Xml& xml) const; - virtual float getParameter(unsigned long /*idx*/) const; - virtual float getParameterOut(unsigned long n) const; - virtual void setParameter(unsigned long /*idx*/, float /*value*/); - virtual int getControllerInfo(int, const char**, int*, int*, int*, int*); + virtual double getParameter(unsigned long /*idx*/) const; + virtual double getParameterOut(unsigned long n) const; + virtual void setParameter(unsigned long /*idx*/, double /*value*/); + virtual int getControllerInfo(int, QString*, int*, int*, int*, int*); #ifdef OSC_SUPPORT OscDssiIF& oscIF() { return _oscif; } @@ -204,23 +202,21 @@ QString fileName() const; 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 activate(); void deactivate(); unsigned long parameters() const; unsigned long parametersOut() const; - void setParam(unsigned long i, float val); - float param(unsigned long i) const; - float paramOut(unsigned long i) const; + void setParam(unsigned long i, double val); + double param(unsigned long i) const; + double paramOut(unsigned long i) const; const char* paramName(unsigned long i); const char* paramOutName(unsigned long i); LADSPA_PortRangeHint range(unsigned long i); LADSPA_PortRangeHint rangeOut(unsigned long i); + float latency(); CtrlValueType ctrlValueType(unsigned long i) const; CtrlList::Mode ctrlMode(unsigned long i) const; diff -Nru muse-2.1.2/muse/evdata.cpp muse-3.0.2+ds1/muse/evdata.cpp --- muse-2.1.2/muse/evdata.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/evdata.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,364 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// sysex_processor.cpp +// (C) Copyright 2017 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 +// 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 "evdata.h" +#include "midi.h" +#include "sysex_helper.h" + +#include +#include + +namespace MusECore { + +//--------------------------------------------------------- +// SysExInputProcessor +// Special processing of system exclusive chunks. +//--------------------------------------------------------- + +SysExInputProcessor::State SysExInputProcessor::processInput(EvData* dst, const unsigned char* src, size_t len, size_t frame) +{ + if(!src || len == 0) + return _state; + + switch(_state) + { + case Clear: + case Finished: + if(*src == ME_SYSEX) + { + // Mark the starting frame as the given frame. + _startFrame = frame; + + // Is this block of data a single chunk? + if((*(src + len - 1) == ME_SYSEX_END)) + { + // Aside from the start and end bytes, is there any useful data? + if(len >= 3) + { + // It is a single chunk sysex - no need to queue, set the EvData directly. + _state = Finished; + dst->setData(src + 1, len - 2); + } + else + // It's a useless chunk, no point in storing it. + _state = Clear; + } + else + { + // Reset or clear the queue (prefer simple reset but memory may grow - clear later). + //_q.reset(); + _q.clear(); + + _state = Filling; + // It's one chunk of others, queue it. Don't include the start byte - + // our EvData is designed to strip the start/end bytes out. + if(len >= 2) + _q.add(src + 1, len - 1); + } + } + else + { + _state = Clear; + fprintf(stderr, "SysExInputProcessor: State is Clear or Finished:%d but chunk start is not ME_SYSEX\n", _state); + } + break; + + case Filling: + if(*src == ME_SYSEX) + { + fprintf(stderr, "SysExInputProcessor: State is Filling but chunk start is ME_SYSEX. Finishing sysex.\n"); + // Finish what we got. + _state = Finished; + dst->setData(this); + // Reset or clear the queue (prefer simple reset but memory may grow - clear later). + //_q.reset(); + _q.clear(); + } + else if((*(src + len - 1) == ME_SYSEX_END)) + { + // Finish what we got. Don't include the end byte - + // our EvData is designed to strip the start/end bytes out. + if(len >= 2) + _q.add(src, len - 1); + _state = Finished; + dst->setData(this); + // Reset or clear the queue (prefer simple reset but memory may grow - clear later). + //_q.reset(); + _q.clear(); + } + else + { + // Add what we got and continue in Filling state. + _q.add(src, len); + } + break; + } + + return _state; +} + +//--------------------------------------------------------- +// SysExOutputProcessor +// Special processing of system exclusive chunks. +//--------------------------------------------------------- + +size_t SysExOutputProcessor::dataSize() const +{ + return _evData.dataLen; +} + +size_t SysExOutputProcessor::curChunkSize() const +{ + switch(_state) + { + case Clear: + case Finished: + fprintf(stderr, "SysExOutputProcessor: curChunkSize called while State is not Sending.\n"); + return 0; + break; + + case Sending: + { + // The remaining number of data bytes (minus any start/end byte). + size_t sz = 0; + if((int)_curPos < _evData.dataLen) + sz = _evData.dataLen - _curPos; + + // Are we on the first chunk? Leave room for the start byte. + if(_curPos == 0) + ++sz; + + // Should there be more chunks? That is, will the data so far - + // plus an end byte - not fit into a chunk? + if(sz > (_chunkSize - 1)) + // Limit the final size. + sz = _chunkSize; + else + // Leave room for the end byte. + ++sz; + + return sz; + } + break; + } + + return 0; +} + +void SysExOutputProcessor::clear() +{ + // Release any reference to the data. + _evData = EvData(); + _state = Clear; + _curPos = 0; +} + +void SysExOutputProcessor::reset() +{ + _state = Clear; + _curPos = 0; +} + +SysExOutputProcessor::State SysExOutputProcessor::setEvData(const EvData& src, size_t frame) +{ + if(!src.data || src.dataLen == 0) + return _state; + + switch(_state) + { + case Clear: + case Finished: + // Keep a reference to the data so that it doesn't disappear between calls, + // so that we can step through the data block. + _evData = src; + + _curPos = 0; + + // Mark the starting frame as the given frame. + _curChunkFrame = frame; + + _state = Sending; + break; + + case Sending: + fprintf(stderr, "SysExOutputProcessor: processOutput called while State is Sending.\n"); + break; + } + + return _state; +} + +bool SysExOutputProcessor::getCurChunk(unsigned char* dst, int sampleRate) +{ + if(!dst) + return false; + + switch(_state) + { + case Clear: + case Finished: + fprintf(stderr, "SysExOutputProcessor: getCurChunk called while State is not Sending.\n"); + return false; + break; + + case Sending: + { + unsigned char* p = dst; + bool is_chunk = false; + + // The remaining number of data bytes (minus any start/end byte). + size_t sz = 0; + if((int)_curPos < _evData.dataLen) + sz = _evData.dataLen - _curPos; + + // Are we on the first chunk? Leave room for the start byte. + if(_curPos == 0) + ++sz; + + // Should there be more chunks? That is, will the data so far - + // plus an end byte - not fit into a chunk? + if(sz > (_chunkSize - 1)) + { + // Limit the final size. + sz = _chunkSize; + is_chunk = true; + } + + // Are we on the first chunk? + if(_curPos == 0) + { + // Add the start byte. + *p++ = ME_SYSEX; + --sz; + } + + // Besides any start byte, is there any actual data to copy? + if(sz != 0) + { + // Copy the data to the destination. + memcpy(p, _evData.data + _curPos, sz); + // Advance the pointer. + p += sz; + // Advance the current position. + _curPos += sz; + } + + // Are there no more chunks to follow? + if(!is_chunk) + { + // Add the end byte. + *p = ME_SYSEX_END; + // We are finished. + _state = Finished; + // Release any reference to the data. + _evData = EvData(); + } + + // Estimate the number of audio frames it should take (or took) to transmit the current midi chunk. + // Advance the current chunk frame so that the driver can schedule the next chunk. + // Do it even if the state has Finished, so the driver can wait until the last chunk is done + // before calling Clear() or Reset() (setting the state to Clear). + _curChunkFrame += sysexDuration(sz, sampleRate); + } + break; + } + + return true; +} + +size_t SysExOutputProcessor::stageEvData(const EvData& evData, unsigned int frame) +{ + // Cannot do if already sending. + if(_state == SysExOutputProcessor::Sending) + return 0; + // Set the event data, and proceed only if state changed to Sending. + if(setEvData(evData, frame) != SysExOutputProcessor::Sending) + return 0; + // Return the current (first) chunk size. + return curChunkSize(); +} + +//--------------------------------------------------------- +// EvData +// variable len event data (sysex, meta etc.) +//--------------------------------------------------------- + +void EvData::setData(const unsigned char* p, int l) +{ + // Setting the data destroys any reference. Dereference now. + // The data may still be shared. Destroy it only if no more references. + if (refCount && (--(*refCount) == 0)) + { + delete refCount; + refCount = 0; + + if(data) + delete[] data; + } + // Clear the data variable. + data = 0; + + if(l > 0) + { + data = new unsigned char[l]; + memcpy(data, p, l); + + // Setting the data destroys any reference. Create a new reference now. + refCount = new int(1); + } + dataLen = l; +} + +void EvData::setData(const SysExInputProcessor* q) +{ + // Let's not risk unterminated data: Accept a queue with a Finished state only. + if(q->state() != SysExInputProcessor::Finished) + return; + // Setting the data destroys any reference. Dereference now. + // The data may still be shared. Destroy it only if no more references. + if (refCount && (--(*refCount) == 0)) + { + delete refCount; + refCount = 0; + + if(data) + delete[] data; + } + // Clear the data variable. + data = 0; + + const size_t l = q->size(); + if(l > 0) + { + // Create a contiguous memory block to hold the data. + data = new unsigned char[l]; + // Copy the non-contiguous chunks of data to the contiguous data. + q->copy(data, l); + // Setting the data destroys any reference. Create a new reference now. + refCount = new int(1); + } + dataLen = l; +} + +} // namespace MusECore + diff -Nru muse-2.1.2/muse/evdata.h muse-3.0.2+ds1/muse/evdata.h --- muse-2.1.2/muse/evdata.h 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/evdata.h 2018-01-29 20:07:03.000000000 +0000 @@ -24,10 +24,12 @@ #ifndef __EVDATA_H__ #define __EVDATA_H__ -#include +#include "memory.h" namespace MusECore { +class SysExInputProcessor; + //--------------------------------------------------------- // EvData // variable len event data (sysex, meta etc.) @@ -43,53 +45,130 @@ EvData() { data = 0; dataLen = 0; - refCount = new int(1); + refCount = 0; } EvData(const EvData& ed) { data = ed.data; dataLen = ed.dataLen; refCount = ed.refCount; + if(refCount) + (*refCount)++; } EvData& operator=(const EvData& ed) { if (data == ed.data) return *this; - if (--(*refCount) == 0) { - delete refCount; - if(data) - delete[] data; - } + if (refCount && (--(*refCount) == 0)) + { + delete refCount; + if(data) + delete[] data; + } + data = ed.data; dataLen = ed.dataLen; refCount = ed.refCount; + if(refCount) + (*refCount)++; return *this; } ~EvData() { - if (--(*refCount) == 0) { + if (refCount && (--(*refCount) == 0)) { if(data) { delete[] data; data = 0; } - if(refCount) - { - delete refCount; - refCount = 0; - } + delete refCount; + refCount = 0; } } - void setData(const unsigned char* p, int l) { - if(data) - delete[] data; // p4.0.27 - data = new unsigned char[l]; - memcpy(data, p, l); - dataLen = l; - } + void setData(const unsigned char* p, int l); + void setData(const SysExInputProcessor* q); }; + +//--------------------------------------------------------- +// SysExInputProcessor +// Special processing of system exclusive chunks. +//--------------------------------------------------------- + +class SysExInputProcessor +{ + public: + enum State { Clear = 0, Filling = 1, Finished = 2 }; + + private: + MemoryQueue _q; + State _state; + size_t _startFrame; + + public: + SysExInputProcessor() : _state(Clear), _startFrame(0) { } + // Returns the state of the queue. + State state() const { return _state; } + // Returns the frame that the sysex started at. + size_t startFrame() const { return _startFrame; } + // Returns the size of the queue. + size_t size() const { return _q.curSize(); } + // Clears the queue. + void clear() { _q.clear(); _state = Clear; } + // Resets the queue. + void reset() { _q.reset(); _state = Clear; } + // Process some input. Return the state. Only when state is Finished will dst hold valid data. + State processInput(EvData* dst, const unsigned char* src, size_t len, size_t frame); + // Copies the queue to a character buffer. + // Returns number of bytes copied. + size_t copy(unsigned char* dst, size_t len) const { return _q.copy(dst, len); } +}; + +//--------------------------------------------------------- +// SysExOutputProcessor +// Special processing of system exclusive chunks. +//--------------------------------------------------------- + +class SysExOutputProcessor +{ + public: + enum State { Clear = 0, Sending = 1, Finished = 2 }; + + private: + size_t _chunkSize; + State _state; + size_t _curChunkFrame; + EvData _evData; + size_t _curPos; + + public: + SysExOutputProcessor() : _chunkSize(256), _state(Clear), _curChunkFrame(0), _curPos(0) { } + // Returns the state of the queue. + State state() const { return _state; } + // Sets the chunk size to be used. Default is 256. + // It may be necessary to adjust the size depending on the driver and its limits. + void setChunkSize(size_t sz = 256) { _chunkSize = sz; } + // Returns the frame that the current chunk should be scheduled at. + size_t curChunkFrame() const { return _curChunkFrame; } + // Returns the size of the data (minus any start/end bytes). + size_t dataSize() const; + // Returns the size of the current chunk (including any start/end bytes). + size_t curChunkSize() const; + // Clears the processor. Releases any reference to data in _evData. Sets state to Clear. + void clear(); + // Resets the processor. Resets _curPos. Sets state to Clear. + void reset(); + // Set up the processor with a complete output data block. Return the state. + // State will change to Sending. Afterwards, call getCurChunk each cycle until state is Finished. + State setEvData(const EvData& src, size_t frame); + // Fills destination with the current chunk. The destination must have at least curChunkSize bytes. + // Afterwards it moves on to the next chunk until the state changes to Finished. Returns true on success. + bool getCurChunk(unsigned char* dst, int sampleRate); + // Convenience method: Performs a state check, calls setEvData, and returns curChunkSize(). + size_t stageEvData(const EvData& evData, unsigned int frame); +}; + } // namespace MusECore #endif diff -Nru muse-2.1.2/muse/eventbase.h muse-3.0.2+ds1/muse/eventbase.h --- muse-2.1.2/muse/eventbase.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/eventbase.h 2017-12-04 21:01:18.000000000 +0000 @@ -26,6 +26,7 @@ #include +#include "type_defs.h" #include "pos.h" #include "event.h" @@ -38,6 +39,11 @@ class EventBase : public PosLen { EventType _type; + static EventID_t idGen; + // An always unique id. + EventID_t _uniqueId; + // Can be either _uniqueId or the same _uniqueId as other clone 'group' events. De-cloning restores it to _uniqueId. + EventID_t _id; protected: int refCount; @@ -45,22 +51,31 @@ public: EventBase(EventType t); - EventBase(const EventBase& ev); + // Creates a non-shared clone with same id, or duplicate with unique id, and 0 ref count and invalid Pos sn. + EventBase(const EventBase& ev, bool duplicate_not_clone = false); - virtual ~EventBase() {} + virtual ~EventBase() { } int getRefCount() const { return refCount; } + + EventID_t id() const { return _id; } + EventID_t newId() { return idGen++; } + void shareId(const EventBase* ev) { _id = ev->_id; } // Makes id same as given event's. Effectively makes the events non-shared clones. + virtual void assign(const EventBase& ev); // Assigns to this event, excluding the _id. + EventType type() const { return _type; } void setType(EventType t) { _type = t; } bool selected() const { return _selected; } void setSelected(bool val) { _selected = val; } void move(int offset); + + virtual bool isSimilarTo(const EventBase& other) const = 0; virtual void read(Xml&) = 0; virtual void write(int, Xml&, const Pos& offset, bool forcePath = false) const = 0; virtual void dump(int n = 0) const; - virtual EventBase* mid(unsigned, unsigned) = 0; + virtual EventBase* mid(unsigned, unsigned) const = 0; friend class Event; virtual bool isNote() const { return false; } @@ -95,7 +110,15 @@ virtual void setSpos(int) { } virtual SndFileR sndFile() const { return 0; } virtual void setSndFile(SndFileR&) { } - virtual EventBase* clone() = 0; + // Creates a non-shared clone, having the same 'group' _id. + // NOTE: Certain pointer members may still be SHARED. Such as the sysex MidiEventBase::edata. + // Be aware when iterating or modifying clones. + virtual EventBase* clone() const = 0; + + // Restores _id to _uniqueId, removing the event from any clone 'group'. + virtual void deClone() { _id = _uniqueId; } + // Creates a copy of the event base, excluding the 'group' _id. + virtual EventBase* duplicate() const = 0; virtual void readAudio(WavePart* /*part*/, unsigned /*offset*/, float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/) { } diff -Nru muse-2.1.2/muse/event.cpp muse-3.0.2+ds1/muse/event.cpp --- muse-2.1.2/muse/event.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/event.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -26,9 +26,14 @@ #include "eventbase.h" #include "waveevent.h" #include "midievent.h" +#include "midi.h" + +//#define USE_SAMPLERATE namespace MusECore { +EventID_t EventBase::idGen=0; + //--------------------------------------------------------- // Event //--------------------------------------------------------- @@ -39,15 +44,31 @@ Pos::setType(_type == Wave ? FRAMES : TICKS); refCount = 0; _selected = false; + _uniqueId = newId(); + _id = _uniqueId; } -EventBase::EventBase(const EventBase& ev) +EventBase::EventBase(const EventBase& ev, bool duplicate_not_clone) : PosLen(ev) { refCount = 0; _selected = ev._selected; _type = ev._type; + _uniqueId = newId(); + _id = duplicate_not_clone ? _uniqueId : ev._id; } + +void EventBase::assign(const EventBase& ev) +{ + if(this == &ev) // Is it a shared clone? + return; + + if(ev.type() != _type) + return; + + (PosLen&)(*this) = (const PosLen&)ev; + setSelected(ev.selected()); +} //--------------------------------------------------------- // move @@ -71,18 +92,41 @@ } //--------------------------------------------------------- +// duplicate +//--------------------------------------------------------- + +Event Event::duplicate() const + { + #ifdef USE_SAMPLERATE + return ev ? Event(ev->duplicate(), _audConv) : Event(); + #else + return ev ? Event(ev->duplicate()) : Event(); + #endif + } + +//--------------------------------------------------------- // clone //--------------------------------------------------------- -Event Event::clone() +Event Event::clone() const { #ifdef USE_SAMPLERATE - return Event(ev->clone(), _audConv); + return ev ? Event(ev->clone(), _audConv) : Event(); #else - return Event(ev->clone()); + return ev ? Event(ev->clone()) : Event(); #endif } +//--------------------------------------------------------- +// deClone +//--------------------------------------------------------- + +void Event::deClone() +{ + if(ev) + ev->deClone(); +} + Event::Event() { ev = 0; @@ -107,10 +151,11 @@ } Event::Event(EventBase* eb) { ev = eb; - ++(ev->refCount); + if(ev) + ++(ev->refCount); #ifdef USE_SAMPLERATE - if(!ev->sndFile().isNull()) + if(ev && !ev->sndFile().isNull()) _audConv = new SRCAudioConverter(ev->sndFile().channels(), SRC_SINC_MEDIUM_QUALITY); #endif } @@ -120,7 +165,8 @@ _audConv = 0; ev = eb; - ++(ev->refCount); + if(ev) + ++(ev->refCount); if(cv) _audConv = cv->reference(); @@ -138,8 +184,40 @@ #endif } +MidiPlayEvent Event::asMidiPlayEvent(unsigned time, int port, int channel) const + { + MidiPlayEvent mpe; + mpe.setChannel(channel); + mpe.setTime(time); + mpe.setPort(port); + mpe.setLoopNum(0); + switch(type()) { + case Note: + mpe.setType(ME_NOTEON); + mpe.setA(dataA()); + mpe.setB(dataB()); + break; + case Controller: + mpe.setType(ME_CONTROLLER); + mpe.setA(dataA()); // controller number + mpe.setB(dataB()); // controller value + break; + case Sysex: + mpe.setType(ME_SYSEX); + mpe.setData(eventData()); + break; + default: + fprintf(stderr, "Event::asMidiPlayEvent: event type %d not implemented\n", + type()); + break; + } + return mpe; + } + bool Event::empty() const { return ev == 0; } EventType Event::type() const { return ev ? ev->type() : Note; } +EventID_t Event::id() const { return ev ? ev->id() : MUSE_INVALID_EVENT_ID; } +void Event::shareId(const Event& e) { if(ev && e.ev) ev->shareId(e.ev); } void Event::setType(EventType t) { if (ev && --(ev->refCount) == 0) { @@ -176,21 +254,32 @@ return *this; } +void Event::assign(const Event& e) +{ + if(!ev || !e.ev || ev == e.ev) + return; + ev->assign(*(e.ev)); +} + bool Event::operator==(const Event& e) const { return ev == e.ev; } +bool Event::isSimilarTo(const Event& other) const +{ + return ev ? ev->isSimilarTo(*other.ev) : (other.ev ? false : true); +} -int Event::getRefCount() const { return ev->getRefCount(); } -bool Event::selected() const { return ev->_selected; } -void Event::setSelected(bool val) { ev->_selected = val; } -void Event::move(int offset) { ev->move(offset); } +int Event::getRefCount() const { return ev ? ev->getRefCount() : 0; } +bool Event::selected() const { return ev ? ev->_selected : false; } +void Event::setSelected(bool val) { if(ev) ev->_selected = val; } +void Event::move(int offset) { if(ev) ev->move(offset); } void Event::read(Xml& xml) { - ev->read(xml); + if(ev) ev->read(xml); #ifdef USE_SAMPLERATE - if(!ev->sndFile().isNull()) + if(ev && !ev->sndFile().isNull()) { if(_audConv) _audConv->setChannels(ev->sndFile().channels()); @@ -201,40 +290,40 @@ } -void Event::write(int a, Xml& xml, const Pos& o, bool forceWavePaths) const { ev->write(a, xml, o, forceWavePaths); } -void Event::dump(int n) const { ev->dump(n); } -Event Event::mid(unsigned a, unsigned b) { return Event(ev->mid(a, b)); } - -bool Event::isNote() const { return ev->isNote(); } -bool Event::isNoteOff() const { return ev->isNoteOff(); } -bool Event::isNoteOff(const Event& e) const { return ev->isNoteOff(e); } -int Event::dataA() const { return ev->dataA(); } -int Event::pitch() const { return ev->dataA(); } -void Event::setA(int val) { ev->setA(val); } -void Event::setPitch(int val) { ev->setA(val); } -int Event::dataB() const { return ev->dataB(); } -int Event::velo() const { return ev->dataB(); } -void Event::setB(int val) { ev->setB(val); } -void Event::setVelo(int val) { ev->setB(val); } -int Event::dataC() const { return ev->dataC(); } -int Event::veloOff() const { return ev->dataC(); } -void Event::setC(int val) { ev->setC(val); } -void Event::setVeloOff(int val) { ev->setC(val); } - -const unsigned char* Event::data() const { return ev->data(); } -int Event::dataLen() const { return ev->dataLen(); } -void Event::setData(const unsigned char* data, int len) { ev->setData(data, len); } -const EvData Event::eventData() const { return ev->eventData(); } - -const QString Event::name() const { return ev->name(); } -void Event::setName(const QString& s) { ev->setName(s); } -int Event::spos() const { return ev->spos(); } -void Event::setSpos(int s) { ev->setSpos(s); } -MusECore::SndFileR Event::sndFile() const { return ev->sndFile(); } +void Event::write(int a, Xml& xml, const Pos& o, bool forceWavePaths) const { if(ev) ev->write(a, xml, o, forceWavePaths); } +void Event::dump(int n) const { if(ev) ev->dump(n); } +Event Event::mid(unsigned a, unsigned b) const { return ev ? Event(ev->mid(a, b)) : Event(); } + +bool Event::isNote() const { return ev ? ev->isNote() : false; } +bool Event::isNoteOff() const { return ev ? ev->isNoteOff() : false; } +bool Event::isNoteOff(const Event& e) const { return ev ? ev->isNoteOff(e) : false; } +int Event::dataA() const { return ev ? ev->dataA() : 0; } +int Event::pitch() const { return ev ? ev->dataA() : 0; } +void Event::setA(int val) { if(ev) ev->setA(val); } +void Event::setPitch(int val) { if(ev) ev->setA(val); } +int Event::dataB() const { return ev ? ev->dataB() : 0; } +int Event::velo() const { return ev ? ev->dataB() : 0; } +void Event::setB(int val) { if(ev) ev->setB(val); } +void Event::setVelo(int val) { if(ev) ev->setB(val); } +int Event::dataC() const { return ev ? ev->dataC() : 0; } +int Event::veloOff() const { return ev ? ev->dataC() : 0; } +void Event::setC(int val) { if(ev) ev->setC(val); } +void Event::setVeloOff(int val) { if(ev) ev->setC(val); } + +const unsigned char* Event::data() const { return ev ? ev->data() : 0; } +int Event::dataLen() const { return ev ? ev->dataLen() : 0; } +void Event::setData(const unsigned char* data, int len) { if(ev) ev->setData(data, len); } +const EvData Event::eventData() const { return ev ? ev->eventData() : EvData(); } + +const QString Event::name() const { return ev ? ev->name() : QString(); } +void Event::setName(const QString& s) { if(ev) ev->setName(s); } +int Event::spos() const { return ev ? ev->spos() : 0; } +void Event::setSpos(int s) { if(ev) ev->setSpos(s); } +MusECore::SndFileR Event::sndFile() const { return ev ? ev->sndFile() : MusECore::SndFileR(); } void Event::setSndFile(MusECore::SndFileR& sf) { - ev->setSndFile(sf); + if(ev) ev->setSndFile(sf); #ifdef USE_SAMPLERATE if(_audConv) @@ -244,7 +333,7 @@ } else { - if(!sf.isNull()) + if(ev && !sf.isNull()) _audConv = new SRCAudioConverter(ev->sndFile().channels(), SRC_SINC_MEDIUM_QUALITY); } #endif @@ -252,19 +341,21 @@ void Event::readAudio(MusECore::WavePart* part, unsigned offset, float** bpp, int channels, int nn, bool doSeek, bool overwrite) { - ev->readAudio(part, offset, bpp, channels, nn, doSeek, overwrite); + if(ev) ev->readAudio(part, offset, bpp, channels, nn, doSeek, overwrite); } -void Event::setTick(unsigned val) { ev->setTick(val); } -unsigned Event::tick() const { return ev->tick(); } -unsigned Event::frame() const { return ev->frame(); } -void Event::setFrame(unsigned val) { ev->setFrame(val); } -void Event::setLenTick(unsigned val) { ev->setLenTick(val); } -void Event::setLenFrame(unsigned val) { ev->setLenFrame(val); } -unsigned Event::lenTick() const { return ev->lenTick(); } -unsigned Event::lenFrame() const { return ev->lenFrame(); } -Pos Event::end() const { return ev->end(); } -unsigned Event::endTick() const { return ev->end().tick(); } -unsigned Event::endFrame() const { return ev->end().frame(); } -void Event::setPos(const Pos& p) { ev->setPos(p); } +void Event::setTick(unsigned val) { if(ev) ev->setTick(val); } +unsigned Event::tick() const { return ev ? ev->tick() : 0; } +unsigned Event::frame() const { return ev ? ev->frame() : 0; } +unsigned Event::posValue() const { return ev ? ev->posValue() : 0; } +void Event::setFrame(unsigned val) { if(ev) ev->setFrame(val); } +void Event::setLenTick(unsigned val) { if(ev) ev->setLenTick(val); } +void Event::setLenFrame(unsigned val) { if(ev) ev->setLenFrame(val); } +unsigned Event::lenTick() const { return ev ? ev->lenTick() : 0; } +unsigned Event::lenFrame() const { return ev ? ev->lenFrame() : 0; } +unsigned Event::lenValue() const { return ev ? ev->lenValue() : 0; } +Pos Event::end() const { return ev ? ev->end() : Pos(); } +unsigned Event::endTick() const { return ev ? ev->end().tick() : 0; } +unsigned Event::endFrame() const { return ev ? ev->end().frame() : 0; } +void Event::setPos(const Pos& p) { if(ev) ev->setPos(p); } } // namespace MusECore diff -Nru muse-2.1.2/muse/event.h muse-3.0.2+ds1/muse/event.h --- muse-2.1.2/muse/event.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/event.h 2018-01-29 20:07:03.000000000 +0000 @@ -27,8 +27,10 @@ #include #include +#include "type_defs.h" #include "pos.h" #include "evdata.h" +#include "mpevent.h" #include "wave.h" // for SndFileR class QString; @@ -53,18 +55,27 @@ public: Event(); Event(EventType t); - Event(const Event& e); - Event(EventBase* eb); + Event(const Event& e); // Creates a true shared clone of the event. They share the same event base pointer. + Event(EventBase* eb); // Wraps an event base in an event and increases the event base's ref count. virtual ~Event(); + MidiPlayEvent asMidiPlayEvent(unsigned time, int port, int channel) const; + bool empty() const; EventType type() const; + // Shared and non-shared clone events have the same id. An empty event returns MUSE_INVALID_EVENT_ID. + EventID_t id() const; + void shareId(const Event& e); // Makes id same as given event's. Effectively makes the events non-shared clones. void setType(EventType t); - Event& operator=(const Event& e); + Event& operator=(const Event& e); // Makes the two events true shared clones. They share the same event base pointer. + + virtual void assign(const Event&); // Assigns to this event, excluding the id. Including its clones. + bool operator==(const Event& e) const; - + bool isSimilarTo(const Event& other) const; + int getRefCount() const; bool selected() const; void setSelected(bool val); @@ -73,8 +84,17 @@ void read(Xml& xml); void write(int a, Xml& xml, const Pos& offset, bool ForceWavePaths = false) const; void dump(int n = 0) const; - Event clone(); - Event mid(unsigned a, unsigned b); + // Creates a non-shared clone of the event base, having the same 'group' id. + // NOTE: Certain pointer members may still be SHARED. Such as the sysex MidiEventBase::edata. + // Be aware when iterating or modifying clones. + Event clone() const; + + // Restores id to original uniqueId, removing the event from any clone 'group'. + void deClone(); + // Creates a copy of the event base, excluding the 'group' _id. + Event duplicate() const; + + Event mid(unsigned a, unsigned b) const; bool isNote() const; bool isNoteOff() const; @@ -109,11 +129,13 @@ void setTick(unsigned val); unsigned tick() const; unsigned frame() const; + unsigned posValue() const; void setFrame(unsigned val); void setLenTick(unsigned val); void setLenFrame(unsigned val); unsigned lenTick() const; unsigned lenFrame() const; + unsigned lenValue() const; Pos end() const; unsigned endTick() const; unsigned endFrame() const; @@ -124,7 +146,7 @@ typedef EL::iterator iEvent; typedef EL::reverse_iterator riEvent; typedef EL::const_iterator ciEvent; -typedef std::pair EventRange; +typedef std::pair EventRange; //--------------------------------------------------------- // EventList @@ -132,21 +154,23 @@ //--------------------------------------------------------- class EventList : public EL { - int ref; // number of references to this EventList - int aref; // number of active references (exclude undo list) void deselect(); public: - EventList() { ref = 0; aref = 0; } - ~EventList() {} - - void incRef(int n) { ref += n; } - int refCount() const { return ref; } - void incARef(int n) { aref += n; } - int arefCount() const { return aref; } - + ciEvent find(const Event&) const; iEvent find(const Event&); - iEvent add(Event& event); + ciEvent findSimilar(const Event&) const; + iEvent findSimilar(const Event&); + ciEvent findId(const Event&) const; // Fast, index t is known. + iEvent findId(const Event&); // Fast, index t is known. + ciEvent findId(unsigned t, EventID_t id) const; // Fast, index t is known. + iEvent findId(unsigned t, EventID_t id); // Fast, index t is known. + ciEvent findId(EventID_t id) const; // Slow, index t is not known + iEvent findId(EventID_t id); // Slow, index t is not known + ciEvent findWithId(const Event&) const; // Finds event base or event id. Fast, index t is known. + iEvent findWithId(const Event&); // Finds event base or event id. Fast, index t is known. + + iEvent add(Event event); void move(Event& event, unsigned tick); void dump() const; void read(Xml& xml, const char* name, bool midi); diff -Nru muse-2.1.2/muse/eventlist.cpp muse-3.0.2+ds1/muse/eventlist.cpp --- muse-2.1.2/muse/eventlist.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/eventlist.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -62,7 +62,7 @@ // add //--------------------------------------------------------- -iEvent EventList::add(Event& event) +iEvent EventList::add(Event event) { // Changed by Tim. An event list containing wave events should be sorted by // frames. WaveTrack::fetchData() relies on the sorting order, and @@ -129,15 +129,133 @@ //--------------------------------------------------------- iEvent EventList::find(const Event& event) +{ + std::pair range = equal_range(event.type() == Wave ? event.frame() : event.tick()); + + for (iEvent i = range.first; i != range.second; ++i) { + if (i->second == event) + return i; + } + return end(); +} + +ciEvent EventList::find(const Event& event) const { EventRange range = equal_range(event.type() == Wave ? event.frame() : event.tick()); - for (iEvent i = range.first; i != range.second; ++i) { + for (ciEvent i = range.first; i != range.second; ++i) { if (i->second == event) return i; } return end(); + } + +iEvent EventList::findSimilar(const Event& event) +{ + std::pair range = equal_range(event.type() == Wave ? event.frame() : event.tick()); + + for (iEvent i = range.first; i != range.second; ++i) { + if (i->second.isSimilarTo(event)) + return i; + } + return end(); +} + +ciEvent EventList::findSimilar(const Event& event) const + { + EventRange range = equal_range(event.type() == Wave ? event.frame() : event.tick()); + + + for (ciEvent i = range.first; i != range.second; ++i) { + if (i->second.isSimilarTo(event)) + return i; + } + return end(); + } + +iEvent EventList::findId(const Event& event) +{ + std::pair range = equal_range(event.type() == Wave ? event.frame() : event.tick()); + + for (iEvent i = range.first; i != range.second; ++i) { + if (i->second.id() == event.id()) + return i; + } + return end(); +} + +ciEvent EventList::findId(const Event& event) const + { + EventRange range = equal_range(event.type() == Wave ? event.frame() : event.tick()); + + + for (ciEvent i = range.first; i != range.second; ++i) { + if (i->second.id() == event.id()) + return i; + } + return end(); + } + +iEvent EventList::findId(unsigned t, EventID_t id) +{ + std::pair range = equal_range(t); + for (iEvent i = range.first; i != range.second; ++i) { + if (i->second.id() == id) + return i; + } + return end(); +} + +ciEvent EventList::findId(unsigned t, EventID_t id) const + { + EventRange range = equal_range(t); + for (ciEvent i = range.first; i != range.second; ++i) { + if (i->second.id() == id) + return i; + } + return end(); + } + +iEvent EventList::findId(EventID_t id) +{ + for (iEvent i = begin(); i != end(); ++i) { + if (i->second.id() == id) + return i; + } + return end(); +} + +ciEvent EventList::findId(EventID_t id) const + { + for (ciEvent i = begin(); i != end(); ++i) { + if (i->second.id() == id) + return i; + } + return end(); + } + +iEvent EventList::findWithId(const Event& event) +{ + std::pair range = equal_range(event.type() == Wave ? event.frame() : event.tick()); + + for (iEvent i = range.first; i != range.second; ++i) { + if (i->second == event || i->second.id() == event.id()) + return i; + } + return end(); +} + +ciEvent EventList::findWithId(const Event& event) const + { + EventRange range = equal_range(event.type() == Wave ? event.frame() : event.tick()); + + + for (ciEvent i = range.first; i != range.second; ++i) { + if (i->second == event || i->second.id() == event.id()) + return i; + } + return end(); } //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/exportmidi.cpp muse-3.0.2+ds1/muse/exportmidi.cpp --- muse-2.1.2/muse/exportmidi.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/exportmidi.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: exportmidi.cpp,v 1.9.2.1 2009/04/01 01:37:10 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012, 2016 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 @@ -49,10 +49,10 @@ static void addController(MPEventList* l, int tick, int port, int channel, int a, int b) { - if (a < CTRL_14_OFFSET) { // 7 Bit Controller + if (a >= CTRL_7_OFFSET && a < (CTRL_7_OFFSET + 0x10000)) { // 7 Bit Controller l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, a, b)); } - else if (a < CTRL_RPN_OFFSET) { // 14 Bit Controller + else if (a >= CTRL_14_OFFSET && a < (CTRL_14_OFFSET + 0x10000)) { // 14 Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; @@ -60,14 +60,14 @@ l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, ctrlH, dataH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, ctrlL, dataL)); } - else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller + else if (a >= CTRL_RPN_OFFSET && a < (CTRL_RPN_OFFSET + 0x10000)) { // RPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HRPN, ctrlH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LRPN, ctrlL)); l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, b)); } - else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller + else if (a >= CTRL_NRPN_OFFSET && a < (CTRL_NRPN_OFFSET + 0x10000)) { // NRPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); @@ -75,9 +75,9 @@ l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, b)); } else if (a == CTRL_PITCH) { - int a = b + 8192; - int b = a >> 7; - l->add(MidiPlayEvent(tick, port, channel, ME_PITCHBEND, a & 0x7f, b & 0x7f)); + int r_a = b + 8192; + int r_b = r_a >> 7; + l->add(MidiPlayEvent(tick, port, channel, ME_PITCHBEND, r_a & 0x7f, r_b & 0x7f)); } else if (a == CTRL_PROGRAM) { int hb = (b >> 16) & 0xff; @@ -103,7 +103,17 @@ // } l->add(MidiPlayEvent(tick+tickoffset, port, channel, ME_PROGRAM, pr, 0)); } - else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller + else if(a == CTRL_AFTERTOUCH) + { + l->add(MidiPlayEvent(tick, port, channel, ME_AFTERTOUCH, b & 0x7f, 0)); + } + else if((a | 0xff) == CTRL_POLYAFTER) + { + l->add(MidiPlayEvent(tick, port, channel, ME_POLYAFTER, a & 0x7f, b & 0x7f)); + } + else if (a >= CTRL_INTERNAL_OFFSET && a < CTRL_RPN14_OFFSET) // Unaccounted for internal controller + return; + else if (a >= CTRL_RPN14_OFFSET && a < (CTRL_RPN14_OFFSET + 0x10000)) { // RPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; @@ -113,7 +123,7 @@ l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, dataH)); l->add(MidiPlayEvent(tick+3, port, channel, ME_CONTROLLER, CTRL_LDATA, dataL)); } - else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller + else if (a >= CTRL_NRPN14_OFFSET && a < (CTRL_NRPN14_OFFSET + 0x10000)) { // NRPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; @@ -131,11 +141,12 @@ // track can be NULL meaning no concept of drum notes is allowed in init sequences. //--------------------------------------------------------- -static void addEventList(MusECore::EventList* evlist, MusECore::MPEventList* mpevlist, MusECore::MidiTrack* track, MusECore::Part* part, int port, int channel) +static void addEventList(const MusECore::EventList& evlist, MusECore::MPEventList* mpevlist, MusECore::MidiTrack* track, MusECore::Part* part, int port, int channel) { - for (MusECore::iEvent i = evlist->begin(); i != evlist->end(); ++i) + DrumMap dm; + for (MusECore::ciEvent i = evlist.begin(); i != evlist.end(); ++i) { - MusECore::Event ev = i->second; + const MusECore::Event& ev = i->second; int tick = ev.tick(); if(part) tick += part->tick(); @@ -147,31 +158,64 @@ printf("Warning: midi note has velocity 0, (ignored)\n"); continue; } - int pitch; - if (track && track->type() == MusECore::Track::DRUM) { - // Map drum-notes to the drum-map values - int instr = ev.pitch(); - pitch = MusEGlobal::drumMap[instr].anote; - } - else - pitch = ev.pitch(); + int pitch = ev.pitch(); + int fin_pitch = pitch; + int fin_port = port; + int fin_chan = channel; + + if(MusEGlobal::config.exportDrumMapOverrides) + { + if (track && track->type() == MusECore::Track::DRUM) { + // Map drum-notes to the drum-map values + fin_pitch = MusEGlobal::drumMap[pitch].anote; + // Default to track port if -1 and track channel if -1. + // Port is only allowed to change in format 1. + if(MusEGlobal::drumMap[pitch].port != -1 && MusEGlobal::config.smfFormat != 0) + fin_port = MusEGlobal::drumMap[pitch].port; + if(MusEGlobal::drumMap[pitch].channel != -1) + fin_chan = MusEGlobal::drumMap[pitch].channel; + } + else if (track && track->type() == MusECore::Track::NEW_DRUM) { + // Map drum-notes to the drum-map values + // We must look at what the drum map WOULD say at the note's tick, + // not what it says now at the current cursor. + track->getMapItemAt(tick, pitch, dm, WorkingDrumMapEntry::AllOverrides); + fin_pitch = dm.anote; + // Default to track port if -1 and track channel if -1. + // Port is only allowed to change in format 1. + if(dm.port != -1 && MusEGlobal::config.smfFormat != 0) + fin_port = dm.port; + if(dm.channel != -1) + fin_chan = dm.channel; + } + } + + // If the port or channel is overridden by a drum map, DO NOT add the event here + // because it requires its own track. That is taken care of in exportMidi(). + if(fin_port != port || + // Channel is allowed to be different but can only cause a new track in format 1. + (fin_chan != channel && MusEGlobal::config.exportChannelOverridesToNewTrack && MusEGlobal::config.smfFormat != 0)) + continue; int velo = ev.velo(); + int veloOff = ev.veloOff(); int len = ev.lenTick(); //--------------------------------------- // apply trackinfo values //--------------------------------------- - if (track && (track->transposition + if (track && ((!track->isDrumTrack() && track->transposition) || track->velocity || track->compression != 100 || track->len != 100)) { - pitch += track->transposition; - if (pitch > 127) - pitch = 127; - if (pitch < 0) - pitch = 0; + // Transpose only midi not drum tracks. + if(!track->isDrumTrack()) + fin_pitch += track->transposition; + if (fin_pitch > 127) + fin_pitch = 127; + if (fin_pitch < 0) + fin_pitch = 0; velo += track->velocity; velo = (velo * track->compression) / 100; @@ -179,21 +223,70 @@ velo = 127; if (velo < 1) // no off event velo = 1; + // REMOVE Tim. Noteoff. Added. Zero means zero. Should mean no note at all? + // Although we might not actually play such an event in the midi.cpp engine, + // I don't like the idea of discarding the event during export. + // Keeping the notes is more important than the velocities, I'd say. + //break; + len = (len * track->len) / 100; } if (len <= 0) len = 1; - mpevlist->add(MusECore::MidiPlayEvent(tick, port, channel, MusECore::ME_NOTEON, pitch, velo)); - + + mpevlist->add(MusECore::MidiPlayEvent(tick, fin_port, fin_chan, MusECore::ME_NOTEON, fin_pitch, velo)); if(MusEGlobal::config.expOptimNoteOffs) // Save space by replacing note offs with note on velocity 0 - mpevlist->add(MusECore::MidiPlayEvent(tick+len, port, channel, MusECore::ME_NOTEON, pitch, 0)); - else - mpevlist->add(MusECore::MidiPlayEvent(tick+len, port, channel, MusECore::ME_NOTEOFF, pitch, velo)); + mpevlist->add(MusECore::MidiPlayEvent(tick+len, fin_port, fin_chan, MusECore::ME_NOTEON, fin_pitch, 0)); + else + mpevlist->add(MusECore::MidiPlayEvent(tick+len, fin_port, fin_chan, MusECore::ME_NOTEOFF, fin_pitch, veloOff)); } break; case MusECore::Controller: - addController(mpevlist, tick, port, channel, ev.dataA(), ev.dataB()); + { + int ctlnum = ev.dataA(); + int fin_ctlnum = ctlnum; + int fin_port = port; + int fin_chan = channel; + + if(MusEGlobal::config.exportDrumMapOverrides) + { + // Is it a drum controller event, according to the track port's instrument? + if(MusEGlobal::midiPorts[port].drumController(ctlnum)) + { + if (track && track->type() == MusECore::Track::DRUM) { + int v_idx = ctlnum & 0x7f; + fin_ctlnum = (ctlnum & ~0xff) | MusEGlobal::drumMap[v_idx].anote; + // Default to track port if -1 and track channel if -1. + // Port is only allowed to change in format 1. + if(MusEGlobal::drumMap[v_idx].port != -1 && MusEGlobal::config.smfFormat != 0) + fin_port = MusEGlobal::drumMap[v_idx].port; + if(MusEGlobal::drumMap[v_idx].channel != -1) + fin_chan = MusEGlobal::drumMap[v_idx].channel; + } + else if (track && track->type() == MusECore::Track::NEW_DRUM) { + int v_idx = ctlnum & 0x7f; + track->getMapItemAt(tick, v_idx, dm, WorkingDrumMapEntry::AllOverrides); + fin_ctlnum = (ctlnum & ~0xff) | dm.anote; + // Default to track port if -1 and track channel if -1. + // Port is only allowed to change in format 1. + if(dm.port != -1 && MusEGlobal::config.smfFormat != 0) + fin_port = dm.port; + if(dm.channel != -1) + fin_chan = dm.channel; + } + } + } + + // If the port or channel is overridden by a drum map, DO NOT add the event here + // because it requires its own track. That is taken care of in exportMidi(). + if(fin_port != port || + // Channel is allowed to be different but can only cause a new track in format 1. + (fin_chan != channel && MusEGlobal::config.exportChannelOverridesToNewTrack && MusEGlobal::config.smfFormat != 0)) + continue; + + addController(mpevlist, tick, fin_port, fin_chan, fin_ctlnum, ev.dataB()); + } break; case MusECore::Sysex: @@ -217,8 +310,87 @@ } } } - - + +static void writeDeviceOrPortMeta(int port, MPEventList* mpel) +{ + if(port >= 0 && port < MIDI_PORTS) + { + if(MusEGlobal::config.exportPortsDevices & MusEGlobal::PORT_NUM_META) + { + unsigned char port_char = (unsigned char)port; + MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, &port_char, 1); + ev.setA(MusECore::ME_META_PORT_CHANGE); // Meta port change + //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! + mpel->add(ev); + } + + if(MusEGlobal::config.exportPortsDevices & MusEGlobal::DEVICE_NAME_META) + { + MusECore::MidiDevice* dev = MusEGlobal::midiPorts[port].device(); + const char* str; + int len; + QByteArray ba; + if(dev && !dev->name().isEmpty()) + ba = dev->name().toLatin1(); + else + ba = QString::number(port).toLatin1(); + str = ba.constData(); + len = ba.length(); + MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)str, len); + ev.setA(MusECore::ME_META_TEXT_9_DEVICE_NAME); // Meta Device Name + //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! + mpel->add(ev); + } + } +} + +static void writeInitSeqOrInstrNameMeta(int port, int channel, MPEventList* mpel) +{ + if(MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument()) + { + //-------------------------- + // Port midi init sequence + //-------------------------- + if(MusEGlobal::config.exportModeInstr & MusEGlobal::MODE_SYSEX) + { + EventList* el = instr->midiInit(); + if(!el->empty()) + addEventList(*el, mpel, NULL, NULL, port, channel); // No track or part passed for init sequences + } + + //-------------------------- + // Instrument Name meta + //-------------------------- + if(!instr->iname().isEmpty() && + (MusEGlobal::config.exportModeInstr & MusEGlobal::INSTRUMENT_NAME_META)) + { + const char* str; + int len; + QByteArray ba = instr->iname().toLatin1(); + str = ba.constData(); + len = ba.length(); + MidiPlayEvent ev(0, port, ME_META, (const unsigned char*)str, len); + ev.setA(ME_META_TEXT_4_INSTRUMENT_NAME); // Meta Instrument Name + //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! + mpel->add(ev); + } + } +} + +static void writeTrackNameMeta(int port, Track* track, MPEventList* mpel) +{ + if (!track->name().isEmpty()) + { + QByteArray ba = track->name().toLatin1(); + const char* name = ba.constData(); + int len = ba.length(); + MidiPlayEvent ev(0, port, ME_META, (const unsigned char*)name, len); + ev.setA(ME_META_TEXT_3_TRACK_NAME); // Meta Sequence/Track Name + //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! + mpel->add(ev); + } +} + } // namespace MusECore namespace MusEGui { @@ -268,9 +440,10 @@ MusECore::TrackList* tl = MusEGlobal::song->tracks(); // Changed to full track list so user can rearrange tracks. MusECore::MidiFileTrackList* mtl = new MusECore::MidiFileTrackList; + MusECore::DrumMap dm; std::set used_ports; - int i = 0; + int track_count = 0; MusECore::MidiFileTrack* mft = 0; for (MusECore::ciTrack im = tl->begin(); im != tl->end(); ++im) { @@ -279,7 +452,7 @@ MusECore::MidiTrack* track = (MusECore::MidiTrack*)(*im); - if (i == 0 || MusEGlobal::config.smfFormat != 0) // Changed to single track. Tim + if (track_count == 0 || MusEGlobal::config.smfFormat != 0) // Changed to single track. Tim { mft = new MusECore::MidiFileTrack; mtl->push_back(mft); @@ -297,7 +470,7 @@ // - tempo map //--------------------------------------------------- - if (i == 0) { + if (track_count == 0) { //--------------------------------------------------- // Write Track Marker // @@ -398,18 +571,8 @@ // track name //----------------------------------- - if (i == 0 || MusEGlobal::config.smfFormat != 0) - { - if (!track->name().isEmpty()) { - QByteArray ba = track->name().toLatin1(); - const char* name = ba.constData(); - int len = ba.length(); - MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)name, len); - ev.setA(MusECore::ME_META_TEXT_3_TRACK_NAME); // Meta Sequence/Track Name - //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! - l->add(ev); - } - } + if (track_count == 0 || MusEGlobal::config.smfFormat != 0) + MusECore::writeTrackNameMeta(port, track, l); //----------------------------------- // track comment @@ -432,40 +595,8 @@ // Write device name or port change meta //----------------------------------------- - if((i == 0 && MusEGlobal::config.exportPortDeviceSMF0) || (MusEGlobal::config.smfFormat != 0)) - { - if(port >= 0 && port < MIDI_PORTS) - { - if(MusEGlobal::config.exportPortsDevices == MusEGlobal::EXPORT_PORTS_DEVICES_ALL || - MusEGlobal::config.exportPortsDevices == MusEGlobal::PORT_NUM_META) - { - unsigned char port_char = (unsigned char)port; - MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, &port_char, 1); - ev.setA(MusECore::ME_META_PORT_CHANGE); // Meta port change - //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! - l->add(ev); - } - - if(MusEGlobal::config.exportPortsDevices == MusEGlobal::EXPORT_PORTS_DEVICES_ALL || - MusEGlobal::config.exportPortsDevices == MusEGlobal::DEVICE_NAME_META) - { - MusECore::MidiDevice* dev = MusEGlobal::midiPorts[port].device(); - const char* str; - int len; - QByteArray ba; - if(dev && !dev->name().isEmpty()) - ba = dev->name().toLatin1(); - else - ba = QString::number(port).toLatin1(); - str = ba.constData(); - len = ba.length(); - MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)str, len); - ev.setA(MusECore::ME_META_TEXT_9_DEVICE_NAME); // Meta Device Name - //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! - l->add(ev); - } - } - } + if((track_count == 0 && MusEGlobal::config.exportPortDeviceSMF0) || (MusEGlobal::config.smfFormat != 0)) + MusECore::writeDeviceOrPortMeta(port, l); //--------------------------------------------------- // Write midi port init sequence: GM/GS/XG etc. @@ -477,64 +608,285 @@ { if(port >= 0 && port < MIDI_PORTS) { - MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); - if(instr) - { - if(i == 0 || MusEGlobal::config.smfFormat != 0) - { - //-------------------------- - // Port midi init sequence - //-------------------------- - if(MusEGlobal::config.exportModeInstr == MusEGlobal::EXPORT_MODE_INSTR_ALL || - MusEGlobal::config.exportModeInstr == MusEGlobal::MODE_SYSEX) - { - MusECore::EventList* el = instr->midiInit(); - if(!el->empty()) - MusECore::addEventList(el, l, NULL, NULL, port, channel); // No track or part passed for init sequences - } - - //-------------------------- - // Instrument Name meta - //-------------------------- - if(!instr->iname().isEmpty() && - (MusEGlobal::config.exportModeInstr == MusEGlobal::EXPORT_MODE_INSTR_ALL || - MusEGlobal::config.exportModeInstr == MusEGlobal::INSTRUMENT_NAME_META)) - { - const char* str; - int len; - QByteArray ba = instr->iname().toLatin1(); - str = ba.constData(); - len = ba.length(); - MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)str, len); - ev.setA(MusECore::ME_META_TEXT_4_INSTRUMENT_NAME); // Meta Instrument Name - //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! - l->add(ev); - } - } - } + if(track_count == 0 || MusEGlobal::config.smfFormat != 0) + MusECore::writeInitSeqOrInstrNameMeta(port, channel, l); used_ports.insert(port); } } + //--------------------------------------------------- + // Write all track events. + //--------------------------------------------------- + MusECore::PartList* parts = track->parts(); for (MusECore::iPart p = parts->begin(); p != parts->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart*) (p->second); - MusECore::EventList* evlist = part->events(); - MusECore::addEventList(evlist, l, track, part, port, channel); + MusECore::addEventList(part->events(), l, track, part, port, channel); } - ++i; + ++track_count; } - + + // For drum tracks with drum map port overrides, we may need to add extra tracks. + // But we can can only do that if multi-track format is chosen. + if(MusEGlobal::config.exportDrumMapOverrides && MusEGlobal::config.smfFormat != 0) + { + MusECore::MidiFileTrackList aux_mtl; + for (MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) + { + if(!(*it)->isDrumTrack()) + continue; + + MusECore::MidiTrack* track = static_cast(*it); + + int port = track->outPort(); + int channel = track->outChannel(); + + MusECore::PartList* parts = track->parts(); + for (MusECore::iPart ip = parts->begin(); ip != parts->end(); ++ip) + { + MusECore::MidiPart* part = (MusECore::MidiPart*) (ip->second); + const MusECore::EventList& evlist = part->events(); + for (MusECore::ciEvent iev = evlist.begin(); iev != evlist.end(); ++iev) + { + const MusECore::Event& ev = iev->second; + int tick = ev.tick() + part->tick(); + switch (ev.type()) + { + case MusECore::Note: + { + if (ev.velo() == 0) { + fprintf(stderr, "Warning: midi note has velocity 0, (ignored)\n"); + continue; + } + int pitch; + int fin_port = port; + int fin_chan = channel; + + if (track->type() == MusECore::Track::DRUM) { + // Map drum-notes to the drum-map values + int instr = ev.pitch(); + pitch = MusEGlobal::drumMap[instr].anote; + // Default to track port if -1 and track channel if -1. + if(MusEGlobal::drumMap[instr].port != -1) + fin_port = MusEGlobal::drumMap[instr].port; + if(MusEGlobal::drumMap[instr].channel != -1) + fin_chan = MusEGlobal::drumMap[instr].channel; + } + else if (track->type() == MusECore::Track::NEW_DRUM) { + // Map drum-notes to the drum-map values + // We must look at what the drum map WOULD say at the note's tick, + // not what it says now at the current cursor. + int instr = ev.pitch(); + track->getMapItemAt(tick, instr, dm, MusECore::WorkingDrumMapEntry::AllOverrides); + pitch = dm.anote; + // Default to track port if -1 and track channel if -1. + if(dm.port != -1) + fin_port = dm.port; + if(dm.channel != -1) + fin_chan = dm.channel; + } + + // We are only looking for port or channel overrides. They require a separate track. + if(fin_port == port && + (fin_chan == channel || !MusEGlobal::config.exportChannelOverridesToNewTrack)) + continue; + + // Is there already a MidiFileTrack for the port? + MusECore::MidiFileTrack* aux_mft = 0; + for(MusECore::iMidiFileTrack imft = aux_mtl.begin(); imft != aux_mtl.end(); ++imft) + { + MusECore::MidiFileTrack* t = *imft; + // Take the first event's port (in this case they are all the same). + const MusECore::MidiPlayEvent& mpe = *t->events.begin(); + if(mpe.port() == fin_port) + { + aux_mft = t; + break; + } + } + // If not, create a new MidiFileTrack. + if(!aux_mft) + { + aux_mft = new MusECore::MidiFileTrack; + aux_mft->_isDrumTrack = true; + + //----------------------------------- + // track name + //----------------------------------- + // TODO: Maybe append some text here? + MusECore::writeTrackNameMeta(fin_port, track, &aux_mft->events); + + //----------------------------------------- + // Write device name or port change meta + //----------------------------------------- + MusECore::writeDeviceOrPortMeta(fin_port, &aux_mft->events); + + //--------------------------------------------------- + // Write midi port init sequence: GM/GS/XG etc. + // and Instrument Name meta. + //--------------------------------------------------- + std::set::iterator iup = used_ports.find(fin_port); + if(iup == used_ports.end()) + { + MusECore::writeInitSeqOrInstrNameMeta(fin_port, fin_chan, &aux_mft->events); + used_ports.insert(fin_port); + } + + aux_mtl.push_back(aux_mft); + mtl->push_back(aux_mft); + + // Increment the track count. + ++track_count; + } + + int velo = ev.velo(); + int veloOff = ev.veloOff(); + int len = ev.lenTick(); + + //--------------------------------------- + // apply trackinfo values + //--------------------------------------- + + if (track && (track->velocity + || track->compression != 100 + || track->len != 100)) { + if (pitch > 127) + pitch = 127; + if (pitch < 0) + pitch = 0; + + velo += track->velocity; + velo = (velo * track->compression) / 100; + if (velo > 127) + velo = 127; + if (velo < 1) // no off event + velo = 1; + // REMOVE Tim. Noteoff. Added. Zero means zero. Should mean no note at all? + // Although we might not actually play such an event in the midi.cpp engine, + // I don't like the idea of discarding the event during export. + // Keeping the notes is more important than the velocities, I'd say. + //break; + + len = (len * track->len) / 100; + } + if (len <= 0) + len = 1; + + aux_mft->events.add(MusECore::MidiPlayEvent(tick, fin_port, fin_chan, MusECore::ME_NOTEON, pitch, velo)); + if(MusEGlobal::config.expOptimNoteOffs) // Save space by replacing note offs with note on velocity 0 + aux_mft->events.add(MusECore::MidiPlayEvent(tick+len, fin_port, fin_chan, MusECore::ME_NOTEON, pitch, 0)); + else + aux_mft->events.add(MusECore::MidiPlayEvent(tick+len, fin_port, fin_chan, MusECore::ME_NOTEOFF, pitch, veloOff)); + } + break; + + case MusECore::Controller: + { + int fin_port = port; + int fin_chan = channel; + int ctlnum = ev.dataA(); + int fin_ctlnum = ctlnum; + + // Is it a drum controller event, according to the track port's instrument? + if(MusEGlobal::midiPorts[port].drumController(ctlnum)) + { + if (track->type() == MusECore::Track::DRUM) { + int v_idx = ctlnum & 0x7f; + fin_ctlnum = (ctlnum & ~0xff) | MusEGlobal::drumMap[v_idx].anote; + // Default to track port if -1 and track channel if -1. + if(MusEGlobal::drumMap[v_idx].port != -1) + fin_port = MusEGlobal::drumMap[v_idx].port; + if(MusEGlobal::drumMap[v_idx].channel != -1) + fin_chan = MusEGlobal::drumMap[v_idx].channel; + } + else if (track->type() == MusECore::Track::NEW_DRUM) { + int v_idx = ctlnum & 0x7f; + track->getMapItemAt(tick, v_idx, dm, MusECore::WorkingDrumMapEntry::AllOverrides); + fin_ctlnum = (ctlnum & ~0xff) | dm.anote; + if(dm.port != -1) + fin_port = dm.port; + if(dm.channel != -1) + fin_chan = dm.channel; + } + } + + // We are only looking for port or channel overrides. They require a separate track. + if(fin_port == port && + (fin_chan == channel || !MusEGlobal::config.exportChannelOverridesToNewTrack)) + continue; + + // Is there already a MidiFileTrack for the port? + MusECore::MidiFileTrack* aux_mft = 0; + for(MusECore::iMidiFileTrack imft = aux_mtl.begin(); imft != aux_mtl.end(); ++imft) + { + MusECore::MidiFileTrack* t = *imft; + // Take the first event's port (in this case they are all the same). + const MusECore::MidiPlayEvent& mpe = *t->events.begin(); + if(mpe.port() == fin_port) + { + aux_mft = t; + break; + } + } + // If not, create a new MidiFileTrack. + if(!aux_mft) + { + aux_mft = new MusECore::MidiFileTrack; + aux_mft->_isDrumTrack = true; + + //----------------------------------- + // track name + //----------------------------------- + // TODO: Maybe append some text here? + MusECore::writeTrackNameMeta(fin_port, track, &aux_mft->events); + + //----------------------------------------- + // Write device name or port change meta + //----------------------------------------- + MusECore::writeDeviceOrPortMeta(fin_port, &aux_mft->events); + + //--------------------------------------------------- + // Write midi port init sequence: GM/GS/XG etc. + // and Instrument Name meta. + //--------------------------------------------------- + std::set::iterator iup = used_ports.find(fin_port); + if(iup == used_ports.end()) + { + MusECore::writeInitSeqOrInstrNameMeta(fin_port, fin_chan, &aux_mft->events); + used_ports.insert(fin_port); + } + + aux_mtl.push_back(aux_mft); + mtl->push_back(aux_mft); + + // Increment the track count. + ++track_count; + } + + MusECore::addController(&aux_mft->events, tick, fin_port, fin_chan, fin_ctlnum, ev.dataB()); + } + break; + + case MusECore::Sysex: + case MusECore::Meta: + case MusECore::Wave: + break; + } + } + } + } + } + + + + + mf.setDivision(MusEGlobal::config.midiDivision); - mf.setTrackList(mtl, i); + // Takes ownership of mtl and its contents. + mf.setTrackList(mtl, track_count); mf.write(); - - // DELETETHIS 4 ??? or is this still an issue? - // TESTING: Cleanup. I did not valgrind this feature in last memleak fixes, but I suspect it leaked. - //for(MusECore::iMidiFileTrack imft = mtl->begin(); imft != mtl->end(); ++imft) - // delete *imft; - //delete mtl; + } } // namespace MusEGui diff -Nru muse-2.1.2/muse/functions.cpp muse-3.0.2+ds1/muse/functions.cpp --- muse-2.1.2/muse/functions.cpp 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/functions.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -2,7 +2,7 @@ // MusE // Linux Music Editor // $Id: functions.cpp,v 1.20.2.19 2011/05/05 20:10 flo93 Exp $ -// (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) +// (C) Copyright 2011,2013 Florian Jung (flo93@sourceforge.net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -71,9 +71,9 @@ -set partlist_to_set(PartList* pl) +set partlist_to_set(PartList* pl) { - set result; + set result; for (PartList::iterator it=pl->begin(); it!=pl->end(); it++) result.insert(it->second); @@ -81,16 +81,16 @@ return result; } -set part_to_set(Part* p) +set part_to_set(const Part* p) { - set result; + set result; result.insert(p); return result; } -set get_all_parts() +set get_all_parts() { - set result; + set result; TrackList* tracks=MusEGlobal::song->tracks(); for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++) @@ -103,9 +103,9 @@ return result; } -set get_all_selected_parts() +set get_all_selected_parts() { - set result; + set result; TrackList* tracks=MusEGlobal::song->tracks(); for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++) @@ -137,14 +137,14 @@ } -map get_events(const set& parts, int range) +map get_events(const set& parts, int range) { - map events; + map events; - for (set::iterator part=parts.begin(); part!=parts.end(); part++) - for (iEvent event=(*part)->events()->begin(); event!=(*part)->events()->end(); event++) + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (ciEvent event=(*part)->events().begin(); event!=(*part)->events().end(); event++) if (is_relevant(event->second, *part, range)) - events.insert(pair(&event->second, *part)); + events.insert(pair(&event->second, *part)); return events; } @@ -152,7 +152,7 @@ -bool modify_notelen(const set& parts) +bool modify_notelen(const set& parts) { if (!MusEGui::gatetime_dialog->exec()) return false; @@ -162,7 +162,7 @@ return true; } -bool modify_velocity(const set& parts) +bool modify_velocity(const set& parts) { if (!MusEGui::velocity_dialog->exec()) return false; @@ -172,7 +172,7 @@ return true; } -bool quantize_notes(const set& parts) +bool quantize_notes(const set& parts) { if (!MusEGui::quantize_dialog->exec()) return false; @@ -185,7 +185,7 @@ return true; } -bool erase_notes(const set& parts) +bool erase_notes(const set& parts) { if (!MusEGui::erase_dialog->exec()) return false; @@ -196,7 +196,7 @@ return true; } -bool delete_overlaps(const set& parts) +bool delete_overlaps(const set& parts) { if (!MusEGui::del_overlaps_dialog->exec()) return false; @@ -206,7 +206,7 @@ return true; } -bool set_notelen(const set& parts) +bool set_notelen(const set& parts) { if (!MusEGui::set_notelen_dialog->exec()) return false; @@ -216,7 +216,7 @@ return true; } -bool move_notes(const set& parts) +bool move_notes(const set& parts) { if (!MusEGui::move_notes_dialog->exec()) return false; @@ -226,7 +226,7 @@ return true; } -bool transpose_notes(const set& parts) +bool transpose_notes(const set& parts) { if (!MusEGui::transpose_dialog->exec()) return false; @@ -236,7 +236,7 @@ return true; } -bool crescendo(const set& parts) +bool crescendo(const set& parts) { if (MusEGlobal::song->rpos() <= MusEGlobal::song->lpos()) { @@ -252,7 +252,7 @@ return true; } -bool legato(const set& parts) +bool legato(const set& parts) { if (!MusEGui::legato_dialog->exec()) return false; @@ -269,7 +269,7 @@ if (!MusEGui::gatetime_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::gatetime_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -285,7 +285,7 @@ if (!MusEGui::velocity_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::velocity_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -301,7 +301,7 @@ if (!MusEGui::quantize_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::quantize_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -320,7 +320,7 @@ if (!MusEGui::erase_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::erase_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -337,7 +337,7 @@ if (!MusEGui::del_overlaps_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::del_overlaps_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -353,7 +353,7 @@ if (!MusEGui::set_notelen_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::set_notelen_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -369,7 +369,7 @@ if (!MusEGui::move_notes_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::move_notes_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -385,7 +385,7 @@ if (!MusEGui::transpose_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::transpose_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -407,7 +407,7 @@ if (!MusEGui::crescendo_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::crescendo_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -423,7 +423,7 @@ if (!MusEGui::legato_dialog->exec()) return false; - set parts; + set parts; if (MusEGui::legato_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) parts=get_all_selected_parts(); else @@ -439,17 +439,17 @@ -bool modify_velocity(const set& parts, int range, int rate, int offset) +bool modify_velocity(const set& parts, int range, int rate, int offset) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; if ( (!events.empty()) && ((rate!=100) || (offset!=0)) ) { - for (map::iterator it=events.begin(); it!=events.end(); it++) + for (map::iterator it=events.begin(); it!=events.end(); it++) { - Event& event=*(it->first); - Part* part=it->second; + const Event& event=*(it->first); + const Part* part=it->second; int velo = event.velo(); @@ -475,17 +475,17 @@ return false; } -bool modify_off_velocity(const set& parts, int range, int rate, int offset) +bool modify_off_velocity(const set& parts, int range, int rate, int offset) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; if ( (!events.empty()) && ((rate!=100) || (offset!=0)) ) { - for (map::iterator it=events.begin(); it!=events.end(); it++) + for (map::iterator it=events.begin(); it!=events.end(); it++) { - Event& event=*(it->first); - Part* part=it->second; + const Event& event=*(it->first); + const Part* part=it->second; int velo = event.veloOff(); @@ -511,20 +511,20 @@ return false; } -bool modify_notelen(const set& parts, int range, int rate, int offset) +bool modify_notelen(const set& parts, int range, int rate, int offset) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; - map partlen; + map partlen; if ( (!events.empty()) && ((rate!=100) || (offset!=0)) ) { - for (map::iterator it=events.begin(); it!=events.end(); it++) + for (map::iterator it=events.begin(); it!=events.end(); it++) { - Event& event=*(it->first); - Part* part=it->second; + const Event& event=*(it->first); + const Part* part=it->second; - unsigned int len = event.lenTick(); //prevent compiler warning: comparison singed/unsigned + unsigned int len = event.lenTick(); //prevent compiler warning: comparison signed/unsigned len = (len * rate) / 100; len += offset; @@ -543,7 +543,7 @@ } } - for (map::iterator it=partlen.begin(); it!=partlen.end(); it++) + for (map::iterator it=partlen.begin(); it!=partlen.end(); it++) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); return MusEGlobal::song->applyOperationGroup(operations); @@ -552,7 +552,7 @@ return false; } -bool set_notelen(const set& parts, int range, int len) +bool set_notelen(const set& parts, int range, int len) { return modify_notelen(parts, range, 0, len); } @@ -579,17 +579,17 @@ return tick_dest1; } -bool quantize_notes(const set& parts, int range, int raster, bool quant_len, int strength, int swing, int threshold) +bool quantize_notes(const set& parts, int range, int raster, bool quant_len, int strength, int swing, int threshold) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; if (!events.empty()) { - for (map::iterator it=events.begin(); it!=events.end(); it++) + for (map::iterator it=events.begin(); it!=events.end(); it++) { - Event& event=*(it->first); - Part* part=it->second; + const Event& event=*(it->first); + const Part* part=it->second; unsigned begin_tick = event.tick() + part->tick(); int begin_diff = quantize_tick(begin_tick, raster, swing) - begin_tick; @@ -625,17 +625,17 @@ return false; } -bool erase_notes(const set& parts, int range, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used) +bool erase_notes(const set& parts, int range, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; if (!events.empty()) { - for (map::iterator it=events.begin(); it!=events.end(); it++) + for (map::iterator it=events.begin(); it!=events.end(); it++) { - Event& event=*(it->first); - Part* part=it->second; + const Event& event=*(it->first); + const Part* part=it->second; if ( (!velo_thres_used && !len_thres_used) || (velo_thres_used && event.velo() < velo_threshold) || @@ -649,17 +649,17 @@ return false; } -bool transpose_notes(const set& parts, int range, signed int halftonesteps) +bool transpose_notes(const set& parts, int range, signed int halftonesteps) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; if ( (!events.empty()) && (halftonesteps!=0) ) { - for (map::iterator it=events.begin(); it!=events.end(); it++) + for (map::iterator it=events.begin(); it!=events.end(); it++) { - Event& event=*(it->first); - Part* part=it->second; + const Event& event=*(it->first); + const Part* part=it->second; Event newEvent = event.clone(); int pitch = event.pitch()+halftonesteps; @@ -675,9 +675,9 @@ return false; } -bool crescendo(const set& parts, int range, int start_val, int end_val, bool absolute) +bool crescendo(const set& parts, int range, int start_val, int end_val, bool absolute) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; int from=MusEGlobal::song->lpos(); @@ -685,10 +685,10 @@ if ( (!events.empty()) && (to>from) ) { - for (map::iterator it=events.begin(); it!=events.end(); it++) + for (map::iterator it=events.begin(); it!=events.end(); it++) { - Event& event=*(it->first); - Part* part=it->second; + const Event& event=*(it->first); + const Part* part=it->second; unsigned tick = event.tick() + part->tick(); float curr_val= (float)start_val + (float)(end_val-start_val) * (tick-from) / (to-from); @@ -713,18 +713,18 @@ return false; } -bool move_notes(const set& parts, int range, signed int ticks) +bool move_notes(const set& parts, int range, signed int ticks) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; - map partlen; + map partlen; if ( (!events.empty()) && (ticks!=0) ) { - for (map::iterator it=events.begin(); it!=events.end(); it++) + for (map::iterator it=events.begin(); it!=events.end(); it++) { - Event& event=*(it->first); - Part* part=it->second; + const Event& event=*(it->first); + const Part* part=it->second; bool del=false; Event newEvent = event.clone(); @@ -752,7 +752,7 @@ operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false)); } - for (map::iterator it=partlen.begin(); it!=partlen.end(); it++) + for (map::iterator it=partlen.begin(); it!=partlen.end(); it++) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); return MusEGlobal::song->applyOperationGroup(operations); @@ -762,29 +762,29 @@ } -bool delete_overlaps(const set& parts, int range) +bool delete_overlaps(const set& parts, int range) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; - set deleted_events; + set deleted_events; if (!events.empty()) { - for (map::iterator it1=events.begin(); it1!=events.end(); it1++) + for (map::iterator it1=events.begin(); it1!=events.end(); it1++) { - Event& event1=*(it1->first); - Part* part1=it1->second; + const Event& event1=*(it1->first); + const Part* part1=it1->second; // we may NOT optimize by letting it2 start at (it1 +1); this optimisation // is only allowed when events was sorted by time. it is, however, sorted // randomly by pointer. - for (map::iterator it2=events.begin(); it2!=events.end(); it2++) + for (map::iterator it2=events.begin(); it2!=events.end(); it2++) { - Event& event2=*(it2->first); - Part* part2=it2->second; + const Event& event2=*(it2->first); + const Part* part2=it2->second; - if ( (part1->events()==part2->events()) && // part1 and part2 are the same or are duplicates + if ( (part1->isCloneOf(part2)) && // part1 and part2 are the same or are duplicates (&event1 != &event2) && // and event1 and event2 aren't the same (deleted_events.find(&event2) == deleted_events.end()) ) //and event2 hasn't been deleted before { @@ -817,34 +817,34 @@ return false; } -bool legato(const set& parts, int range, int min_len, bool dont_shorten) +bool legato(const set& parts, int range, int min_len, bool dont_shorten) { - map events = get_events(parts, range); + map events = get_events(parts, range); Undo operations; if (min_len<=0) min_len=1; if (!events.empty()) { - for (map::iterator it1=events.begin(); it1!=events.end(); it1++) + for (map::iterator it1=events.begin(); it1!=events.end(); it1++) { - Event& event1=*(it1->first); - Part* part1=it1->second; + const Event& event1=*(it1->first); + const Part* part1=it1->second; unsigned len=INT_MAX; // we may NOT optimize by letting it2 start at (it1 +1); this optimisation // is only allowed when events was sorted by time. it is, however, sorted // randomly by pointer. - for (map::iterator it2=events.begin(); it2!=events.end(); it2++) + for (map::iterator it2=events.begin(); it2!=events.end(); it2++) { - Event& event2=*(it2->first); - Part* part2=it2->second; + const Event& event2=*(it2->first); + const Part* part2=it2->second; bool relevant = (event2.tick() >= event1.tick() + min_len); if (dont_shorten) relevant = relevant && (event2.tick() >= event1.endTick()); - if ( (part1->events()==part2->events()) && // part1 and part2 are the same or are duplicates + if ( (part1->isCloneOf(part2)) && // part1 and part2 are the same or are duplicates relevant && // they're not too near (respect min_len and dont_shorten) (event2.tick()-event1.tick() < len ) ) // that's the nearest relevant following note len=event2.tick()-event1.tick(); @@ -869,7 +869,7 @@ -void copy_notes(const set& parts, int range) +void copy_notes(const set& parts, int range) { QMimeData* drag = selected_events_to_mime(parts,range); @@ -926,7 +926,7 @@ return get_groupedevents_len(s); } -bool paste_notes(Part* paste_into_part) +bool paste_notes(const Part* paste_into_part) { unsigned temp_begin = AL::sigmap.raster1(MusEGlobal::song->cpos(),0); unsigned temp_end = AL::sigmap.raster2(temp_begin + get_clipboard_len(), 0); @@ -943,7 +943,7 @@ return true; } -void paste_notes(int max_distance, bool always_new_part, bool never_new_part, Part* paste_into_part, int amount, int raster) +void paste_notes(int max_distance, bool always_new_part, bool never_new_part, const Part* paste_into_part, int amount, int raster) { QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :( QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); // TODO CHECK Tim. @@ -951,12 +951,12 @@ } // if nothing is selected/relevant, this function returns NULL -QMimeData* selected_events_to_mime(const set& parts, int range) +QMimeData* selected_events_to_mime(const set& parts, int range) { unsigned start_tick = INT_MAX; //will be the tick of the first event or INT_MAX if no events are there - for (set::iterator part=parts.begin(); part!=parts.end(); part++) - for (iEvent ev=(*part)->events()->begin(); ev!=(*part)->events()->end(); ev++) + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (ciEvent ev=(*part)->events().begin(); ev!=(*part)->events().end(); ev++) if (is_relevant(ev->second, *part, range)) if (ev->second.tick() < start_tick) start_tick=ev->second.tick(); @@ -978,10 +978,10 @@ Xml xml(tmp); int level = 0; - for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (set::iterator part=parts.begin(); part!=parts.end(); part++) { xml.tag(level++, "eventlist part_id=\"%d\"", (*part)->sn()); - for (iEvent ev=(*part)->events()->begin(); ev!=(*part)->events()->end(); ev++) + for (ciEvent ev=(*part)->events().begin(); ev!=(*part)->events().end(); ev++) if (is_relevant(ev->second, *part, range)) ev->second.write(level, xml, -start_tick); xml.etag(--level, "eventlist"); @@ -993,7 +993,7 @@ } // if nothing is selected/relevant, this function returns NULL -QMimeData* parts_to_mime(const set& parts) +QMimeData* parts_to_mime(const set& parts) { //--------------------------------------------------- @@ -1012,7 +1012,7 @@ bool midi=false; bool wave=false; - for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (set::iterator part=parts.begin(); part!=parts.end(); part++) { if ((*part)->track()->type() == MusECore::Track::MIDI) midi=true; @@ -1078,7 +1078,7 @@ if (tag == "part_id") *part_id = xml.s2().toInt(); else - printf("unknown attribute '%s' in read_eventlist_and_part(), ignoring it...\n", tag.toAscii().data()); + printf("unknown attribute '%s' in read_eventlist_and_part(), ignoring it...\n", tag.toLatin1().data()); break; case Xml::TagStart: @@ -1102,11 +1102,11 @@ } } -void paste_at(const QString& pt, int pos, int max_distance, bool always_new_part, bool never_new_part, Part* paste_into_part, int amount, int raster) +void paste_at(const QString& pt, int pos, int max_distance, bool always_new_part, bool never_new_part, const Part* paste_into_part, int amount, int raster) { Undo operations; - map expand_map; - map > new_part_map; + map expand_map; + map > new_part_map; QByteArray pt_= pt.toLatin1(); Xml xml(pt_.constData()); @@ -1128,9 +1128,9 @@ if (read_eventlist_and_part(xml, &el, &part_id)) { - Part* dest_part; + const Part* dest_part; Track* dest_track; - Part* old_dest_part; + const Part* old_dest_part; if (paste_into_part == NULL) dest_part = partFromSerialNumber(part_id); @@ -1157,13 +1157,12 @@ if (create_new_part) { - dest_part = dest_track->newPart(); - dest_part->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it - // so we must decrement it first :/ - dest_part->setTick(AL::sigmap.raster1(first_paste_tick, config.division)); + Part* newpart = dest_track->newPart(); + newpart->setTick(AL::sigmap.raster1(first_paste_tick, config.division)); new_part_map[old_dest_part].insert(dest_part); operations.push_back(UndoOp(UndoOp::AddPart, dest_part)); + dest_part = newpart; } for (iEvent i = el.begin(); i != el.end(); ++i) @@ -1177,7 +1176,7 @@ } e.setTick(tick); - e.setSelected(true); + e.setSelected(true); // No need to select clones, AddEvent operation below will take care of that. if (e.endTick() > dest_part->lenTick()) // event exceeds part? { @@ -1218,74 +1217,112 @@ out_of_paste_at_for: - for (map::iterator it = expand_map.begin(); it!=expand_map.end(); it++) + for (map::iterator it = expand_map.begin(); it!=expand_map.end(); it++) if (it->second != it->first->lenTick()) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); MusEGlobal::song->informAboutNewParts(new_part_map); // must be called before apply. otherwise // pointer changes (by resize) screw it up MusEGlobal::song->applyOperationGroup(operations); - MusEGlobal::song->update(SC_SELECTION); + MusEGlobal::song->update(SC_SELECTION | SC_PART_SELECTION); } -void select_all(const std::set& parts) +void select_all(const set& parts) { - for (set::iterator part=parts.begin(); part!=parts.end(); part++) - for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + Undo operations; + operations.combobreaker=true; + + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { - Event& event=ev_it->second; - event.setSelected(true); + const Event& event=ev_it->second; + operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, true, event.selected())); } - MusEGlobal::song->update(SC_SELECTION); + MusEGlobal::song->applyOperationGroup(operations); } -void select_none(const std::set& parts) +void select_none(const set& parts) { - for (set::iterator part=parts.begin(); part!=parts.end(); part++) - for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + Undo operations; + operations.combobreaker=true; + + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { - Event& event=ev_it->second; - event.setSelected(false); + const Event& event=ev_it->second; + operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, false, event.selected())); } - MusEGlobal::song->update(SC_SELECTION); + MusEGlobal::song->applyOperationGroup(operations); } -void select_invert(const std::set& parts) +void select_invert(const set& parts) { - for (set::iterator part=parts.begin(); part!=parts.end(); part++) - for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + Undo operations; + operations.combobreaker=true; + + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { - Event& event=ev_it->second; - event.setSelected(!event.selected()); + const Event& event=ev_it->second; + operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, !event.selected(), event.selected())); } - MusEGlobal::song->update(SC_SELECTION); + MusEGlobal::song->applyOperationGroup(operations); } -void select_in_loop(const std::set& parts) +void select_in_loop(const set& parts) { select_none(parts); - for (set::iterator part=parts.begin(); part!=parts.end(); part++) - for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + Undo operations; + operations.combobreaker=true; + + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { - Event& event=ev_it->second; - event.setSelected((event.tick()>=MusEGlobal::song->lpos() && event.endTick()<=MusEGlobal::song->rpos())); + const Event& event=ev_it->second; + operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, (event.tick()>=MusEGlobal::song->lpos() && event.endTick()<=MusEGlobal::song->rpos()), event.selected())); } - MusEGlobal::song->update(SC_SELECTION); + MusEGlobal::song->applyOperationGroup(operations); } -void select_not_in_loop(const std::set& parts) +void select_not_in_loop(const set& parts) { select_none(parts); - for (set::iterator part=parts.begin(); part!=parts.end(); part++) - for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++) + Undo operations; + operations.combobreaker=true; + + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { - Event& event=ev_it->second; - event.setSelected(!(event.tick()>=MusEGlobal::song->lpos() && event.endTick()<=MusEGlobal::song->rpos())); + const Event& event=ev_it->second; + operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, !(event.tick()>=MusEGlobal::song->lpos() && event.endTick()<=MusEGlobal::song->rpos()), event.selected())); } - MusEGlobal::song->update(SC_SELECTION); + MusEGlobal::song->applyOperationGroup(operations); +} + +bool tracks_are_selected() +{ + const TrackList* tl = MusEGlobal::song->tracks(); + for(ciTrack it = tl->begin(); it != tl->end(); ++it) + if((*it)->selected()) + return true; + return false; +} + +bool parts_are_selected() +{ + const TrackList* tl = MusEGlobal::song->tracks(); + for(ciTrack it = tl->begin(); it != tl->end(); ++it) + { + const PartList* pl = (*it)->cparts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + if(ip->second->selected()) + return true; + } + return false; } + void shrink_parts(int raster) { Undo operations; @@ -1299,10 +1336,9 @@ for (iPart part = (*track)->parts()->begin(); part != (*track)->parts()->end(); part++) if (part->second->selected()) { - EventList* events=part->second->events(); unsigned len=0; - for (iEvent ev=events->begin(); ev!=events->end(); ev++) + for (ciEvent ev=part->second->events().begin(); ev!=part->second->events().end(); ev++) if (ev->second.endTick() > len) len=ev->second.endTick(); @@ -1310,47 +1346,29 @@ if (lensecond->lenTick()) - { - MidiPart* new_part = new MidiPart(*(MidiPart*)part->second); - new_part->setLenTick(len); - operations.push_back(UndoOp(UndoOp::ModifyPart, part->second, new_part, true, false)); - } + operations.push_back(UndoOp(UndoOp::ModifyPartLength, part->second, part->second->lenValue(), len, Pos::TICKS)); } MusEGlobal::song->applyOperationGroup(operations); } -void schedule_resize_all_same_len_clone_parts(Part* part, unsigned new_len, Undo& operations) +void schedule_resize_all_same_len_clone_parts(const Part* part, unsigned new_len, Undo& operations) { QSet already_done; for (Undo::iterator op_it=operations.begin(); op_it!=operations.end();op_it++) - if (op_it->type==UndoOp::ModifyPart || op_it->type==UndoOp::DeletePart) - already_done.insert(op_it->nPart); + if (op_it->type==UndoOp::DeletePart) + already_done.insert(op_it->part); - unsigned old_len= part->type() == Pos::FRAMES ? part->lenFrame() : part->lenTick(); + unsigned old_len = part->lenValue(); if (old_len!=new_len) { - Part* part_it=part; + const Part* part_it=part; do { - if (part->type() == Pos::FRAMES) - { - if (part_it->lenFrame()==old_len && !already_done.contains(part_it)) - { - WavePart* new_part = new WavePart(*(WavePart*)part_it); - new_part->setLenFrame(new_len); - operations.push_back(UndoOp(UndoOp::ModifyPart, part_it, new_part, true, false)); - } - } - else - if (part_it->lenTick()==old_len && !already_done.contains(part_it)) - { - MidiPart* new_part = new MidiPart(*(MidiPart*)part_it); - new_part->setLenTick(new_len); - operations.push_back(UndoOp(UndoOp::ModifyPart, part_it, new_part, true, false)); - } + if (part_it->lenValue()==old_len && !already_done.contains(part_it)) + operations.push_back(UndoOp(UndoOp::ModifyPartLength, part_it, old_len, new_len, part->type())); part_it=part_it->nextClone(); } while (part_it!=part); @@ -1370,10 +1388,9 @@ for (iPart part = (*track)->parts()->begin(); part != (*track)->parts()->end(); part++) if (part->second->selected()) { - EventList* events=part->second->events(); unsigned len=part->second->lenTick(); - for (iEvent ev=events->begin(); ev!=events->end(); ev++) + for (ciEvent ev=part->second->events().begin(); ev!=part->second->events().end(); ev++) if (ev->second.endTick() > len) len=ev->second.endTick(); @@ -1381,11 +1398,7 @@ if (len part->second->lenTick()) - { - MidiPart* new_part = new MidiPart(*(MidiPart*)part->second); - new_part->setLenTick(len); - operations.push_back(UndoOp(UndoOp::ModifyPart, part->second, new_part, true, false)); - } + operations.push_back(UndoOp(UndoOp::ModifyPartLength, part->second, part->second->lenValue(), len, Pos::TICKS)); } MusEGlobal::song->applyOperationGroup(operations); @@ -1394,7 +1407,7 @@ void clean_parts() { Undo operations; - set already_processed; + set already_processed; TrackList* tracks = MusEGlobal::song->tracks(); for (iTrack track = tracks->begin(); track != tracks->end(); track++) @@ -1406,7 +1419,7 @@ // multiple clones) unsigned len=0; - Part* part_it=part->second; + const Part* part_it=part->second; do { if (part_it->lenTick() > len) @@ -1419,8 +1432,7 @@ // erase all events exceeding the longest clone of this part // (i.e., erase all hidden events) or shorten them - EventList* el = part->second->events(); - for (iEvent ev=el->begin(); ev!=el->end(); ev++) + for (ciEvent ev=part->second->events().begin(); ev!=part->second->events().end(); ev++) if (ev->second.tick() >= len) operations.push_back(UndoOp(UndoOp::DeleteEvent, ev->second, part->second, true, true)); else if (ev->second.endTick() > len) @@ -1435,33 +1447,67 @@ MusEGlobal::song->applyOperationGroup(operations); } + +bool merge_with_next_part(const Part* oPart) +{ + const Track* track = oPart->track(); + + if(track->type() != Track::WAVE && !track->isMidiTrack()) + return false; + + const PartList* pl = track->cparts(); + const Part* nextPart = 0; + + for (ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + if (ip->second == oPart) + { + ++ip; + if (ip == pl->end()) + return false; + nextPart = ip->second; + break; + } + } + + if (nextPart) + { + set parts; + parts.insert(oPart); + parts.insert(nextPart); + return merge_parts(parts); + } + else + return false; +} + bool merge_selected_parts() { - set temp = get_all_selected_parts(); + set temp = get_all_selected_parts(); return merge_parts(temp); } -bool merge_parts(const set& parts) +bool merge_parts(const set& parts) { - set tracks; - for (set::iterator it=parts.begin(); it!=parts.end(); it++) + set tracks; + for (set::iterator it=parts.begin(); it!=parts.end(); it++) tracks.insert( (*it)->track() ); Undo operations; // process tracks separately - for (set::iterator t_it=tracks.begin(); t_it!=tracks.end(); t_it++) + for (set::iterator t_it=tracks.begin(); t_it!=tracks.end(); t_it++) { - Track* track=*t_it; + const Track* track=*t_it; unsigned begin=INT_MAX, end=0; - Part* first_part=NULL; + const Part* first_part=NULL; // find begin of the first and end of the last part - for (set::iterator it=parts.begin(); it!=parts.end(); it++) + for (set::iterator it=parts.begin(); it!=parts.end(); it++) if ((*it)->track()==track) { - Part* p=*it; + const Part* p=*it; if (p->tick() < begin) { begin=p->tick(); @@ -1479,30 +1525,25 @@ } // create and prepare the new part - Part* new_part = track->newPart(first_part); + Part* new_part = first_part->duplicateEmpty(); new_part->setTick(begin); new_part->setLenTick(end-begin); - EventList* new_el = new_part->events(); - new_el->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it - // so we must decrement it first :/ - new_el->clear(); - // copy all events from the source parts into the new part - for (set::iterator p_it=parts.begin(); p_it!=parts.end(); p_it++) + for (set::iterator p_it=parts.begin(); p_it!=parts.end(); p_it++) if ((*p_it)->track()==track) { - EventList* old_el= (*p_it)->events(); - for (iEvent ev_it=old_el->begin(); ev_it!=old_el->end(); ev_it++) + const EventList& old_el= (*p_it)->events(); + for (ciEvent ev_it=old_el.begin(); ev_it!=old_el.end(); ev_it++) { - Event new_event=ev_it->second; + Event new_event=ev_it->second.clone(); new_event.setTick( new_event.tick() + (*p_it)->tick() - new_part->tick() ); - new_el->add(new_event); + new_part->addEvent(new_event); } } // delete all the source parts - for (set::iterator it=parts.begin(); it!=parts.end(); it++) + for (set::iterator it=parts.begin(); it!=parts.end(); it++) if ((*it)->track()==track) operations.push_back( UndoOp(UndoOp::DeletePart, *it) ); // and add the new one @@ -1512,4 +1553,49 @@ return MusEGlobal::song->applyOperationGroup(operations); } +bool split_part(const Part* part, int tick) +{ + int l1 = tick - part->tick(); + int l2 = part->lenTick() - l1; + if (l1 <= 0 || l2 <= 0) + return false; + Part* p1; + Part* p2; + part->splitPart(tick, p1, p2); + + MusEGlobal::song->informAboutNewParts(part, p1); + MusEGlobal::song->informAboutNewParts(part, p2); + + Undo operations; + operations.push_back(UndoOp(UndoOp::DeletePart, part)); + operations.push_back(UndoOp(UndoOp::AddPart, p1)); + operations.push_back(UndoOp(UndoOp::AddPart, p2)); + return MusEGlobal::song->applyOperationGroup(operations); +} + +bool delete_selected_parts() +{ + Undo operations; + bool partSelected = false; + + TrackList* tl = MusEGlobal::song->tracks(); + + for (iTrack it = tl->begin(); it != tl->end(); ++it) + { + PartList* pl = (*it)->parts(); + for (iPart ip = pl->begin(); ip != pl->end(); ++ip) + { + if (ip->second->selected()) + { + operations.push_back(UndoOp(UndoOp::DeletePart,ip->second)); + partSelected = true; + } + } + } + + MusEGlobal::song->applyOperationGroup(operations); + + return partSelected; +} + } // namespace MusECore diff -Nru muse-2.1.2/muse/functions.h muse-3.0.2+ds1/muse/functions.h --- muse-2.1.2/muse/functions.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/functions.h 2017-12-04 21:01:18.000000000 +0000 @@ -39,36 +39,36 @@ namespace MusECore { class Undo; -std::set partlist_to_set(PartList* pl); -std::set part_to_set(Part* p); -std::map get_events(const std::set& parts, int range); +std::set partlist_to_set(PartList* pl); +std::set part_to_set(const Part* p); +std::map get_events(const std::set& parts, int range); //these functions simply do their job, non-interactively -bool modify_velocity(const std::set& parts, int range, int rate, int offset=0); -bool modify_off_velocity(const std::set& parts, int range, int rate, int offset=0); -bool modify_notelen(const std::set& parts, int range, int rate, int offset=0); -bool quantize_notes(const std::set& parts, int range, int raster, bool len=false, int strength=100, int swing=0, int threshold=0); -bool erase_notes(const std::set& parts, int range, int velo_threshold=0, bool velo_thres_used=false, int len_threshold=0, bool len_thres_used=false); -bool delete_overlaps(const std::set& parts, int range); -bool set_notelen(const std::set& parts, int range, int len); -bool move_notes(const std::set& parts, int range, signed int ticks); -bool transpose_notes(const std::set& parts, int range, signed int halftonesteps); -bool crescendo(const std::set& parts, int range, int start_val, int end_val, bool absolute); -bool legato(const std::set& parts, int range, int min_len=1, bool dont_shorten=false); +bool modify_velocity(const std::set& parts, int range, int rate, int offset=0); +bool modify_off_velocity(const std::set& parts, int range, int rate, int offset=0); +bool modify_notelen(const std::set& parts, int range, int rate, int offset=0); +bool quantize_notes(const std::set& parts, int range, int raster, bool len=false, int strength=100, int swing=0, int threshold=0); +bool erase_notes(const std::set& parts, int range, int velo_threshold=0, bool velo_thres_used=false, int len_threshold=0, bool len_thres_used=false); +bool delete_overlaps(const std::set& parts, int range); +bool set_notelen(const std::set& parts, int range, int len); +bool move_notes(const std::set& parts, int range, signed int ticks); +bool transpose_notes(const std::set& parts, int range, signed int halftonesteps); +bool crescendo(const std::set& parts, int range, int start_val, int end_val, bool absolute); +bool legato(const std::set& parts, int range, int min_len=1, bool dont_shorten=false); //the below functions automatically open the dialog //they return true if you click "ok" and false if "abort" -bool modify_velocity(const std::set& parts); -bool modify_notelen(const std::set& parts); -bool quantize_notes(const std::set& parts); -bool set_notelen(const std::set& parts); -bool move_notes(const std::set& parts); -bool transpose_notes(const std::set& parts); -bool crescendo(const std::set& parts); -bool erase_notes(const std::set& parts); -bool delete_overlaps(const std::set& parts); -bool legato(const std::set& parts); +bool modify_velocity(const std::set& parts); +bool modify_notelen(const std::set& parts); +bool quantize_notes(const std::set& parts); +bool set_notelen(const std::set& parts); +bool move_notes(const std::set& parts); +bool transpose_notes(const std::set& parts); +bool crescendo(const std::set& parts); +bool erase_notes(const std::set& parts); +bool delete_overlaps(const std::set& parts); +bool legato(const std::set& parts); //the below functions operate on selected parts bool modify_velocity(); @@ -84,29 +84,34 @@ //functions for copy'n'paste -void copy_notes(const std::set& parts, int range); -bool paste_notes(Part* paste_into_part=NULL); // shows a dialog -void paste_notes(int max_distance=3072, bool always_new_part=false, bool never_new_part=false, Part* paste_into_part=NULL, int amount=1, int raster=3072); -QMimeData* selected_events_to_mime(const std::set& parts, int range); -QMimeData* parts_to_mime(const std::set& parts); +void copy_notes(const std::set& parts, int range); +bool paste_notes(const Part* paste_into_part=NULL); // shows a dialog +void paste_notes(int max_distance=3072, bool always_new_part=false, bool never_new_part=false, const Part* paste_into_part=NULL, int amount=1, int raster=3072); +QMimeData* selected_events_to_mime(const std::set& parts, int range); +QMimeData* parts_to_mime(const std::set& parts); -void paste_at(const QString& pt, int pos, int max_distance=3072, bool always_new_part=false, bool never_new_part=false, Part* paste_into_part=NULL, int amount=1, int raster=3072); +void paste_at(const QString& pt, int pos, int max_distance=3072, bool always_new_part=false, bool never_new_part=false, const Part* paste_into_part=NULL, int amount=1, int raster=3072); //functions for selections -void select_all(const std::set& parts); -void select_none(const std::set& parts); -void select_invert(const std::set& parts); -void select_in_loop(const std::set& parts); -void select_not_in_loop(const std::set& parts); +void select_all(const std::set& parts); +void select_none(const std::set& parts); +void select_invert(const std::set& parts); +void select_in_loop(const std::set& parts); +void select_not_in_loop(const std::set& parts); +bool tracks_are_selected(); +bool parts_are_selected(); //functions for parts void shrink_parts(int raster=-1); //negative values mean "config.division" void expand_parts(int raster=-1); -void schedule_resize_all_same_len_clone_parts(Part* part, unsigned new_len, Undo& operations); +void schedule_resize_all_same_len_clone_parts(const Part* part, unsigned new_len, Undo& operations); void clean_parts(); +bool merge_with_next_part(const Part* part); bool merge_selected_parts(); -bool merge_parts(const std::set& parts); +bool merge_parts(const std::set& parts); +bool split_part(const Part* part, int tick); +bool delete_selected_parts(); // internal QMimeData* file_to_mimedata(FILE *datafile, QString mimeType); diff -Nru muse-2.1.2/muse/gconfig.cpp muse-3.0.2+ds1/muse/gconfig.cpp --- muse-2.1.2/muse/gconfig.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/gconfig.cpp 2018-01-22 16:43:28.000000000 +0000 @@ -25,7 +25,27 @@ namespace MusEGlobal { +/* --- PLEASE READ BEFORE EDITING --- + * + * The values below are default init parameters for most MusE + * configuration parameters. + * They are however NOT certain to have this value! + * + * This is for two reasons: + * 1) MusE has a config file which overrides these values + * 2) When no configuration file exists a default TEMPLATE + * is loaded from the share dir which overwrites most + * of these values. + * In a perfect world it would overwrite all values and + * these would be removed. + */ + GlobalConfigValues config = { + QStringList(), // pluginLadspaPathList + QStringList(), // pluginDssiPathList + QStringList(), // pluginVstPathList + QStringList(), // pluginLinuxVstPathList + QStringList(), // pluginLv2PathList 170, // globalAlphaBlend { QColor(0xff, 0xff, 0xff), // palette @@ -100,6 +120,7 @@ QColor(84, 97, 114), // trackBg; QColor(109, 174, 178), // selected track Bg; QColor(0x00, 0x00, 0x00), // selected track Fg; + Qt::gray, // trackSectionDividerColor; QColor(74, 150, 194), // midiTrackLabelBg; // Med blue QColor(150, 177, 189), // drumTrackLabelBg; // Pale pastel blue @@ -131,7 +152,8 @@ QColor(255, 255, 255), // midiControllerViewBg QColor(255, 255, 255), // drumListBg QColor(255, 255, 255), // rulerCurrent - + Qt::gray, // midiCanvasBeatColor + Qt::black, // midiCanvasBarColor Qt::lightGray, // waveNonselectedPart Qt::darkGray, // wavePeakColor Qt::black, // waveRmsColor @@ -143,11 +165,39 @@ QColor(54,54,54), // partMidiDarkEventColor QColor(200,200,200), // partMidiLightEventColor + QColor(0,181,241 ), // sliderBarDefaultColor + QColor(228,203,36 ), // sliderDefaultColor + QColor(78,172,35 ), // panSliderColor + QColor(209,86,86 ), // gainSliderColor + QColor(190,190,39 ), // auxSliderColor + QColor(154,135,124), // audioVolumeSliderColor + QColor(153,156,124), // midiVolumeSliderColor + QColor(37,121,255 ), // audioControllerSliderDefaultColor + QColor(220,77,255 ), // audioPropertySliderDefaultColor + QColor(37,121,255 ), // midiControllerSliderDefaultColor + QColor(220,77,255 ), // midiPropertySliderDefaultColor + QColor(100,255,255), // midiPatchReadoutColor + QColor(0,221,255 ), // audioMeterPrimaryColor + QColor(0,221,255 ), // midiMeterPrimaryColor + QColor(208,145,49 ), // rackItemBackgroundColor + + MusEGlobal::WaveOutLine, // waveDrawing + + false, // fixFrozenMDISubWindows Turn on a fix for frozen MDIs in Breeze/Oxygen themes. + + // maxAliasedPointSize At what point size to switch from aliased text to non-aliased text. + // Zero means always use anti-aliasing. For certain widgets that use it. May be more later. + 8, + + false, // enableAlsaMidiDriver Whether to enable the ALSA midi driver 384, // division; 1024, // rtcTicks + 0, // curMidiSyncInPort The currently selected midi sync input port. true, // midiSendInit Send instrument initialization sequences true, // warnInitPending Warn instrument initialization sequences pending false, // midiSendCtlDefaults Send instrument controller defaults at position 0 if none in song + false, // midiSendNullParameters Send null parameters after each (N)RPN event + false, // midiOptimizeControllers Don't send redundant H/L parameters or H/L values true, // warnIfBadTiming Warn if timer res not good false, // velocityPerNote Whether to show per-note or all velocities -60, // int minMeter; @@ -162,14 +212,17 @@ 1, // smf export file format false, // midi export file 2 byte timesigs instead of 4 true, // optimize midi export file note offs + true, // expRunningStatus; Save space by using running status true, // Split imported tracks into multiple parts. true, // importMidiNewStyleDrum true, // importDevNameMetas Import Prefer Device Name metas over port number metas if both exist. true, // importInstrNameMetas Import Prefer Instrument Name metas over Mode sysexes if both exist. - EXPORT_PORTS_DEVICES_ALL, // exportPortsDevices Export port number metas and/or device name metas. + MusEGlobal::PORT_NUM_META | MusEGlobal::DEVICE_NAME_META, // exportPortsDevices. Or'd ExportPortsDevices_t flags. Export port number metas and/or device name metas. true, // exportPortDeviceSMF0 Export a port and/or device meta even for SMF0. - EXPORT_MODE_INSTR_ALL, // exportModeInstr Export mode sysexes and/or instrument name metas. + MusEGlobal::MODE_SYSEX | MusEGlobal::INSTRUMENT_NAME_META, // exportModeInstr. Or'd ExportModeInstr_t flags. Export mode sysexes and/or instrument name metas. QString("GM"), // importMidiDefaultInstr Default to this instrument not Generic, if no match found + true, // exportDrumMapOverrides Apply drum map overrides to export + true, // exportChannelOverridesToNewTrack Drum map Channel overrides go to a separate track 1, // startMode QString(""), // start song path false, // startSongLoadConfig @@ -179,16 +232,22 @@ QRect(0, 0, 600, 200), // GeometryBigTime; { QString("Mixer A"), + QStringList(), QRect(0, 0, 300, 500), // Mixer1 true, true, true, true, - true, true, true, true, true + true, true, true, true, true, + MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW, + QList() }, { QString("Mixer B"), + QStringList(), QRect(200, 200, 300, 500), // Mixer2 true, true, true, true, - true, true, true, true, true - }, + true, true, true, true, true, + MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW, + QList() + }, true, // TransportVisible; false, // BigTimeVisible; false, // mixer1Visible; @@ -210,8 +269,11 @@ false, // useOutputLimiter true, // showDidYouKnow false, // vstInPlace Enable VST in-place processing - 44100, // Dummy audio preferred sample rate - 512, // Dummy audio buffer size + + 44100, // Device audio preferred sample rate + 512, // Device audio buffer size + 0, // Device RtAudio selected backend + QString("./"), // projectBaseFolder true, // projectStoreInFolder true, // useProjectSaveDialog @@ -225,7 +287,27 @@ MusEGlobal::PREFER_NEW, // drumTrackPreference true, // smartFocus 20, // trackHeight - true // borderlessMouse + true, // borderlessMouse + false, // autoSave + false, // scrollableSubMenus + true, // liveWaveUpdate + true, // warnOnFileVersions Warn if file version different than current + MusEGlobal::CONF_LV2_UI_USE_FIRST, //lv2UiBehavior + true, // preferKnobsVsSliders Whether to prefer the use of knobs over sliders, esp in mixer. + true, // showControlValues Whether to show the value along with label in small controls, esp in mixer. + true, // monitorOnRecord Whether to automatically monitor on record arm. + true, // lineEditStyleHack Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. + false, // preferMidiVolumeDb Prefer midi volume as decibels instead of 0-127. + QString("klick1.wav"), // measSample + QString("klick2.wav"), // beatSample + QString("klick3.wav"), // accent1Sample + QString("klick4.wav"), // accent2Sample + MusEGlobal::RoutePreferCanonicalName, // preferredRouteNameOrAlias + true, // routerExpandVertically + 2, // routerGroupingChannels + "", // mixdownPath + true, // showNoteNamesInPianoRoll + }; } // namespace MusEGlobal diff -Nru muse-2.1.2/muse/gconfig.h muse-3.0.2+ds1/muse/gconfig.h --- muse-2.1.2/muse/gconfig.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/gconfig.h 2018-01-22 16:43:28.000000000 +0000 @@ -55,28 +55,35 @@ ONLY_NEW = 3 }; +// Or'd together enum ExportPortsDevices_t { - EXPORT_PORTS_DEVICES_ALL = 0, - PORT_NUM_META = 1, - DEVICE_NAME_META = 2, - EXPORT_PORTS_DEVICES_END = 3 + PORT_NUM_META = 0x01, + DEVICE_NAME_META = 0x02, }; +// Or'd together enum ExportModeInstr_t { - EXPORT_MODE_INSTR_ALL = 0, - MODE_SYSEX = 1, - INSTRUMENT_NAME_META = 2, - EXPORT_MODE_INSTR_END = 3 + MODE_SYSEX = 0x01, + INSTRUMENT_NAME_META = 0x02, }; +enum RouteNameAliasPreference { RoutePreferCanonicalName = 0, RoutePreferFirstAlias = 1, RoutePreferSecondAlias = 2 }; + +enum WaveDrawing { WaveRmsPeak=1, WaveOutLine=2 }; //--------------------------------------------------------- // MixerConfig //--------------------------------------------------------- struct MixerConfig { + enum DisplayOrder { + STRIPS_TRADITIONAL_VIEW = -1004, + STRIPS_EDITED_VIEW = -1003, + STRIPS_ARRANGER_VIEW = -1002, + }; QString name; + QStringList stripOrder; QRect geometry; bool showMidiTracks; bool showDrumTracks; @@ -87,6 +94,8 @@ bool showGroupTracks; bool showAuxTracks; bool showSyntiTracks; + DisplayOrder displayOrder; + QList stripVisibility; void write(int level, MusECore::Xml& xml); void read(MusECore::Xml& xml); @@ -95,8 +104,19 @@ //--------------------------------------------------------- // GlobalConfigValues //--------------------------------------------------------- +enum CONF_LV2_UI_BEHAVIOR { + CONF_LV2_UI_USE_FIRST = 0, + CONF_LV2_UI_ASK_ONCE, + CONF_LV2_UI_ASK_ALWAYS +}; struct GlobalConfigValues { + QStringList pluginLadspaPathList; + QStringList pluginDssiPathList; + QStringList pluginVstPathList; + QStringList pluginLinuxVstPathList; + QStringList pluginLv2PathList; + int globalAlphaBlend; QColor palette[16]; QColor partColors[NUM_PARTCOLORS]; @@ -109,6 +129,7 @@ QColor trackBg; QColor selectTrackBg; QColor selectTrackFg; + QColor trackSectionDividerColor; QColor midiTrackLabelBg; QColor drumTrackLabelBg; @@ -140,6 +161,8 @@ QColor midiControllerViewBg; QColor drumListBg; QColor rulerCurrent; + QColor midiCanvasBeatColor; + QColor midiCanvasBarColor; QColor waveNonselectedPart; QColor wavePeakColor; @@ -152,11 +175,42 @@ QColor partMidiDarkEventColor; QColor partMidiLightEventColor; + QColor sliderBarDefaultColor; + QColor sliderDefaultColor; + QColor panSliderColor; + QColor gainSliderColor; + QColor auxSliderColor; + QColor audioVolumeSliderColor; + QColor midiVolumeSliderColor; + QColor audioControllerSliderDefaultColor; + QColor audioPropertySliderDefaultColor; + QColor midiControllerSliderDefaultColor; + QColor midiPropertySliderDefaultColor; + QColor midiPatchReadoutColor; + + QColor audioMeterPrimaryColor; + QColor midiMeterPrimaryColor; + + QColor rackItemBackgroundColor; + + WaveDrawing waveDrawing; + + // Turn on a fix for frozen MDIs in Breeze/Oxygen themes. + bool fixFrozenMDISubWindows; + + // At what point size to switch from aliased text to non-aliased text. Zero means always use anti-aliasing. + // For certain widgets that use it. May be more later. + int maxAliasedPointSize; + + bool enableAlsaMidiDriver; // Whether to enable the ALSA midi driver int division; int rtcTicks; + int curMidiSyncInPort; // The currently selected midi sync input port. bool midiSendInit; // Send instrument initialization sequences bool warnInitPending; // Warn instrument initialization sequences pending bool midiSendCtlDefaults; // Send instrument controller defaults at position 0 if none in song + bool midiSendNullParameters; // Send null parameters after each (N)RPN event + bool midiOptimizeControllers; // Don't send redundant H/L parameters or H/L values bool warnIfBadTiming; // Warn if timer res not good bool velocityPerNote; // Whether to show per-note or all velocities int minMeter; @@ -171,15 +225,18 @@ int smfFormat; // smf export file type bool exp2ByteTimeSigs; // Export 2 byte time sigs instead of 4 bytes bool expOptimNoteOffs; // Save space by replacing note offs with note on velocity 0 + bool expRunningStatus; // Save space by using running status bool importMidiSplitParts; // Split imported tracks into multiple parts. bool importMidiNewStyleDrum; // Use new style drum tracks bool importDevNameMetas; // Import Prefer Device Name metas over port number metas if both exist. bool importInstrNameMetas; // Import Prefer Instrument Name metas over Mode sysexes if both exist. - ExportPortsDevices_t exportPortsDevices; // Export port number metas and/or device name metas. + int exportPortsDevices; // Or'd ExportPortsDevices_t flags. Export port number metas and/or device name metas. bool exportPortDeviceSMF0; // Export a port and/or device meta even for SMF0. - ExportModeInstr_t exportModeInstr; // Export mode sysexes and/or instrument name metas. + int exportModeInstr; // Or'd ExportModeInstr_t flags. Export mode sysexes and/or instrument name metas. QString importMidiDefaultInstr; // Default to this instrument not Generic, if no match found - + bool exportDrumMapOverrides; // Apply Port, Channel, and ANote drum map overrides to export + bool exportChannelOverridesToNewTrack; // Drum map Channel overrides go to a separate track + int startMode; // 0 - start with last song // 1 - start with default template // 2 - start with song @@ -187,6 +244,7 @@ bool startSongLoadConfig; // Whether to load configuration with the start template or song int guiDivision; // division for tick display + QRect geometryMain; QRect geometryTransport; QRect geometryBigTime; @@ -215,8 +273,10 @@ bool useOutputLimiter; bool showDidYouKnow; bool vstInPlace; // Enable VST in-place processing - int dummyAudioSampleRate; - int dummyAudioBufSize; + int deviceAudioSampleRate; + int deviceAudioBufSize; + int deviceAudioBackend; + QString projectBaseFolder; bool projectStoreInFolder; bool useProjectSaveDialog; @@ -231,6 +291,27 @@ bool smartFocus; int trackHeight; bool borderlessMouse; + bool autoSave; + bool scrollableSubMenus; + bool liveWaveUpdate; //live update wave tracks while recording + bool warnOnFileVersions; // Warn if file version different than current + CONF_LV2_UI_BEHAVIOR lv2UiBehavior; + bool preferKnobsVsSliders; // Whether to prefer the use of knobs over sliders, esp in mixer. + bool showControlValues; // Whether to show the value along with label in small controls, esp in mixer. + bool monitorOnRecord; // Whether to automatically monitor on record arm. + bool lineEditStyleHack; // Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. + bool preferMidiVolumeDb; // Prefer midi volume as decibels instead of 0-127. + + QString measSample; + QString beatSample; + QString accent1Sample; + QString accent2Sample; + RouteNameAliasPreference preferredRouteNameOrAlias; + bool routerExpandVertically; // Whether to expand the router items vertically. (Good use of space but slow!) + // How to group the router channels together for easier multi-channel manipulation. + int routerGroupingChannels; + QString mixdownPath; + bool showNoteNamesInPianoRoll; }; diff -Nru muse-2.1.2/muse/globaldefs.h muse-3.0.2+ds1/muse/globaldefs.h --- muse-2.1.2/muse/globaldefs.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/globaldefs.h 2017-12-04 21:01:18.000000000 +0000 @@ -28,8 +28,9 @@ // MT_GM - General Midi // MT_GS - Roland GS // MT_XG - Yamaha XG +// MT_GM2 - General Midi Level 2 -enum MType { MT_UNKNOWN=0, MT_GM, MT_GS, MT_XG }; +enum MType { MT_UNKNOWN=0, MT_GM, MT_GS, MT_XG, MT_GM2 }; enum AutomationType { AUTO_OFF, AUTO_READ, AUTO_TOUCH, AUTO_WRITE @@ -49,5 +50,9 @@ #define MIDI_CHANNELS 16 // Channels per Port #endif +namespace MusEGui { +enum EditInstrumentTabType { EditInstrumentPatches=0, EditInstrumentDrumMaps=1, EditInstrumentControllers=2, EditInstrumentSysex=3, EditInstrumentInitSeq=4 }; +} + #endif diff -Nru muse-2.1.2/muse/globals.cpp muse-3.0.2+ds1/muse/globals.cpp --- muse-2.1.2/muse/globals.cpp 2013-03-28 20:34:30.000000000 +0000 +++ muse-3.0.2+ds1/muse/globals.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -29,6 +29,7 @@ #include #include #include +#include #include "globals.h" #include "config.h" @@ -49,8 +50,21 @@ bool overrideAudioOutput = false; bool overrideAudioInput = false; +const QString selectableAudioBackendDevices[] = { + "Jack Audio (default)", + "Midi only", + "RtAudio Pulse Audio", + "RtAudio ALSA", + "RtAudio OSS - Open Sound System", + "Russian roulette (RtAudio selects)" +}; + +const int numRtAudioDevices = 6; + QTimer* heartBeatTimer; +bool blinkTimerPhase = false; + bool hIsB = true; // call note h "b" const signed char sharpTab[14][7] = { @@ -113,10 +127,12 @@ bool loadVST = true; bool loadNativeVST = true; bool loadDSSI = true; +bool loadLV2 = true; bool usePythonBridge = false; bool useLASH = true; bool useAlsaWithJack = false; bool noAutoStartJack = false; +bool populateMidiPortsOnStart = true; const char* midi_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2)"), @@ -221,6 +237,12 @@ 0 }; +const char* colors_config_file_pattern[] = { + QT_TRANSLATE_NOOP("file_patterns", "Color configuration files (*.cfc)"), + QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), + 0 +}; + Qt::KeyboardModifiers globalKeyState; // Midi Filter Parameter @@ -248,6 +270,7 @@ QAction* recordAction; QAction* panicAction; QAction* metronomeAction; +QAction* cpuLoadAction; MusEGui::MusE* muse = 0; @@ -293,7 +316,14 @@ int midiLearnChan = -1; int midiLearnCtrl = -1; -uid_t euid, ruid; // effective user id, real user id +const QString inputRoutingToolTipBase = QObject::tr("Input routing"); +const QString noInputRoutingToolTipWarn = inputRoutingToolTipBase + QString("\n") + QObject::tr("Warning: No input routes! Click to connect..."); + +const QString outputRoutingToolTipBase = QObject::tr("Output routing"); +const QString noOutputRoutingToolTipWarn = outputRoutingToolTipBase + QString("\n") + QObject::tr("Warning: No output routes! Click to connect..."); + +// REMOVE Tim. setuid. Removed. +//uid_t euid, ruid; // effective user id, real user id bool midiSeqRunning = false; @@ -304,17 +334,21 @@ void doSetuid() { -#ifndef RTCAP - int status; -#ifdef _POSIX_SAVED_IDS - status = seteuid (euid); -#else - status = setreuid (ruid, euid); -#endif - if (status < 0) { - perror("doSetuid: Couldn't set uid"); - } -#endif +// BUG REMOVE Tim. setuid. Removed. Ancient stuff no longer required? +// Tests OK under several scenarios: Jack started externally, +// Jack started by MusE, Jack started as root, dummy driver etc. +// Was causing crash with new DrumGizmo 0.9.11 +// #ifndef RTCAP +// int status; +// #ifdef _POSIX_SAVED_IDS +// status = seteuid (euid); +// #else +// status = setreuid (ruid, euid); +// #endif +// if (status < 0) { +// perror("doSetuid: Couldn't set uid"); +// } +// #endif } //--------------------------------------------------------- @@ -324,20 +358,24 @@ void undoSetuid() { -#ifndef RTCAP - int status; - -#ifdef _POSIX_SAVED_IDS - status = seteuid (ruid); -#else - status = setreuid (euid, ruid); -#endif - if (status < 0) { - fprintf(stderr, "undoSetuid: Couldn't set uid (eff:%d,real:%d): %s\n", - euid, ruid, strerror(errno)); - exit (status); - } -#endif +// BUG REMOVE Tim. setuid. Removed. Ancient stuff no longer required? +// Tests OK under several scenarios: Jack started externally, +// Jack started by MusE, Jack started as root, dummy driver etc. +// Was causing crash with new DrumGizmo 0.9.11 +// #ifndef RTCAP +// int status; +// +// #ifdef _POSIX_SAVED_IDS +// status = seteuid (ruid); +// #else +// status = setreuid (euid, ruid); +// #endif +// if (status < 0) { +// fprintf(stderr, "undoSetuid: Couldn't set uid (eff:%d,real:%d): %s\n", +// euid, ruid, strerror(errno)); +// exit (status); +// } +// #endif } //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/globals.h muse-3.0.2+ds1/muse/globals.h --- muse-2.1.2/muse/globals.h 2013-03-28 20:34:30.000000000 +0000 +++ muse-3.0.2+ds1/muse/globals.h 2018-01-06 20:31:35.000000000 +0000 @@ -35,6 +35,8 @@ class QAction; class QActionGroup; class QTimer; +class QToolButton; + namespace MusEGui { class MusE; @@ -57,8 +59,22 @@ extern bool overrideAudioOutput; extern bool overrideAudioInput; +extern const QString selectableAudioBackendDevices[]; +extern const int numRtAudioDevices; +enum SelectableAudioBackendDevices { + JackAudio = 0, + DummyAudio = 1, + RtAudioPulse = 2, + RtAudioAlsa = 3, + RtAudioOss = 4, + RtAudioChoice = 5 + //RtAudioJack, -- it's just stupid to keep this option +}; + extern QTimer* heartBeatTimer; +extern bool blinkTimerPhase; + extern bool hIsB; extern const signed char sharpTab[14][7]; @@ -90,8 +106,10 @@ extern bool loadDSSI; extern bool usePythonBridge; extern bool useLASH; +extern bool loadLV2; extern bool useAlsaWithJack; extern bool noAutoStartJack; +extern bool populateMidiPortsOnStart; extern bool realTimeScheduling; extern int realTimePriority; @@ -110,6 +128,7 @@ extern const char* drum_map_file_pattern[]; extern const char* drum_map_file_save_pattern[]; extern const char* audio_file_pattern[]; +extern const char* colors_config_file_pattern[]; extern Qt::KeyboardModifiers globalKeyState; @@ -151,6 +170,7 @@ extern QAction* recordAction; extern QAction* panicAction; extern QAction* metronomeAction; +extern QAction* cpuLoadAction; extern MusEGui::MusE* muse; @@ -195,11 +215,18 @@ extern bool midiSeqRunning; extern bool automation; extern int trackHeight; -// p3.3.55 + +extern const QString inputRoutingToolTipBase; +extern const QString outputRoutingToolTipBase; +extern const QString noInputRoutingToolTipWarn; +extern const QString noOutputRoutingToolTipWarn; + + #define JACK_MIDI_OUT_PORT_SUFFIX "_out" #define JACK_MIDI_IN_PORT_SUFFIX "_in" -extern uid_t euid, ruid; +// REMOVE Tim. setuid. Removed. +// extern uid_t euid, ruid; extern void doSetuid(); extern void undoSetuid(); extern bool checkAudioDevice(); diff -Nru muse-2.1.2/muse/helper.cpp muse-3.0.2+ds1/muse/helper.cpp --- muse-2.1.2/muse/helper.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/helper.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -30,6 +30,7 @@ #include "icons.h" #include "synth.h" #include "functions.h" +#include "operations.h" #include "gconfig.h" #include "driver/jackmidi.h" @@ -40,17 +41,31 @@ #include "audiodev.h" #include "midi.h" #include "midiseq.h" +#include "midictrl.h" #include "popupmenu.h" #include "menutitleitem.h" +#include "dssihost.h" +#include "lv2host.h" +#include "vst_native.h" +#include "appearance.h" +#include "mpevent.h" + +#include #include #include #include +#include #include #include #include +#include #include #include +#include +#include +#include +#include using std::set; @@ -76,8 +91,7 @@ if (v < 0 || v > 127) return QString("----"); int octave = (v / 12) - 2; - QString o; - o.sprintf("%d", octave); + QString o = QString::number(octave); int i = v % 12; QString s(octave < 0 ? valu[i] : vall[i]); if (MusEGlobal::hIsB) { @@ -90,7 +104,383 @@ } +//--------------------------------------------------------- +// dumpMPEvent +//--------------------------------------------------------- + +void dumpMPEvent(const MEvent* ev) + { + fprintf(stderr, "time:%d port:%d chan:%d ", ev->time(), ev->port(), ev->channel()+1); + if (ev->type() == ME_NOTEON) { + QString s = pitch2string(ev->dataA()); + fprintf(stderr, "NoteOn %s(0x%x) %d\n", s.toLatin1().constData(), ev->dataA(), ev->dataB()); + } + else if (ev->type() == ME_NOTEOFF) { + QString s = pitch2string(ev->dataA()); + fprintf(stderr, "NoteOff %s(0x%x) %d\n", s.toLatin1().constData(), ev->dataA(), ev->dataB()); + } + else if (ev->type() == ME_SYSEX) { + fprintf(stderr, "SysEx len %d 0x%0x ...\n", ev->len(), ev->data()[0]); + } + else + fprintf(stderr, "type:0x%02x a=%d b=%d\n", ev->type(), ev->dataA(), ev->dataB()); + } + +#if 0 + +// ------------------------------------------------------------------------------------------------------- +// enumerateJackMidiDevices() +// This version creates separate devices for Jack midi input and outputs. +// It does not attempt to pair them together. +// ------------------------------------------------------------------------------------------------------- + +void enumerateJackMidiDevices() +{ + if(!MusEGlobal::checkAudioDevice()) + return; + + MidiDevice* dev = 0; + PendingOperationList operations; + + // If Jack is running. + if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO) + { + char good_name[ROUTE_PERSISTENT_NAME_SIZE]; + std::list sl; +// sl = MusEGlobal::audioDevice->inputPorts(true, 1); // Ask for second aliases. + sl = MusEGlobal::audioDevice->inputPorts(true); + for(std::list::iterator i = sl.begin(); i != sl.end(); ++i) + { + QByteArray ba = (*i).toLatin1(); + const char* port_name = ba.constData(); + void* const port = MusEGlobal::audioDevice->findPort(port_name); + if(port) + { + //dev = MidiJackDevice::createJackMidiDevice(*i, 1); + dev = MidiJackDevice::createJackMidiDevice(QString(), 1); // Let it pick the name + if(dev) + { + // Get a good routing name. + MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); + + const Route dstRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, good_name); // Persistent route. + // If audio is running, this calls jack_connect() and waits for the audio thread to execute addRoute(). + // If audio is not running, this directly executes addRoute(), bypassing the audio messaging system, + // and jack_connect() is not called. + //MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); + // + // We only want to add the route, not call jack_connect - jack may not have been activated yet. + // If it has been, we should be calling our graph changed handler soon, it will handle actual connections. + // If audio is not running yet, this directly executes addRoute(), bypassing the audio messaging system, + if(!dev->outRoutes()->contains(dstRoute)) + operations.add(MusECore::PendingOperationItem(dev->outRoutes(), dstRoute, MusECore::PendingOperationItem::AddRouteNode)); + } + } + } + + //sl = MusEGlobal::audioDevice->outputPorts(true, 1); // Ask for second aliases. + sl = MusEGlobal::audioDevice->outputPorts(true); + for(std::list::iterator i = sl.begin(); i != sl.end(); ++i) + { + QByteArray ba = (*i).toLatin1(); + const char* port_name = ba.constData(); + void* const port = MusEGlobal::audioDevice->findPort(port_name); + if(port) + { + dev = MidiJackDevice::createJackMidiDevice(QString(), 2); // Let it pick the name + if(dev) + { + // Get a good routing name. + MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); + const Route srcRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, good_name); // Persistent route. + if(!dev->inRoutes()->contains(srcRoute)) + operations.add(MusECore::PendingOperationItem(dev->inRoutes(), srcRoute, MusECore::PendingOperationItem::AddRouteNode)); + } + } + } + } + if(!operations.empty()) + { + //operations.add(MusECore::PendingOperationItem((TrackList*)NULL, PendingOperationItem::UpdateSoloStates)); + MusEGlobal::audio->msgExecutePendingOperations(operations); // Don't update here. + //MusEGlobal::song->update(SC_ROUTE); + } +} + +#else + +// ------------------------------------------------------------------------------------------------------- +// enumerateJackMidiDevices() +// This version attempts to pair together Jack midi input and outputs into single MidiDevices, +// similar to how ALSA presents pairs of inputs and outputs. +// ------------------------------------------------------------------------------------------------------- + +void enumerateJackMidiDevices() +{ + if(!MusEGlobal::checkAudioDevice()) + return; + + PendingOperationList operations; + + // If Jack is running. + if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO) + { + MidiDevice* dev = 0; + char w_good_name[ROUTE_PERSISTENT_NAME_SIZE]; + char r_good_name[ROUTE_PERSISTENT_NAME_SIZE]; + std::list wsl; + std::list rsl; + wsl = MusEGlobal::audioDevice->inputPorts(true); + rsl = MusEGlobal::audioDevice->outputPorts(true); + + for(std::list::iterator wi = wsl.begin(); wi != wsl.end(); ++wi) + { + QByteArray w_ba = (*wi).toLatin1(); + const char* w_port_name = w_ba.constData(); + + bool match_found = false; + void* const w_port = MusEGlobal::audioDevice->findPort(w_port_name); + if(w_port) + { + // Get a good routing name. + MusEGlobal::audioDevice->portName(w_port, w_good_name, ROUTE_PERSISTENT_NAME_SIZE); + + for(std::list::iterator ri = rsl.begin(); ri != rsl.end(); ++ri) + { + QByteArray r_ba = (*ri).toLatin1(); + const char* r_port_name = r_ba.constData(); + + void* const r_port = MusEGlobal::audioDevice->findPort(r_port_name); + if(r_port) + { + // Get a good routing name. + MusEGlobal::audioDevice->portName(r_port, r_good_name, ROUTE_PERSISTENT_NAME_SIZE); + + const size_t w_sz = strlen(w_good_name); + const size_t r_sz = strlen(r_good_name); + size_t start_c = 0; + size_t w_end_c = w_sz; + size_t r_end_c = r_sz; + + while(start_c < w_sz && start_c < r_sz && + w_good_name[start_c] == r_good_name[start_c]) + ++start_c; + + while(w_end_c > 0 && r_end_c > 0) + { + if(w_good_name[w_end_c - 1] != r_good_name[r_end_c - 1]) + break; + --w_end_c; + --r_end_c; + } + + if(w_end_c > start_c && r_end_c > start_c) + { + const char* w_str = w_good_name + start_c; + const char* r_str = r_good_name + start_c; + const size_t w_len = w_end_c - start_c; + const size_t r_len = r_end_c - start_c; + + // Do we have a matching pair? + if((w_len == 7 && r_len == 8 && + strncasecmp(w_str, "capture", w_len) == 0 && + strncasecmp(r_str, "playback", r_len) == 0) || + + (w_len == 8 && r_len == 7 && + strncasecmp(w_str, "playback", w_len) == 0 && + strncasecmp(r_str, "capture", r_len) == 0) || + + (w_len == 5 && r_len == 6 && + strncasecmp(w_str, "input", w_len) == 0 && + strncasecmp(r_str, "output", r_len) == 0) || + + (w_len == 6 && r_len == 5 && + strncasecmp(w_str, "output", w_len) == 0 && + strncasecmp(r_str, "input", r_len) == 0) || + + (w_len == 2 && r_len == 3 && + strncasecmp(w_str, "in", w_len) == 0 && + strncasecmp(r_str, "out", r_len) == 0) || + + (w_len == 3 && r_len == 2 && + strncasecmp(w_str, "out", w_len) == 0 && + strncasecmp(r_str, "in", r_len) == 0) || + + (w_len == 1 && r_len == 1 && + strncasecmp(w_str, "p", w_len) == 0 && + strncasecmp(r_str, "c", r_len) == 0) || + + (w_len == 1 && r_len == 1 && + strncasecmp(w_str, "c", w_len) == 0 && + strncasecmp(r_str, "p", r_len) == 0)) + { + dev = MidiJackDevice::createJackMidiDevice(QString(), 3); // Let it pick the name + if(dev) + { + const Route srcRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, r_good_name); // Persistent route. + const Route dstRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, w_good_name); // Persistent route. + // We only want to add the route, not call jack_connect - jack may not have been activated yet. + // If it has been, we should be calling our graph changed handler soon, it will handle actual connections. + // If audio is not running yet, this directly executes addRoute(), bypassing the audio messaging system, + if(!dev->inRoutes()->contains(srcRoute)) + operations.add(MusECore::PendingOperationItem(dev->inRoutes(), srcRoute, MusECore::PendingOperationItem::AddRouteNode)); + if(!dev->outRoutes()->contains(dstRoute)) + operations.add(MusECore::PendingOperationItem(dev->outRoutes(), dstRoute, MusECore::PendingOperationItem::AddRouteNode)); + } + + rsl.erase(ri); // Done with this read port. Remove. + match_found = true; + break; + } + } + } + } + } + + if(!match_found) + { + // No match was found. Create a single writeable device. + dev = MidiJackDevice::createJackMidiDevice(QString(), 1); // Let it pick the name + if(dev) + { + const Route dstRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, w_good_name); // Persistent route. + // We only want to add the route, not call jack_connect - jack may not have been activated yet. + // If it has been, we should be calling our graph changed handler soon, it will handle actual connections. + // If audio is not running yet, this directly executes addRoute(), bypassing the audio messaging system, + if(!dev->outRoutes()->contains(dstRoute)) + operations.add(MusECore::PendingOperationItem(dev->outRoutes(), dstRoute, MusECore::PendingOperationItem::AddRouteNode)); + } + } + + } + + // Create the remaining readable ports as single readable devices. + for(std::list::iterator ri = rsl.begin(); ri != rsl.end(); ++ri) + { + dev = MidiJackDevice::createJackMidiDevice(QString(), 2); // Let it pick the name + if(dev) + { + QByteArray r_ba = (*ri).toLatin1(); + const char* r_port_name = r_ba.constData(); + + void* const r_port = MusEGlobal::audioDevice->findPort(r_port_name); + if(r_port) + { + // Get a good routing name. + MusEGlobal::audioDevice->portName(r_port, r_good_name, ROUTE_PERSISTENT_NAME_SIZE); + const Route srcRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, r_good_name); // Persistent route. + if(!dev->inRoutes()->contains(srcRoute)) + operations.add(MusECore::PendingOperationItem(dev->inRoutes(), srcRoute, MusECore::PendingOperationItem::AddRouteNode)); + } + } + } + } + + if(!operations.empty()) + { + //operations.add(MusECore::PendingOperationItem((TrackList*)NULL, PendingOperationItem::UpdateSoloStates)); + MusEGlobal::audio->msgExecutePendingOperations(operations); // Don't update here. + //MusEGlobal::song->update(SC_ROUTE); + } +} +#endif // enumerateJackMidiDevices + +// ------------------------------------------------------------------------------------------------------- +// populateMidiPorts() +// Attempts to auto-populate midi ports with found devices. +// ------------------------------------------------------------------------------------------------------- + +void populateMidiPorts() +{ + if(!MusEGlobal::checkAudioDevice()) + return; + + MusECore::MidiDevice* dev = 0; + int port_num = 0; + int jack_midis_found = 0; + bool def_in_found = false; +// bool def_out_found = false; + + // If Jack is running, prefer Jack midi devices over ALSA. + if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::JACK_AUDIO) + { + for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + dev = *i; + if(dev) + { + ++jack_midis_found; + MidiPort* mp = &MusEGlobal::midiPorts[port_num]; + MusEGlobal::audio->msgSetMidiDevice(mp, dev); + +// robert: removing the default init on several places to allow for the case +// where you rather want the midi track to default to the last created port +// this can only happen if there is _no_ default set + +// // Global function initMidiPorts() already sets defs to port #1, but this will override. +// if(!def_out_found && dev->rwFlags() & 0x1) +// { +// mp->setDefaultOutChannels(1); +// def_out_found = true; +// } +// else + mp->setDefaultOutChannels(0); + + if(!def_in_found && dev->rwFlags() & 0x2) + { + mp->setDefaultInChannels(1); + def_in_found = true; + } + else + mp->setDefaultInChannels(0); + + if(++port_num == MIDI_PORTS) + return; + } + } + } + //else + // If Jack is not running, use ALSA devices. + // Try to do the user a favour: If we still have no Jack devices, even if Jack is running, fill with ALSA. + // It is possible user has Jack running on ALSA back-end but without midi support. + // IE. They use Jack for audio but use ALSA for midi! + // If unwanted, remove "|| jack_midis_found == 0". + if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::DUMMY_AUDIO || jack_midis_found == 0) + { + for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + if((*i)->deviceType() != MusECore::MidiDevice::ALSA_MIDI) + continue; + dev = *i; + MidiPort* mp = &MusEGlobal::midiPorts[port_num]; + MusEGlobal::audio->msgSetMidiDevice(mp, dev); + + // robert: removing the default init on several places to allow for the case + // where you rather want the midi track to default to the last created port + // this can only happen if there is _no_ default set + +// // Global function initMidiPorts() already sets defs to port #1, but this will override. +// if(!def_out_found && dev->rwFlags() & 0x1) +// { +// mp->setDefaultOutChannels(1); +// def_out_found = true; +// } +// else + mp->setDefaultOutChannels(0); + + if(!def_in_found && dev->rwFlags() & 0x2) + { + mp->setDefaultInChannels(1); + def_in_found = true; + } + else + mp->setDefaultInChannels(0); + if(++port_num == MIDI_PORTS) + return; + } + } +} Part* partFromSerialNumber(int serial) { @@ -108,7 +498,7 @@ return NULL; } -bool any_event_selected(const set& parts, bool in_range) +bool any_event_selected(const set& parts, bool in_range) { return !get_events(parts, in_range ? 3 : 1).empty(); } @@ -208,7 +598,7 @@ } void write_new_style_drummap(int level, Xml& xml, const char* tagname, - DrumMap* drummap, bool* drummap_hidden, bool full) + DrumMap* drummap, bool full) { xml.tag(level++, tagname); @@ -222,7 +612,9 @@ (dm->lv1 != idm->lv1) || (dm->lv2 != idm->lv2) || (dm->lv3 != idm->lv3) || (dm->lv4 != idm->lv4) || (dm->enote != idm->enote) || (dm->mute != idm->mute) || - (drummap_hidden && drummap_hidden[i]) || full) + (dm->port != idm->port) || (dm->channel != idm->channel) || + (dm->anote != idm->anote) || + (dm->hide != idm->hide) || full) { xml.tag(level++, "entry pitch=\"%d\"", i); @@ -232,21 +624,17 @@ if (full || dm->vol != idm->vol) xml.intTag(level, "vol", dm->vol); if (full || dm->quant != idm->quant) xml.intTag(level, "quant", dm->quant); if (full || dm->len != idm->len) xml.intTag(level, "len", dm->len); + if (full || dm->channel != idm->channel) xml.intTag(level, "channel", dm->channel); + if (full || dm->port != idm->port) xml.intTag(level, "port", dm->port); if (full || dm->lv1 != idm->lv1) xml.intTag(level, "lv1", dm->lv1); if (full || dm->lv2 != idm->lv2) xml.intTag(level, "lv2", dm->lv2); if (full || dm->lv3 != idm->lv3) xml.intTag(level, "lv3", dm->lv3); if (full || dm->lv4 != idm->lv4) xml.intTag(level, "lv4", dm->lv4); if (full || dm->enote != idm->enote) xml.intTag(level, "enote", dm->enote); + if (full || dm->anote != idm->anote) xml.intTag(level, "anote", dm->anote); if (full || dm->mute != idm->mute) xml.intTag(level, "mute", dm->mute); - if (drummap_hidden && - (full || drummap_hidden[i])) xml.intTag(level, "hide", drummap_hidden[i]); - - // anote is ignored anyway, as dm->anote == i, and this is - // already stored in the begin tag (pitch=...) - - // channel and port are ignored as well, as they're not used - // in new-style-drum-mode - + if (full || dm->hide != idm->hide) xml.intTag(level, "hide", dm->hide); + xml.tag(--level, "/entry"); } } @@ -255,7 +643,7 @@ } void read_new_style_drummap(Xml& xml, const char* tagname, - DrumMap* drummap, bool* drummap_hidden, bool compatibility) + DrumMap* drummap, bool compatibility) { for (;;) { @@ -270,7 +658,6 @@ { DrumMap* dm=NULL; DrumMap temporaryMap; - bool* hidden=NULL; for (;;) // nested loop { Xml::Token token = xml.parse(); @@ -280,7 +667,7 @@ case Xml::Error: case Xml::End: goto end_of_nested_for; - + case Xml::Attribut: if (tag == "pitch") { @@ -290,11 +677,10 @@ else { dm = &drummap[pitch]; - hidden = drummap_hidden ? &drummap_hidden[pitch] : NULL; } } break; - + case Xml::TagStart: if (dm==NULL && compatibility == false) printf("ERROR: THIS SHOULD NEVER HAPPEN: no valid 'pitch' attribute in tag, but sub-tags follow in read_new_style_drummap()!\n"); @@ -310,6 +696,10 @@ dm->quant = xml.parseInt(); else if (tag == "len") dm->len = xml.parseInt(); + else if (tag == "channel") + dm->channel = xml.parseInt(); + else if (tag == "port") + dm->port = xml.parseInt(); else if (tag == "lv1") dm->lv1 = xml.parseInt(); else if (tag == "lv2") @@ -324,24 +714,23 @@ int pitch = temporaryMap.enote; drummap[pitch] = temporaryMap; dm = &drummap[pitch]; - hidden = drummap_hidden ? &drummap_hidden[pitch] : NULL; dm->anote = pitch; } } + else if (tag == "anote") + dm->anote = xml.parseInt(); else if (tag == "mute") dm->mute = xml.parseInt(); else if (tag == "hide") - { - if (hidden) *hidden = xml.parseInt(); - } + dm->hide = xml.parseInt(); else xml.unknown("read_new_style_drummap"); break; - + case Xml::TagEnd: if (tag == "entry") goto end_of_nested_for; - + default: break; } @@ -359,7 +748,52 @@ default: break; } - } + } +} + +int readDrummapsEntryPatchCollection(Xml& xml) +{ + int hbank = (CTRL_PROGRAM_VAL_DONT_CARE >> 16) & 0xff; + int lbank = (CTRL_PROGRAM_VAL_DONT_CARE >> 8) & 0xff; + int prog = CTRL_PROGRAM_VAL_DONT_CARE & 0xff; + int last_prog, last_hbank, last_lbank; // OBSOLETE. Not used. + + for (;;) + { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) + { + case Xml::Error: + case Xml::End: + return CTRL_VAL_UNKNOWN; // an invalid collection + + case Xml::TagStart: + xml.unknown("readDrummapsEntryPatchCollection"); + break; + + case Xml::Attribut: + // last_prog, last_hbank, last_lbank are OBSOLETE. Not used. + if (tag == "prog") + parse_range(xml.s2(), &prog, &last_prog); + else if (tag == "lbank") + parse_range(xml.s2(), &lbank, &last_lbank); + else if (tag == "hbank") + parse_range(xml.s2(), &hbank, &last_hbank); + break; + + case Xml::TagEnd: + if (tag == "patch_collection") + return ((hbank & 0xff) << 16) | ((lbank & 0xff) << 8) | (prog & 0xff); + + default: + break; + } + } + + fprintf(stderr, "ERROR: THIS CANNOT HAPPEN: exited infinite loop in readDrummapsEntryPatchCollection()!\n" + " not returning anything. expect undefined behaviour or even crashes.\n"); + return CTRL_VAL_UNKNOWN; // an invalid collection } void record_controller_change_and_maybe_send(unsigned tick, int ctrl_num, int val, MidiTrack* mt) @@ -384,12 +818,435 @@ namespace MusEGui { //--------------------------------------------------------- +// midiPortsPopup +//--------------------------------------------------------- + +QMenu* midiPortsPopup(QWidget* parent, int checkPort, bool includeDefaultEntry) + { + QMenu* p = new QMenu(parent); + QMenu* subp = 0; + QAction *act = 0; + QString name; + const int openConfigId = MIDI_PORTS; + const int defaultId = MIDI_PORTS + 1; + + // Warn if no devices available. Add an item to open midi config. + int pi = 0; + for( ; pi < MIDI_PORTS; ++pi) + { + MusECore::MidiDevice* md = MusEGlobal::midiPorts[pi].device(); + if(md && (md->rwFlags() & 1)) + break; + } + if(pi == MIDI_PORTS) + { + act = p->addAction(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Warning: No output devices!"))); + act->setCheckable(false); + act->setData(-1); + p->addSeparator(); + } + act = p->addAction(QIcon(*MusEGui::settings_midiport_softsynthsIcon), qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Open midi config..."))); + act->setCheckable(false); + act->setData(openConfigId); + p->addSeparator(); + + p->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Output port/device")), p)); + + p->addSeparator(); + + if(includeDefaultEntry) + { + act = p->addAction(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "default"))); + act->setCheckable(false); + act->setData(defaultId); + } + + QVector alsa_list; + QVector jack_list; + QVector synth_list; + QVector *cur_list; + QVector unused_list; + + for (int i = 0; i < MIDI_PORTS; ++i) + { + MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; + MusECore::MidiDevice* md = port->device(); + if(!md) + { + unused_list.push_back(i); + continue; + } + + // Make deleted audio softsynths not show in select dialog + if(md->isSynti()) + { + MusECore::AudioTrack *_track = static_cast(static_cast(md)); + MusECore::TrackList* tl = MusEGlobal::song->tracks(); + if(tl->find(_track) == tl->end()) + continue; + } + + // Only writeable ports, or current one. + if(!(md->rwFlags() & 1) && (i != checkPort)) + continue; + + switch(md->deviceType()) + { + case MusECore::MidiDevice::ALSA_MIDI: + alsa_list.push_back(i); + break; + + case MusECore::MidiDevice::JACK_MIDI: + jack_list.push_back(i); + break; + + case MusECore::MidiDevice::SYNTH_MIDI: + synth_list.push_back(i); + break; + } + } + + // Order the entire listing by device type. + for(int dtype = 0; dtype <= MusECore::MidiDevice::SYNTH_MIDI; ++dtype) + { + switch(dtype) + { + case MusECore::MidiDevice::ALSA_MIDI: + if(!alsa_list.isEmpty()) + p->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "ALSA")), p)); + cur_list = &alsa_list; + break; + + case MusECore::MidiDevice::JACK_MIDI: + if(!jack_list.isEmpty()) + p->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "JACK")), p)); + cur_list = &jack_list; + break; + + case MusECore::MidiDevice::SYNTH_MIDI: + if(!synth_list.isEmpty()) + p->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Synth")), p)); + cur_list = &synth_list; + break; + } + + if(cur_list->isEmpty()) + continue; + + int row = 0; + int sz = cur_list->size(); + + for (int i = 0; i < sz; ++i) + { + const int port = cur_list->at(i); + if(port < 0 || port >= MIDI_PORTS) + continue; + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + name = QString("%1:%2") + .arg(port + 1) + .arg(mp->portname()); + + act = p->addAction(name); + act->setData(port); + act->setCheckable(true); + act->setChecked(port == checkPort); + + ++row; + } + } + + int sz = unused_list.size(); + if(sz > 0) + { + p->addSeparator(); + for (int i = 0; i < sz; ++i) + { + const int port = unused_list.at(i); + // No submenu yet? Create it now. + if(!subp) + { + subp = new QMenu(p); + subp->setTitle(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Empty ports"))); + } + act = subp->addAction(QString().setNum(port + 1)); + act->setData(port); + act->setCheckable(true); + act->setChecked(port == checkPort); + } + } + + if(subp) + p->addMenu(subp); + return p; + } + +//--------------------------------------------------------- +// midiPortsPopupMenu +//--------------------------------------------------------- +void midiPortsPopupMenu(MusECore::Track* t, int x, int y, bool allClassPorts, + const QWidget* widget, bool includeDefaultEntry) +{ + switch(t->type()) { + case MusECore::Track::MIDI: + case MusECore::Track::DRUM: + case MusECore::Track::NEW_DRUM: + case MusECore::Track::AUDIO_SOFTSYNTH: + { + MusECore::MidiTrack* track = 0; + MusECore::SynthI* synthi = 0; + + int potential_new_port_no=-1; + int port = -1; + + if(t->isSynthTrack()) + { + // Cast as SynthI which inherits MidiDevice. + synthi = static_cast(t); + if(!synthi) + return; + port = synthi->midiPort(); + } + else + { + track = static_cast(t); + port = track->outPort(); + } + + // NOTE: If parent is given, causes accelerators to be returned in QAction::text() ! + QMenu* p = MusEGui::midiPortsPopup(0, port, includeDefaultEntry); + + // find first free port number + // do not permit numbers already used in other tracks! + // except if it's only used in this track. + int no; + for (no=0;nomidis()->begin(); it!=MusEGlobal::song->midis()->end(); ++it) + { + MusECore::MidiTrack* mt=*it; + if (mt!=t && mt->outPort()==no) + break; + } + if (it == MusEGlobal::song->midis()->end()) + break; + + // TODO Ports which are used by synths ?? + } + + if (no==MIDI_PORTS) + { + delete p; + printf("THIS IS VERY UNLIKELY TO HAPPEN: no free midi ports! you have used all %i!\n",MIDI_PORTS); + break; + } + + potential_new_port_no=no; + + // Do not include unused devices if the track is a synth track. + if(!synthi) + { + typedef std::map asmap; + typedef std::map::iterator imap; + + asmap mapALSA; + asmap mapJACK; + asmap mapSYNTH; + + int aix = 0x10000000; + int jix = 0x20000000; + int six = 0x30000000; + for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + // don't add devices which are used somewhere + if((*i)->midiPort() >= 0 && (*i)->midiPort() < MIDI_PORTS) + continue; + + switch((*i)->deviceType()) + { + case MusECore::MidiDevice::ALSA_MIDI: + mapALSA.insert(std::pair ((*i)->name().toStdString(), aix)); + ++aix; + break; + + case MusECore::MidiDevice::JACK_MIDI: + mapJACK.insert(std::pair ((*i)->name().toStdString(), jix)); + ++jix; + break; + + case MusECore::MidiDevice::SYNTH_MIDI: + mapSYNTH.insert(std::pair ((*i)->name().toStdString(), six)); + ++six; + break; + } + } + + if (!mapALSA.empty() || !mapJACK.empty() || !mapSYNTH.empty()) + { + QMenu* pup = p->addMenu(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Unused Devices"))); + QAction* act; + + if (!mapALSA.empty()) + { + pup->addAction(new MusEGui::MenuTitleItem("ALSA:", pup)); + + for(imap i = mapALSA.begin(); i != mapALSA.end(); ++i) + { + int idx = i->second; + QString s(i->first.c_str()); + MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::ALSA_MIDI); + if(md) + { + if(md->deviceType() != MusECore::MidiDevice::ALSA_MIDI) + continue; + + act = pup->addAction(md->name()); + act->setData(idx); + } + } + } + + if (!mapJACK.empty()) + { + pup->addAction(new MusEGui::MenuTitleItem("JACK:", pup)); + + for(imap i = mapJACK.begin(); i != mapJACK.end(); ++i) + { + int idx = i->second; + QString s(i->first.c_str()); + MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::JACK_MIDI); + if(md) + { + if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) + continue; + + act = pup->addAction(md->name()); + act->setData(idx); + } + } + } + + if (!mapSYNTH.empty()) + { + pup->addAction(new MusEGui::MenuTitleItem("Synth:", pup)); + + for(imap i = mapSYNTH.begin(); i != mapSYNTH.end(); ++i) + { + int idx = i->second; + QString s(i->first.c_str()); + MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::SYNTH_MIDI); + if(md) + { + if(md->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) + continue; + + act = pup->addAction(md->name()); + act->setData(idx); + } + } + } + } + } + + QAction* act = widget ? p->exec(widget->mapToGlobal(QPoint(x, y)), 0) : p->exec(); + if(!act) + { + delete p; + break; + } + + QString acttext=act->text(); + int n = act->data().toInt(); + delete p; + + if(n < 0) // Invalid item. + break; + + if(n == MIDI_PORTS) // Show port config dialog. + { + MusEGlobal::muse->configMidiPorts(); + break; + } + else if (n >= 0x10000000) + { + // Error, should not happen. + if(synthi) + break; + + int typ; + if (n < 0x20000000) + typ = MusECore::MidiDevice::ALSA_MIDI; + else if (n < 0x30000000) + typ = MusECore::MidiDevice::JACK_MIDI; + else + typ = MusECore::MidiDevice::SYNTH_MIDI; + + MusECore::MidiDevice* sdev = MusEGlobal::midiDevices.find(acttext, typ); + + MusEGlobal::audio->msgSetMidiDevice(&MusEGlobal::midiPorts[potential_new_port_no], sdev); + n=potential_new_port_no; + + MusEGlobal::song->update(); + } + + MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; + MusEGlobal::audio->msgIdle(true); + + // In the case of synths, multiple synths cannot be assigned to the same port. + if(synthi || (!allClassPorts && !t->selected())) + { + if(synthi) + { + MusEGlobal::audio->msgSetMidiDevice(&MusEGlobal::midiPorts[n], synthi); + MusEGlobal::song->update(); + changed |= MusECore::MidiTrack::PortChanged; + } + else if(track) + { + if(n != track->outPort()) + changed |= track->setOutPortAndUpdate(n, false); + } + } + else + { + if(track) + { + MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); + for(MusECore::iMidiTrack myt = tracks->begin(); myt != tracks->end(); ++myt) + { + MusECore::MidiTrack* mt = *myt; + if(n != mt->outPort() && (allClassPorts || mt->selected())) + changed |= mt->setOutPortAndUpdate(n, false); + } + } + } + + MusEGlobal::audio->msgIdle(false); + MusEGlobal::audio->msgUpdateSoloStates(); + MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); + + // Prompt and send init sequences. + MusEGlobal::audio->msgInitMidiDevices(false); + } + break; + + case MusECore::Track::WAVE: + case MusECore::Track::AUDIO_OUTPUT: + case MusECore::Track::AUDIO_INPUT: + case MusECore::Track::AUDIO_GROUP: + case MusECore::Track::AUDIO_AUX: //TODO + break; + } +} + +//--------------------------------------------------------- // populateAddSynth //--------------------------------------------------------- QMenu* populateAddSynth(QWidget* parent) { - QMenu* synp = new QMenu(parent); + QMenu* synp = new PopupMenu(parent); typedef std::multimap asmap; typedef std::multimap::iterator imap; @@ -397,7 +1254,7 @@ const int ntypes = MusECore::Synth::SYNTH_TYPE_END; asmap smaps[ntypes]; - QMenu* mmaps[ntypes]; + PopupMenu* mmaps[ntypes]; for(int itype = 0; itype < ntypes; ++itype) mmaps[itype] = 0; @@ -405,13 +1262,18 @@ MusECore::Synth::Type type; int ii = 0; - for(std::vector::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) + for(std::vector::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) { synth = *i; type = synth->synthType(); +#ifdef DSSI_SUPPORT + if (type == MusECore::Synth::DSSI_SYNTH && ((MusECore::DssiSynth*)synth)->isDssiVst() ) // Place Wine VSTs in a separate sub menu + type = MusECore::Synth::VST_SYNTH; +#endif + if(type >= ntypes) continue; - smaps[type].insert( std::pair (std::string(synth->description().toLower().toLatin1().constData()), ii) ); + smaps[type].insert( std::pair (synth->description().toLower().toStdString(), ii) ); ++ii; } @@ -430,12 +1292,13 @@ // No sub-menu yet? Create it now. if(!mmaps[itype]) { - mmaps[itype] = new QMenu(parent); + mmaps[itype] = new PopupMenu(parent); mmaps[itype]->setIcon(*synthIcon); mmaps[itype]->setTitle(MusECore::synthType2String((MusECore::Synth::Type)itype)); synp->addMenu(mmaps[itype]); } - QAction* act = mmaps[itype]->addAction(synth->description() + " <" + synth->name() + ">"); + //QAction* act = mmaps[itype]->addAction(synth->description() + " <" + synth->name() + ">"); + QAction* act = mmaps[itype]->addAction(synth->description()); act->setData( MENU_ADD_SYNTH_ID_BASE * (itype + 1) + idx ); } } @@ -449,7 +1312,7 @@ // this is also used in "mixer" //--------------------------------------------------------- -QActionGroup* populateAddTrack(QMenu* addTrack, bool populateAll, bool evenIgnoreDrumPreference) +QActionGroup* populateAddTrack(QMenu* addTrack, bool populateAll, bool /*evenIgnoreDrumPreference*/) { QActionGroup* grp = new QActionGroup(addTrack); if (MusEGlobal::config.addHiddenTracks) @@ -461,37 +1324,10 @@ midi->setData(MusECore::Track::MIDI); grp->addAction(midi); - - if (!evenIgnoreDrumPreference && (MusEGlobal::config.drumTrackPreference==MusEGlobal::PREFER_OLD || MusEGlobal::config.drumTrackPreference==MusEGlobal::ONLY_OLD)) - { - QAction* drum = addTrack->addAction(QIcon(*addtrack_drumtrackIcon), - qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Drum Track"))); - drum->setData(MusECore::Track::DRUM); - grp->addAction(drum); - } - - if (!evenIgnoreDrumPreference && (MusEGlobal::config.drumTrackPreference==MusEGlobal::PREFER_NEW || MusEGlobal::config.drumTrackPreference==MusEGlobal::ONLY_NEW)) - { - QAction* newdrum = addTrack->addAction(QIcon(*addtrack_newDrumtrackIcon), - qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Drum Track"))); - newdrum->setData(MusECore::Track::NEW_DRUM); - grp->addAction(newdrum); - } - - if (evenIgnoreDrumPreference || MusEGlobal::config.drumTrackPreference==MusEGlobal::PREFER_NEW) - { - QAction* drum = addTrack->addAction(QIcon(*addtrack_drumtrackIcon), - qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Old Style Drum Track"))); - drum->setData(MusECore::Track::DRUM); - grp->addAction(drum); - } - if (evenIgnoreDrumPreference || MusEGlobal::config.drumTrackPreference==MusEGlobal::PREFER_OLD) - { - QAction* newdrum = addTrack->addAction(QIcon(*addtrack_newDrumtrackIcon), - qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add New Style Drum Track"))); - newdrum->setData(MusECore::Track::NEW_DRUM); - grp->addAction(newdrum); - } + QAction* newdrum = addTrack->addAction(QIcon(*addtrack_newDrumtrackIcon), + qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Drum Track"))); + newdrum->setData(MusECore::Track::NEW_DRUM); + grp->addAction(newdrum); } if (populateAll || MusECore::WaveTrack::visible()) { QAction* wave = addTrack->addAction(QIcon(*addtrack_wavetrackIcon), @@ -664,212 +1500,6 @@ return nfb + "/" + filename + ".med"; } - -#if 1 - -// ------------------------------------------------------------------------------------------------------- -// populateMidiPorts() -// This version creats separate devices for Jack midi input and outputs. -// It does not attempt to pair them together. -// ------------------------------------------------------------------------------------------------------- - -void populateMidiPorts() -{ - if(!MusEGlobal::checkAudioDevice()) - return; - - MusECore::MidiDevice* dev = 0; - - int port_num = 0; - - int jack_midis_found = 0; - - // If Jack is running, prefer Jack midi devices over ALSA. - if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::JACK_AUDIO) - { - std::list sl; - sl = MusEGlobal::audioDevice->inputPorts(true, 1); // Ask for second aliases. - for(std::list::iterator i = sl.begin(); i != sl.end(); ++i) - { - dev = MusECore::MidiJackDevice::createJackMidiDevice(*i, 1); - if(dev) - { - ++jack_midis_found; - MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); - MusECore::Route srcRoute(dev, -1); - MusECore::Route dstRoute(*i, true, -1, MusECore::Route::JACK_ROUTE); - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); - if(++port_num == MIDI_PORTS) - return; - } - } - - sl = MusEGlobal::audioDevice->outputPorts(true, 1); // Ask for second aliases. - for(std::list::iterator i = sl.begin(); i != sl.end(); ++i) - { - dev = MusECore::MidiJackDevice::createJackMidiDevice(*i, 2); - if(dev) - { - ++jack_midis_found; - MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); - MusECore::Route srcRoute(*i, false, -1, MusECore::Route::JACK_ROUTE); - MusECore::Route dstRoute(dev, -1); - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); - if(++port_num == MIDI_PORTS) - return; - } - } - } - //else - // If Jack is not running, use ALSA devices. - // Try to do the user a favour: If we still have no Jack devices, even if Jack is running, fill with ALSA. - // It is possible user has Jack running on ALSA back-end but without midi support. - // IE. They use Jack for audio but use ALSA for midi! - // If unwanted, remove "|| jack_midis_found == 0". - if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::DUMMY_AUDIO || jack_midis_found == 0) - { - for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) - { - if((*i)->deviceType() != MusECore::MidiDevice::ALSA_MIDI) - continue; - dev = *i; - MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); - - if(++port_num == MIDI_PORTS) - return; - } - } -} - -#else // this code is disabled - -// Please don't remove this section as it may be improved. - -// ------------------------------------------------------------------------------------------------------- -// populateMidiPorts() -// This version worked somewhat well with system devices. -// But no, it is virtually impossible to tell from the names whether ports should be paired. -// There is too much room for error - what markers to look for ("capture_"/"playback_") etc. -// It works kind of OK with 'seq' Jack Midi ALSA devices, but not for 'raw' which have a different -// naming structure ("in-hw-0-0-0"/"out-hw-0-0-0"). -// It also fails to combine if the ports were named by a client app, for example another instance of MusE. -// ------------------------------------------------------------------------------------------------------- - -void populateMidiPorts() -{ - if(!MusEGlobal::checkAudioDevice()) - return; - - MusECore::MidiDevice* dev = 0; - - int port_num = 0; - - // If Jack is running, prefer Jack midi devices over ALSA. - if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::JACK_AUDIO) - { - std::list wsl; - std::list rsl; - wsl = MusEGlobal::audioDevice->inputPorts(true, 0); // Ask for first aliases. - rsl = MusEGlobal::audioDevice->outputPorts(true, 0); // Ask for first aliases. - - for(std::list::iterator wi = wsl.begin(); wi != wsl.end(); ++wi) - { - QString ws = *wi; - int y = ws.lastIndexOf("_"); - if(y >= 1) - { - int x = ws.lastIndexOf("_", y-1); - if(x >= 0) - ws.remove(x, y - x); - } - - - bool match_found = false; - for(std::list::iterator ri = rsl.begin(); ri != rsl.end(); ++ri) - { - QString rs = *ri; - int y = rs.lastIndexOf("_"); - if(y >= 1) - { - int x = rs.lastIndexOf("_", y-1); - if(x >= 0) - rs.remove(x, y - x); - } - - // Do we have a matching pair? - if(rs == ws) - { - dev = MusECore::MidiJackDevice::createJackMidiDevice(ws, 3); - if(dev) - { - MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); - MusECore::Route devRoute(dev, -1); - MusECore::Route wdstRoute(*wi, true, -1, MusECore::Route::JACK_ROUTE); - MusECore::Route rsrcRoute(*ri, false, -1, MusECore::Route::JACK_ROUTE); - MusEGlobal::audio->msgAddRoute(devRoute, wdstRoute); - MusEGlobal::audio->msgAddRoute(rsrcRoute, devRoute); - if(++port_num == MIDI_PORTS) - return; - } - - rsl.erase(ri); // Done with this read port. Remove. - match_found = true; - break; - } - } - - if(!match_found) - { - // No match was found. Create a single writeable device. - QString s = *wi; - dev = MusECore::MidiJackDevice::createJackMidiDevice(s, 1); - if(dev) - { - MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); - MusECore::Route srcRoute(dev, -1); - MusECore::Route dstRoute(*wi, true, -1, MusECore::Route::JACK_ROUTE); - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); - if(++port_num == MIDI_PORTS) - return; - } - } - } - - // Create the remaining readable ports as single readable devices. - for(std::list::iterator ri = rsl.begin(); ri != rsl.end(); ++ri) - { - QString s = *ri; - dev = MusECore::MidiJackDevice::createJackMidiDevice(s, 2); - if(dev) - { - MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); - MusECore::Route srcRoute(*ri, false, -1, MusECore::Route::JACK_ROUTE); - MusECore::Route dstRoute(dev, -1); - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); - if(++port_num == MIDI_PORTS) - return; - } - } - } - else - // If Jack is not running, use ALSA devices. - if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::DUMMY_AUDIO) - { - for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) - { - if((*i)->deviceType() != MusECore::MidiDevice::ALSA_MIDI) - continue; - dev = *i; - MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); - - if(++port_num == MIDI_PORTS) - return; - } - } -} -#endif // populateMidiPorts - - struct CI { int num; QString s; @@ -921,12 +1551,18 @@ // Only show controller for current pitch: if (isDrum) { - if ((curDrumPitch == -1) || ((num & 0xff) != MusEGlobal::drumMap[curDrumPitch].anote)) + if ((curDrumPitch < 0) || ((num & 0xff) != MusEGlobal::drumMap[curDrumPitch].anote)) + continue; + } + else if (isNewDrum) + { + if ((curDrumPitch < 0) || ((num & 0xff) != track->drummap()[curDrumPitch].anote)) continue; + } - else if (isNewDrum || isMidi) + else if (isMidi) { - if ((curDrumPitch == -1) || ((num & 0xff) != curDrumPitch)) // FINDMICH does this work? + if ((curDrumPitch < 0) || ((num & 0xff) != curDrumPitch)) // FINDMICH does this work? continue; } else @@ -941,9 +1577,9 @@ if (i == sList.end()) { bool used = false; for (MusECore::iPart ip = part_list->begin(); ip != part_list->end(); ++ip) { - MusECore::EventList* el = ip->second->events(); - for (MusECore::iEvent ie = el->begin(); ie != el->end(); ++ie) { - MusECore::Event e = ie->second; + const MusECore::EventList& el = ip->second->events(); + for (MusECore::ciEvent ie = el.begin(); ie != el.end(); ++ie) { + const MusECore::Event& e = ie->second; if(e.type() != MusECore::Controller) continue; int ctl_num = e.dataA(); @@ -1021,11 +1657,13 @@ int num = cnum; if(ci->second->isPerNoteController()) { - if (isDrum && curDrumPitch!=-1) + if (isDrum && curDrumPitch >= 0) num = (cnum & ~0xff) | MusEGlobal::drumMap[curDrumPitch].anote; - else if ((isNewDrum || isMidi) && curDrumPitch!=-1) - num = (cnum & ~0xff) | curDrumPitch; //FINDMICH does this work? - else + else if (isNewDrum && curDrumPitch >= 0) + num = (cnum & ~0xff) | track->drummap()[curDrumPitch].anote; + else if (isMidi && curDrumPitch >= 0) + num = (cnum & ~0xff) | curDrumPitch; //FINDMICH does this work? + else continue; } @@ -1073,7 +1711,7 @@ if(fmw > est_width) est_width = fmw; PopupMenu* ccSubPop = new PopupMenu(stext, menu, true); // true = enable stay open - for(int num = 0; num < 127; ++num) + for(int num = 0; num < 128; ++num) // If it's not already in the parent menu... if(already_added_nums.find(num) == already_added_nums.end()) ccSubPop->addAction(MusECore::midiCtrlName(num, true))->setData(num); @@ -1167,6 +1805,109 @@ return QLine(x1, y1, x2, y2); } +//--------------------------------------------------------- +// loadTheme +//--------------------------------------------------------- + +void loadTheme(const QString& s, bool force) + { + // Style sheets take priority over styles, and actually + // reset the style object name to empty when set. + const QString oname(qApp->style()->objectName()); + QStringList sl = QStyleFactory::keys(); + if (s.isEmpty() || sl.indexOf(s) == -1) { + if(MusEGlobal::debugMsg) + printf("Set style does not exist, setting default.\n"); + // To find the name of the current style, use objectName(). + if(force || oname.compare(Appearance::getSetDefaultStyle(), Qt::CaseInsensitive) != 0) + { + qApp->setStyle(Appearance::getSetDefaultStyle()); + // Do the style again to fix a bug where the arranger is non-responsive. + if(MusEGlobal::config.fixFrozenMDISubWindows) + qApp->setStyle(Appearance::getSetDefaultStyle()); + + if(MusEGlobal::debugMsg) + { + fprintf(stderr, "loadTheme setting app style to default:%s\n", Appearance::getSetDefaultStyle().toLatin1().constData()); + fprintf(stderr, " App style is now:%s\n", qApp->style()->objectName().toLatin1().constData()); + } + + // No style object name? It will happen when a stylesheet is active. + // Give it a name. NOTE: The object names always seem to be lower case while + // the style factory key names are not. + if(qApp->style()->objectName().isEmpty()) + { + qApp->style()->setObjectName(Appearance::getSetDefaultStyle().toLower()); + if(MusEGlobal::debugMsg) + fprintf(stderr, " Setting empty style object name. App style is now:%s\n", qApp->style()->objectName().toLatin1().constData()); + } + } + } + else if (force || oname.compare(s, Qt::CaseInsensitive) != 0) + { + qApp->setStyle(s); + // Do the style again to fix a bug where the arranger is non-responsive. + if(MusEGlobal::config.fixFrozenMDISubWindows) + qApp->setStyle(s); + + if(MusEGlobal::debugMsg) + { + fprintf(stderr, "loadTheme setting app style to:%s\n", s.toLatin1().constData()); + fprintf(stderr, " app style is now:%s\n", qApp->style()->objectName().toLatin1().constData()); + } + + // No style object name? It will happen when a stylesheet is active. + // Give it a name. NOTE: The object names always seem to be lower case while + // the style factory key names are not. + if(qApp->style()->objectName().isEmpty()) + { + qApp->style()->setObjectName(s.toLower()); + if(MusEGlobal::debugMsg) + fprintf(stderr, " Setting empty style object name. App style is now:%s\n", qApp->style()->objectName().toLatin1().constData()); + } + } + } + +//--------------------------------------------------------- +// loadStyleSheetFile +//--------------------------------------------------------- + +void loadStyleSheetFile(const QString& s) +{ + if(MusEGlobal::debugMsg) + fprintf(stderr, "loadStyleSheetFile:%s\n", s.toLatin1().constData()); + if(s.isEmpty()) + { + qApp->setStyleSheet(s); + return; + } + + QFile cf(s); + if (cf.open(QIODevice::ReadOnly)) { + QByteArray ss = cf.readAll(); + QString sheet(QString::fromUtf8(ss.data())); + qApp->setStyleSheet(sheet); + cf.close(); + } + else + printf("loading style sheet <%s> failed\n", qPrintable(s)); +} + +//--------------------------------------------------------- +// updateThemeAndStyle +// Call when the theme or stylesheet part of the configuration has changed, +// to actually switch them. +//--------------------------------------------------------- + +void updateThemeAndStyle(bool forceStyle) +{ + // Note that setting a stylesheet completely takes over the font until blanked again. + qApp->setFont(MusEGlobal::config.fonts[0]); + loadStyleSheetFile(MusEGlobal::config.styleSheetFile); + loadTheme(MusEGlobal::config.style, forceStyle || !MusEGlobal::config.styleSheetFile.isEmpty()); +} + + } // namespace MusEGui diff -Nru muse-2.1.2/muse/helper.h muse-3.0.2+ds1/muse/helper.h --- muse-2.1.2/muse/helper.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/helper.h 2018-01-29 20:07:03.000000000 +0000 @@ -40,19 +40,25 @@ class Part; class Track; class PartList; +class MEvent; + +void enumerateJackMidiDevices(); +void populateMidiPorts(); QString pitch2string(int v); +void dumpMPEvent(const MEvent* ev); Part* partFromSerialNumber(int serial); -bool any_event_selected(const std::set&, bool in_range=false); +bool any_event_selected(const std::set&, bool in_range=false); bool drummaps_almost_equal(const DrumMap* one, const DrumMap* two, int drummap_size=128); // drummap_hidden may be NULL. void write_new_style_drummap(int level, Xml& xml, const char* tagname, - DrumMap* drummap, bool* drummap_hidden=NULL, bool full=false); + DrumMap* drummap, bool full=false); void read_new_style_drummap(Xml& xml, const char* tagname, - DrumMap* drummap, bool* drummap_hidden=NULL, bool compatibility=false); - + DrumMap* drummap, bool compatibility=false); +int readDrummapsEntryPatchCollection(Xml& xml); + QSet parts_at_tick(unsigned tick); QSet parts_at_tick(unsigned tick, const Track* track); @@ -65,7 +71,12 @@ namespace MusEGui { class PopupMenu; - + +// Lists all used midi ports + devices, plus all empty ports. +QMenu* midiPortsPopup(QWidget* parent = 0, int checkPort = -1, bool includeDefaultEntry = false); +// Includes all listed in midiPortsPopup, plus unused devices. +void midiPortsPopupMenu(MusECore::Track* t, int x, int y, bool allClassPorts, + const QWidget* widget = 0, bool includeDefaultEntry = false); QMenu* populateAddSynth(QWidget* parent); QActionGroup* populateAddTrack(QMenu* addTrack, bool populateAll=false, bool evenIgnoreDrumPreference=false); QStringList localizedStringListFromCharArray(const char** array, const char* context); @@ -75,9 +86,12 @@ QString projectPathFromFilename(QString filename); QString projectExtensionFromFilename(QString filename); QString getUniqueUntitledName(); -void populateMidiPorts(); int populateMidiCtrlMenu(PopupMenu* menu, MusECore::PartList* part_list, MusECore::Part* cur_part, int curDrumPitch); QLine clipQLine(int x1, int y1, int x2, int y2, const QRect& rect); +void loadTheme(const QString&, bool force = false); +void loadStyleSheetFile(const QString&); +// Call when the theme or stylesheet part of the configuration has changed, to actually switch them. +void updateThemeAndStyle(bool forceStyle = false); } #endif diff -Nru muse-2.1.2/muse/icons.cpp muse-3.0.2+ds1/muse/icons.cpp --- muse-2.1.2/muse/icons.cpp 2013-03-28 20:34:30.000000000 +0000 +++ muse-3.0.2+ds1/muse/icons.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -21,7 +21,9 @@ // //========================================================= -#include "globals.h" +// NOTICE: Although it is tempting to use multi-icons (addPixmap, addFile etc.), +// certain styles do not support it, such as QtCurve. +// Therefore the separate icons must be manually set upon each state. #include @@ -43,8 +45,8 @@ #include "xpm/midi_inputplugins_random_rhythm_generator.xpm" #include "xpm/midi_local_off.xpm" #include "xpm/midi_reset_instr.xpm" -#include "xpm/midi_thru_off4.xpm" -#include "xpm/midi_thru_on4.xpm" +#include "xpm/midi_thru_off5.xpm" +#include "xpm/midi_thru_on5.xpm" #include "xpm/settings_appearance_settings.xpm" #include "xpm/settings_configureshortcuts.xpm" #include "xpm/settings_follow_song.xpm" @@ -266,15 +268,28 @@ #include "xpm/sine.xpm" #include "xpm/saw.xpm" + +#include "xpm/pianoNew.xpm" +#include "xpm/presetsNew.xpm" + +#include "xpm/cpu.xpm" + +#include "xpm/router_filter_source.xpm" +#include "xpm/router_filter_destination.xpm" +#include "xpm/router_filter_source_routes.xpm" +#include "xpm/router_filter_destination_routes.xpm" +#include "xpm/router_view_splitter.xpm" + #include "icons.h" -#if QT_VERSION >= 0x040600 -#define MPIXMAP(a,b) QPixmap(QIcon::fromTheme(b, QIcon(QPixmap(a))).pixmap(QPixmap(a).width(),QPixmap(a).height())) -#define MICON(a,b) QIcon(QIcon::fromTheme(b, QIcon(QPixmap(a)))) -#else +//#if QT_VERSION >= 0x040600 +//#define MPIXMAP(a,b) QPixmap(QIcon::fromTheme(b, QIcon(QPixmap(a))).pixmap(QPixmap(a).width(),QPixmap(a).height())) +//#define MICON(a,b) QIcon(QIcon::fromTheme(b, QIcon(QPixmap(a)))) +//#else #define MPIXMAP(a,b) QPixmap(a) +#define MPNGIMG(a) QPixmap(a) #define MICON(a,b) QIcon(QPixmap(a)) -#endif +//#endif namespace MusEGui { @@ -533,6 +548,114 @@ QPixmap* sineIcon; QPixmap* sawIcon; +QIcon* pianoNewIcon; +QIcon* presetsNewIcon; + +QIcon* cpuIcon; +QPixmap* sliderPngImage; +QPixmap* knobPngImage; +QPixmap* knobBasePngImage; +QPixmap* rimBasePngImage; +QPixmap* knobSmallPngImage; + +QPixmap* routerFilterSourceIcon; +QPixmap* routerFilterDestinationIcon; +QPixmap* routerFilterSourceRoutesIcon; +QPixmap* routerFilterDestinationRoutesIcon; +QPixmap* routerViewSplitterIcon; + + +//---------------------------------- +// SVG... +//---------------------------------- + +QPixmap* routingInputSVGPixmap; +QPixmap* routingOutputSVGPixmap; +QPixmap* routingInputUnconnectedSVGPixmap; +QPixmap* routingOutputUnconnectedSVGPixmap; + +QPixmap* headphonesOffSVGPixmap; +QPixmap* headphonesOnSVGPixmap; + +QPixmap* muteOffSVGPixmap; +QPixmap* muteOnSVGPixmap; +QPixmap* muteOnXSVGPixmap; +QPixmap* muteProxyOnSVGPixmap; +QPixmap* muteAndProxyOnSVGPixmap; + +QPixmap* soloOffSVGPixmap; +QPixmap* soloOnSVGPixmap; +QPixmap* soloOnAloneSVGPixmap; +QPixmap* soloProxyOnSVGPixmap; +QPixmap* soloProxyOnAloneSVGPixmap; +QPixmap* soloAndProxyOnSVGPixmap; + +QPixmap* trackOffSVGPixmap; +QPixmap* trackOnSVGPixmap; + +QPixmap* stereoOffSVGPixmap; +QPixmap* stereoOnSVGPixmap; + +QPixmap* preFaderOffSVGPixmap; +QPixmap* preFaderOnSVGPixmap; + +QPixmap* recArmOffSVGPixmap; +QPixmap* recArmOnSVGPixmap; + +QPixmap* monitorOffSVGPixmap; +QPixmap* monitorOnSVGPixmap; + + +QIcon* routingInputSVGIcon; +QIcon* routingOutputSVGIcon; +QIcon* routingInputUnconnectedSVGIcon; +QIcon* routingOutputUnconnectedSVGIcon; + +QIcon* headphonesOffSVGIcon; +QIcon* headphonesOnSVGIcon; + +QIcon* muteOffSVGIcon; +QIcon* muteOnSVGIcon; +QIcon* muteOnXSVGIcon; +QIcon* muteProxyOnSVGIcon; +QIcon* muteAndProxyOnSVGIcon; + +QIcon* soloOffSVGIcon; +QIcon* soloOnSVGIcon; +QIcon* soloOnAloneSVGIcon; +QIcon* soloProxyOnSVGIcon; +QIcon* soloProxyOnAloneSVGIcon; +QIcon* soloAndProxyOnSVGIcon; + +QIcon* trackOffSVGIcon; +QIcon* trackOnSVGIcon; + +QIcon* stereoOffSVGIcon; +QIcon* stereoOnSVGIcon; + +QIcon* preFaderOffSVGIcon; +QIcon* preFaderOnSVGIcon; + +QIcon* recArmOffSVGIcon; +QIcon* recArmOnSVGIcon; + +QIcon* monitorOffSVGIcon; +QIcon* monitorOnSVGIcon; + + +QIcon* soloSVGIcon; +QIcon* soloProxySVGIcon; +QIcon* muteSVGIcon; +QIcon* trackEnableSVGIcon; +QIcon* recArmSVGIcon; +QIcon* recMasterSVGIcon; + +QIcon* stopSVGIcon; +QIcon* playSVGIcon; +QIcon* fastForwardSVGIcon; +QIcon* rewindSVGIcon; +QIcon* rewindToStartSVGIcon; + //--------------------------------------------------------- // initIcons //--------------------------------------------------------- @@ -702,8 +825,8 @@ miditransformSIcon = new MPIXMAP(miditransformS_xpm, NULL); midi_plugSIcon = new MPIXMAP(midi_plugS_xpm, NULL); miditransposeSIcon = new MPIXMAP(miditransposeS_xpm, NULL); - midiThruOnIcon = new MPIXMAP(midi_thru_on4_xpm, NULL); - midiThruOffIcon = new MPIXMAP(midi_thru_off4_xpm, NULL); + midiThruOnIcon = new MPIXMAP(midi_thru_on5_xpm, NULL); + midiThruOffIcon = new MPIXMAP(midi_thru_off5_xpm, NULL); mixerSIcon = new MPIXMAP(mixerS_xpm, NULL); mustangSIcon = new MPIXMAP(mustangS_xpm, NULL); @@ -793,6 +916,138 @@ sineIcon = new MPIXMAP(sine_xpm, NULL); sawIcon = new MPIXMAP(saw_xpm, NULL); + + pianoNewIcon = new MICON(pianoNew_xpm, NULL); + presetsNewIcon = new MICON(presetsNew_xpm, NULL); + + cpuIcon = new MICON(cpu_xpm, NULL); + sliderPngImage = new MPNGIMG(":/png/slider-vol.png"); + + knobPngImage = new MPNGIMG(":/png/knob.png"); + knobBasePngImage = new MPNGIMG(":/png/knob-base.png"); + knobSmallPngImage = new MPNGIMG(":/png/knob-small.png"); + rimBasePngImage = new MPNGIMG(":/png/rim-base.png"); + + routerFilterSourceIcon = new MPIXMAP(router_filter_source_xpm, NULL); + routerFilterDestinationIcon = new MPIXMAP(router_filter_destination_xpm, NULL); + routerFilterSourceRoutesIcon = new MPIXMAP(router_filter_source_routes_xpm, NULL); + routerFilterDestinationRoutesIcon = new MPIXMAP(router_filter_destination_routes_xpm, NULL); + routerViewSplitterIcon = new MPIXMAP(router_view_splitter_xpm, NULL); + + + //---------------------------------- + // SVG... + //---------------------------------- + + routingInputSVGPixmap = new MPIXMAP(":/svg/routing_input.svg", NULL); + routingOutputSVGPixmap = new MPIXMAP(":/svg/routing_output.svg", NULL); + routingInputUnconnectedSVGPixmap = new MPIXMAP(":/svg/routing_input_unconnected.svg", NULL); + routingOutputUnconnectedSVGPixmap = new MPIXMAP(":/svg/routing_output_unconnected.svg", NULL); + + headphonesOffSVGPixmap = new MPIXMAP(":/svg/headphones_off.svg", NULL); + headphonesOnSVGPixmap = new MPIXMAP(":/svg/headphones_on.svg", NULL); + + muteOffSVGPixmap = new MPIXMAP(":/svg/mute_off.svg", NULL); + muteOnSVGPixmap = new MPIXMAP(":/svg/mute_on.svg", NULL); + muteOnXSVGPixmap = new MPIXMAP(":/svg/mute_on_X.svg", NULL); + muteProxyOnSVGPixmap = new MPIXMAP(":/svg/mute_proxy_on.svg", NULL); + muteAndProxyOnSVGPixmap = new MPIXMAP(":/svg/mute_and_proxy_on.svg", NULL); + + soloOffSVGPixmap = new MPIXMAP(":/svg/solo_spotlight_off.svg", NULL); + soloOnSVGPixmap = new MPIXMAP(":/svg/solo_spotlight_on.svg", NULL); + soloOnAloneSVGPixmap = new MPIXMAP(":/svg/solo_spotlight_on_alone.svg", NULL); + soloProxyOnSVGPixmap = new MPIXMAP(":/svg/solo_proxy_spotlight_on.svg", NULL); + soloProxyOnAloneSVGPixmap = new MPIXMAP(":/svg/solo_proxy_spotlight_on_alone.svg", NULL); + soloAndProxyOnSVGPixmap = new MPIXMAP(":/svg/solo_and_proxy_spotlight_on.svg", NULL); + + trackOffSVGPixmap = new MPIXMAP(":/svg/track_off.svg", NULL); + trackOnSVGPixmap = new MPIXMAP(":/svg/track_on.svg", NULL); + + stereoOffSVGPixmap = new MPIXMAP(":/svg/stereo_off.svg", NULL); + stereoOnSVGPixmap = new MPIXMAP(":/svg/stereo_on.svg", NULL); + + preFaderOffSVGPixmap = new MPIXMAP(":/svg/pre_fader_off.svg", NULL); + preFaderOnSVGPixmap = new MPIXMAP(":/svg/pre_fader_on.svg", NULL); + + //recArmOffSVGPixmap = new MPIXMAP(":/svg/rec_arm_off.svg", NULL); + recArmOffSVGPixmap = new MPIXMAP(":/svg/rec_arm_off_default_col.svg", NULL); + recArmOnSVGPixmap = new MPIXMAP(":/svg/rec_arm_on.svg", NULL); + + //monitorOffSVGPixmap = new MPIXMAP(":/svg/monitor_off.svg", NULL); + monitorOffSVGPixmap = new MPIXMAP(":/svg/monitor_off_default_col.svg", NULL); + monitorOnSVGPixmap = new MPIXMAP(":/svg/monitor_on.svg", NULL); + //monitorOffSVGPixmap = new MPIXMAP(":/svg/headphones_off.svg", NULL); + //monitorOnSVGPixmap = new MPIXMAP(":/svg/headphones_on.svg", NULL); + + + routingInputSVGIcon = new QIcon(*routingInputSVGPixmap); + routingOutputSVGIcon = new QIcon(*routingOutputSVGPixmap); + routingInputUnconnectedSVGIcon = new QIcon(*routingInputUnconnectedSVGPixmap); + routingOutputUnconnectedSVGIcon = new QIcon(*routingOutputUnconnectedSVGPixmap); + + headphonesOffSVGIcon = new QIcon(*headphonesOffSVGPixmap); + headphonesOnSVGIcon = new QIcon(*headphonesOnSVGPixmap); + + muteOffSVGIcon = new QIcon(*muteOffSVGPixmap); + muteOnSVGIcon = new QIcon(*muteOnSVGPixmap); + muteOnXSVGIcon = new QIcon(*muteOnXSVGPixmap); + muteProxyOnSVGIcon = new QIcon(*muteProxyOnSVGPixmap); + muteAndProxyOnSVGIcon = new QIcon(*muteAndProxyOnSVGPixmap); + + soloOffSVGIcon = new QIcon(*soloOffSVGPixmap); + soloOnSVGIcon = new QIcon(*soloOnSVGPixmap); + soloOnAloneSVGIcon = new QIcon(*soloOnAloneSVGPixmap); + soloProxyOnSVGIcon = new QIcon(*soloProxyOnSVGPixmap); + soloProxyOnAloneSVGIcon = new QIcon(*soloProxyOnAloneSVGPixmap); + soloAndProxyOnSVGIcon = new QIcon(*soloAndProxyOnSVGPixmap); + + trackOffSVGIcon = new QIcon(*trackOffSVGPixmap); + trackOnSVGIcon = new QIcon(*trackOnSVGPixmap); + + stereoOffSVGIcon = new QIcon(*stereoOffSVGPixmap); + stereoOnSVGIcon = new QIcon(*stereoOnSVGPixmap); + + preFaderOffSVGIcon = new QIcon(*preFaderOffSVGPixmap); + preFaderOnSVGIcon = new QIcon(*preFaderOnSVGPixmap); + + recArmOffSVGIcon = new QIcon(*recArmOffSVGPixmap); + recArmOnSVGIcon = new QIcon(*recArmOnSVGPixmap); + + monitorOffSVGIcon = new QIcon(*monitorOffSVGPixmap); + monitorOnSVGIcon = new QIcon(*monitorOnSVGPixmap); + + + soloSVGIcon = new QIcon(":/svg/headphones_off.svg"); + soloSVGIcon->addFile(":/svg/headphones_on.svg", QSize(), QIcon::Normal, QIcon::On); + // TODO + soloProxySVGIcon = new QIcon(":/svg/headphones_off.svg"); + soloProxySVGIcon->addFile(":/svg/headphones_on.svg", QSize(), QIcon::Normal, QIcon::On); + + muteSVGIcon = new QIcon(":/svg/mute_off.svg"); + muteSVGIcon->addFile(":/svg/mute_on.svg", QSize(), QIcon::Normal, QIcon::On); + + trackEnableSVGIcon = new QIcon(":/svg/track_on.svg"); + trackEnableSVGIcon->addFile(":/svg/track_off.svg", QSize(), QIcon::Normal, QIcon::On); + + //recArmSVGIcon = new QIcon(":/svg/rec_arm_off_default_col.svg"); + recArmSVGIcon = new QIcon(":/svg/rec_arm_off.svg"); + recArmSVGIcon->addFile(":/svg/rec_arm_on.svg", QSize(), QIcon::Normal, QIcon::On); + + //recMasterSVGIcon = new QIcon(":/svg/rec_arm_off_default_col.svg"); + recMasterSVGIcon = new QIcon(":/svg/rec_arm_off.svg"); + recMasterSVGIcon->addFile(":/svg/rec_arm_on.svg", QSize(), QIcon::Normal, QIcon::On); + + + stopSVGIcon = new QIcon(":/svg/stop.svg"); + + playSVGIcon = new QIcon(":/svg/play_off.svg"); + playSVGIcon->addFile(":/svg/play_on.svg", QSize(), QIcon::Normal, QIcon::On); + + fastForwardSVGIcon = new QIcon(":/svg/fast_forward.svg"); + + rewindSVGIcon = new QIcon(":/svg/rewind.svg"); + + rewindToStartSVGIcon = new QIcon(":/svg/rewind_to_start.svg"); } //--------------------------------------------------------- @@ -1041,6 +1296,111 @@ delete sineIcon; delete sawIcon; + + delete cpuIcon; + + delete sliderPngImage; + + delete knobPngImage; + delete knobBasePngImage; + delete knobSmallPngImage; + delete rimBasePngImage; + + delete routerFilterSourceIcon; + delete routerFilterDestinationIcon; + delete routerFilterSourceRoutesIcon; + delete routerFilterDestinationRoutesIcon; + delete routerViewSplitterIcon; + + + //---------------------------------- + // SVG... + //---------------------------------- + + delete routingInputSVGPixmap; + delete routingOutputSVGPixmap; + delete routingInputUnconnectedSVGPixmap; + delete routingOutputUnconnectedSVGPixmap; + + delete headphonesOffSVGPixmap; + delete headphonesOnSVGPixmap; + + delete muteOffSVGPixmap; + delete muteOnSVGPixmap; + delete muteOnXSVGPixmap; + + delete soloOffSVGPixmap; + delete soloOnSVGPixmap; + delete soloOnAloneSVGPixmap; + delete soloProxyOnSVGPixmap; + delete soloProxyOnAloneSVGPixmap; + delete soloAndProxyOnSVGPixmap; + + delete trackOffSVGPixmap; + delete trackOnSVGPixmap; + + delete stereoOffSVGPixmap; + delete stereoOnSVGPixmap; + + delete preFaderOffSVGPixmap; + delete preFaderOnSVGPixmap; + + delete recArmOffSVGPixmap; + delete recArmOnSVGPixmap; + + delete monitorOffSVGPixmap; + delete monitorOnSVGPixmap; + + + delete routingInputSVGIcon; + delete routingOutputSVGIcon; + delete routingInputUnconnectedSVGIcon; + delete routingOutputUnconnectedSVGIcon; + + delete headphonesOffSVGIcon; + delete headphonesOnSVGIcon; + + delete muteOffSVGIcon; + delete muteOnSVGIcon; + delete muteOnXSVGIcon; + delete muteProxyOnSVGIcon; + delete muteAndProxyOnSVGIcon; + + delete soloOffSVGIcon; + delete soloOnSVGIcon; + delete soloOnAloneSVGIcon; + delete soloProxyOnSVGIcon; + delete soloProxyOnAloneSVGIcon; + delete soloAndProxyOnSVGIcon; + + delete trackOffSVGIcon; + delete trackOnSVGIcon; + + delete stereoOffSVGIcon; + delete stereoOnSVGIcon; + + delete preFaderOffSVGIcon; + delete preFaderOnSVGIcon; + + delete recArmOffSVGIcon; + delete recArmOnSVGIcon; + + delete monitorOffSVGIcon; + delete monitorOnSVGIcon; + + + delete soloSVGIcon; + delete soloProxySVGIcon; + delete muteSVGIcon; + delete trackEnableSVGIcon; + delete recArmSVGIcon; + delete recMasterSVGIcon; + + delete stopSVGIcon; + delete playSVGIcon; + delete fastForwardSVGIcon; + delete rewindSVGIcon; + delete rewindToStartSVGIcon; } } // namespace MusEGui diff -Nru muse-2.1.2/muse/icons.h muse-3.0.2+ds1/muse/icons.h --- muse-2.1.2/muse/icons.h 2013-03-28 20:34:30.000000000 +0000 +++ muse-3.0.2+ds1/muse/icons.h 2018-01-06 20:31:35.000000000 +0000 @@ -27,6 +27,11 @@ class QPixmap; class QIcon; + +// NOTICE: Although it is tempting to use multi-icons (addPixmap, addFile etc.), +// certain styles do not support it, such as QtCurve. +// Therefore the separate icons must be manually set upon each state. + namespace MusEGui { extern QPixmap* track_commentIcon; @@ -281,6 +286,114 @@ extern QPixmap* sineIcon; extern QPixmap* sawIcon; +extern QIcon* pianoNewIcon; +extern QIcon* presetsNewIcon; + +extern QIcon* cpuIcon; +extern QPixmap* sliderPngImage; +extern QPixmap* knobPngImage; +extern QPixmap* knobBasePngImage; +extern QPixmap* rimBasePngImage; +extern QPixmap* knobSmallPngImage; + +extern QPixmap* routerFilterSourceIcon; +extern QPixmap* routerFilterDestinationIcon; +extern QPixmap* routerFilterSourceRoutesIcon; +extern QPixmap* routerFilterDestinationRoutesIcon; +extern QPixmap* routerViewSplitterIcon; + + +//---------------------------------- +// SVG... +//---------------------------------- + +extern QPixmap* routingInputSVGPixmap; +extern QPixmap* routingOutputSVGPixmap; +extern QPixmap* routingInputUnconnectedSVGPixmap; +extern QPixmap* routingOutputUnconnectedSVGPixmap; + +extern QPixmap* headphonesOffSVGPixmap; +extern QPixmap* headphonesOnSVGPixmap; + +extern QPixmap* muteOffSVGPixmap; +extern QPixmap* muteOnSVGPixmap; +extern QPixmap* muteOnXSVGPixmap; +extern QPixmap* muteProxyOnSVGPixmap; +extern QPixmap* muteAndProxyOnSVGPixmap; + +extern QPixmap* soloOffSVGPixmap; +extern QPixmap* soloOnSVGPixmap; +extern QPixmap* soloOnAloneSVGPixmap; +extern QPixmap* soloProxyOnSVGPixmap; +extern QPixmap* soloProxyOnAloneSVGPixmap; +extern QPixmap* soloAndProxyOnSVGPixmap; + +extern QPixmap* trackOffSVGPixmap; +extern QPixmap* trackOnSVGPixmap; + +extern QPixmap* stereoOffSVGPixmap; +extern QPixmap* stereoOnSVGPixmap; + +extern QPixmap* preFaderOffSVGPixmap; +extern QPixmap* preFaderOnSVGPixmap; + +extern QPixmap* recArmOffSVGPixmap; +extern QPixmap* recArmOnSVGPixmap; + +extern QPixmap* monitorOffSVGPixmap; +extern QPixmap* monitorOnSVGPixmap; + + +extern QIcon* routingInputSVGIcon; +extern QIcon* routingOutputSVGIcon; +extern QIcon* routingInputUnconnectedSVGIcon; +extern QIcon* routingOutputUnconnectedSVGIcon; + +extern QIcon* headphonesOffSVGIcon; +extern QIcon* headphonesOnSVGIcon; + +extern QIcon* muteOffSVGIcon; +extern QIcon* muteOnSVGIcon; +extern QIcon* muteOnXSVGIcon; +extern QIcon* muteProxyOnSVGIcon; +extern QIcon* muteAndProxyOnSVGIcon; + +extern QIcon* soloOffSVGIcon; +extern QIcon* soloOnSVGIcon; +extern QIcon* soloOnAloneSVGIcon; +extern QIcon* soloProxyOnSVGIcon; +extern QIcon* soloProxyOnAloneSVGIcon; +extern QIcon* soloAndProxyOnSVGIcon; + +extern QIcon* trackOffSVGIcon; +extern QIcon* trackOnSVGIcon; + +extern QIcon* stereoOffSVGIcon; +extern QIcon* stereoOnSVGIcon; + +extern QIcon* preFaderOffSVGIcon; +extern QIcon* preFaderOnSVGIcon; + +extern QIcon* recArmOffSVGIcon; +extern QIcon* recArmOnSVGIcon; + +extern QIcon* monitorOffSVGIcon; +extern QIcon* monitorOnSVGIcon; + + +extern QIcon* soloSVGIcon; +extern QIcon* soloProxySVGIcon; +extern QIcon* muteSVGIcon; +extern QIcon* trackEnableSVGIcon; +extern QIcon* recArmSVGIcon; +extern QIcon* recMasterSVGIcon; + +extern QIcon* stopSVGIcon; +extern QIcon* playSVGIcon; +extern QIcon* fastForwardSVGIcon; +extern QIcon* rewindSVGIcon; +extern QIcon* rewindToStartSVGIcon; + } // namespace MusEGui #endif diff -Nru muse-2.1.2/muse/importmidi.cpp muse-3.0.2+ds1/muse/importmidi.cpp --- muse-2.1.2/muse/importmidi.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/importmidi.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -174,8 +174,9 @@ if(md) { // TEST: Hopefully shouldn't require any routing saves/restorations as in midi port config set device name... - MusEGlobal::midiSeq->msgSetMidiDevice(mp, md); + MusEGlobal::audio->msgSetMidiDevice(mp, md); // TEST: Hopefully can get away with this ouside the loop below... + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //MusEGlobal::muse->changeConfig(true); // save configuration file //MusEGlobal::audio->msgUpdateSoloStates(); //MusEGlobal::song->update(); @@ -230,7 +231,7 @@ { // this overwrites any instrument set for this port: if(mp->instrument() != instr) - mp->setInstrument(instr); + mp->changeInstrument(instr); } } @@ -238,7 +239,8 @@ { // TEST: Hopefully can get away with this here instead of inside the loop above... // TEST: Are these really necessary as in midi port config set device name? - MusEGlobal::muse->changeConfig(true); // save configuration file + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + MusEGlobal::muse->changeConfig(true); // save configuration file MusEGlobal::audio->msgUpdateSoloStates(); // MusEGlobal::song->update(); } @@ -249,8 +251,8 @@ // - calculate tick value for internal resolution // for (MusECore::iMidiFileTrack t = etl->begin(); t != etl->end(); ++t) { - MusECore::MPEventList* el = &((*t)->events); - if (el->empty()) + MusECore::MPEventList& el = ((*t)->events); + if (el.empty()) continue; // // if we split the track, SYSEX and META events go into @@ -264,7 +266,7 @@ MusECore::iMPEvent ev; set< pair > already_processed; - for (ev = el->begin(); ev != el->end(); ++ev) + for (ev = el.begin(); ev != el.end(); ++ev) { if (ev->type() != MusECore::ME_SYSEX && ev->type() != MusECore::ME_META) { @@ -288,9 +290,7 @@ track->setOutPort(port); MusECore::MidiPort* mport = &MusEGlobal::midiPorts[port]; - //MusECore::MidiInstrument* instr = mport->instrument(); - MusECore::EventList* mel = track->events(); - buildMidiEventList(mel, el, track, division, first, false); // Don't do loops. + buildMidiEventList(&track->events, el, track, division, first, false); // Don't do loops. first = false; // Comment Added by T356. @@ -305,8 +305,7 @@ { track->setType(MusECore::Track::DRUM); // remap drum pitch with drumOutmap (was: Inmap. flo93 thought this was wrong) - MusECore::EventList* tevents = track->events(); - for (MusECore::iEvent i = tevents->begin(); i != tevents->end(); ++i) { + for (MusECore::iEvent i = track->events.begin(); i != track->events.end(); ++i) { MusECore::Event ev = i->second; if (ev.isNote()) { int pitch = MusEGlobal::drumOutmap[ev.pitch()]; @@ -339,8 +338,7 @@ MusECore::MidiTrack* track = new MusECore::MidiTrack(); track->setOutChannel(0); track->setOutPort(0); - MusECore::EventList* mel = track->events(); - buildMidiEventList(mel, el, track, division, true, false); // Do SysexMeta. Don't do loops. + buildMidiEventList(&track->events, el, track, division, true, false); // Do SysexMeta. Don't do loops. processTrack(track); MusEGlobal::song->insertTrack0(track, -1); } @@ -383,8 +381,8 @@ void MusE::processTrack(MusECore::MidiTrack* track) { - MusECore::EventList* tevents = track->events(); - if (tevents->empty()) + MusECore::EventList& tevents = track->events; + if (tevents.empty()) return; //--------------------------------------------------- @@ -398,8 +396,8 @@ MusECore::PartList* pl = track->parts(); int lastTick = 0; - for (MusECore::iEvent i = tevents->begin(); i != tevents->end(); ++i) { - MusECore::Event event = i->second; + for (MusECore::ciEvent i = tevents.begin(); i != tevents.end(); ++i) { + const MusECore::Event& event = i->second; int epos = event.tick() + event.lenTick(); if (epos > lastTick) lastTick = epos; @@ -426,8 +424,8 @@ if (lastOff > x2) { continue; } - MusECore::iEvent i1 = tevents->lower_bound(x1); - MusECore::iEvent i2 = tevents->lower_bound(x2); + MusECore::iEvent i1 = tevents.lower_bound(x1); + MusECore::iEvent i2 = tevents.lower_bound(x2); if (i1 == i2) { // empty? if (st != -1) { @@ -444,8 +442,8 @@ st = x1; // begin new part //HACK: //lastOff: - for (MusECore::iEvent i = i1; i != i2; ++i) { - MusECore::Event event = i->second; + for (MusECore::ciEvent i = i1; i != i2; ++i) { + const MusECore::Event& event = i->second; if (event.type() == MusECore::Note) { int off = event.tick() + event.lenTick(); if (off > lastOff) @@ -480,28 +478,27 @@ MusECore::MidiPart* part = (MusECore::MidiPart*)(p->second); int stick = part->tick(); int etick = part->tick() + part->lenTick(); - MusECore::iEvent r1 = tevents->lower_bound(stick); - MusECore::iEvent r2 = tevents->lower_bound(etick); + MusECore::iEvent r1 = tevents.lower_bound(stick); + MusECore::iEvent r2 = tevents.lower_bound(etick); int startTick = part->tick(); - MusECore::EventList* el = part->events(); for (MusECore::iEvent i = r1; i != r2; ++i) { - MusECore::Event ev = i->second; + MusECore::Event& ev = i->second; int ntick = ev.tick() - startTick; ev.setTick(ntick); - el->add(ev); + part->addEvent(ev); } - tevents->erase(r1, r2); + tevents.erase(r1, r2); } - if (tevents->size()) - printf("-----------events left: %zd\n", tevents->size()); - for (MusECore::iEvent i = tevents->begin(); i != tevents->end(); ++i) { + if (tevents.size()) + printf("-----------events left: %zd\n", tevents.size()); + for (MusECore::ciEvent i = tevents.begin(); i != tevents.end(); ++i) { printf("%d===\n", i->first); i->second.dump(); } // all events should be processed: - if (!tevents->empty()) + if (!tevents.empty()) printf("THIS SHOULD NEVER HAPPEN: not all events processed at the end of MusE::processTrack()!\n"); } @@ -620,7 +617,7 @@ if (tag == "part") { // Read the part. MusECore::Part* p = 0; - p = readXmlPart(xml, track); + p = MusECore::Part::readFromXml(xml, track); // If it could not be created... if(!p) { diff -Nru muse-2.1.2/muse/instruments/CMakeLists.txt muse-3.0.2+ds1/muse/instruments/CMakeLists.txt --- muse-2.1.2/muse/instruments/CMakeLists.txt 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/instruments/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( instruments_mocs +QT5_WRAP_CPP ( instruments_mocs editinstrument.h # minstrument.h ) @@ -33,19 +33,17 @@ ## UI files ## file (GLOB instruments_ui_files - # ccontrollerbase.ui # not built. It needs to be converted to Qt4 for revival. + # ccontrollerbase.ui # not built. It needs to be converted to Qt5 for revival. editinstrumentbase.ui ) -QT4_WRAP_UI ( instruments_uis ${instruments_ui_files} ) +QT5_WRAP_UI ( instruments_uis ${instruments_ui_files} ) ## ## List of source files to compile ## file (GLOB instruments_source_files editinstrument.cpp - editinstrument.h minstrument.cpp - minstrument.h ) ## @@ -81,6 +79,7 @@ target_link_libraries ( instruments ${QT_LIBRARIES} icons + widgets ) ## diff -Nru muse-2.1.2/muse/instruments/editinstrumentbase.ui muse-3.0.2+ds1/muse/instruments/editinstrumentbase.ui --- muse-2.1.2/muse/instruments/editinstrumentbase.ui 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/instruments/editinstrumentbase.ui 2017-12-04 21:01:18.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 772 - 421 + 816 + 505 @@ -34,8 +34,17 @@ + + 2 + + + + 0 + 0 + + Instrument Name: @@ -46,6 +55,12 @@ + + + 0 + 0 + + @@ -56,11 +71,65 @@ + + + 0 + 0 + + List of defined instruments. + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + Note off mode: + + + + + + + + 0 + 0 + + + + Note off mode + + + Selects how to handle note off events. +The instrument can use note off events, + or not at all, or convert them to + zero-velocity note on events which helps + save midi interface bandwidth. +Refer to the instrument manufacturer's + midi implementation chart for details. + + + @@ -255,16 +324,19 @@ Patch program number - Patch program number + Patch program number. --- means don't care. + + + --- - 1 + 0 128 - 1 + 0 @@ -287,67 +359,30 @@ - - - - - Drum patch - - - If set, the patch is available only for drum channels. - - - Drum - - - - - - + - - - GM patch - - - If set, the patch is available in a 'GM' or 'NO' midi song type. - + - GM - - - + Show in tracks: - - - GS patch - - - If set, the patch is available in a 'GS' or 'NO' midi song type. - + - GS + Midi - - - XG patch - - - If set, the patch is available in an 'XG' or 'NO' midi song type. - + - XG + &Drum - + Qt::Horizontal @@ -461,10 +496,13 @@ Drum&maps - + - - + + + Qt::Horizontal + + @@ -496,6 +534,9 @@ + + 2 + @@ -541,10 +582,26 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + 2 + @@ -577,6 +634,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -584,280 +654,226 @@ - false + true - + + 0 + + + 0 + + + 0 + + 0 - - - - 0 - 0 - - - - Patch: - - - - - - - false - - - - 0 - 0 - - - - from - - - - - - - false - - - - 0 - 0 - - - - false - - - 1 - - - 128 - - - - - - - false - - - - 0 - 0 - - - - to - - - - - - - false - - - - 0 - 0 - - - - false - - - 1 - - - 128 - - + + + 2 + + + + + + + High Bank + + + + + + + true + + + + 0 + 0 + + + + Patch high bank number + + + Patch high bank number. --- means don't care. + + + --- + + + false + + + 0 + + + 128 + + + 0 + + + + + + + + + + + Low Bank + + + + + + + true + + + + 0 + 0 + + + + Patch low bank number + + + Patch low bank number. --- means don't care. + + + --- + + + false + + + 0 + + + 128 + + + 0 + + + + + + + + + + + Program + + + + + + + true + + + + 0 + 0 + + + + Patch program number + + + Patch program number. --- means don't care. + + + --- + + + false + + + 0 + + + 128 + + + 0 + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - - 0 - 0 - - - - Bank Hi: - - - - - - - false - - - - 0 - 0 - - - - from - - - - - - - false - - - - 0 - 0 - - - - false - - - 1 - - - 128 - - - - - - - false - - - - 0 - 0 - - - - to - - - - - - - false - - - - 0 - 0 - - - - false - - - 1 - - - 128 - - - - - - - - 0 - 0 - - - - Bank Lo: - - - - - - - false - - - - 0 - 0 - - - - from - - - - - - - false - - - - 0 - 0 - - - - false - - - 1 - - - 128 - - - - - - - false - - - - 0 - 0 - - - - to - - - - - - - false - - - - 0 - 0 - - - - false - - - 1 - - - 128 - - + + + 2 + + + + + + 0 + 0 + + + + Patch Name: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + ??? + + + + + + + Qt::Horizontal + + + + 6 + 20 + + + + + @@ -865,11 +881,16 @@ - - - - - + + + + + 100 + 0 + + + + @@ -877,110 +898,7 @@ Contro&llers - - - - - - - Null Parameters: Hi: - - - false - - - - - - - - 0 - 0 - - - - Null parameter number High byte - - - If set, these 'null' parameter numbers will - be sent after each RPN/NRPN event. -This prevents subsequent 'data' events - from corrupting the RPN/NRPN controller. -Typically, set to 127/127, or an unused - RPN/NRPN controller number. - - - off - - - -1 - - - 127 - - - 127 - - - - - - - Lo: - - - false - - - - - - - - 0 - 0 - - - - Null parameter number Low byte - - - If set, these 'null' parameter numbers will - be sent after each RPN/NRPN event. -This prevents subsequent 'data' events - from corrupting the RPN/NRPN controller. -Typically, set to 127/127, or an unused - RPN/NRPN controller number. - - - off - - - -1 - - - 127 - - - 127 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + @@ -1012,7 +930,7 @@ Name - AlignLeft|AlignVCenter + AlignLeading|AlignVCenter @@ -1020,7 +938,7 @@ Type - AlignLeft|AlignVCenter + AlignLeading|AlignVCenter @@ -1028,7 +946,7 @@ H-Ctrl - AlignRight|AlignVCenter + AlignTrailing|AlignVCenter @@ -1036,7 +954,7 @@ L-Ctrl - AlignRight|AlignVCenter + AlignTrailing|AlignVCenter @@ -1044,7 +962,7 @@ Min - AlignRight|AlignVCenter + AlignTrailing|AlignVCenter @@ -1052,7 +970,7 @@ Max - AlignRight|AlignVCenter + AlignTrailing|AlignVCenter @@ -1060,7 +978,15 @@ Def - AlignRight|AlignVCenter + AlignTrailing|AlignVCenter + + + + + Dr def + + + AlignTrailing|AlignVCenter @@ -1068,7 +994,7 @@ Midi - AlignRight|AlignVCenter + AlignTrailing|AlignVCenter @@ -1076,7 +1002,7 @@ Drum - AlignRight|AlignVCenter + AlignTrailing|AlignVCenter @@ -1092,11 +1018,20 @@ Properties - + 2 - + + 2 + + + 2 + + + 2 + + 2 @@ -1491,39 +1426,60 @@ - + - - - Default: - - - Qt::AlignVCenter - - - false - - + + + + + Default: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + + + + + Drum default: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + + - - - - 0 - 0 - - - - - 0 - 0 - - - - Default value. Off: No default. - - - Default (initial) value. Off means no default. + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Default value. Off: No default. + + + Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to @@ -1538,141 +1494,318 @@ values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). - - - off - - - -1 - - - 16383 - - - -1 - - - - - - - H-Bank - - - false - - - - - - - - 0 - 0 - - - - off - - - 0 - - - 128 - - - 0 - - - - - - - L-Bank - - - false - - - - - - - - 0 - 0 - - - - off - - - 0 - - - 128 - - - 0 - - - - - - - Progr. - - - false - - - - - - - - 0 - 0 - - - - off - - - 0 - - - 128 - - - 0 - - - - - - - true - - - - 0 - 0 - - - - - 0 - 0 - - - - ??? - - - - - + + + off + + + -1 + + + 16383 + + + -1 + + + + + + + H-Bank + + + false + + + + + + + + 0 + 0 + + + + off + + + 0 + + + 128 + + + 0 + + + + + + + L-Bank + + + false + + + + + + + + 0 + 0 + + + + off + + + 0 + + + 128 + + + 0 + + + + + + + Progr. + + + false + + + + + + + + 0 + 0 + + + + off + + + 0 + + + 128 + + + 0 + + + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + ??? + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Default value. Off: No default. + + + Default (initial) value. Off means no default. + +If a default value is chosen, the value will be sent + to the controller when the controller is added to + the song (in piano roll or event editor). When + the song is re-loaded, the value is sent again. +Otherwise the controller remains at its last value. +Controllers are also automatically added to a + song upon reception of a midi controller event. + +Caution! Watch out for controllers such as + 'Sustain' and 'ResetAllController' with default + values. You should probably turn 'off' their + default (in piano roll or drum edit, and + instrument editor). + + + off + + + -1 + + + 16383 + + + -1 + + + + + + + H-Bank + + + false + + + + + + + + 0 + 0 + + + + off + + + 0 + + + 128 + + + 0 + + + + + + + L-Bank + + + false + + + + + + + + 0 + 0 + + + + off + + + 0 + + + 128 + + + 0 + + + + + + + Progr. + + + false + + + + + + + + 0 + 0 + + + + off + + + 0 + + + 128 + + + 0 + + + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + ??? + + + + + + + + + @@ -1751,6 +1884,13 @@ + + + Note: The enclosing F0 ... F7 are not required + + + + @@ -1799,6 +1939,67 @@ + + + &Initialization + + + + + + Instrument initialization sequence: + + + + + + + + 1 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Add... + + + + + + + &Change... + + + + + + + &Delete + + + + + + + @@ -1824,8 +2025,8 @@ 0 0 - 772 - 23 + 816 + 30 @@ -1959,10 +2160,6 @@ spinBoxHBank spinBoxLBank spinBoxProgram - checkBoxDrum - checkBoxGM - checkBoxGS - checkBoxXG ctrlName ctrlType spinBoxHCtrlNo @@ -1977,7 +2174,7 @@ fileNewAction - activated() + triggered() EditInstrumentBase fileNew() @@ -1993,7 +2190,7 @@ fileOpenAction - activated() + triggered() EditInstrumentBase fileOpen() @@ -2009,7 +2206,7 @@ fileSaveAction - activated() + triggered() EditInstrumentBase fileSave() @@ -2025,7 +2222,7 @@ fileSaveAsAction - activated() + triggered() EditInstrumentBase fileSaveAs() @@ -2041,7 +2238,7 @@ fileCloseAction - activated() + triggered() EditInstrumentBase fileClose() @@ -2057,7 +2254,7 @@ whatsThisAction - activated() + triggered() EditInstrumentBase helpWhatsThis() @@ -2071,197 +2268,5 @@ - - patchCheckbox - toggled(bool) - label_5 - setEnabled(bool) - - - 214 - 411 - - - 289 - 412 - - - - - patchCheckbox - toggled(bool) - patchFromBox - setEnabled(bool) - - - 214 - 411 - - - 343 - 412 - - - - - patchCheckbox - toggled(bool) - label_8 - setEnabled(bool) - - - 214 - 411 - - - 390 - 412 - - - - - patchCheckbox - toggled(bool) - patchToBox - setEnabled(bool) - - - 214 - 411 - - - 436 - 412 - - - - - lbankCheckbox - toggled(bool) - lbankFromBox - setEnabled(bool) - - - 221 - 469 - - - 343 - 470 - - - - - lbankCheckbox - toggled(bool) - lbankToBox - setEnabled(bool) - - - 221 - 469 - - - 436 - 470 - - - - - lbankCheckbox - toggled(bool) - label_7 - setEnabled(bool) - - - 221 - 469 - - - 289 - 470 - - - - - lbankCheckbox - toggled(bool) - label_10 - setEnabled(bool) - - - 221 - 469 - - - 390 - 470 - - - - - hbankCheckbox - toggled(bool) - hbankFromBox - setEnabled(bool) - - - 220 - 440 - - - 343 - 441 - - - - - hbankCheckbox - toggled(bool) - hbankToBox - setEnabled(bool) - - - 220 - 440 - - - 436 - 441 - - - - - hbankCheckbox - toggled(bool) - label_6 - setEnabled(bool) - - - 220 - 440 - - - 289 - 441 - - - - - hbankCheckbox - toggled(bool) - label_9 - setEnabled(bool) - - - 220 - 440 - - - 390 - 441 - - - diff -Nru muse-2.1.2/muse/instruments/editinstrument.cpp muse-3.0.2+ds1/muse/instruments/editinstrument.cpp --- muse-2.1.2/muse/instruments/editinstrument.cpp 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/instruments/editinstrument.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: editinstrument.cpp,v 1.2.2.6 2009/05/31 05:12:12 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012, 2016 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 @@ -22,7 +22,7 @@ // //========================================================= -#include +#include #include #include @@ -37,20 +37,27 @@ #include #include #include +#include +#include +#include #include -#include "editinstrument.h" #include "minstrument.h" +#include "midictrl.h" +#include "editinstrument.h" #include "globals.h" #include "song.h" #include "xml.h" -#include "midictrl.h" +#include "midi.h" #include "gconfig.h" #include "icons.h" #include "popupmenu.h" #include "dlist.h" #include "drummap.h" #include "header.h" +#include "editevent.h" +#include "operations.h" +#include "audio.h" namespace MusECore { extern int string2sysex(const QString& s, unsigned char** data); @@ -61,18 +68,139 @@ enum { COL_CNAME = 0, COL_TYPE, - COL_HNUM, COL_LNUM, COL_MIN, COL_MAX, COL_DEF, COL_SHOW_MIDI, COL_SHOW_DRUM + COL_HNUM, COL_LNUM, COL_MIN, COL_MAX, COL_DEF, COL_DRUM_DEF, COL_SHOW_MIDI, COL_SHOW_DRUM + }; + +//--------------------------------------------------------- +// InitListItem +//--------------------------------------------------------- + +class InitListItem : public QTreeWidgetItem { + public: + MusECore::Event _event; + MusECore::MidiInstrument* _instr; + + InitListItem(QTreeWidget* parent, MusECore::Event ev, MusECore::MidiInstrument* ins) + : QTreeWidgetItem(parent) { + _event = ev; + _instr = ins; + setData(0, Qt::DisplayRole, colText(0)); + setData(1, Qt::DisplayRole, colText(1)); + setData(2, Qt::DisplayRole, colText(2)); + setData(3, Qt::DisplayRole, colText(3)); + } + virtual QString colText(int col) const; + + //virtual QVariant data(int col, int role) const; + + virtual bool operator< ( const QTreeWidgetItem & other ) const + { + int col = other.treeWidget()->sortColumn(); + InitListItem* eli = (InitListItem*) &other; + switch(col) + { + case 0: + return _event.tick() < eli->_event.tick(); + break; + case 1: + return _event.dataLen() < eli->_event.dataLen(); + break; + case 2: + return colText(col).localeAwareCompare(other.text(col)) < 0; + break; + case 3: + return colText(col).localeAwareCompare(other.text(col)) < 0; + break; + default: + break; + } + return 0; + } }; +// //--------------------------------------------------------- +// // data +// //--------------------------------------------------------- +// +// QVariant InitListItem::data(int col, int role) const +// { +// if(role != Qt::DisplayRole && role != Qt::EditRole) +// return QString(); +// return colText(col); +// } + +//--------------------------------------------------------- +// colText +//--------------------------------------------------------- + +QString InitListItem::colText(int col) const + { + QString s; + QString commentLabel; + switch(col) { + case 0: + s.setNum(_event.tick()); + break; + case 1: + s.setNum(_event.dataLen()); + break; + case 2: + switch(_event.type()) { + case MusECore::Sysex: + { + int i; + for (i = 0; i < 10; ++i) { + if (i >= _event.dataLen()) + break; + s += QString(" 0x"); + QString k; + k.setNum(_event.data()[i] & 0xff, 16); + s += k; + } + if (i == 10) + s += QString("..."); + } + break; + default: + break; + } + break; + case 3: + switch(_event.type()) { + case MusECore::Sysex: + { + return MusECore::nameSysex(_event.dataLen(), _event.data(), _instr); + } + break; + case MusECore::Controller: + s = QObject::tr("Controller !"); + break; + default: + s = QObject::tr("Other !"); + break; + } + break; + + } + return s; + } + //--------------------------------------------------------- // EditInstrument //--------------------------------------------------------- -EditInstrument::EditInstrument(QWidget* parent, Qt::WFlags fl) +EditInstrument::EditInstrument(QWidget* parent, Qt::WindowFlags fl) : QMainWindow(parent, fl) { setupUi(this); + workingInstrument = new MusECore::MidiInstrument(); + + noteOffModeList->addItem(tr("Use note offs"), MusECore::MidiInstrument::NoteOffAll); + noteOffModeList->addItem(tr("No note offs"), MusECore::MidiInstrument::NoteOffNone); + noteOffModeList->addItem(tr("Convert to 0-vel note ons"), MusECore::MidiInstrument::NoteOffConvertToZVNoteOn); + noteOffModeList->setCurrentIndex(0); + ctrlType->addItem(tr("Control7"), MusECore::MidiController::Controller7); ctrlType->addItem(tr("Control14"), MusECore::MidiController::Controller14); ctrlType->addItem(tr("RPN"), MusECore::MidiController::RPN); @@ -81,7 +209,7 @@ ctrlType->addItem(tr("NRPN14"), MusECore::MidiController::NRPN14); ctrlType->addItem(tr("Pitch"), MusECore::MidiController::Pitch); ctrlType->addItem(tr("Program"), MusECore::MidiController::Program); - ctrlType->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); + ctrlType->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); ctrlType->addItem(tr("Aftertouch"), MusECore::MidiController::Aftertouch); ctrlType->setCurrentIndex(0); @@ -94,135 +222,199 @@ toolBar->addAction(QWhatsThis::createAction(this)); Help->addAction(QWhatsThis::createAction(this)); - // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. - checkBoxGM->setEnabled(false); - checkBoxGS->setEnabled(false); - checkBoxXG->setEnabled(false); - checkBoxGM->setVisible(false); - checkBoxGS->setVisible(false); - checkBoxXG->setVisible(false); - // populate instrument list oldMidiInstrument = 0; oldPatchItem = 0; for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { - // Imperfect, crude way of ignoring internal instruments, soft synths etc. If it has a gui, - // it must be an internal instrument. But this will still allow some vst instruments (without a gui) + // Imperfect, crude way of ignoring internal instruments, soft synths etc. If it has a gui, + // it must be an internal instrument. But this will still allow some vst instruments (without a gui) // to show up in the list. // Hmm, try file path instead... //if((*i)->hasGui()) if((*i)->filePath().isEmpty()) continue; - + QListWidgetItem* item = new QListWidgetItem((*i)->iname()); - QVariant v = qVariantFromValue((void*)(*i)); - item->setData(Qt::UserRole, v); + item->setData(Qt::UserRole, QVariant::fromValue((void*)(*i))); instrumentList->addItem(item); } instrumentList->setSelectionMode(QAbstractItemView::SingleSelection); if(instrumentList->item(0)) instrumentList->setCurrentItem(instrumentList->item(0)); - + dlist_header = new Header(dlistContainer, "header"); dlist_header->setFixedHeight(31); + dlist_header->setColumnLabel(tr("H"), COL_HIDE, 20); + dlist_header->setColumnLabel(tr("M"), COL_MUTE, 20); dlist_header->setColumnLabel(tr("Name"), COL_NAME, 120); dlist_header->setColumnLabel(tr("Vol"), COL_VOLUME); - dlist_header->setColumnLabel(tr("Quant"), COL_QUANT, 30); + dlist_header->setColumnLabel(tr("Quant"), COL_QUANT, 40); dlist_header->setColumnLabel(tr("E-Note"), COL_INPUTTRIGGER, 50); - dlist_header->setColumnLabel(tr("Len"), COL_NOTELENGTH); + dlist_header->setColumnLabel(tr("Len"), COL_NOTELENGTH, 40); dlist_header->setColumnLabel(tr("A-Note"), COL_NOTE, 50); + dlist_header->setColumnLabel(tr("Ch"), COL_OUTCHANNEL); + dlist_header->setColumnLabel(tr("Port"), COL_OUTPORT, 70); dlist_header->setColumnLabel(tr("LV1"), COL_LEVEL1); dlist_header->setColumnLabel(tr("LV2"), COL_LEVEL2); dlist_header->setColumnLabel(tr("LV3"), COL_LEVEL3); dlist_header->setColumnLabel(tr("LV4"), COL_LEVEL4); - dlist_header->hideSection(COL_OUTPORT); - dlist_header->hideSection(COL_OUTCHANNEL); - dlist_header->hideSection(COL_HIDE); - dlist_header->hideSection(COL_MUTE); dlist_header->hide(); - + + setHeaderToolTips(); + setHeaderWhatsThis(); + + QFontMetrics fm(initEventList->font()); + int n = fm.width('9'); + int b = 24; + initEventList->setAllColumnsShowFocus(true); + initEventList->setSelectionMode(QAbstractItemView::SingleSelection); + QStringList columnnames; + columnnames << tr("Tick") + << tr("Len") + << tr("Data") + << tr("Name"); + initEventList->setHeaderLabels(columnnames); + initEventList->setColumnWidth(0, n * 6 + b); + initEventList->setColumnWidth(1, n * 6 + b); + //initEventList->setSortingEnabled(true); // No sorting - we want same order of event list! + //initEventList->sortByColumn(0, Qt::AscendingOrder); + connect(initEventList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(editInitListItem(QTreeWidgetItem*))); + connect(initChangeButton, SIGNAL(clicked(bool)), SLOT(initListChangeClicked())); + connect(initAddButton, SIGNAL(clicked(bool)), SLOT(initListAddClicked())); + connect(initDeleteButton, SIGNAL(clicked(bool)), SLOT(initListDeleteClicked())); + ctrlValidLabel->setPixmap(*greendotIcon); - + connect(patchFromBox, SIGNAL(valueChanged(int)), this, SLOT(patchCollectionSpinboxChanged(int))); - connect(patchToBox, SIGNAL(valueChanged(int)), this, SLOT(patchCollectionSpinboxChanged(int))); connect(lbankFromBox, SIGNAL(valueChanged(int)), this, SLOT(patchCollectionSpinboxChanged(int))); - connect(lbankToBox, SIGNAL(valueChanged(int)), this, SLOT(patchCollectionSpinboxChanged(int))); connect(hbankFromBox, SIGNAL(valueChanged(int)), this, SLOT(patchCollectionSpinboxChanged(int))); - connect(hbankToBox, SIGNAL(valueChanged(int)), this, SLOT(patchCollectionSpinboxChanged(int))); - connect(patchCheckbox, SIGNAL(toggled(bool)), this, SLOT(patchCollectionCheckboxChanged(bool))); - connect(lbankCheckbox, SIGNAL(toggled(bool)), this, SLOT(patchCollectionCheckboxChanged(bool))); - connect(hbankCheckbox, SIGNAL(toggled(bool)), this, SLOT(patchCollectionCheckboxChanged(bool))); - + connect(addCollBtn, SIGNAL(clicked()), this, SLOT(addPatchCollection())); connect(rmCollBtn, SIGNAL(clicked()), this, SLOT(delPatchCollection())); connect(copyCollBtn, SIGNAL(clicked()), this, SLOT(copyPatchCollection())); connect(collUpBtn, SIGNAL(clicked()), this, SLOT(patchCollectionUp())); connect(collDownBtn, SIGNAL(clicked()), this, SLOT(patchCollectionDown())); - + connect(patchCollections, SIGNAL(activated(const QModelIndex&)), this, SLOT(patchActivated(const QModelIndex&))); connect(patchCollections, SIGNAL(clicked (const QModelIndex&)), this, SLOT(patchActivated(const QModelIndex&))); - + patch_coll_model=new QStringListModel(); patchCollections->setModel(patch_coll_model); patchCollections->setEditTriggers(QAbstractItemView::NoEditTriggers); - + connect(instrumentList, SIGNAL(itemSelectionChanged()), SLOT(instrumentChanged())); connect(patchView, SIGNAL(itemSelectionChanged()), SLOT(patchChanged())); dlist_vscroll = new QScrollBar(Qt::Vertical, this); dlist_vscroll->setMaximum(128*TH); dlist_vscroll->hide(); - + dlist_grid = new QGridLayout(dlistContainer); dlist_grid->setContentsMargins(0, 0, 0, 0); - dlist_grid->setSpacing(0); + dlist_grid->setSpacing(0); dlist_grid->setRowStretch(1, 100); dlist_grid->setColumnStretch(0, 100); dlist_grid->addWidget(dlist_header, 0, 0); dlist_grid->addWidget(dlist_vscroll, 1,1); - + dlist=NULL; changeInstrument(); - + connect(viewController, SIGNAL(itemSelectionChanged()), SLOT(controllerChanged())); - - connect(instrumentName, SIGNAL(returnPressed()), SLOT(instrumentNameReturn())); - connect(instrumentName, SIGNAL(lostFocus()), SLOT(instrumentNameReturn())); - - connect(patchNameEdit, SIGNAL(returnPressed()), SLOT(patchNameReturn())); - connect(patchNameEdit, SIGNAL(lostFocus()), SLOT(patchNameReturn())); + + connect(noteOffModeList,SIGNAL(activated(int)), SLOT(noteOffModeChanged(int))); + + connect(instrumentName, SIGNAL(editingFinished()), SLOT(instrumentNameReturn())); + connect(patchNameEdit, SIGNAL(editingFinished()), SLOT(patchNameReturn())); + connect(patchDelete, SIGNAL(clicked()), SLOT(deletePatchClicked())); connect(patchNew, SIGNAL(clicked()), SLOT(newPatchClicked())); connect(patchNewGroup, SIGNAL(clicked()), SLOT(newGroupClicked())); - + connect(patchButton, SIGNAL(clicked()), SLOT(patchButtonClicked())); + connect(spinBoxDefault, SIGNAL(valueChanged(int)), SLOT(ctrlDefaultChanged(int))); connect(defPatchH, SIGNAL(valueChanged(int)), SLOT(defPatchChanged(int))); connect(defPatchL, SIGNAL(valueChanged(int)), SLOT(defPatchChanged(int))); connect(defPatchProg, SIGNAL(valueChanged(int)), SLOT(defPatchChanged(int))); + + connect(drumPatchButton, SIGNAL(clicked()), SLOT(drumPatchButtonClicked())); + connect(spinBoxDrumDefault, SIGNAL(valueChanged(int)), SLOT(ctrlDrumDefaultChanged(int))); + connect(defDrumPatchH, SIGNAL(valueChanged(int)), SLOT(defDrumPatchChanged(int))); + connect(defDrumPatchL, SIGNAL(valueChanged(int)), SLOT(defDrumPatchChanged(int))); + connect(defDrumPatchProg, SIGNAL(valueChanged(int)), SLOT(defDrumPatchChanged(int))); + connect(drummapsPatchNames, SIGNAL(clicked()), SLOT(drummapCollectionPatchButtonClicked())); + connect(deleteController, SIGNAL(clicked()), SLOT(deleteControllerClicked())); connect(newController, SIGNAL(clicked()), SLOT(newControllerClicked())); connect(addController, SIGNAL(clicked()), SLOT(addControllerClicked())); connect(ctrlType,SIGNAL(activated(int)), SLOT(ctrlTypeChanged(int))); - connect(ctrlName, SIGNAL(returnPressed()), SLOT(ctrlNameReturn())); - connect(ctrlName, SIGNAL(lostFocus()), SLOT(ctrlNameReturn())); + + connect(ctrlName, SIGNAL(editingFinished()), SLOT(ctrlNameReturn())); + connect(spinBoxHCtrlNo, SIGNAL(valueChanged(int)), SLOT(ctrlNumChanged())); connect(spinBoxLCtrlNo, SIGNAL(valueChanged(int)), SLOT(ctrlNumChanged())); connect(spinBoxMin, SIGNAL(valueChanged(int)), SLOT(ctrlMinChanged(int))); connect(spinBoxMax, SIGNAL(valueChanged(int)), SLOT(ctrlMaxChanged(int))); - connect(spinBoxDefault, SIGNAL(valueChanged(int)), SLOT(ctrlDefaultChanged(int))); - connect(nullParamSpinBoxH, SIGNAL(valueChanged(int)), SLOT(ctrlNullParamHChanged(int))); - connect(nullParamSpinBoxL, SIGNAL(valueChanged(int)), SLOT(ctrlNullParamLChanged(int))); connect(ctrlShowInMidi,SIGNAL(stateChanged(int)), SLOT(ctrlShowInMidiChanged(int))); connect(ctrlShowInDrum,SIGNAL(stateChanged(int)), SLOT(ctrlShowInDrumChanged(int))); - - connect(tabWidget3, SIGNAL(currentChanged(QWidget*)), SLOT(tabChanged(QWidget*))); + + connect(tabWidget3, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); connect(sysexList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(sysexChanged(QListWidgetItem*, QListWidgetItem*))); connect(deleteSysex, SIGNAL(clicked()), SLOT(deleteSysexClicked())); connect(newSysex, SIGNAL(clicked()), SLOT(newSysexClicked())); } +EditInstrument::~EditInstrument() +{ + delete workingInstrument; +} + +//--------------------------------------------------------- +// setHeaderWhatsThis +//--------------------------------------------------------- + +void EditInstrument::setHeaderWhatsThis() + { + dlist_header->setWhatsThis(COL_HIDE, tr("hide instrument")); + dlist_header->setWhatsThis(COL_MUTE, tr("mute instrument")); + dlist_header->setWhatsThis(COL_NAME, tr("sound name")); + dlist_header->setWhatsThis(COL_VOLUME, tr("volume percent")); + dlist_header->setWhatsThis(COL_QUANT, tr("quantisation")); + dlist_header->setWhatsThis(COL_INPUTTRIGGER, tr("this input note triggers the sound")); + dlist_header->setWhatsThis(COL_NOTELENGTH, tr("note length")); + dlist_header->setWhatsThis(COL_NOTE, tr("this is the note which is played")); + dlist_header->setWhatsThis(COL_OUTCHANNEL, tr("override track output channel (hold ctl to affect all rows)")); + dlist_header->setWhatsThis(COL_OUTPORT, tr("override track output port (hold ctl to affect all rows)")); + dlist_header->setWhatsThis(COL_LEVEL1, tr("control + meta keys: draw velocity level 1")); + dlist_header->setWhatsThis(COL_LEVEL2, tr("meta key: draw velocity level 2")); + dlist_header->setWhatsThis(COL_LEVEL3, tr("draw default velocity level 3")); + dlist_header->setWhatsThis(COL_LEVEL4, tr("meta + alt keys: draw velocity level 4")); + } + +//--------------------------------------------------------- +// setHeaderToolTips +//--------------------------------------------------------- + +void EditInstrument::setHeaderToolTips() + { + dlist_header->setToolTip(COL_HIDE, tr("hide instrument")); + dlist_header->setToolTip(COL_MUTE, tr("mute instrument")); + dlist_header->setToolTip(COL_NAME, tr("sound name")); + dlist_header->setToolTip(COL_VOLUME, tr("volume percent")); + dlist_header->setToolTip(COL_QUANT, tr("quantisation")); + dlist_header->setToolTip(COL_INPUTTRIGGER, tr("this input note triggers the sound")); + dlist_header->setToolTip(COL_NOTELENGTH, tr("note length")); + dlist_header->setToolTip(COL_NOTE, tr("this is the note which is played")); + dlist_header->setToolTip(COL_OUTCHANNEL, tr("override track output channel (ctl: affect all rows)")); + dlist_header->setToolTip(COL_OUTPORT, tr("override track output port (ctl: affect all rows)")); + dlist_header->setToolTip(COL_LEVEL1, tr("control + meta keys: draw velocity level 1")); + dlist_header->setToolTip(COL_LEVEL2, tr("meta key: draw velocity level 2")); + dlist_header->setToolTip(COL_LEVEL3, tr("draw default velocity level 3")); + dlist_header->setToolTip(COL_LEVEL4, tr("meta + alt keys: draw velocity level 4")); + } + void EditInstrument::findInstrument(const QString& find_instrument) { if(find_instrument.isEmpty()) @@ -232,78 +424,49 @@ instrumentList->setCurrentItem(found.at(0)); } -void EditInstrument::showTab(TabType n) +void EditInstrument::showTab(EditInstrumentTabType n) { if(n >= tabWidget3->count()) return; tabWidget3->setCurrentIndex(n); } - -void EditInstrument::patchCollectionSpinboxChanged(int) -{ - if (patchFromBox->value() > patchToBox->value()) - patchToBox->setValue(patchFromBox->value()); - - if (lbankFromBox->value() > lbankToBox->value()) - lbankToBox->setValue(lbankFromBox->value()); - - if (hbankFromBox->value() > hbankToBox->value()) - hbankToBox->setValue(hbankFromBox->value()); - - storePatchCollection(); -} -void EditInstrument::patchCollectionCheckboxChanged(bool) +void EditInstrument::patchCollectionSpinboxChanged(int) { storePatchCollection(); + drummapsPatchNames->setText(workingInstrument->getPatchName(0, getDrummapCollectionPatchNumber(), true, false)); } void EditInstrument::storePatchCollection() { using MusECore::patch_drummap_mapping_t; - + using MusECore::patch_drummap_mapping_list_t; + using MusECore::iPatchDrummapMapping_t; + int idx=patchCollections->currentIndex().row(); - std::list* pdm = workingInstrument.get_patch_drummap_mapping(); - if (idx>=0 && (unsigned)idxsize()) + patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. + if(pdm && idx>=0 && (unsigned)idxsize()) { - std::list::iterator it=pdm->begin(); + iPatchDrummapMapping_t it=pdm->begin(); advance(it,idx); - - if (patchCheckbox->isChecked()) - { - it->affected_patches.first_program=patchFromBox->value()-1; - it->affected_patches.last_program=patchToBox->value()-1; - } + + if (patchFromBox->value() == 0) + it->setProg(0xff); else - { - it->affected_patches.first_program=0; - it->affected_patches.last_program=127; - } + it->setProg(patchFromBox->value() - 1); - if (lbankCheckbox->isChecked()) - { - it->affected_patches.first_lbank=lbankFromBox->value()-1; - it->affected_patches.last_lbank=lbankToBox->value()-1; - } + if (lbankFromBox->value() == 0) + it->setLBank(0xff); else - { - it->affected_patches.first_lbank=0; - it->affected_patches.last_lbank=127; - } + it->setLBank(lbankFromBox->value() - 1); - if (hbankCheckbox->isChecked()) - { - it->affected_patches.first_hbank=hbankFromBox->value()-1; - it->affected_patches.last_hbank=hbankToBox->value()-1; - } + if (hbankFromBox->value() == 0) + it->setHBank(0xff); else - { - it->affected_patches.first_hbank=0; - it->affected_patches.last_hbank=127; - } - - workingInstrument.setDirty(true); + it->setHBank(hbankFromBox->value() - 1); + + workingInstrument->setDirty(true); repopulatePatchCollections(); } } @@ -311,72 +474,75 @@ void EditInstrument::fetchPatchCollection() { using MusECore::patch_drummap_mapping_t; - + using MusECore::patch_drummap_mapping_list_t; + using MusECore::iPatchDrummapMapping_t; + int idx=patchCollections->currentIndex().row(); - std::list* pdm = workingInstrument.get_patch_drummap_mapping(); - if (idx>=0 && (unsigned)idxsize()) + patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. + if(pdm && idx>=0 && (unsigned)idxsize()) { - std::list::iterator it=pdm->begin(); + iPatchDrummapMapping_t it=pdm->begin(); advance(it,idx); - + patchFromBox->blockSignals(true); - patchToBox->blockSignals(true); lbankFromBox->blockSignals(true); - lbankToBox->blockSignals(true); hbankFromBox->blockSignals(true); - hbankToBox->blockSignals(true); - - patchFromBox->setValue(it->affected_patches.first_program+1); - patchToBox->setValue(it->affected_patches.last_program+1); - - lbankFromBox->setValue(it->affected_patches.first_lbank+1); - lbankToBox->setValue(it->affected_patches.last_lbank+1); - - hbankFromBox->setValue(it->affected_patches.first_hbank+1); - hbankToBox->setValue(it->affected_patches.last_hbank+1); + + if(it->programDontCare()) + patchFromBox->setValue(0); + else + patchFromBox->setValue(it->prog() + 1); + + if(it->lbankDontCare()) + lbankFromBox->setValue(0); + else + lbankFromBox->setValue(it->lbank() + 1); + + if(it->hbankDontCare()) + hbankFromBox->setValue(0); + else + hbankFromBox->setValue(it->hbank() + 1); patchFromBox->blockSignals(false); - patchToBox->blockSignals(false); lbankFromBox->blockSignals(false); - lbankToBox->blockSignals(false); hbankFromBox->blockSignals(false); - hbankToBox->blockSignals(false); - - patchCheckbox->setChecked(it->affected_patches.first_program>0 || it->affected_patches.last_program < 127); - lbankCheckbox->setChecked(it->affected_patches.first_lbank>0 || it->affected_patches.last_lbank < 127); - hbankCheckbox->setChecked(it->affected_patches.first_hbank>0 || it->affected_patches.last_hbank < 127); + drummapsPatchNames->setText(workingInstrument->getPatchName(0, it->_patch, true, false)); } } void EditInstrument::patchActivated(const QModelIndex& idx) { using MusECore::patch_drummap_mapping_t; - + using MusECore::patch_drummap_mapping_list_t; + using MusECore::iPatchDrummapMapping_t; + if (idx.row()>=0) { using MusECore::DrumMap; - - std::list* tmp = workingInstrument.get_patch_drummap_mapping(); - std::list::iterator it=tmp->begin(); + + patch_drummap_mapping_list_t* tmp = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. + if(!tmp) + return; + iPatchDrummapMapping_t it=tmp->begin(); if ((unsigned)idx.row()>=tmp->size()) printf("THIS SHOULD NEVER HAPPEN: idx.row()>=tmp->size() in EditInstrument::patchActivated()\n"); - + advance(it, idx.row()); DrumMap* dm=it->drummap; - - + + if (dlist) { dlist->hide(); delete dlist; dlist=NULL; } - + dlist=new DList(dlist_header,dlistContainer,1,dm); - + dlist->setYPos(dlist_vscroll->value()); - + connect(dlist_vscroll, SIGNAL(valueChanged(int)), dlist, SLOT(setYPos(int))); dlist_grid->addWidget(dlist, 1, 0); @@ -384,7 +550,7 @@ dlist_header->show(); dlist->show(); dlist_vscroll->show(); - + collUpBtn->setEnabled(idx.row()>0); collDownBtn->setEnabled(idx.row()rowCount()-1); rmCollBtn->setEnabled(true); @@ -398,25 +564,31 @@ void EditInstrument::addPatchCollection() { using MusECore::patch_drummap_mapping_t; - + using MusECore::patch_drummap_mapping_list_t; + using MusECore::iPatchDrummapMapping_t; + int idx=patchCollections->currentIndex().row(); - - std::list* tmp = workingInstrument.get_patch_drummap_mapping(); - std::list::iterator it=tmp->begin(); + + patch_drummap_mapping_list_t* tmp = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. + if(!tmp) + return; + iPatchDrummapMapping_t it=tmp->begin(); advance(it,idx+1); tmp->insert(it,patch_drummap_mapping_t()); repopulatePatchCollections(); patchCollections->setCurrentIndex(patch_coll_model->index(idx+1)); patchActivated(patchCollections->currentIndex()); - - workingInstrument.setDirty(true); + + workingInstrument->setDirty(true); } void EditInstrument::delPatchCollection() { using MusECore::patch_drummap_mapping_t; - + using MusECore::patch_drummap_mapping_list_t; + using MusECore::iPatchDrummapMapping_t; + int idx=patchCollections->currentIndex().row(); if (idx>=0) { @@ -426,7 +598,7 @@ delete dlist; dlist=NULL; } - + dlist_header->hide(); dlist_vscroll->hide(); @@ -436,105 +608,129 @@ collUpBtn->setEnabled(false); collDownBtn->setEnabled(false); - std::list* tmp = workingInstrument.get_patch_drummap_mapping(); - std::list::iterator it=tmp->begin(); - advance(it,idx); - tmp->erase(it); + patch_drummap_mapping_list_t* tmp = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. + if(tmp) + { + iPatchDrummapMapping_t it=tmp->begin(); + advance(it,idx); + tmp->erase(it); + } repopulatePatchCollections(); - + patchActivated(patchCollections->currentIndex()); - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } } void EditInstrument::copyPatchCollection() { using MusECore::patch_drummap_mapping_t; - + using MusECore::patch_drummap_mapping_list_t; + using MusECore::iPatchDrummapMapping_t; + int idx=patchCollections->currentIndex().row(); - - std::list* tmp = workingInstrument.get_patch_drummap_mapping(); - std::list::iterator it=tmp->begin(); + + patch_drummap_mapping_list_t* tmp = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. + if(!tmp) + return; + iPatchDrummapMapping_t it=tmp->begin(); advance(it,idx); patch_drummap_mapping_t tmp2(*it); it++; tmp->insert(it,tmp2); - + patch_coll_model->insertRow(idx+1); patch_coll_model->setData(patch_coll_model->index(idx+1), patch_coll_model->index(idx).data()); patchCollections->setCurrentIndex(patch_coll_model->index(idx+1)); patchActivated(patchCollections->currentIndex()); - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } void EditInstrument::patchCollectionUp() { using MusECore::patch_drummap_mapping_t; - - std::list* pdm = workingInstrument.get_patch_drummap_mapping(); + using MusECore::patch_drummap_mapping_list_t; + using MusECore::iPatchDrummapMapping_t; + + patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. + if(!pdm) + return; + int idx=patchCollections->currentIndex().row(); - if (idx>=1) { - std::list::iterator it=pdm->begin(); + iPatchDrummapMapping_t it=pdm->begin(); advance(it,idx-1); - std::list::iterator it2=it; + iPatchDrummapMapping_t it2=it; it2++; - + //it2 is the element to move, it is the element to put before. - + pdm->insert(it,*it2); pdm->erase(it2); - + repopulatePatchCollections(); patchCollections->setCurrentIndex(patch_coll_model->index(idx-1)); patchActivated(patchCollections->currentIndex()); - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } } void EditInstrument::patchCollectionDown() { using MusECore::patch_drummap_mapping_t; - - std::list* pdm = workingInstrument.get_patch_drummap_mapping(); + using MusECore::patch_drummap_mapping_list_t; + using MusECore::iPatchDrummapMapping_t; + + patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. + if(!pdm) + return; + int idx=patchCollections->currentIndex().row(); - if ((unsigned)idxsize()-1) { - std::list::iterator it=pdm->begin(); + iPatchDrummapMapping_t it=pdm->begin(); advance(it,idx); - std::list::iterator it2=it; + iPatchDrummapMapping_t it2=it; it2++; it2++; - + //it is the element to move, it2 is the element to put before (might be end()) - + pdm->insert(it2,*it); pdm->erase(it); - + repopulatePatchCollections(); patchCollections->setCurrentIndex(patch_coll_model->index(idx+1)); patchActivated(patchCollections->currentIndex()); - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } } void EditInstrument::repopulatePatchCollections() { using MusECore::patch_drummap_mapping_t; - + using MusECore::patch_drummap_mapping_list_t; + using MusECore::iPatchDrummapMapping_t; + int idx=patchCollections->currentIndex().row(); QStringList strlist; - - std::list* pdm = workingInstrument.get_patch_drummap_mapping(); - for (std::list::iterator it=pdm->begin(); it!=pdm->end(); it++) - strlist << it->affected_patches.to_string(); - + + patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. + if(pdm) + { + for (iPatchDrummapMapping_t it=pdm->begin(); it!=pdm->end(); it++) + { + patch_drummap_mapping_t& pd = *it; + // Patch name: Get drum, and no default - we want an exact match. + strlist << (it->to_string() + QString(" (") + workingInstrument->getPatchName(0, pd._patch, true, false) + QString(")")); + } + } + patch_coll_model->setStringList(strlist); patchCollections->setCurrentIndex(patch_coll_model->index(idx)); } @@ -558,7 +754,9 @@ instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); - + + MusECore::PendingOperationList operations; + for (int i = 1;; ++i) { QString s = QString("Instrument-%1").arg(i); bool found = false; @@ -572,43 +770,45 @@ MusECore::MidiInstrument* oi = 0; if(oldMidiInstrument) oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); - MusECore::MidiInstrument* wip = &workingInstrument; + MusECore::MidiInstrument* wip = workingInstrument; if(checkDirty(wip)) // No save was chosen. Restore the actual instrument name. { if(oi) { oldMidiInstrument->setText(oi->iname()); - + // No file path? Only a new unsaved instrument can do that. So delete it. if(oi->filePath().isEmpty()) // Delete the list item and the instrument. deleteInstrument(oldMidiInstrument); - - } + + } } - workingInstrument.setDirty(false); - + workingInstrument->setDirty(false); + MusECore::MidiInstrument* ni = new MusECore::MidiInstrument(s); - MusECore::midiInstruments.push_back(ni); + + operations.add(MusECore::PendingOperationItem(&MusECore::midiInstruments, ni, MusECore::PendingOperationItem::AddMidiInstrument)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + QListWidgetItem* item = new QListWidgetItem(ni->iname()); - - workingInstrument.assign( *ni ); - QVariant v = qVariantFromValue((void*)(ni)); - item->setData(Qt::UserRole, v); + workingInstrument->assign( *ni ); + + item->setData(Qt::UserRole, QVariant::fromValue((void*)(ni))); instrumentList->addItem(item); - + oldMidiInstrument = 0; - + instrumentList->blockSignals(true); instrumentList->setCurrentItem(item); instrumentList->blockSignals(false); - + changeInstrument(); - + // We have our new instrument! So set dirty to true. - workingInstrument.setDirty(true); - + workingInstrument->setDirty(true); + break; } } @@ -629,42 +829,42 @@ void EditInstrument::fileSave() { - if (workingInstrument.filePath().isEmpty()) + if (workingInstrument->filePath().isEmpty()) { saveAs(); return; - } - + } + // Do not allow a direct overwrite of a 'built-in' muse instrument. - QFileInfo qfi(workingInstrument.filePath()); + QFileInfo qfi(workingInstrument->filePath()); if(qfi.absolutePath() == MusEGlobal::museInstruments) { saveAs(); return; } - - FILE* f = fopen(workingInstrument.filePath().toLatin1().constData(), "w"); + + FILE* f = fopen(workingInstrument->filePath().toLatin1().constData(), "w"); if(f == 0) { saveAs(); return; - } - + } + // Allow these to update... instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); - + if(fclose(f) != 0) { - QString s = QString("Creating file:\n") + workingInstrument.filePath() + QString("\nfailed: ") + QString s = QString("Creating file:\n") + workingInstrument->filePath() + QString("\nfailed: ") + QString(strerror(errno) ); QMessageBox::critical(this, tr("MusE: Create file failed"), s); return; } - - if(fileSave(&workingInstrument, workingInstrument.filePath())) - workingInstrument.setDirty(false); + + if(fileSave(workingInstrument, workingInstrument->filePath())) + workingInstrument->setDirty(false); } //--------------------------------------------------------- @@ -673,7 +873,7 @@ bool EditInstrument::fileSave(MusECore::MidiInstrument* instrument, const QString& name) { - FILE* f = fopen(name.toAscii().constData(), "w"); + FILE* f = fopen(name.toLatin1().constData(), "w"); if(f == 0) { QString s("Creating file failed: "); @@ -682,26 +882,57 @@ tr("MusE: Create file failed"), s); return false; } - + MusECore::Xml xml(f); - + updateInstrument(instrument); - + instrument->write(0, xml); - + // Assign the working instrument values to the actual current selected instrument... if(oldMidiInstrument) { MusECore::MidiInstrument* oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); if(oi) { - oi->assign(workingInstrument); - + //oi->assign(*workingInstrument); // Now signal the rest of the app so stuff can change... - MusEGlobal::song->update(SC_CONFIG | SC_MIDI_CONTROLLER); - } + //MusEGlobal::song->update(SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD); + + MusECore::iMidiInstrument imi = MusECore::midiInstruments.find(oi); + if(imi != MusECore::midiInstruments.end()) + { + // Create a new instrument to be switched to. + MusECore::MidiInstrument* ni = new MusECore::MidiInstrument(); + // Assign existing values to the new instrument. + ni->assign(*workingInstrument); + + MusECore::PendingOperationList operations; + // Operation to erase and delete the existing instrument, and add the new instrument. + operations.add(MusECore::PendingOperationItem(&MusECore::midiInstruments, imi, ni, MusECore::PendingOperationItem::ReplaceMidiInstrument)); + // Execute the operations. + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + + workingInstrument->assign( *ni ); // ?? Remove, not required? + + // Set the tree item's data pointer to the new instrument. + oldMidiInstrument->setData(Qt::UserRole, QVariant::fromValue((void*)(ni))); + + changeInstrument(); // ?? Remove, not required? + + // We have our new instrument! So set dirty to true. + //workingInstrument->setDirty(true); + } + else + { + // Error: The instrument was not found. Might as well assign to whatever the thing is. + oi->assign(*workingInstrument); + // Now signal the rest of the app so stuff can change... + MusEGlobal::song->update(SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD); + } + } } - + if(fclose(f) != 0) { QString s = QString("Write File\n") + name + QString("\nfailed: ") @@ -722,50 +953,50 @@ instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); - + QString path = MusEGlobal::museUserInstruments; if(!QDir(MusEGlobal::museUserInstruments).exists()) { printf("MusE Error! User instrument directory: %s does not exist. Should be created at startup!\n", MusEGlobal::museUserInstruments.toLatin1().constData()); - + //path = MusEGlobal::museUser; DELETETHIS? - //path = MusEGlobal::configPath; + //path = MusEGlobal::configPath; } - - if (workingInstrument.filePath().isEmpty()) - path += QString("/%1.idf").arg(workingInstrument.iname()); + + if (workingInstrument->filePath().isEmpty()) + path += QString("/%1.idf").arg(workingInstrument->iname()); else { - QFileInfo fi(workingInstrument.filePath()); - + QFileInfo fi(workingInstrument->filePath()); + // Prompt for a new instrument name if the name has not been changed, to avoid duplicates. if(oldMidiInstrument) { MusECore::MidiInstrument* oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); if(oi) { - if(oi->iname() == workingInstrument.iname()) + if(oi->iname() == workingInstrument->iname()) { // Prompt only if it's a user instrument, to avoid duplicates in the user instrument dir. // This will still allow a user instrument to override a built-in instrument with the same name. if(fi.absolutePath() != MusEGlobal::museInstruments) { - printf("EditInstrument::saveAs Error: Instrument name %s already used!\n", workingInstrument.iname().toLatin1().constData()); - return; - } + printf("EditInstrument::saveAs Error: Instrument name %s already used!\n", workingInstrument->iname().toLatin1().constData()); + return; + } } } - } + } path += QString("/%1.idf").arg(fi.baseName()); } - QString s = QFileDialog::getSaveFileName(this, tr("MusE: Save Instrument Definition").toLatin1().constData(), + QString s = QFileDialog::getSaveFileName(this, tr("MusE: Save Instrument Definition"), path, tr("Instrument Definition (*.idf)")); if (s.isEmpty()) return; - workingInstrument.setFilePath(s); - - if(fileSave(&workingInstrument, s)) - workingInstrument.setDirty(false); + workingInstrument->setFilePath(s); + + if(fileSave(workingInstrument, s)) + workingInstrument->setDirty(false); } //--------------------------------------------------------- @@ -775,31 +1006,31 @@ void EditInstrument::fileSaveAs() { // Is this a new unsaved instrument? Just do a normal save. - if(workingInstrument.filePath().isEmpty()) + if(workingInstrument->filePath().isEmpty()) { saveAs(); return; - } - + } + // Allow these to update... instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); - + MusECore::MidiInstrument* oi = 0; if(oldMidiInstrument) oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); - - int res = checkDirty(&workingInstrument, true); + + int res = checkDirty(workingInstrument, true); switch(res) { // No save: case 1: - workingInstrument.setDirty(false); + workingInstrument->setDirty(false); if(oi) { oldMidiInstrument->setText(oi->iname()); - + // No file path? Only a new unsaved instrument can do that. So delete it. if(oi->filePath().isEmpty()) { @@ -807,39 +1038,39 @@ deleteInstrument(oldMidiInstrument); oldMidiInstrument = 0; } - + changeInstrument(); - + } return; break; - + // Abort: - case 2: + case 2: return; break; - + // Save: case 0: - workingInstrument.setDirty(false); + workingInstrument->setDirty(false); break; } - + bool isuser = false; QString so; - if(workingInstrument.iname().isEmpty()) + if(workingInstrument->iname().isEmpty()) so = QString("Instrument"); - else - so = workingInstrument.iname(); - - for(int i = 1;; ++i) + else + so = workingInstrument->iname(); + + for(int i = 1;; ++i) { QString s = so + QString("-%1").arg(i); - + bool found = false; - for(MusECore::iMidiInstrument imi = MusECore::midiInstruments.begin(); imi != MusECore::midiInstruments.end(); ++imi) + for(MusECore::iMidiInstrument imi = MusECore::midiInstruments.begin(); imi != MusECore::midiInstruments.end(); ++imi) { - if(s == (*imi)->iname()) + if(s == (*imi)->iname()) { // Allow override of built-in instrument names. QFileInfo fi((*imi)->filePath()); @@ -849,30 +1080,30 @@ break; } } - if(found) - continue; - + if(found) + continue; + bool ok; - s = QInputDialog::getText(this, tr("MusE: Save instrument as"), tr("Enter a new unique instrument name:"), + s = QInputDialog::getText(this, tr("MusE: Save instrument as"), tr("Enter a new unique instrument name:"), QLineEdit::Normal, s, &ok); - if(!ok) + if(!ok) return; if(s.isEmpty()) { --i; - continue; - } - + continue; + } + isuser = false; bool builtin = false; found = false; - for(MusECore::iMidiInstrument imi = MusECore::midiInstruments.begin(); imi != MusECore::midiInstruments.end(); ++imi) + for(MusECore::iMidiInstrument imi = MusECore::midiInstruments.begin(); imi != MusECore::midiInstruments.end(); ++imi) { // If an instrument with the same name is found... - if((*imi)->iname() == s) + if((*imi)->iname() == s) { // If it's not the same name as the working instrument, and it's not an internal instrument (soft synth etc.)... - if(s != workingInstrument.iname() && !(*imi)->filePath().isEmpty()) + if(s != workingInstrument->iname() && !(*imi)->filePath().isEmpty()) { QFileInfo fi((*imi)->filePath()); // Allow override of built-in and user instruments: @@ -889,10 +1120,10 @@ Qt::NoButton) == QMessageBox::Ok) { // Set the working instrument's file path to the found instrument's path. - workingInstrument.setFilePath((*imi)->filePath()); + workingInstrument->setFilePath((*imi)->filePath()); // Mark as overwriting a user instrument. isuser = true; - } + } else { found = true; @@ -901,85 +1132,88 @@ } // Assign the found instrument name to the working instrument name. - workingInstrument.setIName(s); - + workingInstrument->setIName(s); + // Find the instrument in the list and set the old instrument to the item. oldMidiInstrument = instrumentList->findItems(s, Qt::MatchExactly)[0]; - + // Mark as a built-in instrument. builtin = true; break; - } + } found = true; break; } } if(found) - { + { so = s; i = 0; - continue; - } - + continue; + } + so = s; - + // If the name does not belong to a built-in instrument... if(!builtin) { MusECore::MidiInstrument* ni = new MusECore::MidiInstrument(); - ni->assign(workingInstrument); + ni->assign(*workingInstrument); ni->setIName(so); ni->setFilePath(QString()); - MusECore::midiInstruments.push_back(ni); + + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(&MusECore::midiInstruments, ni, MusECore::PendingOperationItem::AddMidiInstrument)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + QListWidgetItem* item = new QListWidgetItem(so); - - workingInstrument.assign( *ni ); - QVariant v = qVariantFromValue((void*)(ni)); - item->setData(Qt::UserRole, v); + workingInstrument->assign( *ni ); + + item->setData(Qt::UserRole, QVariant::fromValue((void*)(ni))); instrumentList->addItem(item); - + oldMidiInstrument = 0; - + instrumentList->blockSignals(true); instrumentList->setCurrentItem(item); instrumentList->blockSignals(false); - + changeInstrument(); - + // We have our new instrument! So set dirty to true. - workingInstrument.setDirty(true); - } - + workingInstrument->setDirty(true); + } + break; } - + QString path = MusEGlobal::museUserInstruments; - + if(!QDir(MusEGlobal::museUserInstruments).exists()) { printf("MusE Error! User instrument directory: %s does not exist. Should be created at startup!\n", MusEGlobal::museUserInstruments.toLatin1().constData()); - + //path = MusEGlobal::museUser; DELETETHIS - //path = MusEGlobal::configPath; + //path = MusEGlobal::configPath; } path += QString("/%1.idf").arg(so); - + QString sfn; // If we are overwriting a user instrument just force the path. if(isuser) sfn = path; - else + else { - sfn = QFileDialog::getSaveFileName(this, tr("MusE: Save Instrument Definition").toLatin1().constData(), + sfn = QFileDialog::getSaveFileName(this, tr("MusE: Save Instrument Definition"), path, tr("Instrument Definition (*.idf)")); if (sfn.isEmpty()) return; - workingInstrument.setFilePath(sfn); - } - - if(fileSave(&workingInstrument, sfn)) - workingInstrument.setDirty(false); + workingInstrument->setFilePath(sfn); + } + + if(fileSave(workingInstrument, sfn)) + workingInstrument->setDirty(false); } //--------------------------------------------------------- @@ -1001,21 +1235,21 @@ instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); - + MusECore::MidiInstrument* oi = 0; if(oldMidiInstrument) oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); - - int res = checkDirty(&workingInstrument, true); + + int res = checkDirty(workingInstrument, true); switch(res) { // No save: case 1: - workingInstrument.setDirty(false); + workingInstrument->setDirty(false); if(oi) { oldMidiInstrument->setText(oi->iname()); - + // No file path? Only a new unsaved instrument can do that. So delete it. if(oi->filePath().isEmpty()) { @@ -1023,25 +1257,25 @@ deleteInstrument(oldMidiInstrument); oldMidiInstrument = 0; } - + changeInstrument(); - - } + + } break; - + // Abort: - case 2: + case 2: ev->ignore(); return; break; - + // Save: case 0: - workingInstrument.setDirty(false); + workingInstrument->setDirty(false); break; - + } - + QMainWindow::closeEvent(ev); } @@ -1058,12 +1292,16 @@ oldMidiInstrument = sel; // Assignment - + // Assign will 'delete' any existing patches, groups, or controllers. - workingInstrument.assign( *((MusECore::MidiInstrument*)sel->data(Qt::UserRole).value()) ); - - workingInstrument.setDirty(false); - + workingInstrument->assign( *((MusECore::MidiInstrument*)sel->data(Qt::UserRole).value()) ); + + workingInstrument->setDirty(false); + + noteOffModeList->blockSignals(true); + noteOffModeList->setCurrentIndex(workingInstrument->noteOffMode()); + noteOffModeList->blockSignals(false); + // populate patch list patchView->blockSignals(true); for (int i = 0; i < patchView->topLevelItemCount(); ++i) @@ -1076,101 +1314,79 @@ viewController->clear(); instrumentName->blockSignals(true); - instrumentName->setText(workingInstrument.iname()); + instrumentName->setText(workingInstrument->iname()); instrumentName->blockSignals(false); - - nullParamSpinBoxH->blockSignals(true); - nullParamSpinBoxL->blockSignals(true); - int nv = workingInstrument.nullSendValue(); - if(nv == -1) - { - nullParamSpinBoxH->setValue(-1); - nullParamSpinBoxL->setValue(-1); - } - else - { - int nvh = (nv >> 8) & 0xff; - int nvl = nv & 0xff; - if(nvh == 0xff) - nullParamSpinBoxH->setValue(-1); - else - nullParamSpinBoxH->setValue(nvh & 0x7f); - if(nvl == 0xff) - nullParamSpinBoxL->setValue(-1); - else - nullParamSpinBoxL->setValue(nvl & 0x7f); - } - nullParamSpinBoxH->blockSignals(false); - nullParamSpinBoxL->blockSignals(false); - + sysexList->blockSignals(true); sysexList->clear(); - foreach(const MusECore::SysEx* s, workingInstrument.sysex()) { + foreach(const MusECore::SysEx* s, workingInstrument->sysex()) { + if(!s) + continue; QListWidgetItem* item = new QListWidgetItem(s->name); QVariant v = QVariant::fromValue((void*)s); item->setData(Qt::UserRole, v); sysexList->addItem(item); } if(sysexList->item(0)) - sysexList->item(0)->setSelected(true); + sysexList->setCurrentItem(sysexList->item(0)); sysexList->blockSignals(false); sysexChanged(sysexList->item(0), 0); - - MusECore::PatchGroupList* pg = workingInstrument.groups(); + + populateInitEventList(); + + MusECore::PatchGroupList* pg = workingInstrument->groups(); for (MusECore::ciPatchGroup g = pg->begin(); g != pg->end(); ++g) { - MusECore::PatchGroup* pgp = *g; + MusECore::PatchGroup* pgp = *g; if(pgp) { QTreeWidgetItem* item = new QTreeWidgetItem(patchView); - + item->setText(0, pgp->name); - QVariant v = qVariantFromValue((void*)(pgp)); - item->setData(0, Qt::UserRole, v); - - for (MusECore::ciPatch p = pgp->patches.begin(); p != pgp->patches.end(); ++p) + item->setData(0, Qt::UserRole, QVariant::fromValue((void*)(pgp))); + + for (MusECore::ciPatch p = pgp->patches.begin(); p != pgp->patches.end(); ++p) { MusECore::Patch* patch = *p; if(patch) { QTreeWidgetItem* sitem = new QTreeWidgetItem(item); sitem->setText(0, patch->name); - QVariant v = QVariant::fromValue((void*)patch); - sitem->setData(0, Qt::UserRole, v); - } - } + sitem->setData(0, Qt::UserRole, QVariant::fromValue((void*)patch)); + } + } } - } - + } + oldPatchItem = 0; - + QTreeWidgetItem* fc = patchView->topLevelItem(0); if(fc) { // This may cause a patchChanged call. patchView->blockSignals(true); - fc->setSelected(true); + patchView->setCurrentItem(fc); patchView->blockSignals(false); } - + patchChanged(); - - MusECore::MidiControllerList* cl = workingInstrument.controller(); + + MusECore::MidiControllerList* cl = workingInstrument->controller(); for (MusECore::ciMidiController ic = cl->begin(); ic != cl->end(); ++ic) { MusECore::MidiController* c = ic->second; addControllerToView(c); } - + QTreeWidgetItem *ci = viewController->topLevelItem(0); if(ci) { viewController->blockSignals(true); - ci->setSelected(true); + viewController->setCurrentItem(ci); viewController->blockSignals(false); - } - + } + controllerChanged(); - + repopulatePatchCollections(); if (dlist) { @@ -1178,7 +1394,7 @@ delete dlist; dlist=NULL; } - + dlist_header->hide(); dlist_vscroll->hide(); @@ -1199,13 +1415,14 @@ { QListWidgetItem* sel = instrumentList->currentItem(); - if(!sel) + if(!sel) { return; - + } + MusECore::MidiInstrument* oi = 0; if(oldMidiInstrument) oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); - MusECore::MidiInstrument* wip = &workingInstrument; + MusECore::MidiInstrument* wip = workingInstrument; // Returns true if aborted. if(checkDirty(wip)) { @@ -1213,7 +1430,7 @@ if(oi) { oldMidiInstrument->setText(oi->iname()); - + // No file path? Only a new unsaved instrument can do that. So delete it. if(oi->filePath().isEmpty()) { @@ -1221,10 +1438,10 @@ deleteInstrument(oldMidiInstrument); oldMidiInstrument = 0; } - - } + + } } - workingInstrument.setDirty(false); + workingInstrument->setDirty(false); changeInstrument(); } @@ -1235,40 +1452,40 @@ void EditInstrument::instrumentNameReturn() { - QListWidgetItem* item = instrumentList->currentItem(); + MusECore::MidiInstrument* oi = 0; + if(oldMidiInstrument) + oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); - if (item == 0) - return; + if(!oi) + return; QString s = instrumentName->text(); - - if(s == item->text()) + + if(s == oldMidiInstrument->text()) return; - - MusECore::MidiInstrument* curins = (MusECore::MidiInstrument*)item->data(Qt::UserRole).value(); - - for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) + + for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { - if((*i) != curins && s == (*i)->iname()) + if((*i) != oi && s == (*i)->iname()) { instrumentName->blockSignals(true); // Grab the last valid name from the item text, since the instrument has not been updated yet. - instrumentName->setText(item->text()); + instrumentName->setText(oldMidiInstrument->text()); instrumentName->blockSignals(false); - + QMessageBox::critical(this, tr("MusE: Bad instrument name"), tr("Please choose a unique instrument name.\n(The name might be used by a hidden instrument.)"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); - + return; } - } - - item->setText(s); - workingInstrument.setIName(s); - workingInstrument.setDirty(true); + } + + oldMidiInstrument->setText(s); + workingInstrument->setIName(s); + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -1281,69 +1498,89 @@ return; MusECore::MidiInstrument* ins = (MusECore::MidiInstrument*)item->data(Qt::UserRole).value(); - + // Delete the list item. // Test this: Is this going to change the current selection? instrumentList->blockSignals(true); delete item; instrumentList->blockSignals(false); - - // Test this: Neccessary? DELETETHIS + + // Test this: Necessary? DELETETHIS // if(curritem) // instrumentList->setCurrentItem(curritem); - + if(!ins) return; - + // Remove the instrument from the list. - MusECore::midiInstruments.remove(ins); - + MusECore::iMidiInstrument imi = MusECore::midiInstruments.find(ins); + if(imi != MusECore::midiInstruments.end()) + { + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(&MusECore::midiInstruments, imi, MusECore::PendingOperationItem::DeleteMidiInstrument)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } + // Delete the instrument. delete ins; } //--------------------------------------------------------- +// noteOffModeChanged +//--------------------------------------------------------- + +void EditInstrument::noteOffModeChanged(int idx) +{ + MusECore::MidiInstrument::NoteOffMode t = (MusECore::MidiInstrument::NoteOffMode)noteOffModeList->itemData(idx).toInt(); + if(workingInstrument->noteOffMode() == t) + return; + workingInstrument->setNoteOffMode(t); + workingInstrument->setDirty(true); +} + +//--------------------------------------------------------- // tabChanged -// Added so that patch list is updated when switching tabs, -// so that 'Program' default values and text are current in controller tab. +// Added so that patch list is updated when switching tabs, +// so that 'Program' default values and text are current in controller tab. //--------------------------------------------------------- -void EditInstrument::tabChanged(QWidget* w) +void EditInstrument::tabChanged(int idx) { + QWidget* w = tabWidget3->widget(idx); if(!w) return; - + // If we're switching to the Patches tab, just ignore. if(QString(w->objectName()) == QString("patchesTab")) return; - + if(oldPatchItem) { // Don't bother calling patchChanged, just update the patch or group. if(oldPatchItem->QTreeWidgetItem::parent()) - updatePatch(&workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); + updatePatch(workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); else - updatePatchGroup(&workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); + updatePatchGroup(workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); } - + // We're still on the same item. No need to set oldPatchItem as in patchChanged... - + // If we're switching to the Controller tab, update the default patch button text in case a patch changed... if(QString(w->objectName()) == QString("controllerTab")) { QTreeWidgetItem* sel = viewController->currentItem(); - - if(!sel || !sel->data(0, Qt::UserRole).value()) + + if(!sel || !sel->data(0, Qt::UserRole).value()) return; - + MusECore::MidiController* c = (MusECore::MidiController*)sel->data(0, Qt::UserRole).value(); MusECore::MidiController::ControllerType type = MusECore::midiControllerType(c->num()); - + // Grab the controller number from the actual values showing // and set the patch button text. if(type == MusECore::MidiController::Program) setDefaultPatchName(getDefaultPatchNumber()); - } + } } //--------------------------------------------------------- @@ -1353,68 +1590,68 @@ void EditInstrument::patchNameReturn() { QTreeWidgetItem* item = patchView->currentItem(); - + if (item == 0) return; - + QString s = patchNameEdit->text(); - + if(item->text(0) == s) return; - - MusECore::PatchGroupList* pg = workingInstrument.groups(); - for(MusECore::iPatchGroup g = pg->begin(); g != pg->end(); ++g) + + MusECore::PatchGroupList* pg = workingInstrument->groups(); + for(MusECore::iPatchGroup g = pg->begin(); g != pg->end(); ++g) { MusECore::PatchGroup* pgp = *g; // If the item has a parent, it's a patch item. if(item->QTreeWidgetItem::parent()) { MusECore::Patch* curp = (MusECore::Patch*)item->data(0, Qt::UserRole).value(); - for(MusECore::iPatch p = pgp->patches.begin(); p != pgp->patches.end(); ++p) + for(MusECore::iPatch p = pgp->patches.begin(); p != pgp->patches.end(); ++p) { - if((*p) != curp && (*p)->name == s) + if((*p) != curp && (*p)->name == s) { patchNameEdit->blockSignals(true); // Grab the last valid name from the item text, since the patch has not been updated yet. patchNameEdit->setText(item->text(0)); patchNameEdit->blockSignals(false); - + QMessageBox::critical(this, tr("MusE: Bad patch name"), tr("Please choose a unique patch name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); - + return; } - } + } } else // The item has no parent. It's a patch group item. { MusECore::PatchGroup* curpg = (MusECore::PatchGroup*)item->data(0, Qt::UserRole).value(); - if(pgp != curpg && pgp->name == s) + if(pgp != curpg && pgp->name == s) { patchNameEdit->blockSignals(true); // Grab the last valid name from the item text, since the patch group has not been updated yet. patchNameEdit->setText(item->text(0)); patchNameEdit->blockSignals(false); - + QMessageBox::critical(this, tr("MusE: Bad patchgroup name"), tr("Please choose a unique patchgroup name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); - + return; } } } - + item->setText(0, s); - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -1425,28 +1662,25 @@ if(oldPatchItem) { if(oldPatchItem->parent()) - updatePatch(&workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); + updatePatch(workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); else - updatePatchGroup(&workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); + updatePatchGroup(workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); } - + QTreeWidgetItem* sel = patchView->selectedItems().size() ? patchView->selectedItems()[0] : 0; oldPatchItem = sel; - + if(!sel || !sel->data(0, Qt::UserRole).value()) { patchNameEdit->setText(""); spinBoxHBank->setEnabled(false); spinBoxLBank->setEnabled(false); spinBoxProgram->setEnabled(false); - checkBoxDrum->setEnabled(false); - // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. - //checkBoxGM->setEnabled(false); - //checkBoxGS->setEnabled(false); - //checkBoxXG->setEnabled(false); + showPatchInMidiButton->setEnabled(false); + showPatchInDrumsButton->setEnabled(false); return; } - + // If the item has a parent, it's a patch item. if(sel->parent()) { @@ -1455,24 +1689,18 @@ spinBoxHBank->setEnabled(true); spinBoxLBank->setEnabled(true); spinBoxProgram->setEnabled(true); - checkBoxDrum->setEnabled(true); - // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. - //checkBoxGM->setEnabled(true); - //checkBoxGS->setEnabled(true); - //checkBoxXG->setEnabled(true); - + showPatchInMidiButton->setEnabled(true); + showPatchInDrumsButton->setEnabled(true); + int hb = ((p->hbank + 1) & 0xff); int lb = ((p->lbank + 1) & 0xff); - int pr = ((p->prog + 1) & 0xff); + int pr = ((p->program + 1) & 0xff); spinBoxHBank->setValue(hb); spinBoxLBank->setValue(lb); spinBoxProgram->setValue(pr); - checkBoxDrum->setChecked(p->drum); - // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. - //checkBoxGM->setChecked(p->typ & 1); - //checkBoxGS->setChecked(p->typ & 2); - //checkBoxXG->setChecked(p->typ & 4); - } + showPatchInMidiButton->setChecked(!p->drum); + showPatchInDrumsButton->setChecked(p->drum); + } else // The item is a patch group item. { @@ -1480,12 +1708,9 @@ spinBoxHBank->setEnabled(false); spinBoxLBank->setEnabled(false); spinBoxProgram->setEnabled(false); - checkBoxDrum->setEnabled(false); - // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. - //checkBoxGM->setEnabled(false); - //checkBoxGS->setEnabled(false); - //checkBoxXG->setEnabled(false); - } + showPatchInMidiButton->setEnabled(false); + showPatchInDrumsButton->setEnabled(false); + } } //--------------------------------------------------------- @@ -1495,20 +1720,43 @@ void EditInstrument::defPatchChanged(int) { QTreeWidgetItem* item = viewController->currentItem(); - + if (!item) return; - + MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); - + int val = getDefaultPatchNumber(); - + c->setInitVal(val); - + setDefaultPatchName(val); - + item->setText(COL_DEF, getPatchItemText(val)); - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); +} + +//--------------------------------------------------------- +// defDrumPatchChanged +//--------------------------------------------------------- + +void EditInstrument::defDrumPatchChanged(int) +{ + QTreeWidgetItem* item = viewController->currentItem(); + + if (!item) + return; + + MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); + + int val = getDefaultDrumPatchNumber(); + + c->setDrumInitVal(val); + + setDefaultDrumPatchName(val); + + item->setText(COL_DRUM_DEF, getPatchItemText(val)); + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -1517,70 +1765,25 @@ void EditInstrument::patchButtonClicked() { - QMenu* patchpopup = new QMenu; - - MusECore::PatchGroupList* pg = workingInstrument.groups(); - - if (pg->size() > 1) { - for (MusECore::ciPatchGroup i = pg->begin(); i != pg->end(); ++i) { - MusECore::PatchGroup* pgp = *i; - QMenu* pm = patchpopup->addMenu(pgp->name); - //pm->setCheckable(false);//Qt4 doc says this is unnecessary - pm->setFont(MusEGlobal::config.fonts[0]); - const MusECore::PatchList& pl = pgp->patches; - for (MusECore::ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { - const MusECore::Patch* mp = *ipl; - int id = ((mp->hbank & 0xff) << 16) - + ((mp->lbank & 0xff) << 8) + (mp->prog & 0xff); - QAction *ac1 = pm->addAction(mp->name); - ac1->setData(id); - } - } - } - else if (pg->size() == 1 ){ - // no groups - const MusECore::PatchList& pl = pg->front()->patches; - for (MusECore::ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { - const MusECore::Patch* mp = *ipl; - int id = ((mp->hbank & 0xff) << 16) - + ((mp->lbank & 0xff) << 8) + (mp->prog & 0xff); - QAction *ac2 = patchpopup->addAction(mp->name); - ac2->setData(id); - } - } + popupControllerDefaultPatchList(false); +} - if(patchpopup->actions().count() == 0) - { - delete patchpopup; - return; - } - - QAction* act = patchpopup->exec(patchButton->mapToGlobal(QPoint(10,5))); - if(!act) - { - delete patchpopup; - return; - } - - int rv = act->data().toInt(); - delete patchpopup; +//--------------------------------------------------------- +// drumPatchButtonClicked +//--------------------------------------------------------- - if (rv != -1) - { - setDefaultPatchControls(rv); - - QTreeWidgetItem* item = viewController->currentItem(); +void EditInstrument::drumPatchButtonClicked() +{ + popupControllerDefaultPatchList(true); +} - if(item) - { - MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); - c->setInitVal(rv); - - item->setText(COL_DEF, getPatchItemText(rv)); - } - workingInstrument.setDirty(true); - } - +//--------------------------------------------------------- +// drummapCollectionPatchButtonClicked +//--------------------------------------------------------- + +void EditInstrument::drummapCollectionPatchButtonClicked() +{ + popupDrummapPatchList(); } //--------------------------------------------------------- @@ -1594,13 +1797,15 @@ QString min; QString max; QString def; + QString drumdef; int defval = mctrl->initVal(); + int drumdefval = mctrl->drumInitVal(); int n = mctrl->num(); int h = (n >> 8) & 0x7f; int l = n & 0x7f; if((n & 0xff) == 0xff) l = -1; - + MusECore::MidiController::ControllerType t = MusECore::midiControllerType(n); switch(t) { @@ -1608,7 +1813,7 @@ hnum = "---"; if(l == -1) lnum = "*"; - else + else lnum.setNum(l); min.setNum(mctrl->minVal()); max.setNum(mctrl->maxVal()); @@ -1616,6 +1821,11 @@ def = "---"; else def.setNum(defval); + + if(drumdefval == MusECore::CTRL_VAL_UNKNOWN) + drumdef = "---"; + else + drumdef.setNum(drumdefval); break; case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: @@ -1625,7 +1835,7 @@ hnum.setNum(h); if(l == -1) lnum = "*"; - else + else lnum.setNum(l); min.setNum(mctrl->minVal()); max.setNum(mctrl->maxVal()); @@ -1633,6 +1843,11 @@ def = "---"; else def.setNum(defval); + + if(drumdefval == MusECore::CTRL_VAL_UNKNOWN) + drumdef = "---"; + else + drumdef.setNum(drumdefval); break; case MusECore::MidiController::Pitch: case MusECore::MidiController::PolyAftertouch: @@ -1645,21 +1860,28 @@ def = "---"; else def.setNum(defval); + + if(drumdefval == MusECore::CTRL_VAL_UNKNOWN) + drumdef = "---"; + else + drumdef.setNum(drumdefval); break; case MusECore::MidiController::Program: hnum = "---"; lnum = "---"; min = "---"; max = "---"; - def = getPatchItemText(defval); + def = getPatchItemText(defval); + drumdef = getPatchItemText(defval); break; - + default: hnum = "---"; lnum = "---"; min = "---"; max = "---"; def = "---"; + drumdef = "---"; break; } @@ -1669,44 +1891,43 @@ if(mctrl->showInTracks() & MusECore::MidiController::ShowInDrum) show_drum = "X"; QTreeWidgetItem* ci = new QTreeWidgetItem(viewController, - QStringList() << mctrl->name() << int2ctrlType(t) << hnum << lnum << min << max << def << show_midi << show_drum); + QStringList() << mctrl->name() << int2ctrlType(t) << hnum << lnum << min << max << def << drumdef << show_midi << show_drum); ci->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); ci->setTextAlignment(1, Qt::AlignLeft | Qt::AlignVCenter); - for(int i = 2; i < 9; ++i) + for(int i = 2; i < 10; ++i) ci->setTextAlignment(i, Qt::AlignRight | Qt::AlignVCenter); - QVariant v = qVariantFromValue((void*)(mctrl)); - ci->setData(0, Qt::UserRole, v); - + ci->setData(0, Qt::UserRole, QVariant::fromValue((void*)(mctrl))); + return ci; } - + //--------------------------------------------------------- // controllerChanged //--------------------------------------------------------- void EditInstrument::controllerChanged() { - QTreeWidgetItem* sel = viewController->selectedItems().size() ? viewController->selectedItems()[0] : 0; - - if(!sel || !sel->data(0, Qt::UserRole).value()) + QTreeWidgetItem* sel = viewController->selectedItems().size() ? viewController->selectedItems()[0] : 0; + + if(!sel || !sel->data(0, Qt::UserRole).value()) { ctrlName->blockSignals(true); ctrlName->setText(""); ctrlName->blockSignals(false); return; } - + MusECore::MidiController* c = (MusECore::MidiController*)sel->data(0, Qt::UserRole).value(); - + ctrlName->blockSignals(true); ctrlName->setText(c->name()); ctrlName->blockSignals(false); - + int ctrlH = (c->num() >> 8) & 0x7f; int ctrlL = c->num() & 0x7f; if(c->isPerNoteController()) ctrlL = -1; - + MusECore::MidiController::ControllerType type = MusECore::midiControllerType(c->num()); int idx = ctrlType->findData(type); if(idx != -1) @@ -1718,13 +1939,14 @@ ctrlShowInMidi->setChecked(c->showInTracks() & MusECore::MidiController::ShowInMidi); ctrlShowInDrum->setChecked(c->showInTracks() & MusECore::MidiController::ShowInDrum); - + spinBoxHCtrlNo->blockSignals(true); spinBoxLCtrlNo->blockSignals(true); spinBoxMin->blockSignals(true); spinBoxMax->blockSignals(true); spinBoxDefault->blockSignals(true); - + spinBoxDrumDefault->blockSignals(true); + switch (type) { case MusECore::MidiController::Controller7: spinBoxHCtrlNo->setEnabled(false); @@ -1815,28 +2037,39 @@ spinBoxMax->setEnabled(false); enableDefaultControls(false, false); break; - } - + } + if(type == MusECore::MidiController::Program) { spinBoxDefault->setRange(0, 0); spinBoxDefault->setValue(0); setDefaultPatchControls(c->initVal()); + + spinBoxDrumDefault->setRange(0, 0); + spinBoxDrumDefault->setValue(0); + setDefaultDrumPatchControls(c->drumInitVal()); } else { spinBoxDefault->setRange(c->minVal() - 1, c->maxVal()); if(c->initVal() == MusECore::CTRL_VAL_UNKNOWN) spinBoxDefault->setValue(spinBoxDefault->minimum()); - else + else spinBoxDefault->setValue(c->initVal()); + + spinBoxDrumDefault->setRange(c->minVal() - 1, c->maxVal()); + if(c->drumInitVal() == MusECore::CTRL_VAL_UNKNOWN) + spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); + else + spinBoxDrumDefault->setValue(c->drumInitVal()); } - + spinBoxHCtrlNo->blockSignals(false); spinBoxLCtrlNo->blockSignals(false); spinBoxMin->blockSignals(false); spinBoxMax->blockSignals(false); spinBoxDefault->blockSignals(false); + spinBoxDrumDefault->blockSignals(false); ctrlValidLabel->setPixmap(*greendotIcon); enableNonCtrlControls(true); @@ -1853,36 +2086,36 @@ if (item == 0) return; MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); - + QString cName = ctrlName->text(); - - MusECore::MidiControllerList* cl = workingInstrument.controller(); - for(MusECore::ciMidiController ic = cl->begin(); ic != cl->end(); ++ic) + + MusECore::MidiControllerList* cl = workingInstrument->controller(); + for(MusECore::ciMidiController ic = cl->begin(); ic != cl->end(); ++ic) { MusECore::MidiController* mc = ic->second; - if(mc != c && mc->name() == cName) + if(mc != c && mc->name() == cName) { ctrlName->blockSignals(true); ctrlName->setText(c->name()); ctrlName->blockSignals(false); - + QMessageBox::critical(this, tr("MusE: Bad controller name"), tr("Please choose a unique controller name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); - + return; } } - + if(c->name() == cName) return; c->setName(ctrlName->text()); item->setText(COL_CNAME, ctrlName->text()); - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -1892,14 +2125,14 @@ void EditInstrument::ctrlTypeChanged(int idx) { QTreeWidgetItem* item = viewController->currentItem(); - + if (item == 0) return; - + MusECore::MidiController::ControllerType t = (MusECore::MidiController::ControllerType)ctrlType->itemData(idx).toInt(); MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); int hnum = 0, lnum = 0; - + switch (t) { case MusECore::MidiController::Controller7: spinBoxHCtrlNo->setEnabled(false); @@ -1945,10 +2178,10 @@ spinBoxMax->blockSignals(false); return; break; - } - + } + int new_num = MusECore::MidiController::genNum(t, hnum, lnum); - MusECore::MidiControllerList* cl = workingInstrument.controller(); + MusECore::MidiControllerList* cl = workingInstrument->controller(); // Check if either a per-note controller, or else a regular controller already exists. if(!cl->ctrlAvailable(new_num, c)) { @@ -1956,26 +2189,27 @@ enableNonCtrlControls(false); return; } - + ctrlValidLabel->setPixmap(*greendotIcon); - + if(t == MusECore::midiControllerType(c->num())) - { + { enableNonCtrlControls(true); return; } - - cl->erase(c->num()); + + cl->del(c->num()); c->setNum(new_num); cl->add(c); - + enableNonCtrlControls(true); - + item->setText(COL_TYPE, ctrlType->currentText()); spinBoxMin->blockSignals(true); spinBoxMax->blockSignals(true); spinBoxDefault->blockSignals(true); + spinBoxDrumDefault->blockSignals(true); switch (t) { case MusECore::MidiController::Controller7: @@ -1985,6 +2219,8 @@ spinBoxMax->setValue(127); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); + spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); + spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); if(lnum == -1) item->setText(COL_LNUM, QString("*")); else @@ -1993,6 +2229,7 @@ item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); + item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: @@ -2002,6 +2239,8 @@ spinBoxMax->setValue(127); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); + spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); + spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); if(lnum == -1) item->setText(COL_LNUM, QString("*")); else @@ -2010,6 +2249,7 @@ item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); + item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN14: @@ -2020,6 +2260,8 @@ spinBoxMax->setValue(16383); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); + spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); + spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); if(lnum == -1) item->setText(COL_LNUM, QString("*")); else @@ -2028,6 +2270,7 @@ item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); + item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::Pitch: spinBoxMin->setRange(-8192, 8191); @@ -2036,11 +2279,14 @@ spinBoxMax->setValue(8191); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); + spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); + spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); item->setText(COL_LNUM, QString("---")); item->setText(COL_HNUM, QString("---")); item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); + item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: @@ -2050,11 +2296,14 @@ spinBoxMax->setValue(127); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); + spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); + spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); item->setText(COL_LNUM, QString("---")); item->setText(COL_HNUM, QString("---")); item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); + item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::Program: spinBoxMin->setRange(0, 0); @@ -2063,11 +2312,14 @@ spinBoxMax->setValue(0); spinBoxDefault->setRange(0, 0); spinBoxDefault->setValue(0); + spinBoxDrumDefault->setRange(0, 0); + spinBoxDrumDefault->setValue(0); item->setText(COL_LNUM, QString("---")); item->setText(COL_HNUM, QString("---")); item->setText(COL_MIN, QString("---")); item->setText(COL_MAX, QString("---")); item->setText(COL_DEF, QString("---")); + item->setText(COL_DRUM_DEF, QString("---")); break; // Shouldn't happen... default: @@ -2078,14 +2330,18 @@ spinBoxMin->blockSignals(false); spinBoxMax->blockSignals(false); spinBoxDefault->blockSignals(false); + spinBoxDrumDefault->blockSignals(false); setDefaultPatchControls(0xffffff); + setDefaultDrumPatchControls(0xffffff); + if(t == MusECore::MidiController::Program) { c->setMinVal(0); c->setMaxVal(0xffffff); c->setInitVal(0xffffff); + c->setDrumInitVal(0xffffff); } else { @@ -2093,11 +2349,16 @@ c->setMaxVal(spinBoxMax->value()); if(spinBoxDefault->value() == spinBoxDefault->minimum()) c->setInitVal(MusECore::CTRL_VAL_UNKNOWN); - else + else c->setInitVal(spinBoxDefault->value()); - } - - workingInstrument.setDirty(true); + + if(spinBoxDrumDefault->value() == spinBoxDrumDefault->minimum()) + c->setDrumInitVal(MusECore::CTRL_VAL_UNKNOWN); + else + c->setDrumInitVal(spinBoxDrumDefault->value()); + } + + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2123,7 +2384,7 @@ c->setShowInTracks(show & ~MusECore::MidiController::ShowInMidi); item->setText(COL_SHOW_MIDI, ""); } - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2149,7 +2410,7 @@ c->setShowInTracks(show & ~MusECore::MidiController::ShowInDrum); item->setText(COL_SHOW_DRUM, ""); } - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2161,7 +2422,7 @@ QTreeWidgetItem* item = viewController->currentItem(); if (item == 0 || ctrlType->currentIndex() == -1) return; - MusECore::MidiController::ControllerType t = (MusECore::MidiController::ControllerType)ctrlType->itemData(ctrlType->currentIndex()).toInt(); + MusECore::MidiController::ControllerType t = (MusECore::MidiController::ControllerType)ctrlType->itemData(ctrlType->currentIndex()).toInt(); int hnum = 0, lnum = 0; switch (t) { case MusECore::MidiController::Controller7: @@ -2187,15 +2448,15 @@ return; break; } - + int new_num = MusECore::MidiController::genNum(t, hnum, lnum); if(new_num == -1) { printf("EditInstrument::ctrlNumChanged Error: genNum returned -1\n"); return; } - - MusECore::MidiControllerList* cl = workingInstrument.controller(); + + MusECore::MidiControllerList* cl = workingInstrument->controller(); MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); // Check if either a per-note controller, or else a regular controller already exists. @@ -2207,7 +2468,7 @@ } ctrlValidLabel->setPixmap(*greendotIcon); enableNonCtrlControls(true); - if(cl->erase(c->num()) == 0) + if(cl->del(c->num()) == 0) printf("EditInstrument::ctrlNumChanged Warning: Erase failed! Proceeding anyway.\n"); c->setNum(new_num); cl->add(c); @@ -2234,7 +2495,7 @@ return; } item->setText(COL_TYPE, ctrlType->currentText()); - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2244,17 +2505,17 @@ void EditInstrument::ctrlMinChanged(int val) { QTreeWidgetItem* item = viewController->currentItem(); - + if (item == 0) return; - + QString s; s.setNum(val); item->setText(COL_MIN, s); - + MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); c->setMinVal(val); - + int rng = 0; switch(MusECore::midiControllerType(c->num())) { @@ -2271,12 +2532,12 @@ case MusECore::MidiController::Pitch: rng = 16383; break; - default: - break; + default: + break; } - + int mx = c->maxVal(); - + if(val > mx) { c->setMaxVal(val); @@ -2284,7 +2545,7 @@ spinBoxMax->setValue(val); spinBoxMax->blockSignals(false); item->setText(COL_MAX, s); - } + } else if(mx - val > rng) { @@ -2294,33 +2555,54 @@ spinBoxMax->setValue(mx); spinBoxMax->blockSignals(false); item->setText(COL_MAX, QString().setNum(mx)); - } - + } + spinBoxDefault->blockSignals(true); - + spinBoxDrumDefault->blockSignals(true); + spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); - + spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); + int inval = c->initVal(); if(inval == MusECore::CTRL_VAL_UNKNOWN) spinBoxDefault->setValue(spinBoxDefault->minimum()); - else + else { if(inval < c->minVal()) { c->setInitVal(c->minVal()); spinBoxDefault->setValue(c->minVal()); } - else + else if(inval > c->maxVal()) - { + { c->setInitVal(c->maxVal()); spinBoxDefault->setValue(c->maxVal()); - } - } - + } + } + + inval = c->drumInitVal(); + if(inval == MusECore::CTRL_VAL_UNKNOWN) + spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); + else + { + if(inval < c->minVal()) + { + c->setDrumInitVal(c->minVal()); + spinBoxDrumDefault->setValue(c->minVal()); + } + else + if(inval > c->maxVal()) + { + c->setDrumInitVal(c->maxVal()); + spinBoxDrumDefault->setValue(c->maxVal()); + } + } + spinBoxDefault->blockSignals(false); - - workingInstrument.setDirty(true); + spinBoxDrumDefault->blockSignals(false); + + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2330,17 +2612,17 @@ void EditInstrument::ctrlMaxChanged(int val) { QTreeWidgetItem* item = viewController->currentItem(); - + if (item == 0) return; - + QString s; s.setNum(val); item->setText(COL_MAX, s); - + MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); c->setMaxVal(val); - + int rng = 0; switch(MusECore::midiControllerType(c->num())) { @@ -2357,12 +2639,12 @@ case MusECore::MidiController::Pitch: rng = 16383; break; - default: - break; + default: + break; } - + int mn = c->minVal(); - + if(val < mn) { c->setMinVal(val); @@ -2370,7 +2652,7 @@ spinBoxMin->setValue(val); spinBoxMin->blockSignals(false); item->setText(COL_MIN, s); - } + } else if(val - mn > rng) { @@ -2380,33 +2662,33 @@ spinBoxMin->setValue(mn); spinBoxMin->blockSignals(false); item->setText(COL_MIN, QString().setNum(mn)); - } - + } + spinBoxDefault->blockSignals(true); - + spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); - + int inval = c->initVal(); if(inval == MusECore::CTRL_VAL_UNKNOWN) spinBoxDefault->setValue(spinBoxDefault->minimum()); - else + else { if(inval < c->minVal()) { c->setInitVal(c->minVal()); spinBoxDefault->setValue(c->minVal()); } - else + else if(inval > c->maxVal()) - { + { c->setInitVal(c->maxVal()); spinBoxDefault->setValue(c->maxVal()); - } - } - + } + } + spinBoxDefault->blockSignals(false); - - workingInstrument.setDirty(true); + + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2419,82 +2701,46 @@ if (item == 0) return; - + MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); - + if(val == c->minVal() - 1) { c->setInitVal(MusECore::CTRL_VAL_UNKNOWN); item->setText(COL_DEF, QString("---")); - } + } else { c->setInitVal(val); item->setText(COL_DEF, QString().setNum(val)); } - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } //--------------------------------------------------------- -// ctrlNullParamHChanged +// ctrlDrumDefaultChanged //--------------------------------------------------------- -void EditInstrument::ctrlNullParamHChanged(int nvh) +void EditInstrument::ctrlDrumDefaultChanged(int val) { - int nvl = nullParamSpinBoxL->value(); - if(nvh == -1) - { - nullParamSpinBoxL->blockSignals(true); - nullParamSpinBoxL->setValue(-1); - nullParamSpinBoxL->blockSignals(false); - nvl = -1; - } - else - { - if(nvl == -1) - { - nullParamSpinBoxL->blockSignals(true); - nullParamSpinBoxL->setValue(0); - nullParamSpinBoxL->blockSignals(false); - nvl = 0; - } - } - if(nvh == -1 && nvl == -1) - workingInstrument.setNullSendValue(-1); - else - workingInstrument.setNullSendValue((nvh << 8) | nvl); - workingInstrument.setDirty(true); -} - -//--------------------------------------------------------- -// ctrlNullParamLChanged -//--------------------------------------------------------- + QTreeWidgetItem* item = viewController->currentItem(); -void EditInstrument::ctrlNullParamLChanged(int nvl) -{ - int nvh = nullParamSpinBoxH->value(); - if(nvl == -1) - { - nullParamSpinBoxH->blockSignals(true); - nullParamSpinBoxH->setValue(-1); - nullParamSpinBoxH->blockSignals(false); - nvh = -1; - } - else - { - if(nvh == -1) - { - nullParamSpinBoxH->blockSignals(true); - nullParamSpinBoxH->setValue(0); - nullParamSpinBoxH->blockSignals(false); - nvh = 0; - } - } - if(nvh == -1 && nvl == -1) - workingInstrument.setNullSendValue(-1); - else - workingInstrument.setNullSendValue((nvh << 8) | nvl); - workingInstrument.setDirty(true); + if (item == 0) + return; + + MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); + + if(val == c->minVal() - 1) + { + c->setDrumInitVal(MusECore::CTRL_VAL_UNKNOWN); + item->setText(COL_DRUM_DEF, QString("---")); + } + else + { + c->setDrumInitVal(val); + item->setText(COL_DRUM_DEF, QString().setNum(val)); + } + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2513,7 +2759,7 @@ } unsigned char* data; int len = MusECore::string2sysex(sysexData->toPlainText(), &data); - if (len == -1) // Conversion unsuccessful? + if (len == -1) // Conversion unsuccessful? { QMessageBox::information(0, QString("MusE"), @@ -2537,7 +2783,7 @@ { if (old) { MusECore::SysEx* so = (MusECore::SysEx*)old->data(Qt::UserRole).value(); - updateSysex(&workingInstrument, so); + updateSysex(workingInstrument, so); } if (sel == 0) { sysexName->setText(""); @@ -2568,9 +2814,9 @@ if(item2 == 0) return; MusECore::SysEx* sysex = (MusECore::SysEx*)item2->data(Qt::UserRole).value(); - workingInstrument.removeSysex(sysex); + workingInstrument->removeSysex(sysex); delete item2; - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2582,9 +2828,9 @@ QString sysexName; for (int i = 1;; ++i) { sysexName = QString("Sysex-%1").arg(i); - + bool found = false; - foreach(const MusECore::SysEx* s, workingInstrument.sysex()) { + foreach(const MusECore::SysEx* s, workingInstrument->sysex()) { if (s->name == sysexName) { found = true; break; @@ -2595,14 +2841,14 @@ } MusECore::SysEx* nsysex = new MusECore::SysEx; nsysex->name = sysexName; - workingInstrument.addSysex(nsysex); + workingInstrument->addSysex(nsysex); QListWidgetItem* item = new QListWidgetItem(sysexName); QVariant v = QVariant::fromValue((void*)nsysex); item->setData(Qt::UserRole, v); sysexList->addItem(item); sysexList->setCurrentItem(item); - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } @@ -2616,12 +2862,12 @@ if (pi == 0) return; - + // If the item has a parent item, it's a patch item... if(pi->parent()) { MusECore::PatchGroup* group = (MusECore::PatchGroup*)(pi->parent())->data(0, Qt::UserRole).value(); - + // If there is an allocated patch in the data, delete it. MusECore::Patch* patch = (MusECore::Patch*)pi->data(0, Qt::UserRole).value(); if(patch) @@ -2630,7 +2876,7 @@ group->patches.remove(patch); delete patch; - } + } } else // The item has no parent item, it's a patch group item... @@ -2639,44 +2885,44 @@ MusECore::PatchGroup* group = (MusECore::PatchGroup*)pi->data(0, Qt::UserRole).value(); if(group) { - - MusECore::PatchGroupList* pg = workingInstrument.groups(); + + MusECore::PatchGroupList* pg = workingInstrument->groups(); for(MusECore::iPatchGroup ipg = pg->begin(); ipg != pg->end(); ++ipg) { - + if(*ipg == group) { pg->erase(ipg); break; - } + } } - + const MusECore::PatchList& pl = group->patches; for(MusECore::ciPatch ip = pl.begin(); ip != pl.end(); ++ip) { // Delete the patch. if(*ip) - delete *ip; + delete *ip; } - + // Now delete the group. delete group; - - } + + } } - + // Now delete the patch or group item (and any child patch items) from the list view tree. - // !!! This will trigger a patchChanged call. + // !!! This will trigger a patchChanged call. patchView->blockSignals(true); delete pi; if(patchView->currentItem()) patchView->currentItem()->setSelected(true); patchView->blockSignals(false); - + oldPatchItem = 0; patchChanged(); - - workingInstrument.setDirty(true); + + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2688,12 +2934,12 @@ if(oldPatchItem) { if(oldPatchItem->parent()) - updatePatch(&workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); - else - updatePatchGroup(&workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); - } + updatePatch(workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); + else + updatePatchGroup(workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); + } - MusECore::PatchGroupList* pg = workingInstrument.groups(); + MusECore::PatchGroupList* pg = workingInstrument->groups(); QString patchName; for (int i = 1;; ++i) { patchName = QString("Patch-%1").arg(i); @@ -2719,24 +2965,24 @@ if (pi == 0) return; - + MusECore::Patch* selpatch = 0; - + // If there is a parent item then pi is a patch item, and there must be a parent patch group item. if(pi->parent()) { // Remember the current selected patch. selpatch = (MusECore::Patch*)pi->data(0, Qt::UserRole).value(); - + pi = pi->parent(); } - + MusECore::PatchGroup* group = (MusECore::PatchGroup*)pi->data(0, Qt::UserRole).value(); if(!group) return; - - - // Create a new Patch, then store its pointer in a new patch item, + + + // Create a new Patch, then store its pointer in a new patch item, // to be added later to the patch group only upon save... //Patch patch; //patch.name = patchName; @@ -2746,40 +2992,40 @@ int prg = 0; patch->hbank = hb; patch->lbank = lb; - patch->prog = prg; - //patch->typ = -1; + patch->program = prg; + //patch->typ = -1; patch->drum = false; - + if(selpatch) { hb = selpatch->hbank; lb = selpatch->lbank; - prg = selpatch->prog; - //patch->typ = selpatch->typ; - patch->drum = selpatch->drum; + prg = selpatch->program; + //patch->typ = selpatch->typ; + patch->drum = selpatch->drum; } - + bool found = false; - - // The 129 is to accommodate -1 values. Yes, it may cause one extra redundant loop but hey, + + // The 129 is to accommodate -1 values. Yes, it may cause one extra redundant loop but hey, // if it hasn't found an available patch number by then, another loop won't matter. for(int k = 0; k < 129; ++k) { for(int j = 0; j < 129; ++j) { - for(int i = 0; i < 128; ++i) + for(int i = 0; i < 128; ++i) { found = false; - for(MusECore::iPatchGroup g = pg->begin(); g != pg->end(); ++g) + for(MusECore::iPatchGroup g = pg->begin(); g != pg->end(); ++g) { MusECore::PatchGroup* pgp = *g; - for(MusECore::iPatch ip = pgp->patches.begin(); ip != pgp->patches.end(); ++ip) + for(MusECore::iPatch ip = pgp->patches.begin(); ip != pgp->patches.end(); ++ip) { MusECore::Patch* p = *ip; - if((p->prog == ((prg + i) & 0x7f)) && - ((p->lbank == -1 && lb == -1) || (p->lbank == ((lb + j) & 0x7f))) && - ((p->hbank == -1 && hb == -1) || (p->hbank == ((hb + k) & 0x7f)))) + if((p->program == ((prg + i) & 0x7f)) && + ((p->lbank == -1 && lb == -1) || (p->lbank == ((lb + j) & 0x7f))) && + ((p->hbank == -1 && hb == -1) || (p->hbank == ((hb + k) & 0x7f)))) { found = true; break; @@ -2788,62 +3034,58 @@ if(found) break; } - + if(!found) { - patch->prog = (prg + i) & 0x7f; + patch->program = (prg + i) & 0x7f; if(lb == -1) patch->lbank = -1; - else + else patch->lbank = (lb + j) & 0x7f; - + if(hb == -1) patch->hbank = -1; - else + else patch->hbank = (hb + k) & 0x7f; - + break; - } - + } + } if(!found) break; - } + } if(!found) break; - } - + } + patch->name = patchName; group->patches.push_back(patch); QTreeWidgetItem* sitem = new QTreeWidgetItem(pi); sitem->setText(0, patchName); - + patchNameEdit->setText(patchName); - - QVariant v = qVariantFromValue((void*)(patch)); - sitem->setData(0, Qt::UserRole, v); - + + sitem->setData(0, Qt::UserRole, QVariant::fromValue((void*)(patch))); + // May cause patchChanged call. patchView->blockSignals(true); - sitem->setSelected(true); - patchView->scrollToItem((QTreeWidgetItem*)sitem, QAbstractItemView::EnsureVisible); + patchView->setCurrentItem(sitem); + patchView->scrollToItem(sitem, QAbstractItemView::EnsureVisible); patchView->blockSignals(false); - + spinBoxHBank->setEnabled(true); spinBoxLBank->setEnabled(true); spinBoxProgram->setEnabled(true); - checkBoxDrum->setEnabled(true); - // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. - //checkBoxGM->setEnabled(true); - //checkBoxGS->setEnabled(true); - //checkBoxXG->setEnabled(true); - + showPatchInMidiButton->setEnabled(true); + showPatchInDrumsButton->setEnabled(true); + oldPatchItem = 0; patchChanged(); - - workingInstrument.setDirty(true); + + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2855,12 +3097,12 @@ if(oldPatchItem) { if(oldPatchItem->parent()) - updatePatch(&workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); - else - updatePatchGroup(&workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); - } - - MusECore::PatchGroupList* pg = workingInstrument.groups(); + updatePatch(workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); + else + updatePatchGroup(workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); + } + + MusECore::PatchGroupList* pg = workingInstrument->groups(); QString groupName; for (int i = 1;; ++i) { groupName = QString("Group-%1").arg(i); @@ -2876,40 +3118,36 @@ break; } - // Create a new PatchGroup, then store its pointer in a new patch group item, + // Create a new PatchGroup, then store its pointer in a new patch group item, // to be added later to the instrument only upon save... MusECore::PatchGroup* group = new MusECore::PatchGroup; group->name = groupName; - + pg->push_back(group); - + QTreeWidgetItem* sitem = new QTreeWidgetItem(patchView); sitem->setText(0, groupName); - + patchNameEdit->setText(groupName); - - // Set the list view item's data. - QVariant v = qVariantFromValue((void*)(group)); - sitem->setData(0, Qt::UserRole, v); + + // Set the list view item's data. + sitem->setData(0, Qt::UserRole, QVariant::fromValue((void*)(group))); //sitem->setAuxData((void*)pgp); - + // May cause patchChanged call. patchView->blockSignals(true); - sitem->setSelected(true); + patchView->setCurrentItem(sitem); patchView->blockSignals(false); - + oldPatchItem = sitem; - + spinBoxHBank->setEnabled(false); spinBoxLBank->setEnabled(false); spinBoxProgram->setEnabled(false); - checkBoxDrum->setEnabled(false); - // REMOVE Tim. OBSOLETE. When gui boxes are finally removed. - //checkBoxGM->setEnabled(false); - //checkBoxGS->setEnabled(false); - //checkBoxXG->setEnabled(false); - - workingInstrument.setDirty(true); + showPatchInMidiButton->setEnabled(false); + showPatchInDrumsButton->setEnabled(false); + + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2919,18 +3157,18 @@ void EditInstrument::deleteControllerClicked() { QTreeWidgetItem* item = viewController->currentItem(); - + if(!item) return; - + MusECore::MidiController* ctrl = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); if(!ctrl) return; - - workingInstrument.controller()->erase(ctrl->num()); + + workingInstrument->controller()->del(ctrl->num()); // Now delete the controller. delete ctrl; - + // Now remove the controller item from the list. // This may cause a controllerChanged call. viewController->blockSignals(true); @@ -2938,10 +3176,10 @@ if(viewController->currentItem()) viewController->currentItem()->setSelected(true); viewController->blockSignals(false); - + controllerChanged(); - - workingInstrument.setDirty(true); + + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -2949,9 +3187,9 @@ //--------------------------------------------------------- void EditInstrument::newControllerClicked() - { + { QString cName; - MusECore::MidiControllerList* cl = workingInstrument.controller(); + MusECore::MidiControllerList* cl = workingInstrument->controller(); for (int i = 1;; ++i) { cName = QString("Controller-%1").arg(i); bool found = false; @@ -2971,6 +3209,7 @@ ctrl->setMinVal(0); ctrl->setMaxVal(127); ctrl->setInitVal(MusECore::CTRL_VAL_UNKNOWN); + ctrl->setDrumInitVal(MusECore::CTRL_VAL_UNKNOWN); QTreeWidgetItem* ci = viewController->currentItem(); @@ -3021,7 +3260,7 @@ for(int k = (h & 0xffff0000); k < MusECore::CTRL_NONE_OFFSET; k += 0x10000) { // Don't copy internal controllers. - if(k == MusECore::CTRL_INTERNAL_OFFSET) + if(k == MusECore::CTRL_INTERNAL_OFFSET) { found = true; continue; @@ -3060,16 +3299,16 @@ break; } - if(found) + if(found) { QMessageBox::critical(this, tr("New controller: Error"), tr("Error! All control numbers are taken up!\nClean up the instrument!")); - delete ctrl; + delete ctrl; return; } - + ctrl->setName(cName); - - workingInstrument.controller()->add(ctrl); + + workingInstrument->controller()->add(ctrl); QTreeWidgetItem* item = addControllerToView(ctrl); if(viewController->currentItem() != item) @@ -3080,7 +3319,7 @@ controllerChanged(); } - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } //--------------------------------------------------------- @@ -3091,8 +3330,8 @@ { // Add Common Controls not already found in instrument: PopupMenu* pup = new PopupMenu(true); // true = enable stay open. Don't bother with parent. - MusECore::MidiControllerList* cl = workingInstrument.controller(); - for(int num = 0; num < 127; ++num) + MusECore::MidiControllerList* cl = workingInstrument->controller(); + for(int num = 0; num < 128; ++num) { // If it's not already in the parent menu... if(cl->find(num) == cl->end()) @@ -3112,7 +3351,7 @@ if(!act || (act->data().toInt() == -1)) return; int rv = act->data().toInt(); - MusECore::MidiControllerList* cl = workingInstrument.controller(); + MusECore::MidiControllerList* cl = workingInstrument->controller(); if(cl->find(rv) == cl->end()) { int num = rv; // = MusECore::MidiController::genNum(MusECore::MidiController::Controller7, 0, rv); @@ -3121,9 +3360,10 @@ ctrl->setMinVal(0); ctrl->setMaxVal(127); ctrl->setInitVal(MusECore::CTRL_VAL_UNKNOWN); + ctrl->setDrumInitVal(MusECore::CTRL_VAL_UNKNOWN); ctrl->setName(MusECore::midiCtrlName(num, false)); - - workingInstrument.controller()->add(ctrl); + + workingInstrument->controller()->add(ctrl); QTreeWidgetItem* item = addControllerToView(ctrl); @@ -3135,7 +3375,7 @@ controllerChanged(); } - workingInstrument.setDirty(true); + workingInstrument->setDirty(true); } } @@ -3145,8 +3385,8 @@ void EditInstrument::updatePatchGroup(MusECore::MidiInstrument* instrument, MusECore::PatchGroup* pg) { - QString a = pg->name; - QString b = patchNameEdit->text(); + QString a = pg->name; + QString b = patchNameEdit->text(); if (pg->name != patchNameEdit->text()) { pg->name = patchNameEdit->text(); instrument->setDirty(true); @@ -3163,52 +3403,32 @@ p->name = patchNameEdit->text(); instrument->setDirty(true); } - + signed char hb = (spinBoxHBank->value() - 1) & 0xff; if (p->hbank != hb) { p->hbank = hb; - + instrument->setDirty(true); } - + signed char lb = (spinBoxLBank->value() - 1) & 0xff; if (p->lbank != lb) { p->lbank = lb; - + instrument->setDirty(true); } - + signed char pr = (spinBoxProgram->value() - 1) & 0xff; - if (p->prog != pr) { - p->prog = pr; - + if (p->program != pr) { + p->program = pr; + instrument->setDirty(true); } - - if (p->drum != checkBoxDrum->isChecked()) { - p->drum = checkBoxDrum->isChecked(); + + if (p->drum != showPatchInDrumsButton->isChecked()) { // Midi and drums radio buttons are exclusive. + p->drum = showPatchInDrumsButton->isChecked(); instrument->setDirty(true); } - - // there is no logical xor in c++ -// REMOVE Tim. OBSOLETE. When gui boxes are finally removed. -// bool a = p->typ & 1; -// bool b = p->typ & 2; -// bool c = p->typ & 4; -// bool aa = checkBoxGM->isChecked(); -// bool bb = checkBoxGS->isChecked(); -// bool cc = checkBoxXG->isChecked(); -// if ((a ^ aa) || (b ^ bb) || (c ^ cc)) { -// int value = 0; -// if (checkBoxGM->isChecked()) -// value |= 1; -// if (checkBoxGS->isChecked()) -// value |= 2; -// if (checkBoxXG->isChecked()) -// value |= 4; -// p->typ = value; -// instrument->setDirty(true); -// } } //--------------------------------------------------------- @@ -3224,14 +3444,14 @@ } QTreeWidgetItem* patchItem = patchView->currentItem(); - if (patchItem) - { + if (patchItem) + { // If the item has a parent, it's a patch item. if(patchItem->parent()) updatePatch(instrument, (MusECore::Patch*)patchItem->data(0, Qt::UserRole).value()); else updatePatchGroup(instrument, (MusECore::PatchGroup*)patchItem->data(0, Qt::UserRole).value()); - + } } @@ -3247,12 +3467,12 @@ return 0; int n; - if(isClose) + if(isClose) n = QMessageBox::warning(this, tr("MusE"), tr("The current Instrument contains unsaved data\n" "Save Current Instrument?"), tr("&Save"), tr("&Nosave"), tr("&Abort"), 0, 2); - else + else n = QMessageBox::warning(this, tr("MusE"), tr("The current Instrument contains unsaved data\n" "Save Current Instrument?"), @@ -3261,7 +3481,7 @@ if (i->filePath().isEmpty()) { saveAs(); - } + } else { FILE* f = fopen(i->filePath().toLatin1().constData(), "w"); if(f == 0) @@ -3269,7 +3489,7 @@ else { if(fclose(f) != 0) printf("EditInstrument::checkDirty: Error closing file\n"); - + if(fileSave(i, i->filePath())) i->setDirty(false); } @@ -3287,7 +3507,7 @@ { QString s; if(val == MusECore::CTRL_VAL_UNKNOWN) - s = "---"; + s = QString("---"); else { int hb = ((val >> 16) & 0xff) + 1; @@ -3299,11 +3519,11 @@ int pr = (val & 0xff) + 1; if (pr == 0x100) pr = 0; - s.sprintf("%d-%d-%d", hb, lb, pr); - } - + s = QString("%1-%2-%3").arg(hb).arg(lb).arg(pr); + } + return s; -} +} //--------------------------------------------------------- // enableDefaultControls @@ -3322,6 +3542,18 @@ defPatchH->setEnabled(enPatch); defPatchL->setEnabled(enPatch); defPatchProg->setEnabled(enPatch); + + spinBoxDrumDefault->setEnabled(enVal); + drumPatchButton->setEnabled(enPatch); + if(!enPatch) + { + drumPatchButton->blockSignals(true); + drumPatchButton->setText("---"); + drumPatchButton->blockSignals(false); + } + defDrumPatchH->setEnabled(enPatch); + defDrumPatchL->setEnabled(enPatch); + defDrumPatchProg->setEnabled(enPatch); } //--------------------------------------------------------- @@ -3389,10 +3621,16 @@ defPatchL->setEnabled(false); defPatchProg->setEnabled(false); + spinBoxDrumDefault->setEnabled(false); + drumPatchButton->setEnabled(false); + defDrumPatchH->setEnabled(false); + defDrumPatchL->setEnabled(false); + defDrumPatchProg->setEnabled(false); + spinBoxMin->setEnabled(false); spinBoxMax->setEnabled(false); } - + ctrlShowInMidi->setEnabled(v); ctrlShowInDrum->setEnabled(v); @@ -3406,16 +3644,27 @@ void EditInstrument::setDefaultPatchName(int val) { patchButton->blockSignals(true); - patchButton->setText(getPatchName(val)); + patchButton->setText(getPatchName(val, false)); // false = non-drums. patchButton->blockSignals(false); } //--------------------------------------------------------- +// setDefaultPatchName +//--------------------------------------------------------- + +void EditInstrument::setDefaultDrumPatchName(int val) +{ + drumPatchButton->blockSignals(true); + drumPatchButton->setText(getPatchName(val, true)); // true = drums. + drumPatchButton->blockSignals(false); +} + +//--------------------------------------------------------- // getDefaultPatchNumber //--------------------------------------------------------- int EditInstrument::getDefaultPatchNumber() -{ +{ int hval = defPatchH->value() - 1; int lval = defPatchL->value() - 1; int prog = defPatchProg->value() - 1; @@ -3425,10 +3674,48 @@ lval = 0xff; if(prog == -1) prog = 0xff; - - return ((hval & 0xff) << 16) + ((lval & 0xff) << 8) + (prog & 0xff); + + return ((hval & 0xff) << 16) + ((lval & 0xff) << 8) + (prog & 0xff); +} + +//--------------------------------------------------------- +// getDefaultDrumPatchNumber +//--------------------------------------------------------- + +int EditInstrument::getDefaultDrumPatchNumber() +{ + int hval = defDrumPatchH->value() - 1; + int lval = defDrumPatchL->value() - 1; + int prog = defDrumPatchProg->value() - 1; + if(hval == -1) + hval = 0xff; + if(lval == -1) + lval = 0xff; + if(prog == -1) + prog = 0xff; + + return ((hval & 0xff) << 16) + ((lval & 0xff) << 8) + (prog & 0xff); +} + +//--------------------------------------------------------- +// getDrummapCollectionPatchNumber +//--------------------------------------------------------- + +int EditInstrument::getDrummapCollectionPatchNumber() +{ + int hval = hbankFromBox->value() - 1; + int lval = lbankFromBox->value() - 1; + int prog = patchFromBox->value() - 1; + if(hval == -1) + hval = 0xff; + if(lval == -1) + lval = 0xff; + if(prog == -1) + prog = 0xff; + + return ((hval & 0xff) << 16) + ((lval & 0xff) << 8) + (prog & 0xff); } - + //--------------------------------------------------------- // setDefaultPatchNumbers //--------------------------------------------------------- @@ -3438,7 +3725,7 @@ int hb; int lb; int pr; - + if(val == MusECore::CTRL_VAL_UNKNOWN) hb = lb = pr = 0; else @@ -3452,13 +3739,13 @@ pr = (val & 0xff) + 1; if (pr == 0x100) pr = 0; - } - + } + defPatchH->blockSignals(true); defPatchL->blockSignals(true); defPatchProg->blockSignals(true); - defPatchH->setValue(hb); - defPatchL->setValue(lb); + defPatchH->setValue(hb); + defPatchL->setValue(lb); defPatchProg->setValue(pr); defPatchH->blockSignals(false); defPatchL->blockSignals(false); @@ -3466,6 +3753,42 @@ } //--------------------------------------------------------- +// setDefaultDrumPatchNumbers +//--------------------------------------------------------- + +void EditInstrument::setDefaultDrumPatchNumbers(int val) +{ + int hb; + int lb; + int pr; + + if(val == MusECore::CTRL_VAL_UNKNOWN) + hb = lb = pr = 0; + else + { + hb = ((val >> 16) & 0xff) + 1; + if (hb == 0x100) + hb = 0; + lb = ((val >> 8) & 0xff) + 1; + if (lb == 0x100) + lb = 0; + pr = (val & 0xff) + 1; + if (pr == 0x100) + pr = 0; + } + + defDrumPatchH->blockSignals(true); + defDrumPatchL->blockSignals(true); + defDrumPatchProg->blockSignals(true); + defDrumPatchH->setValue(hb); + defDrumPatchL->setValue(lb); + defDrumPatchProg->setValue(pr); + defDrumPatchH->blockSignals(false); + defDrumPatchL->blockSignals(false); + defDrumPatchProg->blockSignals(false); +} + +//--------------------------------------------------------- // setDefaultPatchControls //--------------------------------------------------------- @@ -3476,37 +3799,266 @@ } //--------------------------------------------------------- +// setDefaultDrumPatchControls +//--------------------------------------------------------- + +void EditInstrument::setDefaultDrumPatchControls(int val) +{ + setDefaultDrumPatchNumbers(val); + setDefaultDrumPatchName(val); +} + +//--------------------------------------------------------- // getPatchName //--------------------------------------------------------- -QString EditInstrument::getPatchName(int prog) +QString EditInstrument::getPatchName(int prog, bool drum, bool includeDefault) { - int pr = prog & 0xff; - if(prog == MusECore::CTRL_VAL_UNKNOWN || pr == 0xff) - return "---"; - - int hbank = (prog >> 16) & 0xff; - int lbank = (prog >> 8) & 0xff; - - MusECore::PatchGroupList* pg = workingInstrument.groups(); - - for(MusECore::ciPatchGroup i = pg->begin(); i != pg->end(); ++i) { - const MusECore::PatchList& pl = (*i)->patches; - for (MusECore::ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { - const MusECore::Patch* mp = *ipl; - if (//(mp->typ & tmask) && DELETETHIS - (pr == mp->prog) - //&& ((drum && mode != MT_GM) || DELETETHIS - // (mp->drum == drumchan)) - - //&& (hbank == mp->hbank || !hb || mp->hbank == -1) - //&& (lbank == mp->lbank || !lb || mp->lbank == -1)) - && (hbank == mp->hbank || mp->hbank == -1) - && (lbank == mp->lbank || mp->lbank == -1)) - return mp->name; - } - } - return "---"; + if(MusECore::Patch* p = workingInstrument->groups()->findPatch(prog, drum, includeDefault)) + return p->name; + return "---"; +} + +QMenu* EditInstrument::createPopupPatchList(bool drum) +{ + QMenu* patchpopup = new QMenu; + + MusECore::PatchGroupList* pg = workingInstrument->groups(); + + if (pg->size() > 1) { + for (MusECore::ciPatchGroup i = pg->begin(); i != pg->end(); ++i) { + MusECore::PatchGroup* pgp = *i; + QMenu* pm = 0; + const MusECore::PatchList& pl = pgp->patches; + for (MusECore::ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { + const MusECore::Patch* mp = *ipl; + if(mp->drum != drum) + continue; + if(!pm) { + pm = new QMenu(pgp->name, patchpopup); + patchpopup->addMenu(pm); + pm->setFont(MusEGlobal::config.fonts[0]); + } + int id = ((mp->hbank & 0xff) << 16) + + ((mp->lbank & 0xff) << 8) + (mp->program & 0xff); + QAction *ac1 = pm->addAction(mp->name); + ac1->setData(id); + } + } + } + else if (pg->size() == 1 ){ + // no groups + const MusECore::PatchList& pl = pg->front()->patches; + for (MusECore::ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { + const MusECore::Patch* mp = *ipl; + if(mp->drum != drum) + continue; + int id = ((mp->hbank & 0xff) << 16) + + ((mp->lbank & 0xff) << 8) + (mp->program & 0xff); + QAction *ac2 = patchpopup->addAction(mp->name); + ac2->setData(id); + } + } + + if(patchpopup->actions().count() == 0) + { + delete patchpopup; + return NULL; + } + + return patchpopup; +} + +void EditInstrument::popupDrummapPatchList() +{ + QMenu* patchpopup = createPopupPatchList(true); + if(!patchpopup) + return; + + QAction* act = patchpopup->exec(drummapsPatchNames->mapToGlobal(QPoint(10,5))); + if(!act) + { + delete patchpopup; + return; + } + + bool ok; + int rv = act->data().toInt(&ok); + delete patchpopup; + + if(!ok || rv == -1) + return; + + int hb = (rv >> 16) & 0xff; + int lb = (rv >> 8) & 0xff; + int pr = rv & 0xff; + + patchFromBox->blockSignals(true); + lbankFromBox->blockSignals(true); + hbankFromBox->blockSignals(true); + + if(hb == 0xff) + hbankFromBox->setValue(0); + else + hbankFromBox->setValue(hb + 1); + + if(lb == 0xff) + lbankFromBox->setValue(0); + else + lbankFromBox->setValue(lb + 1); + + if(pr == 0xff) + patchFromBox->setValue(0); + else + patchFromBox->setValue(pr + 1); + + patchFromBox->blockSignals(false); + lbankFromBox->blockSignals(false); + hbankFromBox->blockSignals(false); + + storePatchCollection(); +} + +void EditInstrument::popupControllerDefaultPatchList(bool drum) +{ + QMenu* patchpopup = createPopupPatchList(drum); + if(!patchpopup) + return; + + QAction* act = patchpopup->exec((drum ? drumPatchButton : patchButton)->mapToGlobal(QPoint(10,5))); + if(!act) + { + delete patchpopup; + return; + } + + bool ok; + int rv = act->data().toInt(&ok); + delete patchpopup; + + if(!ok || rv == -1) + return; + + if(drum) + setDefaultDrumPatchControls(rv); + else + setDefaultPatchControls(rv); + + QTreeWidgetItem* item = viewController->currentItem(); + + if(item) + { + MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); + if(drum) + { + c->setDrumInitVal(rv); + item->setText(COL_DRUM_DEF, getPatchItemText(rv)); + } + else + { + c->setInitVal(rv); + item->setText(COL_DEF, getPatchItemText(rv)); + } + } + workingInstrument->setDirty(true); +} + +//--------------------------------------------------------- +// populateInitEventList +//--------------------------------------------------------- + +void EditInstrument::populateInitEventList() +{ + initEventList->blockSignals(true); + initEventList->clear(); + MusECore::EventList* el = workingInstrument->midiInit(); + for(MusECore::iEvent ie = el->begin(); ie != el->end(); ++ie) + { + InitListItem* item = new InitListItem(initEventList, ie->second, workingInstrument); + initEventList->addTopLevelItem(item); + } + if(initEventList->topLevelItem(0)) + initEventList->setCurrentItem(initEventList->topLevelItem(0)); + initEventList->blockSignals(false); +} + +//--------------------------------------------------------- +// initListChangeClicked +//--------------------------------------------------------- + +void EditInstrument::initListChangeClicked() +{ + InitListItem* item = static_cast(initEventList->currentItem()); + if(!item) + return; + editInitListItem(item); +} + +//--------------------------------------------------------- +// editInitListItem +//--------------------------------------------------------- + +void EditInstrument::editInitListItem(QTreeWidgetItem* item) +{ + InitListItem* ev = (InitListItem*)item; + if(ev->_event.type() != MusECore::Sysex) + return; + int tick = ev->_event.tick(); + MusECore::Event nevent = EditSysexDialog::getEvent(tick, ev->_event, this, workingInstrument); + if(!nevent.empty()) + { + MusECore::EventList* el = workingInstrument->midiInit(); + MusECore::iEvent ie = el->find(ev->_event); + if(ie != el->end()) + el->erase(ie); + el->add(nevent); + //delete item; + //item = new InitListItem(initEventList, nevent, &workingInstrument); + //initEventList->addTopLevelItem(item); + //initEventList->setCurrentItem(item); + populateInitEventList(); + workingInstrument->setDirty(true); + } +} + +//--------------------------------------------------------- +// initListAddClicked +//--------------------------------------------------------- + +void EditInstrument::initListAddClicked() +{ + //MusECore::Event event = EditSysexDialog::getEvent(curPart->tick(), MusECore::Event(), this); + // TODO Get current item tick, if any + MusECore::Event event = EditSysexDialog::getEvent(0, MusECore::Event(), this, workingInstrument); + if(!event.empty()) + { + workingInstrument->midiInit()->add(event); + //InitListItem* item = new InitListItem(initEventList, event, &workingInstrument); + //initEventList->addTopLevelItem(item); + //initEventList->setCurrentItem(item); + populateInitEventList(); + workingInstrument->setDirty(true); + } +} + +//--------------------------------------------------------- +// initListDeleteClicked +//--------------------------------------------------------- + +void EditInstrument::initListDeleteClicked() +{ + InitListItem* item = static_cast(initEventList->currentItem()); + if(!item) + return; + MusECore::EventList* el = workingInstrument->midiInit(); + MusECore::iEvent ie = el->find(item->_event); + if(ie != el->end()) + { + el->erase(ie); + populateInitEventList(); + } + //delete item; + workingInstrument->setDirty(true); } } // namespace MusEGui diff -Nru muse-2.1.2/muse/instruments/editinstrument.h muse-3.0.2+ds1/muse/instruments/editinstrument.h --- muse-2.1.2/muse/instruments/editinstrument.h 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/instruments/editinstrument.h 2017-12-04 21:01:18.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: editinstrument.h,v 1.1.1.1.2.4 2009/05/31 05:12:12 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012, 2016 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 @@ -26,8 +26,8 @@ #define __EDITINSTRUMENT_H__ #include "ui_editinstrumentbase.h" -#include "minstrument.h" -#include "midictrl.h" + +#include "globaldefs.h" class QDialog; class QMenu; @@ -37,6 +37,15 @@ class QString; class QAction; +namespace MusECore { + +class MidiInstrument; +class MidiController; +struct Patch; +struct PatchGroup; +struct SysEx; +} + namespace MusEGui { class Header; @@ -49,7 +58,7 @@ class EditInstrument : public QMainWindow, public Ui::EditInstrumentBase { Q_OBJECT - MusECore::MidiInstrument workingInstrument; + MusECore::MidiInstrument* workingInstrument; QListWidgetItem* oldMidiInstrument; QTreeWidgetItem* oldPatchItem; @@ -59,8 +68,8 @@ QGridLayout* dlist_grid; QStringListModel* patch_coll_model; - - + void setHeaderToolTips(); + void setHeaderWhatsThis(); void closeEvent(QCloseEvent*); int checkDirty(MusECore::MidiInstrument*, bool isClose = false); bool fileSave(MusECore::MidiInstrument*, const QString&); @@ -70,17 +79,26 @@ void updatePatchGroup(MusECore::MidiInstrument*, MusECore::PatchGroup*); void updateSysex(MusECore::MidiInstrument*, MusECore::SysEx*); void changeInstrument(); + void populateInitEventList(); QTreeWidgetItem* addControllerToView(MusECore::MidiController* mctrl); QString getPatchItemText(int); void enableDefaultControls(bool, bool); void enableNonCtrlControls(bool); void setDefaultPatchName(int); + void setDefaultDrumPatchName(int); int getDefaultPatchNumber(); + int getDefaultDrumPatchNumber(); + int getDrummapCollectionPatchNumber(); void setDefaultPatchNumbers(int); + void setDefaultDrumPatchNumbers(int); void setDefaultPatchControls(int); - QString getPatchName(int); + void setDefaultDrumPatchControls(int); + QString getPatchName(int prog, bool drum = false, bool includeDefault = true); + QMenu* createPopupPatchList(bool drum = false); + void popupDrummapPatchList(); + void popupControllerDefaultPatchList(bool drum = false); void deleteInstrument(QListWidgetItem*); - + private slots: virtual void fileNew(); virtual void fileOpen(); @@ -89,7 +107,8 @@ virtual void fileClose(); virtual void helpWhatsThis(); void instrumentChanged(); - void tabChanged(QWidget*); + void noteOffModeChanged(int); + void tabChanged(int); void patchChanged(); void controllerChanged(); void instrumentNameReturn(); @@ -98,7 +117,10 @@ void newPatchClicked(); void newGroupClicked(); void patchButtonClicked(); + void drumPatchButtonClicked(); + void drummapCollectionPatchButtonClicked(); void defPatchChanged(int); + void defDrumPatchChanged(int); void deleteControllerClicked(); void newControllerClicked(); void addControllerClicked(); @@ -109,16 +131,18 @@ void ctrlMinChanged(int); void ctrlMaxChanged(int); void ctrlDefaultChanged(int); + void ctrlDrumDefaultChanged(int); void ctrlShowInMidiChanged(int); void ctrlShowInDrumChanged(int); void sysexChanged(QListWidgetItem*, QListWidgetItem*); void deleteSysexClicked(); void newSysexClicked(); - void ctrlNullParamHChanged(int); - void ctrlNullParamLChanged(int); + void editInitListItem(QTreeWidgetItem* item); + void initListDeleteClicked(); + void initListAddClicked(); + void initListChangeClicked(); void patchCollectionSpinboxChanged(int); - void patchCollectionCheckboxChanged(bool); void patchActivated(const QModelIndex&); void addPatchCollection(); void delPatchCollection(); @@ -130,11 +154,10 @@ void fetchPatchCollection(); public: - enum TabType { Patches=0, DrumMaps=1, Controllers=2, Sysex=3 }; - - EditInstrument(QWidget* parent = 0, Qt::WFlags fl = Qt::Window); + EditInstrument(QWidget* parent = 0, Qt::WindowFlags fl = Qt::Window); + virtual ~EditInstrument(); void findInstrument(const QString& find_instrument); - void showTab(TabType); + void showTab(EditInstrumentTabType); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/instruments/midictrledit.cpp muse-3.0.2+ds1/muse/instruments/midictrledit.cpp --- muse-2.1.2/muse/instruments/midictrledit.cpp 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/instruments/midictrledit.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -102,7 +102,7 @@ // MidiControllerEditDialog //--------------------------------------------------------- -MidiControllerEditDialog::MidiControllerEditDialog(QWidget* parent, const char* name, bool modal, Qt::WFlags fl) +MidiControllerEditDialog::MidiControllerEditDialog(QWidget* parent, const char* name, bool modal, Qt::WindowFlags fl) : MidiControllerEditDialogBase(parent, name, modal, fl) { _lastPort = midiPortsList->currentItem(); @@ -178,9 +178,7 @@ for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* port = &midiPorts[i]; MidiDevice* dev = port->device(); - QString name; - name.sprintf("%d(%s)", port->portno()+1, - dev ? dev->name().toLatin1() : "none"); + QString name = QString("%1(%2)").arg(port->portno() + 1).arg(dev ? dev->name() : QString("none")); midiPortsList->insertItem(name, i); } _lastPort = midiPortsList->currentItem(); diff -Nru muse-2.1.2/muse/instruments/midictrledit.h muse-3.0.2+ds1/muse/instruments/midictrledit.h --- muse-2.1.2/muse/instruments/midictrledit.h 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/instruments/midictrledit.h 2017-12-04 21:01:18.000000000 +0000 @@ -63,7 +63,7 @@ void songChanged(MusECore::SongChangedFlags_t); public: - MidiControllerEditDialog(QWidget* parent = 0, const char* name = 0, bool modal = false, Qt::WFlags fl = 0); + MidiControllerEditDialog(QWidget* parent = 0, const char* name = 0, bool modal = false, Qt::WindowFlags fl = 0); }; extern MidiControllerEditDialog* midiControllerEditDialog; diff -Nru muse-2.1.2/muse/instruments/minstrument.cpp muse-3.0.2+ds1/muse/instruments/minstrument.cpp --- muse-2.1.2/muse/instruments/minstrument.cpp 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/instruments/minstrument.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: minstrument.cpp,v 1.10.2.5 2009/03/28 01:46:10 terminator356 Exp $ // // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -28,6 +29,7 @@ #include #include #include +#include #include "minstrument.h" #include "midiport.h" @@ -44,6 +46,15 @@ #include "drummap.h" #include "helper.h" +#ifdef _USE_INSTRUMENT_OVERRIDES_ +namespace MusEGlobal { + // This list holds instrument drum map overrides read from config. + // Whenever an instrument has been loaded it will adopt any corresponding item in this list. + // (Instruments are loaded long after config is loaded. So we need this 'holding' list.) + MusECore::WorkingDrumMapInstrumentList workingDrumMapInstrumentList; +} +#endif + namespace MusECore { MidiInstrumentList midiInstruments; @@ -56,7 +67,8 @@ int string2sysex(const QString& s, unsigned char** data) { - const char* src = s.toLatin1().constData(); + QByteArray ba = s.toLatin1(); + const char* src = ba.constData(); char buffer[2048]; char* dst = buffer; @@ -65,7 +77,7 @@ while (*src == ' ' || *src == '\n') { ++src; } - if(!(*src)) + if(!(*src)) break; char* ep; long val = strtol(src, &ep, 16); @@ -74,6 +86,9 @@ return -1; } src = ep; + // Strip all f0 and f7 (whether accidental or on purpose enclosing etc). + if(val == MusECore::ME_SYSEX || val == MusECore::ME_SYSEX_END) + continue; *dst++ = val; if (dst - buffer >= 2048) { printf("string2sysex: Hex String too long (2048 bytes limit)\n"); @@ -81,6 +96,7 @@ } } } + int len = dst - buffer; if(len > 0) { @@ -90,7 +106,7 @@ } else *data = 0; - + return len; } @@ -101,14 +117,16 @@ QString sysex2string(int len, unsigned char* data) { QString d; - QString s; for (int i = 0; i < len; ++i) { if ((i > 0) && ((i % 8)==0)) { - d += "\n"; + d += QString("\n"); } else if (i) - d += " "; - d += s.sprintf("%02x", data[i]); + d += QString(" "); + // Strip all f0 and f7 (whether accidental or on purpose enclosing etc). + if(data[i] == MusECore::ME_SYSEX || data[i] == MusECore::ME_SYSEX_END) + continue; + d += QString("%1").arg(data[i], 2, 16, QLatin1Char('0')); } return d; } @@ -154,13 +172,13 @@ static void loadIDF(QFileInfo* fi) { - FILE* f = fopen(fi->filePath().toAscii().constData(), "r"); + FILE* f = fopen(fi->filePath().toLatin1().constData(), "r"); if (f == 0) return; if (MusEGlobal::debugMsg) printf("READ IDF %s\n", fi->filePath().toLatin1().constData()); Xml xml(f); - + bool skipmode = true; for (;;) { Xml::Token token = xml.parse(); @@ -178,7 +196,7 @@ MidiInstrument* i = new MidiInstrument(); i->setFilePath(fi->filePath()); i->read(xml); - // Ignore duplicate named instruments. + // Ignore duplicate named instruments. iMidiInstrument ii = midiInstruments.begin(); for(; ii != midiInstruments.end(); ++ii) { @@ -186,9 +204,37 @@ break; } if(ii == midiInstruments.end()) + { + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + // Add in the drum map overrides that were found in config. + // They can only be added now that the instrument has been loaded. + ciWorkingDrumMapInstrumentList_t iwdmil = + MusEGlobal::workingDrumMapInstrumentList.find(i->iname().toStdString()); + if(iwdmil != MusEGlobal::workingDrumMapInstrumentList.end()) + { + const WorkingDrumMapPatchList& wdmil = iwdmil->second; + patch_drummap_mapping_list_t* pdml = i->get_patch_drummap_mapping(); + int patch; + for(ciWorkingDrumMapPatchList_t iwdmpl = wdmil.begin(); iwdmpl != wdmil.end(); ++iwdmpl) + { + patch = iwdmpl->first; + iPatchDrummapMapping_t ipdm = pdml->find(patch, false); // No default. + if(ipdm != pdml->end()) + { + patch_drummap_mapping_t& pdm = *ipdm; + const WorkingDrumMapList& wdml = iwdmpl->second; + pdm._workingDrumMapList = wdml; + } + } + // TODO: Done with the config override, so erase it? Hm, maybe we might need it later... + //MusEGlobal::workingDrumMapInstrumentList.erase(iwdmil); + } +#endif midiInstruments.push_back(i); + } else - delete i; + delete i; } else xml.unknown("muse"); @@ -204,8 +250,8 @@ } } fclose(f); - - + + } //--------------------------------------------------------- @@ -216,6 +262,36 @@ { genericMidiInstrument = new MidiInstrument(QWidget::tr("generic midi")); midiInstruments.push_back(genericMidiInstrument); + + // Initialize with a default drum map on default channel. Patch is default 0xffffff. GM-1 does not specify a drum patch number. + ChannelDrumMappingList* cdml = genericMidiInstrument->getChannelDrumMapping(); + cdml->add(-1, patch_drummap_mapping_list_t()); + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + // Add in the drum map overrides that were found in config. + // They can only be added now that the instrument has been created. + ciWorkingDrumMapInstrumentList_t iwdmil = + MusEGlobal::workingDrumMapInstrumentList.find(genericMidiInstrument->iname().toStdString()); + if(iwdmil != MusEGlobal::workingDrumMapInstrumentList.end()) + { + const WorkingDrumMapPatchList& wdmil = iwdmil->second; + int patch; + for(ciWorkingDrumMapPatchList_t iwdmpl = wdmil.begin(); iwdmpl != wdmil.end(); ++iwdmpl) + { + patch = iwdmpl->first; + iPatchDrummapMapping_t ipdm = pdml->find(patch, false); // No default. + if(ipdm != pdml->end()) + { + patch_drummap_mapping_t& pdm = *ipdm; + const WorkingDrumMapList& wdml = iwdmpl->second; + pdm._workingDrumMapList = wdml; + } + } + // TODO: Done with the config override, so erase it? Hm, maybe we might need it later... + //MusEGlobal::workingDrumMapInstrumentList.erase(iwdmil); + } +#endif + if (MusEGlobal::debugMsg) printf("load user instrument definitions from <%s>\n", MusEGlobal::museUserInstruments.toLatin1().constData()); QDir usrInstrumentsDir(MusEGlobal::museUserInstruments, QString("*.idf")); @@ -227,7 +303,7 @@ ++it; } } - + if (MusEGlobal::debugMsg) printf("load instrument definitions from <%s>\n", MusEGlobal::museInstruments.toLatin1().constData()); QDir instrumentsDir(MusEGlobal::museInstruments, QString("*.idf")); @@ -241,7 +317,7 @@ } else printf("Instrument directory not found: %s\n", MusEGlobal::museInstruments.toLatin1().constData()); - + } //--------------------------------------------------------- @@ -285,14 +361,42 @@ } //--------------------------------------------------------- +// findMidiInstrument +//--------------------------------------------------------- + +iMidiInstrument MidiInstrumentList::find(const MidiInstrument* instr) + { + for (iMidiInstrument i = begin(); + i != end(); ++i) { + if (*i == instr) { + return i; + } + } + return end(); + } + +#ifdef _USE_INSTRUMENT_OVERRIDES_ +void MidiInstrumentList::writeDrummapOverrides(int level, Xml& xml) const +{ + MidiInstrument* mi; + for(ciMidiInstrument imi = begin(); imi != end(); ++imi) + { + mi = *imi; + mi->writeDrummapOverrides(level, xml); + } +} +#endif + +//--------------------------------------------------------- // MidiInstrument //--------------------------------------------------------- void MidiInstrument::init() { + _noteOffMode = NoteOffAll; // By default, use note offs. _tmpMidiStateVersion = 1; // Assume old version. readMidiState will overwrite anyway. - _nullvalue = -1; _initScript = 0; + _waitForLSB = true; _midiInit = new EventList(); _midiReset = new EventList(); _midiState = new EventList(); @@ -301,10 +405,10 @@ // add some default controller to controller list // this controllers are always available for all instruments // - MidiController* prog = new MidiController("Program", CTRL_PROGRAM, 0, 0xffffff, 0); + MidiController* prog = new MidiController("Program", CTRL_PROGRAM, 0, 0xffffff, 0, 0); _controller->add(prog); _dirty = false; - + } MidiInstrument::MidiInstrument() @@ -328,36 +432,36 @@ MidiInstrument::~MidiInstrument() { - for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) + for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) { PatchGroup* pgp = *g; const PatchList& pl = pgp->patches; for (ciPatch p = pl.begin(); p != pl.end(); ++p) - { + { delete *p; } - delete pgp; + delete pgp; } - - + + delete _midiInit; delete _midiReset; delete _midiState; - for(iMidiController i = _controller->begin(); i != _controller->end(); ++i) + for(iMidiController i = _controller->begin(); i != _controller->end(); ++i) delete i->second; delete _controller; - + if (_initScript) delete _initScript; - + if(!_sysex.isEmpty()) { int j = _sysex.size(); for(int i = 0; i < j; ++i) delete _sysex.at(i); } - - patch_drummap_mapping.clear(); + + _channelDrumMapping.clear(); } //--------------------------------------------------------- @@ -369,20 +473,21 @@ //--------------------------------------------------------- // TODO: Copy the _initScript (if and when it is ever used) //--------------------------------------------------------- - - for(iMidiController i = _controller->begin(); i != _controller->end(); ++i) + + for(iMidiController i = _controller->begin(); i != _controller->end(); ++i) delete i->second; - _controller->clear(); - - _nullvalue = ins._nullvalue; + _controller->clr(); + _waitForLSB = ins._waitForLSB; + _noteOffMode = ins._noteOffMode; + // Assignment for(ciMidiController i = ins._controller->begin(); i != ins._controller->end(); ++i) { MidiController* mc = i->second; _controller->add(new MidiController(*mc)); - } - + } + if(!_sysex.isEmpty()) { int j = _sysex.size(); @@ -396,12 +501,12 @@ for(int i = 0; i < j; ++i) _sysex.append(new MusECore::SysEx(*(ins.sysex().at(i)))); } - + *(_midiInit) = *(ins._midiInit); *(_midiReset) = *(ins._midiReset); *(_midiState) = *(ins._midiState); - - for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) + + for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) { PatchGroup* pgp = *g; const PatchList& pl = pgp->patches; @@ -409,13 +514,13 @@ { delete *p; } - - delete pgp; + + delete pgp; } pg.clear(); - + // Assignment - for(ciPatchGroup g = ins.pg.begin(); g != ins.pg.end(); ++g) + for(ciPatchGroup g = ins.pg.begin(); g != ins.pg.end(); ++g) { PatchGroup* pgp = *g; const PatchList& pl = pgp->patches; @@ -426,26 +531,26 @@ { Patch* pp = *p; Patch* np = new Patch; - //np->typ = pp->typ; + //np->typ = pp->typ; np->hbank = pp->hbank; np->lbank = pp->lbank; - np->prog = pp->prog; + np->program = pp->program; np->name = pp->name; np->drum = pp->drum; npg->patches.push_back(np); - } + } } - + _name = ins._name; _filePath = ins._filePath; - - patch_drummap_mapping=ins.patch_drummap_mapping; - + + _channelDrumMapping = ins._channelDrumMapping; + // Hmm, dirty, yes? But init sets it to false... DELETETHIS //_dirty = ins._dirty; //_dirty = false; //_dirty = true; - + return *this; } @@ -457,6 +562,8 @@ { if(_name == "GM") return MT_GM; + if(_name == "GM2") + return MT_GM2; if(_name == "GS") return MT_GS; if(_name == "XG") @@ -467,28 +574,28 @@ //--------------------------------------------------------- // reset // send note off to all channels +// To be called by audio thread only. //--------------------------------------------------------- void MidiInstrument::reset(int portNo) { MusECore::MidiPort* port = &MusEGlobal::midiPorts[portNo]; - if(port->device() == 0) // p4.0.15 + if(port->device() == 0) return; MusECore::MidiPlayEvent ev; - ev.setType(0x90); + ev.setType(ME_NOTEOFF); ev.setPort(portNo); - ev.setTime(0); // p4.0.15 - - for (int chan = 0; chan < MIDI_CHANNELS; ++chan) + ev.setTime(0); // Immediate processing. TODO: Use curFrame? + ev.setB(64); + + for (int chan = 0; chan < MIDI_CHANNELS; ++chan) { ev.setChannel(chan); - for (int pitch = 0; pitch < 128; ++pitch) + for (int pitch = 0; pitch < 128; ++pitch) { ev.setA(pitch); - ev.setB(0); - - port->sendEvent(ev); + port->device()->putEvent(ev, MidiDevice::NotLate); } } } @@ -534,10 +641,9 @@ void Patch::read(Xml& xml) { - //typ = -1; hbank = -1; lbank = -1; - prog = 0; + program = -1; drum = false; for (;;) { Xml::Token token = xml.parse(); @@ -554,15 +660,14 @@ name = xml.s2(); else if (tag == "mode") // Obsolete { - //typ = xml.s2().toInt(); - xml.s2().toInt(); + xml.s2().toInt(); } else if (tag == "hbank") hbank = xml.s2().toInt(); else if (tag == "lbank") lbank = xml.s2().toInt(); else if (tag == "prog") - prog = xml.s2().toInt(); + program = xml.s2().toInt(); else if (tag == "drum") drum = xml.s2().toInt(); break; @@ -582,22 +687,84 @@ void Patch::write(int level, Xml& xml) { xml.nput(level, ""); } +iPatch PatchList::find(int patch, bool drum, bool includeDefault) +{ + int pnum; + Patch* p; + iPatch ip_default = end(); + for(iPatch ip = begin(); ip != end(); ++ip) + { + p = *ip; + pnum = p->patch(); + // Look for an exact match above all else. The given patch must be valid. + if(patch != CTRL_VAL_UNKNOWN && pnum == patch && p->drum == drum) + return ip; + // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). + if(includeDefault && p->dontCare() && p->drum == drum && ip_default == end()) + ip_default = ip; + } + return ip_default; +} + +ciPatch PatchList::find(int patch, bool drum, bool includeDefault) const +{ + int pnum; + const Patch* p; + ciPatch ip_default = end(); + for(ciPatch ip = begin(); ip != end(); ++ip) + { + p = *ip; + pnum = p->patch(); + // Look for an exact match above all else. The given patch must be valid. + if(patch != CTRL_VAL_UNKNOWN && pnum == patch && p->drum == drum) + return ip; + // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). + if(includeDefault && p->dontCare() && p->drum == drum && ip_default == end()) + ip_default = ip; + } + return ip_default; +} + +Patch* PatchGroupList::findPatch(int patch, bool drum, bool includeDefault) +{ + for(iPatchGroup ipg = begin(); ipg != end(); ++ipg) + { + PatchGroup* pg = *ipg; + iPatch ip = pg->patches.find(patch, drum, includeDefault); + if(ip != pg->patches.end()) + return *ip; + } + return 0; +} + +Patch* PatchGroupList::findPatch(int patch, bool drum, bool includeDefault) const +{ + for(ciPatchGroup ipg = begin(); ipg != end(); ++ipg) + { + const PatchGroup* pg = *ipg; + ciPatch ip = pg->patches.find(patch, drum, includeDefault); + if(ip != pg->patches.end()) + return *ip; + } + return 0; +} + + //--------------------------------------------------------- // SysEx //--------------------------------------------------------- @@ -637,13 +804,13 @@ case Xml::End: return false; case Xml::TagStart: - if (tag == "comment") + if (tag == "comment") comment = xml.parse1(); - else if (tag == "data") + else if (tag == "data") { unsigned char*d; int len = string2sysex(xml.parse1(), &d); - // Was the conversion succesful, even if empty? + // Was the conversion successful, even if empty? if(len != -1) { // Delete existing. @@ -669,20 +836,20 @@ break; } } - - return false; + + return false; } void SysEx::write(int level, Xml& xml) { xml.nput(level, "\n", Xml::xmlString(name).toLatin1().constData()); - + level++; if(!comment.isEmpty()) xml.strTag(level, "comment", Xml::xmlString(comment).toLatin1().constData()); if(dataLen > 0 && data) xml.strTag(level, "data", sysex2string(dataLen, data)); - + xml.etag(level, "SysEx"); } @@ -692,19 +859,19 @@ void MidiInstrument::readMidiState(Xml& xml) { - // p4.0.27 A kludge to support old midistates by wrapping them in the proper header. + // A kludge to support old midistates by wrapping them in the proper header. _tmpMidiStateVersion = 1; // Assume old (unmarked) first version 1. - for (;;) + for (;;) { Xml::Token token = xml.parse(); const QString tag = xml.s1(); - switch (token) + switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: - if (tag == "event") + if (tag == "event") { Event e(Note); e.read(xml); @@ -714,13 +881,13 @@ xml.unknown("midistate"); break; case Xml::Attribut: - if(tag == "version") + if(tag == "version") _tmpMidiStateVersion = xml.s2().toInt(); else xml.unknown("MidiInstrument"); break; case Xml::TagEnd: - if(tag == "midistate") + if(tag == "midistate") return; default: break; @@ -730,8 +897,8 @@ void MidiInstrument::readDrummaps(Xml& xml) { - patch_drummap_mapping.clear(); - + //_channelDrumMapping.clear(); // ??? + const QString start_tag = xml.s1(); for (;;) { Xml::Token token = xml.parse(); @@ -741,16 +908,23 @@ case Xml::Error: case Xml::End: return; - + case Xml::TagStart: - if (tag == "entry") - patch_drummap_mapping.push_back(readDrummapsEntry(xml)); + if (tag == "drumMapChannel") + _channelDrumMapping.read(xml); + else if (tag == "entry") + { + patch_drummap_mapping_list_t pdml; + pdml.read(xml); + if(!pdml.empty()) + _channelDrumMapping.add(-1, pdml); // Add to the default channel. + } else xml.unknown("MidiInstrument::readDrummaps"); break; case Xml::TagEnd: - if (tag == "Drummaps") + if (tag == start_tag) return; default: @@ -761,124 +935,12 @@ " not returning anything. expect undefined behaviour or even crashes.\n"); } -patch_drummap_mapping_t MidiInstrument::readDrummapsEntry(Xml& xml) -{ - using std::list; - - patch_collection_t collection; - DrumMap* drummap=new DrumMap[128]; - for (int i=0;i<128;i++) - drummap[i]=iNewDrumMap[i]; - - for (;;) - { - Xml::Token token = xml.parse(); - const QString& tag = xml.s1(); - switch (token) - { - case Xml::Error: - case Xml::End: - return patch_drummap_mapping_t(collection, drummap); - - case Xml::TagStart: - if (tag == "patch_collection") - collection=readDrummapsEntryPatchCollection(xml); - else if (tag == "drummap") - read_new_style_drummap(xml, "drummap", drummap); - else - xml.unknown("MidiInstrument::readDrummapsEntry"); - break; - - case Xml::TagEnd: - if (tag == "entry") - return patch_drummap_mapping_t(collection, drummap); - - default: - break; - } - } - printf("ERROR: THIS CANNOT HAPPEN: exited infinite loop in MidiInstrument::readDrummapsEntry()!\n" - " not returning anything. expect undefined behaviour or even crashes.\n"); - return patch_drummap_mapping_t(); -} - -patch_collection_t MidiInstrument::readDrummapsEntryPatchCollection(Xml& xml) -{ - int first_prog=0, last_prog=256; // this means: - int first_lbank=0, last_lbank=256; // "does not matter" - int first_hbank=0, last_hbank=256; - - for (;;) - { - Xml::Token token = xml.parse(); - const QString& tag = xml.s1(); - switch (token) - { - case Xml::Error: - case Xml::End: - return patch_collection_t(-1,-1,-1,-1,-1,-1); // an invalid collection - - case Xml::TagStart: - xml.unknown("MidiInstrument::readDrummapsEntryPatchCollection"); - break; - - case Xml::Attribut: - if (tag == "prog") - parse_range(xml.s2(), &first_prog, &last_prog); - else if (tag == "lbank") - parse_range(xml.s2(), &first_lbank, &last_lbank); - else if (tag == "hbank") - parse_range(xml.s2(), &first_hbank, &last_hbank); - break; - - case Xml::TagEnd: - if (tag == "patch_collection") - return patch_collection_t(first_prog, last_prog, first_lbank, last_lbank, first_hbank, last_hbank); - - default: - break; - } - } - - printf("ERROR: THIS CANNOT HAPPEN: exited infinite loop in MidiInstrument::readDrummapsEntryPatchCollection()!\n" - " not returning anything. expect undefined behaviour or even crashes.\n"); -} - void MidiInstrument::writeDrummaps(int level, Xml& xml) const { xml.tag(level++, "Drummaps"); - - for (std::list::const_iterator it=patch_drummap_mapping.begin(); - it!=patch_drummap_mapping.end(); it++) - { - xml.tag(level++, "entry"); - - const patch_collection_t* ap = &it->affected_patches; - QString tmp="first_program==ap->last_program) - tmp+="prog=\""+QString::number(ap->first_program)+"\" "; - else if (! (ap->first_program==0 && ap->last_program>=127)) - tmp+="prog=\""+QString::number(ap->first_program)+"-"+QString::number(ap->last_program)+"\" "; - - if (ap->first_lbank==ap->last_lbank) - tmp+="lbank=\""+QString::number(ap->first_lbank)+"\" "; - else if (! (ap->first_lbank==0 && ap->last_lbank>=127)) - tmp+="lbank=\""+QString::number(ap->first_lbank)+"-"+QString::number(ap->last_lbank)+"\" "; - - if (ap->first_hbank==ap->last_hbank) - tmp+="hbank=\""+QString::number(ap->first_hbank)+"\" "; - else if (! (ap->first_hbank==0 && ap->last_hbank>=127)) - tmp+="hbank=\""+QString::number(ap->first_hbank)+"-"+QString::number(ap->last_hbank)+"\" "; - - tmp+="/>\n"; - - xml.nput(level, tmp.toAscii().data()); - - write_new_style_drummap(level, xml, "drummap", it->drummap); - - xml.etag(--level, "entry"); - } - + + _channelDrumMapping.write(level, xml); + xml.etag(--level, "Drummaps"); } @@ -888,9 +950,6 @@ void MidiInstrument::read(Xml& xml) { - bool ok; - int base = 10; - _nullvalue = -1; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); @@ -918,7 +977,6 @@ else if (tag == "Controller") { MidiController* mc = new MidiController(); mc->read(xml); - // Added by Tim. Copied from muse 2. // // HACK: make predefined "Program" controller overloadable // @@ -926,12 +984,12 @@ for (iMidiController i = _controller->begin(); i != _controller->end(); ++i) { if (i->second->name() == mc->name()) { delete i->second; - _controller->erase(i); + _controller->del(i); break; } } } - + _controller->add(mc); } else if (tag == "Drummaps") { @@ -970,9 +1028,9 @@ case Xml::Attribut: if (tag == "name") setIName(xml.s2()); - else if(tag == "nullparam") { - _nullvalue = xml.s2().toInt(&ok, base); - } + else if(tag == "nullparam") { } // Obsolete. + else if(tag == "NoteOffMode") + _noteOffMode = (NoteOffMode)xml.s2().toInt(); // Default is NoteOffAll. break; case Xml::TagEnd: if (tag == "MidiInstrument") @@ -993,13 +1051,9 @@ xml.tag(level, "muse version=\"1.0\""); level++; xml.nput(level, ""); level++; @@ -1021,24 +1075,68 @@ for(int i = 0; i < j; ++i) _sysex.at(i)->write(level, xml); } - + xml.tag(level++, "Init"); for(ciEvent ev=_midiInit->begin(); ev != _midiInit->end(); ++ev) ev->second.write(level, xml, MusECore::Pos(0, true)); xml.etag(--level, "Init"); - + // ------------- // TODO: What about _midiReset, _midiState, and _initScript ? // ------------- - + writeDrummaps(level, xml); - + level--; xml.etag(level, "MidiInstrument"); level--; xml.etag(level, "muse"); } +#ifdef _USE_INSTRUMENT_OVERRIDES_ +void MidiInstrument::writeDrummapOverrides(int level, Xml& xml) const +{ + for(ciPatchDrummapMapping_t ipdm = patch_drummap_mapping.begin(); ipdm != patch_drummap_mapping.end(); ++ipdm) + { + if(!(*ipdm)._workingDrumMapList.empty()) + { + xml.tag(level++, "drummapOverrides instrument=\"%s\"", Xml::xmlString(iname()).toLatin1().constData()); + patch_drummap_mapping.writeDrummapOverrides(level, xml); + xml.etag(--level, "drummapOverrides"); + break; + } + } +} +#endif + +patch_drummap_mapping_list_t* MidiInstrument::get_patch_drummap_mapping(int channel, bool includeDefault) +{ + patch_drummap_mapping_list_t* pdml = _channelDrumMapping.find(channel, includeDefault); + if(!pdml) + // Not found? Search the global mapping list. + return genericMidiInstrument->getChannelDrumMapping()->find(channel, includeDefault); + return pdml; +} + + +//--------------------------------------------------------- +// populateInstrPopup (static) +//--------------------------------------------------------- + +void MidiInstrument::populateInstrPopup(MusEGui::PopupMenu* menu, MidiInstrument* /*current*/, bool show_synths) + { + menu->clear(); + for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i + != MusECore::midiInstruments.end(); ++i) + { + // Do not list synths. Although it is possible to assign a synth + // as an instrument to a non-synth device, we should not allow this. + // (One reason is that the 'show gui' column is then enabled, which + // makes no sense for a non-synth device). + if(show_synths || !(*i)->isSynti()) + menu->addAction((*i)->iname()); + } + } //--------------------------------------------------------- // populatePatchPopup @@ -1047,8 +1145,8 @@ void MidiInstrument::populatePatchPopup(MusEGui::PopupMenu* menu, int /*chan*/, bool drum) { menu->clear(); - //int mask = 7; - + //int mask = 7; + if (pg.size() > 1) { for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { PatchGroup* pgp = *i; @@ -1056,15 +1154,15 @@ const PatchList& pl = pgp->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const Patch* mp = *ipl; - if (//(mp->typ & mask) && + if (//(mp->typ & mask) && (mp->drum == drum)) { if(!pm) { - pm = new MusEGui::PopupMenu(pgp->name, menu, menu->stayOpen()); // Use the parent stayOpen here. + pm = new MusEGui::PopupMenu(pgp->name, menu, menu->stayOpen()); // Use the parent stayOpen here. menu->addMenu(pm); pm->setFont(MusEGlobal::config.fonts[0]); } int id = ((mp->hbank & 0xff) << 16) - + ((mp->lbank & 0xff) << 8) + (mp->prog & 0xff); + + ((mp->lbank & 0xff) << 8) + (mp->program & 0xff); QAction* act = pm->addAction(mp->name); act->setData(id); } @@ -1078,7 +1176,7 @@ const Patch* mp = *ipl; //if (mp->typ & mask) { int id = ((mp->hbank & 0xff) << 16) - + ((mp->lbank & 0xff) << 8) + (mp->prog & 0xff); + + ((mp->lbank & 0xff) << 8) + (mp->program & 0xff); QAction* act = menu->addAction(mp->name); act->setData(id); //} @@ -1087,66 +1185,308 @@ } +void MidiInstrument::getMapItem(int channel, int patch, int index, DrumMap& dest_map, int +#ifdef _USE_INSTRUMENT_OVERRIDES_ +overrideType +#endif +) const +{ + const patch_drummap_mapping_list_t* pdml = _channelDrumMapping.find(channel, true); // Include default. + if(!pdml) + { + fprintf(stderr, "MidiInstrument::getMapItem Error: No channel:%d mapping or default found. Using iNewDrumMap.\n", channel); + dest_map = iNewDrumMap[index]; + return; + } + // Always search this instrument's mapping first. + ciPatchDrummapMapping_t ipdm = pdml->find(patch, false); // Don't include defaults here. + if(ipdm == pdml->end()) + { + // Not found? Is there a default patch mapping? +#ifdef _USE_INSTRUMENT_OVERRIDES_ + if(overrideType & WorkingDrumMapEntry::InstrumentDefaultOverride) +#endif + ipdm = pdml->find(CTRL_PROGRAM_VAL_DONT_CARE, false); // Don't include defaults here. -//--------------------------------------------------------- -// getPatchName -//--------------------------------------------------------- - -QString MidiInstrument::getPatchName(int /*channel*/, int prog, bool drum) const + if(ipdm == pdml->end()) + { + // Not found? Search the global mapping list. + patch_drummap_mapping_list_t* def_pdml = genericMidiInstrument->get_patch_drummap_mapping(channel, false); + if(!def_pdml) { - int pr = prog & 0xff; - if(prog == CTRL_VAL_UNKNOWN || pr == 0xff) - return ""; - - int hbank = (prog >> 16) & 0xff; - int lbank = (prog >> 8) & 0xff; - //int tmask = 1; - - bool hb = hbank != 0xff; - bool lb = lbank != 0xff; - for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { - const PatchList& pl = (*i)->patches; - for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { - const Patch* mp = *ipl; - if (//(mp->typ & tmask) && - (pr == mp->prog) - && (mp->drum == drum) - - && (hbank == mp->hbank || !hb || mp->hbank == -1) - && (lbank == mp->lbank || !lb || mp->lbank == -1)) - return mp->name; - } - } - return ""; + //fprintf(stderr, "MidiInstrument::getMapItem Error: No default patch mapping found in genericMidiInstrument. Using iNewDrumMap.\n"); + dest_map = iNewDrumMap[index]; + return; + } + ipdm = def_pdml->find(patch, false); // Don't include defaults here. + if(ipdm == def_pdml->end()) + { + // Not found? Is there a default patch mapping? +#ifdef _USE_INSTRUMENT_OVERRIDES_ + if(overrideType & WorkingDrumMapEntry::InstrumentDefaultOverride) +#endif + ipdm = def_pdml->find(CTRL_PROGRAM_VAL_DONT_CARE, false); // Don't include defaults here. + + if(ipdm == def_pdml->end()) + { + // Not found? Use the global drum map. + // Update: This shouldn't really happen now, since we have added a default patch drum map to the genericMidiInstrument. + fprintf(stderr, "MidiInstrument::getMapItem Error: No default patch mapping found in genericMidiInstrument. Using iNewDrumMap.\n"); + dest_map = iNewDrumMap[index]; + return; + } } + } + } + const patch_drummap_mapping_t& pdm = (*ipdm); -unsigned MidiInstrument::getNextPatch(int channel, unsigned patch, bool drum) -{ - QList haystack=getPatches(channel,drum); - if (haystack.empty()) return MusECore::CTRL_VAL_UNKNOWN; - - int prog=patch&0xFF; - int lbank=(patch>>8)&0xFF; - int hbank=(patch>>16)&0xFF; - - dumb_patchlist_entry_t needle=dumb_patchlist_entry_t(prog, (lbank!=0xFF)?lbank:-1, (hbank!=0xFF)?hbank:-1); - - QList::iterator it; - for (it=haystack.begin(); it!=haystack.end(); it++) - if ((*it) == needle) - break; - - if (it==haystack.end()) //not found? use first entry - it=haystack.begin(); - else - { - for (;it!=haystack.end(); it++) + dest_map = pdm.drummap[index]; + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + // Did we request to include any instrument overrides? + if(!(overrideType & WorkingDrumMapEntry::InstrumentOverride)) + return; + + // Get any instrument overrides. + ciWorkingDrumMapPatch_t iwdp = pdm._workingDrumMapList.find(index); + if(iwdp == pdm._workingDrumMapList.end()) + return; + + const WorkingDrumMapEntry& wdm = iwdp->second; + + if(wdm._fields & WorkingDrumMapEntry::NameField) + dest_map.name = wdm._mapItem.name; + + if(wdm._fields & WorkingDrumMapEntry::VolField) + dest_map.vol = wdm._mapItem.vol; + + if(wdm._fields & WorkingDrumMapEntry::QuantField) + dest_map.quant = wdm._mapItem.quant; + + if(wdm._fields & WorkingDrumMapEntry::LenField) + dest_map.len = wdm._mapItem.len; + + if(wdm._fields & WorkingDrumMapEntry::ChanField) + dest_map.channel = wdm._mapItem.channel; + + if(wdm._fields & WorkingDrumMapEntry::PortField) + dest_map.port = wdm._mapItem.port; + + if(wdm._fields & WorkingDrumMapEntry::Lv1Field) + dest_map.lv1 = wdm._mapItem.lv1; + + if(wdm._fields & WorkingDrumMapEntry::Lv2Field) + dest_map.lv2 = wdm._mapItem.lv2; + + if(wdm._fields & WorkingDrumMapEntry::Lv3Field) + dest_map.lv3 = wdm._mapItem.lv3; + + if(wdm._fields & WorkingDrumMapEntry::Lv4Field) + dest_map.lv4 = wdm._mapItem.lv4; + + if(wdm._fields & WorkingDrumMapEntry::ENoteField) + dest_map.enote = wdm._mapItem.enote; + + if(wdm._fields & WorkingDrumMapEntry::ANoteField) + dest_map.anote = wdm._mapItem.anote; + + if(wdm._fields & WorkingDrumMapEntry::MuteField) + dest_map.mute = wdm._mapItem.mute; + + if(wdm._fields & WorkingDrumMapEntry::HideField) + dest_map.hide = wdm._mapItem.hide; +#endif + +} + + + +#ifdef _USE_INSTRUMENT_OVERRIDES_ +int MidiInstrument::isWorkingMapItem(int patch, int index, int fields) const +{ + int ret = WorkingDrumMapEntry::NoOverride; + + // Is there a default patch override for this drum map item? + bool def_ipdm_valid = true; + ciPatchDrummapMapping_t def_ipdm = patch_drummap_mapping.find(CTRL_PROGRAM_VAL_DONT_CARE, false); // Don't include defaults here. + if(def_ipdm == patch_drummap_mapping.end()) + { + // Not found? Search the global mapping list. + def_ipdm = genericMidiInstrument->get_patch_drummap_mapping()->find(CTRL_PROGRAM_VAL_DONT_CARE, false); + if(def_ipdm == genericMidiInstrument->get_patch_drummap_mapping()->end()) + def_ipdm_valid = false; + } + if(def_ipdm_valid) + { + const patch_drummap_mapping_t& pdm = (*def_ipdm); + ciWorkingDrumMapPatch_t iwdp = pdm._workingDrumMapList.find(index); + if(iwdp != pdm._workingDrumMapList.end()) + { + const WorkingDrumMapEntry& wdm = iwdp->second; + if(wdm._fields & fields) + ret |= WorkingDrumMapEntry::InstrumentDefaultOverride; + } + } + + // Is there a patch override for this drum map item? + // Always search this instrument's mapping first. + bool ipdm_valid = true; + ciPatchDrummapMapping_t ipdm = patch_drummap_mapping.find(patch, false); + if(ipdm == patch_drummap_mapping.end()) + { + // Not found? Search the global mapping list. + ipdm = MusECore::genericMidiInstrument->get_patch_drummap_mapping()->find(patch, false); + if(ipdm == MusECore::genericMidiInstrument->get_patch_drummap_mapping()->end()) + ipdm_valid = false; + } + if(ipdm_valid) + { + const patch_drummap_mapping_t& pdm = (*ipdm); + ciWorkingDrumMapPatch_t iwdp = pdm._workingDrumMapList.find(index); + if(iwdp != pdm._workingDrumMapList.end()) + { + const WorkingDrumMapEntry& wdm = iwdp->second; + if(wdm._fields & fields) + ret |= WorkingDrumMapEntry::InstrumentOverride; + } + } + + return ret; +} + +void MidiInstrument::clearDrumMapOverrides() +{ + for(iPatchDrummapMapping_t ipdm = patch_drummap_mapping.begin(); ipdm != patch_drummap_mapping.end(); ++ipdm) + { + patch_drummap_mapping_t& pdm = *ipdm; + pdm._workingDrumMapList.clear(); + } +} + +bool MidiInstrument::setWorkingDrumMapItem(int patch, int index, const WorkingDrumMapEntry& item, bool isReset) +{ + // Special value. Save it from searching. +// if(patch == CTRL_VAL_UNKNOWN) +// return false; + +// iPatchDrummapMapping_t patch_ipm; +// patch_ipm = patch_drummap_mapping.find(patch); +// // You can't edit a drum map item in a collection that doesn't exist. +// if(patch_ipm == patch_drummap_mapping.end()) +// return false; + + // Always search this instrument's mapping first. + iPatchDrummapMapping_t ipdm = patch_drummap_mapping.find(patch, false); // Don't include defaults here. + if(ipdm == patch_drummap_mapping.end()) + { + // Not found? Search the global mapping list. + ipdm = MusECore::genericMidiInstrument->get_patch_drummap_mapping()->find(patch, false); // Don't include defaults here. + // Not found? You can't edit a drum map item in a collection that doesn't exist. + if(ipdm == MusECore::genericMidiInstrument->get_patch_drummap_mapping()->end()) + return false; + } + + patch_drummap_mapping_t& pdm = *ipdm; + + const int fields = item._fields; + + DrumMap cur_dm; + getMapItem(patch, index, cur_dm, WorkingDrumMapEntry::InstrumentOverride | WorkingDrumMapEntry::InstrumentDefaultOverride); + const int cur_enote = cur_dm.enote; + + if(isReset) + pdm.removeWorkingDrumMapEntry(index, fields); + else + pdm.addWorkingDrumMapEntry(index, item); + + DrumMap new_dm; + getMapItem(patch, index, new_dm, WorkingDrumMapEntry::InstrumentOverride | WorkingDrumMapEntry::InstrumentDefaultOverride); + + if(fields & WorkingDrumMapEntry::ENoteField) + { + int new_enote = new_dm.enote; + int other_index = pdm.drum_in_map[new_enote]; + { + DrumMap other_dm; + if(isReset) + { + // Here we need to see the map item value just /before/ any override, so that we can tell + // whether this other_index brute-force 'reset' value is still technically an + // override, and either remove or add (modify) the list appropriately. + getMapItem(patch, other_index, other_dm, WorkingDrumMapEntry::InstrumentDefaultOverride); + if(other_dm.enote == cur_enote) + { + // The values are equal. This is technically no longer an override and we may remove it. + //_workingDrumMapPatchList->remove(patch, other_index, WorkingDrumMapEntry::ENoteField); + pdm.removeWorkingDrumMapEntry(other_index, WorkingDrumMapEntry::ENoteField); + } + else + { + // The values are not equal. This is technically still an override, so add (modify) it. + other_dm.enote = cur_enote; + WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField); + //_workingDrumMapPatchList->add(patch, other_index, other_wdme); + pdm.addWorkingDrumMapEntry(other_index, other_wdme); + } + } + else + { + other_dm.enote = cur_enote; + WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField); + //_workingDrumMapPatchList->add(patch, other_index, other_wdme); + pdm.addWorkingDrumMapEntry(other_index, other_wdme); + } + pdm.drum_in_map[cur_enote] = other_index; + pdm.drum_in_map[new_enote] = index; + } + } + + + + + return true; +} +#endif + + +//--------------------------------------------------------- +// getPatchName +//--------------------------------------------------------- + +QString MidiInstrument::getPatchName(int /*channel*/, int prog, bool drum, bool includeDefault) const + { + if(MusECore::Patch* p = pg.findPatch(prog, drum, includeDefault)) + return p->name; + return ""; + } + +unsigned MidiInstrument::getNextPatch(int channel, unsigned patch, bool drum) +{ + QList haystack=getPatches(channel,drum); + if (haystack.empty()) return MusECore::CTRL_VAL_UNKNOWN; + + int prog=patch&0xFF; + int lbank=(patch>>8)&0xFF; + int hbank=(patch>>16)&0xFF; + + dumb_patchlist_entry_t needle=dumb_patchlist_entry_t(prog, (lbank!=0xFF)?lbank:-1, (hbank!=0xFF)?hbank:-1); + + QList::iterator it; + for (it=haystack.begin(); it!=haystack.end(); it++) + if ((*it) == needle) + break; + + if (it==haystack.end()) //not found? use first entry + it=haystack.begin(); + else + { + for (;it!=haystack.end(); it++) if ((*it)!=needle) break; if (it==haystack.end()) it=haystack.begin(); //wrap-over } - + return (it->prog&0xFF) | ((((it->lbank==-1)?0xFF:it->lbank)<<8)&0xFF00) | ((((it->hbank==-1)?0xFF:it->hbank)<<16)&0xFF0000); @@ -1156,18 +1496,18 @@ { QList haystack=getPatches(channel,drum); if (haystack.empty()) return MusECore::CTRL_VAL_UNKNOWN; - + int prog=patch&0xFF; int lbank=(patch>>8)&0xFF; int hbank=(patch>>16)&0xFF; - + dumb_patchlist_entry_t needle=dumb_patchlist_entry_t(prog, (lbank!=0xFF)?lbank:-1, (hbank!=0xFF)?hbank:-1); - + QList::iterator it; for (it=haystack.begin(); it!=haystack.end(); it++) if ((*it) == needle) break; - + if (it==haystack.end()) //not found? use first entry it=haystack.begin(); else @@ -1175,7 +1515,7 @@ if (it==haystack.begin()) it=haystack.end(); //wrap-over it--; } - + return (it->prog&0xFF) | ((((it->lbank==-1)?0xFF:it->lbank)<<8)&0xFF00) | ((((it->hbank==-1)?0xFF:it->hbank)<<16)&0xFF0000); @@ -1185,122 +1525,1107 @@ { //int tmask = 1; QList tmp; - + for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { const PatchList& pl = (*i)->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const Patch* mp = *ipl; - if (//(mp->typ & tmask) && - (mp->drum == drum)) + if (//(mp->typ & tmask) && + (mp->drum == drum)) { - int prog = mp->prog; + int prog = mp->program; int lbank = mp->lbank; int hbank = mp->hbank; tmp.push_back(dumb_patchlist_entry_t(prog,lbank,hbank)); } } } - + return tmp; } -const DrumMap* MidiInstrument::drummap_for_patch(int patch) const -{ - using std::list; - - int program = (patch & 0x0000FF); - int lbank = (patch & 0x00FF00) >> 8; - int hbank = (patch & 0xFF0000) >> 16; - - for (list::const_iterator it=patch_drummap_mapping.begin(); - it!=patch_drummap_mapping.end(); it++) - { - const patch_collection_t* ap = &it->affected_patches; - // if the entry matches our patch - if ( (program >= ap->first_program && program <= ap->last_program) && - (hbank >= ap->first_hbank && hbank <= ap->last_hbank) && - (lbank >= ap->first_lbank && lbank <= ap->last_lbank) ) - { - return it->drummap; - } - } - - // if nothing was found - return iNewDrumMap; -} +//--------------------------------------------------------- +// patch_drummap_mapping_t +//--------------------------------------------------------- patch_drummap_mapping_t::patch_drummap_mapping_t() { + _patch = CTRL_PROGRAM_VAL_DONT_CARE; drummap=new DrumMap[128]; for (int i=0;i<128;i++) drummap[i]=iNewDrumMap[i]; + update_drum_in_map(); } patch_drummap_mapping_t::patch_drummap_mapping_t(const patch_drummap_mapping_t& that) { - drummap=new DrumMap[128]; - for (int i=0;i<128;i++) - drummap[i]=that.drummap[i]; - - affected_patches=that.affected_patches; + drummap=NULL; + if(that.drummap) + { + drummap=new DrumMap[128]; + for (int i=0;i<128;i++) + drummap[i]=that.drummap[i]; + } + + _patch = that._patch; + update_drum_in_map(); } patch_drummap_mapping_t& patch_drummap_mapping_t::operator=(const patch_drummap_mapping_t& that) { if (drummap) delete [] drummap; - - drummap=new DrumMap[128]; - for (int i=0;i<128;i++) - drummap[i]=that.drummap[i]; - - affected_patches=that.affected_patches; - - return *this; + drummap=NULL; + + if(that.drummap) + { + drummap=new DrumMap[128]; + for (int i=0;i<128;i++) + drummap[i]=that.drummap[i]; + } + + _patch = that._patch; + + update_drum_in_map(); + return *this; +} + +bool patch_drummap_mapping_t::isValid() const +{ + return _patch != CTRL_VAL_UNKNOWN && drummap != NULL; } patch_drummap_mapping_t::~patch_drummap_mapping_t() { - delete [] drummap; + if(drummap) + delete [] drummap; +} + +#ifdef _USE_INSTRUMENT_OVERRIDES_ +void patch_drummap_mapping_t::addWorkingDrumMapEntry(int index,const WorkingDrumMapEntry& item) +{ + _workingDrumMapList.add(index, item); +} + +void patch_drummap_mapping_t::removeWorkingDrumMapEntry(int index, const WorkingDrumMapEntry& item) +{ + _workingDrumMapList.remove(index, item); +} + +void patch_drummap_mapping_t::removeWorkingDrumMapEntry(int index, int fields) +{ + _workingDrumMapList.remove(index, fields); +} +#endif + +void patch_drummap_mapping_t::update_drum_in_map() +{ + if(drummap) + { + for(int i = 0; i < 128; ++i) + drum_in_map[(int)drummap[i].enote] = i; + } + else + { + for(int i = 0; i < 128; ++i) + drum_in_map[i] = i; + } + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + int index; + int enote; + for(ciWorkingDrumMapPatch_t iwdmp = _workingDrumMapList.begin(); iwdmp != _workingDrumMapList.end(); ++iwdmp) + { + const WorkingDrumMapEntry& wde = iwdmp->second; + if(wde._fields & WorkingDrumMapEntry::ENoteField) + { + index = iwdmp->first; + const DrumMap& dm = wde._mapItem; + enote = (int)dm.enote; + drum_in_map[enote] = index; + } + } +#endif +} + +bool patch_drummap_mapping_t::isPatchInRange(int patch, bool includeDefault) const +{ + // No exceptions: If all three prg, hb, and lb are don't care, then patch is always in range. + if(dontCare()) + return includeDefault; + + // Special value. Unknown cannot be part of a collection (unless don't care). + if(!isValid() || patch == CTRL_VAL_UNKNOWN) + return false; + + const int hb = (patch >> 16) & 0xff; + const int lb = (patch >> 8) & 0xff; + const int pr = patch & 0xff; + + const bool hboff = hb >= 128; + const bool lboff = lb >= 128; + const bool prgoff = pr >= 128; // Shouldn't happen. + + return (programDontCare() || (!prgoff && pr == prog())) && + (hbankDontCare() || (!hboff && hb == hbank())) && + (lbankDontCare() || (!lboff && lb == lbank())); } -QString patch_collection_t::to_string() +QString patch_drummap_mapping_t::to_string() { QString tmp; - - if (first_program==0 && last_program>=127 && - first_lbank==0 && last_lbank>=127 && - first_hbank==0 && last_hbank>=127) + + if (dontCare()) tmp="default"; else { - tmp+="prog: "; - if (first_program==last_program) - tmp+=QString::number(first_program+1); - else if (! (first_program==0 && last_program>=127)) - tmp+=QString::number(first_program+1)+"-"+QString::number(last_program+1); + if(hbankDontCare()) + tmp += "---"; else - tmp+="*"; - - tmp+=" bank="; - if (first_lbank==last_lbank) - tmp+=QString::number(first_lbank+1); - else if (! (first_lbank==0 && last_lbank>=127)) - tmp+=QString::number(first_lbank+1)+"-"+QString::number(last_lbank+1); + tmp += QString::number(hbank() + 1); + + tmp+=" / "; + + if(lbankDontCare()) + tmp += "---"; else - tmp+="*"; + tmp += QString::number(lbank() + 1); - tmp+="/"; - if (first_hbank==last_hbank) - tmp+=QString::number(first_hbank+1); - else if (! (first_hbank==0 && last_hbank>=127)) - tmp+=QString::number(first_hbank+1)+"-"+QString::number(last_hbank+1); + tmp+=" / "; + + if(programDontCare()) + tmp += "---"; else - tmp+="*"; - + tmp += QString::number(prog() + 1); } return tmp; } +//--------------------------------------------------------- +// patch_drummap_mapping_t +//--------------------------------------------------------- + +void patch_drummap_mapping_list_t::add(const patch_drummap_mapping_list_t& other) +{ + for(ciPatchDrummapMapping_t ipdm = other.begin(); ipdm != other.end(); ++ipdm) + { + const patch_drummap_mapping_t& pdm = *ipdm; + add(pdm); + } +} + +void patch_drummap_mapping_list_t::add(const patch_drummap_mapping_t& pdm) +{ + // No duplicates: If a mapping item by that patch already exists, replace it. + iPatchDrummapMapping_t ipdm = find(pdm._patch, false); // No default. + if(ipdm == end()) + push_back(pdm); + else + *ipdm = pdm; +} + +iPatchDrummapMapping_t patch_drummap_mapping_list_t::find(int patch, bool includeDefault) +{ + iPatchDrummapMapping_t ipdm_default = end(); + for(iPatchDrummapMapping_t ipdm = begin(); ipdm != end(); ++ipdm) + { + // Look for an exact match above all else. The given patch must be valid. + if(patch != CTRL_VAL_UNKNOWN && ipdm->_patch == patch) + return ipdm; + // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). + if(includeDefault && ipdm->dontCare() && ipdm_default == end()) + ipdm_default = ipdm; + } + return ipdm_default; +} + +ciPatchDrummapMapping_t patch_drummap_mapping_list_t::find(int patch, bool includeDefault) const +{ + ciPatchDrummapMapping_t ipdm_default = end(); + for(ciPatchDrummapMapping_t ipdm = begin(); ipdm != end(); ++ipdm) + { + // Look for an exact match above all else. The given patch must be valid. + if(patch != CTRL_VAL_UNKNOWN && ipdm->_patch == patch) + return ipdm; + // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). + if(includeDefault && ipdm->dontCare() && ipdm_default == end()) + ipdm_default = ipdm; + } + return ipdm_default; +} + +void patch_drummap_mapping_list_t::read(Xml& xml) +{ + int patch = CTRL_PROGRAM_VAL_DONT_CARE; + DrumMap* drummap=new DrumMap[128]; + for (int i=0;i<128;i++) + drummap[i]=iNewDrumMap[i]; + + for (;;) + { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) + { + case Xml::Error: + case Xml::End: + delete drummap; + return; + + case Xml::TagStart: + if (tag == "patch_collection") + patch = readDrummapsEntryPatchCollection(xml); + else if (tag == "drummap") + read_new_style_drummap(xml, "drummap", drummap); + else + xml.unknown("patch_drummap_mapping_list_t::read"); + break; + + case Xml::TagEnd: + if (tag == "entry") + { + push_back(patch_drummap_mapping_t(drummap, patch)); + return; + } + + default: + break; + } + } + printf("ERROR: THIS CANNOT HAPPEN: exited infinite loop in patch_drummap_mapping_list_t::read()!\n" + " not returning anything. expect undefined behaviour or even crashes.\n"); + delete drummap; + return; +} + +void patch_drummap_mapping_list_t::write(int level, Xml& xml) const +{ + for (ciPatchDrummapMapping_t it = begin(); + it != end(); it++) + { + xml.tag(level++, "entry"); + + const patch_drummap_mapping_t& pdm = *it; + + if(!pdm.dontCare()) + { + QString tmp="drummap); + //write_new_style_drummap(level, xml, "drummap", it->drummap, true); // true = Need to save all entries. + + xml.etag(--level, "entry"); + } +} + +#ifdef _USE_INSTRUMENT_OVERRIDES_ +void patch_drummap_mapping_list_t::writeDrummapOverrides(int level, Xml& xml) const +{ + for(ciPatchDrummapMapping_t ipdm = begin(); ipdm != end(); ++ipdm) + { + const patch_drummap_mapping_t& pdm = *ipdm; + if(pdm._workingDrumMapList.empty()) + continue; + xml.tag(level++, "drumMapPatch patch=\"%d\"", pdm._patch); + pdm._workingDrumMapList.write(level, xml); + xml.etag(--level, "drumMapPatch"); + } +} +#endif + +//--------------------------------------------------------- +// WorkingDrumMapEntry +//--------------------------------------------------------- + +WorkingDrumMapEntry::WorkingDrumMapEntry() +{ + _fields = NoField; +} + +WorkingDrumMapEntry::WorkingDrumMapEntry(const DrumMap& dm, fields_t fields) +{ + _fields = fields; + _mapItem = dm; +} + +WorkingDrumMapEntry::WorkingDrumMapEntry(const WorkingDrumMapEntry& other) +{ + _fields = other._fields; + _mapItem = other._mapItem; +} + +WorkingDrumMapEntry& WorkingDrumMapEntry::operator=(const WorkingDrumMapEntry& other) +{ + _fields = other._fields; + _mapItem = other._mapItem; + return *this; +} + +//--------------------------------------------------------- +// WorkingDrumMapList +//--------------------------------------------------------- + +void WorkingDrumMapList::add(int index, const WorkingDrumMapEntry& item) +{ + WorkingDrumMapPatchInsertResult_t res = insert(WorkingDrumMapPatchInsertPair_t(index, item)); + if(res.second == false) + { + iWorkingDrumMapPatch_t& iwp = res.first; + WorkingDrumMapEntry& wde = iwp->second; + + if(item._fields & WorkingDrumMapEntry::NameField) + wde._mapItem.name = item._mapItem.name; + + if(item._fields & WorkingDrumMapEntry::VolField) + wde._mapItem.vol = item._mapItem.vol; + + if(item._fields & WorkingDrumMapEntry::QuantField) + wde._mapItem.quant = item._mapItem.quant; + + if(item._fields & WorkingDrumMapEntry::LenField) + wde._mapItem.len = item._mapItem.len; + + if(item._fields & WorkingDrumMapEntry::ChanField) + wde._mapItem.channel = item._mapItem.channel; + + if(item._fields & WorkingDrumMapEntry::PortField) + wde._mapItem.port = item._mapItem.port; + + if(item._fields & WorkingDrumMapEntry::Lv1Field) + wde._mapItem.lv1 = item._mapItem.lv1; + + if(item._fields & WorkingDrumMapEntry::Lv2Field) + wde._mapItem.lv2 = item._mapItem.lv2; + + if(item._fields & WorkingDrumMapEntry::Lv3Field) + wde._mapItem.lv3 = item._mapItem.lv3; + + if(item._fields & WorkingDrumMapEntry::Lv4Field) + wde._mapItem.lv4 = item._mapItem.lv4; + + if(item._fields & WorkingDrumMapEntry::ENoteField) + wde._mapItem.enote = item._mapItem.enote; + + if(item._fields & WorkingDrumMapEntry::ANoteField) + wde._mapItem.anote = item._mapItem.anote; + + if(item._fields & WorkingDrumMapEntry::MuteField) + wde._mapItem.mute = item._mapItem.mute; + + if(item._fields & WorkingDrumMapEntry::HideField) + wde._mapItem.hide = item._mapItem.hide; + + wde._fields |= item._fields; + } +} + +int WorkingDrumMapList::remove(int index, const WorkingDrumMapEntry& item) +{ + return remove(index, item._fields); +} + +int WorkingDrumMapList::remove(int index, int fields) +{ + iWorkingDrumMapPatch_t iwp = find(index); + if(iwp == end()) + return fields; + WorkingDrumMapEntry& wde = iwp->second; + int ret = wde._fields ^ fields; + wde._fields &= ~fields; + ret ^= wde._fields; + if(wde._fields == WorkingDrumMapEntry::NoField) + erase(iwp); + return ret; +} + +void WorkingDrumMapList::read(Xml& xml, bool fillUnused, int defaultIndex) +{ + const QString start_tag = xml.s1(); + int index = defaultIndex; + int index_read; + bool enote_read = false; + bool anote_read = false; + bool ok; + WorkingDrumMapEntry wdme; + if(fillUnused) + { + // Must initialize the map item in case some fields aren't given. + wdme._mapItem.init(); + // Technically we are overriding all fields even if some are not given. + wdme._fields = WorkingDrumMapEntry::AllFields; + } + + for (;;) { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) { + case Xml::Error: + case Xml::End: + return; + case Xml::TagStart: + if (tag == "name") + { + wdme._mapItem.name = xml.parse1(); + wdme._fields |= WorkingDrumMapEntry::NameField; + } + else if (tag == "vol") + { + wdme._mapItem.vol = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::VolField; + } + else if (tag == "quant") + { + wdme._mapItem.quant = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::QuantField; + } + else if (tag == "len") + { + wdme._mapItem.len = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::LenField; + } + else if (tag == "channel") + { + wdme._mapItem.channel = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::ChanField; + } + else if (tag == "port") + { + wdme._mapItem.port = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::PortField; + } + else if (tag == "lv1") + { + wdme._mapItem.lv1 = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::Lv1Field; + } + else if (tag == "lv2") + { + wdme._mapItem.lv2 = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::Lv2Field; + } + else if (tag == "lv3") + { + wdme._mapItem.lv3 = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::Lv3Field; + } + else if (tag == "lv4") + { + wdme._mapItem.lv4 = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::Lv4Field; + } + else if (tag == "enote") + { + wdme._mapItem.enote = xml.parseInt(); + enote_read = true; + wdme._fields |= WorkingDrumMapEntry::ENoteField; + } + else if (tag == "anote") + { + wdme._mapItem.anote = xml.parseInt(); + anote_read = true; + wdme._fields |= WorkingDrumMapEntry::ANoteField; + } + else if (tag == "mute") + { + wdme._mapItem.mute = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::MuteField; + } + else if (tag == "hide") + { + wdme._mapItem.hide = xml.parseInt(); + wdme._fields |= WorkingDrumMapEntry::HideField; + } + else + xml.unknown(start_tag.toLatin1().constData()); + break; + case Xml::Attribut: + if (tag == "idx" || tag == "pitch") + { + index_read = xml.s2().toInt(&ok); + if(ok) + index = index_read; + } + break; + case Xml::TagEnd: + if (tag == start_tag) + { + if(index >= 0 && index < 128) + { + // If no enote was given, set it to the index. + if(fillUnused) + { + if(!enote_read) + wdme._mapItem.enote = index; + // If no anote was given, set it to the enote. + if(!anote_read) + wdme._mapItem.anote = wdme._mapItem.enote; + } + insert(WorkingDrumMapPatchInsertPair_t(index, wdme)); + } + return; + } + default: + break; + } + } +} + +void WorkingDrumMapList::write(int level, Xml& xml) const +{ + int index; + for(ciWorkingDrumMapPatch_t iwdp = begin(); iwdp != end(); ++iwdp) + { + index = iwdp->first; + xml.tag(level++, "entry idx=\"%d\"", index); + + const WorkingDrumMapEntry& wde = iwdp->second; + + if(wde._fields & WorkingDrumMapEntry::NameField) + xml.strTag(level, "name", wde._mapItem.name); + + if(wde._fields & WorkingDrumMapEntry::VolField) + xml.intTag(level, "vol", wde._mapItem.vol); + + if(wde._fields & WorkingDrumMapEntry::QuantField) + xml.intTag(level, "quant", wde._mapItem.quant); + + if(wde._fields & WorkingDrumMapEntry::LenField) + xml.intTag(level, "len", wde._mapItem.len); + + if(wde._fields & WorkingDrumMapEntry::ChanField) + xml.intTag(level, "channel", wde._mapItem.channel); + + if(wde._fields & WorkingDrumMapEntry::PortField) + xml.intTag(level, "port", wde._mapItem.port); + + if(wde._fields & WorkingDrumMapEntry::Lv1Field) + xml.intTag(level, "lv1", wde._mapItem.lv1); + + if(wde._fields & WorkingDrumMapEntry::Lv2Field) + xml.intTag(level, "lv2", wde._mapItem.lv2); + + if(wde._fields & WorkingDrumMapEntry::Lv3Field) + xml.intTag(level, "lv3", wde._mapItem.lv3); + + if(wde._fields & WorkingDrumMapEntry::Lv4Field) + xml.intTag(level, "lv4", wde._mapItem.lv4); + + if(wde._fields & WorkingDrumMapEntry::ENoteField) + xml.intTag(level, "enote", wde._mapItem.enote); + + if(wde._fields & WorkingDrumMapEntry::ANoteField) + xml.intTag(level, "anote", wde._mapItem.anote); + + if(wde._fields & WorkingDrumMapEntry::MuteField) + xml.intTag(level, "mute", wde._mapItem.mute); + + if(wde._fields & WorkingDrumMapEntry::HideField) + xml.intTag(level, "hide", wde._mapItem.hide); + + xml.tag(--level, "/entry"); + } +} + + +//--------------------------------------------------------- +// WorkingDrumMapInstrumentList +//--------------------------------------------------------- + +void WorkingDrumMapInstrumentList::read(Xml& xml) +{ + const QString start_tag = xml.s1(); + QString instr_name; + WorkingDrumMapPatchList wdmpl; + for (;;) { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) { + case Xml::Error: + case Xml::End: + return; + case Xml::TagStart: + if (tag == "drumMapPatch") + // false = Do not fill in unused items. + wdmpl.read(xml, false); + else + xml.unknown(start_tag.toLatin1().constData()); + break; + case Xml::Attribut: + if (tag == "instrument") + { + instr_name = xml.s2(); + } + break; + case Xml::TagEnd: + if (tag == start_tag) + { + if(!instr_name.isEmpty() && !wdmpl.empty()) + insert(WorkingDrumMapInstrumentListInsertPair_t(instr_name.toStdString(), wdmpl)); + return; + } + default: + break; + } + } +} + + +//--------------------------------------------------------- +// WorkingDrumMapPatchList +//--------------------------------------------------------- + +void WorkingDrumMapPatchList::add(const WorkingDrumMapPatchList& other) +{ + int patch; + int index; + for(ciWorkingDrumMapPatchList_t iwdmpl = other.begin(); iwdmpl != other.end(); ++iwdmpl) + { + patch = iwdmpl->first; + const WorkingDrumMapList& wdml = iwdmpl->second; + WorkingDrumMapPatchListInsertResult_t res = insert(WorkingDrumMapPatchListInsertPair_t(patch, wdml)); + iWorkingDrumMapPatchList_t res_iwdmpl = res.first; + if(res_iwdmpl == end()) // Error. + continue; + WorkingDrumMapList& res_wdml = res_iwdmpl->second; + for(iWorkingDrumMapPatch_t res_iwdp = res_wdml.begin(); res_iwdp != res_wdml.end(); ++res_iwdp) + { + index = res_iwdp->first; + WorkingDrumMapEntry& wdme = res_iwdp->second; + res_wdml.add(index, wdme); + } + } +} + +void WorkingDrumMapPatchList::add(int patch, const WorkingDrumMapList& list) +{ + insert(WorkingDrumMapPatchListInsertPair_t(patch, list)); +} + +void WorkingDrumMapPatchList::add(int patch, int index, const WorkingDrumMapEntry& item) +{ + WorkingDrumMapPatchListInsertResult_t res = insert(WorkingDrumMapPatchListInsertPair_t(patch, WorkingDrumMapList())); + iWorkingDrumMapPatchList_t iwdmpl = res.first; + if(iwdmpl == end()) // Error, should exist. + return; + WorkingDrumMapList& wdml = iwdmpl->second; + wdml.add(index, item); +} + +void WorkingDrumMapPatchList::remove(int patch, int index, const WorkingDrumMapEntry& item, bool includeDefault) +{ + remove(patch, index, item._fields, includeDefault); +} + +void WorkingDrumMapPatchList::remove(int patch, int index, int fields, bool includeDefault) +{ + // Remove requested fields from the exact patch number first. + iWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch); + if(iwdmpl != end()) + { + WorkingDrumMapList& wdml = iwdmpl->second; + // Consider defaults and real patch overrides as part of the same deal, + // remove all requested fields from BOTH. + //fields = wdml.remove(index, fields); + wdml.remove(index, fields); + // No more items in the list? Remove this container list. + if(wdml.empty()) + erase(iwdmpl); + } + + // Consider defaults and real patch overrides as part of the same deal, + // remove all requested fields from BOTH. + //if(includeDefault && fields != WorkingDrumMapEntry::NoField) + if(includeDefault) + { + iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE); + if(iwdmpl != end()) + { + WorkingDrumMapList& wdml = iwdmpl->second; + wdml.remove(index, fields); + // No more items in the list? Remove this container list. + if(wdml.empty()) + erase(iwdmpl); + } + } +} + +void WorkingDrumMapPatchList::remove(int patch, bool includeDefault) +{ + // Remove requested fields from the exact patch number first. + iWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch); + if(iwdmpl != end()) + erase(iwdmpl); + // Patch map not found? Look for a default patch number (all three pr, hb, lb = don't care). + else if(includeDefault) + { + iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE); + if(iwdmpl != end()) + erase(iwdmpl); + } +} + +WorkingDrumMapList* WorkingDrumMapPatchList::find(int patch, bool includeDefault) +{ + // Look for an exact match above all else. The given patch must be valid. + iWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch); + // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). + if(iwdmpl == end()) + { + if(!includeDefault) + return NULL; + iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE); + if(iwdmpl == end()) + return NULL; + } + return &iwdmpl->second; +} + +const WorkingDrumMapList* WorkingDrumMapPatchList::find(int patch, bool includeDefault) const +{ + // Look for an exact match above all else. The given patch must be valid. + ciWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch); + // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). + if(iwdmpl == end()) + { + if(!includeDefault) + return NULL; + iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE); + if(iwdmpl == end()) + return NULL; + } + return &iwdmpl->second; +} + +WorkingDrumMapEntry* WorkingDrumMapPatchList::find(int patch, int index, bool includeDefault) +{ + WorkingDrumMapList* wdmpl = find(patch, includeDefault); + if(!wdmpl) + return NULL; + iWorkingDrumMapPatch_t iwdmp = wdmpl->find(index); + if(iwdmp == wdmpl->end()) + return NULL; + return &iwdmp->second; +} + +const WorkingDrumMapEntry* WorkingDrumMapPatchList::find(int patch, int index, bool includeDefault) const +{ + const WorkingDrumMapList* wdmpl = find(patch, includeDefault); + if(!wdmpl) + return NULL; + ciWorkingDrumMapPatch_t iwdmp = wdmpl->find(index); + if(iwdmp == wdmpl->end()) + return NULL; + return &iwdmp->second; +} + +void WorkingDrumMapPatchList::read(Xml& xml, bool fillUnused) +{ + const QString start_tag = xml.s1(); + // Default "don't care" patch number, in case no patch number found. + int patch = 0xffffff; + int patch_read; + bool ok; + int index = 0; + WorkingDrumMapList wdml; + +// TODO Need to move this stuff up into the caller, because a default patch map may not have been loaded yet! +// For now, we rely on the loaded map being trustworthy with no duplicate enotes. Still the situation IS compensated +// for at the lowest level in MidiTrack::normalizeDrumMap(), so it IS tolerant of mistakes in the loaded map. +// +// REMOVE Tim. newdrums. Removed. +// WorkingDrumMapList* def_wdml = 0; +// //if(patch != CTRL_PROGRAM_VAL_DONT_CARE) +// def_wdml = find(CTRL_PROGRAM_VAL_DONT_CARE, false); +// WorkingDrumMapEntry new_wdme; +// // We can init these outside of the loop. +// new_wdme._fields = WorkingDrumMapEntry::AllFields; +// new_wdme._mapItem.init(); +// +// bool used_index[128]; +// int used_enotes[128]; +// for(int i = 0; i < 128; ++i) +// { +// used_index[i] = false; +// used_enotes[i] = 0; +// } +// char unused_enotes[128]; +// int unused_enotes_sz = 0; +// char unused_index[128]; +// int unused_index_sz = 0; + + for (;;) { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) { + case Xml::Error: + case Xml::End: + return; + case Xml::TagStart: + if (tag == "entry") + { + // In case there are no index attributes in this drum map, + // we use a running index. + wdml.read(xml, fillUnused, index); + ++index; + } + else if (tag == "comment") + xml.parse(); + else + xml.unknown(start_tag.toLatin1().constData()); + break; + case Xml::Attribut: + if (tag == "patch") + { + patch_read = xml.s2().toInt(&ok); + if(ok) + patch = patch_read; + } + break; + case Xml::TagEnd: + if (tag == start_tag) + { + + if(!wdml.empty()) + { +// // // We can only deal with duplicate enotes here if requesting to +// // // fill up all items, because in the context of further overrides +// // // masking any unused ones here, we cannot fully know which enotes are used. +// //if(fillUnused) +// //{ +// // Find all the used enotes and indexes. +// for(iWorkingDrumMapPatch_t iwdml = wdml.begin(); iwdml != wdml.end(); ++iwdml) +// { +// used_index[iwdml->first] = true; +// ++used_enotes[(unsigned char)iwdml->second._mapItem.enote]; +// } +// +// // Find all the unused enotes and indexes. +// for(int i = 0; i < 128; ++i) +// { +// if(!used_index[i]) +// unused_index[unused_index_sz++] = i; +// if(used_enotes[i] == 0) +// unused_enotes[unused_enotes_sz++] = i; +// } +// +// // Ensure there are NO duplicate enotes in the existing map items so far. +// int unused_enotes_cnt = 0; +// for(iWorkingDrumMapPatch_t iwdml = wdml.begin(); iwdml != wdml.end(); ++iwdml) +// { +// // More than 1 (this) usage? +// if(used_enotes[(unsigned char)iwdml->second._mapItem.enote] > 1) +// { +// if(unused_enotes_cnt >= unused_enotes_sz) +// { +// fprintf(stderr, "WorkingDrumMapPatchList::read: Error: unused_enotes_cnt >= unused_enotes_sz:%d\n", +// unused_enotes_sz); +// break; +// } +// --used_enotes[(unsigned char)iwdml->second._mapItem.enote]; +// iwdml->second._mapItem.enote = unused_enotes[unused_enotes_cnt++]; +// } +// } +// +// // Technically we are overriding the entire map, even if some map items weren't given. +// // In case of loading a partial or incomplete map, ensure that all 128 map items are filled. +// for(int i = 0; i < unused_index_sz; ++i) +// { +// if(unused_enotes_cnt >= unused_enotes_sz) +// { +// fprintf(stderr, "WorkingDrumMapPatchList::read: Error filling unused items: unused_enotes_cnt >= unused_enotes_sz:%d\n", +// unused_enotes_sz); +// break; +// } +// // Set the enote. +// new_wdme._mapItem.enote = unused_enotes[unused_enotes_cnt++]; +// // Might as well set the anote to enote. +// new_wdme._mapItem.anote = new_wdme._mapItem.enote; +// // Insert the item at the unused index. +// wdml.insert(WorkingDrumMapPatchInsertPair_t(unused_index[i], new_wdme)); +// } +// //} + + // Insert the working drum map list at the patch. + insert(WorkingDrumMapPatchListInsertPair_t(patch, wdml)); + } + return; + } + default: + break; + } + } +} + +void WorkingDrumMapPatchList::write(int level, Xml& xml) const +{ + int patch; + for(ciWorkingDrumMapPatchList_t iwdpl = begin(); iwdpl != end(); ++iwdpl) + { + const WorkingDrumMapList& wdml = iwdpl->second; + if(wdml.empty()) + continue; + patch = iwdpl->first; + xml.tag(level++, "drumMapPatch patch=\"%d\"", patch); + wdml.write(level, xml); + xml.etag(--level, "drumMapPatch"); + } +} + + +//--------------------------------------------------------- +// ChannelDrumMappingList +//--------------------------------------------------------- + +ChannelDrumMappingList::ChannelDrumMappingList() +{ + // Ensure there is always a default channel. + // Initialize with a default drum map on default channel. Patch is default 0xffffff. GM-1 does not specify a drum patch number. + add(-1, patch_drummap_mapping_list_t()); +} + +void ChannelDrumMappingList::add(const ChannelDrumMappingList& other) +{ + int channel; + + for(ciChannelDrumMappingList_t icdml = other.begin(); icdml != other.end(); ++icdml) + { + channel = icdml->first; + const patch_drummap_mapping_list_t& pdml = icdml->second; + add(channel, pdml); + } +} + +void ChannelDrumMappingList::add(int channel, const patch_drummap_mapping_list_t& list) +{ + ChannelDrumMappingListInsertResult_t res = insert(ChannelDrumMappingListInsertPair_t(channel, list)); + if(res.second == false) + { + iChannelDrumMappingList_t res_icdml = res.first; + patch_drummap_mapping_list_t& res_pdml = res_icdml->second; + res_pdml.add(list); + } +} + +patch_drummap_mapping_list_t* ChannelDrumMappingList::find(int channel, bool includeDefault) +{ + // Look for an exact match above all else. The given channel must be valid. + iChannelDrumMappingList_t icdml = ChannelDrumMappingList_t::find(channel); + // If no exact match is found we'll take a default if found. + if(icdml == end()) + { + if(!includeDefault) + return NULL; + icdml = ChannelDrumMappingList_t::find(-1); + if(icdml == end()) + return NULL; + } + return &icdml->second; +} + +const patch_drummap_mapping_list_t* ChannelDrumMappingList::find(int channel, bool includeDefault) const +{ + // Look for an exact match above all else. The given channel must be valid. + ciChannelDrumMappingList_t icdml = ChannelDrumMappingList_t::find(channel); + // If no exact match is found we'll take a default if found. + if(icdml == end()) + { + if(!includeDefault) + return NULL; + icdml = ChannelDrumMappingList_t::find(-1); + if(icdml == end()) + return NULL; + } + return &icdml->second; +} + +void ChannelDrumMappingList::read(Xml& xml) +{ + const QString start_tag = xml.s1(); + // Default "don't care" channel number, in case no channel number found. + int channel = -1; // Default. + int channel_read; + bool ok; + + for (;;) { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) { + case Xml::Error: + case Xml::End: + return; + case Xml::TagStart: + if (tag == "entry") + { + patch_drummap_mapping_list_t pdml; + pdml.read(xml); + if(!pdml.empty()) + add(channel, pdml); + } + else if (tag == "comment") + xml.parse(); + else + xml.unknown(start_tag.toLatin1().constData()); + break; + case Xml::Attribut: + if (tag == "channel") + { + channel_read = xml.s2().toInt(&ok); + if(ok) + channel = channel_read; + } + break; + case Xml::TagEnd: + if (tag == start_tag) + return; + default: + break; + } + } +} + +void ChannelDrumMappingList::write(int level, Xml& xml) const +{ + int channel; + + // Count the items used. + int sz = 0; + for(ciChannelDrumMappingList_t icdml = begin(); icdml != end(); ++icdml) + { + const patch_drummap_mapping_list_t& pdml = icdml->second; + if(pdml.empty()) + continue; + ++sz; + } + + for(ciChannelDrumMappingList_t icdml = begin(); icdml != end(); ++icdml) + { + const patch_drummap_mapping_list_t& pdml = icdml->second; + if(pdml.empty()) + continue; + channel = icdml->first; + + // Don't bother with the drumMapChannel tag if not required. + if(sz >= 2 || channel != -1) // -1 is default. + xml.tag(level++, "drumMapChannel channel=\"%d\"", channel); + + pdml.write(level, xml); + + if(sz >= 2 || channel != -1) // -1 is default. + xml.etag(--level, "drumMapChannel"); + } +} + } // namespace MusECore diff -Nru muse-2.1.2/muse/instruments/minstrument.h muse-3.0.2+ds1/muse/instruments/minstrument.h --- muse-2.1.2/muse/instruments/minstrument.h 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/instruments/minstrument.h 2018-01-26 21:59:38.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: minstrument.h,v 1.3.2.3 2009/03/09 02:05:18 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -25,9 +26,19 @@ #define __MINSTRUMENT_H__ #include "globaldefs.h" +#include #include #include +#include #include +#include "midiedit/drummap.h" + +// REMOVE Tim. newdrums. Added. +// Adds the ability to override at instrument level. +// But it just makes things too complex for the user. +// And in a way is unnecessary and overkill, since we +// already allow modifying an instrument. +//#define _USE_INSTRUMENT_OVERRIDES_ namespace MusEGui { class PopupMenu; @@ -39,7 +50,6 @@ class MidiPort; class MidiPlayEvent; class Xml; -class DrumMap; //--------------------------------------------------------- @@ -47,16 +57,26 @@ //--------------------------------------------------------- struct Patch { - // REMOVE Tim. OBSOLETE. - //signed char typ; // 1 - GM 2 - GS 4 - XG - signed char hbank, lbank, prog; + signed char hbank, lbank, program; QString name; bool drum; + + inline int patch() const { return (((unsigned int)hbank & 0xff) << 16) | (((unsigned int)lbank & 0xff) << 8) | ((unsigned int)program & 0xff); } + inline bool dontCare() const { return hbankDontCare() && lbankDontCare() && programDontCare(); } + inline bool hbankDontCare() const { return hbank < 0; } + inline bool lbankDontCare() const { return lbank < 0; } + inline bool programDontCare() const { return program < 0; } + void read(Xml&); void write(int level, Xml&); }; -typedef std::list PatchList; +class PatchList : public std::list { + public: + iterator find(int patch, bool drum, bool includeDefault); + const_iterator find(int patch, bool drum, bool includeDefault) const; +}; + typedef PatchList::iterator iPatch; typedef PatchList::const_iterator ciPatch; @@ -70,7 +90,12 @@ void read(Xml&); }; -typedef std::vector PatchGroupList; +class PatchGroupList : public std::vector { + public: + Patch* findPatch(int patch, bool drum, bool includeDefault); + Patch* findPatch(int patch, bool drum, bool includeDefault) const; +}; + typedef PatchGroupList::iterator iPatchGroup; typedef PatchGroupList::const_iterator ciPatchGroup; @@ -87,46 +112,6 @@ ~SysEx(); }; -struct patch_collection_t -{ - int first_program; - int last_program; - int first_hbank; - int last_hbank; - int first_lbank; - int last_lbank; - - patch_collection_t(int p1=0, int p2=127, int l1=0, int l2=127, int h1=0, int h2=127) - { - first_program=p1; - last_program=p2; - first_lbank=l1; - last_lbank=l2; - first_hbank=h1; - last_hbank=h2; - } - - QString to_string(); -}; - -struct patch_drummap_mapping_t -{ - patch_collection_t affected_patches; - DrumMap* drummap; - - patch_drummap_mapping_t(const patch_collection_t& a, DrumMap* d) - { - affected_patches=a; - drummap=d; - } - - patch_drummap_mapping_t(const patch_drummap_mapping_t& that); - patch_drummap_mapping_t(); - ~patch_drummap_mapping_t(); - - patch_drummap_mapping_t& operator=(const patch_drummap_mapping_t& that); -}; - struct dumb_patchlist_entry_t { int prog; @@ -165,17 +150,213 @@ } }; +struct WorkingDrumMapEntry { +#ifdef _USE_INSTRUMENT_OVERRIDES_ + enum OverrideType { NoOverride = 0x0, TrackDefaultOverride = 0x1, TrackOverride = 0x2, InstrumentDefaultOverride = 0x4, InstrumentOverride = 0x8, + AllOverrides = InstrumentDefaultOverride | InstrumentOverride | TrackDefaultOverride | TrackOverride }; +#else + enum OverrideType { NoOverride = 0x0, TrackDefaultOverride = 0x1, TrackOverride = 0x2, + AllOverrides = TrackDefaultOverride | TrackOverride }; +#endif + + enum Field { NoField = 0x0, NameField = 0x1, VolField = 0x2, QuantField = 0x4, LenField = 0x8, ChanField = 0x10, PortField = 0x20, + Lv1Field = 0x40, Lv2Field = 0x80, Lv3Field = 0x100, Lv4Field = 0x200, ENoteField = 0x400, ANoteField = 0x800, + MuteField = 0x1000, HideField = 0x2000, + AllFields = NameField | VolField | QuantField | LenField | ChanField | PortField | Lv1Field | Lv2Field | Lv3Field | Lv4Field | + ENoteField | ANoteField | MuteField | HideField}; + // OR'd Fields. + typedef int override_t; + typedef int fields_t; + + DrumMap _mapItem; + fields_t _fields; + + // Starts with null _mapItem. + WorkingDrumMapEntry(); + // Allocates _mapItem and copies dm to it. + WorkingDrumMapEntry(const DrumMap& dm, fields_t fields); + // Copy ctor. + WorkingDrumMapEntry(const WorkingDrumMapEntry&); + WorkingDrumMapEntry& operator=(const WorkingDrumMapEntry&); +}; + +typedef std::map < int /*index*/, WorkingDrumMapEntry, std::less > WorkingDrumMapList_t; +typedef WorkingDrumMapList_t::iterator iWorkingDrumMapPatch_t; +typedef WorkingDrumMapList_t::const_iterator ciWorkingDrumMapPatch_t; +typedef std::pair WorkingDrumMapPatchInsertResult_t; +typedef std::pair WorkingDrumMapPatchInsertPair_t; +typedef std::pair WorkingDrumMapPatchRangePair_t; + +class WorkingDrumMapList : public WorkingDrumMapList_t { + + public: + void add(int index,const WorkingDrumMapEntry& item); + // Returns the requested fields that were NOT removed. + int remove(int index, const WorkingDrumMapEntry& item); + // Returns the requested fields that were NOT removed. + int remove(int index, int fields); + // If fillUnused is true it will fill any unused fields. + void read(Xml&, bool fillUnused, int defaultIndex = -1); + void write(int level, Xml&) const; +}; + + +typedef std::map < int /*patch*/, WorkingDrumMapList, std::less > WorkingDrumMapPatchList_t; +typedef WorkingDrumMapPatchList_t::iterator iWorkingDrumMapPatchList_t; +typedef WorkingDrumMapPatchList_t::const_iterator ciWorkingDrumMapPatchList_t; +typedef std::pair WorkingDrumMapPatchListInsertResult_t; +typedef std::pair WorkingDrumMapPatchListInsertPair_t; + +class WorkingDrumMapPatchList : public WorkingDrumMapPatchList_t { + + public: + void add(const WorkingDrumMapPatchList& other); + void add(int patch, const WorkingDrumMapList& list); + void add(int patch, int index, const WorkingDrumMapEntry& item); + void remove(int patch, int index, const WorkingDrumMapEntry& item, bool includeDefault); + void remove(int patch, int index, int fields, bool includeDefault); + void remove(int patch, bool includeDefault); + WorkingDrumMapList* find(int patch, bool includeDefault); + const WorkingDrumMapList* find(int patch, bool includeDefault) const; + WorkingDrumMapEntry* find(int patch, int index, bool includeDefault); + const WorkingDrumMapEntry* find(int patch, int index, bool includeDefault) const; + + // If fillUnused is true it will fill any unused items to ensure that all 128 items are filled. + void read(Xml&, bool fillUnused); + void write(int level, Xml&) const; +}; + + +typedef std::map < std::string /*instrument name*/, WorkingDrumMapPatchList > WorkingDrumMapInstrumentList_t; +typedef WorkingDrumMapInstrumentList_t::iterator iWorkingDrumMapInstrumentList_t; +typedef WorkingDrumMapInstrumentList_t::const_iterator ciWorkingDrumMapInstrumentList_t; +typedef std::pair WorkingDrumMapInstrumentListInsertResult_t; +typedef std::pair WorkingDrumMapInstrumentListInsertPair_t; + + +class WorkingDrumMapInstrumentList : public WorkingDrumMapInstrumentList_t { + public: + void read(Xml&); +}; + +struct patch_drummap_mapping_t +{ + int _patch; + DrumMap* drummap; + int drum_in_map[128]; + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + // A list of user-altered drum map items. + WorkingDrumMapList _workingDrumMapList; +#endif + + // A NULL drummap is allowed, it means invalid (ie. invalid() returns true). + patch_drummap_mapping_t(DrumMap* d, int patch = 0xffffff) + { + drummap = d; + _patch = patch; + update_drum_in_map(); + } + + patch_drummap_mapping_t(const patch_drummap_mapping_t& that); + patch_drummap_mapping_t(); + ~patch_drummap_mapping_t(); + + patch_drummap_mapping_t& operator=(const patch_drummap_mapping_t& that); + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + void addWorkingDrumMapEntry(int index,const WorkingDrumMapEntry& item); + void removeWorkingDrumMapEntry(int index, const WorkingDrumMapEntry& item); + void removeWorkingDrumMapEntry(int index, int fields); +#endif + + int map_drum_in(int enote) { return drum_in_map[enote]; } + void update_drum_in_map(); + + inline int hbank() const { return (_patch >> 16) & 0xff; } + inline int lbank() const { return (_patch >> 8) & 0xff; } + inline int prog() const { return _patch & 0xff; } + inline void setHBank(int v) { _patch = (_patch & 0x00ffff) | ((v & 0xff) << 16); } + inline void setLBank(int v) { _patch = (_patch & 0xff00ff) | ((v & 0xff) << 8); } + inline void setProg(int v) { _patch = (_patch & 0xffff00) | (v & 0xff); } + inline bool dontCare() const { return hbankDontCare() && lbankDontCare() && programDontCare(); } + inline bool hbankDontCare() const { const int hb = hbank(); return hb < 0 || hb > 127; } + inline bool lbankDontCare() const { const int lb = lbank(); return lb < 0 || lb > 127; } + inline bool programDontCare() const { const int pr = prog(); return pr < 0 || pr > 127; } + + bool isPatchInRange(int patch, bool includeDefault) const; + + bool isValid() const; + + QString to_string(); +}; + +class patch_drummap_mapping_list_t : public std::list +{ + public: + void add(const patch_drummap_mapping_list_t& other); + void add(const patch_drummap_mapping_t& pdm); + iterator find(int patch, bool includeDefault); + const_iterator find(int patch, bool includeDefault) const; + void read(Xml& xml); + void write(int level, Xml&) const; +#ifdef _USE_INSTRUMENT_OVERRIDES_ + void writeDrummapOverrides(int level, Xml& xml) const; +#endif +}; + +typedef patch_drummap_mapping_list_t::iterator iPatchDrummapMapping_t; +typedef patch_drummap_mapping_list_t::const_iterator ciPatchDrummapMapping_t; + + +// NOTE: Channel == -1 (default) is legal. +typedef std::map < int /*channel*/, patch_drummap_mapping_list_t, std::less > ChannelDrumMappingList_t; +typedef ChannelDrumMappingList_t::iterator iChannelDrumMappingList_t; +typedef ChannelDrumMappingList_t::const_iterator ciChannelDrumMappingList_t; +typedef std::pair ChannelDrumMappingListInsertResult_t; +typedef std::pair ChannelDrumMappingListInsertPair_t; + +class ChannelDrumMappingList : public ChannelDrumMappingList_t { + public: + ChannelDrumMappingList(); + + void add(const ChannelDrumMappingList& other); + void add(int channel, const patch_drummap_mapping_list_t& list); + //void add(int patch, int index, const WorkingDrumMapEntry& item); + //void remove(int patch, int index, const WorkingDrumMapEntry& item, bool includeDefault); + //void remove(int patch, int index, int fields, bool includeDefault); + //void remove(int patch, bool includeDefault); + patch_drummap_mapping_list_t* find(int channel, bool includeDefault); + const patch_drummap_mapping_list_t* find(int channel, bool includeDefault) const; + //WorkingDrumMapEntry* find(int patch, int index, bool includeDefault); + //const WorkingDrumMapEntry* find(int patch, int index, bool includeDefault) const; + + // If fillUnused is true it will fill any unused items to ensure that all 128 items are filled. + void read(Xml&); + void write(int level, Xml&) const; +}; + + + //--------------------------------------------------------- // MidiInstrument //--------------------------------------------------------- class MidiInstrument { + public: + // NoteOffAll = Use all note offs. + // NoteOffNone = Do not use any note offs. + // NoteOffConvertToZVNoteOn = Convert all note offs to zero-velocity note ons. + enum NoteOffMode { NoteOffAll=0, NoteOffNone, NoteOffConvertToZVNoteOn }; + + private: PatchGroupList pg; MidiControllerList* _controller; QList _sysex; - std::list patch_drummap_mapping; + ChannelDrumMappingList _channelDrumMapping; bool _dirty; - int _nullvalue; + bool _waitForLSB; // Whether 14-bit controllers wait for LSB, or MSB and LSB are separate. + NoteOffMode _noteOffMode; void init(); @@ -192,8 +373,6 @@ void writeDrummaps(int level, Xml& xml) const; void readDrummaps(Xml& xml); - patch_drummap_mapping_t readDrummapsEntry(Xml& xml); - patch_collection_t readDrummapsEntryPatchCollection(Xml& xml); public: MidiInstrument(); @@ -202,8 +381,8 @@ const QString& iname() const { return _name; } void setIName(const QString& txt) { _name = txt; } MType midiType() const; + virtual bool isSynti() const { return false; } - //MidiInstrument& uniqueCopy(const MidiInstrument&); // Assign will 'delete' all existing patches and groups from the instrument. MidiInstrument& assign(const MidiInstrument&); QString filePath() const { return _filePath; } @@ -214,37 +393,52 @@ const QList& sysex() const { return _sysex; } void removeSysex(SysEx* sysex) { _sysex.removeAll(sysex); } void addSysex(SysEx* sysex) { _sysex.append(sysex); } - - const DrumMap* drummap_for_patch(int patch) const; + + QList getPatches(int channel, bool drum); unsigned getNextPatch(int channel, unsigned patch, bool drum); unsigned getPrevPatch(int channel, unsigned patch, bool drum); - +#ifdef _USE_INSTRUMENT_OVERRIDES_ + bool setWorkingDrumMapItem(int patch, int index, const WorkingDrumMapEntry&, bool isReset); + // Returns OR'd WorkingDrumMapEntry::OverrideType flags indicating whether a map item's members, + // given by 'fields' (OR'd WorkingDrumMapEntry::Fields), are either the original or working map item. + // Here in MidiInstrument the flags can be NoOverride and InstrumentOverride. See corresponding function + // in MidiTrack for additional TrackOverride flag use. + int isWorkingMapItem(int patch, int index, int fields) const; + void clearDrumMapOverrides(); +#endif + // Returns a map item with members filled from either the original or working map item, + // depending on which Field flags are set. The returned map includes any requested + // WorkingDrumMapEntry::OverrideType instrument overrides. Channel can be -1 meaning default. + virtual void getMapItem(int channel, int patch, int index, DrumMap& dest_map, int overrideType = WorkingDrumMapEntry::AllOverrides) const; + EventList* midiInit() const { return _midiInit; } EventList* midiReset() const { return _midiReset; } EventList* midiState() const { return _midiState; } const char* initScript() const { return _initScript; } MidiControllerList* controller() const { return _controller; } - int nullSendValue() { return _nullvalue; } - void setNullSendValue(int v) { _nullvalue = v; } + bool waitForLSB() { return _waitForLSB; } + void setWaitForLSB(bool v) { _waitForLSB = v; } + + // Virtual so that inheriters (synths etc) can return whatever they want. + virtual NoteOffMode noteOffMode() const { return _noteOffMode; } + // For non-synths, users can set this value. + void setNoteOffMode(NoteOffMode mode) { _noteOffMode = mode; } void readMidiState(Xml& xml); - virtual bool guiVisible() const { return false; } - virtual void showGui(bool) {} - virtual bool hasGui() const { return false; } - virtual bool nativeGuiVisible() const { return false; } - virtual void showNativeGui(bool) {} - virtual bool hasNativeGui() const { return false; } - virtual void writeToGui(const MidiPlayEvent&) {} - virtual void reset(int); - virtual QString getPatchName(int,int,bool) const; + virtual QString getPatchName(int channel, int prog, bool drum, bool includeDefault) const; virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool); + static void populateInstrPopup(MusEGui::PopupMenu*, MidiInstrument* current = 0, bool show_synths = false); // Static void read(Xml&); void write(int level, Xml&); - +#ifdef _USE_INSTRUMENT_OVERRIDES_ + void writeDrummapOverrides(int level, Xml&) const; +#endif + PatchGroupList* groups() { return &pg; } - std::list* get_patch_drummap_mapping() { return &patch_drummap_mapping; } + patch_drummap_mapping_list_t* get_patch_drummap_mapping(int channel, bool includeDefault); + ChannelDrumMappingList* getChannelDrumMapping() { return &_channelDrumMapping; } }; //--------------------------------------------------------- @@ -255,9 +449,14 @@ public: MidiInstrumentList() {} + iterator find(const MidiInstrument* instr); +#ifdef _USE_INSTRUMENT_OVERRIDES_ + void writeDrummapOverrides(int level, Xml&) const; +#endif }; typedef MidiInstrumentList::iterator iMidiInstrument; +typedef MidiInstrumentList::const_iterator ciMidiInstrument; extern MidiInstrumentList midiInstruments; extern MidiInstrument* genericMidiInstrument; @@ -268,5 +467,11 @@ } // namespace MusECore +#ifdef _USE_INSTRUMENT_OVERRIDES_ +namespace MusEGlobal { + extern MusECore::WorkingDrumMapInstrumentList workingDrumMapInstrumentList; +} +#endif + #endif diff -Nru muse-2.1.2/muse/keyevent.cpp muse-3.0.2+ds1/muse/keyevent.cpp --- muse-2.1.2/muse/keyevent.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/keyevent.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -30,6 +30,7 @@ #include "gconfig.h" #include "xml.h" #include "keyevent.h" +#include "operations.h" namespace MusEGlobal { MusECore::KeyList keymap; @@ -69,6 +70,47 @@ } } +void KeyList::add(KeyEvent e) +{ + int tick = e.tick; + key_enum k = e.key; + std::pair res = insert(std::pair (tick, e)); + if(!res.second) + { + fprintf(stderr, "KeyList::add insert failed: keylist:%p key:%d tick:%d\n", + this, e.key, e.tick); + } + else + { + iKeyEvent ike = res.first; + ++ike; // There is always a 'next' key event - there is always one at index MAX_TICK. + KeyEvent& ne = ike->second; + + // Swap the values. (This is how the key list works.) + e.key = ne.key; + e.tick = ne.tick; + ne.key = k; + ne.tick = tick; + } +} + +//--------------------------------------------------------- +// addOperation +//--------------------------------------------------------- + +void KeyList::addOperation(unsigned tick, key_enum key, PendingOperationList& ops) +{ + if (tick > MAX_TICK) + tick = MAX_TICK; + + iKeyEvent e = upper_bound(tick); + if(tick == e->second.tick) + ops.add(PendingOperationItem(this, e, key, PendingOperationItem::ModifyKey)); + else + // These are the desired tick and key but add will do the proper swapping with next event. + ops.add(MusECore::PendingOperationItem(this, key, tick, PendingOperationItem::AddKey)); +} + //--------------------------------------------------------- // KeyList::dump //--------------------------------------------------------- @@ -135,15 +177,19 @@ } //--------------------------------------------------------- -// change +// delOperation //--------------------------------------------------------- -void KeyList::change(unsigned tick, key_enum newkey) - { - iKeyEvent e = find(tick); - e->second.key = newkey; - } - +void KeyList::delOperation(unsigned tick, PendingOperationList& ops) +{ + iKeyEvent e = find(tick); + if (e == end()) { + printf("KeyList::delOperation tick:%d not found\n", tick); + return; + } + PendingOperationItem poi(this, e, PendingOperationItem::DeleteKey); + ops.add(poi); +} //--------------------------------------------------------- // addKey diff -Nru muse-2.1.2/muse/keyevent.h muse-3.0.2+ds1/muse/keyevent.h --- muse-2.1.2/muse/keyevent.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/keyevent.h 2017-12-04 21:01:18.000000000 +0000 @@ -32,6 +32,8 @@ namespace MusECore { class Xml; +class PendingOperationList; +struct PendingOperationItem; //don't change this enum! changing the numeric values will affect //all files using key_enum, and even worse: @@ -95,8 +97,10 @@ class KeyList : public KEYLIST { + friend struct PendingOperationItem; + void add(unsigned tick, key_enum tempo); - void change(unsigned tick, key_enum newKey); + void add(KeyEvent e); void del(iKeyEvent); void del(unsigned tick); @@ -113,6 +117,9 @@ void addKey(unsigned t, key_enum newKey); void delKey(unsigned tick); + + void addOperation(unsigned tick, key_enum key, PendingOperationList& ops); + void delOperation(unsigned tick, PendingOperationList& ops); }; } // namespace MusECore diff -Nru muse-2.1.2/muse/latency_compensator.cpp muse-3.0.2+ds1/muse/latency_compensator.cpp --- muse-2.1.2/muse/latency_compensator.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/latency_compensator.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,134 @@ +//=================================================================== +// MusE +// Linux Music Editor +// +// latency_compensator.cpp +// (C) Copyright 2016 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 +// 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 + +#include "latency_compensator.h" + +#define DELAY_BUFFER_SIZE 16384 + +namespace MusECore { + +LatencyCompensator::LatencyCompensator(unsigned long channels, unsigned long bufferSize) + : _channels(channels), _bufferSize(bufferSize) +{ + _buffer = new float*[channels]; + _delays = new unsigned long[channels]; + _writePointers = new unsigned long[channels]; + + for(int i = 0; i < _channels; ++i) + { + _buffer[i] = new float[_bufferSize]; + memset(_buffer[i], 0, sizeof(float) * _bufferSize); + _delays[i] = 0; + _writePointers[i] = 0; + } +} + +LatencyCompensator::~LatencyCompensator() +{ + for(int i = 0; i < _channels; ++i) + delete [] _buffer[i]; + delete [] _buffer; + delete [] _delays; + delete [] _writePointers; +} + +void LatencyCompensator::clear() +{ + for(int i = 0; i < _channels; ++i) + memset(_buffer[i], 0, sizeof(float) * _bufferSize); +} + +void LatencyCompensator::setBufferSize(unsigned long size) +{ + _bufferSize = size; + for(int i = 0; i < _channels; ++i) + { + delete [] _buffer[i]; + _buffer[i] = new float[_bufferSize]; + memset(_buffer[i], 0, sizeof(float) * _bufferSize); + } +} + +void LatencyCompensator::setChannels(unsigned long channels) +{ + for(int i = 0; i < _channels; ++i) + delete [] _buffer[i]; + delete [] _buffer; + delete [] _delays; + delete [] _writePointers; + + _buffer = new float*[channels]; + _delays = new unsigned long[channels]; + _writePointers = new unsigned long[channels]; + + for(int i = 0; i < _channels; ++i) + { + _buffer[i] = new float[_bufferSize]; + memset(_buffer[i], 0, sizeof(float) * _bufferSize); + _delays[i] = 0; + _writePointers[i] = 0; + } +} + +void LatencyCompensator::run(unsigned long SampleCount, float** data) +{ + float inputSample; + unsigned long readOffset; + unsigned long bufsz_mask; + unsigned long writeOffset; + unsigned long i; + + bufsz_mask = _bufferSize - 1; + + float* input; + float* output; + float* buf; + + for(int ch = 0; ch < _channels; ++ch) + { + input = data[ch]; + output = data[ch]; + buf = _buffer[ch]; + + writeOffset = _writePointers[ch]; + readOffset = writeOffset + _bufferSize - _delays[ch]; + + for(i = 0; i < SampleCount; i++) + { + inputSample = *(input++); + + *(output++) = (buf[((i + readOffset) & bufsz_mask)]); + //*(output++) = (buf[((i + readOffset) % _bufferSize)]); + + buf[((i + writeOffset) & bufsz_mask)] = inputSample; + //buf[((i + writeOffset) % _bufferSize)] = inputSample; + } + + _writePointers[ch] = (_writePointers[ch] + SampleCount) & bufsz_mask; + //_writePointers[ch] = (_writePointers[ch] + SampleCount) % _bufferSize; + } +} + +} // namespace MusECore diff -Nru muse-2.1.2/muse/latency_compensator.h muse-3.0.2+ds1/muse/latency_compensator.h --- muse-2.1.2/muse/latency_compensator.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/latency_compensator.h 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,52 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// latency_compensator.h +// (C) Copyright 2016 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 +// 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 __LATENCY_COMPENSATOR_H__ +#define __LATENCY_COMPENSATOR_H__ + +namespace MusECore { + +class LatencyCompensator +{ + private: + unsigned long _channels; + unsigned long _bufferSize; // Must be power of two. + unsigned long* _writePointers; + unsigned long* _delays; // One for each channel. In samples. + float** _input; + float** _output; + float** _buffer; + + public: + LatencyCompensator(unsigned long channels = 1, unsigned long bufferSize = 16384); + virtual ~LatencyCompensator(); + + void clear(); + void setBufferSize(unsigned long size); + void setChannels(unsigned long channels); + void run(unsigned long SampleCount, float** data); +}; + +} // namespace MusECore + +#endif diff -Nru muse-2.1.2/muse/liste/CMakeLists.txt muse-3.0.2+ds1/muse/liste/CMakeLists.txt --- muse-2.1.2/muse/liste/CMakeLists.txt 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/liste/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,34 +24,14 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( liste_mocs - # listedit.h - # ctrllistedit.h - # tracklistedit.h - # partlistedit.h - # ieventdialog.h - editevent.h +QT5_WRAP_CPP ( liste_mocs listedit.h ) ## -## UI files -## -file (GLOB liste_ui_files - editctrlbase.ui - ) -QT4_WRAP_UI ( liste_uis ${liste_ui_files} ) - -## ## List of source files to compile ## file (GLOB liste_source_files - # listedit.cpp - # ctrllistedit.cpp - # partlistedit.cpp - # tracklistedit.cpp - # ieventdialog.cpp - editevent.cpp listedit.cpp ) @@ -61,7 +41,6 @@ add_library ( liste ${MODULES_BUILD} ${liste_source_files} ${liste_mocs} - ${liste_uis} ) ## @@ -70,7 +49,6 @@ set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${liste_source_files} - ${liste_ui_files} CACHE INTERNAL "" ) diff -Nru muse-2.1.2/muse/liste/editctrlbase.ui muse-3.0.2+ds1/muse/liste/editctrlbase.ui --- muse-2.1.2/muse/liste/editctrlbase.ui 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/liste/editctrlbase.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,845 +0,0 @@ - - - EditCtrlBase - - - - 0 - 0 - 601 - 413 - - - - MusE: Edit Controller Event - - - true - - - - - - Time Position - - - false - - - - - - - Available Controller: - - - false - - - - - - - Create New Controller - - - - - - true - - - true - - - - - - - - - - - - - - - 0 - 0 - 0 - - - - - - - 238 - 234 - 222 - - - - - - - 255 - 255 - 255 - - - - - - - 246 - 244 - 238 - - - - - - - 119 - 117 - 111 - - - - - - - 158 - 155 - 147 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 104 - 137 - 236 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 128 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 238 - 234 - 222 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 119 - 117 - 111 - - - - - - - 158 - 155 - 147 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 104 - 137 - 236 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 128 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 192 - - - - - - - 128 - 0 - 128 - - - - - - - - - 128 - 128 - 128 - - - - - - - 238 - 234 - 222 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 119 - 117 - 111 - - - - - - - 158 - 155 - 147 - - - - - - - 128 - 128 - 128 - - - - - - - 255 - 255 - 255 - - - - - - - 128 - 128 - 128 - - - - - - - 255 - 255 - 255 - - - - - - - 104 - 137 - 236 - - - - - - - 0 - 0 - 0 - - - - - - - 0 - 0 - 128 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 192 - - - - - - - 128 - 0 - 128 - - - - - - - - 0 - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 40 - - - - - - - - Controller - - - false - - - - - - - QFrame::Sunken - - - textLabel3 - - - false - - - - - - - Note - - - false - - - - - - - 127 - - - - - - - Value - - - false - - - - - - - 127 - - - - - - - 127 - - - Qt::Horizontal - - - - - - - - - - - H-Bank - - - false - - - - - - - L-Bank - - - false - - - - - - - Program - - - false - - - - - - - off - - - 0 - - - 128 - - - 0 - - - - - - - off - - - 0 - - - 128 - - - 0 - - - - - - - 1 - - - 128 - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 140 - - - - - - - - pushButton4 - - - - - - - - - - - 6 - - - 0 - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - - - &OK - - - - - - true - - - true - - - - - - - &Cancel - - - - - - true - - - - - - - - - QFrame::HLine - - - QFrame::Raised - - - 3 - - - 2 - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 60 - 20 - - - - - - - - QFrame::VLine - - - QFrame::Raised - - - 3 - - - 2 - - - - - - - - - Awl::PosEdit - QWidget -

    awl/posedit.h
    - - - - - - valSlider - valueChanged(int) - valSpinBox - setValue(int) - - - 20 - 20 - - - 20 - 20 - - - - - valSpinBox - valueChanged(int) - valSlider - setValue(int) - - - 20 - 20 - - - 20 - 20 - - - - - diff -Nru muse-2.1.2/muse/liste/editevent.cpp muse-3.0.2+ds1/muse/liste/editevent.cpp --- muse-2.1.2/muse/liste/editevent.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/liste/editevent.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,913 +0,0 @@ -//========================================================= -// MusE -// Linux Music Editor -// $Id: editevent.cpp,v 1.12.2.6 2009/02/02 21:38:00 terminator356 Exp $ -// (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) -// -// 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "awl/posedit.h" - -#include "song.h" -#include "event.h" -#include "midictrl.h" -#include "editevent.h" -#include "pitchedit.h" -#include "intlabel.h" -#include "globals.h" -#include "gconfig.h" -#include "midiport.h" -#include "midiedit/drummap.h" -#include "instruments/minstrument.h" -#include "midi.h" -#include "popupmenu.h" - -namespace MusEGui { - -//--------------------------------------------------------- -// string2qhex -//--------------------------------------------------------- - -QString string2hex(const unsigned char* data, int len) - { - QString d; - QString s; - for (int i = 0; i < len; ++i) { - if ((i > 0) && ((i % 8)==0)) { - d += "\n"; - } - else if (i) - d += " "; - d += s.sprintf("%02x", data[i]); - } - return d; - } - -//--------------------------------------------------------- -// hex2string -//--------------------------------------------------------- - -char* hex2string(QWidget* parent, const char* src, int& len) - { - char buffer[2048]; - char* dst = buffer; - - while (*src) { - while (*src == ' ' || *src == '\n') - ++src; - char* ep; - long val = strtol(src, &ep, 16); - if (ep == src) { - QMessageBox::information(parent, - QString("MusE"), - QWidget::tr("Cannot convert sysex string")); - return 0; - } - src = ep; - *dst++ = val; - if (dst - buffer >= 2048) { - QMessageBox::information(parent, - QString("MusE"), - QWidget::tr("Hex String too long (2048 bytes limit)")); - return 0; - } - } - len = dst - buffer; - if(len == 0) - return 0; - char* b = new char[len+1]; - memcpy(b, buffer, len); - b[len] = 0; - return b; - } - -//--------------------------------------------------------- -// getEvent -//--------------------------------------------------------- - -MusECore::Event EditNoteDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) - { - EditNoteDialog* dlg = new EditNoteDialog(tick, event, parent); - MusECore::Event nevent; - if (dlg->exec() == QDialog::Accepted) { - nevent = dlg->event(); - } - delete dlg; - return nevent; - } - -MusECore::Event EditSysexDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) - { - EditSysexDialog* dlg = new EditSysexDialog(tick, event, parent); - MusECore::Event nevent; - if (dlg->exec() == QDialog::Accepted) { - nevent = dlg->event(); - } - delete dlg; - return nevent; - } - -MusECore::Event EditMetaDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) - { - EditEventDialog* dlg = new EditMetaDialog(tick, event, parent); - MusECore::Event nevent; - if (dlg->exec() == QDialog::Accepted) { - nevent = dlg->event(); - } - delete dlg; - return nevent; - } - -//--------------------------------------------------------- -// EditEventDialog -//--------------------------------------------------------- - -EditEventDialog::EditEventDialog(QWidget* parent) - : QDialog(parent) - { - QVBoxLayout* xlayout = new QVBoxLayout; - layout1 = new QGridLayout; // ddskrjo this - xlayout->addLayout(layout1); - - //--------------------------------------------------- - // Ok, Cancel - //--------------------------------------------------- - - QBoxLayout* w5 = new QHBoxLayout; // ddskrjo this - QPushButton* okB = new QPushButton(tr("Ok")); - okB->setDefault(true); - QPushButton* cancelB = new QPushButton(tr("Cancel")); - okB->setFixedWidth(80); - cancelB->setFixedWidth(80); - w5->addWidget(okB); - w5->addSpacing(12); - w5->addWidget(cancelB); - w5->addStretch(1); - xlayout->addLayout(w5); - setLayout(xlayout); - connect(cancelB, SIGNAL(clicked()), SLOT(reject())); - connect(okB, SIGNAL(clicked()), SLOT(accept())); - } - -//--------------------------------------------------------- -// EditNoteDialog -//--------------------------------------------------------- - -EditNoteDialog::EditNoteDialog(int tick, const MusECore::Event& event, - QWidget* parent) - : QDialog(parent) - { - setupUi(this); - if (!event.empty()) { - epos->setValue(tick); - il1->setValue(event.lenTick()); - pl->setValue(event.pitch()); - il2->setValue(event.velo()); - il3->setValue(event.veloOff()); - } - else { - epos->setValue(tick); - il1->setValue(96); - pl->setValue(64); - il2->setValue(100); - il3->setValue(0); - } - } - -//--------------------------------------------------------- -// EditNoteDialog::event -//--------------------------------------------------------- - -MusECore::Event EditNoteDialog::event() - { - MusECore::Event event(MusECore::Note); - event.setTick(epos->pos().tick()); - event.setA(pl->value()); - event.setB(il2->value()); - event.setC(il3->value()); - event.setLenTick(il1->value()); - return event; - } - -//--------------------------------------------------------- -// EditSysExDialog -//--------------------------------------------------------- - -EditSysexDialog::EditSysexDialog(int tick, const MusECore::Event& event, - QWidget* parent) - : QDialog(parent) - { - setupUi(this); - sysex = 0; - if (!event.empty()) { - epos->setValue(tick); - edit->setText(string2hex(event.data(), event.dataLen())); - } - else { - epos->setValue(tick); - } - } - -//--------------------------------------------------------- -// ~EditSysexDialog -//--------------------------------------------------------- - -EditSysexDialog::~EditSysexDialog() - { - if (sysex) - delete sysex; - } - -//--------------------------------------------------------- -// EditSysExDialog::event -//--------------------------------------------------------- - -MusECore::Event EditSysexDialog::event() - { - MusECore::Event event(MusECore::Sysex); - event.setTick(epos->pos().tick()); - event.setData(sysex, len); - return event; - } - -//--------------------------------------------------------- -// accept -//--------------------------------------------------------- - -void EditSysexDialog::accept() - { - QString qsrc = edit->toPlainText(); - QByteArray ba = qsrc.toLatin1(); - const char* src = ba.constData(); - - sysex = (unsigned char*)hex2string(this, src, len); - if (sysex) - QDialog::accept(); - } - -//--------------------------------------------------------- -// EditMetaDialog -//--------------------------------------------------------- - -EditMetaDialog::EditMetaDialog(int tick, const MusECore::Event& ev, - QWidget* parent) - : EditEventDialog(parent) - { - meta = 0; - setWindowTitle(tr("MusE: Enter Meta Event")); - - QLabel* l1 = new QLabel(tr("Time Position")); - ///epos = new PosEdit; - epos = new Awl::PosEdit; - - QLabel* l2 = new QLabel(tr("Meta Type")); - il2 = new MusEGui::IntLabel(-1, 0, 127, this, -1); - il2->setFixedWidth(100); - il2->setFrame(true); - il2->setDark(); - typeLabel = new QLabel; - typeLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - QHBoxLayout* typeLayout = new QHBoxLayout; - typeLayout->addWidget(il2); - typeLayout->addWidget(typeLabel); - typeLayout->addStretch(); - - hexButton = new QRadioButton(tr("Enter Hex")); - hexButton->setChecked(true); - connect(hexButton, SIGNAL(toggled(bool)), SLOT(toggled(bool))); - - edit = new QTextEdit; - edit->setFont(MusEGlobal::config.fonts[0]); - - if (!ev.empty()) { - epos->setValue(tick); - il2->setValue(ev.dataA()); - toggled(true); - edit->setText(string2hex(ev.data(), ev.dataLen())); - } - else { - epos->setValue(tick); - il2->setValue(0); - } - - typeChanged(il2->value()); - connect(il2, SIGNAL(valueChanged(int)), SLOT(typeChanged(int))); - - layout1->addWidget(l1, 0, 0); - layout1->addWidget(epos, 0, 1, Qt::AlignLeft); - layout1->addWidget(l2, 1, 0); - - //layout1->addWidget(il2, 1, 1, AlignLeft); - layout1->addLayout(typeLayout, 1, 1); - - //layout1->addMultiCellWidget(hexButton, 2, 2, 0, 1); - //layout1->addMultiCellWidget(edit, 3, 3, 0, 1); - layout1->addWidget(hexButton, 2, 0, 1, 2); - layout1->addWidget(edit, 3, 0, 1, 2); - } - -//--------------------------------------------------------- -// typeChanged -//--------------------------------------------------------- - -void EditMetaDialog::typeChanged(int val) -{ - typeLabel->setText(MusECore::midiMetaName(val)); -} - -//--------------------------------------------------------- -// toggled -//--------------------------------------------------------- - -void EditMetaDialog::toggled(bool flag) - { - QString qsrc = edit->toPlainText(); - QByteArray ba = qsrc.toLatin1(); - const char* src = ba.constData(); - edit->clear(); - - QString dst; - if (flag) { // convert to hex - dst = string2hex((unsigned char*)src, ba.length()); - } - else { // convert to string - int len; - dst = hex2string(this, src, len); - } - edit->setText(dst); - } - -//--------------------------------------------------------- -// ~EditMetaDialog -//--------------------------------------------------------- - -EditMetaDialog::~EditMetaDialog() - { - if (meta) - delete meta; - } - -//--------------------------------------------------------- -// EditMetaDialog::event -//--------------------------------------------------------- - -MusECore::Event EditMetaDialog::event() - { - MusECore::Event event(MusECore::Meta); - event.setTick(epos->pos().tick()); - event.setA(il2->value()); - event.setData(meta, len); // TODO ?? - return event; - } - -//--------------------------------------------------------- -// accept -//--------------------------------------------------------- - -void EditMetaDialog::accept() - { - QString qsrc = edit->toPlainText(); - QByteArray ba = qsrc.toLatin1(); - const char* src = ba.constData(); - if (!hexButton->isChecked()) { - meta = (unsigned char*)strdup(src); - len = ba.length(); - QDialog::accept(); - return; - } - - meta = (unsigned char*)hex2string(this, src, len); - if (meta) - QDialog::accept(); - } - -//--------------------------------------------------------- -// getEvent -//--------------------------------------------------------- - -MusECore::Event EditCtrlDialog::getEvent(int tick, const MusECore::Event& event, - const MusECore::MidiPart* part, QWidget* parent) - { - EditCtrlDialog* dlg = new EditCtrlDialog(tick, event, part, parent); - MusECore::Event nevent; - if (dlg->exec() == QDialog::Accepted) { - nevent = dlg->event(); - } - delete dlg; - return nevent; - } - -//--------------------------------------------------------- -// EditCtrlDialog::event -//--------------------------------------------------------- - -MusECore::Event EditCtrlDialog::event() - { - MusECore::Event event(MusECore::Controller); - event.setTick(timePos->pos().tick()); - - int cnum = 0; - QListWidgetItem* item = ctrlList->currentItem(); - if(item != 0) - cnum = item->data(Qt::UserRole).toInt(); - - MusECore::MidiTrack* track = part->track(); - bool isDrum = track->type() == MusECore::Track::DRUM; - MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; - int channel = track->outChannel(); - - int evnum = cnum; - int num = cnum; - if((cnum & 0xff) == 0xff) - { - evnum = (cnum & ~0xff) | (noteSpinBox->value() & 0x7f); - num = evnum; - if(isDrum) - { - MusECore::DrumMap* dm = &MusEGlobal::drumMap[noteSpinBox->value() & 0x7f]; - num = (cnum & ~0xff) | dm->anote; - // Default to track port if -1 and track channel if -1. - if(dm->port != -1) - port = &MusEGlobal::midiPorts[dm->port]; - if(dm->channel != -1) - channel = dm->channel; - } - } - - MusECore::MidiController* c = port->midiController(cnum); - MusECore::MidiCtrlValListList* cll = port->controller(); - - if(cll->find(channel, num) == cll->end()) - { - MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); - cll->add(channel, vl); - } - - event.setA(evnum); - if(cnum == MusECore::CTRL_PROGRAM) - { - int hb = hbank->value(); - int lb = lbank->value(); - int prog = program->value(); - if (hb > 0 && hb < 129) - hb -= 1; - else - hb = 0xff; - if (lb > 0 && lb < 129) - lb -= 1; - else - lb = 0xff; - if (prog > 0 && prog < 129) - prog -= 1; - else - prog = 0xff; - int val = (hb << 16) + (lb << 8) + prog; - event.setB(val); - } - else - event.setB(valSlider->value() + c->bias()); - return event; - } - -//--------------------------------------------------------- -// EditCtrlDialog -// PosEdit* timePos; -// QSlider* valSlider; -// QSpinBox* valSpinBox; -// QLabel* controllerName; -// QListWidget* ctrlList; -// QPushButton* buttonNewController; -//--------------------------------------------------------- - -struct CI { - int num; - QString s; - bool used; - bool off; - bool instrument; - CI(int n, const QString& ss, bool u, bool o, bool i) : num(n), s(ss), used(u), off(o), instrument(i) {} - }; - -EditCtrlDialog::EditCtrlDialog(int tick, const MusECore::Event& event, - const MusECore::MidiPart* p, QWidget* parent) - : QDialog(parent), part(p) - { - setupUi(this); - widgetStack->setAutoFillBackground(true); - - MusECore::MidiTrack* track = part->track(); - MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; - bool isDrum = track->type() == MusECore::Track::DRUM; - bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; - bool isMidi = track->type() == MusECore::Track::MIDI; - MusECore::MidiCtrlValListList* cll = port->controller(); - int channel = track->outChannel(); - MusECore::MidiInstrument* instr = port->instrument(); - MusECore::MidiControllerList* mcl = instr->controller(); - int val = 0; - int ev_num = 0; - int num = 0; - int ev_cnum = 0; - int ev_note = -1; - if (!event.empty()) { - ev_num = event.dataA(); - num = ev_num; - ev_cnum = ev_num; - val = event.dataB(); - if(port->drumController(ev_num)) - { - ev_cnum |= 0xff; - if(isDrum) - num = (ev_num & ~0xff) | MusEGlobal::drumMap[ev_num & 0xff].anote; - ev_note = ev_num & 0xff; - } - } - - MusECore::MidiController* mc = port->midiController(ev_num); - - ctrlList->clear(); - ctrlList->setSelectionMode(QAbstractItemView::SingleSelection); - - //--------------------------------------------------- - // build list of midi controllers for current - // MusECore::MidiPort/channel - //--------------------------------------------------- - - std::list sList; - typedef std::list::iterator isList; - std::set already_added_nums; - - for (MusECore::iMidiCtrlValList it = cll->begin(); it != cll->end(); ++it) { - MusECore::MidiCtrlValList* cl = it->second; - int ch = it->first >> 24; - if(ch != channel) - continue; - MusECore::MidiController* c = port->midiController(cl->num()); - bool isDrumCtrl = (c->isPerNoteController()); - int show = c->showInTracks(); - int cnum = c->num(); - int clnum = cl->num(); - isList i = sList.begin(); - for (; i != sList.end(); ++i) { - if (i->num == cnum) - break; - } - - if (i == sList.end()) { - bool used = (clnum == num); - bool off = cl->hwVal() == MusECore::CTRL_VAL_UNKNOWN; // Does it have a value or is it 'off'? - // Filter if not used and off. But if there's something there, we must show it. - //if(!used && off && - if(!used && //off && - (((isDrumCtrl || isNewDrum) && !(show & MusECore::MidiController::ShowInDrum)) || - (isMidi && !(show & MusECore::MidiController::ShowInMidi)))) - continue; - bool isinstr = mcl->find(cnum) != mcl->end(); - // Need to distinguish between global default controllers and - // instrument defined controllers. Instrument takes priority over global - // ie they 'overtake' definition of a global controller such that the - // global def is no longer available. - //sList.push_back(CI(num, - sList.push_back(CI(cnum, - isinstr ? MusECore::midiCtrlNumString(cnum, true) + c->name() : MusECore::midiCtrlName(cnum, true), - used, off, isinstr)); - already_added_nums.insert(num); - } - } - - // Add instrument-defined controllers: - QListWidgetItem* sel_item = 0; - for (isList i = sList.begin(); i != sList.end(); ++i) - { - // Filter if not used and off. But if there's something there, we must show it. - if(!i->instrument && !i->used && i->off) - continue; - QListWidgetItem* item = new QListWidgetItem(i->s, ctrlList); - item->setData(Qt::UserRole, i->num); - if(i->num == ev_cnum) - sel_item = item; - } - if(sel_item) - ctrlList->setCurrentItem(sel_item); - - valSlider->setRange(mc->minVal(), mc->maxVal()); - valSpinBox->setRange(mc->minVal(), mc->maxVal()); - - controllerName->setText(mc->name()); - - if(!event.empty()) - { - if(ev_num == MusECore::CTRL_PROGRAM) - { - widgetStack->setCurrentIndex(1); - updatePatch(val); - } - else - { - widgetStack->setCurrentIndex(0); - valSlider->setValue(val - mc->bias()); - - if(mc->isPerNoteController()) - { - noteSpinBox->setVisible(true); - noteSpinBox->setEnabled(true); - noteLabel->setVisible(true); - noteLabel->setEnabled(true); - if(ev_note != -1) - noteSpinBox->setValue(ev_note); - } - else - { - noteSpinBox->setEnabled(false); - noteSpinBox->setVisible(false); - noteLabel->setEnabled(false); - noteLabel->setVisible(false); - } - } - } - else - { - noteSpinBox->setEnabled(false); - noteSpinBox->setVisible(false); - noteLabel->setEnabled(false); - noteLabel->setVisible(false); - if(sel_item) - ctrlListClicked(sel_item); - } - connect(ctrlList, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(ctrlListClicked(QListWidgetItem*))); - connect(buttonNewController, SIGNAL(clicked()), SLOT(newController())); - connect(hbank, SIGNAL(valueChanged(int)), SLOT(programChanged())); - connect(lbank, SIGNAL(valueChanged(int)), SLOT(programChanged())); - connect(program, SIGNAL(valueChanged(int)), SLOT(programChanged())); - connect(patchName, SIGNAL(released()), SLOT(instrPopup())); - connect(buttonCancel, SIGNAL(clicked()), SLOT(reject())); - connect(buttonOk, SIGNAL(clicked()), SLOT(accept())); - timePos->setValue(tick); - - } -//--------------------------------------------------------- -// newController -//--------------------------------------------------------- - -void EditCtrlDialog::newController() - { - MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); - - // populate popup with all controllers available for - // current instrument - - MusECore::MidiTrack* track = part->track(); - int portn = track->outPort(); - MusECore::MidiPort* port = &MusEGlobal::midiPorts[portn]; - bool isDrum = track->type() == MusECore::Track::DRUM; - bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; - bool isMidi = track->type() == MusECore::Track::MIDI; - MusECore::MidiInstrument* instr = port->instrument(); - MusECore::MidiControllerList* mcl = instr->controller(); - - MusECore::MidiCtrlValListList* cll = port->controller(); - int channel = track->outChannel(); - for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) - { - MusECore::MidiController* c = ci->second; - int cnum = c->num(); - int show = c->showInTracks(); - if(((isDrum || isNewDrum) && !(show & MusECore::MidiController::ShowInDrum)) || - (isMidi && !(show & MusECore::MidiController::ShowInMidi))) - continue; - // If it's not already in the parent menu... - int idx = 0; - for(; idx < ctrlList->count(); ++idx) { - if(ctrlList->item(idx)->data(Qt::UserRole).toInt() == cnum) - break; - } - if(idx >= ctrlList->count()) { - QAction* act = pup->addAction(MusECore::midiCtrlNumString(cnum, true) + c->name()); - act->setData(cnum); - } - } - - QAction* act = pup->exec(buttonNewController->mapToGlobal(QPoint(0,0))); - if (act && act->data().toInt() != -1) { - int rv = act->data().toInt(); - int cnum = rv; - for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) { - MusECore::MidiController* mc = ci->second; - if (mc->num() == cnum) { - // Create a new controller list if it does not exist. - // FIXME: Sorry no per-pitch controller lists created here - // (meaning you should only create one 'new' one at a time) - // because the user has not had a chance to choose a pitch yet. - // They are handled in accept(), where there are more checks and creations. - if(!mc->isPerNoteController() && cll->find(channel, rv) == cll->end()) - { - MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(rv); - cll->add(channel, vl); - } - int idx = 0; - for (; idx < ctrlList->count() ;++idx) { - QListWidgetItem* item = ctrlList->item(idx); - int item_data = item->data(Qt::UserRole).toInt(); - if(item_data == cnum) - { - ctrlList->setCurrentItem(item); - ctrlListClicked(item); - break; - } - } - if (idx >= ctrlList->count()) { - QListWidgetItem* new_item = new QListWidgetItem(act->text(), ctrlList); - new_item->setData(Qt::UserRole, cnum); - ctrlList->setCurrentItem(new_item); - ctrlListClicked(new_item); - break; - } - break; - } - } - } - delete pup; - } -//--------------------------------------------------------- -// ctrlListClicked -//--------------------------------------------------------- - -void EditCtrlDialog::ctrlListClicked(QListWidgetItem* item) - { - if(item == 0) - return; - int cnum = item->data(Qt::UserRole).toInt(); - MusECore::MidiTrack* track = part->track(); - int portn = track->outPort(); - MusECore::MidiPort* port = &MusEGlobal::midiPorts[portn]; - MusECore::MidiController* c = port->midiController(cnum); - int val; - if (cnum == MusECore::CTRL_PROGRAM) { - widgetStack->setCurrentIndex(1); - - val = c->initVal(); - if(val == MusECore::CTRL_VAL_UNKNOWN) - val = 0; - updatePatch(val); - } - else { - widgetStack->setCurrentIndex(0); - if(c->isPerNoteController()) - { - noteSpinBox->setEnabled(true); - noteSpinBox->setVisible(true); - noteLabel->setEnabled(true); - noteLabel->setVisible(true); - } - else - { - noteSpinBox->setEnabled(false); - noteSpinBox->setVisible(false); - noteLabel->setEnabled(false); - noteLabel->setVisible(false); - } - valSlider->setRange(c->minVal(), c->maxVal()); - valSpinBox->setRange(c->minVal(), c->maxVal()); - controllerName->setText(c->name()); - val = c->initVal(); - - if(val == MusECore::CTRL_VAL_UNKNOWN || val == 0) - { - switch(cnum) - { - case MusECore::CTRL_PANPOT: - val = 64 - c->bias(); - break; - case MusECore::CTRL_VOLUME: - val = 100; - break; - default: - val = 0; - break; - } - } - valSlider->setValue(val); - } - } - -//--------------------------------------------------------- -// updatePatch -//--------------------------------------------------------- - -void EditCtrlDialog::updatePatch(int val) - { - MusECore::MidiTrack* track = part->track(); - int port = track->outPort(); - int channel = track->outChannel(); - MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); - patchName->setText(instr->getPatchName(channel, val, track->isDrumTrack())); - - int hb = ((val >> 16) & 0xff) + 1; - if (hb == 0x100) - hb = 0; - int lb = ((val >> 8) & 0xff) + 1; - if (lb == 0x100) - lb = 0; - int pr = (val & 0xff) + 1; - if (pr == 0x100) - pr = 0; - - hbank->blockSignals(true); - lbank->blockSignals(true); - program->blockSignals(true); - - hbank->setValue(hb); - lbank->setValue(lb); - program->setValue(pr); - - hbank->blockSignals(false); - lbank->blockSignals(false); - program->blockSignals(false); - } - -//--------------------------------------------------------- -// instrPopup -//--------------------------------------------------------- - -void EditCtrlDialog::instrPopup() - { - MusECore::MidiTrack* track = part->track(); - int channel = track->outChannel(); - int port = track->outPort(); - MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); - - MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); - instr->populatePatchPopup(pup, channel, track->isDrumTrack()); - - if(pup->actions().count() == 0) - { - delete pup; - return; - } - - QAction* rv = pup->exec(patchName->mapToGlobal(QPoint(10,5))); - if (rv) { - updatePatch(rv->data().toInt()); - } - - delete pup; - } - -//--------------------------------------------------------- -// programChanged -//--------------------------------------------------------- - -void EditCtrlDialog::programChanged() - { - int hb = hbank->value(); - int lb = lbank->value(); - int prog = program->value(); - - if (hb > 0 && hb < 129) - hb -= 1; - else - hb = 0xff; - if (lb > 0 && lb < 129) - lb -= 1; - else - lb = 0xff; - if (prog > 0 && prog < 129) - prog -= 1; - else - prog = 0xff; - - int val = (hb << 16) + (lb << 8) + prog; - updatePatch(val); - } - -} // namespace MusEGui diff -Nru muse-2.1.2/muse/liste/editevent.h muse-3.0.2+ds1/muse/liste/editevent.h --- muse-2.1.2/muse/liste/editevent.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/liste/editevent.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,170 +0,0 @@ -//========================================================= -// MusE -// Linux Music Editor -// $Id: editevent.h,v 1.6.2.1 2008/05/21 00:28:53 terminator356 Exp $ -// (C) Copyright 1999 Werner Schweer (ws@seh.de) -// -// 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 __EDIT_EVENT_H__ -#define __EDIT_EVENT_H__ - -#include "ui_editnotedialogbase.h" -#include "ui_editsysexdialogbase.h" -#include "ui_editctrlbase.h" -#include "event.h" - -class QDialog; -class QLabel; -class QGridLayout; -class QTextEdit; -class QRadioButton; -class QListWidgetItem; -class QMenu; - -namespace Awl { - class PosEdit; - }; - -namespace MusECore { -class MidiPart; -} - -namespace MusEGui { - -class IntLabel; -class PitchEdit; - - -//--------------------------------------------------------- -// EditEventDialog -//--------------------------------------------------------- - -class EditEventDialog : public QDialog { - Q_OBJECT - - protected: - QGridLayout* layout1; - - public: - EditEventDialog(QWidget* parent=0); - virtual MusECore::Event event() = 0; - }; - -//--------------------------------------------------------- -// EditNoteDialog -//--------------------------------------------------------- - -class EditNoteDialog : public QDialog, public Ui::EditNoteDialogBase { - Q_OBJECT - - public: - EditNoteDialog(int tick, const MusECore::Event&, - QWidget* parent=0); - static MusECore::Event getEvent(int tick, const MusECore::Event&, - QWidget* parent = 0); - virtual MusECore::Event event(); - }; - -//--------------------------------------------------------- -// EditSysExDialog -//--------------------------------------------------------- - -class EditSysexDialog : public QDialog, public Ui::EditSysexDialogBase { - Q_OBJECT - - unsigned char* sysex; - int len; - - protected: - QGridLayout* layout; - - private slots: - virtual void accept(); - - public: - EditSysexDialog(int tick, const MusECore::Event&, - QWidget* parent=0); - ~EditSysexDialog(); - static MusECore::Event getEvent(int tick, const MusECore::Event&, - QWidget* parent = 0); - virtual MusECore::Event event(); - }; - -//--------------------------------------------------------- -// EditCtrlDialog -//--------------------------------------------------------- - -class EditCtrlDialog : public QDialog, public Ui::EditCtrlBase { - Q_OBJECT - - const MusECore::MidiPart* part; - void updatePatch(int val); - - private slots: - void ctrlListClicked(QListWidgetItem*); - void newController(); - void programChanged(); - void instrPopup(); - - protected: - QGridLayout* layout; - - public: - EditCtrlDialog(int tick, const MusECore::Event&, - const MusECore::MidiPart*, QWidget* parent=0); - static MusECore::Event getEvent(int tick, const MusECore::Event&, const MusECore::MidiPart*, - QWidget* parent = 0); - virtual MusECore::Event event(); - }; - -//--------------------------------------------------------- -// EditMetaDialog -//--------------------------------------------------------- - -class EditMetaDialog : public EditEventDialog { - Q_OBJECT - - unsigned char* meta; - int len; - Awl::PosEdit* epos; - QTextEdit* edit; - MusEGui::IntLabel* il2; - QRadioButton* hexButton; - QLabel* typeLabel; - - protected: - QGridLayout* layout; - - private slots: - virtual void accept(); - void toggled(bool); - void typeChanged(int); - - public: - EditMetaDialog(int tick, const MusECore::Event&, - QWidget* parent=0); - ~EditMetaDialog(); - static MusECore::Event getEvent(int tick, const MusECore::Event&, - QWidget* parent = 0); - virtual MusECore::Event event(); - }; - -} // namespace MusEGui - -#endif - diff -Nru muse-2.1.2/muse/liste/listedit.cpp muse-3.0.2+ds1/muse/liste/listedit.cpp --- muse-2.1.2/muse/liste/listedit.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/liste/listedit.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -45,6 +45,7 @@ #include "event.h" #include "midiport.h" #include "midictrl.h" +#include "minstrument.h" #include "app.h" #include "gconfig.h" @@ -201,7 +202,8 @@ if (type == 0) return; - if (type & (SC_PART_REMOVED | SC_PART_MODIFIED + if (type & (// SC_MIDI_TRACK_PROP FIXME Needed, but might make it slow! + SC_PART_REMOVED | SC_PART_MODIFIED | SC_PART_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED | SC_EVENT_INSERTED | SC_SELECTION)) { if (type & (SC_PART_REMOVED | SC_PART_INSERTED | SC_PART_MODIFIED)) @@ -247,8 +249,8 @@ MusECore::MidiPart* part = (MusECore::MidiPart*) (p->second); if (part->sn() == curPartId) curPart = part; - MusECore::EventList* el = part->events(); - for (MusECore::iEvent i = el->begin(); i != el->end(); ++i) { + + for (MusECore::ciEvent i = part->events().begin(); i != part->events().end(); ++i) { EventListItem* item = new EventListItem(liste, i->second, part); for (int col = 0; col < liste->columnCount(); ++col) item->setText(col, item->text(col)); @@ -285,7 +287,6 @@ QString EventListItem::text(int col) const { QString s; - QString commentLabel; switch(col) { case 0: s.setNum(event.tick()); @@ -296,7 +297,10 @@ int bar, beat; unsigned tick; AL::sigmap.tickValues(t, &bar, &beat, &tick); - s.sprintf("%04d.%02d.%03d", bar+1, beat+1, tick); + s = QString("%1.%2.%3") + .arg(bar + 1, 4, 10, QLatin1Char('0')) + .arg(beat + 1, 2, 10, QLatin1Char('0')) + .arg(tick, 3, 10, QLatin1Char('0')); } break; case 2: @@ -324,30 +328,9 @@ } break; case MusECore::Sysex: - { - commentLabel = QString("len "); - QString k; - k.setNum(event.dataLen()); - commentLabel += k; - commentLabel += QString(" "); - - commentLabel += MusECore::nameSysex(event.dataLen(), event.data()); - int i; - for (i = 0; i < 10; ++i) { - if (i >= event.dataLen()) - break; - commentLabel += QString(" 0x"); - QString k; - k.setNum(event.data()[i] & 0xff, 16); - commentLabel += k; - } - if (i == 10) - commentLabel += QString("..."); - } s = QString("SysEx"); break; case MusECore::Meta: - commentLabel = midiMetaComment(event); s = QString("Meta"); break; case MusECore::Wave: @@ -357,7 +340,14 @@ } break; case 3: - s.setNum(part->track()->outChannel() + 1); + switch(event.type()) { + case MusECore::Sysex: + case MusECore::Meta: + break; + + default: + s.setNum(part->track()->outChannel() + 1); + } break; case 4: if (event.isNote()) @@ -365,7 +355,16 @@ else if (event.type() == MusECore::Controller) s.setNum(event.dataA() & 0xffff); // mask off type bits else - s.setNum(event.dataA()); + { + switch(event.type()) { + case MusECore::Sysex: + case MusECore::Meta: + break; + + default: + s.setNum(event.dataA()); + } + } break; case 5: if(event.type() == MusECore::Controller && @@ -381,16 +380,44 @@ int pr = (val & 0xff) + 1; if (pr == 0x100) pr = 0; - s.sprintf("%d-%d-%d", hb, lb, pr); + s = QString("%1-%2-%3").arg(hb).arg(lb).arg(pr); } else - s.setNum(event.dataB()); + { + switch(event.type()) { + case MusECore::Sysex: + case MusECore::Meta: + break; + + default: + s.setNum(event.dataB()); + } + } break; case 6: - s.setNum(event.dataC()); + switch(event.type()) { + case MusECore::Sysex: + case MusECore::Meta: + break; + + default: + s.setNum(event.dataC()); + } break; case 7: - s.setNum(event.lenTick()); + switch(event.type()) { + case MusECore::Sysex: + case MusECore::Meta: + s.setNum(event.dataLen()); + break; + + case MusECore::Controller: + break; + + default: + s.setNum(event.lenTick()); + break; + } break; case 8: switch(event.type()) { @@ -402,14 +429,13 @@ } break; case MusECore::Sysex: + case MusECore::Meta: { - s = QString("len "); - QString k; - k.setNum(event.dataLen()); - s += k; - s += QString(" "); - - commentLabel += MusECore::nameSysex(event.dataLen(), event.data()); + if(event.type() == MusECore::Sysex) + s = MusECore::nameSysex(event.dataLen(), event.data(), + MusEGlobal::midiPorts[part->track()->outPort()].instrument()) + QString(" "); + else if(event.type() == MusECore::Meta) + s = midiMetaComment(event) + QString(" "); int i; for (i = 0; i < 10; ++i) { if (i >= event.dataLen()) @@ -421,11 +447,9 @@ } if (i == 10) s += QString("..."); + } break; - case MusECore::Meta: - s = midiMetaComment(event); - break; default: break; } @@ -451,10 +475,10 @@ insertCtrl = new QAction(QIcon(*ctrlIcon), tr("insert Ctrl"), insertItems); insertMeta = new QAction(QIcon(*metaIcon), tr("insert Meta"), insertItems); - connect(insertNote, SIGNAL(activated()), SLOT(editInsertNote())); - connect(insertSysEx, SIGNAL(activated()), SLOT(editInsertSysEx())); - connect(insertCtrl, SIGNAL(activated()), SLOT(editInsertCtrl())); - connect(insertMeta, SIGNAL(activated()), SLOT(editInsertMeta())); + connect(insertNote, SIGNAL(triggered()), SLOT(editInsertNote())); + connect(insertSysEx, SIGNAL(triggered()), SLOT(editInsertSysEx())); + connect(insertCtrl, SIGNAL(triggered()), SLOT(editInsertCtrl())); + connect(insertMeta, SIGNAL(triggered()), SLOT(editInsertMeta())); //---------Pulldown Menu---------------------------- @@ -502,6 +526,16 @@ // Toolbars --------------------------------------------------------- + + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + + addToolBarBreak(); + QToolBar* insertTools = addToolBar(tr("Insert tools")); insertTools->setObjectName("list insert tools"); insertTools->addActions(insertItems->actions()); @@ -623,7 +657,10 @@ if(!curPart) return; - MusECore::Event event = EditSysexDialog::getEvent(curPart->tick(), MusECore::Event(), this); + MusECore::MidiInstrument* minstr = NULL; + if(curPart->track()) + minstr = MusEGlobal::midiPorts[curPart->track()->outPort()].instrument(); + MusECore::Event event = EditSysexDialog::getEvent(curPart->tick(), MusECore::Event(), this, minstr); if (!event.empty()) { //No events before beginning of part + take Part offset into consideration unsigned tick = event.tick(); @@ -699,7 +736,12 @@ nevent = EditCtrlDialog::getEvent(tick, event, part, this); break; case MusECore::Sysex: - nevent = EditSysexDialog::getEvent(tick, event, this); + { + MusECore::MidiInstrument* minstr = NULL; + if(part->track()) + minstr = MusEGlobal::midiPorts[part->track()->outPort()].instrument(); + nevent = EditSysexDialog::getEvent(tick, event, this, minstr); + } break; case MusECore::Meta: nevent = EditMetaDialog::getEvent(tick, event, this); @@ -811,10 +853,12 @@ void ListEdit::selectionChanged() { bool update = false; + EventListItem* eli; for (int row = 0; row < liste->topLevelItemCount(); ++row) { QTreeWidgetItem* i = liste->topLevelItem(row); - if (i->isSelected() ^ ((EventListItem*)i)->event.selected()) { - ((EventListItem*)i)->event.setSelected(i->isSelected()); + eli = (EventListItem*)i; + if (i->isSelected() ^ eli->event.selected()) { + MusEGlobal::song->selectEvent(eli->event, eli->part, i->isSelected()); update = true; } } diff -Nru muse-2.1.2/muse/lock_free_buffer.h muse-3.0.2+ds1/muse/lock_free_buffer.h --- muse-2.1.2/muse/lock_free_buffer.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/lock_free_buffer.h 2017-12-17 21:07:38.000000000 +0000 @@ -0,0 +1,769 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// lock_free_buffer.h +// (C) Copyright 1999-2002 Werner Schweer (ws@seh.de) +// (C) Copyright 2012, 2017 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 +// 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 __LOCK_FREE_BUFFER_H__ +#define __LOCK_FREE_BUFFER_H__ + +#include +#include + +namespace MusECore { + + +//--------------------------------------------------------- +// LockFreeBuffer +//--------------------------------------------------------- + +template + +class LockFreeBuffer +{ + int _capacity; + int _id; // Optional ID value. + T *_fifo; + volatile int _size; + int _wIndex; + int _rIndex; + int _sizeSnapshot; + T _dummyRetValue; + + public: + // Start simple with just 2, like a flipping buffer for example. + LockFreeBuffer(int capacity = 2, int id = 0) + : _capacity(capacity), _id(id) + { + _dummyRetValue = T(); + _fifo = new T[_capacity]; + clear(); + } + + ~LockFreeBuffer() + { + if(_fifo) + delete[] _fifo; + } + + int id() const { return _id; } + + void setCapacity(int capacity = 2) + { + if(_fifo) + delete _fifo; + _fifo = 0; + _capacity = capacity; + _fifo = new T[_capacity]; + } + + // This is only for the writer. + // Returns true on fifo overflow + bool put(const T& item) + { + if (_size < _capacity) + { + _fifo[_wIndex] = item; + _wIndex = (_wIndex + 1) % _capacity; + // q_atomic_increment(&_size); + ++_size; + return false; + } + return true; + } + + // This is only for the reader. + T get() + { + if(_size <= 0) + return _dummyRetValue; + T item(_fifo[_rIndex]); + _rIndex = (_rIndex + 1) % _capacity; + --_size; + return item; + } + + // This is only for the reader. + const T& peek(int n = 0) + { + const int idx = (_rIndex + n) % _capacity; + return _fifo[idx]; + } + +// // This is only for the reader. +// // A non-constant version of peek so that we can modify the items in-place. +// T& peekNonConst(int n = 0) +// { +// const int idx = (_rIndex + n) % _capacity; +// return _fifo[idx]; +// } + + // This is only for the reader. + // Returns true if error (nothing to remove). + bool remove() + { + if(_size <= 0) + return true; + _rIndex = (_rIndex + 1) % _capacity; + --_size; + return false; + } + + // This is only for the reader. + // Returns the number of items in the buffer. + // If NOT requesting the size snapshot, this conveniently stores a snapshot (cached) version + // of the size for consistent behaviour later. If requesting the size snapshot, it does not + // update the snapshot itself. + int getSize(bool useSizeSnapshot/* = false*/) + { + const int sz = useSizeSnapshot ? _sizeSnapshot : _size; + if(!useSizeSnapshot) + _sizeSnapshot = sz; + return sz; + } + // This is only for the reader. + bool isEmpty(bool useSizeSnapshot/* = false*/) const { return useSizeSnapshot ? _sizeSnapshot == 0 : _size == 0; } + // This is not thread safe, call it only when it is safe to do so. + void clear() { _size = 0; _sizeSnapshot = 0; _wIndex = 0; _rIndex = 0; } + // Clear the 'read' side of the ring buffer, which also clears the size. + // NOTE: A corresponding clearWrite() is not provided because + // it is dangerous to reset the size from the sender side - + // the receiver might cache the size, briefly. The sender should + // only grow the size while the receiver should only shrink it. + void clearRead() { _size = 0; _sizeSnapshot = 0; _rIndex = _wIndex; } +}; + +// // template +// // class LockFreeMultiBuffer +// // { +// // int _listCapacity; +// // LockFreeBuffer *_list; +// // //volatile int _size; +// // //int _wIndex; +// // //int _rIndex; +// // +// // public: +// // // Start simple with just 2, like a flipping buffer for example. +// // LockFreeMultiBuffer(int listCapacity = 1) +// // { +// // _listCapacity = listCapacity; +// // _list = new LockFreeBuffer[_listCapacity]; +// // //clear(); +// // } +// // ~LockFreeMultiBuffer() +// // { +// // if(_list) +// // delete[] _list; +// // } +// // +// // void setListCapacity(int listCapacity = 1) +// // { +// // if(_list) +// // delete _list; +// // _list = 0; +// // _listCapacity = listCapacity; +// // _list = new LockFreeBuffer[_listCapacity]; +// // } +// // +// // }; + +// template +// class LockFreeMultiBuffer : public std::map*, std::less > +// { +// public: +// typedef typename std::map*, std::less > vlist; +// typedef typename vlist::iterator iLockFreeMultiBuffer; +// typedef typename vlist::const_iterator ciLockFreeMultiBuffer; +// +// private: +// // int _curId; +// T _dummyRetValue; +// +// public: +// //LockFreeMultiBuffer() : _curId(0) { } +// LockFreeMultiBuffer() { _dummyRetValue = T(); } +// ~LockFreeMultiBuffer() +// { +// for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) +// { +// if(i->second) +// delete i->second; +// } +// } +// +// // Returns new buffer or zero if duplicate id or other error. +// // Start simple with just 2, like a flipping buffer for example. +// LockFreeBuffer* createBuffer(int id, int capacity = 2) +// { +// LockFreeBuffer* buf = new LockFreeBuffer(capacity, id); +// std::pair < iLockFreeMultiBuffer, bool > res = +// vlist::insert(std::pair < const int, LockFreeBuffer* >(buf->id(), buf)); +// // if(res.second) +// // { +// // const int c_id = _curId; +// // ++_curId; +// // return c_id; +// // } +// // return -1; +// +// if(res.second) +// return buf; +// +// delete buf; +// return 0; +// } +// +// // Returns true on error. +// bool deleteBuffer(int id) +// { +// //if(id < 0) +// // return true; +// iLockFreeMultiBuffer i = vlist::find(id); +// if(i == vlist::end()) +// return true; +// if(i->second) +// delete i->second; +// vlist::erase(i); +// return false; +// } +// +// LockFreeBuffer* findBuffer(int id) +// { +// //if(id < 0) +// // return 0; +// iLockFreeMultiBuffer i = vlist::find(id); +// if(i == vlist::end()) +// return 0; +// return i->second; +// } +// +// // Returns true on invalid id. +// bool setCapacity(int id, int capacity = 2) +// { +// //if(id < 0) +// // return true; +// iLockFreeMultiBuffer i = vlist::find(id); +// if(i == vlist::end()) +// return true; +// i->second->setCapacity(capacity); +// return false; +// } +// +// // This is only for the writer. +// // Returns true on invalid id, or on fifo overflow of that id's buffer. +// bool put(int id, const T& item) +// { +// //if(id < 0) +// // return true; +// iLockFreeMultiBuffer i = vlist::find(id); +// if(i == vlist::end()) +// return true; +// return i->second->put(item); +// } +// +// // This is only for the reader. +// T get(bool useSizeSnapshot/* = false*/) +// { +// iLockFreeMultiBuffer least_i = vlist::end(); +// bool is_first = true; +// for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) +// { +// LockFreeBuffer* buf = i->second; +// if(!buf || buf->isEmpty(useSizeSnapshot)) +// continue; +// const T& temp_val = buf->peek(); +// if(is_first) +// { +// is_first = false; +// least_i = i; +// //least_t = temp_val; +// continue; +// } +// else if(temp_val < least_i->second->peek()) +// least_i = i; +// } +// +// if(least_i != vlist::end()) +// return least_i->second->get(); +// +// return _dummyRetValue; +// } +// +// // This is only for the reader. +// const T& peek(bool useSizeSnapshot/* = false*/, int n = 0) // const +// { +// iLockFreeMultiBuffer least_i = vlist::end(); +// bool is_first = true; +// int buf_sz; +// for(int idx = 0; idx <= n; ++idx) // Yes, that's <= +// { +// for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) +// { +// LockFreeBuffer* buf = i->second; +// if(!buf) +// continue; +// buf_sz = buf->getSize(useSizeSnapshot); +// if(buf_sz == 0 || n >= buf_sz) +// continue; +// const T& temp_val = buf->peek(); +// if(is_first) +// { +// is_first = false; +// least_i = i; +// +// //if(idx == n) +// // break; +// //++idx; +// continue; +// } +// else if(temp_val < least_i->second->peek()) +// { +// least_i = i; +// +// //if(idx == n) +// // break; +// //++idx; +// } +// } +// if(idx == n) +// break; +// ++idx; +// } +// +// if(least_i != vlist::end()) +// return least_i->second->peek(); +// +// return _dummyRetValue; +// } +// +// // // This is only for the reader. +// // // A non-constant version of peek so that we can modify the items in-place. +// // T& peekNonConst(bool useSizeSnapshot/* = false*/, int n = 0) // const +// // { +// // iLockFreeMultiBuffer least_i = vlist::end(); +// // bool is_first = true; +// // int buf_sz; +// // for(int idx = 0; idx <= n; ++idx) // Yes, that's <= +// // { +// // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) +// // { +// // LockFreeBuffer* buf = i->second; +// // if(!buf) +// // continue; +// // buf_sz = buf->getSize(useSizeSnapshot); +// // if(buf_sz == 0 || n >= buf_sz) +// // continue; +// // T& temp_val = buf->peekNonConst(); +// // if(is_first) +// // { +// // is_first = false; +// // least_i = i; +// // +// // //if(idx == n) +// // // break; +// // //++idx; +// // continue; +// // } +// // else if(temp_val < least_i->second->peekNonConst()) +// // { +// // least_i = i; +// // +// // //if(idx == n) +// // // break; +// // //++idx; +// // } +// // } +// // if(idx == n) +// // break; +// // ++idx; +// // } +// // +// // if(least_i != vlist::end()) +// // return least_i->second->peekNonConst(); +// // +// // return _dummyRetValue; +// // } +// +// // This is only for the reader. +// // Returns true if error (nothing to remove). +// bool remove(bool useSizeSnapshot/* = false*/) +// { +// iLockFreeMultiBuffer least_i = vlist::end(); +// bool is_first = true; +// for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) +// { +// LockFreeBuffer* buf = i->second; +// if(!buf || buf->isEmpty(useSizeSnapshot)) +// continue; +// const T& temp_val = buf->peek(); +// if(is_first) +// { +// is_first = false; +// least_i = i; +// continue; +// } +// else if(temp_val < least_i->second->peek()) +// least_i = i; +// } +// +// if(least_i != vlist::end()) +// return least_i->second->remove(); +// +// return true; +// } +// +// // This is only for the reader. +// // Returns the total number of items in the buffers. +// // Also conveniently stores a cached version of the size for consistent behaviour later. +// int getSize(bool useSizeSnapshot/* = false*/) const +// { +// int sz = 0; +// // Hm, maybe not so accurate, sizes may be susceptable to +// // asynchronous change as we iterate here... +// for(ciLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) +// { +// if(LockFreeBuffer* buf = i->second) +// sz += buf->getSize(useSizeSnapshot); +// } +// return sz; +// } +// +// // This is only for the reader. +// bool isEmpty(bool useSizeSnapshot/* = false*/) const +// { +// // Hm, maybe not so accurate, sizes may be susceptable to +// // asynchronous change as we iterate here... +// for(ciLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) +// { +// if(const LockFreeBuffer* buf = i->second) +// { +// if(!buf->isEmpty(useSizeSnapshot)) +// return false; +// } +// } +// return true; +// } +// +// // This is not thread safe, call it only when it is safe to do so. +// void clear() +// { +// for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) +// { +// if(LockFreeBuffer* buf = i->second) +// buf->clear(); +// } +// } +// +// // Clear the 'read' side of the ring buffer, which also clears the size. +// // NOTE: A corresponding clearWrite() is not provided because +// // it is dangerous to reset the size from the sender side - +// // the receiver might cache the size, briefly. The sender should +// // only grow the size while the receiver should only shrink it. +// void clearRead() +// { +// for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) +// { +// if(LockFreeBuffer* buf = i->second) +// buf->clearRead(); +// } +// } +// }; + +//--------------------------------------------------------- +// LockFreeMPSCBuffer +// A lock-free Multi-Producer Single-Consumer buffer. +// Similar to a FIFO or Ring Buffer, but uses a fixed number of 'bins'. +// There are no position counters or modulo operations. +// There is no size or peek method. +// It is intended to be fully consumed at reading time. +//--------------------------------------------------------- + +template +class LockFreeMPSCBuffer +{ + T _array[capacity]; + std::atomic _inUse[capacity]; + std::atomic _hasData[capacity]; + + public: + LockFreeMPSCBuffer() { clear(); } + + // Returns the buffer capacity. + unsigned int bufferCapacity() const { return capacity; } + + // This is only for the writer. + // Returns true on success, false if buffer overflow. + bool put(const T& item) + { + bool expected; + for(unsigned int i = 0; i < capacity; ++i) + { + // Expecting a not-in-use bin. Must reset expected each time. + expected = false; + // Safely check and set the bin's inUse flag. + if(_inUse[i].compare_exchange_strong(expected, true)) + { + // Bin was not in use, now safely marked as in use. Now set the item. + _array[i] = item; + // Safely set the hasData flag for the reader to examine. + _hasData[i].store(true); + // Success. + return true; + } + } + // Sorry, all bins were full. A buffer overflow condition. + return false; + } + + // This is only for the reader. + // Returns true on success, false if there was no data + // available at the bin index or other error. + bool get(T& dst, unsigned int index) + { + if(index >= capacity) + return false; + + // Expecting hasData true. + bool expected = true; + // Safely check if there is data in the bin, and reset the + // bin's hasData and inUse flags. Clear the hasData flag first !!! + if(_hasData[index].compare_exchange_strong(expected, false)) + { + // It is safe to store the value in the destination. + dst = _array[index]; + // Now clear the inUse flag !!! + _inUse[index].store(false); + // Success. + return true; + } + // Sorry, there was no data available in that bin. + return false; + } + + // This is only for the reader. + // Returns true on success. + bool remove(unsigned int index) + { + if(index >= capacity) + return false; + + // Expecting hasData true. + bool expected = true; + // Safely check and reset the bin's hasData and inUse flags. + if(_hasData[index].compare_exchange_strong(expected, false)) + { + _inUse[index].store(false); + // Success. + return true; + } + // Sorry, there was no data available in that bin. + return false; + } + + // Not thread safe. Only call when safe to do so, + // like constructor etc. + void clear() + { + for(unsigned int i = 0; i < capacity; ++i) + { + // Clear the hasData flag first !!! + _hasData[i].store(false); + // Now clear the inUse flag !!! + _inUse[i].store(false); + } + } + + // This is only for the reader. + void clearRead() + { + bool expected; + for(unsigned int i = 0; i < capacity; ++i) + { + // Expecting hasData true. Must reset expected each time. + expected = true; + // Safely check and reset the bin's hasData and inUse flags. + if(_hasData[i].compare_exchange_strong(expected, false)) + _inUse[i].store(false); + } + } +}; + +//--------------------------------------------------------- +// LockFreeMPSCRingBuffer +//--------------------------------------------------------- + +template + +class LockFreeMPSCRingBuffer +{ + unsigned int _capacity; + T *_fifo; + std::atomic _size; + std::atomic _wIndex; + std::atomic _rIndex; + unsigned int _capacityMask; + unsigned int _sizeSnapshot; + + // Rounds to the nearest or equal power of 2. + // For 0, 1, and 2, always returns 2. + unsigned int roundCapacity(unsigned int reqCap) const + { + unsigned int i; + for(i = 1; (1U << i) < reqCap; i++); + return 1U << i; + } + + public: + // Start simple with just 2, like a flipping buffer for example. + LockFreeMPSCRingBuffer(unsigned int capacity = 2) + { + _capacity = roundCapacity(capacity); + _capacityMask = _capacity - 1; + _fifo = new T[_capacity]; + clear(); + } + + ~LockFreeMPSCRingBuffer() + { + if(_fifo) + delete[] _fifo; + } + + void setCapacity(unsigned int capacity = 2) + { + if(_fifo) + delete _fifo; + _fifo = 0; + _capacity = roundCapacity(capacity); + _capacityMask = _capacity - 1; + _fifo = new T[_capacity]; + } + + // This is only for the writer. + // Returns true on success, false on fifo overflow or other error. + bool put(const T& item) + { + // Buffer full? Overflow condition. + if(_size.load() >= _capacity) + return false; + + // Safely read, then increment, the current write position. + //std::atomic pos = _wIndex++; + unsigned int pos = _wIndex++; + // Mask the position for a circular effect. + pos &= _capacityMask; + // Store the item in that position. + _fifo[pos] = item; + // Now safely increment the size. + _size++; + // Success. + return true; + } + + // This is only for the reader. + // Returns true on success, false if nothing to read or other error. + // NOTE: This is not multi-reader safe. Yet. + bool get(T& dst) + { + // Nothing to read? + if(_size.load() == 0) + return false; + + // Safely read, then increment, the current read position. + //std::atomic pos = _rIndex++; + unsigned int pos = _rIndex++; + // Mask the position for a circular effect. + pos &= _capacityMask; + // Store the item in that position into the destination. + dst = _fifo[pos]; + // Now safely decrement the size. + _size--; + // Success. + return true; + } + + // This is only for the reader. + // NOTE: This is not multi-reader safe. Yet. + const T& peek(unsigned int n = 0) + { + // Safely read the current read position. + //std::atomic pos = _rIndex.load(); + unsigned int pos = _rIndex.load(); + // Add the desired position. + pos += n; + // Mask the position for a circular effect. + pos &= _capacityMask; + return _fifo[pos]; + } + +// // This is only for the reader. +// // A non-constant version of peek so that we can modify the items in-place. +// T& peekNonConst(int n = 0) +// { +// const int idx = (_rIndex + n) % _capacity; +// return _fifo[idx]; +// } + + // This is only for the reader. + // Returns true on success or false if nothing to remove or other error. + bool remove() + { + // Nothing to read? + if(_size.load() == 0) + return false; + + // Safely increment the current read position. + _rIndex++; + // Now safely decrement the size. + _size--; + // Success. + return true; + } + + // This is only for the reader. + // Returns the number of items in the buffer. + // If NOT requesting the size snapshot, this conveniently stores a snapshot (cached) version + // of the size for consistent behaviour later. If requesting the size snapshot, it does not + // update the snapshot itself. + unsigned int getSize(bool useSizeSnapshot/* = false*/) + { + const unsigned int sz = useSizeSnapshot ? _sizeSnapshot : _size.load(); + if(!useSizeSnapshot) + _sizeSnapshot = sz; + return sz; + } + // This is only for the reader. + bool isEmpty(bool useSizeSnapshot/* = false*/) const { return useSizeSnapshot ? _sizeSnapshot == 0 : _size.load() == 0; } + // This is not thread safe, call it only when it is safe to do so. + void clear() { _size.store(0); _sizeSnapshot = 0; _wIndex.store(0); _rIndex.store(0); } + // This is only for the reader. + // Clear the 'read' side of the ring buffer, which also clears the size. + // NOTE: A corresponding clearWrite() is not provided because it is dangerous to reset + // the size from the sender side - the receiver might cache the size, briefly. + // The sender should only grow the size while the receiver should only shrink it. + //void clearRead() { _size = 0; _sizeSnapshot = 0; _rIndex = _wIndex; } + void clearRead() { _size.store(0); _sizeSnapshot = 0; _rIndex.store(_wIndex); } +}; + +} // namespace MusECore + +#endif + diff -Nru muse-2.1.2/muse/lv2Gtk2Support/CMakeLists.txt muse-3.0.2+ds1/muse/lv2Gtk2Support/CMakeLists.txt --- muse-2.1.2/muse/lv2Gtk2Support/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/lv2Gtk2Support/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,60 @@ + +include_directories(${GTK2_INCLUDE_DIRS}) + +## +## List of source files to compile +## +file (GLOB lv2_gtk2_support_source_files + lv2Gtk2Support.cpp + ) + +## +## Define target +## +add_library ( lv2_gtk2_support ${MODULES_BUILD} + ${lv2_gtk2_support_source_files} + ) + +## +## Append to the list of translations +## +# set (FILES_TO_TRANSLATE +# ${FILES_TO_TRANSLATE} +# ${lv2_gtk2_support_source_files} +# CACHE INTERNAL "" +# ) + +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -shared -pedantic -Wall -Wextra -Werror -std=c++11") +# set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g -D_DEBUG") +# set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os -g0 -DNDEBUG") +# # set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -Wall -Wextra -fPIC ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" CACHE STRING "Release w/deb info CXX flags" FORCE) +# # set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-Os -g0 -D_DEBUG ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" CACHE STRING "Release w/deb info CXX flags" FORCE) +# set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -Os -g0 -D_DEBUG") + +## +## Compilation flags and target name +## +set_target_properties( lv2_gtk2_support + # Avoid treating warnings as errors here, unlike the rest of the app. + PROPERTIES COMPILE_FLAGS "-std=c++11 -Wno-error" + # COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h -std=c++11" + OUTPUT_NAME muse_lv2_gtk2_support + ) + +## +## Linkage +## +target_link_libraries ( lv2_gtk2_support + ${GTK2_LIBRARIES} + ) + +## +## Install location +## +if ( ${MODULES_BUILD} STREQUAL SHARED ) + install(TARGETS lv2_gtk2_support + DESTINATION ${MusE_MODULES_DIR} + ) +else ( ${MODULES_BUILD} STREQUAL SHARED ) + target_link_libraries (lv2_gtk2_support core) +endif ( ${MODULES_BUILD} STREQUAL SHARED ) diff -Nru muse-2.1.2/muse/lv2Gtk2Support/lv2Gtk2Support.cpp muse-3.0.2+ds1/muse/lv2Gtk2Support/lv2Gtk2Support.cpp --- muse-2.1.2/muse/lv2Gtk2Support/lv2Gtk2Support.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/lv2Gtk2Support/lv2Gtk2Support.cpp 2018-01-24 21:54:26.000000000 +0000 @@ -0,0 +1,114 @@ + +#include "lv2Gtk2Support.h" + +#include +#include +#include +#include + +namespace MusEGui +{ + +Gtk::Main *gtkmm2Main = NULL; + +typedef void(*sz_cb_fn)(int, int, void *); + +static void +plug_on_size_request(GtkWidget* widget, GtkRequisition* requisition, gpointer user_data) +{ + sz_cb_fn fn = reinterpret_cast(user_data); + int width = requisition->width; + int height = requisition->height; + void *arg = static_cast(g_object_get_data(G_OBJECT(widget), "lv2Gtk2Helper_arg")); + fn(width, height, arg); +} + +static void +plug_on_size_allocate(GtkWidget* widget, GdkRectangle* allocation, gpointer user_data) +{ + sz_cb_fn fn = reinterpret_cast(user_data); + int width = allocation->width; + int height = allocation->height; + void *arg = static_cast(g_object_get_data(G_OBJECT(widget), "lv2Gtk2Helper_arg")); + gtk_widget_set_size_request( widget, width, height ); + fn(width, height, arg); +} + +bool lv2Gtk2Helper_init() +{ + gtk_init(NULL, NULL); + //create gtkmm2 main class // Not required? + gtkmm2Main = new Gtk::Main(NULL, NULL); + return true; +} + +void *lv2Gtk2Helper_gtk_plug_new(unsigned long winId, void *arg) +{ + GtkWidget *gtkPlug = gtk_plug_new(winId); + g_object_set_data(G_OBJECT(gtkPlug), "lv2Gtk2Helper_arg", arg); + return static_cast(gtkPlug); +} + +void *lv2Gtk2Helper_gtk_window_new(void *arg) +{ + GtkWidget *gtkWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_object_set_data(G_OBJECT(gtkWindow), "lv2Gtk2Helper_arg", arg); + return static_cast(gtkWindow); +} + +void lv2Gtk2Helper_gtk_widget_destroy(void *plug) +{ + gtk_widget_destroy(static_cast(plug)); +} + +void lv2Gtk2Helper_gtk_container_add(void *plug, void *w) +{ + gtk_container_add(GTK_CONTAINER(plug), static_cast(w)); +} + +void lv2Gtk2Helper_gtk_widget_show_all(void *plug) +{ + //gtk_widget_realize(static_cast(plug)); + gtk_widget_show_all(static_cast(plug)); +} + +void lv2Gtk2Helper_gtk_widget_get_allocation(void *plug, int *width, int *height) +{ + GtkAllocation allocSize; + gtk_widget_get_allocation(static_cast(plug), &allocSize); + *width = allocSize.width; + *height = allocSize.height; +} + +void lv2Gtk2Helper_register_allocate_cb(void *plug, sz_cb_fn fn) +{ + g_signal_connect(G_OBJECT(plug), "size-allocate", G_CALLBACK(plug_on_size_allocate), reinterpret_cast(fn)); +} + +void lv2Gtk2Helper_register_resize_cb(void *plug, sz_cb_fn fn) +{ + g_signal_connect(G_OBJECT(plug), "size-request", G_CALLBACK(plug_on_size_request), reinterpret_cast(fn)); +} + +unsigned long lv2Gtk2Helper_gdk_x11_drawable_get_xid(void *plug) +{ + //GdkWindow *w =gtk_widget_get_window(static_cast(widget)); + //return gdk_x11_drawable_get_xid(w); + return gtk_plug_get_id(static_cast(plug)); +} + +unsigned long lv2Gtk2Helper_gtk_window_get_xid(void *window) +{ + return GDK_WINDOW_XID(gtk_widget_get_window(static_cast(window))); +} + +void lv2Gtk2Helper_deinit() +{ + if(gtkmm2Main != NULL) + { + delete gtkmm2Main; + gtkmm2Main = NULL; + } +} + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/lv2Gtk2Support/lv2Gtk2Support.h muse-3.0.2+ds1/muse/lv2Gtk2Support/lv2Gtk2Support.h --- muse-2.1.2/muse/lv2Gtk2Support/lv2Gtk2Support.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/lv2Gtk2Support/lv2Gtk2Support.h 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,23 @@ +#ifndef __LV2_GTK2_SUPPORT_H__ +#define __LV2_GTK2_SUPPORT_H__ + +namespace MusEGui +{ + + typedef void(*sz_cb_fn)(int, int, void *); + bool lv2Gtk2Helper_init(); + void *lv2Gtk2Helper_gtk_plug_new(unsigned long winId, void *arg); + void *lv2Gtk2Helper_gtk_window_new(void *arg); + void lv2Gtk2Helper_gtk_widget_destroy(void *plug); + void lv2Gtk2Helper_gtk_container_add(void *plug, void *w); + void lv2Gtk2Helper_gtk_widget_show_all(void *plug); + void lv2Gtk2Helper_gtk_widget_get_allocation(void *plug, int *width, int *height); + void lv2Gtk2Helper_register_allocate_cb(void *plug, sz_cb_fn fn); + void lv2Gtk2Helper_register_resize_cb(void *plug, sz_cb_fn fn); + unsigned long lv2Gtk2Helper_gdk_x11_drawable_get_xid(void *plug); + unsigned long lv2Gtk2Helper_gtk_window_get_xid(void *window); + void lv2Gtk2Helper_deinit(); + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/lv2host.cpp muse-3.0.2+ds1/muse/lv2host.cpp --- muse-2.1.2/muse/lv2host.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/lv2host.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -0,0 +1,5542 @@ +//============================================================================= +// MusE +// Linux Music Editor +// +// lv2host.cpp +// Copyright (C) 2014 by Deryabin Andrew +// 2017 - Implement LV2_STATE__StateChanged #565 (danvd) +// +// 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 "config.h" +#ifdef LV2_SUPPORT + +#define LV2_HOST_CPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +//#include +#include +#include +#include + +#include "lv2host.h" +#include "synth.h" +#include "audio.h" +#include "jackaudio.h" +#include "midi.h" +#include "midiport.h" +#include "stringparam.h" +#include "plugin.h" +#include "controlfifo.h" +#include "xml.h" +#include "song.h" +#include "ctrl.h" +#include "minstrument.h" + +#include "app.h" +#include "globals.h" +#include "globaldefs.h" +#include "gconfig.h" +#include "widgets/popupmenu.h" +#include "widgets/menutitleitem.h" +#include "icons.h" +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + + +// Uncomment to print audio process info. +//#define LV2_DEBUG_PROCESS + +// Uncomment to print general info. +// (There is also the CMake option LV2_DEBUG for more output.) +//#define LV2_DEBUG + +#ifdef HAVE_GTK2 +#include "lv2Gtk2Support/lv2Gtk2Support.h" +#endif + +// Define to use GtkPlug instead of GtkWindow for a Gtk plugin gui container. +// This works better than GtkWindow for some plugins. +// For example with GtkWindow, AMSynth fails to embed into the container window +// resulting in two separate windows. +#define LV2_GUI_USE_GTKPLUG ; + +namespace MusECore +{ + +#define NS_EXT "http://lv2plug.in/ns/ext/" +#define NS_LV2CORE "http://lv2plug.in/ns/lv2core" + +#define LV2_INSTRUMENT_CLASS NS_LV2CORE "#InstrumentPlugin" +#define LV2_F_BOUNDED_BLOCK_LENGTH LV2_BUF_SIZE__boundedBlockLength +#define LV2_F_FIXED_BLOCK_LENGTH LV2_BUF_SIZE__fixedBlockLength +#define LV2_F_POWER_OF_2_BLOCK_LENGTH LV2_BUF_SIZE__powerOf2BlockLength +#define LV2_P_SEQ_SIZE LV2_BUF_SIZE__sequenceSize +#define LV2_P_MAX_BLKLEN LV2_BUF_SIZE__maxBlockLength +#define LV2_P_MIN_BLKLEN LV2_BUF_SIZE__minBlockLength +#define LV2_P_SAMPLE_RATE LV2_PARAMETERS__sampleRate +#define LV2_F_OPTIONS LV2_OPTIONS__options +#define LV2_F_URID_MAP LV2_URID__map +#define LV2_F_URID_UNMAP LV2_URID__unmap +#define LV2_F_URI_MAP LV2_URI_MAP_URI +#define LV2_F_UI_PARENT LV2_UI__parent +#define LV2_F_INSTANCE_ACCESS NS_EXT "instance-access" +#define LV2_F_DATA_ACCESS LV2_DATA_ACCESS_URI +#define LV2_F_UI_EXTERNAL_HOST LV2_EXTERNAL_UI__Host +#define LV2_F_WORKER_SCHEDULE LV2_WORKER__schedule +#define LV2_F_WORKER_INTERFACE LV2_WORKER__interface +#define LV2_F_UI_IDLE LV2_UI__idleInterface +#define LV2_F_UI_Qt5_UI LV2_UI_PREFIX "Qt5UI" +#define LV2_UI_HOST_URI LV2_F_UI_Qt5_UI +#define LV2_UI_EXTERNAL LV2_EXTERNAL_UI__Widget +#define LV2_UI_EXTERNAL_DEPRECATED LV2_EXTERNAL_UI_DEPRECATED_URI +#define LV2_F_DEFAULT_STATE LV2_STATE_PREFIX "loadDefaultState" +#define LV2_F_STATE_CHANGED LV2_STATE_PREFIX "StateChanged" + + +static LilvWorld *lilvWorld = 0; +static int uniqueID = 1; + +//uri cache structure. +typedef struct +{ + LilvNode *atom_AtomPort; + LilvNode *ev_EventPort; + LilvNode *lv2_AudioPort; + LilvNode *lv2_ControlPort; + LilvNode *lv2_InputPort; + LilvNode *lv2_OutputPort; + LilvNode *lv2_connectionOptional; + LilvNode *host_uiType; + LilvNode *ext_uiType; + LilvNode *ext_d_uiType; + LilvNode *lv2_portDiscrete; + LilvNode *lv2_portContinuous; + LilvNode *lv2_portLogarithmic; + LilvNode *lv2_portInteger; + LilvNode *lv2_portTrigger; + LilvNode *lv2_portToggled; + LilvNode *lv2_TimePosition; + LilvNode *lv2_FreeWheelPort; + LilvNode *lv2_SampleRate; + LilvNode *lv2_CVPort; + LilvNode *lv2_psetPreset; + LilvNode *lv2_rdfsLabel; + LilvNode *lv2_actionSavePreset; + LilvNode *lv2_actionUpdatePresets; + LilvNode *end; ///< NULL terminator for easy freeing of entire structure +} CacheNodes; + +LV2_URID Synth_Urid_Map(LV2_URID_Unmap_Handle _host_data, const char *uri) +{ + LV2Synth *_synth = reinterpret_cast(_host_data); + + if(_synth == NULL) //broken plugin + { + return 0; + } + + return _synth->mapUrid(uri); +} + +const char *Synth_Urid_Unmap(LV2_URID_Unmap_Handle _host_data, LV2_URID id) +{ + LV2Synth *_synth = reinterpret_cast(_host_data); + + if(_synth == NULL) //broken plugin + { + return NULL; + } + + return _synth->unmapUrid(id); +} + +LV2_URID Synth_Uri_Map(LV2_URI_Map_Callback_Data _host_data, const char *, const char *uri) +{ + LV2Synth *_synth = reinterpret_cast(_host_data); + + if(_synth == NULL) //broken plugin + { + return 0; + } + + return _synth->mapUrid(uri); +} + + +static CacheNodes lv2CacheNodes; + +LV2_Feature lv2Features [] = +{ + {LV2_F_URID_MAP, NULL}, + {LV2_F_URID_UNMAP, NULL}, + {LV2_F_URI_MAP, NULL}, + {LV2_F_BOUNDED_BLOCK_LENGTH, NULL}, + {LV2_F_FIXED_BLOCK_LENGTH, NULL}, + {LV2_F_POWER_OF_2_BLOCK_LENGTH, NULL}, + {LV2_F_UI_PARENT, NULL}, + {LV2_F_INSTANCE_ACCESS, NULL}, + {LV2_F_UI_EXTERNAL_HOST, NULL}, + {LV2_UI_EXTERNAL_DEPRECATED, NULL}, + {LV2_F_WORKER_SCHEDULE, NULL}, + {LV2_F_UI_IDLE, NULL}, + {LV2_F_OPTIONS, NULL}, + {LV2_UI__resize, NULL}, + {LV2_PROGRAMS__Host, NULL}, + {LV2_LOG__log, NULL}, + {LV2_STATE__makePath, NULL}, + {LV2_STATE__mapPath, NULL}, + {LV2_F_STATE_CHANGED, NULL}, + {LV2_F_DATA_ACCESS, NULL} //must be the last always! +}; + +std::vector synthsToFree; + +#define SIZEOF_ARRAY(x) sizeof(x)/sizeof(x[0]) + +void initLV2() +{ +#ifdef HAVE_GTK2 + //----------------- + // Initialize Gtk + //----------------- + MusEGui::lv2Gtk2Helper_init(); +#endif + + std::set supportedFeatures; + uint32_t i = 0; + + if(MusEGlobal::debugMsg) + std::cerr << "LV2: MusE supports these features:" << std::endl; + + for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) + { + supportedFeatures.insert(lv2Features [i].URI); + if(MusEGlobal::debugMsg) + std::cerr << "\t" << lv2Features [i].URI << std::endl; + } + + lilvWorld = lilv_world_new(); + + lv2CacheNodes.atom_AtomPort = lilv_new_uri(lilvWorld, LV2_ATOM__AtomPort); + lv2CacheNodes.ev_EventPort = lilv_new_uri(lilvWorld, LV2_EVENT__EventPort); + lv2CacheNodes.lv2_AudioPort = lilv_new_uri(lilvWorld, LV2_CORE__AudioPort); + lv2CacheNodes.lv2_ControlPort = lilv_new_uri(lilvWorld, LV2_CORE__ControlPort); + lv2CacheNodes.lv2_InputPort = lilv_new_uri(lilvWorld, LV2_CORE__InputPort); + lv2CacheNodes.lv2_OutputPort = lilv_new_uri(lilvWorld, LV2_CORE__OutputPort); + lv2CacheNodes.lv2_connectionOptional = lilv_new_uri(lilvWorld, LV2_CORE__connectionOptional); + lv2CacheNodes.host_uiType = lilv_new_uri(lilvWorld, LV2_UI_HOST_URI); + lv2CacheNodes.ext_uiType = lilv_new_uri(lilvWorld, LV2_UI_EXTERNAL); + lv2CacheNodes.ext_d_uiType = lilv_new_uri(lilvWorld, LV2_UI_EXTERNAL_DEPRECATED); + lv2CacheNodes.lv2_portContinuous = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__continuousCV); + lv2CacheNodes.lv2_portDiscrete = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__discreteCV); + lv2CacheNodes.lv2_portLogarithmic = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__logarithmic); + lv2CacheNodes.lv2_portInteger = lilv_new_uri(lilvWorld, LV2_CORE__integer); + lv2CacheNodes.lv2_portTrigger = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__trigger); + lv2CacheNodes.lv2_portToggled = lilv_new_uri(lilvWorld, LV2_CORE__toggled); + lv2CacheNodes.lv2_TimePosition = lilv_new_uri(lilvWorld, LV2_TIME__Position); + lv2CacheNodes.lv2_FreeWheelPort = lilv_new_uri(lilvWorld, LV2_CORE__freeWheeling); + lv2CacheNodes.lv2_SampleRate = lilv_new_uri(lilvWorld, LV2_CORE__sampleRate); + lv2CacheNodes.lv2_CVPort = lilv_new_uri(lilvWorld, LV2_CORE__CVPort); + lv2CacheNodes.lv2_psetPreset = lilv_new_uri(lilvWorld, LV2_PRESETS__Preset); + lv2CacheNodes.lv2_rdfsLabel = lilv_new_uri(lilvWorld, "http://www.w3.org/2000/01/rdf-schema#label"); + lv2CacheNodes.lv2_actionSavePreset = lilv_new_uri(lilvWorld, "http://www.muse-sequencer.org/lv2host#lv2_actionSavePreset"); + lv2CacheNodes.lv2_actionUpdatePresets= lilv_new_uri(lilvWorld, "http://www.muse-sequencer.org/lv2host#lv2_actionUpdatePresets"); + lv2CacheNodes.end = NULL; + + lilv_world_load_all(lilvWorld); + const LilvPlugins *plugins = lilv_world_get_all_plugins(lilvWorld); + LilvIter *pit = lilv_plugins_begin(plugins); + + while(true) + { + if(lilv_plugins_is_end(plugins, pit)) + { + break; + } + + const LilvPlugin *plugin = lilv_plugins_get(plugins, pit); + + if(lilv_plugin_is_replaced(plugin)) + { + pit = lilv_plugins_next(plugins, pit); + continue; + } + + LilvNode *nameNode = lilv_plugin_get_name(plugin); + //const LilvNode *uriNode = lilv_plugin_get_uri(plugin); + + if(lilv_node_is_string(nameNode)) + { + bool shouldLoad = true; + const char *pluginName = lilv_node_as_string(nameNode); + //const char *pluginUri = lilv_node_as_string(uriNode); + if(MusEGlobal::debugMsg) + std::cerr << "Found LV2 plugin: " << pluginName << std::endl; + // lilv_uri_to_path is deprecated. Use lilv_file_uri_parse instead. Return value must be freed with lilv_free. + const char *lfp = lilv_file_uri_parse(lilv_node_as_string(lilv_plugin_get_library_uri(plugin)), NULL); + if(MusEGlobal::debugMsg) + std::cerr << "Library path: " << lfp << std::endl; + + if(MusEGlobal::debugMsg) + { + const LilvPluginClass *cls = lilv_plugin_get_class(plugin); + const LilvNode *ncuri = lilv_plugin_class_get_uri(cls); + const char *clsname = lilv_node_as_uri(ncuri); + std::cerr << "Plugin class: " << clsname << std::endl; + bool isSynth = false; + if(strcmp(clsname, LV2_INSTRUMENT_CLASS) == 0) + { + isSynth = true; + } + if(isSynth) + { + std::cerr << "Plugin is synth" << std::endl; + } + } + +#ifdef DEBUG_LV2 + std::cerr << "\tRequired features (by uri):" << std::endl; +#endif + LilvNodes *fts = lilv_plugin_get_required_features(plugin); + LilvIter *nit = lilv_nodes_begin(fts); + + Plugin::PluginFeatures reqfeat = Plugin::NoFeatures; + while(true) + { + if(lilv_nodes_is_end(fts, nit)) + { + break; + } + + const LilvNode *fnode = lilv_nodes_get(fts, nit); + const char *uri = lilv_node_as_uri(fnode); + bool isSupported = (supportedFeatures.find(uri) != supportedFeatures.end()); +#ifdef DEBUG_LV2 + std::cerr << "\t - " << uri << " (" << (isSupported ? "supported" : "not supported") << ")" << std::endl; +#endif + + if(isSupported) + { + if(strcmp(uri, LV2_F_FIXED_BLOCK_LENGTH) == 0) + reqfeat |= Plugin::FixedBlockSize; + else if(strcmp(uri, LV2_F_POWER_OF_2_BLOCK_LENGTH) == 0) + reqfeat |= Plugin::PowerOf2BlockSize; + } + else + { + shouldLoad = false; + std::cerr << "\t LV2: " << pluginName << ": Required feature: " << uri << ": not supported!" << std::endl; + } + + nit = lilv_nodes_next(fts, nit); + + } + + lilv_nodes_free(fts); + + + + //if (shouldLoad && isSynth) + if(shouldLoad) //load all plugins for now, not only synths + { + QFileInfo fi(lfp); + QString name = QString(pluginName) + QString(" LV2"); + //QString label = QString(pluginUri) + QString("_LV2"); + // Make sure it doesn't already exist. + std::vector::iterator is; + + for(is = MusEGlobal::synthis.begin(); is != MusEGlobal::synthis.end(); ++is) + { + Synth *s = *is; + + if(s->name() == name && s->baseName() == fi.completeBaseName()) + { + break; + } + } + + if(is == MusEGlobal::synthis.end()) + { + LilvNode *nAuthor = lilv_plugin_get_author_name(plugin); + QString author; + + if(nAuthor != NULL) + { + author = lilv_node_as_string(nAuthor); + lilv_node_free(nAuthor); + } + + LV2Synth *s = new LV2Synth(fi, name, name, author, plugin, reqfeat); + + if(s->isConstructed()) + { + + if((s->isSynth() && s->outPorts() > 0) + || (s->inPorts() > 0 && s->outPorts() > 0)) + //insert plugins with audio ins and outs to synths list too + { + MusEGlobal::synthis.push_back(s); + } + else + { + synthsToFree.push_back(s); + } + + if(s->inPorts() > 0 && s->outPorts() > 0) // insert to plugin list + { + MusEGlobal::plugins.push_back(new LV2PluginWrapper(s, reqfeat)); + + } + } + else + { + delete s; + } + + } + } + lilv_free((void*)lfp); // Must free. + } + + if(nameNode != NULL) + { + lilv_node_free(nameNode); + } + + pit = lilv_plugins_next(plugins, pit); + } + +} + +void deinitLV2() +{ + + for(size_t i = 0; i < synthsToFree.size(); i++) + { + delete synthsToFree [i]; + + } + synthsToFree.clear(); + + for(LilvNode **n = (LilvNode **)&lv2CacheNodes; *n; ++n) + { + lilv_node_free(*n); + } + +#ifdef HAVE_GTK2 + MusEGui::lv2Gtk2Helper_deinit(); +#endif + + lilv_world_free(lilvWorld); + lilvWorld = NULL; + +} + +void LV2Synth::lv2ui_ExtUi_Closed(LV2UI_Controller contr) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)contr; + assert(state != NULL); //this shouldn't happen + assert(state->widget != NULL); // this too + assert(state->pluginWindow != NULL); + + state->pluginWindow->setClosing(true); + + + //state->uiTimer->stopNextTime(false); +} + +void LV2Synth::lv2ui_SendChangedControls(LV2PluginWrapper_State *state) +{ + if(state != NULL && state->uiDesc != NULL && state->uiDesc->port_event != NULL && state->uiInst != NULL) + { + size_t numControls = 0; + MusECore::Port *controls = NULL; + size_t numControlsOut = 0; + MusECore::Port *controlsOut = NULL; + LV2Synth *synth = state->synth; + + if(state->plugInst != NULL) + { + numControls = state->plugInst->controlPorts; + controls = state->plugInst->controls; + numControlsOut = state->plugInst->controlOutPorts; + controlsOut = state->plugInst->controlsOut; + + } + else if(state->sif != NULL) + { + numControls = state->sif->_inportsControl; + controls = state->sif->_controls; + numControlsOut = state->sif->_outportsControl; + controlsOut = state->sif->_controlsOut; + } + + if(numControls > 0) + { + assert(controls != NULL); + } + + if(numControlsOut > 0) + { + assert(controlsOut != NULL); + } + for(uint32_t i = 0; i < numControls; ++i) + { + if(state->controlTimers [i] > 0) + { + --state->controlTimers [i]; + continue; + } + if(state->controlsMask [i]) + { + state->controlsMask [i] = false; + + // Force send if re-opening. + if(state->uiIsOpening || state->lastControls [i] != controls [i].val) + { + state->lastControls [i] = controls [i].val; + state->uiDesc->port_event(state->uiInst, + controls [i].idx, + sizeof(float), 0, + &controls [i].val); + } + } + } + + for(uint32_t i = 0; i < numControlsOut; ++i) + { + // Force send if re-opening. + if(state->uiIsOpening || state->lastControlsOut [i] != controlsOut [i].val) + { + state->lastControlsOut [i] = controlsOut [i].val; + state->uiDesc->port_event(state->uiInst, + controlsOut [i].idx, + sizeof(float), 0, + &controlsOut [i].val); + } + + } + + //process gui atom events (control events are already set by getData or apply. + size_t fifoItemSize = state->plugControlEvt.getItemSize(); + size_t dataSize = 0; + uint32_t port_index = 0; + char evtBuffer [fifoItemSize]; + while(state->plugControlEvt.get(&port_index, &dataSize, evtBuffer)) + { + state->uiDesc->port_event(state->uiInst, port_index, dataSize, synth->_uAtom_EventTransfer, evtBuffer); + } + } +} + + + +void LV2Synth::lv2ui_PortWrite(LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, void const *buffer) +{ + LV2Synth::lv2state_PortWrite(controller, port_index, buffer_size, protocol, buffer, true); +} + +void LV2Synth::lv2ui_Touch(LV2UI_Controller /*controller*/, uint32_t /*port_index*/, bool grabbed __attribute__ ((unused))) +{ +#ifdef DEBUG_LV2 + std::cerr << "LV2Synth::lv2ui_UiTouch: port: %u " << (grabbed ? "grabbed" : "released") << std::endl; +#endif + +} + + + + + +void LV2Synth::lv2state_FillFeatures(LV2PluginWrapper_State *state) +{ + uint32_t i; + LV2Synth *synth = state->synth; + LV2_Feature *_ifeatures = state->_ifeatures; + LV2_Feature **_ppifeatures = state->_ppifeatures; + + //state->uiTimer = new LV2PluginWrapper_Timer(state); + + state->wrkSched.handle = (LV2_Worker_Schedule_Handle)state; + state->wrkSched.schedule_work = LV2Synth::lv2wrk_scheduleWork; + state->wrkIface = NULL; + state->wrkThread = new LV2PluginWrapper_Worker(state); + + state->extHost.plugin_human_id = state->human_id = NULL; + state->extHost.ui_closed = LV2Synth::lv2ui_ExtUi_Closed; + + state->extData.data_access = NULL; + + for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) + { + _ifeatures [i] = synth->_features [i]; + + if(_ifeatures [i].URI == NULL) + { + break; + } + + if(i == synth->_fInstanceAccess) + { + _ifeatures [i].data = NULL; + } + else if(i == synth->_fExtUiHost) + { + _ifeatures [i].data = &state->extHost; + } + else if(i == synth->_fExtUiHostD) + { + _ifeatures [i].data = &state->extHost; + } + else if(i == synth->_fDataAccess) + { + _ifeatures [i].data = &state->extData; + } + else if(i == synth->_fWrkSchedule) + { + _ifeatures [i].data = &state->wrkSched; + } + else if(i == synth->_fUiResize) + { + _ifeatures [i].data = &state->uiResize; + } + else if(i == synth->_fPrgHost) + { + _ifeatures [i].data = &state->prgHost; + } + else if(i == synth->_fMakePath) + { + _ifeatures [i].data = &state->makePath; + } + else if(i == synth->_fMapPath) + { + _ifeatures [i].data = &state->mapPath; + } + + _ppifeatures [i] = &_ifeatures [i]; + } + + _ppifeatures [i] = NULL; + + state->curBpm = 0.0;//double(60000000.0/MusEGlobal::tempomap.tempo(MusEGlobal::song->cpos())); + state->curIsPlaying = MusEGlobal::audio->isPlaying(); + state->curFrame = MusEGlobal::audioDevice->getCurFrame(); + lv2_atom_forge_init(&state->atomForge, &synth->_lv2_urid_map); + + LV2Synth::lv2state_InitMidiPorts(state); +} + +void LV2Synth::lv2state_PostInstantiate(LV2PluginWrapper_State *state) +{ + LV2Synth *synth = state->synth; + const LV2_Descriptor *descr = lilv_instance_get_descriptor(state->handle); + + state->_ifeatures [synth->_fInstanceAccess].data = lilv_instance_get_handle(state->handle); + + if(descr->extension_data != NULL) + { + state->extData.data_access = descr->extension_data; + } + else + { + state->_ppifeatures [synth->_fDataAccess] = NULL; + } + + state->controlsNameMap.clear(); + + size_t nCpIn = synth->_controlInPorts.size(); + size_t nCpOut = synth->_controlOutPorts.size(); + + if(nCpIn > 0) + { + state->lastControls = new float [nCpIn]; + state->controlsMask = new bool [nCpIn]; + state->controlTimers = new int [nCpIn]; + for(uint32_t i = 0; i < nCpIn; i++) + { + state->lastControls [i] = synth->_pluginControlsDefault [synth->_controlInPorts [i].index]; + state->controlsMask [i] = false; + state->controlTimers [i] = 0; + state->controlsNameMap.insert(std::pair(QString(synth->_controlInPorts [i].cName).toLower(), i)); + state->controlsSymMap.insert(std::pair(QString(synth->_controlInPorts [i].cSym).toLower(), i)); + } + } + + if(nCpOut > 0) + { + state->lastControlsOut = new float [nCpOut]; + for(uint32_t i = 0; i < nCpOut; i++) + { + state->lastControlsOut [i] = synth->_pluginControlsDefault [synth->_controlOutPorts [i].index]; + } + } + + //fill pointers for CV port types; + + uint32_t numAllPorts = lilv_plugin_get_num_ports(synth->_handle); + + state->pluginCVPorts = new float *[numAllPorts]; + int rv = posix_memalign((void **)&state->pluginCVPorts, 16, sizeof(float *) * numAllPorts); + if(rv != 0) + { + fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + + memset(state->pluginCVPorts, 0, sizeof(float *) * numAllPorts); + + for(size_t i = 0; i < synth->_controlInPorts.size(); ++i) + { + if(synth->_controlInPorts [i].isCVPort) + { + size_t idx = synth->_controlInPorts [i].index; + rv = posix_memalign((void **)&state->pluginCVPorts [idx], 16, sizeof(float) * MusEGlobal::segmentSize); + + if(rv != 0) + { + fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + for(size_t k = 0; k < MusEGlobal::segmentSize; ++k) + { + state->pluginCVPorts [idx] [k] = synth->_controlInPorts [i].defVal; + } + lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); + } + } + + for(size_t i = 0; i < synth->_controlOutPorts.size(); ++i) + { + if(synth->_controlOutPorts [i].isCVPort) + { + size_t idx = synth->_controlOutPorts [i].index; + rv = posix_memalign((void **)&state->pluginCVPorts [idx], 16, sizeof(float) * MusEGlobal::segmentSize); + + if(rv != 0) + { + fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + for(size_t k = 0; k < MusEGlobal::segmentSize; ++k) + { + state->pluginCVPorts [idx] [k] = synth->_controlOutPorts [i].defVal; + } + lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); + } + } + + for(size_t i = 0; i < state->midiInPorts.size(); i++) + { + lilv_instance_connect_port(state->handle, state->midiInPorts [i].index, (void *)state->midiInPorts [i].buffer->getRawBuffer()); + } + + for(size_t i = 0; i < state->midiOutPorts.size(); i++) + { + lilv_instance_connect_port(state->handle, state->midiOutPorts [i].index, (void *)state->midiOutPorts [i].buffer->getRawBuffer()); + } + + //query for state interface + state->iState = (LV2_State_Interface *)lilv_instance_get_extension_data(state->handle, LV2_STATE__interface); + //query for LV2Worker interface + state->wrkIface = (LV2_Worker_Interface *)lilv_instance_get_extension_data(state->handle, LV2_F_WORKER_INTERFACE); + //query for programs interface + state->prgIface = (LV2_Programs_Interface *)lilv_instance_get_extension_data(state->handle, LV2_PROGRAMSNEW__Interface); + if(state->prgIface != NULL) + { + state->newPrgIface = true; + } + else + { + state->newPrgIface = false; + state->prgIface = (LV2_Programs_Interface *)lilv_instance_get_extension_data(state->handle, LV2_PROGRAMS__Interface); + } + + LV2Synth::lv2prg_updatePrograms(state); + + state->wrkThread->start(QThread::LowPriority); + +} + +void LV2Synth::lv2ui_FreeDescriptors(LV2PluginWrapper_State *state) +{ + if(state->uiDesc != NULL && state->uiInst != NULL) + state->uiDesc->cleanup(state->uiInst); + + state->uiInst = *(void **)(&state->uiDesc) = NULL; + +#ifdef HAVE_GTK2 + if(state->gtk2Plug != NULL) + MusEGui::lv2Gtk2Helper_gtk_widget_destroy(state->gtk2Plug); +#endif + + state->gtk2Plug = NULL; + + if(state->uiDlHandle != NULL) + { + dlclose(state->uiDlHandle); + state->uiDlHandle = NULL; + } + +} + +void LV2Synth::lv2state_FreeState(LV2PluginWrapper_State *state) +{ + assert(state != NULL); + + state->wrkThread->setClosing(); + state->wrkThread->wait(); + delete state->wrkThread; + + if(state->human_id != NULL) + free(state->human_id); + if(state->lastControls) + { + delete [] state->lastControls; + state->lastControls = NULL; + } + if(state->controlsMask) + { + delete [] state->controlsMask; + state->controlsMask = NULL; + } + + if(state->controlTimers) + { + delete [] state->controlTimers; + state->controlTimers = NULL; + + } + + if(state->lastControlsOut) + { + delete [] state->lastControlsOut; + state->lastControlsOut = NULL; + } + + LV2Synth::lv2ui_FreeDescriptors(state); + + if(state->handle != NULL) + { + lilv_instance_free(state->handle); + state->handle = NULL; + } + + delete state; +} + +void LV2Synth::lv2audio_SendTransport(LV2PluginWrapper_State *state, LV2EvBuf *buffer, unsigned long nsamp) +{ + //send transport events if any + LV2Synth *synth = state->synth; + unsigned int cur_frame = MusEGlobal::audio->pos().frame(); + Pos p(MusEGlobal::extSyncFlag.value() ? MusEGlobal::audio->tickPos() : cur_frame, MusEGlobal::extSyncFlag.value() ? true : false); + double curBpm = (60000000.0 / MusEGlobal::tempomap.tempo(p.tick())) * double(MusEGlobal::tempomap.globalTempo())/100.0; + bool curIsPlaying = MusEGlobal::audio->isPlaying(); + unsigned int curFrame = MusEGlobal::audioDevice->getCurFrame(); + // if(state->curFrame != curFrame + // || state->curIsPlaying != curIsPlaying + // || state->curBpm != curBpm) + // { + + //send transport/tempo changes always + //as some plugins revert to default settings when not playing + state->curFrame = curFrame; + state->curIsPlaying = curIsPlaying; + state->curBpm = curBpm; + uint8_t pos_buf[1024]; + memset(pos_buf, 0, sizeof(pos_buf)); + LV2_Atom* lv2_pos = (LV2_Atom*)pos_buf; + /* Build an LV2 position object to report change to plugin */ + LV2_Atom_Forge* atomForge = &state->atomForge; + lv2_atom_forge_set_buffer(atomForge, pos_buf, sizeof(pos_buf)); + LV2_Atom_Forge_Frame frame; + lv2_atom_forge_object(atomForge, &frame, 1, synth->_uTime_Position); + lv2_atom_forge_key(atomForge, synth->_uTime_frame); + lv2_atom_forge_long(atomForge, curFrame); + lv2_atom_forge_key(atomForge, synth->_uTime_speed); + lv2_atom_forge_float(atomForge, curIsPlaying ? 1.0 : 0.0); + lv2_atom_forge_key(atomForge, synth->_uTime_beatsPerMinute); + lv2_atom_forge_float(atomForge, (float)curBpm); + buffer->write(nsamp, 0, lv2_pos->type, lv2_pos->size, (const uint8_t *)LV2_ATOM_BODY(lv2_pos)); +} + +void LV2Synth::lv2state_InitMidiPorts(LV2PluginWrapper_State *state) +{ + LV2Synth *synth = state->synth; + state->midiInPorts = synth->_midiInPorts; + state->midiOutPorts = synth->_midiOutPorts; + state->inPortsMidi= state->midiInPorts.size(); + state->outPortsMidi = state->midiOutPorts.size(); + //connect midi and control ports + for(size_t i = 0; i < state->midiInPorts.size(); i++) + { + LV2EvBuf *newEvBuffer = new LV2EvBuf(true, state->midiInPorts [i].old_api, synth->_uAtom_Sequence, synth->_uAtom_Chunk); + if(!newEvBuffer) + { + abort(); + } + state->midiInPorts [i].buffer = newEvBuffer; + state->idx2EvtPorts.insert(std::make_pair(state->midiInPorts [i].index, newEvBuffer)); + } + + for(size_t i = 0; i < state->midiOutPorts.size(); i++) + { + LV2EvBuf *newEvBuffer = new LV2EvBuf(false, state->midiOutPorts [i].old_api, synth->_uAtom_Sequence, synth->_uAtom_Chunk); + if(!newEvBuffer) + { + abort(); + } + state->midiOutPorts [i].buffer = newEvBuffer; + state->idx2EvtPorts.insert(std::make_pair(state->midiOutPorts [i].index, newEvBuffer)); + } + +} + +void LV2Synth::lv2audio_preProcessMidiPorts(LV2PluginWrapper_State *state, unsigned long nsamp) +{ + for(size_t j = 0; j < state->inPortsMidi; j++) + { + state->midiInPorts [j].buffer->resetBuffer(); + } + + for(size_t j = 0; j < state->outPortsMidi; j++) + { + state->midiOutPorts [j].buffer->resetBuffer(); + } + + if(state->inPortsMidi > 0) + { + LV2EvBuf *rawMidiBuffer = state->midiInPorts [0].buffer; + + if(state->midiInPorts [0].supportsTimePos) + { + //send transport events if any + LV2Synth::lv2audio_SendTransport(state, rawMidiBuffer, nsamp); + } + } + + //process gui atom events (control events are already set by getData or apply call). + size_t fifoItemSize = state->uiControlEvt.getItemSize(); + size_t dataSize = 0; + uint32_t port_index = 0; + char evtBuffer [fifoItemSize]; + while(state->uiControlEvt.get(&port_index, &dataSize, evtBuffer)) + { + std::map::iterator it = state->idx2EvtPorts.find(port_index); + if(it != state->idx2EvtPorts.end()) + { + LV2EvBuf *buffer = it->second; + const LV2_Atom* const atom = (const LV2_Atom*)evtBuffer; + buffer->write(nsamp, 0, atom->type, atom->size, static_cast(LV2_ATOM_BODY_CONST(atom))); + } + + } +} + +void LV2Synth::lv2audio_postProcessMidiPorts(LV2PluginWrapper_State *state, unsigned long) +{ + //send Atom events to gui. + //Synchronize send rate with gui update rate + + + size_t fifoItemSize = state->plugControlEvt.getItemSize(); + + size_t outp = state->midiOutPorts.size(); + + for(size_t j = 0; j < outp; j++) + { + if(!state->midiOutPorts [j].old_api) + { + do + { + uint32_t frames, subframes, type, size; + uint8_t *data = NULL; + if(!state->midiOutPorts [j].buffer->read(&frames, &subframes, &type, &size, &data)) + { + break; + } + if(type == state->synth->_uAtom_Object) + { + const LV2_Atom_Object_Body *aObjBody = reinterpret_cast(data); + if(aObjBody->otype == state->synth->_uAtom_StateChanged) + { + //Just make song status dirty (pending event) - something had changed in the plugin controls + state->songDirtyPending = true; + } + } + if(state->uiInst == NULL) + { + continue; + } + unsigned char atom_data [fifoItemSize]; + LV2_Atom *atom_evt = reinterpret_cast(atom_data); + atom_evt->type = type; + atom_evt->size = size; + if(fifoItemSize - sizeof(LV2_Atom) < size) + { +#ifdef DEBUG_LV2 + std::cerr << "LV2Synth::lv2audio_postProcessMidiPorts(): Plugin event data is bigger than rt fifo item size. Skipping." << std::endl; +#endif + continue; + } + unsigned char *evt = static_cast(LV2_ATOM_BODY(atom_evt)); + memcpy(evt, data, size); + + state->plugControlEvt.put(state->midiOutPorts [j].index, sizeof(LV2_Atom) + size, atom_evt); + } + while(true); + + } + } + +} + +void LV2Synth::lv2ui_PostShow(LV2PluginWrapper_State *state) +{ + assert(state->pluginWindow != NULL); + assert(state->uiDesc != NULL); + assert(state->uiInst != NULL); + + if(state->uiDesc->port_event != NULL) + { + uint32_t numControls = 0; + Port *controls = NULL; + + if(state->plugInst != NULL) + { + numControls = state->plugInst->controlPorts; + controls = state->plugInst->controls; + + } + else if(state->sif != NULL) + { + numControls = state->sif->_inportsControl; + controls = state->sif->_controls; + } + + if(numControls > 0) + { + assert(controls != NULL); + } + + + + for(uint32_t i = 0; i < numControls; ++i) + { + state->uiDesc->port_event(state->uiInst, + controls [i].idx, + sizeof(float), 0, + &controls [i].val); + } + + } + + // Set the flag to tell the update timer to force sending all controls and program. + state->uiIsOpening = true; + + state->pluginWindow->startNextTime(); + +} + +int LV2Synth::lv2ui_Resize(LV2UI_Feature_Handle handle, int width, int height) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + if(state->widget != NULL && state->hasGui) + { + ((LV2PluginWrapper_Window *)state->widget)->resize(width, height); + QWidget *ewWin = ((LV2PluginWrapper_Window *)state->widget)->findChild(); + if(ewWin != NULL) + { + ewWin->resize(width, height); + } + else + { +#ifdef LV2_GUI_USE_QWIDGET + // TODO Check this, maybe wrong widget, maybe need the one contained by it? + QWidget *ewCent= ((LV2PluginWrapper_Window *)state->widget); +#else + QWidget *ewCent= ((LV2PluginWrapper_Window *)state->widget)->centralWidget(); +#endif + if(ewCent != NULL) + { + ewCent->resize(width, height); + } + } + state->uiX11Size.setWidth(width); + state->uiX11Size.setHeight(height); + return 0; + } + return 1; +} + +void LV2Synth::lv2ui_Gtk2AllocateCb(int width, int height, void *arg) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)arg; + if(state == NULL) + return; + if(!state->gtk2AllocateCompleted && state->widget != NULL && state->hasGui && state->gtk2Plug != NULL) + { + state->gtk2AllocateCompleted = true; + ((LV2PluginWrapper_Window *)state->widget)->setMinimumSize(width, height); + } +} + +void LV2Synth::lv2ui_Gtk2ResizeCb(int width, int height, void *arg) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)arg; + if(state == NULL) + return; + if(!state->gtk2ResizeCompleted && state->widget != NULL && state->hasGui && state->gtk2Plug != NULL) + { + state->gtk2ResizeCompleted = true; + ((LV2PluginWrapper_Window *)state->widget)->resize(width, height); + } +} + +void LV2Synth::lv2ui_ShowNativeGui(LV2PluginWrapper_State *state, bool bShow) +{ + LV2Synth* synth = state->synth; + LV2PluginWrapper_Window *win = NULL; + + if(synth->_pluginUiTypes.size() == 0) + return; + + //state->uiTimer->stopNextTime(); + if(state->pluginWindow != NULL) + state->pluginWindow->stopNextTime(); + + if(!bShow) + return; + + LV2_PLUGIN_UI_TYPES::iterator itUi; + + if((state->uiCurrent == NULL) || MusEGlobal::config.lv2UiBehavior == MusEGlobal::CONF_LV2_UI_ASK_ALWAYS) + { + state->uiCurrent = NULL; + state->gtk2ResizeCompleted = false; + state->gtk2AllocateCompleted = false; + QAction *aUiTypeSelected = NULL; + if((synth->_pluginUiTypes.size() == 1) || MusEGlobal::config.lv2UiBehavior == MusEGlobal::CONF_LV2_UI_USE_FIRST) + { + state->uiCurrent = synth->_pluginUiTypes.begin()->first; + } + else + { + QMenu mGuisPopup; + MusEGui::MenuTitleItem *aUiTypeHeader = new MusEGui::MenuTitleItem(QObject::tr("Select gui type"), NULL); + aUiTypeHeader->setEnabled(false); + QFont fHeader; + fHeader.setBold(true); + fHeader.setUnderline(true); + aUiTypeHeader->setFont(fHeader); + mGuisPopup.addAction(aUiTypeHeader); + + for(itUi = synth->_pluginUiTypes.begin(); itUi != synth->_pluginUiTypes.end(); itUi++) + { + const LilvUI *selectedUi = itUi->first; + const LilvNode *pluginUiType = itUi->second.second; + QAction *aUiType = new QAction(QString(lilv_node_as_string(pluginUiType)), NULL); + aUiType->setData(QVariant(reinterpret_cast(selectedUi))); + mGuisPopup.addAction(aUiType); + } + + aUiTypeSelected = mGuisPopup.exec(QCursor::pos()); + if(aUiTypeSelected == NULL) + { + return; + } + state->uiCurrent = reinterpret_cast(aUiTypeSelected->data().toLongLong()); + } + + } + + itUi = synth->_pluginUiTypes.find(state->uiCurrent); + + assert(itUi != synth->_pluginUiTypes.end()); + + + const LilvUI *selectedUi = itUi->first; + bool bExtUi = itUi->second.first; + const LilvNode *pluginUiType = itUi->second.second; + state->uiIdleIface = NULL; + if(bExtUi) + { + state->hasGui = false; + state->hasExternalGui = true; + } + else + { + state->hasGui = true; + state->hasExternalGui = false; + } + +#ifdef LV2_GUI_USE_QWIDGET + win = new LV2PluginWrapper_Window(state, Q_NULLPTR, Qt::Window); +#else + win = new LV2PluginWrapper_Window(state); +#endif + + state->uiX11Size.setWidth(0); + state->uiX11Size.setHeight(0); + + if(win != NULL) + { + state->widget = win; + state->pluginWindow = win; + const char *cUiUri = lilv_node_as_uri(pluginUiType); + const char *cUiTypeUri = lilv_node_as_uri(lilv_ui_get_uri(selectedUi)); + bool bEmbed = false; + bool bGtk = false; + QWidget *ewWin = NULL; + QWindow *x11QtWindow = NULL; + state->gtk2Plug = NULL; + state->_ifeatures [synth->_fUiParent].data = NULL; + if(strcmp(LV2_UI__X11UI, cUiUri) == 0) + { + bEmbed = true; + ewWin = new QWidget(); + +#ifdef LV2_GUI_USE_QWIDGET + QVBoxLayout* layout = new QVBoxLayout(); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(ewWin); + win->setLayout(layout); + +#else + win->setCentralWidget(ewWin); +#endif + + state->_ifeatures [synth->_fUiParent].data = (void*)ewWin->winId(); + } + else if(strcmp(LV2_UI__GtkUI, cUiUri) == 0) + { +#ifndef HAVE_GTK2 + win->stopNextTime(); + return; +#else + + bEmbed = true; + bGtk = true; + +#ifdef LV2_GUI_USE_GTKPLUG + state->gtk2Plug = MusEGui::lv2Gtk2Helper_gtk_plug_new(0, state); +#else + state->gtk2Plug = MusEGui::lv2Gtk2Helper_gtk_window_new(state); +#endif + + MusEGui::lv2Gtk2Helper_register_allocate_cb(static_cast(state->gtk2Plug), lv2ui_Gtk2AllocateCb); + MusEGui::lv2Gtk2Helper_register_resize_cb(static_cast(state->gtk2Plug), lv2ui_Gtk2ResizeCb); + +#endif + + } + else if(strcmp(LV2_F_UI_Qt5_UI, cUiUri) == 0) //Qt5 uis are handled natively + { + state->_ifeatures [synth->_fUiParent].data = win; + } + else //external uis + { + state->_ifeatures [synth->_fUiParent].data = NULL; + } + + //now open ui library file + + // lilv_uri_to_path is deprecated. Use lilv_file_uri_parse instead. Return value must be freed with lilv_free. + const char *uiPath = lilv_file_uri_parse(lilv_node_as_uri(lilv_ui_get_binary_uri(selectedUi)), NULL); +// REMOVE Tim. LV2. Changed. TESTING. RESTORE. Qt4 versions of synthv1,drumk,? crashes on Qt5. +// TESTED: On my system it gets much farther into the call now, dozens of Qt4 calls into it, +// but ultimately still ends up crashing on a call to dlopen libkdecore.5 for some reason. +// state->uiDlHandle = dlopen(uiPath, RTLD_NOW); + //state->uiDlHandle = dlmopen(LM_ID_NEWLM, uiPath, RTLD_LAZY | RTLD_DEEPBIND); // Just a test + state->uiDlHandle = dlopen(uiPath, RTLD_NOW | RTLD_DEEPBIND); + + lilv_free((void*)uiPath); // Must free. + if(state->uiDlHandle == NULL) + { + win->stopNextTime(); + return; + } + + //find lv2 ui descriptor function and call it to get ui descriptor struct + LV2UI_DescriptorFunction lv2fUiDesc; + *(void **)(&lv2fUiDesc) = dlsym(state->uiDlHandle, "lv2ui_descriptor"); + if(lv2fUiDesc == NULL) + { + win->stopNextTime(); + return; + } + + state->uiDesc = NULL; + + for(uint32_t i = 0; ;++i) + { + state->uiDesc = lv2fUiDesc(i); + if(state->uiDesc == NULL) + break; + + if(strcmp(state->uiDesc->URI, cUiTypeUri) == 0) //found selected ui + break; + } + + if(state->uiDesc == NULL) + { + win->stopNextTime(); + return; + } + + void *uiW = NULL; + // lilv_uri_to_path is deprecated. Use lilv_file_uri_parse instead. Return value must be freed with lilv_free. + const char* bundle_path = lilv_file_uri_parse(lilv_node_as_uri(lilv_ui_get_bundle_uri(selectedUi)), NULL); + state->uiInst = state->uiDesc->instantiate(state->uiDesc, + lilv_node_as_uri(lilv_plugin_get_uri(synth->_handle)), + bundle_path, + LV2Synth::lv2ui_PortWrite, + state, + &uiW, + state->_ppifeatures); + + + lilv_free((void*)bundle_path); // Must free. + + if(state->uiInst != NULL) + { + state->uiIdleIface = NULL; + state->uiPrgIface = NULL; + if(state->uiDesc->extension_data != NULL) + { + state->uiIdleIface = (LV2UI_Idle_Interface *)state->uiDesc->extension_data(LV2_F_UI_IDLE); + state->uiPrgIface = (LV2_Programs_UI_Interface *)state->uiDesc->extension_data(LV2_PROGRAMSNEW__UIInterface); + if(state->uiPrgIface != NULL) + { + state->newPrgIface = true; + } + else + { + state->newPrgIface = false; + state->uiPrgIface = (LV2_Programs_UI_Interface *)state->uiDesc->extension_data(LV2_PROGRAMS__UIInterface); + } + } + if(state->hasGui) + { + if(!bEmbed) + { +#ifdef LV2_GUI_USE_QWIDGET + QVBoxLayout* layout = new QVBoxLayout(); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(static_cast(uiW)); + win->setLayout(layout); +#else + win->setCentralWidget(static_cast(uiW)); +#endif + } + else + { + if(bGtk) + { + +#ifdef HAVE_GTK2 + MusEGui::lv2Gtk2Helper_gtk_container_add(state->gtk2Plug, uiW); + MusEGui::lv2Gtk2Helper_gtk_widget_show_all(state->gtk2Plug); + +#ifdef LV2_GUI_USE_GTKPLUG + unsigned long plugX11Id = MusEGui::lv2Gtk2Helper_gdk_x11_drawable_get_xid(state->gtk2Plug); +#else + unsigned long plugX11Id = MusEGui::lv2Gtk2Helper_gtk_window_get_xid(state->gtk2Plug); +#endif + + x11QtWindow = QWindow::fromWinId(plugX11Id); + ewWin = QWidget::createWindowContainer(x11QtWindow, win); + state->pluginQWindow = x11QtWindow; + +#ifdef LV2_GUI_USE_QWIDGET + QVBoxLayout* layout = new QVBoxLayout(); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(ewWin); + win->setLayout(layout); +#else + win->setCentralWidget(ewWin); +#endif + + if(state->uiX11Size.width() == 0 || state->uiX11Size.height() == 0) + { + int w = 0; + int h = 0; + MusEGui::lv2Gtk2Helper_gtk_widget_get_allocation(uiW, &w, &h); + win->setMinimumSize(w, h); + win->resize(w, h); + } +#endif + } + else + { + if(state->uiX11Size.width() == 0 || state->uiX11Size.height() == 0) + win->resize(ewWin->size()); + } + } + + win->show(); + win->setWindowTitle(state->extHost.plugin_human_id); + } + else if(state->hasExternalGui) + { + state->widget = uiW; + LV2_EXTERNAL_UI_SHOW((LV2_External_UI_Widget *)state->widget); + } + + LV2Synth::lv2ui_PostShow(state); + return; + + } + + } + if(win != NULL) + { + win->stopNextTime(); + } + state->pluginWindow = NULL; + state->widget = NULL; + state->uiCurrent = NULL; + + //no ui is shown + state->hasExternalGui = state->hasGui = false; + +} + + +const void *LV2Synth::lv2state_stateRetreive(LV2_State_Handle handle, uint32_t key, size_t *size, uint32_t *type, uint32_t *flags) +{ + QMap >::const_iterator it; + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + LV2Synth *synth = state->synth; + const char *cKey = synth->unmapUrid(key); + + assert(cKey != NULL); //should'n happen + + QString strKey = QString(cKey); + it = state->iStateValues.find(strKey); + if(it != state->iStateValues.end()) + { + if(it.value().second.type() == QVariant::ByteArray) + { + QString sType = it.value().first; + QByteArray arrType = sType.toUtf8(); + *type = synth->mapUrid(arrType.constData()); + *flags = LV2_STATE_IS_POD; + QByteArray valArr = it.value().second.toByteArray(); + if(sType.compare(QString(LV2_ATOM__Path)) == 0) //prepend project folder to abstract path + { + QString plugFolder = ((state->sif != NULL) ? state->sif->name() : state->plugInst->name()) + QString("/"); + QString strPath = QString::fromUtf8(valArr.data()); + QFile ff(strPath); + QFileInfo fiPath(ff); + if(fiPath.isRelative()) + { + if(strPath.indexOf(plugFolder) < 0) + { + strPath = plugFolder + strPath; + } + strPath = MusEGlobal::museProject + QString("/") + strPath; + valArr = strPath.toUtf8(); + int len = strPath.length(); + valArr.setRawData(strPath.toUtf8().constData(), len + 1); + valArr [len] = 0; + } + } + size_t i; + size_t numValues = state->numStateValues; + for(i = 0; i < numValues; ++i) + { + if(state->tmpValues [i] == NULL) + break; + } + assert(i < numValues); //sanity check + size_t sz = valArr.size(); + state->iStateValues.remove(strKey); + if(sz > 0) + { + state->tmpValues [i] = new char [sz]; + memset(state->tmpValues [i], 0, sz); + memcpy(state->tmpValues [i], valArr.constData(), sz); + *size = sz; + return state->tmpValues [i]; + } + } + } + + return NULL; +} + +LV2_State_Status LV2Synth::lv2state_stateStore(LV2_State_Handle handle, uint32_t key, const void *value, size_t size, uint32_t type, uint32_t flags) +{ + if(flags & (LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE)) + { + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + LV2Synth *synth = state->synth; + const char *uriKey = synth->unmapUrid(key); + const char *uriType = synth->unmapUrid(type); + assert(uriType != NULL && uriKey != NULL); //FIXME: buggy plugin or uridBiMap realization? + QString strKey = QString(uriKey); + QMap >::const_iterator it = state->iStateValues.find(strKey); + if(it == state->iStateValues.end()) + { + QString strUriType = uriType; + QVariant varVal = QByteArray((const char *)value, size); + state->iStateValues.insert(strKey, QPair(strUriType, varVal)); + } + return LV2_STATE_SUCCESS; + } + return LV2_STATE_ERR_BAD_FLAGS; +} + +LV2_Worker_Status LV2Synth::lv2wrk_scheduleWork(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data) +{ +#ifdef DEBUG_LV2 + std::cerr << "LV2Synth::lv2wrk_scheduleWork" << std::endl; +#endif + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + + //assert(state->wrkEndWork != true); + if(state->wrkEndWork != false) + return LV2_WORKER_ERR_UNKNOWN; + + state->wrkDataSize = size; + state->wrkDataBuffer = data; + if(MusEGlobal::audio->freewheel()) //don't wait for a thread. Do it now + state->wrkThread->makeWork(); + else + return state->wrkThread->scheduleWork(); + + return LV2_WORKER_SUCCESS; +} + +LV2_Worker_Status LV2Synth::lv2wrk_respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void *data) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + + state->wrkDataSize = size; + state->wrkDataBuffer = data; + state->wrkEndWork = true; + + return LV2_WORKER_SUCCESS; +} + +void LV2Synth::lv2conf_write(LV2PluginWrapper_State *state, int level, Xml &xml) +{ + state->iStateValues.clear(); + state->numStateValues = 0; + + if(state->iState != NULL) + { + state->iState->save(lilv_instance_get_handle(state->handle), LV2Synth::lv2state_stateStore, state, LV2_STATE_IS_POD, state->_ppifeatures); + } + + if(state->sif != NULL) // write control ports values only for synths + { + for(size_t c = 0; c < state->sif->_inportsControl; c++) + { + state->iStateValues.insert(QString(state->sif->_controlInPorts [c].cName), QPair(QString(""), QVariant((double)state->sif->_controls[c].val))); + } + } + + if(state->uiCurrent != NULL) + { + const char *cUiUri = lilv_node_as_uri(lilv_ui_get_uri(state->uiCurrent)); + state->iStateValues.insert(QString(cUiUri), QPair(QString(""), QVariant(QString(cUiUri)))); + } + + QByteArray arrOut; + QDataStream streamOut(&arrOut, QIODevice::WriteOnly); + streamOut << state->iStateValues; + QByteArray outEnc64 = arrOut.toBase64(); + QString customData(outEnc64); + for (int pos=0; pos < customData.size(); pos+=150) + { + customData.insert(pos++,'\n'); // add newlines for readability + } + xml.strTag(level, "customData", customData); +} + +void LV2Synth::lv2conf_set(LV2PluginWrapper_State *state, const std::vector &customParams) +{ + if(customParams.size() == 0) + return; + + state->iStateValues.clear(); + for(size_t i = 0; i < customParams.size(); i++) + { + QString param = customParams [i]; + param.remove('\n'); // remove all linebreaks that may have been added to prettyprint the songs file + QByteArray paramIn; + paramIn.append(param); + QByteArray dec64 = QByteArray::fromBase64(paramIn); + QDataStream streamIn(&dec64, QIODevice::ReadOnly); + streamIn >> state->iStateValues; + break; //one customData tag includes all data in base64 + } + + size_t numValues = state->iStateValues.size(); + state->numStateValues = numValues; + if(state->iState != NULL && numValues > 0) + { + state->tmpValues = new char*[numValues];; + memset(state->tmpValues, 0, numValues * sizeof(char *)); + state->iState->restore(lilv_instance_get_handle(state->handle), LV2Synth::lv2state_stateRetreive, state, 0, state->_ppifeatures); + for(size_t i = 0; i < numValues; ++i) + { + if(state->tmpValues [i] != NULL) + delete [] state->tmpValues [i]; + } + delete [] state->tmpValues; + state->tmpValues = NULL; + } + + QMap >::const_iterator it; + for(it = state->iStateValues.begin(); it != state->iStateValues.end(); ++it) + { + QString name = it.key(); + QVariant qVal = it.value().second; + if(!name.isEmpty() && qVal.isValid()) + { + if(qVal.type() == QVariant::String) // plugin ui uri + { + QString sUiUri = qVal.toString(); + LV2_PLUGIN_UI_TYPES::iterator it; + for(it = state->synth->_pluginUiTypes.begin(); it != state->synth->_pluginUiTypes.end(); ++it) + { + if(sUiUri == QString(lilv_node_as_uri(lilv_ui_get_uri(it->first)))) + { + state->uiCurrent = it->first; + break; + } + } + } + else + { + if(state->sif != NULL) //setting control value only for synths + { + + bool ok = false; + float val = (float)qVal.toDouble(&ok); + if(ok) + { + std::map::iterator it = state->controlsNameMap.find(name.toLower()); + if(it != state->controlsNameMap.end()) + { + size_t ctrlNum = it->second; + state->sif->_controls [ctrlNum].val = state->sif->_controls [ctrlNum].tmpVal = val; + + } + } + } + } + + + } + } + +} + +unsigned LV2Synth::lv2ui_IsSupported(const char *, const char *ui_type_uri) +{ + if(strcmp(LV2_F_UI_Qt5_UI, ui_type_uri) == 0 + || (strcmp(LV2_UI__GtkUI, ui_type_uri) == 0) + || strcmp(LV2_UI__X11UI, ui_type_uri) == 0) + { + return 1; + } + return 0; +} + +void LV2Synth::lv2prg_updatePrograms(LV2PluginWrapper_State *state) +{ + assert(state != NULL); + state->index2prg.clear(); + state->prg2index.clear(); + if(state->prgIface != NULL) + { + uint32_t iPrg = 0; + const LV2_Program_Descriptor *pDescr = NULL; + while((pDescr = state->prgIface->get_program( + lilv_instance_get_handle(state->handle), iPrg)) != NULL) + { + // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. + uint32_t hb = pDescr->bank >> 8; + uint32_t lb = pDescr->bank & 0xff; + if(hb < 128 && lb < 128 && pDescr->program < 128) + { + lv2ExtProgram extPrg; + extPrg.index = iPrg; + extPrg.bank = pDescr->bank; + extPrg.prog = pDescr->program; + extPrg.useIndex = true; + extPrg.name = QString(pDescr->name); + + state->index2prg.insert(std::make_pair(iPrg, extPrg)); + hb &= 0x7f; + lb &= 0x7f; + uint32_t midiprg = (hb << 16) + (lb << 8) + extPrg.prog; + state->prg2index.insert(std::make_pair(midiprg, iPrg)); + } + ++iPrg; + } + } + +} + +int LV2Synth::lv2_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...) +{ + va_list argptr; + va_start(argptr, fmt); + int ret = LV2Synth::lv2_vprintf(handle, type, fmt, argptr); + va_end(argptr); + return ret; +} + +int LV2Synth::lv2_vprintf(LV2_Log_Handle, LV2_URID, const char *fmt, va_list ap) +{ + return vprintf(fmt, ap); + +} + +char *LV2Synth::lv2state_makePath(LV2_State_Make_Path_Handle handle, const char *path) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + assert(state != NULL); + + QFile ff(path); + QFileInfo fiPath(ff); + + if(fiPath.isAbsolute()) + { + return strdup(path); + } + + QString plugName = (state->sif != NULL) ? state->sif->name() : state->plugInst->name(); + QString dirName = MusEGlobal::museProject + QString("/") + plugName; + QDir dir; + dir.mkpath(dirName); + + QString resPath = dirName + QString("/") + QString(path); + return strdup(resPath.toUtf8().constData()); + +} + +char *LV2Synth::lv2state_abstractPath(LV2_State_Map_Path_Handle handle, const char *absolute_path) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + assert(state != NULL); + + //some plugins do not support abstract paths properly, + //so return duplicate without modification for now + //return strdup(absolute_path); + + QString resPath = QString(absolute_path); + int rIdx = resPath.lastIndexOf('/'); + if(rIdx > -1) + { + resPath = resPath.mid(rIdx + 1); + } + QString plugName = (state->sif != NULL) ? state->sif->name() : state->plugInst->name(); + QDir dir; + QString prjPath = MusEGlobal::museProject + QString("/") + plugName; + dir.mkpath(prjPath); + QFile ff(absolute_path); + QFileInfo fiPath(ff); + if(resPath.length() && fiPath.isAbsolute() && resPath != QString(absolute_path)) + { + QFile::link(QString(absolute_path), prjPath + QString("/") + resPath); + } + + if(strlen(absolute_path) == 0) + { + resPath = prjPath + QString("/") + resPath; + } + + + return strdup(resPath.toUtf8().constData()); + +} + +char *LV2Synth::lv2state_absolutePath(LV2_State_Map_Path_Handle handle, const char *abstract_path) +{ + return LV2Synth::lv2state_makePath((LV2_State_Make_Path_Handle)handle, abstract_path); +} + +void LV2Synth::lv2state_populatePresetsMenu(LV2PluginWrapper_State *state, MusEGui::PopupMenu *menu) +{ + menu->clear(); + menu->setIcon(QIcon(*MusEGui::presetsNewIcon)); + LV2Synth *synth = state->synth; + //this is good by slow down menu population. + //So it's called only on changes (preset save/manual update) + //LV2Synth::lv2state_UnloadLoadPresets(synth, true); + MusEGui::MenuTitleItem *actPresetActionsHeader = new MusEGui::MenuTitleItem(QObject::tr("Preset actions"), menu); + menu->addAction(actPresetActionsHeader); + QAction *actSave = menu->addAction(QObject::tr("Save preset...")); + actSave->setObjectName("lv2state_presets_save_action"); + actSave->setData(QVariant::fromValue(static_cast(lv2CacheNodes.lv2_actionSavePreset))); + QAction *actUpdate = menu->addAction(QObject::tr("Update list")); + actUpdate->setObjectName("lv2state_presets_update_action"); + actUpdate->setData(QVariant::fromValue(static_cast(lv2CacheNodes.lv2_actionUpdatePresets))); + std::map::iterator it; + MusEGui::MenuTitleItem *actSavedPresetsHeader = new MusEGui::MenuTitleItem(QObject::tr("Saved presets"), menu); + menu->addAction(actSavedPresetsHeader); + + for(it = synth->_presets.begin(); it != synth->_presets.end(); ++it) + { + QAction *act = menu->addAction(it->first); + act->setData(QVariant::fromValue(static_cast((it->second)))); + } + if(menu->actions().size() == 0) + { + QAction *act = menu->addAction(QObject::tr("No presets found")); + act->setDisabled(true); + act->setData(QVariant::fromValue(NULL)); + } + + + +} + +void LV2Synth::lv2state_PortWrite(LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, const void *buffer, bool fromUi) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)controller; + + assert(state != NULL); //this shouldn't happen + assert(state->inst != NULL || state->sif != NULL); // this too + + if(protocol != 0 && protocol != state->synth->_uAtom_EventTransfer) + { +#ifdef DEBUG_LV2 + std::cerr << "LV2Synth::lv2state_PortWrite: unsupported protocol (" << protocol << ")" << std::endl; +#endif + return; + } + + if(protocol == state->synth->_uAtom_EventTransfer) //put atom transfers to dedicated ring buffer + { +#ifdef DEBUG_LV2 + std::cerr << "LV2Synth::lv2state_PortWrite: atom_EventTransfer, port = " << port_index << ", size =" << buffer_size << std::endl; +#endif + state->uiControlEvt.put(port_index, buffer_size, buffer); + return; + } + + std::map::iterator it = state->synth->_idxToControlMap.find(port_index); + + if(it == state->synth->_idxToControlMap.end()) + { +#ifdef DEBUG_LV2 + std::cerr << "LV2Synth::lv2state_PortWrite: wrong port index (" << port_index << ")" << std::endl; +#endif + return; + } + + uint32_t cport = it->second; + float value = *(float *)buffer; + // Schedules a timed control change: + ControlEvent ce; + ce.unique = false; + ce.fromGui = fromUi; // It came from the plugin's own GUI (or not). + ce.idx = cport; + ce.value = value; + // Don't use timestamp(), because it's circular, which is making it impossible to deal + // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. + ce.frame = MusEGlobal::audio->curFrame(); + + ControlFifo *_controlFifo = NULL; + if(state->inst != NULL) + { + _controlFifo = &state->plugInst->_controlFifo; + if(fromUi) + { + // Record automation: + // Take care of this immediately, because we don't want the silly delay associated with + // processing the fifo one-at-a-time in the apply(). + // NOTE: With some vsts we don't receive control events until the user RELEASES a control. + // So the events all arrive at once when the user releases a control. + // That makes this pretty useless... But what the heck... + //AutomationType at = AUTO_OFF; + if(state->plugInst->_track && state->plugInst->_id != -1) + { + unsigned long id = genACnum(state->plugInst->_id, cport); + state->plugInst->_track->recordAutomation(id, value); + //at = state->plugInst->_track->automationType(); + } + + //state->plugInst->enableController(cport, false); + } + } + else if(state->sif != NULL) + { + _controlFifo = &state->sif->_controlFifo; + if(fromUi) + { + // Record automation: + // Take care of this immediately, because we don't want the silly delay associated with + // processing the fifo one-at-a-time in the apply(). + // NOTE: With some vsts we don't receive control events until the user RELEASES a control. + // So the events all arrive at once when the user releases a control. + // That makes this pretty useless... But what the heck... + if(state->sif->id() != -1) + { + unsigned long pid = genACnum(state->sif->id(), cport); + state->sif->synti->recordAutomation(pid, value); + } + + //state->sif->enableController(cport, false); + } + } + + if(fromUi) + { + state->controlTimers [cport] = 1000 / 30; // 1 sec controllers will not be send to guis + } + + assert(_controlFifo != NULL); + if(_controlFifo->put(ce)) + std::cerr << "LV2Synth::lv2state_PortWrite: fifo overflow: in control number:" << cport << std::endl; + +#ifdef DEBUG_LV2 + std::cerr << "LV2Synth::lv2state_PortWrite: port=" << cport << "(" << port_index << ")" << ", val=" << value << std::endl; +#endif + + +} + +void LV2Synth::lv2state_setPortValue(const char *port_symbol, void *user_data, const void *value, uint32_t size, uint32_t type) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)user_data; + assert(state != NULL); + std::map::iterator it = state->controlsSymMap.find(QString::fromUtf8(port_symbol).toLower()); + if(it != state->controlsSymMap.end()) + { + size_t ctrlNum = it->second; + uint32_t ctrlIdx = state->synth->_controlInPorts [ctrlNum].index; + float fvalue; + if (type == state->atomForge.Float) + { + fvalue = *(const float*)value; + } + else if (type == state->atomForge.Double) + { + fvalue = *(const double*)value; + } + else if (type == state->atomForge.Int) + { + fvalue = *(const int32_t*)value; + } + else if (type == state->atomForge.Long) + { + fvalue = *(const int64_t*)value; + } + else + { + fprintf(stderr, "error: Preset `%s' value has bad type <%s>\n", + port_symbol, state->synth->uridBiMap.unmap(type)); + return; + } + LV2Synth::lv2state_PortWrite((LV2UI_Controller)user_data, ctrlIdx, size, 0, &fvalue, false); + } + +} + +const void *LV2Synth::lv2state_getPortValue(const char *port_symbol, void *user_data, uint32_t *size, uint32_t *type) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)user_data; + assert(state != NULL); + std::map::iterator it = state->controlsSymMap.find(QString::fromUtf8(port_symbol).toLower()); + *size = *type = 0; + if(it != state->controlsSymMap.end()) + { + size_t ctrlNum = it->second; + MusECore::Port *controls = NULL; + + if(state->plugInst != NULL) + { + controls = state->plugInst->controls; + + } + else if(state->sif != NULL) + { + controls = state->sif->_controls; + } + + if(controls != NULL) + { + *size = sizeof(float); + *type = state->atomForge.Float; + return &controls [ctrlNum].val; + } + } + + return NULL; + +} + +void LV2Synth::lv2state_applyPreset(LV2PluginWrapper_State *state, LilvNode *preset) +{ + //handle special actions first + if(preset == lv2CacheNodes.lv2_actionSavePreset) + { + bool isOk = false; + QString presetName = QInputDialog::getText(MusEGlobal::muse, QObject::tr("Enter new preset name"), + QObject::tr(("Preset name:")), QLineEdit::Normal, + QString(""), &isOk); + if(isOk && !presetName.isEmpty()) + { + presetName = presetName.trimmed(); + QString synthName = state->synth->name().replace(' ', '_'); + QString presetDir = MusEGlobal::museUser + QString("/.lv2/") + + synthName + QString("_") + + presetName + QString(".lv2/"); + QString presetFile = synthName + QString("_") + presetName + + QString(".ttl"); + QString plugName = (state->sif != NULL) ? state->sif->name() : state->plugInst->name(); + QString plugFileDirName = MusEGlobal::museProject + QString("/") + plugName; + char *cPresetName = strdup(presetName.toUtf8().constData()); + char *cPresetDir = strdup(presetDir.toUtf8().constData()); + char *cPresetFile = strdup(presetFile.toUtf8().constData()); + char *cPlugFileDirName = strdup(plugFileDirName.toUtf8().constData()); + LilvState* const lilvState = lilv_state_new_from_instance(state->synth->_handle, state->handle, &state->synth->_lv2_urid_map, + cPlugFileDirName, cPresetDir, cPresetDir, cPresetDir, + LV2Synth::lv2state_getPortValue, state, + LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE, NULL); + + lilv_state_set_label(lilvState, cPresetName); + + lilv_state_save(lilvWorld, &state->synth->_lv2_urid_map, + &state->synth->_lv2_urid_unmap, + lilvState, NULL, cPresetDir, + cPresetFile); + + lilv_state_free(lilvState); + free(cPresetName); + free(cPresetDir); + free(cPresetFile); + free(cPlugFileDirName); + LV2Synth::lv2state_UnloadLoadPresets(state->synth, true, true); + + } + + return; + } + else if(preset == lv2CacheNodes.lv2_actionUpdatePresets) + { + LV2Synth::lv2state_UnloadLoadPresets(state->synth, true, true); + return; + } + LilvState* lilvState = lilv_state_new_from_world(lilvWorld, &state->synth->_lv2_urid_map, preset); + if(lilvState) + { + lilv_state_restore(lilvState, state->handle, LV2Synth::lv2state_setPortValue, state, 0, NULL); + lilv_state_free(lilvState); + } + +} + +void LV2Synth::lv2state_UnloadLoadPresets(LV2Synth *synth, bool load, bool update) +{ + assert(synth != NULL); + + //std::cerr << "LV2Synth::lv2state_UnloadLoadPresets: handling <" << synth->_name.toStdString() << ">" << std::endl; + + std::map::iterator it; + for(it = synth->_presets.begin(); it != synth->_presets.end(); ++it) + { + lilv_world_unload_resource(lilvWorld, it->second); + lilv_node_free(it->second); + } + synth->_presets.clear(); + + + + if(load) + { + if(update) + { + //rescan and refresh user-defined presets first + QDirIterator dir_it(MusEGlobal::museUser + QString("/.lv2"), QStringList() << "*.lv2", QDir::Dirs, QDirIterator::NoIteratorFlags); + while (dir_it.hasNext()) + { + QString nextDir = dir_it.next() + QString("/"); + std::cerr << nextDir.toStdString() << std::endl; + SerdNode sdir = serd_node_new_file_uri((const uint8_t*)nextDir.toUtf8().constData(), 0, 0, 0); + LilvNode* ldir = lilv_new_uri(lilvWorld, (const char*)sdir.buf); + lilv_world_unload_bundle(lilvWorld, ldir); + lilv_world_load_bundle(lilvWorld, ldir); + serd_node_free(&sdir); + lilv_node_free(ldir); + } + } + + //scan for preserts + LilvNodes* presets = lilv_plugin_get_related(synth->_handle, lv2CacheNodes.lv2_psetPreset); + LILV_FOREACH(nodes, i, presets) + { + const LilvNode* preset = lilv_nodes_get(presets, i); +#ifdef DEBUG_LV2 + std::cerr << "\tPreset: " << lilv_node_as_uri(preset) << std::endl; +#endif + lilv_world_load_resource(lilvWorld, preset); + LilvNodes* pLabels = lilv_world_find_nodes(lilvWorld, preset, lv2CacheNodes.lv2_rdfsLabel, NULL); + if (pLabels != NULL) + { + const LilvNode* pLabel = lilv_nodes_get_first(pLabels); + synth->_presets.insert(std::make_pair(lilv_node_as_string(pLabel), lilv_node_duplicate(preset))); + lilv_nodes_free(pLabels); + } + else + { +#ifdef DEBUG_LV2 + std::cerr << "\t\tPreset <%s> has no rdfs:label" << lilv_node_as_string(lilv_nodes_get(presets, i)) << std::endl; +#endif + } + } + lilv_nodes_free(presets); + + } + +} + + + + +void LV2SynthIF::lv2prg_Changed(LV2_Programs_Handle handle, int32_t index) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; +#ifdef DEBUG_LV2 + std::cerr << "LV2Synth::lv2prg_Changed: index: " << index << std::endl; +#endif + if(state->sif && state->sif->synti) + { + std::map::iterator itIndex = state->index2prg.find(index); + if(itIndex == state->index2prg.end()) + return; + int ch = 0; + int port = state->sif->synti->midiPort(); + const lv2ExtProgram &extPrg = itIndex->second; + uint32_t hb = extPrg.bank >> 8; + uint32_t lb = extPrg.bank & 0xff; + if(hb > 127 || lb > 127 || extPrg.prog > 127) + return; + hb &= 0x7f; + lb &= 0x7f; + state->sif->synti->setCurrentProg(ch, extPrg.prog, lb, hb); + const int rv = (hb << 16) | (lb << 8) | extPrg.prog; + if(port != -1) + { + MidiPlayEvent event(0, port, ch, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, rv); + //MusEGlobal::midiPorts[port].sendEvent(event); + MusEGlobal::midiPorts[port].sendHwCtrlState(event, false); + if(state->sif->id() != -1 && state->sif->_controls != NULL) + { + for(unsigned long k = 0; k < state->sif->_inportsControl; ++k) + { + state->sif->synti->setPluginCtrlVal(genACnum(state->sif->id(), k), state->sif->_controls[k].val); + } + } + } + } +} + + +LV2Synth::LV2Synth(const QFileInfo &fi, QString label, QString name, QString author, const LilvPlugin *_plugin, Plugin::PluginFeatures reqFeatures) + : Synth(fi, label, name, author, QString(""), reqFeatures), + _handle(_plugin), + _features(NULL), + _ppfeatures(NULL), + _options(NULL), + _isSynth(false), + _uis(NULL), + _hasFreeWheelPort(false), + _isConstructed(false), + _pluginControlsDefault(NULL), + _pluginControlsMin(NULL), + _pluginControlsMax(NULL) +{ + + //fake id for LV2PluginWrapper functionality + _uniqueID = uniqueID++; + + _midi_event_id = mapUrid(LV2_MIDI__MidiEvent); + + _uTime_Position = mapUrid(LV2_TIME__Position); + _uTime_frame = mapUrid(LV2_TIME__frame); + _uTime_speed = mapUrid(LV2_TIME__speed); + _uTime_beatsPerMinute = mapUrid(LV2_TIME__beatsPerMinute); + _uTime_barBeat = mapUrid(LV2_TIME__barBeat); + _uAtom_EventTransfer = mapUrid(LV2_ATOM__eventTransfer); + _uAtom_Chunk = mapUrid(LV2_ATOM__Chunk); + _uAtom_Sequence = mapUrid(LV2_ATOM__Sequence); + _uAtom_StateChanged = mapUrid(LV2_F_STATE_CHANGED); + _uAtom_Object = mapUrid(LV2_ATOM__Object); + + _sampleRate = (double)MusEGlobal::sampleRate; + _fSampleRate = (float)MusEGlobal::sampleRate; + + //prepare features and options arrays + LV2_Options_Option _tmpl_options [] = + { + {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_SAMPLE_RATE), sizeof(float), uridBiMap.map(LV2_ATOM__Float), &_fSampleRate}, + {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_MIN_BLKLEN), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &MusEGlobal::segmentSize}, + {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_MAX_BLKLEN), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &MusEGlobal::segmentSize}, + {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_SEQ_SIZE), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &MusEGlobal::segmentSize}, + {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_CORE__sampleRate), sizeof(double), uridBiMap.map(LV2_ATOM__Double), &_sampleRate}, + {LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL} + + }; + + _options = new LV2_Options_Option[SIZEOF_ARRAY(_tmpl_options)]; // last option is NULLs + + for(uint32_t i = 0; i < SIZEOF_ARRAY(_tmpl_options); i++) + { + _options [i] = _tmpl_options [i]; + } + + _features = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; + _ppfeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; + _lv2_urid_map.map = Synth_Urid_Map; + _lv2_urid_map.handle = this; + _lv2_urid_unmap.unmap = Synth_Urid_Unmap; + _lv2_urid_unmap.handle = this; + _lv2_uri_map.uri_to_id = Synth_Uri_Map; + _lv2_uri_map.callback_data = this; + _lv2_log_log.handle = this; + _lv2_log_log.printf = LV2Synth::lv2_printf; + _lv2_log_log.vprintf = LV2Synth::lv2_vprintf; + + uint32_t i; + + for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) + { + _features [i] = lv2Features [i]; + + if(_features [i].URI == NULL) + { + break; + } + + if(std::string(LV2_F_URID_MAP) == _features [i].URI) + { + _features [i].data = &_lv2_urid_map; + } + else if(std::string(LV2_F_URID_UNMAP) == _features [i].URI) + { + _features [i].data = &_lv2_urid_unmap; + } + else if(std::string(LV2_F_URI_MAP) == _features [i].URI) + { + _features [i].data = &_lv2_uri_map; + } + else if(std::string(LV2_F_OPTIONS) == _features [i].URI) + { + _features [i].data = _options; + } + else if(std::string(LV2_F_INSTANCE_ACCESS) == _features [i].URI) + { + _fInstanceAccess = i; + } + else if(std::string(LV2_F_UI_PARENT) == _features [i].URI) + { + _fUiParent = i; + } + else if((std::string(LV2_F_UI_EXTERNAL_HOST) == _features [i].URI)) + { + _fExtUiHost = i; + } + else if((std::string(LV2_UI_EXTERNAL_DEPRECATED) == _features [i].URI)) + { + _fExtUiHostD = i; + } + else if((std::string(LV2_F_WORKER_SCHEDULE) == _features [i].URI)) + { + _fWrkSchedule = i; + } + else if((std::string(LV2_UI__resize) == _features [i].URI)) + { + _fUiResize = i; + } + else if((std::string(LV2_PROGRAMS__Host) == _features [i].URI)) + { + _fPrgHost = i; + } + else if((std::string(LV2_LOG__log) == _features [i].URI)) + { + _features [i].data = &_lv2_log_log; + } + else if((std::string(LV2_STATE__makePath) == _features [i].URI)) + { + _fMakePath = i; + } + else if((std::string(LV2_STATE__mapPath) == _features [i].URI)) + { + _fMapPath = i; + } + else if(std::string(LV2_F_DATA_ACCESS) == _features [i].URI) + { + _fDataAccess = i; //must be the last! + } + + _ppfeatures [i] = &_features [i]; + } + + _ppfeatures [i] = 0; + + //enum plugin ports; + uint32_t numPorts = lilv_plugin_get_num_ports(_handle); + + const LilvPort *lilvFreeWheelPort = lilv_plugin_get_port_by_designation(_handle, lv2CacheNodes.lv2_InputPort, lv2CacheNodes.lv2_FreeWheelPort); + + _pluginControlsDefault = new float [numPorts]; + _pluginControlsMin = new float [numPorts]; + _pluginControlsMax = new float [numPorts]; + memset(_pluginControlsDefault, 0, sizeof(float) * numPorts); + memset(_pluginControlsMin, 0, sizeof(float) * numPorts); + memset(_pluginControlsMax, 0, sizeof(float) * numPorts); + + lilv_plugin_get_port_ranges_float(_handle, _pluginControlsMin, _pluginControlsMax, _pluginControlsDefault); + + for(uint32_t i = 0; i < numPorts; i++) + { + const LilvPort *_port = lilv_plugin_get_port_by_index(_handle, i); + LilvNode *_nPname = lilv_port_get_name(_handle, _port); + const LilvNode *_nPsym = lilv_port_get_symbol(_handle, _port); + char cAutoGenPortName [1024]; + char cAutoGenPortSym [1024]; + memset(cAutoGenPortName, 0, sizeof(cAutoGenPortName)); + memset(cAutoGenPortSym, 0, sizeof(cAutoGenPortSym)); + snprintf(cAutoGenPortName, sizeof(cAutoGenPortName) - 1, "autoport #%u", i); + snprintf(cAutoGenPortSym, sizeof(cAutoGenPortSym) - 1, "autoport#%u", i); + const char *_portName = cAutoGenPortName; + const char *_portSym = cAutoGenPortSym; + + if(_nPname != 0) + { + _portName = lilv_node_as_string(_nPname); + } + + if(_nPsym != 0) + { + _portSym = lilv_node_as_string(_nPsym); + } + + const bool optional = lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_connectionOptional); + + LV2_MIDI_PORTS *mPorts = &_midiOutPorts; + LV2_CONTROL_PORTS *cPorts = &_controlOutPorts; + LV2_AUDIO_PORTS *aPorts = &_audioOutPorts; + + if(lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_InputPort)) + { + mPorts = &_midiInPorts; + cPorts = &_controlInPorts; + aPorts = &_audioInPorts; + } + else if(!lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_OutputPort)) + { +#ifdef DEBUG_LV2 + std::cerr << "plugin has port with unknown direction - ignoring" << std::endl; +#endif + if(_nPname != 0) + lilv_node_free(_nPname); + continue; + } + + bool isCVPort = lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_CVPort); + + if(lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_ControlPort) || isCVPort) + { + LV2ControlPortType _cType = LV2_PORT_CONTINUOUS; + if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portDiscrete)) + _cType = LV2_PORT_DISCRETE; + else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portInteger)) + _cType = LV2_PORT_INTEGER; + else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portTrigger) + || lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portToggled)) + _cType = LV2_PORT_TRIGGER; + else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portLogarithmic)) + _cType = LV2_PORT_LOGARITHMIC; + + cPorts->push_back(LV2ControlPort(_port, i, 0.0f, _portName, _portSym, _cType, isCVPort)); + + if(std::isnan(_pluginControlsDefault [i])) + _pluginControlsDefault [i] = 0; + if(std::isnan(_pluginControlsMin [i])) + _pluginControlsMin [i] = 0; + if(std::isnan(_pluginControlsMax [i])) + _pluginControlsMax [i] = 0; + + if(isCVPort) + { + _pluginControlsDefault [i] = 1; + _pluginControlsMin [i] = 0; + _pluginControlsMax [i] = 1; + } + else if (lilv_port_has_property (_handle, _port, lv2CacheNodes.lv2_SampleRate)) + { + _pluginControlsDefault [i] *= MusEGlobal::sampleRate; + _pluginControlsMin [i] *= MusEGlobal::sampleRate; + _pluginControlsMax [i] *= MusEGlobal::sampleRate; + } + + } + else if(lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_AudioPort)) + { + aPorts->push_back(LV2AudioPort(_port, i, NULL, _portName)); + } + else if(lilv_port_is_a(_handle, _port, lv2CacheNodes.ev_EventPort)) + { + bool portSupportsTimePos = lilv_port_supports_event(_handle, _port, lv2CacheNodes.lv2_TimePosition); + mPorts->push_back(LV2MidiPort(_port, i, _portName, true /* old api is on */,portSupportsTimePos)); + } + else if(lilv_port_is_a(_handle, _port, lv2CacheNodes.atom_AtomPort)) + { + bool portSupportsTimePos = lilv_port_supports_event(_handle, _port, lv2CacheNodes.lv2_TimePosition); + mPorts->push_back(LV2MidiPort(_port, i, _portName, false /* old api is off */, portSupportsTimePos)); + } + else if(!optional) + { +//#ifdef DEBUG_LV2 + std::cerr << "plugin has port with unknown type - ignoring plugin " << label.toStdString() << "!" << std::endl; +//#endif + if(_nPname != 0) + lilv_node_free(_nPname); + return; + } + + if(_nPname != 0) + lilv_node_free(_nPname); + } + + for(uint32_t i = 0; i < _controlInPorts.size(); ++i) + { + _idxToControlMap.insert(std::pair(_controlInPorts [i].index, i)); + if(lilvFreeWheelPort != NULL) + { + if(lilv_port_get_index(_handle, _controlInPorts [i].port) == lilv_port_get_index(_handle, lilvFreeWheelPort)) + { + _hasFreeWheelPort = true; + _freeWheelPortIndex = i; + } + } + } + + const LilvPluginClass *cls = lilv_plugin_get_class(_plugin); + const LilvNode *ncuri = lilv_plugin_class_get_uri(cls); + const char *clsname = lilv_node_as_uri(ncuri); + if((strcmp(clsname, LV2_INSTRUMENT_CLASS) == 0) && (_midiInPorts.size() > 0)) + { + _isSynth = true; + } + + const LilvNode *pluginUIType = NULL; + _uis = NULL; + + + _uis = lilv_plugin_get_uis(_handle); + + if(_uis) + { + LilvIter *it = lilv_uis_begin(_uis); + +#ifdef DEBUG_LV2 + std::cerr << "Plugin support uis of type:" << std::endl; +#endif + + + while(!lilv_uis_is_end(_uis, it)) + { + const LilvUI *ui = lilv_uis_get(_uis, it); + + if(lilv_ui_is_supported(ui, + LV2Synth::lv2ui_IsSupported, + lv2CacheNodes.host_uiType, + &pluginUIType)) + { +#ifdef DEBUG_LV2 + const char *strUiType = lilv_node_as_string(pluginUIType); //internal uis are preferred + std::cerr << "Plugin " << label.toStdString() << " supports ui of type " << strUiType << std::endl; +#endif + +#ifndef HAVE_GTK2 + const char *cUiUri = lilv_node_as_uri(pluginUIType); + if(strcmp(LV2_UI__GtkUI, cUiUri) != 0) +#endif + + _pluginUiTypes.insert(std::make_pair(ui, std::make_pair(false, pluginUIType))); + } + else + { + const LilvNodes *nUiClss = lilv_ui_get_classes(ui); + LilvIter *nit = lilv_nodes_begin(nUiClss); + + while(!lilv_nodes_is_end(_uis, nit)) + { + const LilvNode *nUiCls = lilv_nodes_get(nUiClss, nit); +#ifdef DEBUG_LV2 + const char *ClsStr = lilv_node_as_string(nUiCls); + std::cerr << ClsStr << std::endl; +#endif + bool extUi = lilv_node_equals(nUiCls, lv2CacheNodes.ext_uiType); + bool extdUi = lilv_node_equals(nUiCls, lv2CacheNodes.ext_d_uiType); + if(extUi || extdUi) + { + pluginUIType = extUi ? lv2CacheNodes.ext_uiType : lv2CacheNodes.ext_d_uiType; +#ifdef DEBUG_LV2 + std::cerr << "Plugin " << label.toStdString() << " supports ui of type " << LV2_UI_EXTERNAL << std::endl; +#endif + +#ifndef HAVE_GTK2 + const char *cUiUri = lilv_node_as_uri(pluginUIType); + if(strcmp(LV2_UI__GtkUI, cUiUri) != 0) +#endif + + _pluginUiTypes.insert(std::make_pair(ui, std::make_pair(true, pluginUIType))); + } + + nit = lilv_nodes_next(nUiClss, nit); + } + + } + + it = lilv_uis_next(_uis, it); + }; + + + } + + _presets.clear(); + + LV2Synth::lv2state_UnloadLoadPresets(this, true); + + _isConstructed = true; +} + +LV2Synth::~LV2Synth() +{ + LV2Synth::lv2state_UnloadLoadPresets(this); + + if(_ppfeatures) + { + delete [] _ppfeatures; + _ppfeatures = NULL; + } + + if(_features) + { + delete [] _features; + _features = NULL; + } + + if(_options) + { + delete [] _options; + _options = NULL; + } + + if(_uis != NULL) + { + lilv_uis_free(_uis); + _uis = NULL; + } +} + + +SynthIF *LV2Synth::createSIF(SynthI *synthi) +{ + ++_instances; + LV2SynthIF *sif = new LV2SynthIF(synthi); + + if(!sif->init(this)) + { + delete sif; + sif = NULL; + } + + return sif; +} + +LV2_URID LV2Synth::mapUrid(const char *uri) +{ + return uridBiMap.map(uri); +} + +const char *LV2Synth::unmapUrid(LV2_URID id) +{ + return uridBiMap.unmap(id); +} + +LV2SynthIF::~LV2SynthIF() +{ + if(_state != NULL) + { + _state->deleteLater = true; + //_uiState->uiTimer->stopNextTime(false); + if(_state->pluginWindow != NULL) + _state->pluginWindow->stopNextTime(); + else + LV2Synth::lv2state_FreeState(_state); + _state = NULL; + + } + + LV2_AUDIO_PORTS::iterator _itA = _audioInPorts.begin(); + + for(; _itA != _audioInPorts.end(); ++_itA) + { + free((*_itA).buffer); + } + + _itA = _audioOutPorts.begin(); + + for(; _itA != _audioOutPorts.end(); ++_itA) + { + free((*_itA).buffer); + } + + if(_audioInSilenceBuf) + free(_audioInSilenceBuf); + + if(_audioInBuffers) + { + delete [] _audioInBuffers; + _audioInBuffers = NULL; + } + + if(_audioOutBuffers) + { + delete [] _audioOutBuffers; + _audioOutBuffers = NULL; + } + + if(_controls) + { + delete [] _controls; + } + + if(_controlsOut) + { + delete [] _controlsOut; + } + + if(_ppifeatures) + { + delete [] _ppifeatures; + _ppifeatures = NULL; + } + + if(_ifeatures) + { + delete [] _ifeatures; + _ifeatures = NULL; + } +} + +LV2SynthIF::LV2SynthIF(SynthI *s): SynthIF(s) +{ + _synth = NULL; + _handle = NULL; + _audioInBuffers = NULL; + _audioOutBuffers = NULL; + _inports = 0; + _outports = 0; + _controls = NULL; + _controlsOut = NULL; + _inportsControl = 0; + _outportsControl = 0; + _inportsMidi = 0, + _outportsMidi = 0, + _audioInSilenceBuf = NULL; + _ifeatures = NULL; + _ppifeatures = NULL; + _state = NULL; +} + +bool LV2SynthIF::init(LV2Synth *s) +{ + _synth = s; + + //use LV2Synth features as template + + _state = new LV2PluginWrapper_State; + _state->inst = NULL; + _state->widget = NULL; + _state->uiInst = NULL; + _state->plugInst = NULL; + _state->_ifeatures = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; + _state->_ppifeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; + _state->sif = this; + _state->synth = _synth; + + LV2Synth::lv2state_FillFeatures(_state); + + _state->handle = _handle = lilv_plugin_instantiate(_synth->_handle, (double)MusEGlobal::sampleRate, _state->_ppifeatures); + + if(_handle == NULL) + { + delete [] _state->_ppifeatures; + _state->_ppifeatures = NULL; + delete [] _state->_ifeatures; + _state->_ifeatures = NULL; + return false; + } + + _audioInPorts = s->_audioInPorts; + _audioOutPorts = s->_audioOutPorts; + _controlInPorts = s->_controlInPorts; + _controlOutPorts = s->_controlOutPorts; + _inportsMidi = _state->midiInPorts.size(); + _outportsMidi = _state->midiOutPorts.size(); + + + + _inportsControl = _controlInPorts.size(); + _outportsControl = _controlOutPorts.size(); + + if(_inportsControl != 0) + { + _controls = new Port[_inportsControl]; + } + else + { + _controls = NULL; + } + + if(_outportsControl != 0) + { + _controlsOut = new Port[_outportsControl]; + } + else + { + _controlsOut = NULL; + } + + _synth->midiCtl2PortMap.clear(); + _synth->port2MidiCtlMap.clear(); + + for(size_t i = 0; i < _inportsControl; i++) + { + uint32_t idx = _controlInPorts [i].index; + _controls [i].idx = idx; + _controls [i].val = _controls [i].tmpVal = _controlInPorts [i].defVal = _synth->_pluginControlsDefault [idx]; + if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == i) + _controls [i].enCtrl = false; + else + _controls [i].enCtrl = true; + _controlInPorts [i].minVal = _synth->_pluginControlsMin [idx]; + _controlInPorts [i].maxVal = _synth->_pluginControlsMax [idx]; + + int ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + i; + + // We have a controller number! Insert it and the lv2 port number into both maps. + _synth->midiCtl2PortMap.insert(std::pair(ctlnum, i)); + _synth->port2MidiCtlMap.insert(std::pair(i, ctlnum)); + + int id = genACnum(MAX_PLUGINS, i); + CtrlList *cl; + CtrlListList *cll = track()->controller(); + iCtrlList icl = cll->find(id); + + if(icl == cll->end()) + { + cl = new CtrlList(id); + cll->add(cl); + cl->setCurVal(_controls[i].val); + } + else + { + cl = icl->second; + _controls[i].val = cl->curVal(); + } + + cl->setRange(_synth->_pluginControlsMin [idx], _synth->_pluginControlsMax [idx]); + cl->setName(QString(_controlInPorts [i].cName)); + CtrlValueType vt = VAL_LINEAR; + switch(_controlInPorts [i].cType) + { + case LV2_PORT_CONTINUOUS: + vt = VAL_LINEAR; + break; + case LV2_PORT_DISCRETE: + case LV2_PORT_INTEGER: + vt = VAL_INT; + break; + case LV2_PORT_LOGARITHMIC: + vt = VAL_LOG; + break; + case LV2_PORT_TRIGGER: + vt = VAL_BOOL; + break; + default: + break; + } + + cl->setValueType(vt); + cl->setMode(((_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) + ||(_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC))? CtrlList::INTERPOLATE : CtrlList::DISCRETE); + + if(!_controlInPorts [i].isCVPort) + lilv_instance_connect_port(_handle, idx, &_controls [i].val); + } + + for(size_t i = 0; i < _outportsControl; i++) + { + uint32_t idx = _controlOutPorts [i].index; + _controlsOut[i].idx = idx; + _controlsOut[i].val = 0.0; + _controlsOut[i].tmpVal = 0.0; + _controlsOut[i].enCtrl = false; + _controlOutPorts [i].defVal = _controlOutPorts [i].minVal = _controlOutPorts [i].maxVal = 0.0; + if(!_controlOutPorts [i].isCVPort) + lilv_instance_connect_port(_handle, idx, &_controlsOut[i].val); + } + + + int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + + if(rv != 0) + { + fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + { + _audioInSilenceBuf[q] = MusEGlobal::denormalBias; + } + } + else + { + memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + } + + //cache number of ports + _inports = _audioInPorts.size(); + _outports = _audioOutPorts.size(); + + + if(_inports > 0) + { + _audioInBuffers = new float*[_inports]; + + for(size_t i = 0; i < _inports; i++) + { + int rv = posix_memalign((void **)&_audioInBuffers [i], 16, sizeof(float) * MusEGlobal::segmentSize); + + if(rv != 0) + { + fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + { + _audioInBuffers [i][q] = MusEGlobal::denormalBias; + } + } + else + { + memset(_audioInBuffers [i], 0, sizeof(float) * MusEGlobal::segmentSize); + } + + _audioInPorts [i].buffer = _audioInBuffers [i]; + lilv_instance_connect_port(_handle, _audioInPorts [i].index, _audioInBuffers [i]); + } + } + + if(_outports > 0) + { + _audioOutBuffers = new float*[_outports]; + + for(size_t i = 0; i < _outports; i++) + { + int rv = posix_memalign((void **)&_audioOutBuffers [i], 16, sizeof(float) * MusEGlobal::segmentSize); + + if(rv != 0) + { + fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + { + _audioOutBuffers [i][q] = MusEGlobal::denormalBias; + } + } + else + { + memset(_audioOutBuffers [i], 0, sizeof(float) * MusEGlobal::segmentSize); + } + + _audioOutPorts [i].buffer = _audioOutBuffers [i]; + lilv_instance_connect_port(_handle, _audioOutPorts [i].index, _audioOutBuffers [i]); + } + } + + LV2Synth::lv2state_PostInstantiate(_state); + activate(); + + + + return true; + +} + +void LV2SynthIF::doSelectProgram(unsigned char channel, int bankH, int bankL, int prog) +{ +// // Only if there's something to change... +// if(bankH >= 128 && bankL >= 128 && prog >= 128) +// return; + + if(bankH > 127) // Map "dont care" to 0 + bankH = 0; + if(bankL > 127) + bankL = 0; + if(prog > 127) + prog = 0; + + const int bank = (bankH << 8) | bankL; + + if(_state && _state->prgIface && (_state->prgIface->select_program || _state->prgIface->select_program_for_channel)) + { + if(_state->newPrgIface) + _state->prgIface->select_program_for_channel(lilv_instance_get_handle(_state->handle), channel, (uint32_t)bank, (uint32_t)prog); + else + _state->prgIface->select_program(lilv_instance_get_handle(_state->handle), (uint32_t)bank, (uint32_t)prog); + + + + /* + * 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 LV2 plugin is allowed to modify its own control-input ports.) + */ + if(id() != -1) + { + for(unsigned long k = 0; k < _inportsControl; ++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); + } + } + + //set update ui program flag + _state->uiChannel = channel; + _state->uiBank = bank; + _state->uiProg = prog; + _state->uiDoSelectPrg = true; + } +} + +void LV2SynthIF::sendLv2MidiEvent(LV2EvBuf *evBuf, long frame, int paramCount, uint8_t a, uint8_t b, uint8_t c) +{ + if(paramCount < 1 || paramCount > 3) + return; + + if(evBuf) + { + uint8_t midiEv [paramCount]; + midiEv [0] = a; + if(paramCount >= 2) + midiEv [1] = b; + if(paramCount == 3) + midiEv [2] = c; + evBuf->write(frame, 0, _synth->_midi_event_id, paramCount, midiEv); + } +} + +int LV2SynthIF::channels() const +{ + return (_outports) > MAX_CHANNELS ? MAX_CHANNELS : (_outports) ; + +} + +int LV2SynthIF::totalInChannels() const +{ + return _inports; +} + +int LV2SynthIF::totalOutChannels() const +{ + return _outports; +} + +void LV2SynthIF::activate() +{ + if(_handle) + { + lilv_instance_activate(_handle); + } +} + + +void LV2SynthIF::deactivate() +{ + if(_handle) + { + lilv_instance_deactivate(_handle); + } + +} + +void LV2SynthIF::deactivate3() +{ + deactivate(); +} + +int LV2SynthIF::eventsPending() const +{ + //TODO: what's this? + return 0; +} + +bool LV2SynthIF::lv2MidiControlValues(size_t port, int ctlnum, int *min, int *max, int *def) +{ + + float fmin, fmax, fdef; + int imin; + float frng; + + fdef = _controlInPorts [port].defVal; + fmin = _controlInPorts [port].minVal; + fmax = _controlInPorts [port].maxVal; + bool hasdef = (fdef == fdef); + + if(fmin != fmin) + { + fmin = 0.0; + } + + if(fmax != fmax) + { + fmax = 0.0; + } + + MidiController::ControllerType t = midiControllerType(ctlnum); + +#ifdef PLUGIN_DEBUGIN + printf("lv2MidiControlValues: ctlnum:%d ladspa port:%lu has default?:%d default:%f\n", ctlnum, port, hasdef, fdef); +#endif + + + frng = fmax - fmin; + imin = lrintf(fmin); + //imax = lrintf(fmax); + + int ctlmn = 0; + int ctlmx = 127;// Avoid divide-by-zero error below. + + +#ifdef PLUGIN_DEBUGIN + printf("lv2MidiControlValues: port min:%f max:%f \n", fmin, fmax); +#endif + + bool isneg = (imin < 0); + int bias = 0; + + switch(t) + { + case MidiController::RPN: + case MidiController::NRPN: + case MidiController::Controller7: + if(isneg) + { + ctlmn = -64; + ctlmx = 63; + bias = -64; + } + else + { + ctlmn = 0; + ctlmx = 127; + } + + break; + + case MidiController::Controller14: + case MidiController::RPN14: + case MidiController::NRPN14: + if(isneg) + { + ctlmn = -8192; + ctlmx = 8191; + bias = -8192; + } + else + { + ctlmn = 0; + ctlmx = 16383; + } + + break; + + case MidiController::Program: + ctlmn = 0; + ctlmx = 0x3fff; // FIXME: Really should not happen or be allowed. What to do here... + break; + + case MidiController::Pitch: + ctlmn = -8192; + ctlmx = 8191; + break; + + case MidiController::Velo: // cannot happen + default: + break; + } + + float fctlrng = float(ctlmx - ctlmn); + + + // It's a floating point control, just use wide open maximum range. + *min = ctlmn; + *max = ctlmx; + + float normdef = (frng == 0.0) ? 0.0 : fdef / frng; + fdef = normdef * fctlrng; + + // FIXME: TODO: Incorrect... Fix this somewhat more trivial stuff later.... + + *def = (int)lrintf(fdef) + bias; + +#ifdef PLUGIN_DEBUGIN + printf("lv2MidiControlValues: setting default:%d\n", *def); +#endif + + return hasdef; +} + +//--------------------------------------------------------- +// midi2LadspaValue +//--------------------------------------------------------- + +float LV2SynthIF::midi2Lv2Value(unsigned long port, int ctlnum, int val) +{ + + float fmin, fmax; + int imin; + float frng; + + MidiController::ControllerType t = midiControllerType(ctlnum); + +#ifdef PLUGIN_DEBUGIN + printf("midi2Lv2Value: ctlnum:%d port:%lu val:%d\n", ctlnum, port, val); +#endif + + fmin = _controlInPorts [port].minVal; + fmax = _controlInPorts [port].maxVal; + + if(fmin != fmin) + { + fmin = 0.0; + } + + if(fmax != fmax) + { + fmax = 0.0; + } + + + + frng = fmax - fmin; + imin = lrintf(fmin); + + + int ctlmn = 0; + int ctlmx = 127; + +#ifdef PLUGIN_DEBUGIN + printf("midi2Lv2Value: port min:%f max:%f \n", fmin, fmax); +#endif + + bool isneg = (imin < 0); + int bval = val; + //int cval = val; + + switch(t) + { + case MidiController::RPN: + case MidiController::NRPN: + case MidiController::Controller7: + if(isneg) + { + ctlmn = -64; + ctlmx = 63; + bval -= 64; + //cval -= 64; + } + else + { + ctlmn = 0; + ctlmx = 127; + //cval -= 64; + } + + break; + + case MidiController::Controller14: + case MidiController::RPN14: + case MidiController::NRPN14: + if(isneg) + { + ctlmn = -8192; + ctlmx = 8191; + bval -= 8192; + //cval -= 8192; + } + else + { + ctlmn = 0; + ctlmx = 16383; + //cval -= 8192; + } + + break; + + case MidiController::Program: + ctlmn = 0; + ctlmx = 0xffffff; + break; + + case MidiController::Pitch: + ctlmn = -8192; + ctlmx = 8191; + break; + + case MidiController::Velo: // cannot happen + default: + break; + } + + int ctlrng = ctlmx - ctlmn; + float fctlrng = float(ctlmx - ctlmn); + + + // Avoid divide-by-zero error below. + if(ctlrng == 0) + { + return 0.0; + } + + // It's a floating point control, just use wide open maximum range. + float normval = float(bval) / fctlrng; + float ret = normval * frng + fmin; + +#ifdef PLUGIN_DEBUGIN + printf("midi2Lv2Value: float returning:%f\n", ret); +#endif + + return ret; +} + +int LV2SynthIF::getControllerInfo(int id, QString* name, int *ctrl, int *min, int *max, int *initval) +{ + size_t _id = (size_t)id; + + if(_id == _inportsControl || _id == _inportsControl + 1) + { + // + // It is unknown at this point whether or not a synth recognizes aftertouch and poly aftertouch + // (channel and key pressure) midi messages, so add support for them now (as controllers). + // + if(_id == _inportsControl) + { + *ctrl = CTRL_POLYAFTER; + } + else if(_id == _inportsControl + 1) + { + *ctrl = CTRL_AFTERTOUCH; + } + + *min = 0; + *max = 127; + *initval = CTRL_VAL_UNKNOWN; + *name = midiCtrlName(*ctrl); + return ++_id; + } + else if(_id >= _inportsControl + 2) + { + return 0; + } + + int ctlnum; // = DSSI_NONE; + + // No controller number? Give it one. + //if(ctlnum == DSSI_NONE) + //{ + // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. + ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + _id; + //} + + + int def = CTRL_VAL_UNKNOWN; + + if(lv2MidiControlValues(_id, ctlnum, min, max, &def)) + { + *initval = def; + } + else + { + *initval = CTRL_VAL_UNKNOWN; + } + +#ifdef DEBUG_LV2 + printf("LV2SynthIF::getControllerInfo passed ctlnum:%d min:%d max:%d initval:%d\n", ctlnum, *min, *max, *initval); +#endif + + *ctrl = ctlnum; + *name = QString(_controlInPorts[_id].cName); + return ++_id; + +} + + + +bool LV2SynthIF::processEvent(const MidiPlayEvent &e, LV2EvBuf *evBuf, long frame) +{ + int chn = e.channel(); + int a = e.dataA(); + int b = e.dataB(); + int type = e.type(); + +#ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", e.type(), chn, a, b); +#endif + + // REMOVE Tim. Noteoff. Added. + const MidiInstrument::NoteOffMode nom = synti->noteOffMode(); + + switch(type) + { + case ME_NOTEON: +#ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_NOTEON\n"); +#endif + // REMOVE Tim. Noteoff. Changed. +// if(b) +// { +// sendLv2MidiEvent(evBuf, frame, (type | chn) & 0xff, a & 0x7f, b & 0x7f); +// } +// else +// { +// sendLv2MidiEvent(evBuf, frame, (ME_NOTEOFF | chn) & 0xff, a & 0x7f, 0); +// } + if(b == 0) + { + // Handle zero-velocity note ons. Technically this is an error because internal midi paths + // are now all 'note-off' without zero-vel note ons - they're converted to note offs. + // Nothing should be setting a Note type Event's on velocity to zero. + // But just in case... If we get this warning, it means there is still code to change. + fprintf(stderr, "LV2SynthIF::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", e.time(), e.type(), chn, a, b); + switch(nom) + { + // Instrument uses note offs. Convert to zero-vel note off. + case MidiInstrument::NoteOffAll: + //if(MusEGlobal::midiOutputTrace) + // fprintf(stderr, "MidiOut: LV2: Following event will be converted to zero-velocity note off:\n"); + sendLv2MidiEvent(evBuf, frame, 3, (ME_NOTEOFF | chn) & 0xff, a & 0x7f, 0); + break; + + // Instrument uses no note offs at all. Send as-is. + case MidiInstrument::NoteOffNone: + // Instrument converts all note offs to zero-vel note ons. Send as-is. + case MidiInstrument::NoteOffConvertToZVNoteOn: + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + break; + } + } + else + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + + break; + + case ME_NOTEOFF: +#ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_NOTEOFF\n"); +#endif + + // REMOVE Tim. Noteoff. Changed. +// sendLv2MidiEvent(evBuf, frame, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + switch(nom) + { + // Instrument uses note offs. Send as-is. + case MidiInstrument::NoteOffAll: + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + break; + + // Instrument uses no note offs at all. Send nothing. Eat up the event - return false. + case MidiInstrument::NoteOffNone: + return false; + + // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. + case MidiInstrument::NoteOffConvertToZVNoteOn: + //if(MusEGlobal::midiOutputTrace) + // fprintf(stderr, "MidiOut: LV2: Following event will be converted to zero-velocity note on:\n"); + sendLv2MidiEvent(evBuf, frame, 3, (ME_NOTEON | chn) & 0xff, a & 0x7f, 0); + break; + } + + break; + + case ME_PROGRAM: + { +#ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_PROGRAM\n"); +#endif + + int hb, lb; + synti->currentProg(chn, NULL, &lb, &hb); + synti->setCurrentProg(chn, a & 0xff, lb, hb); + doSelectProgram(chn, hb, lb, a); + + // Event pointer not filled. Return false. + return false; + } + break; + + case ME_CONTROLLER: + { +#ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER\n"); +#endif + + // Our internal hwCtrl controllers support the 'unknown' value. + // Don't send 'unknown' values to the driver. Ignore and return no error. + if(b == CTRL_VAL_UNKNOWN) + return false; + + if(a == CTRL_PROGRAM) + { +#ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); +#endif + + int hb = (b >> 16) & 0xff; + int lb = (b >> 8) & 0xff; + int pr = b & 0xff; + synti->setCurrentProg(chn, pr, lb, hb); + doSelectProgram(chn, hb, lb, pr); + + // Event pointer not filled. Return false. + return false; + } + + if(a == CTRL_HBANK) + { + int lb, pr; + synti->currentProg(chn, &pr, &lb, NULL); + synti->setCurrentProg(chn, pr, lb, b & 0xff); + doSelectProgram(chn, b, lb, pr); + // Event pointer not filled. Return false. + return false; + } + + if(a == CTRL_LBANK) + { + int hb, pr; + synti->currentProg(chn, &pr, NULL, &hb); + synti->setCurrentProg(chn, pr, b & 0xff, hb); + doSelectProgram(chn, hb, b, pr); + // Event pointer not filled. Return false. + return false; + } + + if(a == CTRL_PITCH) + { +#ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); +#endif + + b += 8192; + sendLv2MidiEvent(evBuf, frame, 3, (ME_PITCHBEND | chn) & 0xff, b & 0x7f, (b >> 7) & 0x7f); + // Event pointer filled. Return true. + return true; + } + + if(a == CTRL_AFTERTOUCH) + { +#ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_AFTERTOUCH\n"); +#endif + sendLv2MidiEvent(evBuf, frame, 2, (ME_AFTERTOUCH | chn) & 0xff, b & 0x7f, 0); + // Event pointer filled. Return true. + return true; + } + + if((a | 0xff) == CTRL_POLYAFTER) + { +#ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_POLYAFTER\n"); +#endif + sendLv2MidiEvent(evBuf, frame, 3, (ME_POLYAFTER | chn) & 0xff, a & 0x7f, b & 0x7f); + // Event pointer filled. Return true. + return true; + } + + ciMidiCtl2LadspaPort ip = _synth->midiCtl2PortMap.find(a); + + // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)? + // NOTE: There's no way to tell which of these controllers is supported by the plugin. + // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port. + if(ip == _synth->midiCtl2PortMap.end()) + { + + if(midiControllerType(a) == MidiController::NRPN14 + || midiControllerType(a) == MidiController::NRPN) + { + //send full nrpn control sequence (4 values): + // 99 + ((a & 0xff00) >> 8) - first byte + // 98 + (a & 0xff) - second byte + // 6 + ((b & 0x3f80) >> 7) - third byte + // 38 + (b & 0x7f) - fourth byte + + + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 99, ((a & 0xff00) >> 8)); + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 98, (a & 0xff)); + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 6, ((b & 0x3f80) >> 7)); + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 38, (b & 0x7f)); + return true; + + } + else if(midiControllerType(a) != MidiController::Controller7) + { + return false; // Event pointer not filled. Return false. + } + + // Fill the event. +#ifdef LV2_DEBUG + printf("LV2SynthIF::processEvent non-ladspa filling midi event chn:%d dataA:%d dataB:%d\n", chn, a, b); +#endif + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + return true; + } + + unsigned long k = ip->second; + int ctlnum; // = DSSI_NONE; + + // No midi controller for the ladspa port? Send to ladspa control. + //if(ctlnum == DSSI_NONE) + //{ + // Sanity check. + if(k > _inportsControl) + { + return false; + } + + // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. + ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); + //} + + + float val = midi2Lv2Value(k, ctlnum, b); + +#ifdef LV2_DEBUG + //fprintf(stderr, "LV2SynthIF::processEvent control port:%lu port:%lu dataA:%d Converting val from:%d to lv2:%f\n", i, k, a, b, val); + fprintf(stderr, "LV2SynthIF::processEvent port:%lu dataA:%d Converting val from:%d to lv2:%f\n", k, a, b, val); +#endif + + // Set the lv2 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 lv2 control change, return false - the event is not filled. + return false; + } + break; + + case ME_PITCHBEND: + a += 8192; + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, (a >> 7) & 0x7f); + break; + + case ME_AFTERTOUCH: + sendLv2MidiEvent(evBuf, frame, 2, (type | chn) & 0xff, a & 0x7f, 0); + break; + + case ME_POLYAFTER: + sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + break; + + default: + if(MusEGlobal::debugMsg) + { + fprintf(stderr, "LV2SynthIF::processEvent midi event unknown type:%d\n", e.type()); + } + + // Event not filled. + return false; + break; + } + + return true; + +} + + +bool LV2SynthIF::getData(MidiPort *, unsigned int pos, int ports, unsigned int nframes, float **buffer) +{ + const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); + // All ports must be connected to something! + const unsigned long nop = ((unsigned long) ports) > _outports ? _outports : ((unsigned long) ports); + const bool usefixedrate = (requiredFeatures() & Plugin::FixedBlockSize);; + const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; + const unsigned long min_per_mask = min_per - 1; // min_per must be power of 2 + + unsigned long sample = 0; + AudioTrack *atrack = track(); + const AutomationType at = atrack->automationType(); + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + CtrlListList *cll = atrack->controller(); + ciCtrlList icl_first; + const int plug_id = id(); + + LV2EvBuf *evBuf = (_inportsMidi > 0) ? _state->midiInPorts [0].buffer : NULL; + + //set freewheeling property if plugin supports it + if(_synth->_hasFreeWheelPort) + { + _controls [_synth->_freeWheelPortIndex].val = MusEGlobal::audio->freewheel() ? 1.0f : 0.0f; + } + + if(plug_id != -1 && ports != 0) // Don't bother if not 'running'. + { + icl_first = cll->lower_bound(genACnum(plug_id, 0)); + } + + bool used_in_chan_array[_inports]; // Don't bother initializing if not 'running'. + + // Don't bother if not 'running'. + if(ports != 0) + { + // Initialize the array. + for(size_t i = 0; i < _inports; ++i) + used_in_chan_array[i] = false; + + if(!atrack->noInRoute()) + { + RouteList *irl = atrack->inRoutes(); + for(ciRoute i = irl->begin(); i != irl->end(); ++i) + { + if(i->track->isMidiTrack()) + continue; + // Only this synth knows how many destination channels there are, + // while only the track knows how many source channels there are. + // So take care of the destination channels here, and let the track handle the source channels. + const int dst_ch = i->channel <= -1 ? 0 : i->channel; + if((unsigned long)dst_ch >= _inports) + continue; + const int dst_chs = i->channels <= -1 ? _inports : i->channels; + //const int total_ins = atrack->totalRoutableInputs(Route::TRACK_ROUTE); + const int src_ch = i->remoteChannel <= -1 ? 0 : i->remoteChannel; + const int src_chs = i->channels; + + int fin_dst_chs = dst_chs; + if((unsigned long)(dst_ch + fin_dst_chs) > _inports) + fin_dst_chs = _inports - dst_ch; + + static_cast(i->track)->copyData(pos, + dst_ch, dst_chs, fin_dst_chs, + src_ch, src_chs, + nframes, &_audioInBuffers[0], + false, used_in_chan_array); + const int nxt_ch = dst_ch + fin_dst_chs; + for(int ch = dst_ch; ch < nxt_ch; ++ch) + used_in_chan_array[ch] = true; + } + } + } + + + int cur_slice = 0; + + while(sample < nframes) + { + unsigned long nsamp = nframes - sample; + const unsigned long slice_frame = pos + 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(ports != 0) // Don't bother if not 'running'. + { + ciCtrlList icl = icl_first; + + for(unsigned long k = 0; k < _inportsControl; ++k) + { + //don't process freewheel port + if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == k) + continue; + + CtrlList *cl = (cll && plug_id != -1 && icl != cll->end()) ? icl->second : NULL; + CtrlInterpolate &ci = _controls[k].interp; + + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)))) + { + if(cl && plug_id != -1 && (unsigned long)cl->id() == genACnum(plug_id, k)) + { + cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); + + if(icl != cll->end()) + { + ++icl; + } + } + else + { + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = _controls[k].val; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; + } + } + else + { + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. + { + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + + if(cl && cll && icl != cll->end()) + { + ++icl; + } + } + + if(!usefixedrate && MusEGlobal::audio->isPlaying()) + { + unsigned long samps = nsamp; + + if(ci.eFrame != -1) + { + samps = (unsigned long)ci.eFrame - slice_frame; + } + + if(!ci.doInterp && samps > min_per) + { + samps &= ~min_per_mask; + + if((samps & min_per_mask) != 0) + { + samps += min_per; + } + } + else + { + samps = min_per; + } + + if(samps < nsamp) + { + nsamp = samps; + } + + } + + if(ci.doInterp && cl) + { + _controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); + } + else + { + _controls[k].val = ci.sVal; + } + _state->controlsMask [k] = true; + +#ifdef LV2_DEBUG_PROCESS + fprintf(stderr, "LV2SynthIF::getData k:%lu val:%f sample:%lu ci.eFrame:%d nsamp:%lu \n", k, _controls[k].val, sample, ci.eFrame, nsamp); +#endif + + } + } + + + bool found = false; + unsigned long frame = 0; + unsigned long index = 0; + + // Get all control ring buffer items valid for this time period... + while(!_controlFifo.isEmpty()) + { + unsigned long evframe; + const ControlEvent& v = _controlFifo.peek(); + // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. + // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. + evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; + +#ifdef DEBUG_LV2 + fprintf(stderr, "LV2SynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", + found, evframe, frame, v.frame, v.idx, v.value, v.unique); +#endif + + // Protection. Observed this condition. Why? Supposed to be linear timestamps. + if(found && evframe < frame) + { + fprintf(stderr, "LV2SynthIF::getData *** Error: evframe:%lu < frame:%lu event: frame:%lu idx:%lu val:%f unique:%d\n", + evframe, frame, v.frame, v.idx, v.value, v.unique); + + // No choice but to ignore it. + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + continue; + } + + 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)) // Fixed rate and must reply to all. + + { + break; + } + +// _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + + found = true; + frame = evframe; + index = v.idx; + + if(index >= _inportsControl) // Sanity check. + { + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + break; + } + + //don't process freewheel port + if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == index) + { + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + continue; + } + + if(ports == 0) // Don't bother if not 'running'. + { + _controls[index].val = v.value; // Might as well at least update these. + } + else + { + CtrlInterpolate *ci = &_controls[index].interp; + // Tell it to stop the current ramp at this frame, when it does stop, set this value: + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; + } + + // Need to update the automation value, otherwise it overwrites later with the last automation value. + if(plug_id != -1) + { + synti->setPluginCtrlVal(genACnum(plug_id, index), v.value); + } + if(v.fromGui) //don't send gui control changes back + { + _state->lastControls [index] = v.value; + _state->controlsMask [index] = false; + } + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + } + + 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: 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) + { + LV2Synth::lv2audio_preProcessMidiPorts(_state, nsamp); + + // Get the state of the stop flag. + const bool do_stop = synti->stopFlag(); + + MidiPlayEvent buf_ev; + + // Transfer the user lock-free buffer events to the user sorted multi-set. + // False = don't use the size snapshot, but update it. + const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); + for(unsigned int i = 0; i < usr_buf_sz; ++i) + { + if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) + synti->_outUserEvents.insert(buf_ev); + } + + // Transfer the playback lock-free buffer events to the playback sorted multi-set. + const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); + for(unsigned int i = 0; i < pb_buf_sz; ++i) + { + // Are we stopping? Just remove the item. + if(do_stop) + synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); + // Otherwise get the item. + else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) + synti->_outPlaybackEvents.insert(buf_ev); + } + + // Are we stopping? + if(do_stop) + { + // Transport has stopped, purge ALL further scheduled playback events now. + synti->_outPlaybackEvents.clear(); + // Reset the flag. + synti->setStopFlag(false); + } + + iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); + iMPEvent impe_us = synti->_outUserEvents.begin(); + bool using_pb; + + while(1) + { + if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) + using_pb = *impe_pb < *impe_us; + else if(impe_pb != synti->_outPlaybackEvents.end()) + using_pb = true; + else if(impe_us != synti->_outUserEvents.end()) + using_pb = false; + else break; + + const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us; + + #ifdef LV2_DEBUG + fprintf(stderr, "LV2SynthIF::getData eventFifos event time:%d\n", e.time()); + #endif + + if(e.time() >= (sample + nsamp + syncFrame)) + break; + + if(ports != 0) // Don't bother if not 'running'. + { + // Time-stamp the event. + unsigned int ft = (e.time() < syncFrame) ? 0 : e.time() - syncFrame; + ft = (ft < sample) ? 0 : ft - sample; + + if(ft >= nsamp) + { + fprintf(stderr, "LV2SynthIF::getData: eventFifos event time:%d out of range. pos:%d syncFrame:%u ft:%u sample:%lu nsamp:%lu\n", + e.time(), pos, syncFrame, ft, sample, nsamp); + ft = nsamp - 1; + } + if(processEvent(e, evBuf, ft)) + { + + } + } + + // Done with ring buffer's event. Remove it. + // C++11. + if(using_pb) + impe_pb = synti->_outPlaybackEvents.erase(impe_pb); + else + impe_us = synti->_outUserEvents.erase(impe_us); + } + + + if(ports != 0) // Don't bother if not 'running'. + { + + //connect ports + for(size_t j = 0; j < _inports; ++j) + { + if(used_in_chan_array [j]) + { + lilv_instance_connect_port(_handle, _audioInPorts [j].index, _audioInBuffers [j] + sample); + } + else + { + lilv_instance_connect_port(_handle, _audioInPorts [j].index, _audioInSilenceBuf + sample); + } + } + + for(size_t j = 0; j < nop; ++j) + { + lilv_instance_connect_port(_handle, _audioOutPorts [j].index, buffer [j] + sample); + } + + for(size_t j = nop; j < _outports; j++) + { + lilv_instance_connect_port(_handle, _audioOutPorts [j].index, _audioOutBuffers [j] + sample); + } + + for(size_t j = 0; j < _inportsControl; ++j) + { + uint32_t idx = _controlInPorts [j].index; + if(_state->pluginCVPorts [idx] != NULL) + { + float cvVal = _controls [j].val; + for(size_t jj = 0; jj < nsamp; ++jj) + { + _state->pluginCVPorts [idx] [jj + sample] = cvVal; + } + lilv_instance_connect_port(_handle, idx, _state->pluginCVPorts [idx] + sample); + } + } +#ifdef LV2_DEBUG_PROCESS + //dump atom sequence events to stderr + if(evBuf) + { + evBuf->dump(); + } +#endif + + lilv_instance_run(_handle, nsamp); + //notify worker that this run() finished + if(_state->wrkIface && _state->wrkIface->end_run) + _state->wrkIface->end_run(lilv_instance_get_handle(_handle)); + //notify worker about processed data (if any) + if(_state->wrkIface && _state->wrkIface->work_response && _state->wrkEndWork) + { + _state->wrkIface->work_response(lilv_instance_get_handle(_handle), _state->wrkDataSize, _state->wrkDataBuffer); + _state->wrkDataSize = 0; + _state->wrkDataBuffer = NULL; + _state->wrkEndWork = false; + } + + LV2Synth::lv2audio_postProcessMidiPorts(_state, nsamp); + + + } + + sample += nsamp; + } + + ++cur_slice; // Slice is done. Moving on to any next slice now... + } + + return true; +} + +void LV2SynthIF::getNativeGeometry(int *x, int *y, int *w, int *h) const +{ + if(_state->pluginWindow != NULL && !_state->hasExternalGui) + { + QRect g = _state->pluginWindow->geometry(); + if(x) *x = g.x(); + if(y) *y = g.y(); + if(w) *w = g.width(); + if(h) *h = g.height(); + return; + } + + // Fall back to blank geometry. + SynthIF::getNativeGeometry(x, y, w, h); +} + +double LV2SynthIF::getParameter(long unsigned int n) const +{ + if(n >= _inportsControl) + { + std::cout << "LV2SynthIF::getParameter param number " << n << " out of range of ports: " << _inportsControl << std::endl; + return 0.0; + } + + if(!_controls) + { + return 0.0; + } + + return _controls[n].val; +} + +double LV2SynthIF::getParameterOut(long unsigned int n) const +{ + if(n >= _outportsControl) + { + std::cout << "LV2SynthIF::getParameterOut param number " << n << " out of range of ports: " << _outportsControl << std::endl; + return 0.0; + } + + if(!_controlsOut) + { + return 0.0; + } + + return _controlsOut[n].val; + +} + + +QString LV2SynthIF::getPatchName(int /* ch */, int prog, bool) const +{ + uint32_t program = prog & 0xff; + uint32_t lbank = (prog >> 8) & 0xff; + uint32_t hbank = (prog >> 16) & 0xff; + + if (program > 127) // Map "dont care" to 0 + program = 0; + if (lbank > 127) + lbank = 0; + if (hbank > 127) + hbank = 0; + const uint32_t patch = (hbank << 16) | (lbank << 8) | program; + + std::map::iterator itPrg = _state->prg2index.find(patch); + if(itPrg == _state->prg2index.end()) + return QString("?"); + uint32_t index = itPrg->second; + std::map::iterator itIndex = _state->index2prg.find(index); + if(itIndex == _state->index2prg.end()) + return QString("?"); + return QString(itIndex->second.name); + +} + +void LV2SynthIF::guiHeartBeat() +{ + //check for pending song dirty status + if(_state->songDirtyPending){ + MusEGlobal::song->setDirty(); + _state->songDirtyPending = false; + } + +} + +bool LV2SynthIF::hasGui() const +{ + return true; +} + +bool LV2SynthIF::hasNativeGui() const +{ + return (_synth->_pluginUiTypes.size() > 0); +} + +bool LV2SynthIF::nativeGuiVisible() const +{ + if(_state != NULL) + { + if(_state->hasExternalGui) + { + return (_state->widget != NULL); + } + else if(_state->hasGui && _state->widget != NULL) + { + return ((QWidget *)_state->widget)->isVisible(); + } + } + + return false; +} + +void LV2SynthIF::populatePatchPopup(MusEGui::PopupMenu *menu, int, bool) +{ + LV2Synth::lv2prg_updatePrograms(_state); + menu->clear(); + MusEGui::PopupMenu *subMenuPrograms = new MusEGui::PopupMenu(menu->parent()); + subMenuPrograms->setTitle(QObject::tr("Midi programs")); + subMenuPrograms->setIcon(QIcon(*MusEGui::pianoNewIcon)); + menu->addMenu(subMenuPrograms); + MusEGui::PopupMenu *subMenuPresets = new MusEGui::PopupMenu(menu->parent()); + subMenuPresets->setTitle(QObject::tr("Presets")); + menu->addMenu(subMenuPresets); + + //First: fill programs submenu + std::map submenus; + std::map::iterator itIndex; + for(itIndex = _state->index2prg.begin(); itIndex != _state->index2prg.end(); ++itIndex) + { + const lv2ExtProgram &extPrg = itIndex->second; + uint32_t hb = extPrg.bank >> 8; + uint32_t lb = extPrg.bank & 0xff; + // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. + if(hb > 127 || lb > 127 || extPrg.prog > 127) + continue; + hb &= 0x7f; + lb &= 0x7f; + const uint32_t patch_bank = (hb << 8) | lb; + const uint32_t patch = (patch_bank << 8) | extPrg.prog; + + std::map::iterator itS = submenus.find(patch_bank); + MusEGui::PopupMenu *submenu= NULL; + if(itS != submenus.end()) + { + submenu = itS->second; + } + else + { + submenu = new MusEGui::PopupMenu(menu->parent()); + submenu->setTitle(QString("Bank #") + QString::number(extPrg.bank + 1)); + subMenuPrograms->addMenu(submenu); + submenus.insert(std::make_pair(patch_bank, submenu)); + } + + QAction *act = submenu->addAction(extPrg.name); + act->setData(patch); + } + + //Second:: Fill presets submenu + LV2Synth::lv2state_populatePresetsMenu(_state, subMenuPresets); +} + +void LV2SynthIF::preProcessAlways() +{ + +} + +MidiPlayEvent LV2SynthIF::receiveEvent() +{ + return MidiPlayEvent(); + +} + +void LV2SynthIF::setNativeGeometry(int x, int y, int w, int h) +{ + // Store the native geometry. + SynthIF::setNativeGeometry(x, y, w, h); + + if(_state->pluginWindow && !_state->hasExternalGui) + { + //_state->pluginWindow->move(x, y); + //don't resize lv2 uis - this is handles at plugin level + //_uiState->pluginWindow->resize(w, h); + +#ifdef QT_SHOW_POS_BUG_WORKAROUND + // Because of the bug, no matter what we must supply a position, + // even upon first showing... + + // Check sane size. + if(w == 0) + w = _state->pluginWindow->sizeHint().width(); + if(h == 0) + h = _state->pluginWindow->sizeHint().height(); + + // No size hint? Try minimum size. + if(w == 0) + w = _state->pluginWindow->minimumSize().width(); + if(h == 0) + h = _state->pluginWindow->minimumSize().height(); + + // Fallback. + if(w == 0) + w = 400; + if(h == 0) + h = 300; + + _state->pluginWindow->setGeometry(x, y, w, h); + +#else + + // If the saved geometry is valid, use it. + // Otherwise this is probably the first time showing, + // so do not set a geometry - let Qt pick one + // (using auto-placement and sizeHint). + if(!(x == 0 && y == 0 && w == 0 && h == 0)) + { + // Check sane size. + if(w == 0) + w = _state->pluginWindow->sizeHint().width(); + if(h == 0) + h = _state->pluginWindow->sizeHint().height(); + + // No size hint? Try minimum size. + if(w == 0) + w = _state->pluginWindow->minimumSize().width(); + if(h == 0) + h = _state->pluginWindow->minimumSize().height(); + + // Fallback. + if(w == 0) + w = 400; + if(h == 0) + h = 300; + + _state->pluginWindow->setGeometry(x, y, w, h); + } +#endif + } +} + +void LV2SynthIF::setParameter(long unsigned int idx, double value) +{ + addScheduledControlEvent(idx, value, MusEGlobal::audio->curFrame()); +} + +void LV2SynthIF::showNativeGui(bool bShow) +{ + if(track() != NULL) + { + if(_state->human_id != NULL) + { + free(_state->human_id); + } + + _state->extHost.plugin_human_id = _state->human_id = strdup((track()->name() + QString(": ") + name()).toUtf8().constData()); + } + + LV2Synth::lv2ui_ShowNativeGui(_state, bShow); +} + +void LV2SynthIF::write(int level, Xml &xml) const +{ + LV2Synth::lv2conf_write(_state, level, xml); +} + +void LV2SynthIF::setCustomData(const std::vector< QString > &customParams) +{ + LV2Synth::lv2conf_set(_state, customParams); +} + + +double LV2SynthIF::param(long unsigned int i) const +{ + return getParameter(i); +} + +long unsigned int LV2SynthIF::parameters() const +{ + return _inportsControl; +} + +long unsigned int LV2SynthIF::parametersOut() const +{ + return _outportsControl; +} + +const char *LV2SynthIF::paramName(long unsigned int i) +{ + return _controlInPorts [i].cName; +} + +const char *LV2SynthIF::paramOutName(long unsigned int i) +{ + return _controlOutPorts [i].cName; +} + +CtrlValueType LV2SynthIF::ctrlValueType(unsigned long i) const +{ + CtrlValueType vt = VAL_LINEAR; + std::map::iterator it = _synth->_idxToControlMap.find(i); + assert(it != _synth->_idxToControlMap.end()); + i = it->second; + assert(i < _inportsControl); + + switch(_synth->_controlInPorts [i].cType) + { + case LV2_PORT_CONTINUOUS: + vt = VAL_LINEAR; + break; + case LV2_PORT_DISCRETE: + case LV2_PORT_INTEGER: + vt = VAL_INT; + break; + case LV2_PORT_LOGARITHMIC: + vt = VAL_LOG; + break; + case LV2_PORT_TRIGGER: + vt = VAL_BOOL; + break; + default: + break; + } + + return vt; + +} + +CtrlList::Mode LV2SynthIF::ctrlMode(unsigned long i) const +{ + std::map::iterator it = _synth->_idxToControlMap.find(i); + assert(it != _synth->_idxToControlMap.end()); + i = it->second; + assert(i < _inportsControl); + + return ((_synth->_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) + ||(_synth->_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC)) ? CtrlList::INTERPOLATE : CtrlList::DISCRETE; +} + +LADSPA_PortRangeHint LV2SynthIF::range(unsigned long i) +{ + assert(i < _inportsControl); + LADSPA_PortRangeHint hint; + hint.HintDescriptor = 0; + hint.LowerBound = _controlInPorts [i].minVal; + hint.UpperBound = _controlInPorts [i].maxVal; + + if(hint.LowerBound == hint.LowerBound) + { + hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; + } + + if(hint.UpperBound == hint.UpperBound) + { + hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; + } + + return hint; +} + +LADSPA_PortRangeHint LV2SynthIF::rangeOut(unsigned long i) +{ + assert(i < _outportsControl); + LADSPA_PortRangeHint hint; + hint.HintDescriptor = 0; + hint.LowerBound = _controlOutPorts [i].minVal; + hint.UpperBound = _controlOutPorts [i].maxVal; + + if(hint.LowerBound == hint.LowerBound) + { + hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; + } + + if(hint.UpperBound == hint.UpperBound) + { + hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; + } + + return hint; + +} + + + +double LV2SynthIF::paramOut(long unsigned int i) const +{ + return getParameterOut(i); +} + +void LV2SynthIF::setParam(long unsigned int i, double val) +{ + setParameter(i, val); +} + +void LV2SynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; } +bool LV2SynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl; } +void LV2SynthIF::enableAllControllers(bool v) +{ + if(!_synth) + return; + for(unsigned long i = 0; i < _inportsControl; ++i) + _controls[i].enCtrl = v; +} +void LV2SynthIF::updateControllers() { } + +void LV2SynthIF::populatePresetsMenu(MusEGui::PopupMenu *menu) +{ + LV2Synth::lv2state_populatePresetsMenu(_state, menu); +} + +void LV2SynthIF::applyPreset(void *preset) +{ + LV2Synth::lv2state_applyPreset(_state, static_cast(preset)); +} + + + +void LV2SynthIF::writeConfiguration(int level, Xml &xml) +{ + MusECore::SynthIF::writeConfiguration(level, xml); +} + +bool LV2SynthIF::readConfiguration(Xml &xml, bool readPreset) +{ + return MusECore::SynthIF::readConfiguration(xml, readPreset); +} + +void LV2PluginWrapper_Window::hideEvent(QHideEvent *e) +{ + if(_state->plugInst != NULL) + _state->plugInst->saveNativeGeometry(geometry().x(), geometry().y(), geometry().width(), geometry().height()); + else if(_state->sif != NULL) + _state->sif->saveNativeGeometry(geometry().x(), geometry().y(), geometry().width(), geometry().height()); + + e->ignore(); + QMainWindow::hideEvent(e); +} + +void LV2PluginWrapper_Window::showEvent(QShowEvent *e) +{ + int x = 0, y = 0, w = 0, h = 0; + if(_state->plugInst != NULL) + _state->plugInst->savedNativeGeometry(&x, &y, &w, &h); + else if(_state->sif != NULL) + _state->sif->savedNativeGeometry(&x, &y, &w, &h); + +#ifdef QT_SHOW_POS_BUG_WORKAROUND + // Because of the bug, no matter what we must supply a position, + // even upon first showing... + + // Check sane size. + if(w == 0) + w = sizeHint().width(); + if(h == 0) + h = sizeHint().height(); + + // No size hint? Try minimum size. + if(w == 0) + w = minimumSize().width(); + if(h == 0) + h = minimumSize().height(); + + // Fallback. + if(w == 0) + w = 400; + if(h == 0) + h = 300; + + setGeometry(x, y, w, h); + +#else + + // If the saved geometry is valid, use it. + // Otherwise this is probably the first time showing, + // so do not set a geometry - let Qt pick one + // (using auto-placement and sizeHint). + if(!(x == 0 && y == 0 && w == 0 && h == 0)) + { + // Check sane size. + if(w == 0) + w = sizeHint().width(); + if(h == 0) + h = sizeHint().height(); + + // No size hint? Try minimum size. + if(w == 0) + w = minimumSize().width(); + if(h == 0) + h = minimumSize().height(); + + // Fallback. + if(w == 0) + w = 400; + if(h == 0) + h = 300; + + setGeometry(x, y, w, h); + } +#endif + + // Convenience: If the window was minimized, restore it. + if(isMinimized()) + setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + + e->ignore(); + QMainWindow::showEvent(e); +} + +void LV2PluginWrapper_Window::closeEvent(QCloseEvent *event) +{ + assert(_state != NULL); + event->accept(); + + stopUpdateTimer(); + + //if(_state->gtk2Plug != NULL) + //{ + if(_state->pluginQWindow != NULL) + { + _state->pluginQWindow->setParent(NULL); + delete _state->pluginQWindow; + _state->pluginQWindow = NULL; + } + //} + + if(_state->deleteLater) + { + LV2Synth::lv2state_FreeState(_state); + + } + else + { + //_state->uiTimer->stopNextTime(false); + _state->widget = NULL; + _state->pluginWindow = NULL; + _state->uiDoSelectPrg = false; + _state->uiPrgIface = NULL; + + LV2Synth::lv2ui_FreeDescriptors(_state); + } + + // Reset the flag, just to be sure. + _state->uiIsOpening = false; + + // The widget is automatically deleted by use of the + // WA_DeleteOnClose attribute in the constructor. +} + +void LV2PluginWrapper_Window::stopUpdateTimer() +{ + if(updateTimer.isActive()) + updateTimer.stop(); + while(updateTimer.isActive()) + { + QCoreApplication::processEvents(); + } +} + + +#ifdef LV2_GUI_USE_QWIDGET +LV2PluginWrapper_Window::LV2PluginWrapper_Window(LV2PluginWrapper_State *state, + QWidget *parent, + Qt::WindowFlags flags) + : QWidget(parent, flags), _state ( state ), _closing(false) +#else +LV2PluginWrapper_Window::LV2PluginWrapper_Window(LV2PluginWrapper_State *state, + QWidget *parent, + Qt::WindowFlags flags) + : QMainWindow(parent, flags), _state ( state ), _closing(false) +#endif +{ + // Automatically delete the wiget when it closes. + setAttribute(Qt::WA_DeleteOnClose, true); + connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateGui())); + connect(this, SIGNAL(makeStopFromGuiThread()), this, SLOT(stopFromGuiThread())); + connect(this, SIGNAL(makeStartFromGuiThread()), this, SLOT(startFromGuiThread())); +} + +LV2PluginWrapper_Window::~LV2PluginWrapper_Window() +{ +#ifdef DEBUG_LV2 + std::cout << "LV2PluginWrapper_Window::~LV2PluginWrapper_Window()" << std::endl; +#endif +} + +void LV2PluginWrapper_Window::startNextTime() +{ + emit makeStartFromGuiThread(); +} + + + + +void LV2PluginWrapper_Window::stopNextTime() +{ + setClosing(true); + emit makeStopFromGuiThread(); +} + +void LV2PluginWrapper_Window::updateGui() +{ + if(_state->deleteLater || _closing) + { + stopNextTime(); + return; + } + LV2Synth::lv2ui_SendChangedControls(_state); + + //send program change if any + // Force send if re-opening. + if(_state->uiIsOpening || _state->uiDoSelectPrg) + { + _state->uiDoSelectPrg = false; + if(_state->uiPrgIface != NULL && (_state->uiPrgIface->select_program != NULL || _state->uiPrgIface->select_program_for_channel != NULL)) + { + if(_state->newPrgIface) + _state->uiPrgIface->select_program_for_channel(lilv_instance_get_handle(_state->handle), _state->uiChannel, (uint32_t)_state->uiBank, (uint32_t)_state->uiProg); + else + _state->uiPrgIface->select_program(lilv_instance_get_handle(_state->handle), (uint32_t)_state->uiBank, (uint32_t)_state->uiProg); + } + } + + // Reset the flag. + _state->uiIsOpening = false; + + //call ui idle callback if any + if(_state->uiIdleIface != NULL) + { + int iRet = _state->uiIdleIface->idle(_state->uiInst); + if(iRet != 0) // ui don't want us to call it's idle callback any more + _state->uiIdleIface = NULL; + } + + if(_state->hasExternalGui) + { + LV2_EXTERNAL_UI_RUN((LV2_External_UI_Widget *)_state->widget); + } + + //if(_closing) + //stopNextTime(); +} + +void LV2PluginWrapper_Window::stopFromGuiThread() +{ + stopUpdateTimer(); + emit close(); +} + +void LV2PluginWrapper_Window::startFromGuiThread() +{ + stopUpdateTimer(); + updateTimer.start(1000/30); +} + + +LV2PluginWrapper::LV2PluginWrapper(LV2Synth *s, PluginFeatures reqFeatures) +{ + _synth = s; + + _requiredFeatures = reqFeatures; + + _fakeLd.Label = strdup(_synth->name().toUtf8().constData()); + _fakeLd.Name = strdup(_synth->name().toUtf8().constData()); + _fakeLd.UniqueID = _synth->_uniqueID; + _fakeLd.Maker = strdup(_synth->maker().toUtf8().constData()); + _fakeLd.Copyright = strdup(_synth->version().toUtf8().constData()); + _isLV2Plugin = true; + _isLV2Synth = s->_isSynth; + int numPorts = _synth->_audioInPorts.size() + + _synth->_audioOutPorts.size() + + _synth->_controlInPorts.size() + + _synth->_controlOutPorts.size() + + _synth->_midiInPorts.size() + + _synth->_midiOutPorts.size(); + _fakeLd.PortCount = numPorts; + _fakePds = new LADSPA_PortDescriptor [numPorts]; + memset(_fakePds, 0, sizeof(int) * numPorts); + + for(size_t i = 0; i < _synth->_audioInPorts.size(); i++) + { + _fakePds [_synth->_audioInPorts [i].index] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + } + + for(size_t i = 0; i < _synth->_audioOutPorts.size(); i++) + { + _fakePds [_synth->_audioOutPorts [i].index] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + } + + for(size_t i = 0; i < _synth->_controlInPorts.size(); i++) + { + _fakePds [_synth->_controlInPorts [i].index] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + } + + for(size_t i = 0; i < _synth->_controlOutPorts.size(); i++) + { + _fakePds [_synth->_controlOutPorts [i].index] = LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL; + } + + _fakeLd.PortNames = NULL; + _fakeLd.PortRangeHints = NULL; + _fakeLd.PortDescriptors = _fakePds; + _fakeLd.Properties = 0; + plugin = &_fakeLd; + _isDssi = false; + _isDssiSynth = false; + _isVstNativePlugin = false; + _isVstNativeSynth = false; + +#ifdef DSSI_SUPPORT + dssi_descr = NULL; +#endif + + fi = _synth->info; + ladspa = NULL; + _handle = 0; + _references = 0; + _instNo = 0; + _label = _synth->name(); + _name = _synth->description(); + _uniqueID = plugin->UniqueID; + _maker = _synth->maker(); + _copyright = _synth->version(); + + _portCount = plugin->PortCount; + + _inports = 0; + _outports = 0; + _controlInPorts = 0; + _controlOutPorts = 0; + + for(unsigned long k = 0; k < _portCount; ++k) + { + LADSPA_PortDescriptor pd = plugin->PortDescriptors[k]; + + if(pd & LADSPA_PORT_AUDIO) + { + if(pd & LADSPA_PORT_INPUT) + { + ++_inports; + } + else if(pd & LADSPA_PORT_OUTPUT) + { + ++_outports; + } + } + else if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + { + ++_controlInPorts; + } + else if(pd & LADSPA_PORT_OUTPUT) + { + ++_controlOutPorts; + } + } + } +} + +LV2PluginWrapper::~LV2PluginWrapper() +{ + free((void*)_fakeLd.Label); + free((void*)_fakeLd.Name); + free((void*)_fakeLd.Maker); + free((void*)_fakeLd.Copyright); + delete [] _fakePds; +} + +LADSPA_Handle LV2PluginWrapper::instantiate(PluginI *plugi) +{ + LV2PluginWrapper_State *state = new LV2PluginWrapper_State; + state->inst = this; + state->widget = NULL; + state->uiInst = NULL; + state->plugInst = plugi; + state->_ifeatures = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; + state->_ppifeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; + state->sif = NULL; + state->synth = _synth; + LV2Synth::lv2state_FillFeatures(state); + + state->handle = lilv_plugin_instantiate(_synth->_handle, (double)MusEGlobal::sampleRate, state->_ppifeatures); + + if(state->handle == NULL) + { + delete [] state->_ppifeatures; + delete [] state->_ifeatures; + return NULL; + } + + //_states.insert(std::pair(state->handle, state)); + + LV2Synth::lv2state_PostInstantiate(state); + + return (LADSPA_Handle)state; + +} + +void LV2PluginWrapper::connectPort(LADSPA_Handle handle, long unsigned int port, float *value) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + lilv_instance_connect_port((LilvInstance *)state->handle, port, (void *)value); +} + +int LV2PluginWrapper::incReferences(int ref) +{ + _synth->incInstances(ref); + return _synth->instances(); +} +void LV2PluginWrapper::activate(LADSPA_Handle handle) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + lilv_instance_activate((LilvInstance *) state->handle); +} +void LV2PluginWrapper::deactivate(LADSPA_Handle handle) +{ + if (handle) + { + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + lilv_instance_deactivate((LilvInstance *) state->handle); + } +} +void LV2PluginWrapper::cleanup(LADSPA_Handle handle) +{ + if(handle != NULL) + { + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + + state->deleteLater = true; + if(state->pluginWindow != NULL) + state->pluginWindow->stopNextTime(); + else + LV2Synth::lv2state_FreeState(state); + + } +} + +void LV2PluginWrapper::apply(LADSPA_Handle handle, unsigned long n) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + + LV2Synth::lv2audio_preProcessMidiPorts(state, n); + + //set freewheeling property if plugin supports it + if(state->synth->_hasFreeWheelPort) + { + state->plugInst->controls[_synth->_freeWheelPortIndex].val = MusEGlobal::audio->freewheel() ? 1.0f : 0.0f; + } + + for(size_t j = 0; j < state->plugInst->controlPorts; ++j) + { + uint32_t idx = state->synth->_controlInPorts [j].index; + if(state->pluginCVPorts [idx] != NULL) + { + float cvVal = state->plugInst->controls [j].val; + for(size_t jj = 0; jj < n; ++jj) + { + state->pluginCVPorts [idx] [jj] = cvVal; + } + lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); + } + } + + for(size_t j = 0; j < state->plugInst->controlOutPorts; ++j) + { + uint32_t idx = state->synth->_controlOutPorts [j].index; + if(state->pluginCVPorts [idx] != NULL) + { + float cvVal = state->plugInst->controlsOut [j].val; + for(size_t jj = 0; jj < n; ++jj) + { + state->pluginCVPorts [idx] [jj] = cvVal; + } + lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); + } + } + + + lilv_instance_run(state->handle, n); + //notify worker that this run() finished + if(state->wrkIface && state->wrkIface->end_run) + state->wrkIface->end_run(lilv_instance_get_handle(state->handle)); + //notify worker about processes data (if any) + if(state->wrkIface && state->wrkIface->work_response && state->wrkEndWork) + { + state->wrkEndWork = false; + state->wrkIface->work_response(lilv_instance_get_handle(state->handle), state->wrkDataSize, state->wrkDataBuffer); + state->wrkDataSize = 0; + state->wrkDataBuffer = NULL; + } + + LV2Synth::lv2audio_postProcessMidiPorts(state, n); +} +LADSPA_PortDescriptor LV2PluginWrapper::portd(unsigned long k) const +{ + return _fakeLd.PortDescriptors[k]; +} + +LADSPA_PortRangeHint LV2PluginWrapper::range(unsigned long i) +{ + LADSPA_PortRangeHint hint; + hint.HintDescriptor = 0; + hint.LowerBound = _synth->_pluginControlsMin [i]; + hint.UpperBound = _synth->_pluginControlsMax [i]; + + if(hint.LowerBound == hint.LowerBound) + { + hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; + } + + if(hint.UpperBound == hint.UpperBound) + { + hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; + } + + return hint; +} +void LV2PluginWrapper::range(unsigned long i, float *min, float *max) const +{ + *min = _synth->_pluginControlsMin [i]; + *max = _synth->_pluginControlsMax [i]; +} + +double LV2PluginWrapper::defaultValue(unsigned long port) const +{ + return _synth->_pluginControlsDefault [port]; +} +const char *LV2PluginWrapper::portName(unsigned long i) +{ + return lilv_node_as_string(lilv_port_get_name(_synth->_handle, lilv_plugin_get_port_by_index(_synth->_handle, i))); +} + +CtrlValueType LV2PluginWrapper::ctrlValueType(unsigned long i) const +{ + CtrlValueType vt = VAL_LINEAR; + std::map::iterator it = _synth->_idxToControlMap.find(i); + assert(it != _synth->_idxToControlMap.end()); + i = it->second; + assert(i < _controlInPorts); + + switch(_synth->_controlInPorts [i].cType) + { + case LV2_PORT_CONTINUOUS: + vt = VAL_LINEAR; + break; + case LV2_PORT_DISCRETE: + case LV2_PORT_INTEGER: + vt = VAL_INT; + break; + case LV2_PORT_LOGARITHMIC: + vt = VAL_LOG; + break; + case LV2_PORT_TRIGGER: + vt = VAL_BOOL; + break; + default: + break; + } + + return vt; +} +CtrlList::Mode LV2PluginWrapper::ctrlMode(unsigned long i) const +{ + std::map::iterator it = _synth->_idxToControlMap.find(i); + assert(it != _synth->_idxToControlMap.end()); + i = it->second; + assert(i < _controlInPorts); + + return ((_synth->_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) + ||(_synth->_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC)) ? CtrlList::INTERPOLATE : CtrlList::DISCRETE; +} +bool LV2PluginWrapper::hasNativeGui() const +{ + return (_synth->_pluginUiTypes.size() > 0); +} + +void LV2PluginWrapper::showNativeGui(PluginI *p, bool bShow) +{ + assert(p->instances > 0); + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; + + if(p->track() != NULL) + { + if(state->human_id != NULL) + { + free(state->human_id); + } + + state->extHost.plugin_human_id = state->human_id = strdup((p->track()->name() + QString(": ") + label()).toUtf8().constData()); + } + + LV2Synth::lv2ui_ShowNativeGui(state, bShow); +} + +bool LV2PluginWrapper::nativeGuiVisible(const PluginI *p) const +{ + assert(p->instances > 0); + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; + return (state->widget != NULL); +} + +void LV2PluginWrapper::setLastStateControls(LADSPA_Handle handle, size_t index, bool bSetMask, bool bSetVal, bool bMask, float fVal) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + assert(state != NULL); + + if(_controlInPorts == 0) + return; + + if(bSetMask) + state->controlsMask [index] = bMask; + + if(bSetVal) + state->lastControls [index] = fVal; + +} + +void LV2PluginWrapper::writeConfiguration(LADSPA_Handle handle, int level, Xml &xml) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + assert(state != NULL); + + LV2Synth::lv2conf_write(state, level, xml); +} + +void LV2PluginWrapper::setCustomData(LADSPA_Handle handle, const std::vector &customParams) +{ + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; + assert(state != NULL); + + LV2Synth::lv2conf_set(state, customParams); +} + +void LV2PluginWrapper::populatePresetsMenu(PluginI *p, MusEGui::PopupMenu *menu) +{ + assert(p->instances > 0); + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; + assert(state != NULL); + + LV2Synth::lv2state_populatePresetsMenu(state, menu); + +} + +void LV2PluginWrapper::applyPreset(PluginI *p, void *preset) +{ + assert(p->instances > 0); + LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; + assert(state != NULL); + + LV2Synth::lv2state_applyPreset(state, static_cast(preset)); + +} + +void LV2PluginWrapper_Worker::run() +{ + while(true) + { + _mSem.acquire(1); + if(_closing) + break; + makeWork(); + + } + +} + +LV2_Worker_Status LV2PluginWrapper_Worker::scheduleWork() +{ + if(_mSem.available() != 0) + return LV2_WORKER_ERR_NO_SPACE; + _mSem.release(1); + return LV2_WORKER_SUCCESS; + +} + +void LV2PluginWrapper_Worker::makeWork() +{ +#ifdef DEBUG_LV2 + std::cerr << "LV2PluginWrapper_Worker::makeWork" << std::endl; +#endif + if(_state->wrkIface && _state->wrkIface->work) + { + const void *dataBuffer = _state->wrkDataBuffer; + uint32_t dataSize = _state->wrkDataSize; + _state->wrkDataBuffer = NULL; + _state->wrkDataSize = 0; + if(_state->wrkIface->work(lilv_instance_get_handle(_state->handle), + LV2Synth::lv2wrk_respond, + _state, + dataSize, + dataBuffer) != LV2_WORKER_SUCCESS) + { + _state->wrkEndWork = false; + _state->wrkDataBuffer = NULL; + _state->wrkDataSize = 0; + } + } + +} + +LV2EvBuf::LV2EvBuf(bool isInput, bool oldApi, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk) + :_isInput(isInput), _oldApi(oldApi), _uAtomTypeSequence(atomTypeSequence), _uAtomTypeChunk(atomTypeChunk) +{ + if(_isInput) + { + // Resize and fill with initial value. + _buffer.resize(LV2_EVBUF_SIZE, 0); + } + else + { + // Reserve the space. + _buffer.reserve(LV2_EVBUF_SIZE); + // Add one item, the first item. + _buffer.assign(sizeof(LV2_Atom_Sequence), 0); + } + +#ifdef LV2_DEBUG + std::cerr << "LV2EvBuf ctor: _buffer size:" << _buffer.size() << " capacity:" << _buffer.capacity() << std::endl; +#endif + + resetBuffer(); +} + +size_t LV2EvBuf::mkPadSize(size_t size) +{ + return (size + 7U) & (~7U); +} + +void LV2EvBuf::resetPointers(bool r, bool w) +{ + if(!r && !w) + return; + size_t ptr = 0; + if(_oldApi) + { + ptr = sizeof(LV2_Event_Buffer); + } + else + { + ptr = sizeof(LV2_Atom_Sequence); + } + + if(r) + { + curRPointer = ptr; + } + if(w) + { + curWPointer = ptr; + } +} + +void LV2EvBuf::resetBuffer() +{ + if(_oldApi) + { + _evbuf = reinterpret_cast(&_buffer [0]); + _evbuf->capacity = _buffer.size() - sizeof(LV2_Event_Buffer); + _evbuf->data = reinterpret_cast(_evbuf + 1); + _evbuf->event_count = 0; + _evbuf->header_size = sizeof(LV2_Event_Buffer); + _evbuf->stamp_type = LV2_EVENT_AUDIO_STAMP; + _evbuf->size = 0; + } + else + { + _seqbuf = reinterpret_cast(&_buffer [0]); + if(!_isInput) + { + _seqbuf->atom.type = _uAtomTypeChunk; + _seqbuf->atom.size = _buffer.size() - sizeof(LV2_Atom_Sequence); + } + else + { + _seqbuf->atom.type = _uAtomTypeSequence; + _seqbuf->atom.size = sizeof(LV2_Atom_Sequence_Body); + } + _seqbuf->body.unit = 0; + _seqbuf->body.pad = 0; + } + resetPointers(true, true); +} + +bool LV2EvBuf::write(uint32_t frames, uint32_t subframes, uint32_t type, uint32_t size, const uint8_t *data) +{ + if(!_isInput) + return false; + if(_oldApi) + { + size_t paddedSize = mkPadSize(sizeof(LV2_Event) + size); + size_t resSize = curWPointer + paddedSize; + if(resSize > _buffer.size()) + { + std::cerr << "LV2 Event buffer overflow! frames=" << frames << ", size=" << size << std::endl; + return false; + } + LV2_Event *ev = reinterpret_cast(&_buffer [curWPointer]); + ev->frames = frames; + ev->subframes = subframes; + ev->type = type; + ev->size = size; + memcpy((void *)(ev + 1), data, size); + curWPointer += paddedSize; + _evbuf->size += paddedSize; + _evbuf->event_count++; + } + else + { + size_t paddedSize = mkPadSize(sizeof(LV2_Atom_Event) + size); + size_t resSize = curWPointer + paddedSize; + if(resSize > _buffer.size()) + { + std::cerr << "LV2 Atom_Event buffer overflow! frames=" << frames << ", size=" << size << std::endl; + return false; + } + + LV2_Atom_Event *ev = reinterpret_cast(&_buffer [curWPointer]); + ev->time.frames = frames; + ev->body.size = size; + ev->body.type = type; + memcpy(LV2_ATOM_BODY(&ev->body), data, size); + _seqbuf->atom.size += paddedSize; + curWPointer += paddedSize; + } + return true; +} + +bool LV2EvBuf::read(uint32_t *frames, uint32_t *subframes, uint32_t *type, uint32_t *size, uint8_t **data) +{ + *frames = *subframes = *type = *size = 0; + *data = NULL; + if(_isInput) + return false; + if(_oldApi) + { + LV2_Event *ev = reinterpret_cast(&_buffer [curRPointer]); + if((_evbuf->size + sizeof(LV2_Event_Buffer) - curRPointer) < sizeof(LV2_Event)) + { + return false; + } + *frames = ev->frames; + *subframes = ev->subframes; + *type = ev->type; + *size = ev->size; + *data = reinterpret_cast(ev + 1); + size_t padSize = mkPadSize(sizeof(LV2_Event) + ev->size); + curRPointer += padSize; + } + else + { + LV2_Atom_Event *ev = reinterpret_cast(&_buffer [curRPointer]); + if((_seqbuf->atom.size + sizeof(LV2_Atom_Sequence) - curRPointer) < sizeof(LV2_Atom_Event)) + { + return false; + } + *frames = ev->time.frames; + *type = ev->body.type; + *size = ev->body.size; + *data = reinterpret_cast(LV2_ATOM_BODY(&ev->body)); + size_t padSize = mkPadSize(sizeof(LV2_Atom_Event) + ev->body.size); + curRPointer += padSize; + + } + return true; +} + +uint8_t *LV2EvBuf::getRawBuffer() +{ + return &_buffer [0]; +} + +void LV2EvBuf::dump() +{ + if(_oldApi){ + return; + } + + int n = 1; + LV2_Atom_Sequence *b = (LV2_Atom_Sequence *)&_buffer [0]; + LV2_ATOM_SEQUENCE_FOREACH(b, s) + { + if(n == 1) + { + fprintf(stderr, "-------------- Atom seq dump START---------------\n"); + } + fprintf(stderr, "\tSeq. no.: %d\n", n); + fprintf(stderr, "\t\tFrames: %ld\n", (long)s->time.frames); + fprintf(stderr, "\t\tSize: %d\n", s->body.size); + fprintf(stderr, "\t\tType: %d\n", s->body.type); + fprintf(stderr, "\t\tData (hex):\n"); + uint8_t *d = (uint8_t *)LV2_ATOM_BODY(&s->body); + for(uint32_t i = 0; i < s->body.size; i++) + { + if((i % 10) == 0) + { + fprintf(stderr, "\n\t\t"); + } + else + { + fprintf(stderr, " "); + } + fprintf(stderr, "0x%02X", d [i]); + } + fprintf(stderr, "\n"); + + n++; + } + + if(n > 1) + { + fprintf(stderr, "-------------- Atom seq dump END---------------\n\n"); + } +} + +LV2SimpleRTFifo::LV2SimpleRTFifo(size_t size): + fifoSize(size), + itemSize(LV2_RT_FIFO_ITEM_SIZE) +{ + eventsBuffer.resize(fifoSize); + assert(eventsBuffer.size() == fifoSize); + readIndex = writeIndex = 0; + for(size_t i = 0; i < fifoSize; ++i) + { + eventsBuffer [i].port_index = 0; + eventsBuffer [i].buffer_size = 0; + eventsBuffer [i].data = new char [itemSize]; + } + +} + +LV2SimpleRTFifo::~LV2SimpleRTFifo() +{ + for(size_t i = 0; i < fifoSize; ++i) + { + delete [] eventsBuffer [i].data; + } +} + +bool LV2SimpleRTFifo::put(uint32_t port_index, uint32_t size, const void *data) +{ + if(size > itemSize) + { +#ifdef DEBUG_LV2 + std::cerr << "LV2SimpleRTFifo:put(): size("< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "midictrl.h" +#include "synth.h" +#include "stringparam.h" + +#include "plugin.h" + +#endif + +// Define to use QWidget instead of QMainWindow for the plugin gui container. +// #define LV2_GUI_USE_QWIDGET ; + +namespace MusECore +{ + +#ifdef LV2_SUPPORT + +class LV2Synth; + +#define LV2_RT_FIFO_SIZE 128 +#define LV2_RT_FIFO_ITEM_SIZE (std::max(size_t(4096 * 16), size_t(MusEGlobal::segmentSize * 16))) +#define LV2_EVBUF_SIZE (2*LV2_RT_FIFO_ITEM_SIZE) + +struct LV2MidiEvent +{ + int64_t frame; + uint8_t midi [4]; +}; + +class LV2EvBuf +{ + enum LV2_BUF_TYPE + { + LV2_BUF_EVENT, + LV2_BUF_ATOM + }; + std::vector _buffer; + size_t curWPointer; + size_t curRPointer; + bool _isInput; + bool _oldApi; + LV2_URID _uAtomTypeSequence; + LV2_URID _uAtomTypeChunk; + LV2_Atom_Sequence *_seqbuf; + LV2_Event_Buffer *_evbuf; +public: + LV2EvBuf(bool isInput, bool oldApi, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk); + inline size_t mkPadSize(size_t size); + inline void resetPointers(bool r, bool w); + inline void resetBuffer(); + bool write(uint32_t frames, uint32_t subframes, uint32_t type, uint32_t size, const uint8_t *data); + bool read(uint32_t *frames, uint32_t *subframes, uint32_t *type, uint32_t *size, uint8_t **data ); + uint8_t *getRawBuffer(); + void dump(); +}; + +class LV2SimpleRTFifo +{ +public: + typedef struct _lv2_uiControlEvent + { + uint32_t port_index; + long buffer_size; + char *data; + } lv2_uiControlEvent; +private: + //size_t numItems; + std::vector eventsBuffer; + size_t readIndex; + size_t writeIndex; + size_t fifoSize; + size_t itemSize; +public: + LV2SimpleRTFifo(size_t size); + ~LV2SimpleRTFifo(); + inline size_t getItemSize(){return itemSize; } + bool put(uint32_t port_index, uint32_t size, const void *data); + bool get(uint32_t *port_index, size_t *szOut, char *data_out); +}; + + + +struct LV2MidiPort +{ + LV2MidiPort (const LilvPort *_p, uint32_t _i, QString _n, bool _f, bool _supportsTimePos) : + port ( _p ), index ( _i ), name ( _n ), old_api ( _f ), supportsTimePos(_supportsTimePos), buffer(0){} + const LilvPort *port; + uint32_t index; //plugin real port index + QString name; + bool old_api; //true for LV2_Event port + bool supportsTimePos; + LV2EvBuf *buffer; +}; + +enum LV2ControlPortType +{ + LV2_PORT_DISCRETE = 1, + LV2_PORT_INTEGER, + LV2_PORT_CONTINUOUS, + LV2_PORT_LOGARITHMIC, + LV2_PORT_TRIGGER +}; + +struct LV2ControlPort +{ + LV2ControlPort ( const LilvPort *_p, uint32_t _i, float _c, const char *_n, const char *_s, LV2ControlPortType _ctype, bool _isCVPort = false) : + port ( _p ), index ( _i ), defVal ( _c ), minVal( _c ), maxVal ( _c ), cType(_ctype), + isCVPort(_isCVPort) + { + cName = strdup ( _n ); + cSym = strdup(_s); + } + LV2ControlPort ( const LV2ControlPort &other ) : + port ( other.port ), index ( other.index ), defVal ( other.defVal ), + minVal(other.minVal), maxVal(other.maxVal), cType(other.cType), + isCVPort(other.isCVPort) + { + cName = strdup ( other.cName ); + cSym = strdup(other.cSym); + } + ~LV2ControlPort() + { + free ( cName ); + cName = NULL; + free(cSym); + cSym = NULL; + } + const LilvPort *port; + uint32_t index; //plugin real port index + float defVal; //default control value + float minVal; //minimum control value + float maxVal; //maximum control value + char *cName; //cached value to share between function calls + char *cSym; //cached port symbol + LV2ControlPortType cType; + bool isCVPort; +}; + +struct LV2AudioPort +{ + LV2AudioPort ( const LilvPort *_p, uint32_t _i, float *_b, QString _n ) : + port ( _p ), index ( _i ), buffer ( _b ), name ( _n ) {} + const LilvPort *port; + uint32_t index; //plugin real port index + float *buffer; //audio buffer + QString name; +}; + +struct cmp_str +{ + bool operator() ( char const *a, char const *b ) const + { + return std::strcmp ( a, b ) < 0; + } +}; + +typedef std::vector LV2_MIDI_PORTS; +typedef std::vector LV2_CONTROL_PORTS; +typedef std::vector LV2_AUDIO_PORTS; +typedef std::map LV2_SYNTH_URID_MAP; +typedef std::map LV2_SYNTH_URID_RMAP; + +class LV2UridBiMap +{ +private: + LV2_SYNTH_URID_MAP _map; + LV2_SYNTH_URID_RMAP _rmap; + uint32_t nextId; + QMutex idLock; +public: + LV2UridBiMap(); + ~LV2UridBiMap(); + LV2_URID map ( const char *uri ); + const char *unmap ( uint32_t id ); +}; + +class LV2SynthIF; +struct LV2PluginWrapper_State; + +typedef std::map > LV2_PLUGIN_UI_TYPES; + +class LV2Synth : public Synth +{ +private: + const LilvPlugin *_handle; + LV2UridBiMap uridBiMap; + LV2_Feature *_features; + LV2_Feature **_ppfeatures; + LV2_Options_Option *_options; + LV2_URID_Map _lv2_urid_map; + LV2_URID_Unmap _lv2_urid_unmap; + LV2_URI_Map_Feature _lv2_uri_map; + LV2_Log_Log _lv2_log_log; + double _sampleRate; + float _fSampleRate; + bool _isSynth; + int _uniqueID; + uint32_t _midi_event_id; + LilvUIs *_uis; + std::map _idxToControlMap; + + LV2_PLUGIN_UI_TYPES _pluginUiTypes; + + //templates for LV2SynthIF and LV2PluginWrapper instantiation + LV2_MIDI_PORTS _midiInPorts; + LV2_MIDI_PORTS _midiOutPorts; + LV2_CONTROL_PORTS _controlInPorts; + LV2_CONTROL_PORTS _controlOutPorts; + LV2_AUDIO_PORTS _audioInPorts; + LV2_AUDIO_PORTS _audioOutPorts; + + MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to LV2 port numbers. + MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps LV2 port numbers to midi controller numbers. + uint32_t _fInstanceAccess; + uint32_t _fUiParent; + uint32_t _fExtUiHost; + uint32_t _fExtUiHostD; + uint32_t _fDataAccess; + uint32_t _fWrkSchedule; + uint32_t _fUiResize; + uint32_t _fPrgHost; + uint32_t _fMakePath; + uint32_t _fMapPath; + //const LilvNode *_pluginUIType = NULL; + LV2_URID _uTime_Position; + LV2_URID _uTime_frame; + LV2_URID _uTime_speed; + LV2_URID _uTime_beatsPerMinute; + LV2_URID _uTime_barBeat; + LV2_URID _uAtom_EventTransfer; + LV2_URID _uAtom_Chunk; + LV2_URID _uAtom_Sequence; + LV2_URID _uAtom_StateChanged; + LV2_URID _uAtom_Object; + bool _hasFreeWheelPort; + uint32_t _freeWheelPortIndex; + bool _isConstructed; + float *_pluginControlsDefault; + float *_pluginControlsMin; + float *_pluginControlsMax; + std::map _presets; +public: + virtual Type synthType() const { + return _isSynth ? LV2_SYNTH : LV2_EFFECT; + } + LV2Synth ( const QFileInfo &fi, QString label, QString name, QString author, + const LilvPlugin *_plugin, Plugin::PluginFeatures reqFeatures = Plugin::NoFeatures ); + virtual ~LV2Synth(); + virtual SynthIF *createSIF ( SynthI * ); + bool isSynth() { + return _isSynth; + } + + //own public functions + LV2_URID mapUrid ( const char *uri ); + const char *unmapUrid ( LV2_URID id ); + size_t inPorts() { + return _audioInPorts.size(); + } + size_t outPorts() { + return _audioOutPorts.size(); + } + bool isConstructed() {return _isConstructed; } + static void lv2ui_PostShow ( LV2PluginWrapper_State *state ); + static int lv2ui_Resize ( LV2UI_Feature_Handle handle, int width, int height ); + static void lv2ui_Gtk2AllocateCb(int width, int height, void *arg); + static void lv2ui_Gtk2ResizeCb(int width, int height, void *arg); + static void lv2ui_ShowNativeGui ( LV2PluginWrapper_State *state, bool bShow ); + static void lv2ui_PortWrite ( LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, void const *buffer ); + static void lv2ui_Touch (LV2UI_Controller controller, uint32_t port_index, bool grabbed); + static void lv2ui_ExtUi_Closed ( LV2UI_Controller contr ); + static void lv2ui_SendChangedControls(LV2PluginWrapper_State *state); + static void lv2state_FillFeatures ( LV2PluginWrapper_State *state ); + static void lv2state_PostInstantiate ( LV2PluginWrapper_State *state ); + static void lv2ui_FreeDescriptors(LV2PluginWrapper_State *state); + static void lv2state_FreeState(LV2PluginWrapper_State *state); + static void lv2audio_SendTransport(LV2PluginWrapper_State *state, LV2EvBuf *buffer, unsigned long nsamp); + static void lv2state_InitMidiPorts ( LV2PluginWrapper_State *state ); + static void inline lv2audio_preProcessMidiPorts (LV2PluginWrapper_State *state, unsigned long nsamp); + static void inline lv2audio_postProcessMidiPorts (LV2PluginWrapper_State *state, unsigned long nsamp); + static const void *lv2state_stateRetreive ( LV2_State_Handle handle, uint32_t key, size_t *size, uint32_t *type, uint32_t *flags ); + static LV2_State_Status lv2state_stateStore ( LV2_State_Handle handle, uint32_t key, const void *value, size_t size, uint32_t type, uint32_t flags ); + static LV2_Worker_Status lv2wrk_scheduleWork(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data); + static LV2_Worker_Status lv2wrk_respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void* data); + static void lv2conf_write(LV2PluginWrapper_State *state, int level, Xml &xml); + static void lv2conf_set(LV2PluginWrapper_State *state, const std::vector & customParams); + static unsigned lv2ui_IsSupported (const char *, const char *ui_type_uri); + static void lv2prg_updatePrograms(LV2PluginWrapper_State *state); + static int lv2_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...); + static int lv2_vprintf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, va_list ap); + static char *lv2state_makePath(LV2_State_Make_Path_Handle handle, const char *path); + static char *lv2state_abstractPath(LV2_State_Map_Path_Handle handle, const char *absolute_path); + static char *lv2state_absolutePath(LV2_State_Map_Path_Handle handle, const char *abstract_path); + static void lv2state_populatePresetsMenu(LV2PluginWrapper_State *state, MusEGui::PopupMenu *menu); + static void lv2state_PortWrite ( LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, void const *buffer, bool fromUi); + static void lv2state_setPortValue(const char *port_symbol, void *user_data, const void *value, uint32_t size, uint32_t type); + static const void* lv2state_getPortValue(const char *port_symbol, void *user_data, uint32_t *size, uint32_t *type); + static void lv2state_applyPreset(LV2PluginWrapper_State *state, LilvNode *preset); + static void lv2state_UnloadLoadPresets(LV2Synth *synth, bool load = false, bool update = false); + friend class LV2SynthIF; + friend class LV2PluginWrapper; + friend class LV2SynthIF_Timer; + + +}; + + +class LV2SynthIF : public SynthIF +{ +private: + LV2Synth *_synth; + LilvInstance *_handle; + LV2_CONTROL_PORTS _controlInPorts; + LV2_CONTROL_PORTS _controlOutPorts; + LV2_AUDIO_PORTS _audioInPorts; + LV2_AUDIO_PORTS _audioOutPorts; + LV2_Feature *_ifeatures; + LV2_Feature **_ppifeatures; + Port *_controls; + Port *_controlsOut; + bool init ( LV2Synth *s ); + size_t _inports; + size_t _outports; + size_t _inportsControl; + size_t _outportsControl; + size_t _inportsMidi; + size_t _outportsMidi; + float **_audioInBuffers; + float **_audioOutBuffers; + float *_audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. + void doSelectProgram(unsigned char channel, int bankH, int bankL, int prog); + inline void sendLv2MidiEvent(LV2EvBuf *evBuf, long frame, int paramCount, uint8_t a, uint8_t b = 0, uint8_t c = 0); + bool processEvent (const MidiPlayEvent &, LV2EvBuf *evBuf, long frame); + bool lv2MidiControlValues ( size_t port, int ctlnum, int *min, int *max, int *def ); + float midi2Lv2Value ( unsigned long port, int ctlnum, int val ); + LV2PluginWrapper_State *_state; + +public: + LV2SynthIF ( SynthI *s ); + virtual ~LV2SynthIF(); + + //virtual methods from SynthIF + virtual void guiHeartBeat(); + virtual bool hasGui() const; + virtual bool nativeGuiVisible() const; + virtual void showNativeGui ( bool v ); + virtual bool hasNativeGui() const; + virtual void getNativeGeometry ( int *, int *, int *, int * ) const; + virtual void setNativeGeometry (int x, int y, int w, int h); + virtual void preProcessAlways(); + virtual bool getData ( MidiPort *, unsigned pos, int ports, unsigned n, float **buffer ); + virtual MidiPlayEvent receiveEvent(); + virtual int eventsPending() const; + + virtual int channels() const; + virtual int totalOutChannels() const; + virtual int totalInChannels() const; + void activate(); + virtual void deactivate(); + virtual void deactivate3(); + virtual QString getPatchName (int, int, bool ) const; + virtual void populatePatchPopup ( MusEGui::PopupMenu *, int, bool ); + virtual void write ( int level, Xml &xml ) const; + virtual double getParameter ( unsigned long idx ) const; + virtual double getParameterOut ( unsigned long n ) const; + virtual void setParameter ( unsigned long idx, double value ); + virtual int getControllerInfo ( int id, QString* name, int *ctrl, int *min, int *max, int *initval ); + + virtual void writeConfiguration ( int level, Xml &xml ); + virtual bool readConfiguration ( Xml &xml, bool readPreset=false ); + + virtual void setCustomData ( const std::vector & ); + + + unsigned long parameters() const; + unsigned long parametersOut() const; + void setParam ( unsigned long i, double val ); + double param ( unsigned long i ) const; + double paramOut ( unsigned long i ) const; + const char *paramName ( unsigned long i ); + const char *paramOutName ( unsigned long i ); + virtual CtrlValueType ctrlValueType ( unsigned long ) const; + virtual CtrlList::Mode ctrlMode ( unsigned long ) const; + virtual LADSPA_PortRangeHint range(unsigned long i); + virtual LADSPA_PortRangeHint rangeOut(unsigned long i); + + virtual void enableController(unsigned long i, bool v = true); + virtual bool controllerEnabled(unsigned long i) const; + virtual void enableAllControllers(bool v = true); + virtual void updateControllers(); + + void populatePresetsMenu(MusEGui::PopupMenu *menu); + void applyPreset(void *preset); + + + int id() { + return MAX_PLUGINS; + } + + static void lv2prg_Changed(LV2_Programs_Handle handle, int32_t index); + + friend class LV2Synth; +}; + + +class LV2PluginWrapper; +class LV2PluginWrapper_Worker; +class LV2PluginWrapper_Window; + +typedef struct _lv2ExtProgram +{ + uint32_t index; + uint32_t bank; + uint32_t prog; + QString name; + bool useIndex; + bool operator<(const _lv2ExtProgram& other) const + { + if(useIndex == other.useIndex && useIndex == true) + return index < other.index; + + if(bank < other.bank) + return true; + else if(bank == other.bank && prog < other.prog) + return true; + return false; + } + + bool operator==(const _lv2ExtProgram& other) const + { + if(useIndex == other.useIndex && useIndex == true) + return index == other.index; + + return (bank == other.bank && prog == other.prog); + } + + +} lv2ExtProgram; + +struct LV2PluginWrapper_State { + LV2PluginWrapper_State(): + _ifeatures(NULL), + _ppifeatures(NULL), + widget(NULL), + handle(NULL), + uiDlHandle(NULL), + uiDesc(NULL), + uiInst(NULL), + inst(NULL), + lastControls(NULL), + controlsMask(NULL), + lastControlsOut(NULL), + plugInst(NULL), + sif(NULL), + synth(NULL), + human_id(NULL), + iState(NULL), + tmpValues(NULL), + numStateValues(0), + wrkDataSize(0), + wrkDataBuffer(0), + wrkThread(NULL), + wrkEndWork(false), + controlTimers(NULL), + deleteLater(false), + hasGui(false), + hasExternalGui(false), + uiIdleIface(NULL), + uiCurrent(NULL), + uiX11Size(0, 0), + pluginWindow(NULL), + pluginQWindow(NULL), + + prgIface(NULL), + uiPrgIface(NULL), + uiDoSelectPrg(false), + newPrgIface(false), + uiChannel(0), + uiBank(0), + uiProg(0), + gtk2Plug(NULL), + pluginCVPorts(NULL), + uiControlEvt(LV2_RT_FIFO_SIZE), + plugControlEvt(LV2_RT_FIFO_SIZE), + gtk2ResizeCompleted(false), + gtk2AllocateCompleted(false), + songDirtyPending(false), + uiIsOpening(false) + { + extHost.plugin_human_id = NULL; + extHost.ui_closed = NULL; + uiResize.handle = (LV2UI_Feature_Handle)this; + uiResize.ui_resize = LV2Synth::lv2ui_Resize; + prgHost.handle = (LV2_Programs_Handle)this; + prgHost.program_changed = LV2SynthIF::lv2prg_Changed; + makePath.handle = (LV2_State_Make_Path_Handle)this; + makePath.path = LV2Synth::lv2state_makePath; + mapPath.handle = (LV2_State_Map_Path_Handle)this; + mapPath.absolute_path = LV2Synth::lv2state_absolutePath; + mapPath.abstract_path = LV2Synth::lv2state_abstractPath; + + midiInPorts.clear(); + midiOutPorts.clear(); + idx2EvtPorts.clear(); + inPortsMidi = outPortsMidi = 0; + } + + LV2_Feature *_ifeatures; + LV2_Feature **_ppifeatures; + void *widget; + LV2_External_UI_Host extHost; + LV2_Extension_Data_Feature extData; + LV2_Worker_Schedule wrkSched; + LV2_State_Make_Path makePath; + LV2_State_Map_Path mapPath; + LilvInstance *handle; + void *uiDlHandle; + const LV2UI_Descriptor *uiDesc; + LV2UI_Handle uiInst; + LV2PluginWrapper *inst; + float *lastControls; + bool *controlsMask; + float *lastControlsOut; + PluginI *plugInst; + LV2SynthIF *sif; + LV2Synth *synth; + char *human_id; + LV2_State_Interface *iState; + QMap > iStateValues; + char **tmpValues; + size_t numStateValues; + uint32_t wrkDataSize; + const void *wrkDataBuffer; + LV2PluginWrapper_Worker *wrkThread; + LV2_Worker_Interface *wrkIface; + bool wrkEndWork; + int *controlTimers; + bool deleteLater; + LV2_Atom_Forge atomForge; + float curBpm; + bool curIsPlaying; + unsigned int curFrame; + bool hasGui; + bool hasExternalGui; + LV2UI_Idle_Interface *uiIdleIface; + const LilvUI *uiCurrent; + LV2UI_Resize uiResize; + QSize uiX11Size; + LV2PluginWrapper_Window *pluginWindow; + QWindow *pluginQWindow; + LV2_MIDI_PORTS midiInPorts; + LV2_MIDI_PORTS midiOutPorts; + size_t inPortsMidi; + size_t outPortsMidi; + LV2_Programs_Interface *prgIface; + LV2_Programs_UI_Interface *uiPrgIface; + bool uiDoSelectPrg; + bool newPrgIface; + std::map index2prg; + std::map prg2index; + LV2_Programs_Host prgHost; + unsigned char uiChannel; + int uiBank; + int uiProg; + void *gtk2Plug; + std::map controlsNameMap; + std::map controlsSymMap; + float **pluginCVPorts; + LV2SimpleRTFifo uiControlEvt; + LV2SimpleRTFifo plugControlEvt; + std::map idx2EvtPorts; + bool gtk2ResizeCompleted; + bool gtk2AllocateCompleted; + bool songDirtyPending; + bool uiIsOpening; +}; + + +class LV2PluginWrapper_Worker :public QThread +{ +private: + LV2PluginWrapper_State *_state; + QSemaphore _mSem; + bool _closing; +public: + explicit LV2PluginWrapper_Worker ( LV2PluginWrapper_State *s ) : QThread(), + _state ( s ), + _mSem(0), + _closing(false) + {} + + void run(); + LV2_Worker_Status scheduleWork(); + void makeWork(); + void setClosing() {_closing = true; _mSem.release();} + +}; + + +#ifdef LV2_GUI_USE_QWIDGET +class LV2PluginWrapper_Window : public QWidget +#else +class LV2PluginWrapper_Window : public QMainWindow +#endif +{ + Q_OBJECT +protected: + void closeEvent ( QCloseEvent *event ); + void showEvent(QShowEvent *e); + void hideEvent(QHideEvent *e); +private: + LV2PluginWrapper_State *_state; + bool _closing; + QTimer updateTimer; + void stopUpdateTimer(); +public: + explicit LV2PluginWrapper_Window ( LV2PluginWrapper_State *state, + QWidget *parent = Q_NULLPTR, + Qt::WindowFlags flags = Qt::WindowFlags()); + ~LV2PluginWrapper_Window(); + void startNextTime(); + void stopNextTime(); + void doChangeControls(); + void setClosing(bool closing) {_closing = closing; } +signals: + void makeStopFromGuiThread(); + void makeStartFromGuiThread(); +public slots: + void updateGui(); + void stopFromGuiThread(); + void startFromGuiThread(); +}; + + +class LV2PluginWrapper: public Plugin +{ +private: + LV2Synth *_synth; + LADSPA_Descriptor _fakeLd; + LADSPA_PortDescriptor *_fakePds; +public: + LV2PluginWrapper ( LV2Synth *s, PluginFeatures reqFeatures = NoFeatures ); + LV2Synth *synth() { + return _synth; + } + virtual ~LV2PluginWrapper(); + virtual LADSPA_Handle instantiate ( PluginI * ); + virtual int incReferences ( int ref ); + virtual void activate ( LADSPA_Handle handle ); + virtual void deactivate ( LADSPA_Handle handle ); + virtual void cleanup ( LADSPA_Handle handle ); + virtual void connectPort ( LADSPA_Handle handle, unsigned long port, float *value ); + virtual void apply ( LADSPA_Handle handle, unsigned long n ); + virtual LADSPA_PortDescriptor portd ( unsigned long k ) const; + + virtual LADSPA_PortRangeHint range ( unsigned long i ); + virtual void range ( unsigned long i, float *min, float *max ) const; + + virtual double defaultValue ( unsigned long port ) const; + virtual const char *portName ( unsigned long i ); + virtual CtrlValueType ctrlValueType ( unsigned long ) const; + virtual CtrlList::Mode ctrlMode ( unsigned long ) const; + virtual bool hasNativeGui() const; + virtual void showNativeGui ( PluginI *p, bool bShow ); + virtual bool nativeGuiVisible (const PluginI *p ) const; + virtual void setLastStateControls(LADSPA_Handle handle, size_t index, bool bSetMask, bool bSetVal, bool bMask, float fVal); + virtual void writeConfiguration(LADSPA_Handle handle, int level, Xml& xml); + virtual void setCustomData (LADSPA_Handle handle, const std::vector & customParams); + + void populatePresetsMenu(PluginI *p, MusEGui::PopupMenu *menu); + void applyPreset(PluginI *p, void *preset); +}; + +#endif // LV2_SUPPORT + +extern void initLV2(); + +} // namespace MusECore + +#endif + + + diff -Nru muse-2.1.2/muse/lv2Support/ChangeLog muse-3.0.2+ds1/muse/lv2Support/ChangeLog --- muse-2.1.2/muse/lv2Support/ChangeLog 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/lv2Support/ChangeLog 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,23 @@ +12.05.2016: + - Update all libs to latest versions. (Tim) + * Steps to update the built-in lv2 support library versions: + (A guide really, new versions may deprecate things or need new tweaks): + 1: Bring (copy) all the new versions' folders to lv2Support folder. + 2: Delete all unnecessary text files and build scripts etc, and doc, + bindings, test, utils folders etc, also the lv2/lv2plug.in/ns/meta + folder and index.html.in We want just all the lib c/h files. + 3: Comment out all #include "sord_config.h", #include "serd_config.h" + and #include "lilv_config.h" since WE supply those config values. + 4: Apply the fix of 24.03.2016 if desired. + 5: Be sure to change the hard-coded lv2, lilv, serd, sord and sratom + built-in version numbers, in the top-level cmakelists.txt, + so that cmake can find these newly added versions. + 6: Delete the old versions' folders. + 7: Remember to remove/add folders from/to the git repo. +24.03.2016: + - Changed lilv/lib.c: lilv_lib_open(): Added RTLD_DEEPBIND to dlopen() call. (Tim) + Fixes at least two Qt4 external-ui synths (synthv1, drumk) crashing. + Could not find any more such plugins to test. + See the corresponding ui dlopen call in LV2Synth::lv2ui_ShowNativeGui() + for a similar fix and test results. + diff -Nru muse-2.1.2/muse/lv2Support/CMakeLists.txt muse-3.0.2+ds1/muse/lv2Support/CMakeLists.txt --- muse-2.1.2/muse/lv2Support/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/lv2Support/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,148 @@ + +cmake_policy(SET CMP0005 NEW) + +include_directories(${muse_sord_dir}/src) +include_directories(${muse_lilv_dir}/src) + +## +## List of source files to compile +## +file (GLOB lv2_support_files +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/uri-map/uri-map.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/parameters/parameters.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/buf-size/buf-size.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/instance-access/instance-access.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/time/time.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/event/event.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/event/event-helpers.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/presets/presets.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/resize-port/resize-port.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/patch/patch.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/options/options.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/data-access/data-access.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/urid/urid.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/port-props/port-props.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/state/state.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/morph/morph.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/midi/midi.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/worker/worker.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/port-groups/port-groups.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/log/logger.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/log/log.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/atom/atom-test.c +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/atom/atom.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/atom/forge.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/ext/atom/util.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/extensions/units/units.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/extensions/ui/ui.h +${muse_lv2_dir}/lv2/lv2plug.in/ns/lv2core/lv2.h + +${muse_sratom_dir}/sratom/sratom.h +${muse_sratom_dir}/src/sratom.c + +${muse_sord_dir}/sord/sord.h +${muse_sord_dir}/src/sord.c +${muse_sord_dir}/src/syntax.c +${muse_sord_dir}/src/sord_internal.h + +${muse_serd_dir}/src/writer.c +${muse_serd_dir}/src/reader.c +${muse_serd_dir}/src/uri.c +${muse_serd_dir}/src/serd_internal.h +${muse_serd_dir}/src/env.c +${muse_serd_dir}/serd/serd.h +${muse_serd_dir}/src/string.c +${muse_serd_dir}/src/node.c + +${muse_lilv_dir}/src/world.c +${muse_lilv_dir}/src/collections.c +${muse_lilv_dir}/src/scalepoint.c +${muse_lilv_dir}/src/lilv_internal.h +${muse_lilv_dir}/src/lib.c +${muse_lilv_dir}/src/query.c +${muse_lilv_dir}/lilv/lilv.h +${muse_lilv_dir}/src/port.c +${muse_lilv_dir}/src/ui.c +${muse_lilv_dir}/src/state.c +${muse_lilv_dir}/src/plugin.c +${muse_lilv_dir}/src/instance.c +${muse_lilv_dir}/src/node.c +${muse_lilv_dir}/src/pluginclass.c +${muse_lilv_dir}/src/util.c + +${muse_sord_dir}/src/zix/common.h +${muse_sord_dir}/src/zix/digest.c +${muse_sord_dir}/src/zix/hash.h +${muse_sord_dir}/src/zix/digest.h +${muse_sord_dir}/src/zix/btree.h +${muse_sord_dir}/src/zix/hash.c +${muse_sord_dir}/src/zix/btree.c +${muse_lilv_dir}/src/zix/tree.h +${muse_lilv_dir}/src/zix/tree.c + +lv2extui.h +lv2extprg.h +) + +set(CMAKE_C_FLAGS "-std=c99") + +if (PCRE_FOUND) + include_directories(${PCRE_INCLUDE_DIRS}) + add_definitions(-DHAVE_PCRE=1) +endif (PCRE_FOUND) + +CHECK_INCLUDE_FILES (sys/file.h HAVE_FILE_H) + +if(HAVE_FILE_H) +# Check for jack session availability +check_c_source_compiles ( + "#include + #include + #define _POSIX_C_SOURCE=1 + int main(int argc, char **argv) + { + FILE *f = 0; + flock(0, 0); + fileno(f); + return 0; + }" HasFileNo +) + if (HasFileNo) + add_definitions(-DHAVE_FLOCK=1) + add_definitions(-DHAVE_FILENO=1) + endif (HasFileNo) +endif(HAVE_FILE_H) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fPIC") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -g -Wall -fPIC") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -g0 -Werror -fPIC") + +include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) +add_definitions(-DHAVE_LV2=1) +add_definitions(-DHAVE_SERD=1) +add_definitions(-DHAVE_SORD=1) +add_definitions(-DLILV_VERSION=${muse_lilv_ver}) +add_definitions(-DLILV_NEW_LV2=1) +add_definitions(-DLILV_PATH_SEP=":") +add_definitions(-DLILV_DIR_SEP="/") +add_definitions(-DLILV_INTERNAL=1) +add_definitions(-D_FILE_OFFSET_BITS=64) +add_definitions(-DSERD_INTERNAL=1) +add_definitions(-DSORD_INTERNAL=1) +add_definitions(-DSRATOM_INTERNAL=1) +Set (LILV_DEFAULT_LV2_PATH ~/.lv2:/usr/lib${LIB_SUFFIX}/lv2:/usr/local/lib${LIB_SUFFIX}/lv2) +add_definitions(-DLILV_DEFAULT_LV2_PATH="${LILV_DEFAULT_LV2_PATH}") + +message (STATUS "LILV_DEFAULT_LV2_PATH= ${LILV_DEFAULT_LV2_PATH}") + +## +## Define target +## +add_library ( lv2_support STATIC ${lv2_support_files} ) + +# Complains if C not set. +set_target_properties( lv2_support + PROPERTIES LINKER_LANGUAGE C + ) + diff -Nru muse-2.1.2/muse/lv2Support/lv2extprg.h muse-3.0.2+ds1/muse/lv2Support/lv2extprg.h --- muse-2.1.2/muse/lv2Support/lv2extprg.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/lv2Support/lv2extprg.h 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,189 @@ +/* + LV2 Programs Extension + Copyright 2012 Filipe Coelho + 2014 Deryabin Andrew + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file lv2extprg.h + C header for the LV2 programs extension . +*/ + +#ifndef LV2_PROGRAMS_H +#define LV2_PROGRAMS_H + +#define LV2_PROGRAMS_URI "http://kxstudio.sf.net/ns/lv2ext/programs" +#define LV2_PROGRAMS_PREFIX LV2_PROGRAMS_URI "#" + +#define LV2_PROGRAMS__Host LV2_PROGRAMS_PREFIX "Host" +#define LV2_PROGRAMS__Interface LV2_PROGRAMS_PREFIX "Interface" +#define LV2_PROGRAMS__UIInterface LV2_PROGRAMS_PREFIX "UIInterface" + +#define LV2_PROGRAMSNEW_URI "http://yoshimi.sf.net/ns/lv2ext/programs" +#define LV2_PROGRAMSNEW_PREFIX LV2_PROGRAMSNEW_URI "#" + +#define LV2_PROGRAMSNEW__Interface LV2_PROGRAMSNEW_PREFIX "Interface" +#define LV2_PROGRAMSNEW__UIInterface LV2_PROGRAMSNEW_PREFIX "UIInterface" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* LV2_Programs_Handle; + +typedef struct _LV2_Program_Descriptor { + + /** Bank number for this program. Note that this extension does not + support MIDI-style separation of bank LSB and MSB values. There is + no restriction on the set of available banks: the numbers do not + need to be contiguous, there does not need to be a bank 0, etc. */ + uint32_t bank; + + /** Program number (unique within its bank) for this program. There is + no restriction on the set of available programs: the numbers do not + need to be contiguous, there does not need to be a program 0, etc. */ + uint32_t program; + + /** Name of the program. */ + const char * name; + +} LV2_Program_Descriptor; + +/** + Programs extension, plugin data. + + When the plugin's extension_data is called with argument LV2_PROGRAMS__Interface, + the plugin MUST return an LV2_Programs_Instance structure, which remains valid + for the lifetime of the plugin. +*/ +typedef struct _LV2_Programs_Interface { + /** + * get_program() + * + * This member is a function pointer that provides a description + * of a program (named preset sound) available on this plugin. + * + * The index argument is an index into the plugin's list of + * programs, not a program number as represented by the Program + * field of the LV2_Program_Descriptor. (This distinction is + * needed to support plugins that use non-contiguous program or + * bank numbers.) + * + * This function returns a LV2_Program_Descriptor pointer that is + * guaranteed to be valid only until the next call to get_program + * or deactivate, on the same plugin instance. This function must + * return NULL if passed an index argument out of range, so that + * the host can use it to query the number of programs as well as + * their properties. + */ + const LV2_Program_Descriptor *(*get_program)(LV2_Handle handle, + uint32_t index); + + /** + * select_program() + * + * This member is a function pointer that selects a new program + * for this plugin. The program change should take effect + * immediately at the start of the next run() call. (This + * means that a host providing the capability of changing programs + * between any two notes on a track must vary the block size so as + * to place the program change at the right place. A host that + * wanted to avoid this would probably just instantiate a plugin + * for each program.) + * + * Plugins should ignore a select_program() call with an invalid + * bank or program. + * + * A plugin is not required to select any particular default + * program on activate(): it's the host's duty to set a program + * explicitly. + * + * 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 LV2 plugin is allowed to modify its own control-input ports.) + */ + void (*select_program)(LV2_Handle handle, + uint32_t bank, + uint32_t program); + + void (*select_program_for_channel)(LV2_Handle handle, + unsigned char channel, + uint32_t bank, + uint32_t program); + +} LV2_Programs_Interface; + + +/** + Programs extension, UI data. + + When the UI's extension_data is called with argument LV2_PROGRAMS__UIInterface, + the UI MUST return an LV2_Programs_UI_Interface structure, which remains valid + for the lifetime of the UI. +*/ +typedef struct _LV2_Programs_UI_Interface { + /** + * select_program() + * + * This is exactly the same as select_program in LV2_Programs_Instance, + * but this struct relates to the UI instead of the plugin. + * + * When called, UIs should update their state to match the selected program. + */ + void (*select_program)(LV2UI_Handle handle, + uint32_t bank, + uint32_t program); + + void (*select_program_for_channel)(LV2_Handle handle, + unsigned char channel, + uint32_t bank, + uint32_t program); + +} LV2_Programs_UI_Interface; + +/** + Feature data for LV2_PROGRAMS__Host. +*/ +typedef struct _LV2_Programs_Host { + /** + * Opaque host data. + */ + LV2_Programs_Handle handle; + + /** + * program_changed() + * + * Tell the host to reload a plugin's program. + * Parameter handle MUST be the 'handle' member of this struct. + * Parameter index is program index to change. + * When index is -1, host should reload all the programs. + * + * The plugin MUST NEVER call this function on a RT context or during run(). + * + * NOTE: This call is to inform the host about a program's bank, program or name change. + * It DOES NOT change the current selected program. + */ + void (*program_changed)(LV2_Programs_Handle handle, + int32_t index); + +} LV2_Programs_Host; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_PROGRAMS_H */ diff -Nru muse-2.1.2/muse/lv2Support/lv2extui.h muse-3.0.2+ds1/muse/lv2Support/lv2extui.h --- muse-2.1.2/muse/lv2Support/lv2extui.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/lv2Support/lv2extui.h 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,109 @@ +/* + LV2 External UI extension + This work is in public domain. + + This file 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. + + If you have questions, contact Filipe Coelho (aka falkTX) + or ask in #lad channel, FreeNode IRC network. +*/ + +/** + @file lv2_external_ui.h + C header for the LV2 External UI extension . +*/ + +#ifndef LV2_EXTERNAL_UI_H +#define LV2_EXTERNAL_UI_H + +#include "lv2/lv2plug.in/ns/extensions/ui/ui.h" + +#define LV2_EXTERNAL_UI_URI "http://kxstudio.sf.net/ns/lv2ext/external-ui" +#define LV2_EXTERNAL_UI_PREFIX LV2_EXTERNAL_UI_URI "#" + +#define LV2_EXTERNAL_UI__Host LV2_EXTERNAL_UI_PREFIX "Host" +#define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget" + +/** This extension used to be defined by a lv2plug.in URI */ +#define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned + * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget. + * UI is created in invisible state. + */ +typedef struct _LV2_External_UI_Widget { + /** + * Host calls this function regulary. UI library implementing the + * callback may do IPC or redraw the UI. + * + * @param _this_ the UI context + */ + void (*run)(struct _LV2_External_UI_Widget * _this_); + + /** + * Host calls this function to make the plugin UI visible. + * + * @param _this_ the UI context + */ + void (*show)(struct _LV2_External_UI_Widget * _this_); + + /** + * Host calls this function to make the plugin UI invisible again. + * + * @param _this_ the UI context + */ + void (*hide)(struct _LV2_External_UI_Widget * _this_); + +} LV2_External_UI_Widget; + +#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr) +#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr) +#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr) + +/** + * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature. + * LV2_Feature::data must be pointer to LV2_External_UI_Host. + */ +typedef struct _LV2_External_UI_Host { + /** + * Callback that plugin UI will call when UI (GUI window) is closed by user. + * This callback will be called during execution of LV2_External_UI_Widget::run() + * (i.e. not from background thread). + * + * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup(). + * If host wants to make the UI visible again, the UI must be reinstantiated. + * + * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI, + * some hosts will not call LV2UI_Descriptor::cleanup() as they should, + * and may call show() again without re-initialization. + * + * @param controller Host context associated with plugin UI, as + * supplied to LV2UI_Descriptor::instantiate(). + */ + void (*ui_closed)(LV2UI_Controller controller); + + /** + * Optional (may be NULL) "user friendly" identifier which the UI + * may display to allow a user to easily associate this particular + * UI instance with the correct plugin instance as it is represented + * by the host (e.g. "track 1" or "channel 4"). + * + * If supplied by host, the string will be referenced only during + * LV2UI_Descriptor::instantiate() + */ + const char * plugin_human_id; + +} LV2_External_UI_Host; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_EXTERNAL_UI_H */ diff -Nru muse-2.1.2/muse/main.cpp muse-3.0.2+ds1/muse/main.cpp --- muse-2.1.2/muse/main.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/main.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -42,13 +42,17 @@ #include #include #include + +#include "config.h" + +#ifdef ALSA_SUPPORT #include +#endif #include "al/dsp.h" #include "app.h" #include "audio.h" #include "audiodev.h" -#include "config.h" #include "gconfig.h" #include "globals.h" #include "helper.h" @@ -59,6 +63,8 @@ #include "minstrument.h" #include "midiport.h" #include "mididev.h" +#include "plugin.h" +#include "wavepreview.h" #ifdef HAVE_LASH #include @@ -66,6 +72,9 @@ namespace MusECore { extern bool initDummyAudio(); +#ifdef HAVE_RTAUDIO +extern bool initRtAudio(bool forceDefault = false); +#endif extern bool initJackAudio(); extern void initMidiController(); extern void initMetronome(); @@ -74,14 +83,22 @@ extern void initVST_Native(); extern void initPlugins(); extern void initDSSI(); +#ifdef LV2_SUPPORT +extern void initLV2(); +extern void deinitLV2(); +#endif extern void readConfiguration(); extern void initMidiSequencer(); -extern void initAudio(); +extern void exitMidiSequencer(); +extern void initAudio(); extern void initAudioPrefetch(); extern void initMidiSynth(); +#ifdef ALSA_SUPPORT extern snd_seq_t * alsaSeq; +#endif + extern void setAlsaClientName(const char*); } @@ -91,35 +108,18 @@ #ifdef HAVE_LASH extern lash_client_t * lash_client; #endif +extern QStringList projectRecentList; } -static QString locale_override; +enum AudioDriverSelect { + DriverConfigSetting, + DummyAudioOverride, + JackAudioOverride, + RtAudioOverride, -//--------------------------------------------------------- -// getCapabilities -//--------------------------------------------------------- +}; -static void getCapabilities() - { -#ifdef RTCAP -#ifdef __linux__ - const char* napp = getenv("GIVERTCAP"); - if (napp == 0) - napp = "givertcap"; - int pid = fork(); - if (pid == 0) { - if (execlp(napp, napp, 0) == -1) - perror("exec givertcap failed"); - } - else if (pid == -1) { - perror("fork givertcap failed"); - } - else { - waitpid(pid, 0, 0); - } -#endif // __linux__ -#endif - } +static QString locale_override; //--------------------------------------------------------- // printVersion @@ -127,7 +127,10 @@ static void printVersion(const char* prog) { - fprintf(stderr, "%s: Linux Music Editor; Version %s, (svn revision %s)\n", prog, VERSION, SVNVERSION); + if (strcmp("", GITSTRING)) + fprintf(stderr, "%s: Linux Music Editor; Version %s, (%s)\n", prog, VERSION, GITSTRING); + else + fprintf(stderr, "%s: Linux Music Editor; Version %s\n", prog, VERSION); } //--------------------------------------------------------- @@ -156,31 +159,42 @@ } bool notify(QObject* receiver, QEvent* event) { - bool flag = QApplication::notify(receiver, event); - if (event->type() == QEvent::KeyPress) { - QKeyEvent* ke = (QKeyEvent*)event; - MusEGlobal::globalKeyState = ke->modifiers(); - bool accepted = ke->isAccepted(); - if (!accepted) { - int key = ke->key(); - if (((QInputEvent*)ke)->modifiers() & Qt::ShiftModifier) - key += Qt::SHIFT; - if (((QInputEvent*)ke)->modifiers() & Qt::AltModifier) - key += Qt::ALT; - if (((QInputEvent*)ke)->modifiers() & Qt::ControlModifier) - key+= Qt::CTRL; - muse->kbAccel(key); - return true; - } - } - if (event->type() == QEvent::KeyRelease) { - QKeyEvent* ke = (QKeyEvent*)event; - ///MusEGlobal::globalKeyState = ke->stateAfter(); - MusEGlobal::globalKeyState = ke->modifiers(); - } + bool flag = QApplication::notify(receiver, event); + if (event->type() == QEvent::KeyPress) { +#if QT_VERSION >= 0x050000 + const QMetaObject * mo = receiver->metaObject(); + bool forQWidgetWindow = false; + if (mo){ + if (strcmp(mo->className(), "QWidgetWindow") == 0) + forQWidgetWindow = true; + } + if(forQWidgetWindow){ + return false; + } +#endif + QKeyEvent* ke = (QKeyEvent*)event; + MusEGlobal::globalKeyState = ke->modifiers(); + bool accepted = ke->isAccepted(); + if (!accepted) { + int key = ke->key(); + if (((QInputEvent*)ke)->modifiers() & Qt::ShiftModifier) + key += Qt::SHIFT; + if (((QInputEvent*)ke)->modifiers() & Qt::AltModifier) + key += Qt::ALT; + if (((QInputEvent*)ke)->modifiers() & Qt::ControlModifier) + key+= Qt::CTRL; + muse->kbAccel(key); + return true; + } + } + if (event->type() == QEvent::KeyRelease) { + QKeyEvent* ke = (QKeyEvent*)event; + ///MusEGlobal::globalKeyState = ke->stateAfter(); + MusEGlobal::globalKeyState = ke->modifiers(); + } - return flag; - } + return flag; + } #ifdef HAVE_LASH virtual void timerEvent (QTimerEvent*) { @@ -228,8 +242,13 @@ prog, txt, prog); fprintf(stderr, " -h This help\n"); fprintf(stderr, " -v Print version\n"); - fprintf(stderr, " -a No audio, use dummy audio driver, plus ALSA midi\n"); + fprintf(stderr, " -a Alsa midi only (using dummy audio driver)\n"); +#ifdef HAVE_RTAUDIO + fprintf(stderr, " -t Use RtAudio driver (with Pulse Audio driver).\n"); +#endif + fprintf(stderr, " -j Use JAckAudio driver to connect to Jack audio server\n"); fprintf(stderr, " -J Do not try to auto-start the Jack audio server\n"); + fprintf(stderr, " -F Do not auto-populate midi ports with midi devices found, at startup\n"); fprintf(stderr, " -A Force inclusion of ALSA midi even if using Jack\n"); fprintf(stderr, " -P n Set audio driver real time priority to n\n"); fprintf(stderr, " (Dummy only, default 40. Else fixed by Jack.)\n"); @@ -245,6 +264,9 @@ #ifdef DSSI_SUPPORT fprintf(stderr, " -I Don't load DSSI plugins\n"); #endif +#ifdef LV2_SUPPORT + fprintf(stderr, " -2 Don't load LV2 plugins\n"); +#endif #ifdef HAVE_LASH fprintf(stderr, " -L Don't use LASH\n"); #endif @@ -280,23 +302,38 @@ fprintf(stderr, "\n"); - fprintf(stderr, "Some useful environment variables:\n"); + fprintf(stderr, "Some useful environment variables:\n\n"); + fprintf(stderr, " LANG: Help browser language suffix (en etc.)\n\n"); + fprintf(stderr, "These variables are read ONCE upon first-time run, to fill the Plugin Paths\n" + " in Global Settings. Afterwards the paths can be altered in Global Settings:\n\n"); fprintf(stderr, " LADSPA_PATH: Override where to look for ladspa plugins, or else\n" " ~/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa\n\n"); #ifdef DSSI_SUPPORT fprintf(stderr, " DSSI_PATH: Override where to look for dssi plugins (dssi-vst plugins: VST_PATH), or else\n" - " ~/dssi:/usr/local/lib64/dssi:/usr/lib64/dssi:/usr/local/lib/dssi:/usr/lib/dssi\n\n" ); + " ~/dssi:/usr/local/lib64/dssi:/usr/lib64/dssi:/usr/local/lib/dssi:/usr/lib/dssi\n\n" ); #endif #ifdef VST_NATIVE_SUPPORT fprintf(stderr, " VST_NATIVE_PATH: Override where to look for native vst plugins, or else VST_PATH, or else\n" - " ~/vst:/usr/local/lib64/vst:/usr/local/lib/vst:/usr/lib64/vst:/usr/lib/vst\n\n"); + " ~/.vst:~/vst:/usr/local/lib64/vst:/usr/local/lib/vst:/usr/lib64/vst:/usr/lib/vst\n\n"); +#endif +#ifdef LV2_SUPPORT + fprintf(stderr, " LV2_PATH: Override where to look for LV2 plugins or else\n" + " ~/.lv2:/usr/local/lib/lv2:/usr/lib/lv2\n\n"); #endif - fprintf(stderr, " LANG: Help browser language suffix (en etc.)\n"); - fprintf(stderr, "\n"); } + +void fallbackDummy() { + + fprintf(stderr, "Falling back to dummy audio driver\n"); + QMessageBox::critical(NULL, "MusE fatal error", "MusE failed to find selected audio server.

    " + "MusE will continue without audio support (-a switch)!"); + MusEGlobal::realTimeScheduling = true; + MusECore::initDummyAudio(); +} + //--------------------------------------------------------- // main //--------------------------------------------------------- @@ -319,22 +356,22 @@ bool cConfExists = cConf.exists(); if (!cConfExists) { - printf ("creating new config...\n"); + fprintf(stderr, "creating new config...\n"); if (cConfTempl.copy(MusEGlobal::configName)) - printf (" success.\n"); + fprintf(stderr, " success.\n"); else - printf (" FAILED!\n"); + fprintf(stderr, " FAILED!\n"); } QFile cConfQt (MusEGlobal::configPath + QString("/MusE-qt.conf")); QFile cConfTemplQt (MusEGlobal::museGlobalShare + QString("/templates/MusE-qt.conf")); if (! cConfQt.exists()) { - printf ("creating new qt config...\n"); + fprintf(stderr, "creating new qt config...\n"); if (cConfTemplQt.copy(cConfQt.fileName())) - printf (" success.\n"); + fprintf(stderr, " success.\n"); else - printf (" FAILED!\n"); + fprintf(stderr, " FAILED!\n"); } MusEGui::initShortCuts(); @@ -344,400 +381,666 @@ if(!cConfExists) { MusEGlobal::config.projectBaseFolder = MusEGlobal::museUser + QString("/MusE"); - MusEGlobal::config.startSong = MusEGlobal::museGlobalShare + QString("/templates/default.med"); + MusEGlobal::config.startSong = ""; } - // May need this. Tested OK. Grab the default style BEFORE calling setStyle and creating the app. - //{ int dummy_argc = 1; char** dummy_argv = &argv[0]; - // QApplication dummy_app(dummy_argc, dummy_argv); - // MusEGui::Appearance::defaultStyle = dummy_app.style()->objectName(); } - //QStringList sl = QStyleFactory::keys(); - //if (sl.indexOf(MusEGlobal::config.style) != -1) { - // QStyle* style = QApplication::setStyle(MusEGlobal::config.style); - // style->setObjectName(MusEGlobal::config.style); - //} - - // Let LASH remove its recognized arguments first (generally longer than Qt's). - // Tip: LADISH's LASH emulation (current 1.0) does not take any arguments. -#ifdef HAVE_LASH - lash_args_t * lash_args = 0; - lash_args = lash_extract_args (&argc, &argv); -#endif + if(MusEGlobal::config.pluginLadspaPathList.isEmpty()) + { + QString pth(getenv("LADSPA_PATH")); + if(pth.isEmpty()) + { + MusEGlobal::config.pluginLadspaPathList << + (MusEGlobal::museUser + QString("/ladspa")) << + QString("/usr/local/lib64/ladspa") << + QString("/usr/local/lib/ladspa") << + QString("/usr/lib64/ladspa") << + QString("/usr/lib/ladspa"); + setenv("LADSPA_PATH", MusEGlobal::config.pluginLadspaPathList.join(":").toLatin1().constData(), true); + } + else + MusEGlobal::config.pluginLadspaPathList = pth.split(":", QString::SkipEmptyParts); + } + else + setenv("LADSPA_PATH", MusEGlobal::config.pluginLadspaPathList.join(":").toLatin1().constData(), true); - // Now create the application, and let Qt remove recognized arguments. - MuseApplication app(argc, argv); - MusEGui::Appearance::defaultStyle = app.style()->objectName(); // NOTE: May need alternate method, above. + if(MusEGlobal::config.pluginDssiPathList.isEmpty()) + { + QString pth(getenv("DSSI_PATH")); + if(pth.isEmpty()) + { + MusEGlobal::config.pluginDssiPathList << + (MusEGlobal::museUser + QString("/dssi")) << + QString("/usr/local/lib64/dssi") << + QString("/usr/local/lib/dssi") << + QString("/usr/lib64/dssi") << + QString("/usr/lib/dssi"); + setenv("DSSI_PATH", MusEGlobal::config.pluginDssiPathList.join(":").toLatin1().constData(), true); + } + else + MusEGlobal::config.pluginDssiPathList = pth.split(":", QString::SkipEmptyParts); + } + else + setenv("DSSI_PATH", MusEGlobal::config.pluginDssiPathList.join(":").toLatin1().constData(), true); - QString optstr("aJAhvdDumMsP:Y:l:py"); -#ifdef VST_SUPPORT - optstr += QString("V"); -#endif -#ifdef VST_NATIVE_SUPPORT - optstr += QString("N"); -#endif -#ifdef DSSI_SUPPORT - optstr += QString("I"); -#endif -#ifdef HAVE_LASH - optstr += QString("L"); -#endif + if(MusEGlobal::config.pluginVstPathList.isEmpty()) + { + QString pth(getenv("VST_PATH")); + if(!pth.isEmpty()) + MusEGlobal::config.pluginVstPathList = pth.split(":", QString::SkipEmptyParts); + else + { + MusEGlobal::config.pluginVstPathList << + (MusEGlobal::museUser + QString("/.vst")) << + QString("/usr/local/lib64/vst") << + QString("/usr/local/lib/vst") << + QString("/usr/lib64/vst") << + QString("/usr/lib/vst"); + setenv("VST_PATH", MusEGlobal::config.pluginVstPathList.join(":").toLatin1().constData(), true); + } + } + else + setenv("VST_PATH", MusEGlobal::config.pluginVstPathList.join(":").toLatin1().constData(), true); - bool noAudio = false; - int i; + if(MusEGlobal::config.pluginLinuxVstPathList.isEmpty()) + { + QString pth(getenv("LINUX_VST_PATH")); + if(!pth.isEmpty()) + MusEGlobal::config.pluginLinuxVstPathList = pth.split(":", QString::SkipEmptyParts); + else + { + pth = QString(getenv("VST_PATH")); + if(!pth.isEmpty()) + MusEGlobal::config.pluginLinuxVstPathList = pth.split(":", QString::SkipEmptyParts); + else + { + MusEGlobal::config.pluginLinuxVstPathList << + (MusEGlobal::museUser + QString("/.vst")) << + QString("/usr/local/lib64/vst") << + QString("/usr/local/lib/vst") << + QString("/usr/lib64/vst") << + QString("/usr/lib/vst"); + setenv("LINUX_VST_PATH", MusEGlobal::config.pluginLv2PathList.join(":").toLatin1().constData(), true); + } + } + } + else + setenv("LINUX_VST_PATH", MusEGlobal::config.pluginLinuxVstPathList.join(":").toLatin1().constData(), true); - // Now read the remaining arguments as our own... - while ((i = getopt(argc, argv, optstr.toLatin1().constData())) != EOF) { - char c = (char)i; - switch (c) { - case 'v': printVersion(argv[0]); -#ifdef HAVE_LASH - if(lash_args) lash_args_destroy(lash_args); -#endif - return 0; - case 'a': - noAudio = true; - break; - case 'J': - MusEGlobal::noAutoStartJack = true; - break; - case 'A': - MusEGlobal::useAlsaWithJack = true; - break; - case 'd': - MusEGlobal::debugMode = true; - MusEGlobal::realTimeScheduling = false; - break; - case 'D': - if (!MusEGlobal::debugMsg) - MusEGlobal::debugMsg=true; - else - MusEGlobal::heavyDebugMsg=true; - break; - case 'm': MusEGlobal::midiInputTrace = true; break; - case 'M': MusEGlobal::midiOutputTrace = true; break; - case 's': MusEGlobal::debugSync = true; break; - case 'u': MusEGlobal::unityWorkaround = true; break; - case 'P': MusEGlobal::realTimePriority = atoi(optarg); break; - case 'Y': MusEGlobal::midiRTPrioOverride = atoi(optarg); break; - case 'p': MusEGlobal::loadPlugins = false; break; - case 'V': MusEGlobal::loadVST = false; break; - case 'N': MusEGlobal::loadNativeVST = false; break; - case 'I': MusEGlobal::loadDSSI = false; break; - case 'L': MusEGlobal::useLASH = false; break; - case 'y': MusEGlobal::usePythonBridge = true; break; - case 'l': locale_override = QString(optarg); break; - case 'h': usage(argv[0], argv[1]); -#ifdef HAVE_LASH - if(lash_args) lash_args_destroy(lash_args); -#endif - return -1; - default: usage(argv[0], "bad argument"); -#ifdef HAVE_LASH - if(lash_args) lash_args_destroy(lash_args); -#endif - return -1; - } + // Special for LV2: Since we use the recommended lilv_world_load_all() + // not lilv_world_load_bundle(), LV2_PATH seems to be the only way to set paths. + if(MusEGlobal::config.pluginLv2PathList.isEmpty()) + { + QString pth(getenv("LV2_PATH")); + if(pth.isEmpty()) + { + MusEGlobal::config.pluginLv2PathList << + (MusEGlobal::museUser + QString("/.lv2")) << + QString("/usr/local/lib64/lv2") << + QString("/usr/local/lib/lv2") << + QString("/usr/lib64/lv2") << + QString("/usr/lib/lv2"); + setenv("LV2_PATH", MusEGlobal::config.pluginLv2PathList.join(":").toLatin1().constData(), true); + } + else + MusEGlobal::config.pluginLv2PathList = pth.split(":", QString::SkipEmptyParts); + } + else + setenv("LV2_PATH", MusEGlobal::config.pluginLv2PathList.join(":").toLatin1().constData(), true); + + int rv = 0; + bool is_restarting = true; // First-time init true. + while(is_restarting) + { + is_restarting = false; + + // Make working copies of the arguments. + const int argument_count = argc; + int argc_copy = argc; + char** argv_copy = 0; + if(argument_count > 0) + { + argv_copy = (char**)malloc(argument_count * sizeof(char*)); + int len = 0; + for(int i = 0; i < argument_count; ++i) + { + argv_copy[i] = 0; + if(argv[i]) + { + len = strlen(argv[i]); + argv_copy[i] = (char*)malloc(len + 2); + strcpy(argv_copy[i], argv[i]); } - - argc -= optind; - ++argc; - - MusEGlobal::ruid = getuid(); - MusEGlobal::euid = geteuid(); - MusEGlobal::undoSetuid(); - getCapabilities(); - if (MusEGlobal::debugMsg) - printf("Start euid: %d ruid: %d, Now euid %d\n", - MusEGlobal::euid, MusEGlobal::ruid, geteuid()); - - srand(time(0)); // initialize random number generator - //signal(SIGCHLD, catchSignal); // interferes with initVST(). see also app.cpp, function catchSignal() - - static QTranslator translator(0); - QString locale(QLocale::system().name()); - if (locale_override.length() >0 ) - locale = locale_override; - if (locale != "C") { - QString loc("muse_"); - loc += locale; - if (translator.load(loc, QString(".")) == false) { - QString lp(MusEGlobal::museGlobalShare); - lp += QString("/locale"); - if (translator.load(loc, lp) == false) { - printf("no locale <%s>/<%s>\n", loc.toLatin1().constData(), lp.toLatin1().constData()); - } } - app.installTranslator(&translator); - } - printf("LOCALE %s\n",QLocale::system().name().toLatin1().data()); + } - if (QLocale::system().name() == "de" || locale_override == "de") { - printf("locale de - setting override parameter.\n"); - MusEGlobal::hIsB = false; - } - - QApplication::addLibraryPath(MusEGlobal::museGlobalLib + "/qtplugins"); - if (MusEGlobal::debugMsg) { - QStringList list = app.libraryPaths(); - QStringList::Iterator it = list.begin(); - printf("QtLibraryPath:\n"); - while(it != list.end()) { - printf(" <%s>\n", (*it).toLatin1().constData()); - ++it; + // Let LASH remove its recognized arguments first (generally longer than Qt's). + // Tip: LADISH's LASH emulation (current 1.0) does not take any arguments. + #ifdef HAVE_LASH + lash_args_t * lash_args = 0; + lash_args = lash_extract_args (&argc_copy, &argv_copy); + #endif + + // Now create the application, and let Qt remove recognized arguments. + MuseApplication app(argc_copy, argv_copy); + if(QStyle* def_style = app.style()) + { + const QString appStyleObjName = def_style->objectName(); + MusEGui::Appearance::getSetDefaultStyle(&appStyleObjName); + } + + // NOTE: Set the stylesheet and style as early as possible! + // Any later invites trouble - typically the colours may be off, + // but currently with Breeze or Oxygen, MDI sub windows may be frozen! + // Working with Breeze maintainer to fix problem... 2017/06/06 Tim. + MusEGui::updateThemeAndStyle(); + + QString optstr("atJjFAhvdDumMsP:Y:l:py"); + #ifdef VST_SUPPORT + optstr += QString("V"); + #endif + #ifdef VST_NATIVE_SUPPORT + optstr += QString("N"); + #endif + #ifdef DSSI_SUPPORT + optstr += QString("I"); + #endif + #ifdef HAVE_LASH + optstr += QString("L"); + #endif + #ifdef LV2_SUPPORT + optstr += QString("2"); + #endif + + AudioDriverSelect audioType = DriverConfigSetting; + int i; + + // Now read the remaining arguments as our own... + while ((i = getopt(argc_copy, argv_copy, optstr.toLatin1().constData())) != EOF) { + char c = (char)i; + switch (c) { + case 'v': printVersion(argv_copy[0]); + #ifdef HAVE_LASH + if(lash_args) lash_args_destroy(lash_args); + #endif + return 0; + case 'a': + audioType = DummyAudioOverride; + break; + case 't': + audioType = RtAudioOverride; + break; + case 'J': + MusEGlobal::noAutoStartJack = true; + break; + case 'j': + audioType = JackAudioOverride; + break; + case 'F': + MusEGlobal::populateMidiPortsOnStart = false; + break; + case 'A': + MusEGlobal::useAlsaWithJack = true; + break; + case 'd': + MusEGlobal::debugMode = true; + MusEGlobal::realTimeScheduling = false; + break; + case 'D': + if (!MusEGlobal::debugMsg) + MusEGlobal::debugMsg=true; + else + MusEGlobal::heavyDebugMsg=true; + break; + case 'm': MusEGlobal::midiInputTrace = true; break; + case 'M': MusEGlobal::midiOutputTrace = true; break; + case 's': MusEGlobal::debugSync = true; break; + case 'u': MusEGlobal::unityWorkaround = true; break; + case 'P': MusEGlobal::realTimePriority = atoi(optarg); break; + case 'Y': MusEGlobal::midiRTPrioOverride = atoi(optarg); break; + case 'p': MusEGlobal::loadPlugins = false; break; + case 'V': MusEGlobal::loadVST = false; break; + case 'N': MusEGlobal::loadNativeVST = false; break; + case 'I': MusEGlobal::loadDSSI = false; break; + case 'L': MusEGlobal::useLASH = false; break; + case '2': MusEGlobal::loadLV2 = false; break; + case 'y': MusEGlobal::usePythonBridge = true; break; + case 'l': locale_override = QString(optarg); break; + case 'h': usage(argv_copy[0], argv_copy[1]); + #ifdef HAVE_LASH + if(lash_args) lash_args_destroy(lash_args); + #endif + return -1; + default: usage(argv_copy[0], "bad argument"); + #ifdef HAVE_LASH + if(lash_args) lash_args_destroy(lash_args); + #endif + return -1; + } + } + + argc_copy -= optind; + ++argc_copy; + + srand(time(0)); // initialize random number generator + //signal(SIGCHLD, catchSignal); // interferes with initVST(). see also app.cpp, function catchSignal() + + static QTranslator translator(0); + QString locale(QLocale::system().name()); + if (locale_override.length() >0 ) + locale = locale_override; + if (locale != "C") { + QString loc("muse_"); + loc += locale; + if (translator.load(loc, QString(".")) == false) { + QString lp(MusEGlobal::museGlobalShare); + lp += QString("/locale"); + if (translator.load(loc, lp) == false) { + fprintf(stderr, "no locale <%s>/<%s>\n", loc.toLatin1().constData(), lp.toLatin1().constData()); } } + app.installTranslator(&translator); + } + + QLocale def_loc(locale); + QLocale::setDefault(def_loc); + + fprintf(stderr, "LOCALE %s\n",QLocale().name().toLatin1().data()); + + if (QLocale().name() == "de" || locale_override == "de") { + fprintf(stderr, "locale de - setting 'note h is B' override parameter.\n"); + MusEGlobal::hIsB = false; + } - // Create user templates dir if it doesn't exist - QDir utemplDir = QDir(MusEGlobal::configPath + QString("/templates")); - if(!utemplDir.exists()) - { - utemplDir.mkpath("."); - // Support old versions: Copy existing templates over. - QDir old_utemplDir = QDir(QString(getenv("HOME")) + QString("/templates")); - if(old_utemplDir.exists()) - { - // We really just want these, even though it's possible other filenames were saved. - // Another application might have used that directory. - QStringList flt; - flt << "*.med" << "*.med.gz" << "*.med.bz2" << "*.mid" << "*.midi" << "*.kar"; - old_utemplDir.setNameFilters(flt); - - QFileInfoList fil = old_utemplDir.entryInfoList(); - QFileInfo fi; - foreach(fi, fil) + QApplication::addLibraryPath(MusEGlobal::museGlobalLib + "/qtplugins"); + if (MusEGlobal::debugMsg) { + QStringList list = app.libraryPaths(); + QStringList::Iterator it = list.begin(); + fprintf(stderr, "QtLibraryPath:\n"); + while(it != list.end()) { + fprintf(stderr, " <%s>\n", (*it).toLatin1().constData()); + ++it; + } + } + + // Create user templates dir if it doesn't exist + QDir utemplDir = QDir(MusEGlobal::configPath + QString("/templates")); + if(!utemplDir.exists()) + { + utemplDir.mkpath("."); + // Support old versions: Copy existing templates over. + QDir old_utemplDir = QDir(QString(getenv("HOME")) + QString("/templates")); + if(old_utemplDir.exists()) { - QString fn = fi.fileName(); - QFile f(fi.absoluteFilePath()); - f.copy(utemplDir.absolutePath() + "/" + fn); + // We really just want these, even though it's possible other filenames were saved. + // Another application might have used that directory. + QStringList flt; + flt << "*.med" << "*.med.gz" << "*.med.bz2" << "*.mid" << "*.midi" << "*.kar"; + old_utemplDir.setNameFilters(flt); + + QFileInfoList fil = old_utemplDir.entryInfoList(); + QFileInfo fi; + foreach(fi, fil) + { + QString fn = fi.fileName(); + QFile f(fi.absoluteFilePath()); + f.copy(utemplDir.absolutePath() + "/" + fn); + } } } - } - - // Create user instruments dir if it doesn't exist - QString uinstrPath = MusEGlobal::configPath + QString("/instruments"); - QDir uinstrDir = QDir(uinstrPath); - if(!uinstrDir.exists()) - uinstrDir.mkpath("."); - if(!MusEGlobal::config.userInstrumentsDir.isEmpty() && MusEGlobal::config.userInstrumentsDir != uinstrPath) // Only if it is different. - { - // Support old versions: Copy existing instruments over. - QDir old_uinstrDir(MusEGlobal::config.userInstrumentsDir); - if(old_uinstrDir.exists()) - { - QStringList flt; flt << "*.idf"; - old_uinstrDir.setNameFilters(flt); - - QFileInfoList fil = old_uinstrDir.entryInfoList(); - QFileInfo fi; - foreach(fi, fil) + + // Create user instruments dir if it doesn't exist + QString uinstrPath = MusEGlobal::configPath + QString("/instruments"); + QDir uinstrDir = QDir(uinstrPath); + if(!uinstrDir.exists()) + uinstrDir.mkpath("."); + if(!MusEGlobal::config.userInstrumentsDir.isEmpty() && MusEGlobal::config.userInstrumentsDir != uinstrPath) // Only if it is different. + { + // Support old versions: Copy existing instruments over. + QDir old_uinstrDir(MusEGlobal::config.userInstrumentsDir); + if(old_uinstrDir.exists()) { - QString fn = fi.fileName(); - QFile f(fi.absoluteFilePath()); - QFile newf(uinstrDir.absolutePath() + "/" + fn); - if(!newf.exists()) - { - f.copy(newf.fileName()); - } + QStringList flt; flt << "*.idf"; + old_uinstrDir.setNameFilters(flt); + + QFileInfoList fil = old_uinstrDir.entryInfoList(); + QFileInfo fi; + foreach(fi, fil) + { + QString fn = fi.fileName(); + QFile f(fi.absoluteFilePath()); + QFile newf(uinstrDir.absolutePath() + "/" + fn); + if(!newf.exists()) + { + f.copy(newf.fileName()); + } + } } } - } - MusEGlobal::museUserInstruments = uinstrPath; - - // NOTE: May need alternate method, above. - // If setStyle is called after MusE is created, bug: I get transparent background in MDI windows, other artifacts. - // Docs say any style should be set before QApplication created, but this actually works OK up to that point! - QStringList sl = QStyleFactory::keys(); - if (sl.indexOf(MusEGlobal::config.style) != -1) - { - QStyle* style = app.setStyle(MusEGlobal::config.style); - style->setObjectName(MusEGlobal::config.style); - } - - AL::initDsp(); - MusECore::initAudio(); - - MusEGui::initIcons(); + MusEGlobal::museUserInstruments = uinstrPath; - MusECore::initMidiSynth(); // Need to do this now so that Add Track -> Synth menu is populated when MusE is created. - - MusEGlobal::muse = new MusEGui::MusE(); - app.setMuse(MusEGlobal::muse); + AL::initDsp(); + MusECore::initAudio(); - MusEGui::init_function_dialogs(); - MusEGui::retranslate_function_dialogs(); - - // SHOW MUSE SPLASH SCREEN - if (MusEGlobal::config.showSplashScreen) { + MusEGui::initIcons(); + + MusECore::initMidiSynth(); // Need to do this now so that Add Track -> Synth menu is populated when MusE is created. + + MusEGlobal::muse = new MusEGui::MusE(); + app.setMuse(MusEGlobal::muse); + + MusEGui::init_function_dialogs(); + MusEGui::retranslate_function_dialogs(); + + // SHOW MUSE SPLASH SCREEN + if (MusEGlobal::config.showSplashScreen) { QPixmap splsh(MusEGlobal::museGlobalShare + "/splash.png"); if (!splsh.isNull()) { - QSplashScreen* muse_splash = new QSplashScreen(splsh, - Qt::WindowStaysOnTopHint); - muse_splash->setAttribute(Qt::WA_DeleteOnClose); // Possibly also Qt::X11BypassWindowManagerHint - muse_splash->show(); - muse_splash->showMessage("MusE " + QString(VERSION) ); - QTimer* stimer = new QTimer(0); - muse_splash->connect(stimer, SIGNAL(timeout()), muse_splash, SLOT(close())); - stimer->setSingleShot(true); - stimer->start(6000); - QApplication::processEvents(); - } + QSplashScreen* muse_splash = new QSplashScreen(splsh, + Qt::WindowStaysOnTopHint); + muse_splash->setAttribute(Qt::WA_DeleteOnClose); // Possibly also Qt::X11BypassWindowManagerHint + muse_splash->show(); + muse_splash->showMessage("MusE " + QString(VERSION) ); + QTimer* stimer = new QTimer(0); + muse_splash->connect(stimer, SIGNAL(timeout()), muse_splash, SLOT(close())); + stimer->setSingleShot(true); + stimer->start(6000); + QApplication::processEvents(); } + } - if (MusEGlobal::config.useDenormalBias) - printf("Denormal protection enabled.\n"); - if (MusEGlobal::debugMsg) { - printf("global lib: <%s>\n", MusEGlobal::museGlobalLib.toLatin1().constData()); - printf("global share: <%s>\n", MusEGlobal::museGlobalShare.toLatin1().constData()); - printf("muse home: <%s>\n", MusEGlobal::museUser.toLatin1().constData()); - printf("project dir: <%s>\n", MusEGlobal::museProject.toLatin1().constData()); - printf("user instruments: <%s>\n", MusEGlobal::museUserInstruments.toLatin1().constData()); - } - - //rlimit lim; getrlimit(RLIMIT_RTPRIO, &lim); - //printf("RLIMIT_RTPRIO soft:%d hard:%d\n", lim.rlim_cur, lim.rlim_max); // Reported 80, 80 even with non-RT kernel. - if (MusEGlobal::realTimePriority < sched_get_priority_min(SCHED_FIFO)) - MusEGlobal::realTimePriority = sched_get_priority_min(SCHED_FIFO); - else if (MusEGlobal::realTimePriority > sched_get_priority_max(SCHED_FIFO)) - MusEGlobal::realTimePriority = sched_get_priority_max(SCHED_FIFO); - // If we requested to force the midi thread priority... - if(MusEGlobal::midiRTPrioOverride > 0) - { - if (MusEGlobal::midiRTPrioOverride < sched_get_priority_min(SCHED_FIFO)) - MusEGlobal::midiRTPrioOverride = sched_get_priority_min(SCHED_FIFO); - else if (MusEGlobal::midiRTPrioOverride > sched_get_priority_max(SCHED_FIFO)) - MusEGlobal::midiRTPrioOverride = sched_get_priority_max(SCHED_FIFO); - } - - if (MusEGlobal::debugMode) { - MusECore::initDummyAudio(); + if (MusEGlobal::config.useDenormalBias) { + fprintf(stderr, "Denormal protection enabled.\n"); + } + if (MusEGlobal::debugMsg) { + fprintf(stderr, "global lib: <%s>\n", MusEGlobal::museGlobalLib.toLatin1().constData()); + fprintf(stderr, "global share: <%s>\n", MusEGlobal::museGlobalShare.toLatin1().constData()); + fprintf(stderr, "muse home: <%s>\n", MusEGlobal::museUser.toLatin1().constData()); + fprintf(stderr, "project dir: <%s>\n", MusEGlobal::museProject.toLatin1().constData()); + fprintf(stderr, "user instruments: <%s>\n", MusEGlobal::museUserInstruments.toLatin1().constData()); + } + + //rlimit lim; getrlimit(RLIMIT_RTPRIO, &lim); + //fprintf(stderr, "RLIMIT_RTPRIO soft:%d hard:%d\n", lim.rlim_cur, lim.rlim_max); // Reported 80, 80 even with non-RT kernel. + if (MusEGlobal::realTimePriority < sched_get_priority_min(SCHED_FIFO)) + MusEGlobal::realTimePriority = sched_get_priority_min(SCHED_FIFO); + else if (MusEGlobal::realTimePriority > sched_get_priority_max(SCHED_FIFO)) + MusEGlobal::realTimePriority = sched_get_priority_max(SCHED_FIFO); + // If we requested to force the midi thread priority... + if(MusEGlobal::midiRTPrioOverride > 0) + { + if (MusEGlobal::midiRTPrioOverride < sched_get_priority_min(SCHED_FIFO)) + MusEGlobal::midiRTPrioOverride = sched_get_priority_min(SCHED_FIFO); + else if (MusEGlobal::midiRTPrioOverride > sched_get_priority_max(SCHED_FIFO)) + MusEGlobal::midiRTPrioOverride = sched_get_priority_max(SCHED_FIFO); + } + +#ifdef HAVE_LASH + bool using_jack = false; +#endif + if (MusEGlobal::debugMode) { MusEGlobal::realTimeScheduling = false; - } - else if (noAudio) { MusECore::initDummyAudio(); + } + else if (audioType == DummyAudioOverride) { + fprintf(stderr, "Force Dummy Audio driver\n"); MusEGlobal::realTimeScheduling = true; - } - else if (MusECore::initJackAudio()) { - if (!MusEGlobal::debugMode) - { - QMessageBox::critical(NULL, "MusE fatal error", "MusE failed to find a Jack audio server.

    " - "MusE will continue without audio support (-a switch)!

    " - "If this was not intended check that Jack was started. " - "If Jack was started check that it was\n" - "started as the same user as MusE.\n"); - - MusECore::initDummyAudio(); - noAudio = true; - MusEGlobal::realTimeScheduling = true; - if (MusEGlobal::debugMode) { - MusEGlobal::realTimeScheduling = false; - } - } - else - { - fprintf(stderr, "fatal error: no JACK audio server found\n"); - fprintf(stderr, "no audio functions available\n"); - fprintf(stderr, "*** experimental mode -- no play possible ***\n"); - MusECore::initDummyAudio(); - } + MusECore::initDummyAudio(); + } +#ifdef HAVE_RTAUDIO + else if (audioType == RtAudioOverride) { + fprintf(stderr, "Force RtAudio with Pulse Backend\n"); MusEGlobal::realTimeScheduling = true; + if(MusECore::initRtAudio(true)) + fallbackDummy(); + else + fprintf(stderr, "Using rtAudio\n"); + } +#endif + else if (audioType == JackAudioOverride) { + if(MusECore::initJackAudio()) + fallbackDummy(); + else + { +#ifdef HAVE_LASH + using_jack = true; +#endif + fprintf(stderr, "...Using Jack\n"); + } + } + else if (audioType == DriverConfigSetting) { + fprintf(stderr, "Select audio device from configuration : %d\n", MusEGlobal::config.deviceAudioBackend); + switch (MusEGlobal::config.deviceAudioBackend) { + case MusEGlobal::DummyAudio: + { + fprintf(stderr, "User DummyAudio backend - selected through configuration\n"); + MusEGlobal::realTimeScheduling = true; + MusECore::initDummyAudio(); + break; + } + case MusEGlobal::RtAudioAlsa: + case MusEGlobal::RtAudioOss: +// case MusEGlobal::RtAudioJack: + case MusEGlobal::RtAudioChoice: + case MusEGlobal::RtAudioPulse: + { + fprintf(stderr, "User RtAudio backend - backend selected through configuration: "); + if(MusEGlobal::config.deviceAudioBackend >= MusEGlobal::numRtAudioDevices) + fprintf(stderr, "Unknown"); + else + fprintf(stderr, "%s", + MusEGlobal::selectableAudioBackendDevices[MusEGlobal::config.deviceAudioBackend]. + toLatin1().constData()); + fprintf(stderr, "\n"); + + MusEGlobal::realTimeScheduling = true; +#ifdef HAVE_RTAUDIO + if(MusECore::initRtAudio()) + fallbackDummy(); + else + fprintf(stderr, "Using rtAudio\n"); +#else + fallbackDummy(); +#endif + break; + } + case MusEGlobal::JackAudio: + { + fprintf(stderr, "User JackAudio backend - backend selected through configuration\n"); + if (MusECore::initJackAudio()) + { + MusEGlobal::realTimeScheduling = true; + // Force default Pulse. +#ifdef HAVE_RTAUDIO + if(MusECore::initRtAudio(true)) + fallbackDummy(); + else + fprintf(stderr, "Using rtAudio Pulse\n"); +#else + fallbackDummy(); +#endif + } + else + { +#ifdef HAVE_LASH + using_jack = true; +#endif + fprintf(stderr, "Using Jack\n"); + } + + break; + } + } + } + + MusEGlobal::realTimeScheduling = MusEGlobal::audioDevice->isRealtime(); + + // ??? With Jack2 this reports true even if it is not running realtime. + // Jack says: "Cannot use real-time scheduling (RR/10)(1: Operation not permitted)". The kernel is non-RT. + // I cannot seem to find a reliable answer to the question, even with dummy audio and system calls. + + MusEGlobal::useJackTransport.setValue(true); + + // setup the prefetch fifo length now that the segmentSize is known + MusEGlobal::fifoLength = 131072 / MusEGlobal::segmentSize; + MusECore::initAudioPrefetch(); + + // WARNING Must do it this way. Call registerClient long AFTER Jack client is created and MusE ALSA client is + // created (in initMidiDevices), otherwise random crashes can occur within Jack <= 1.9.8. Fixed in Jack 1.9.9. Tim. + // This initMidiDevices will automatically initialize the midiSeq sequencer thread, but not start it - that's a bit later on. + MusECore::initMidiDevices(); + // Wait until things have settled. One second seems OK so far. + for(int t = 0; t < 100; ++t) + usleep(10000); + // Now it is safe to call registerClient. + MusEGlobal::audioDevice->registerClient(); + + MusECore::initMidiController(); + MusECore::initMidiInstruments(); + MusECore::initMidiPorts(); + + if (MusEGlobal::loadPlugins) + MusECore::initPlugins(); + + if (MusEGlobal::loadVST) + MusECore::initVST(); + + if (MusEGlobal::loadNativeVST) + MusECore::initVST_Native(); + + if(MusEGlobal::loadDSSI) + MusECore::initDSSI(); + #ifdef LV2_SUPPORT + if(MusEGlobal::loadLV2) + MusECore::initLV2(); + #endif + + MusECore::initOSC(); + + MusECore::initMetronome(); + + MusECore::initWavePreview(MusEGlobal::segmentSize); + + MusECore::enumerateJackMidiDevices(); + + #ifdef HAVE_LASH + { + MusEGui::lash_client = 0; + if(MusEGlobal::useLASH) + { + int lash_flags = LASH_Config_File; + const char *muse_name = PACKAGE_NAME; + MusEGui::lash_client = lash_init (lash_args, muse_name, lash_flags, LASH_PROTOCOL(2,0)); + #ifdef ALSA_SUPPORT + if(MusECore::alsaSeq) + lash_alsa_client_id (MusEGui::lash_client, snd_seq_client_id (MusECore::alsaSeq)); + #endif + //if (audioType != DummyAudio) { + if (using_jack) { + const char *jack_name = MusEGlobal::audioDevice->clientName(); + lash_jack_client_name (MusEGui::lash_client, jack_name); } - else - MusEGlobal::realTimeScheduling = MusEGlobal::audioDevice->isRealtime(); + } + if(lash_args) + lash_args_destroy(lash_args); + } + #endif /* HAVE_LASH */ - // ??? With Jack2 this reports true even if it is not running realtime. - // Jack says: "Cannot use real-time scheduling (RR/10)(1: Operation not permitted)". The kernel is non-RT. - // I cannot seem to find a reliable answer to the question, even with dummy audio and system calls. - - MusEGlobal::useJackTransport.setValue(true); - - // setup the prefetch fifo length now that the segmentSize is known - MusEGlobal::fifoLength = 131072 / MusEGlobal::segmentSize; - MusECore::initAudioPrefetch(); - - // WARNING Must do it this way. Call registerClient long AFTER Jack client is created and MusE ALSA client is - // created (in initMidiDevices), otherwise random crashes can occur within Jack <= 1.9.8. Fixed in Jack 1.9.9. Tim. - MusECore::initMidiDevices(); - // Wait until things have settled. One second seems OK so far. - for(int t = 0; t < 100; ++t) - usleep(10000); - // Now it is safe to call registerClient. - MusEGlobal::audioDevice->registerClient(); - - MusECore::initMidiController(); - MusECore::initMidiInstruments(); - MusECore::initMidiPorts(); - MusECore::initMidiSequencer(); - MusEGlobal::midiSeq->checkAndReportTimingResolution(); - - if (MusEGlobal::loadPlugins) - MusECore::initPlugins(); + if (!MusEGlobal::debugMode) { + if (mlockall(MCL_CURRENT | MCL_FUTURE)) + perror("WARNING: Cannot lock memory:"); + } - if (MusEGlobal::loadVST) - MusECore::initVST(); + MusEGlobal::muse->populateAddTrack(); // could possibly be done in a thread. - if (MusEGlobal::loadNativeVST) - MusECore::initVST_Native(); + MusEGlobal::muse->show(); - if(MusEGlobal::loadDSSI) - MusECore::initDSSI(); - - MusECore::initOSC(); - - MusECore::initMetronome(); - -#ifdef HAVE_LASH - { - MusEGui::lash_client = 0; - if(MusEGlobal::useLASH) + // Let the configuration settings take effect. Do not save. + MusEGlobal::muse->changeConfig(false); + // Set style and stylesheet, and do not force the style + //MusEGui::updateThemeAndStyle(); // Works better if called just after app created, above. + + MusEGlobal::muse->seqStart(); + + // If the sequencer object was created, report timing. + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->checkAndReportTimingResolution(); + + //-------------------------------------------------- + // Auto-fill the midi ports, if appropriate. + //-------------------------------------------------- + if(MusEGlobal::populateMidiPortsOnStart && + argc_copy < 2 && + (MusEGlobal::config.startMode == 1 || MusEGlobal::config.startMode == 2) && + !MusEGlobal::config.startSongLoadConfig) + MusECore::populateMidiPorts(); + + //-------------------------------------------------- + // Load the default song. + //-------------------------------------------------- + MusEGlobal::muse->loadDefaultSong(argc_copy, &argv_copy[optind]); + + QTimer::singleShot(100, MusEGlobal::muse, SLOT(showDidYouKnowDialog())); + + rv = app.exec(); + if(MusEGlobal::debugMsg) + fprintf(stderr, "app.exec() returned:%d\nDeleting main MusE object\n", rv); + + if (MusEGlobal::loadPlugins) { - int lash_flags = LASH_Config_File; - const char *muse_name = PACKAGE_NAME; - MusEGui::lash_client = lash_init (lash_args, muse_name, lash_flags, LASH_PROTOCOL(2,0)); - if(MusECore::alsaSeq) - lash_alsa_client_id (MusEGui::lash_client, snd_seq_client_id (MusECore::alsaSeq)); - if (!noAudio) { - const char *jack_name = MusEGlobal::audioDevice->clientName(); - lash_jack_client_name (MusEGui::lash_client, jack_name); - } + for (MusECore::iPlugin i = MusEGlobal::plugins.begin(); i != MusEGlobal::plugins.end(); ++i) + delete (*i); + MusEGlobal::plugins.clear(); } - if(lash_args) - lash_args_destroy(lash_args); - } -#endif /* HAVE_LASH */ - if (!MusEGlobal::debugMode) { - if (mlockall(MCL_CURRENT | MCL_FUTURE)) - perror("WARNING: Cannot lock memory:"); - } - - MusEGlobal::muse->show(); - MusEGlobal::muse->seqStart(); - - //-------------------------------------------------- - // Auto-fill the midi ports, if appropriate. - //-------------------------------------------------- - if(argc < 2 && (MusEGlobal::config.startMode == 1 || MusEGlobal::config.startMode == 2) && !MusEGlobal::config.startSongLoadConfig) - { - MusEGui::populateMidiPorts(); - //MusEGlobal::muse->changeConfig(true); // save configuration file - //MusEGlobal::song->update(); - } - - //-------------------------------------------------- - // Load the default song. - //-------------------------------------------------- - MusEGlobal::muse->loadDefaultSong(argc, &argv[optind]); + MusECore::exitWavePreview(); + + #ifdef LV2_SUPPORT + if(MusEGlobal::loadLV2) + MusECore::deinitLV2(); + #endif + + // In case the sequencer object is still alive, make sure to destroy it now. + MusECore::exitMidiSequencer(); + + // Grab the restart flag before deleting muse. + is_restarting = MusEGlobal::muse->restartingApp(); + + // Now delete the application. + delete MusEGlobal::muse; + + // These are owned by muse and deleted above. Reset to zero now. + MusEGlobal::undoRedo = 0; + MusEGlobal::undoAction = 0; + MusEGlobal::redoAction = 0; + + // Reset the option index. + // NOTE: See optind manual for special resetting values. + // Traditionally 1 is set, but here we may need GNU specific 0. + optind = 0; + + // Free the working copies of the arguments. + if(argv_copy) + { + for(int i = 0; i < argument_count; ++i) + { + if(argv_copy[i]) + free(argv_copy[i]); + } + free(argv_copy); + } + + // Reset these before restarting, seems to work better, + // makes a difference with the MDI freezing problem, above. + app.setStyleSheet(""); + app.setStyle(MusEGlobal::config.style); + + // Reset the recently opened list. + MusEGui::projectRecentList.clear(); + } - QTimer::singleShot(100, MusEGlobal::muse, SLOT(showDidYouKnowDialog())); - - int rv = app.exec(); - if(MusEGlobal::debugMsg) - printf("app.exec() returned:%d\nDeleting main MusE object\n", rv); - delete MusEGlobal::muse; - if(MusEGlobal::debugMsg) - printf("Finished! Exiting main, return value:%d\n", rv); + fprintf(stderr, "Finished! Exiting main, return value:%d\n", rv); return rv; } diff -Nru muse-2.1.2/muse/marker/CMakeLists.txt muse-3.0.2+ds1/muse/marker/CMakeLists.txt --- muse-2.1.2/muse/marker/CMakeLists.txt 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/marker/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( marker_mocs +QT5_WRAP_CPP ( marker_mocs markerview.h ) diff -Nru muse-2.1.2/muse/marker/markerview.cpp muse-3.0.2+ds1/muse/marker/markerview.cpp --- muse-2.1.2/muse/marker/markerview.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/marker/markerview.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -105,7 +105,7 @@ void MarkerItem::setLock(bool lck) { - setIcon(COL_LOCK, QIcon(lck ? *lockIcon : 0)); + setIcon(COL_LOCK, lck ? QIcon(*lockIcon) : QIcon()); _marker = MusEGlobal::song->setMarkerLock(_marker, lck); } @@ -121,7 +121,10 @@ int bar, beat; unsigned tick; AL::sigmap.tickValues(v, &bar, &beat, &tick); - s.sprintf("%04d.%02d.%03d", bar+1, beat+1, tick); + s = QString("%1.%2.%3") + .arg(bar + 1, 4, 10, QLatin1Char('0')) + .arg(beat + 1, 2, 10, QLatin1Char('0')) + .arg(tick, 3, 10, QLatin1Char('0')); setText(COL_TICK, s); double time = double(MusEGlobal::tempomap.tick2frame(v))/double(MusEGlobal::sampleRate); @@ -145,8 +148,12 @@ } int frame = int(rest); int subframe = int((rest-frame)*100); - s.sprintf("%02d:%02d:%02d:%02d:%02d", - hour, min, sec, frame, subframe); + s = QString("%1:%2:%3:%4:%5") + .arg(hour, 2, 10, QLatin1Char('0')) + .arg(min, 2, 10, QLatin1Char('0')) + .arg(sec, 2, 10, QLatin1Char('0')) + .arg(frame, 2, 10, QLatin1Char('0')) + .arg(subframe, 2, 10, QLatin1Char('0')); setText(COL_SMPTE, s); } @@ -171,10 +178,10 @@ setWindowTitle(tr("MusE: Marker")); QAction* markerAdd = new QAction(QIcon(*flagIcon), tr("add marker"), this); - connect(markerAdd, SIGNAL(activated()), SLOT(addMarker())); + connect(markerAdd, SIGNAL(triggered()), SLOT(addMarker())); QAction* markerDelete = new QAction(QIcon(*deleteIcon), tr("delete marker"), this); - connect(markerDelete, SIGNAL(activated()), SLOT(deleteMarker())); + connect(markerDelete, SIGNAL(triggered()), SLOT(deleteMarker())); //---------Pulldown Menu---------------------------- QMenu* editMenu = menuBar()->addMenu(tr("&Edit")); @@ -190,6 +197,14 @@ // Toolbars --------------------------------------------------------- + + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + QToolBar* edit = addToolBar(tr("edit tools")); edit->setObjectName("marker edit tools"); edit->addAction(markerAdd); @@ -413,12 +428,8 @@ // songChanged //--------------------------------------------------------- -void MarkerView::songChanged(MusECore::SongChangedFlags_t flags) +void MarkerView::songChanged(MusECore::SongChangedFlags_t /*flags*/) { - // Is it simply a midi controller value adjustment? Forget it. - if(flags == SC_MIDI_CONTROLLER) - return; - updateList(); } diff -Nru muse-2.1.2/muse/master/CMakeLists.txt muse-3.0.2+ds1/muse/master/CMakeLists.txt --- muse-2.1.2/muse/master/CMakeLists.txt 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/master/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( master_mocs +QT5_WRAP_CPP ( master_mocs lmaster.h masteredit.h master.h diff -Nru muse-2.1.2/muse/master/lmaster.cpp muse-3.0.2+ds1/muse/master/lmaster.cpp --- muse-2.1.2/muse/master/lmaster.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/master/lmaster.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -174,7 +174,7 @@ tempoAction = menuEdit->addAction(tr("Insert Tempo")); signAction = menuEdit->addAction(tr("Insert Signature")); keyAction = menuEdit->addAction(tr("Insert Key")); - posAction = menuEdit->addAction(tr("Edit Positon")); + posAction = menuEdit->addAction(tr("Edit Position")); valAction = menuEdit->addAction(tr("Edit Value")); delAction = menuEdit->addAction(tr("Delete Event")); delAction->setShortcut(Qt::Key_Delete); @@ -202,6 +202,16 @@ connect(signalMapper, SIGNAL(mapped(int)), SLOT(cmd(int))); // Toolbars --------------------------------------------------------- + + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + + addToolBarBreak(); + QToolBar* edit = addToolBar(tr("Edit tools")); edit->setObjectName("Master List Edit Tools"); QToolButton* tempoButton = new QToolButton(); @@ -868,13 +878,19 @@ int bar, beat; unsigned tick; AL::sigmap.tickValues(t, &bar, &beat, &tick); - c1.sprintf("%04d.%02d.%03d", bar+1, beat+1, tick); + c1 = QString("%1.%2.%3") + .arg(bar + 1, 4, 10, QLatin1Char('0')) + .arg(beat + 1, 2, 10, QLatin1Char('0')) + .arg(tick, 3, 10, QLatin1Char('0')); double time = double(MusEGlobal::tempomap.tick2frame(t)) / double(MusEGlobal::sampleRate); int min = int(time) / 60; int sec = int(time) % 60; int msec = int((time - (min*60 + sec)) * 1000.0); - c2.sprintf("%03d:%02d:%03d", min, sec, msec); + c2 = QString("%1:%2:%3") + .arg(min, 3, 10, QLatin1Char('0')) + .arg(sec, 2, 10, QLatin1Char('0')) + .arg(msec, 3, 10, QLatin1Char('0')); c3 = "Key"; c4 = keyToString(ev.key); setText(0, c1); @@ -897,13 +913,19 @@ int bar, beat; unsigned tick; AL::sigmap.tickValues(t, &bar, &beat, &tick); - c1.sprintf("%04d.%02d.%03d", bar+1, beat+1, tick); + c1 = QString("%1.%2.%3") + .arg(bar + 1, 4, 10, QLatin1Char('0')) + .arg(beat + 1, 2, 10, QLatin1Char('0')) + .arg(tick, 3, 10, QLatin1Char('0')); double time = double(MusEGlobal::tempomap.tick2frame(t)) / double(MusEGlobal::sampleRate); int min = int(time) / 60; int sec = int(time) % 60; int msec = int((time - (min*60 + sec)) * 1000.0); - c2.sprintf("%03d:%02d:%03d", min, sec, msec); + c2 = QString("%1:%2:%3") + .arg(min, 3, 10, QLatin1Char('0')) + .arg(sec, 2, 10, QLatin1Char('0')) + .arg(msec, 3, 10, QLatin1Char('0')); c3 = "Tempo"; double dt = (1000000.0 * 60.0)/ev->tempo; c4.setNum(dt, 'f', 3); @@ -925,15 +947,21 @@ int bar, beat; unsigned tick; AL::sigmap.tickValues(t, &bar, &beat, &tick); - c1.sprintf("%04d.%02d.%03d", bar+1, beat+1, tick); + c1 = QString("%1.%2.%3") + .arg(bar + 1, 4, 10, QLatin1Char('0')) + .arg(beat + 1, 2, 10, QLatin1Char('0')) + .arg(tick, 3, 10, QLatin1Char('0')); double time = double(MusEGlobal::tempomap.tick2frame(t)) / double (MusEGlobal::sampleRate); int min = int(time) / 60; int sec = int(time) % 60; int msec = int((time - (min*60 + sec)) * 1000.0); - c2.sprintf("%03d:%02d:%03d", min, sec, msec); + c2 = QString("%1:%2:%3") + .arg(min, 3, 10, QLatin1Char('0')) + .arg(sec, 2, 10, QLatin1Char('0')) + .arg(msec, 3, 10, QLatin1Char('0')); c3 = "Timesig"; - c4.sprintf("%d / %d", ev->sig.z, ev->sig.n); + c4 = QString("%1 / %2").arg(ev->sig.z).arg(ev->sig.n); setText(0, c1); setText(1, c2); setText(2, c3); diff -Nru muse-2.1.2/muse/master/master.cpp muse-3.0.2+ds1/muse/master/master.cpp --- muse-2.1.2/muse/master/master.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/master/master.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -206,6 +206,7 @@ p.setPen(Qt::blue); p.drawLine(xp, y, xp, y+h); } + } //--------------------------------------------------------- @@ -216,17 +217,71 @@ { drawTickRaster(p, rect.x(), rect.y(), rect.width(), rect.height(), 0); + + if ((tool == MusEGui::DrawTool) && drawLineMode) { + p.setPen(Qt::black); + p.drawLine(line1x, line1y, line2x, line2y); + p.drawLine(line1x, line1y+1, line2x, line2y+1); + } } //--------------------------------------------------------- +// newValRamp +//--------------------------------------------------------- + +void Master::newValRamp(int x1, int y1, int x2, int y2) +{ + MusECore::Undo operations; + +// loop through all tick positions between x1 and x2 +// remove all tempo changes and add new ones for changed + + int tickStart = editor->rasterVal1(x1); + int tickEnd = editor->rasterVal2(x2); + + const MusECore::TempoList* tl = &MusEGlobal::tempomap; + for (MusECore::ciTEvent i = tl->begin(); i != tl->end(); ++i) { + MusECore::TEvent* e = i->second; + int startOldTick = i->second->tick; + //if (startOldTick > tickStart && startOldTick <= tickEnd ) { // REMOVE Tim. Sharing. Wrong comparison? + if (startOldTick >= tickStart && startOldTick > 0 && startOldTick < tickEnd ) { // Erasure at tick 0 forbidden. + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteTempo, startOldTick, e->tempo)); + //printf("tempo = %f %d %d\n", 60000000.0/e->tempo, endOldTick, startOldTick); + } + } + + int priorTick = editor->rasterVal1(x1); + int tempoVal = int(60000000000.0/(280000 - y1)); + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTempo, tickStart, tempoVal)); + int tick = editor->rasterVal1(x1); + for (int i = x1; tick < tickEnd; i++) { + tick = editor->rasterVal1(i); + if (tick > priorTick) { + double xproportion = double(tick-tickStart)/double(tickEnd-tickStart); + int yproportion = double(y2 - y1) * xproportion; + int yNew = y1 + yproportion; + int tempoVal = int(60000000000.0/(280000 - yNew)); + //printf("tickStart %d tickEnd %d yNew %d xproportion %f yproportion %d\n", tickStart, tickEnd, yNew, xproportion, yproportion); + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTempo, tick, tempoVal)); + priorTick = tick; + } + } + + MusEGlobal::song->applyOperationGroup(operations); +} + + +//--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- void Master::viewMousePressEvent(QMouseEvent* event) { start = event->pos(); + int xpos = start.x(); + int ypos = start.y(); + MusEGui::Tool activeTool = tool; -// bool shift = event->state() & ShiftButton; switch (activeTool) { case MusEGui::PointerTool: @@ -245,6 +300,20 @@ deleteVal(start.x(), start.x()); break; + case MusEGui::DrawTool: + if (drawLineMode) { + line2x = xpos; + line2y = ypos; + newValRamp(line1x, line1y, line2x, line2y); + drawLineMode = false; + } + else { + line2x = line1x = xpos; + line2y = line1y = ypos; + drawLineMode = true; + } + redraw(); + break; default: break; } @@ -257,9 +326,12 @@ void Master::viewMouseMoveEvent(QMouseEvent* event) { QPoint pos = event->pos(); -// QPoint dist = pos - start; -// bool moving = dist.y() >= 3 || dist.y() <= 3 || dist.x() >= 3 || dist.x() <= 3; DELETETHIS - + if (tool == MusEGui::DrawTool && drawLineMode) { + line2x = pos.x(); + line2y = pos.y(); + redraw(); + return; + } switch (drag) { case DRAG_NEW: newVal(start.x(), pos.x(), pos.y()); @@ -349,6 +421,9 @@ case MusEGui::PencilTool: setCursor(QCursor(*pencilIcon, 4, 15)); break; + case MusEGui::DrawTool: + drawLineMode = false; + break; default: setCursor(QCursor(Qt::ArrowCursor)); break; @@ -373,5 +448,4 @@ MusEGlobal::audio->msgAddTempo(xx1, int(60000000000.0/(280000 - y)), false); redraw(); } - } // namespace MusEGui diff -Nru muse-2.1.2/muse/master/masteredit.cpp muse-3.0.2+ds1/muse/master/masteredit.cpp --- muse-2.1.2/muse/master/masteredit.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/master/masteredit.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -116,7 +116,18 @@ settingsMenu->addAction(fullscreenAction); // Toolbars --------------------------------------------------------- - MusEGui::EditToolBar* tools2 = new MusEGui::EditToolBar(this, MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool); + + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + + addToolBarBreak(); + + // Already has an object name. + MusEGui::EditToolBar* tools2 = new MusEGui::EditToolBar(this, MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool| MusEGui::DrawTool); addToolBar(tools2); QToolBar* enableMaster = addToolBar(tr("Enable master")); @@ -131,7 +142,8 @@ connect(enableButton, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setMasterFlag(bool))); QToolBar* info = addToolBar(tr("Info")); - info->setObjectName("Info"); + // Make it appear like a Toolbar1 object. + info->setObjectName("Pos/Snap/Solo-tools"); QLabel* label = new QLabel(tr("Cursor")); label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); label->setIndent(3); @@ -285,13 +297,14 @@ case MusECore::Xml::TagStart: if (tag == "midieditor") MidiEditor::readStatus(xml); + else if (tag == "xpos") + hscroll->setPos(xml.parseInt()); + else if (tag == "xmag") + hscroll->setMag(xml.parseInt()); else if (tag == "ypos") vscroll->setPos(xml.parseInt()); - else if (tag == "ymag") { - // vscroll->setMag(xml.parseInt()); - int mag = xml.parseInt(); - vscroll->setMag(mag); - } + else if (tag == "ymag") + vscroll->setMag(xml.parseInt()); else xml.unknown("MasterEdit"); break; @@ -324,8 +337,10 @@ void MasterEdit::writeStatus(int level, MusECore::Xml& xml) const { xml.tag(level++, "master"); - xml.intTag(level, "ypos", vscroll->pos()); + xml.intTag(level, "xmag", hscroll->mag()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", vscroll->mag()); + xml.intTag(level, "ypos", vscroll->pos()); MidiEditor::writeStatus(level, xml); xml.tag(level, "/master"); } diff -Nru muse-2.1.2/muse/master/master.h muse-3.0.2+ds1/muse/master/master.h --- muse-2.1.2/muse/master/master.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/master/master.h 2017-12-04 21:01:18.000000000 +0000 @@ -57,6 +57,11 @@ DragMode drag; MidiEditor* editor; + int line1x; + int line1y; + int line2x; + int line2y; + bool drawLineMode; virtual void pdraw(QPainter&, const QRect&); virtual void viewMouseMoveEvent(QMouseEvent* event); @@ -69,6 +74,8 @@ bool deleteVal1(unsigned int x1, unsigned int x2); void deleteVal(int x1, int x2); + void newValRamp(int x1, int y1, int x2, int y2); + private slots: void songChanged(MusECore::SongChangedFlags_t); diff -Nru muse-2.1.2/muse/memory.cpp muse-3.0.2+ds1/muse/memory.cpp --- muse-2.1.2/muse/memory.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/memory.cpp 2017-12-17 21:07:38.000000000 +0000 @@ -22,7 +22,10 @@ //========================================================= #include "memory.h" +#include +// NOTE: Keep this code in case we need a dimensioned pool! +#if 0 Pool audioRTmemoryPool; Pool midiRTmemoryPool; @@ -78,6 +81,175 @@ head[idx] = reinterpret_cast(start); } +#endif + +//--------------------------------------------------------- +// MemoryQueue +//--------------------------------------------------------- + +MemoryQueue::MemoryQueue() +{ + _endChunk = 0; + _curSize = 0; + _curOffest = 0; + + // Preallocate. + grow(); + // Remember the very first chunk. + _startChunk = _endChunk; + // Start writing from the first chunk. + _curWriteChunk = _startChunk; +} + +//--------------------------------------------------------- +// ~MemoryQueue +//--------------------------------------------------------- + +MemoryQueue::~MemoryQueue() +{ + // Delete all chunks. + Chunk* n = _startChunk; + while(n) + { + Chunk* p = n; + n = n->_next; + delete p; + } +} + +//--------------------------------------------------------- +// clear +//--------------------------------------------------------- + +void MemoryQueue::clear() +{ + // Delete all chunks except the first one, keep it around. That avoids an initial grow. + if(_startChunk) + { + Chunk* n = _startChunk->_next; + while(n) + { + Chunk* p = n; + n = n->_next; + delete p; + } + } + _endChunk = _startChunk; + // Reset the writer. + reset(); +} + +//--------------------------------------------------------- +// reset +//--------------------------------------------------------- + +void MemoryQueue::reset() +{ + // Start writing from the first chunk. + _curWriteChunk = _startChunk; + _curSize = 0; + _curOffest = 0; +} + +//--------------------------------------------------------- +// grow +//--------------------------------------------------------- + +void MemoryQueue::grow() +{ + Chunk* n = new Chunk(); + //Chunk* n = new Chunk(_chunkSize); + n->_next = 0; + if(_endChunk) + _endChunk->_next = n; + _endChunk = n; +} + +//--------------------------------------------------------- +// add +// Return true if successful. +//--------------------------------------------------------- + +bool MemoryQueue::add(const unsigned char* src, size_t len) +{ + if(!src || len == 0 || !_curWriteChunk) + return false; + const unsigned char* pp = src; + size_t remain = len; + size_t bytes; + while(true) + { + bytes = Chunk::ChunkSize - _curOffest; + if(remain < bytes) + bytes = remain; + memcpy(_curWriteChunk->_mem + _curOffest, pp, bytes); + _curSize += bytes; + _curOffest += bytes; + if(_curOffest == Chunk::ChunkSize) + { + _curOffest = 0; + // Does the next chunk exist? + if(_endChunk->_next) + { + // Advance the current write chunk to the next existing chunk. + _curWriteChunk = _endChunk->_next; + } + else + { + // Create a new chunk. + grow(); + // The _endChunk changed. Advance the current write chunk to it. + _curWriteChunk = _endChunk; + } + } + remain -= bytes; + // No more remaining? Done. + if(remain == 0) + break; + // Advance the source read pointer. + pp += bytes; + } + return true; +} + +//--------------------------------------------------------- +// copy +// Return true if successful. +//--------------------------------------------------------- + +// Return true if successful. +size_t MemoryQueue::copy(unsigned char* dst, size_t len) const +{ + if(!dst || len == 0 || _curSize == 0 || !_startChunk) + return 0; + // Limit number of requested bytes to actual available size. + if(len > _curSize) + len = _curSize; + unsigned char* pp = dst; + size_t remain = len; + size_t bytes; + // Start reading at the very first chunk. + Chunk* read_chunk = _startChunk; + while(true) + { + bytes = Chunk::ChunkSize; + if(remain < bytes) + bytes = remain; + memcpy(pp, read_chunk->_mem, bytes); + remain -= bytes; + // No more remaining? Done. + if(remain == 0) + break; + // The next chunk must already exist. + if(!read_chunk->_next) + break; + // Advance the read chunk to the next existing chunk. + read_chunk = read_chunk->_next; + // Advance the destination write pointer. + pp += bytes; + } + return len - remain; +} #ifdef TEST //========================================================= diff -Nru muse-2.1.2/muse/memory.h muse-3.0.2+ds1/muse/memory.h --- muse-2.1.2/muse/memory.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/memory.h 2017-12-17 21:07:38.000000000 +0000 @@ -26,9 +26,9 @@ #include #include -#include -#include +// NOTE: Keep this code in case we need a dimensioned pool! +#if 0 // most of the following code is based on examples // from Bjarne Stroustrup: "Die C++ Programmiersprache" @@ -41,11 +41,12 @@ Verweis* next; }; struct Chunk { - enum { size = 4 * 1024 }; + enum { size = 4 * 2048 }; Chunk* next; char mem[size]; }; - enum { dimension = 21 }; + // Gives about 300 bytes maximum request @ 8 bytes item size. + enum { dimension = 40 }; Chunk* chunks[dimension]; Verweis* head[dimension]; Pool(Pool&); @@ -100,97 +101,143 @@ extern Pool audioRTmemoryPool; extern Pool midiRTmemoryPool; +#endif + + //--------------------------------------------------------- -// audioRTalloc +// TypedMemoryPool +// Most of the following code is based on examples +// from Bjarne Stroustrup: "Die C++ Programmiersprache" //--------------------------------------------------------- -template class audioRTalloc +template class TypedMemoryPool +{ + struct Verweis { + Verweis* next; + }; + struct Chunk { + enum { size = itemsPerChunk * sizeof(T) }; + Chunk* next; + char mem[size]; + }; + Chunk* chunks; + Verweis* head; + TypedMemoryPool(TypedMemoryPool&); + void operator=(TypedMemoryPool&); + + void grow() { - public: - typedef T value_type; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - - typedef T* pointer; - typedef const T* const_pointer; - - typedef T& reference; - typedef const T& const_reference; - - pointer address(reference x) const { return &x; } - const_pointer address(const_reference x) const { return &x; } - - audioRTalloc(); - template audioRTalloc(const audioRTalloc&) {} - ~audioRTalloc() {} - - pointer allocate(size_type n, void * = 0) { - return static_cast(audioRTmemoryPool.alloc(n * sizeof(T))); - } - void deallocate(pointer p, size_type n) { - audioRTmemoryPool.free(p, n * sizeof(T)); - } - - audioRTalloc& operator=(const audioRTalloc&) { return *this; } - void construct(pointer p, const T& val) { - new ((T*) p) T(val); - } - void destroy(pointer p) { - p->~T(); - } - size_type max_size() const { return size_t(-1); } - - template struct rebind { typedef audioRTalloc other; }; - template audioRTalloc& operator=(const audioRTalloc&) { return *this; } - }; + const int esize = sizeof(T); + Chunk* n = new Chunk; + n->next = chunks; + chunks = n; + const int nelem = Chunk::size / esize; + char* start = n->mem; + char* last = &start[(nelem-1) * esize]; + for(char* p = start; p < last; p += esize) + reinterpret_cast(p)->next = + reinterpret_cast(p + esize); + reinterpret_cast(last)->next = 0; + head = reinterpret_cast(start); + } -template audioRTalloc::audioRTalloc() {} + public: + TypedMemoryPool() + { + head = 0; + chunks = 0; + grow(); // preallocate + } + + ~TypedMemoryPool() + { + Chunk* n = chunks; + while (n) + { + Chunk* p = n; + n = n->next; + delete p; + } + } + + void* alloc(size_t items) + { + if(items == 0) + return 0; + if(items != 1) + { + printf("panic: TypedMemoryPool::alloc items requested:%u != 1\n", + (unsigned int)items); + exit(-1); + } + if(head == 0) + grow(); + Verweis* p = head; + head = p->next; + return p; + } + + void free(void* b, size_t items) + { + if(b == 0 || items == 0) + return; + if(items != 1) + { + printf("panic: TypedMemoryPool::free items requested:%u != 1\n", + (unsigned int)items); + exit(-1); + } + Verweis* p = static_cast(b); + p->next = head; + head = p; + } +}; //--------------------------------------------------------- -// midiRTalloc +// MemoryQueue +// An efficient queue which grows by fixed chunk sizes, +// for single threads only. //--------------------------------------------------------- -template class midiRTalloc +class MemoryQueue { + struct Chunk { - public: - typedef T value_type; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - - typedef T* pointer; - typedef const T* const_pointer; - - typedef T& reference; - typedef const T& const_reference; - - pointer address(reference x) const { return &x; } - const_pointer address(const_reference x) const { return &x; } - - midiRTalloc(); - template midiRTalloc(const midiRTalloc&) {} - ~midiRTalloc() {} - - pointer allocate(size_type n, void * = 0) { - return static_cast(midiRTmemoryPool.alloc(n * sizeof(T))); - } - void deallocate(pointer p, size_type n) { - midiRTmemoryPool.free(p, n * sizeof(T)); - } + enum { ChunkSize = 8 * 1024 }; + Chunk* _next; + char _mem[ChunkSize]; + }; + Chunk* _startChunk; + Chunk* _endChunk; + Chunk* _curWriteChunk; + size_t _curSize; + size_t _curOffest; + + MemoryQueue(MemoryQueue&); + void operator=(MemoryQueue&); + void grow(); - midiRTalloc& operator=(const midiRTalloc&) { return *this; } - void construct(pointer p, const T& val) { - new ((T*) p) T(val); - } - void destroy(pointer p) { - p->~T(); - } - size_type max_size() const { return size_t(-1); } + public: + MemoryQueue(); + ~MemoryQueue(); - template struct rebind { typedef midiRTalloc other; }; - template midiRTalloc& operator=(const midiRTalloc&) { return *this; } + // Static. Returns whether the given length in bytes needs to be chunked. + static bool chunkable(size_t len) { return len > Chunk::ChunkSize; } + + // Returns current size in bytes. + size_t curSize() const { return _curSize; } + // Deletes all chunks except the first (to avoid a preallocation), and calls reset. + void clear(); + // Resets the size and current write position, but does not clear. + // Existing chunks will be used, and new ones will be created (allocated) if required. + // This saves having to clear (which deletes) before every use, at the expense + // of keeping what could be an ever increasing memory block alive. + void reset(); + // Return true if successful. + bool add(const unsigned char* src, size_t len); + // Copies the queue to a character buffer. + // Returns number of bytes copied. + size_t copy(unsigned char* dst, size_t len) const; }; -template midiRTalloc::midiRTalloc() {} - #endif diff -Nru muse-2.1.2/muse/midi_consts.h muse-3.0.2+ds1/muse/midi_consts.h --- muse-2.1.2/muse/midi_consts.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/midi_consts.h 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,128 @@ +//========================================================= +// MusE +// Linux Music Editor +// midi_consts.h +// +// (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2011-2016 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 +// 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_CONSTS_H__ +#define __MIDI_CONSTS_H__ + +namespace MusECore { + +enum { + ME_NOTEOFF = 0x80, + ME_NOTEON = 0x90, + ME_POLYAFTER = 0xa0, + ME_CONTROLLER = 0xb0, + ME_PROGRAM = 0xc0, + ME_AFTERTOUCH = 0xd0, + ME_PITCHBEND = 0xe0, + ME_SYSEX = 0xf0, + ME_META = 0xff, + ME_MTC_QUARTER = 0xf1, + ME_SONGPOS = 0xf2, + ME_SONGSEL = 0xf3, + ME_TUNE_REQ = 0xf6, + ME_SYSEX_END = 0xf7, + ME_CLOCK = 0xf8, + ME_TICK = 0xf9, + ME_START = 0xfa, + ME_CONTINUE = 0xfb, + ME_STOP = 0xfc, + ME_SENSE = 0xfe + }; + +//-------------------------------------- +// Recognized / transmitted meta events: +//-------------------------------------- +enum { + ME_META_TEXT_0_SEQUENCE_NUMBER = 0x00, + ME_META_TEXT_1_COMMENT = 0x01, + ME_META_TEXT_2_COPYRIGHT = 0x02, + ME_META_TEXT_3_TRACK_NAME = 0x03, + ME_META_TEXT_4_INSTRUMENT_NAME = 0x04, + ME_META_TEXT_5_LYRIC = 0x05, + ME_META_TEXT_6_MARKER = 0x06, + ME_META_TEXT_7_CUE_POINT = 0x07, + ME_META_TEXT_8 = 0x08, + ME_META_TEXT_9_DEVICE_NAME = 0x09, + ME_META_TEXT_A = 0x0a, + ME_META_TEXT_B = 0x0b, + ME_META_TEXT_C = 0x0c, + ME_META_TEXT_D = 0x0d, + ME_META_TEXT_E = 0x0e, + ME_META_TEXT_F_TRACK_COMMENT = 0x0f, + ME_META_CHANNEL_CHANGE = 0x20, + ME_META_PORT_CHANGE = 0x21, + ME_META_END_OF_TRACK = 0x2f, + ME_META_SET_TEMPO = 0x51, + ME_META_SMPTE_OFFSET = 0x54, + ME_META_TIME_SIGNATURE = 0x58, + ME_META_KEY_SIGNATURE = 0x59, + ME_META_SEQ_SPECIFIC_1 = 0x74, + ME_META_SEQ_SPECIFIC_2 = 0x7f +}; + +const unsigned char gmOnMsg[] = { 0x7e, 0x7f, 0x09, 0x01 }; +const unsigned char gm2OnMsg[] = { 0x7e, 0x7f, 0x09, 0x03 }; +const unsigned char gmOffMsg[] = { 0x7e, 0x7f, 0x09, 0x02 }; +const unsigned char gsOnMsg[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7f, 0x00, 0x41 }; +const unsigned char gsOnMsg2[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x33, 0x50, 0x3c }; +const unsigned char gsOnMsg3[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x34, 0x50, 0x3b }; +const unsigned char xgOnMsg[] = { 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00 }; +const unsigned int gmOnMsgLen = sizeof(gmOnMsg); +const unsigned int gm2OnMsgLen = sizeof(gm2OnMsg); +const unsigned int gmOffMsgLen = sizeof(gmOffMsg); +const unsigned int gsOnMsgLen = sizeof(gsOnMsg); +const unsigned int gsOnMsg2Len = sizeof(gsOnMsg2); +const unsigned int gsOnMsg3Len = sizeof(gsOnMsg3); +const unsigned int xgOnMsgLen = sizeof(xgOnMsg); + +const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; +const unsigned char mmcStopMsg[] = { 0x7f, 0x7f, 0x06, 0x01 }; +const unsigned char mmcLocateMsg[] = { 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, 0, 0, 0, 0, 0 }; + +const unsigned int mmcDeferredPlayMsgLen = sizeof(mmcDeferredPlayMsg); +const unsigned int mmcStopMsgLen = sizeof(mmcStopMsg); +const unsigned int mmcLocateMsgLen = sizeof(mmcLocateMsg); + +// Use these in all the synths and their guis. +// NOTE: Some synths and hosts might not use this scheme. For example, MESS requires it for IPC, +// and both MESS and DSSI use it to store init data in the form of a sysex. +// +// Added this here for ease, since they all include this file. +// +// A special MusE soft synth sysex manufacturer ID. +#define MUSE_SYNTH_SYSEX_MFG_ID 0x7c +// Following the MFG_ID, besides synth specific IDs, this reserved special ID indicates +// a MusE SYSTEM ID will follow in the next byte. +#define MUSE_SYSEX_SYSTEM_ID 0x7f +// This SYSTEM command will force any relevant drum maps to update. +// When a synth's note names have changed, it should issue this command. +// So far, this command is really just a special requirement for the fluidsynth MESS plugin. +// It is the only way to inform the host to update the maps. +#define MUSE_SYSEX_SYSTEM_UPDATE_DRUM_MAPS_ID 0x00 + +} // namespace MusECore + +#endif + + diff -Nru muse-2.1.2/muse/midi.cpp muse-3.0.2+ds1/muse/midi.cpp --- muse-2.1.2/muse/midi.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/midi.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: midi.cpp,v 1.43.2.22 2009/11/09 20:28:28 terminator356 Exp $ // // (C) Copyright 1999/2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2016 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 @@ -33,6 +33,7 @@ #include "midictrl.h" #include "marker/marker.h" #include "midiport.h" +#include "minstrument.h" #include "midictrl.h" #include "sync.h" #include "audio.h" @@ -45,30 +46,20 @@ #include "midiseq.h" #include "gconfig.h" #include "ticksynth.h" +#include "mpevent.h" -namespace MusECore { +// REMOVE Tim. Persistent routes. Added. Make this permanent later if it works OK and makes good sense. +#define _USE_MIDI_ROUTE_PER_CHANNEL_ -extern void dump(const unsigned char* p, int n); +// Undefine if and when multiple output routes are added to midi tracks. +#define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ +// For debugging output: Uncomment the fprintf section. +#define DEBUG_MIDI(dev, format, args...) // fprintf(dev, format, ##args); -const unsigned char gmOnMsg[] = { 0x7e, 0x7f, 0x09, 0x01 }; -const unsigned char gsOnMsg[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7f, 0x00, 0x41 }; -const unsigned char gsOnMsg2[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x33, 0x50, 0x3c }; -const unsigned char gsOnMsg3[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x34, 0x50, 0x3b }; -const unsigned char xgOnMsg[] = { 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00 }; -const unsigned int gmOnMsgLen = sizeof(gmOnMsg); -const unsigned int gsOnMsgLen = sizeof(gsOnMsg); -const unsigned int gsOnMsg2Len = sizeof(gsOnMsg2); -const unsigned int gsOnMsg3Len = sizeof(gsOnMsg3); -const unsigned int xgOnMsgLen = sizeof(xgOnMsg); - -const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; -const unsigned char mmcStopMsg[] = { 0x7f, 0x7f, 0x06, 0x01 }; -const unsigned char mmcLocateMsg[] = { 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, 0, 0, 0, 0, 0 }; - -const unsigned int mmcDeferredPlayMsgLen = sizeof(mmcDeferredPlayMsg); -const unsigned int mmcStopMsgLen = sizeof(mmcStopMsg); -const unsigned int mmcLocateMsgLen = sizeof(mmcLocateMsg); +namespace MusECore { + +extern void dump(const unsigned char* p, int n); #define CALC_TICK(the_tick) lrintf((float(the_tick) * float(MusEGlobal::config.division) + float(div/2)) / float(div)); @@ -88,8 +79,8 @@ case 5: s = "Text 5: Lyric"; break; case 6: s = "Text 6: Marker"; break; case 7: s = "Text 7: Cue Point"; break; - case 8: s = "Text 8"; break; - case 9: s = "Text 9: Device Name"; break; + case 8: s = "Text 8"; break; + case 9: s = "Text 9: Device Name"; break; case 0x0a: s = "Text A"; break; case 0x0b: s = "Text B"; break; case 0x0c: s = "Text C"; break; @@ -115,11 +106,12 @@ // QString nameSysex //--------------------------------------------------------- -QString nameSysex(unsigned int len, const unsigned char* buf) +QString nameSysex(unsigned int len, const unsigned char* buf, MidiInstrument* instr) { QString s; if(len == 0) return s; + switch(buf[0]) { case 0x00: if(len < 3) @@ -127,12 +119,12 @@ if (buf[1] == 0 && buf[2] == 0x41) s = "Microsoft"; break; - case 0x01: s = "Sequential Circuits: "; break; - case 0x02: s = "Big Briar: "; break; - case 0x03: s = "Octave / Plateau: "; break; - case 0x04: s = "Moog: "; break; - case 0x05: s = "Passport Designs: "; break; - case 0x06: s = "Lexicon: "; break; + case 0x01: s = "Sequential Circuits"; break; + case 0x02: s = "Big Briar"; break; + case 0x03: s = "Octave / Plateau"; break; + case 0x04: s = "Moog"; break; + case 0x05: s = "Passport Designs"; break; + case 0x06: s = "Lexicon"; break; case 0x07: s = "Kurzweil"; break; case 0x08: s = "Fender"; break; case 0x09: s = "Gulbransen"; break; @@ -142,18 +134,18 @@ case 0x0d: s = "Techmar"; break; case 0x0e: s = "Matthews Research"; break; case 0x10: s = "Oberheim"; break; - case 0x11: s = "PAIA: "; break; - case 0x12: s = "Simmons: "; break; + case 0x11: s = "PAIA"; break; + case 0x12: s = "Simmons"; break; case 0x13: s = "DigiDesign"; break; - case 0x14: s = "Fairlight: "; break; + case 0x14: s = "Fairlight"; break; case 0x15: s = "JL Cooper"; break; case 0x16: s = "Lowery"; break; case 0x17: s = "Lin"; break; case 0x18: s = "Emu"; break; case 0x1b: s = "Peavy"; break; - case 0x20: s = "Bon Tempi: "; break; - case 0x21: s = "S.I.E.L: "; break; - case 0x23: s = "SyntheAxe: "; break; + case 0x20: s = "Bon Tempi"; break; + case 0x21: s = "S.I.E.L"; break; + case 0x23: s = "SyntheAxe"; break; case 0x24: s = "Hohner"; break; case 0x25: s = "Crumar"; break; case 0x26: s = "Solton"; break; @@ -163,28 +155,77 @@ case 0x2f: s = "Elka"; break; case 0x36: s = "Cheetah"; break; case 0x3e: s = "Waldorf"; break; - case 0x40: s = "Kawai: "; break; - case 0x41: s = "Roland: "; break; - case 0x42: s = "Korg: "; break; - case 0x43: s = "Yamaha: "; break; + case 0x40: s = "Kawai"; break; + case 0x41: s = "Roland"; break; + case 0x42: s = "Korg"; break; + case 0x43: s = "Yamaha"; break; case 0x44: s = "Casio"; break; case 0x45: s = "Akai"; break; - case MUSE_SYNTH_SYSEX_MFG_ID: s = "MusE Soft Synth"; break; + case MUSE_SYNTH_SYSEX_MFG_ID: s = "MusE Soft Synth"; break; case 0x7d: s = "Educational Use"; break; case 0x7e: s = "Universal: Non Real Time"; break; case 0x7f: s = "Universal: Real Time"; break; - default: s = "??: "; break; + default: s = "??"; break; } + + if(instr) + { + // Check for user-defined sysex in instrument... + foreach(const MusECore::SysEx* sx, instr->sysex()) + { + if((int)len == sx->dataLen && memcmp(buf, sx->data, len) == 0) + return s + QString(": ") + sx->name; + } + } + // // following messages should not show up in event list // they are filtered while importing midi files // if ((len == gmOnMsgLen) && memcmp(buf, gmOnMsg, gmOnMsgLen) == 0) - s += "GM-ON"; + s += ": GM-ON"; + else if ((len == gm2OnMsgLen) && memcmp(buf, gm2OnMsg, gm2OnMsgLen) == 0) + s += ": GM2-ON"; + else if ((len == gmOffMsgLen) && memcmp(buf, gmOffMsg, gmOffMsgLen) == 0) + s += ": GM-OFF"; else if ((len == gsOnMsgLen) && memcmp(buf, gsOnMsg, gsOnMsgLen) == 0) - s += "GS-ON"; + s += ": GS-ON"; else if ((len == xgOnMsgLen) && memcmp(buf, xgOnMsg, xgOnMsgLen) == 0) - s += "XG-ON"; + s += ": XG-ON"; + return s; + } + +//--------------------------------------------------------- +// QString sysexComment +//--------------------------------------------------------- + +QString sysexComment(unsigned int len, const unsigned char* buf, MidiInstrument* instr) + { + QString s; + if(len == 0) + return s; + + if(instr) + { + // Check for user-defined sysex in instrument... + foreach(const MusECore::SysEx* sx, instr->sysex()) + { + if((int)len == sx->dataLen && memcmp(buf, sx->data, len) == 0) + return sx->comment; + } + } + + // These are the common ones we know about so far... + if ((len == gmOnMsgLen) && memcmp(buf, gmOnMsg, gmOnMsgLen) == 0) + s = QObject::tr("Switch on General Midi Level 1 mode"); + else if ((len == gm2OnMsgLen) && memcmp(buf, gm2OnMsg, gm2OnMsgLen) == 0) + s = QObject::tr("Switch on General Midi Level 2 mode"); + else if ((len == gmOffMsgLen) && memcmp(buf, gmOffMsg, gmOffMsgLen) == 0) + s = QObject::tr("Switch off General Midi Level 1 or 2"); + else if ((len == gsOnMsgLen) && memcmp(buf, gsOnMsg, gsOnMsgLen) == 0) + s = QObject::tr("Switch on Roland GS mode"); + else if ((len == xgOnMsgLen) && memcmp(buf, xgOnMsg, xgOnMsgLen) == 0) + s = QObject::tr("Switch on Yamaha XG mode"); return s; } @@ -198,7 +239,7 @@ // generally: how to handle incomplete messages //--------------------------------------------------------- -void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, +void buildMidiEventList(EventList* del, const MPEventList& el, MidiTrack* track, int div, bool addSysexMeta, bool doLoops) { int hbank = 0xff; @@ -211,7 +252,19 @@ EventList mel; - for (iMPEvent i = el->begin(); i != el->end(); ++i) { + MidiInstrument::NoteOffMode nom = MidiInstrument::NoteOffAll; + MidiPort* mp = 0; + MidiInstrument* minstr = 0; + const int port = track->outPort(); + if(port >= 0 && port < MIDI_PORTS) + { + mp = &MusEGlobal::midiPorts[port]; + minstr = mp->instrument(); + if(minstr) + nom = minstr->noteOffMode(); + } + + for (iMPEvent i = el.begin(); i != el.end(); ++i) { MidiPlayEvent ev = *i; if (!addSysexMeta && (ev.type() == ME_SYSEX || ev.type() == ME_META)) continue; @@ -219,6 +272,9 @@ || ((ev.channel() == track->outChannel()) && (ev.port() == track->outPort())))) continue; unsigned tick = ev.time(); + + DEBUG_MIDI(stderr, "buildMidiEventList tick:%d dataA:%d dataB:%d\n", + ev.time(), ev.dataA(), ev.dataB()); if(doLoops) { @@ -227,11 +283,11 @@ int loopn = ev.loopNum(); int loopc = MusEGlobal::audio->loopCount(); int cmode = MusEGlobal::song->cycleMode(); // CYCLE_NORMAL, CYCLE_MIX, CYCLE_REPLACE - // If we want REPLACE and the event was recorded in a previous loop, + // If we want REPLACE and the event was recorded in a previous loop, // just ignore it. This will effectively ignore ALL previous loop events inside // the left and right markers, regardless of where recording was started or stopped. - // We want to keep any loop 0 note-offs from notes which crossed over the left marker. - // To avoid more searching here, just keep ALL note-offs from loop 0, and let code below + // We want to keep any loop 0 note-offs from notes which crossed over the left marker. + // To avoid more searching here, just keep ALL note-offs from loop 0, and let code below // sort out and keep which ones had note-ons. if(!(ev.isNoteOff() && loopn == 0)) { @@ -246,11 +302,11 @@ unsigned endRec = MusEGlobal::audio->getEndRecordPos().tick(); if((tick < endRec && loopn < loopc) || (tick >= endRec && loopn < (loopc - 1))) continue; - } - } + } + } } } - + Event e; switch(ev.type()) { case ME_NOTEON: @@ -260,9 +316,13 @@ int instr = MusEGlobal::drumInmap[ev.dataA()]; e.setPitch(instr); } + else if (track->type() == Track::NEW_DRUM) { + int instr = track->map_drum_in(ev.dataA()); + e.setPitch(instr); + } else e.setPitch(ev.dataA()); - + e.setVelo(ev.dataB()); e.setLenTick(0); break; @@ -272,9 +332,13 @@ int instr = MusEGlobal::drumInmap[ev.dataA()]; e.setPitch(instr); } + else if (track->type() == Track::NEW_DRUM) { + int instr = track->map_drum_in(ev.dataA()); + e.setPitch(instr); + } else e.setPitch(ev.dataA()); - + e.setVelo(0); e.setVeloOff(ev.dataB()); e.setLenTick(0); @@ -284,7 +348,7 @@ e.setA((CTRL_POLYAFTER & ~0xff) | (ev.dataA() & 0x7f)); e.setB(ev.dataB()); break; - + case ME_CONTROLLER: { int val = ev.dataB(); @@ -305,7 +369,7 @@ iMPEvent ii = i; ++ii; bool found = false; - for (; ii != el->end(); ++ii) { + for (; ii != el.end(); ++ii) { MidiPlayEvent ev = *ii; if (ev.type() == ME_CONTROLLER) { if (ev.dataA() == CTRL_LDATA) { @@ -317,7 +381,7 @@ } if (!found) { if (rpnh == -1 || rpnl == -1) { - printf("parameter number not defined, data 0x%x\n", datah); + fprintf(stderr, "parameter number not defined, data 0x%x\n", datah); } else { int ctrl = dataType | (rpnh << 8) | rpnl; @@ -333,7 +397,7 @@ datal = val; if (rpnh == -1 || rpnl == -1) { - printf("parameter number not defined, data 0x%x 0x%x, tick %d, channel %d\n", + fprintf(stderr, "parameter number not defined, data 0x%x 0x%x, tick %d, channel %d\n", datah, datal, tick, track->outChannel()); break; } @@ -371,7 +435,7 @@ e.setType(Controller); int ctl = ev.dataA(); e.setA(ctl); - + if(track->type() == Track::DRUM) { // Is it a drum controller event, according to the track port's instrument? @@ -380,7 +444,15 @@ // Store an index into the drum map. e.setA((ctl & ~0xff) | MusEGlobal::drumInmap[ctl & 0x7f]); } - + else if(track->type() == Track::NEW_DRUM) + { + // Is it a drum controller event, according to the track port's instrument? + MidiController *mc = MusEGlobal::midiPorts[track->outPort()].drumController(ctl); + if(mc) + // Store an index into the drum map. + e.setA((ctl & ~0xff) | track->map_drum_in(ctl & 0x7f)); + } + e.setB(val); break; } @@ -458,8 +530,8 @@ break; case ME_META_KEY_SIGNATURE: // Key Signature break; - default: - printf("buildMidiEventList: unknown Meta 0x%x %d unabsorbed, adding instead to track:%s\n", ev.dataA(), ev.dataA(), track->name().toLatin1().constData()); + default: + fprintf(stderr, "buildMidiEventList: unknown Meta 0x%x %d unabsorbed, adding instead to track:%s\n", ev.dataA(), ev.dataA(), track->name().toLatin1().constData()); e.setType(Meta); e.setA(ev.dataA()); e.setData(ev.data(), ev.len()); @@ -469,9 +541,92 @@ } // switch(ev.type() if (!e.empty()) { e.setTick(tick); - mel.add(e); + + //------------------------------------------- + // Check for and prevent duplicate events + //------------------------------------------- + + const int midi_evtype = ev.type(); + const bool midi_noteoff = (midi_evtype == ME_NOTEOFF) || (midi_evtype == ME_NOTEON && ev.dataB() == 0); + const bool midi_noteon = midi_evtype == ME_NOTEON && ev.dataB() != 0; + const bool midi_controller = midi_evtype == ME_CONTROLLER; + bool noteon_found = false; + bool noteoff_found = false; + bool ctrlval_found = false; + //bool other_ctrlval_found = false; + if(i != el.begin()) + { + iMPEvent k = i; + while(k != el.begin()) + { + --k; + MidiPlayEvent k_ev = *k; + if(k_ev.channel() != ev.channel() || k_ev.port() != ev.port()) + continue; + const int check_midi_evtype = k_ev.type(); + const bool check_midi_noteoff = (check_midi_evtype == ME_NOTEOFF) || (check_midi_evtype == ME_NOTEON && k_ev.dataB() == 0); + const bool check_midi_noteon = check_midi_evtype == ME_NOTEON && k_ev.dataB() != 0; + const bool check_midi_controller = check_midi_evtype == ME_CONTROLLER; + if(midi_noteon || midi_noteoff) + { + if(ev.dataA() == k_ev.dataA()) // Note + { + if(check_midi_noteon) + { + // Check the instrument's note-off mode: If it does not support note-offs, + // don't bother doing duplicate note-on checks. + // This allows drum input triggers (no note offs at all), although it is awkward to + // first have to choose an output instrument with no note-off mode. + if(!midi_noteon || (nom != MidiInstrument::NoteOffNone)) + noteon_found = true; + break; + } + if(check_midi_noteoff) + { + noteoff_found = true; + break; + } + } + } + else if(midi_controller) + { + if(ev.dataA() == k_ev.dataA()) // Controller number + { + if(check_midi_controller) + { + // All we can really do is prevent multiple events at the same time. + // We must allow multiple events at different times having the same value, + // since the sender may have wanted it that way (a 'flat' graph). + if(ev.time() == k_ev.time()) // Event time + ctrlval_found = true; + // Optimization: Do not allow multiple events at different times having the same value. + // Nice, but can't really discard these, sender may have wanted it that way (a 'flat' graph). + #if 0 + if(ev.dataB() == k_ev.dataB()) // Controller value + ctrlval_found = true; + else + other_ctrlval_found = true; + #endif + break; + } + } + } + else + { + // TODO: Other types! + } + } } - } // i != el->end() + // Accept the event only if no duplicate was found. // TODO: Other types! + if((midi_noteon && !noteon_found) || + (midi_noteoff && !noteoff_found) || + //(midi_controller && (other_ctrlval_found || !ctrlval_found))) + (midi_controller && !ctrlval_found) || + // Accept any other type of event. + (!midi_noteon && !midi_noteoff && !midi_controller) ) + mel.add(e); + } + } // i != el.end() //--------------------------------------------------- @@ -482,12 +637,12 @@ Event ev = i->second; if (ev.isNote()) { if (!ev.isNoteOff()) { - - // If the event length is not zero, it means the event and its + + // If the event length is not zero, it means the event and its // note on/off have already been taken care of. So ignore it. if(ev.lenTick() != 0) continue; - + iEvent k; for (k = mel.lower_bound(ev.tick()); k != mel.end(); ++k) { Event event = k->second; @@ -495,7 +650,7 @@ int t = k->first - i->first; if (t <= 0) { if (MusEGlobal::debugMsg) { - printf("Note len is (%d-%d)=%d, set to 1\n", + fprintf(stderr, "Note len is (%d-%d)=%d, set to 1\n", k->first, i->first, k->first - i->first); ev.dump(); event.dump(); @@ -508,7 +663,7 @@ } } if (k == mel.end()) { - printf("-no note-off! %d pitch %d velo %d\n", + fprintf(stderr, "-no note-off! %d pitch %d velo %d\n", ev.tick(), ev.pitch(), ev.velo()); // // switch off at end of measure @@ -517,24 +672,25 @@ ev.setLenTick(endTick-ev.tick()); } else { - if (k==i) + if (k==i) { //this will never happen, because i->second has to be a NOTE ON, //while k has to be a NOTE OFF. but in case something changes: - printf("ERROR: THIS SHOULD NEVER HAPPEN: k==i in midi.cpp:buildMidiEventList()\n"); - else + fprintf(stderr, "ERROR: THIS SHOULD NEVER HAPPEN: k==i in midi.cpp:buildMidiEventList()\n"); + } + else { mel.erase(k); + } i = mel.begin(); continue; } } - } + } } - for (iEvent i = mel.begin(); i != mel.end(); ++i) { Event ev = i->second; if (ev.isNoteOff()) { - printf("+extra note-off! %d pitch %d velo %d\n", + fprintf(stderr, "+extra note-off! %d pitch %d velo %d\n", i->first, ev.pitch(), ev.velo()); continue; } @@ -563,38 +719,71 @@ //--------------------------------------------------------- // sendLocalOff +// Can be called by any thread. //--------------------------------------------------------- void Audio::sendLocalOff() { + MidiPlayEvent ev; + ev.setTime(0); // Immediate processing. TODO Use curFrame? + ev.setType(MusECore::ME_CONTROLLER); + ev.setA(MusECore::CTRL_LOCAL_OFF); + ev.setB(0); for (int k = 0; k < MIDI_PORTS; ++k) { for (int i = 0; i < MIDI_CHANNELS; ++i) - MusEGlobal::midiPorts[k].sendEvent(MusECore::MidiPlayEvent(0, k, i, MusECore::ME_CONTROLLER, MusECore::CTRL_LOCAL_OFF, 0), true); + { + ev.setPort(k); + ev.setChannel(i); + // This is a 'trigger' event. Send to the device, but do not send to the + // midi port controllers because it leaves them in this state. + if(MusEGlobal::midiPorts[k].device()) + MusEGlobal::midiPorts[k].device()->putEvent(ev, MidiDevice::NotLate); + } } } //--------------------------------------------------------- // panic +// Can be called by any thread. //--------------------------------------------------------- void Audio::panic() { + MidiPlayEvent ev; + ev.setTime(0); // Immediate processing. TODO Use curFrame? + ev.setType(MusECore::ME_CONTROLLER); + ev.setB(0); + + // TODO Reset those controllers back to unknown! for (int i = 0; i < MIDI_PORTS; ++i) { MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; - if (port == 0) // ?? - continue; for (int chan = 0; chan < MIDI_CHANNELS; ++chan) { if (MusEGlobal::debugMsg) - printf("send all sound of to midi port %d channel %d\n", i, chan); - port->sendEvent(MusECore::MidiPlayEvent(0, i, chan, MusECore::ME_CONTROLLER, MusECore::CTRL_ALL_SOUNDS_OFF, 0), true); - port->sendEvent(MusECore::MidiPlayEvent(0, i, chan, MusECore::ME_CONTROLLER, MusECore::CTRL_RESET_ALL_CTRL, 0), true); + fprintf(stderr, "send all sound of to midi port %d channel %d\n", i, chan); + + ev.setPort(i); + ev.setChannel(chan); + + ev.setA(MusECore::CTRL_ALL_SOUNDS_OFF); + // This is a 'trigger' event. Send to the device, but do not send to the + // midi port controllers because it leaves them in this state. + //port->putHwCtrlEvent(ev); + if(port->device()) + port->device()->putEvent(ev, MidiDevice::NotLate); + + ev.setA(MusECore::CTRL_RESET_ALL_CTRL); + // This is a 'trigger' event. Send to the device, but do not send to the + // midi port controllers because it leaves them in this state. + //port->putHwCtrlEvent(ev); + if(port->device()) + port->device()->putEvent(ev, MidiDevice::NotLate); } } } //--------------------------------------------------------- // initDevices -// - called when instrument init sequences plus controller +// - called when instrument init sequences plus controller // defaults should be checked and/or sent // - called from arranger pulldown menu //--------------------------------------------------------- @@ -607,6 +796,437 @@ } //--------------------------------------------------------- +// seekMidi +// Called from audio thread only. +//--------------------------------------------------------- + +void Audio::seekMidi() +{ + unsigned pos = MusEGlobal::audio->tickPos(); + const bool playing = isPlaying(); + + // Bit-wise channels that are used. + int used_ports[MIDI_PORTS]; + // Initialize the array. + for(int i = 0; i < MIDI_PORTS; ++i) + used_ports[i] = 0; + + // Find all used channels on all used ports. + bool drum_found = false; + if(MusEGlobal::song->click() && + MusEGlobal::clickPort < MIDI_PORTS && + MusEGlobal::clickChan < MIDI_CHANNELS) + used_ports[MusEGlobal::clickPort] |= (1 << MusEGlobal::clickChan); + MidiTrackList* tl = MusEGlobal::song->midis(); + for(ciMidiTrack imt = tl->begin(); imt != tl->end(); ++imt) + { + MidiTrack* mt = *imt; + + //------------------------------------------------------------ + // While we are at it, flush out any track-related playback stuck notes + // (NOT 'live' notes) which were not put directly to the device + //------------------------------------------------------------ + MPEventList& mel = mt->stuckNotes; + for(iMPEvent i = mel.begin(), i_next = i; i != mel.end(); i = i_next) + { + ++i_next; + + MidiPlayEvent ev(*i); + const int ev_port = ev.port(); + if(ev_port >= 0 && ev_port < MIDI_PORTS) + { + MidiPort* mp = &MusEGlobal::midiPorts[ev_port]; + ev.setTime(0); // Immediate processing. TODO Use curFrame? + if(mp->device()) + mp->device()->putEvent(ev, MidiDevice::NotLate); + } + mel.erase(i); + } + + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + + if(mt->type() == MusECore::Track::DRUM) + { + if(!drum_found) + { + drum_found = true; + for(int i = 0; i < DRUM_MAPSIZE; ++i) + { + // Default to track port if -1 and track channel if -1. + int mport = MusEGlobal::drumMap[i].port; + if(mport == -1) + mport = mt->outPort(); + int mchan = MusEGlobal::drumMap[i].channel; + if(mchan == -1) + mchan = mt->outChannel(); + if(mport >= 0 && mport < MIDI_PORTS && mchan >= 0 && mchan < MIDI_CHANNELS) + used_ports[mport] |= (1 << mchan); + } + } + } + else + { + const int mport = mt->outPort(); + const int mchan = mt->outChannel(); + if(mport >= 0 && mport < MIDI_PORTS && mchan >= 0 && mchan < MIDI_CHANNELS) + used_ports[mport] |= (1 << mchan); + } + +#else + MusECore::RouteList* rl = mt->outRoutes(); + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + switch(ir->type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + { + if(mt->type() == MusECore::Track::DRUM) + { + if(!drum_found) + { + drum_found = true; + for(int i = 0; i < DRUM_MAPSIZE; ++i) + { + // Default to track port if -1 and track channel if -1. + int mport = MusEGlobal::drumMap[i].port; + if(mport == -1) + mport = ir->midiPort; + int mchan = MusEGlobal::drumMap[i].channel; + if(mchan == -1) + mchan = ir->channel; + if(mport >= 0 && mport < MIDI_PORTS && mchan >= 0 && mchan < MIDI_CHANNELS) + used_ports[mport] |= (1 << mchan); + } + } + } + else + { + const int mport = ir->midiPort; + const int mchan = ir->channel; + if(mport >= 0 && mport < MIDI_PORTS && mchan >= 0 && mchan < MIDI_CHANNELS) + used_ports[mport] |= (1 << mchan); + } + } + break; + + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; + } + } +#endif + } + + for(int i = 0; i < MIDI_PORTS; ++i) + { + if(used_ports[i] == 0) + continue; + + MidiPort* mp = &MusEGlobal::midiPorts[i]; + MidiDevice* md = mp->device(); + + //--------------------------------------------------- + // Send STOP + //--------------------------------------------------- + + // Don't send if external sync is on. The master, and our sync routing system will take care of that. + if(!MusEGlobal::extSyncFlag.value()) + { + if(mp->syncInfo().MRTOut()) + { + // Shall we check for device write open flag to see if it's ok to send?... + //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) + //if(!(openFlags() & 1)) + // continue; + mp->sendStop(); + } + } + + //--------------------------------------------------- + // If playing, clear all notes and flush out any + // stuck notes which were put directly to the device + //--------------------------------------------------- + + if(md && playing) + md->handleSeek(); + + //--------------------------------------------------- + // reset sustain + //--------------------------------------------------- + + if(md) + { + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + { + const MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); + md->putEvent(ev, MidiDevice::NotLate); + } + } + } + + MidiInstrument* instr = mp->instrument(); + MidiCtrlValListList* cll = mp->controller(); + + for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) + { + MidiCtrlValList* vl = ivl->second; + int chan = ivl->first >> 24; + if(!(used_ports[i] & (1 << chan))) // Channel not used in song? + continue; + int ctlnum = vl->num(); + + // Find the first non-muted value at the given tick... + bool values_found = false; + bool found_value = false; + + iMidiCtrlVal imcv = vl->lower_bound(pos); + if(imcv != vl->end() && imcv->first == (int)pos) + { + for( ; imcv != vl->end() && imcv->first == (int)pos; ++imcv) + { + const Part* p = imcv->second.part; + if(!p) + continue; + // Ignore values that are outside of the part. + if(pos < p->tick() || pos >= (p->tick() + p->lenTick())) + continue; + values_found = true; + // Ignore if part or track is muted or off. + if(p->mute()) + continue; + const Track* track = p->track(); + if(track && (track->isMute() || track->off())) + continue; + found_value = true; + break; + } + } + else + { + while(imcv != vl->begin()) + { + --imcv; + const Part* p = imcv->second.part; + if(!p) + continue; + // Ignore values that are outside of the part. + unsigned t = imcv->first; + if(t < p->tick() || t >= (p->tick() + p->lenTick())) + continue; + values_found = true; + // Ignore if part or track is muted or off. + if(p->mute()) + continue; + const Track* track = p->track(); + if(track && (track->isMute() || track->off())) + continue; + found_value = true; + break; + } + } + + if(found_value) + { + int fin_port = i; + MidiPort* fin_mp = mp; + int fin_chan = chan; + int fin_ctlnum = ctlnum; + // Is it a drum controller event, according to the track port's instrument? + if(mp->drumController(ctlnum)) + { + if(const Part* p = imcv->second.part) + { + if(Track* t = p->track()) + { + if(t->type() == MusECore::Track::NEW_DRUM) + { + MidiTrack* mt = static_cast(t); + int v_idx = ctlnum & 0x7f; + fin_ctlnum = (ctlnum & ~0xff) | mt->drummap()[v_idx].anote; + int map_port = mt->drummap()[v_idx].port; + if(map_port != -1) + { + fin_port = map_port; + fin_mp = &MusEGlobal::midiPorts[fin_port]; + } + int map_chan = mt->drummap()[v_idx].channel; + if(map_chan != -1) + fin_chan = map_chan; + } + } + } + } + + const MidiPlayEvent ev(0, fin_port, fin_chan, ME_CONTROLLER, fin_ctlnum, imcv->second.val); + // This is the audio thread. Just set directly. + fin_mp->setHwCtrlState(ev); + // Don't bother sending any sustain values to the device, because we already + // just sent out zero sustain values, above. Just set the hw state. + // When play resumes, the correct values are sent again if necessary in Audio::startRolling(). + if(fin_ctlnum != CTRL_SUSTAIN && fin_mp->device()) + fin_mp->device()->putEvent(ev, MidiDevice::NotLate); + } + + // Either no value was found, or they were outside parts, or pos is in the unknown area before the first value. + // Send instrument default initial values. NOT for syntis. Use midiState and/or initParams for that. + //if((imcv == vl->end() || !done) && !MusEGlobal::song->record() && instr && !isSynti()) + // Hmm, without refinement we can only do this at position 0, due to possible 'skipped' values outside parts, above. + if(instr && md && !md->isSynti() && !values_found && + MusEGlobal::config.midiSendCtlDefaults && !MusEGlobal::song->record() && pos == 0) + { + MidiControllerList* mcl = instr->controller(); + ciMidiController imc = mcl->find(vl->num()); + if(imc != mcl->end()) + { + MidiController* mc = imc->second; + if(mc->initVal() != CTRL_VAL_UNKNOWN) + { + //fprintf(stderr, "Audio::seekMidi: !values_found: calling sendEvent: ctlnum:%d val:%d\n", ctlnum, mc->initVal() + mc->bias()); + // Use sendEvent to get the optimizations and limiting. No force sending. Note the addition of bias. + const MidiPlayEvent ev(0, i, chan, ME_CONTROLLER, ctlnum, mc->initVal() + mc->bias()); + // This is the audio thread. Just set directly. + mp->setHwCtrlState(ev); + md->putEvent(ev, MidiDevice::NotLate); + } + } + } + + //--------------------------------------------------- + // Send STOP and "set song position pointer" + //--------------------------------------------------- + + // Don't send if external sync is on. The master, and our sync routing system will take care of that. + if(!MusEGlobal::extSyncFlag.value()) + { + if(mp->syncInfo().MRTOut()) + { + int beat = (pos * 4) / MusEGlobal::config.division; + mp->sendSongpos(beat); + } + } + } + } +} + +//--------------------------------------------------------- +// extClockHistoryTick2Frame +// Convert tick to frame using the external clock history list. +// The function takes a tick relative to zero (ie. relative to the first event in a processing batch). +// The returned clock frames occurred during the previous audio cycle(s), so you may want to shift +// the frames forward by one audio segment size for scheduling purposes. +// CAUTION: There must be at least one valid clock in the history, +// otherwise it returns zero. Don't feed this a tick +// greater than or equal to the next tick, it will simply return +// the very last frame, which is not very useful since +// that will just bunch the events together at the last frame. +//--------------------------------------------------------- + +unsigned int Audio::extClockHistoryTick2Frame(unsigned int tick) const +{ + if(_extClockHistorySize == 0) + { + fprintf(stderr, "Error: Audio::extClockTickToFrame(): empty list\n"); + return 0; + } + + const int div = MusEGlobal::config.division / 24; + if(div == 0) + return 0; // Prevent divide by zero. + + int index = tick / div; + if(index >= _extClockHistorySize) + { + fprintf(stderr, "Error: Audio::extClockTickToFrame(): index:%d >= size:%d\n", index, _extClockHistorySize); + index = _extClockHistorySize - 1; + } + +// Divide the clock period by the division and interpolate for even better resolution. +// FIXME: Darn, too bad we can't use this. It would work, but the previous cycle +// has no knowledge of what to put at the end, and the current cycle +// would end up lumping together events at the start which should have +// been played at end of previous cycle. +// const unsigned int subtick = tick % div; +// const unsigned int frame = _extClockLastFrame + double(_extClockHistory[index] - _extClockLastFrame) * (double(subtick) / double(div)); + const unsigned int frame = _extClockHistory[index].frame(); + + return frame; +} + +//--------------------------------------------------------- +// extClockHistoryTick2Frame +// Convert frame to tick using the external clock history list. +// The function takes an absolute linearly increasing frame and returns a tick relative to zero +// (ie. relative to the first event in a processing batch). +// CAUTION: There must be at least one valid clock in the history, +// otherwise it returns zero. Don't feed this a frame +// greater than or equal to the next frame, it will simply return +// the very last tick, which is not very useful since +// that will just bunch the events together at the last tick. +//--------------------------------------------------------- + +unsigned int Audio::extClockHistoryFrame2Tick(unsigned int frame) const +{ + if(_extClockHistorySize == 0) + { + fprintf(stderr, "Error: Audio::extClockHistoryFrame2Tick(): empty list\n"); + return curTickPos; + } + + const unsigned int div = MusEGlobal::config.division / 24; + + bool found = false; + unsigned int val = 0; + + for(int i = _extClockHistorySize - 1; i >= 0; --i) + { + DEBUG_MIDI(stderr, "Audio::extClockHistoryFrame2Tick(): frame:%u i:%d _extClockHistory[i]._frame:%u\n", + frame, i, _extClockHistory[i].frame()); + + if(_extClockHistory[i].frame() <= frame) + { + if(!found) + { + found = true; + int clocks = 0; + unsigned int offset = curTickPos; + + for(int k = i; k >= 0; --k) + { + if(_extClockHistory[k].isFirstClock()) + { + if(_extClockHistory[k].externState() == ExtMidiClock::ExternStarted) + offset = 0; + } + + if(!_extClockHistory[k].isPlaying()) + break; + + if(k < i) // Ignore first clock. + ++clocks; + } + + val = offset + clocks * div; + } + } + } + if(found) + return val; + + fprintf(stderr, "Error: Audio::extClockHistoryFrame2Tick(): frame:%u out of range. Returning zero. _extClockHistorySize:%u\n", + frame, _extClockHistorySize); + + // We don't know the state of the last clock, we can only assume it was playing. + if(curTickPos >= div) + return curTickPos - div; + + return curTickPos; +} + +//--------------------------------------------------------- // collectEvents // collect events for next audio segment //--------------------------------------------------------- @@ -617,21 +1237,22 @@ int channel = track->outChannel(); int defaultPort = port; - MidiDevice* md = MusEGlobal::midiPorts[port].device(); + MidiPort* mp = &MusEGlobal::midiPorts[port]; + MidiDevice* md = mp->device(); PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart*)(p->second); - // dont play muted parts + // don't play muted parts if (part->mute()) continue; - EventList* events = part->events(); + const EventList& events = part->events(); unsigned partTick = part->tick(); unsigned partLen = part->lenTick(); int delay = track->delay; if (cts > nts) { - printf("processMidi: FATAL: cur > next %d > %d\n", + fprintf(stderr, "processMidi: FATAL: cur > next %d > %d\n", cts, nts); return; } @@ -640,18 +1261,18 @@ continue; unsigned stick = (offset > cts) ? 0 : cts - offset; unsigned etick = nts - offset; - // Do not play events which are past the end of this part. + // Do not play events which are past the end of this part. if(etick > partLen) continue; - - iEvent ie = events->lower_bound(stick); - iEvent iend = events->lower_bound(etick); + + ciEvent ie = events.lower_bound(stick); + ciEvent iend = events.lower_bound(etick); for (; ie != iend; ++ie) { Event ev = ie->second; port = defaultPort; //Reset each loop // - // dont play any meta events + // don't play any meta events // if (ev.type() == Meta) continue; @@ -668,13 +1289,35 @@ continue; } unsigned tick = ev.tick() + offset; - unsigned frame = MusEGlobal::tempomap.tick2frame(tick) + frameOffset; + + //----------------------------------------------------------------- + // Determining the playback scheduling frame from the event's tick: + //----------------------------------------------------------------- + unsigned frame; + if(MusEGlobal::extSyncFlag.value()) + // If external sync is on, look up the scheduling frame from the tick, + // in the external clock history list (which is cleared, re-composed, and processed each cycle). + // The function takes a tick relative to zero (ie. relative to the first event in this batch). + // The returned clock frame occurred during the previous audio cycle(s), so shift the frame + // forward by one audio segment size. + frame = extClockHistoryTick2Frame(tick - stick) + MusEGlobal::segmentSize; + else + { + // If external sync is off, look up the scheduling frame from our tempo list + // ie. normal playback. + const unsigned int fr = MusEGlobal::tempomap.tick2frame(tick); + const unsigned int pos_fr = pos().frame(); + frame = (fr < pos_fr) ? 0 : fr - pos_fr; + frame += syncFrame; + } + switch (ev.type()) { case Note: { int len = ev.lenTick(); int pitch = ev.pitch(); int velo = ev.velo(); + int veloOff = ev.veloOff(); if (track->type() == Track::DRUM) { // Map drum-notes to the drum-map values int instr = ev.pitch(); @@ -687,8 +1330,23 @@ if(channel == -1) channel = track->outChannel(); velo = int(double(velo) * (double(MusEGlobal::drumMap[instr].vol) / 100.0)) ; + veloOff = int(double(veloOff) * (double(MusEGlobal::drumMap[instr].vol) / 100.0)) ; } - else if (track->type() != Track::NEW_DRUM) { + else if (track->type() == Track::NEW_DRUM) { + // Map drum-notes to the drum-map values + int instr = ev.pitch(); + pitch = track->drummap()[instr].anote; + // Default to track port if -1 and track channel if -1. + port = track->drummap()[instr].port; //This changes to non-default port + if(port == -1) + port = track->outPort(); + channel = track->drummap()[instr].channel; + if(channel == -1) + channel = track->outChannel(); + velo = int(double(velo) * (double(track->drummap()[instr].vol) / 100.0)) ; + veloOff = int(double(veloOff) * (double(track->drummap()[instr].vol) / 100.0)) ; + } + else if (track->type() == Track::MIDI) { // transpose non drum notes pitch += (track->transposition + MusEGlobal::song->globalPitchShift()); } @@ -697,41 +1355,47 @@ pitch = 127; if (pitch < 0) pitch = 0; + + // Apply track velocity and compression to both note-on and note-off velocity... velo += track->velocity; velo = (velo * track->compression) / 100; if (velo > 127) velo = 127; if (velo < 1) // no off event - velo = 1; + // Zero means zero. Should mean no note at all? + //velo = 1; + continue; + veloOff += track->velocity; + veloOff = (veloOff * track->compression) / 100; + if (veloOff > 127) + veloOff = 127; + if (veloOff < 1) + veloOff = 0; + len = (len * track->len) / 100; - if (len <= 0) // dont allow zero length + if (len <= 0) // don't allow zero length len = 1; - int veloOff = ev.veloOff(); if (port == defaultPort) { - // If syncing to external midi sync, we cannot use the tempo map. - // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. p3.3.25 - if(MusEGlobal::extSyncFlag.value()) - md->addScheduledEvent(MusECore::MidiPlayEvent(tick, port, channel, MusECore::ME_NOTEON, pitch, velo)); - else - md->addScheduledEvent(MusECore::MidiPlayEvent(frame, port, channel, MusECore::ME_NOTEON, pitch, velo)); - - md->addStuckNote(MusECore::MidiPlayEvent(tick + len, port, channel, - veloOff ? MusECore::ME_NOTEOFF : MusECore::ME_NOTEON, pitch, veloOff)); + if (md) { + md->putEvent( + MusECore::MidiPlayEvent(frame, port, channel, MusECore::ME_NOTEON, pitch, velo), + MidiDevice::NotLate, MidiDevice::PlaybackBuffer); + track->addStuckNote(MusECore::MidiPlayEvent(tick + len, port, channel, + MusECore::ME_NOTEOFF, pitch, veloOff)); + } } else { //Handle events to different port than standard. MidiDevice* mdAlt = MusEGlobal::midiPorts[port].device(); if (mdAlt) { - if(MusEGlobal::extSyncFlag.value()) // p3.3.25 - mdAlt->addScheduledEvent(MusECore::MidiPlayEvent(tick, port, channel, MusECore::ME_NOTEON, pitch, velo)); - else - mdAlt->addScheduledEvent(MusECore::MidiPlayEvent(frame, port, channel, MusECore::ME_NOTEON, pitch, velo)); - - mdAlt->addStuckNote(MusECore::MidiPlayEvent(tick + len, port, channel, - veloOff ? MusECore::ME_NOTEOFF : MusECore::ME_NOTEON, pitch, veloOff)); + mdAlt->putEvent( + MusECore::MidiPlayEvent(frame, port, channel, MusECore::ME_NOTEON, pitch, velo), + MidiDevice::NotLate, MidiDevice::PlaybackBuffer); + track->addStuckNote(MusECore::MidiPlayEvent(tick + len, port, channel, + MusECore::ME_NOTEOFF, pitch, veloOff)); } } - + if(velo > track->activity()) track->setActivity(velo); } @@ -756,34 +1420,75 @@ channel = MusEGlobal::drumMap[instr].channel; if(channel == -1) channel = track->outChannel(); - MidiDevice* mdAlt = MusEGlobal::midiPorts[port].device(); - if(mdAlt) - { - // If syncing to external midi sync, we cannot use the tempo map. - // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. p3.3.25 - if(MusEGlobal::extSyncFlag.value()) - mdAlt->addScheduledEvent(MusECore::MidiPlayEvent(tick, port, channel, - MusECore::ME_CONTROLLER, ctl | pitch, ev.dataB())); - else - mdAlt->addScheduledEvent(MusECore::MidiPlayEvent(frame, port, channel, - MusECore::ME_CONTROLLER, ctl | pitch, ev.dataB())); - } - break; - } + + MusECore::MidiPlayEvent mpeAlt(frame, port, channel, + MusECore::ME_CONTROLLER, + ctl | pitch, + ev.dataB()); + + MidiPort* mpAlt = &MusEGlobal::midiPorts[port]; + // TODO Maybe grab the flag from the 'Optimize Controllers' Global Setting, + // which so far was meant for (N)RPN stuff. For now, just force it. + // This is the audio thread. Just set directly. + mpAlt->setHwCtrlState(mpeAlt); + if(MidiDevice* mdAlt = mpAlt->device()) + mdAlt->putEvent(mpeAlt, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); + + break; // Break out. + } } - if(MusEGlobal::extSyncFlag.value()) // p3.3.25 - md->addScheduledEvent(MusECore::MidiPlayEvent(tick, port, channel, ev)); - else - md->addScheduledEvent(MusECore::MidiPlayEvent(frame, port, channel, ev)); - } + else if (track->type() == Track::NEW_DRUM) + { + int ctl = ev.dataA(); + // Is it a drum controller event, according to the track port's instrument? + MusECore::MidiController *mc = MusEGlobal::midiPorts[defaultPort].drumController(ctl); + if(mc) + { + int instr = ctl & 0x7f; + ctl &= ~0xff; + int pitch = track->drummap()[instr].anote & 0x7f; + // Default to track port if -1 and track channel if -1. + port = track->drummap()[instr].port; //This changes to non-default port + if(port == -1) + port = track->outPort(); + channel = track->drummap()[instr].channel; + if(channel == -1) + channel = track->outChannel(); + + MusECore::MidiPlayEvent mpeAlt(frame, port, channel, + MusECore::ME_CONTROLLER, + ctl | pitch, + ev.dataB()); + + MidiPort* mpAlt = &MusEGlobal::midiPorts[port]; + // TODO Maybe grab the flag from the 'Optimize Controllers' Global Setting, + // which so far was meant for (N)RPN stuff. For now, just force it. + // This is the audio thread. Just set directly. + mpAlt->setHwCtrlState(mpeAlt); + if(MidiDevice* mdAlt = mpAlt->device()) + mdAlt->putEvent(mpeAlt, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); + + break; // Break out. + } + } + + MusECore::MidiPlayEvent mpe = ev.asMidiPlayEvent(frame, port, channel); + // TODO Maybe grab the flag from the 'Optimize Controllers' Global Setting, + // which so far was meant for (N)RPN stuff. For now, just force it. + // This is the audio thread. Just set directly. + mp->setHwCtrlState(mpe); + if(md) + md->putEvent(mpe, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); + } break; - + default: - if(MusEGlobal::extSyncFlag.value()) // p3.3.25 - md->addScheduledEvent(MusECore::MidiPlayEvent(tick, port, channel, ev)); - else - md->addScheduledEvent(MusECore::MidiPlayEvent(frame, port, channel, ev)); - + + if(md) + { + md->putEvent(ev.asMidiPlayEvent(frame, port, channel), + MidiDevice::NotLate, MidiDevice::PlaybackBuffer); + } break; } } @@ -801,49 +1506,88 @@ 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) + const bool extsync = MusEGlobal::extSyncFlag.value(); + const bool playing = isPlaying(); + + for (iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { MidiDevice* md = *id; + int port = md->midiPort(); // Port should be same as event.port() from this device. Same idea event.channel(). - // klumsy hack for MESS synti devices: + // Process events sent by synthesizers (which in turn may have been passed by their GUI -> synth FIFOs). + // Receive events sent from a synth's gui thread (which might be different than our gui thread) to the audio thread. if(md->isSynti()) { SynthI* s = (SynthI*)md; - while (s->eventsPending()) + while (s->eventsPending()) { - MusECore::MidiRecordEvent ev = s->receiveEvent(); - md->recordEvent(ev); + MidiRecordEvent ev = s->receiveEvent(); + // FIXME: This is for recording the events sent by GUI. + // It never gets a chance to be processed since reading of + // record FIFOs is done only by connected input ROUTES, below. + // To be useful, the synth itself must be allowed to be chosen + // as an input route, which is simple enough, but we currently don't + // list synths as inputs for fear of too many INCOMPATIBLE messages + // from DIFFERING synths. However, we could allow ONLY THIS synth + // to be listed and therefore be automatically connected too, if desired. + //md->recordEvent(ev); + // + // For now, instead of recording, here is the minimum that we must do: + + // Intercept any special MusE system sysex messages. (This IS the system right here.) + bool intercepted = false; + const int type = ev.type(); + switch(type) + { + case ME_SYSEX: + { + const unsigned char* p = ev.data(); + int n = ev.len(); + if(n >= 3) + { + if(p[0] == MUSE_SYNTH_SYSEX_MFG_ID) + { + if(p[1] == MUSE_SYSEX_SYSTEM_ID && p[2] == MUSE_SYSEX_SYSTEM_UPDATE_DRUM_MAPS_ID) + { + intercepted = true; + if(port >= 0 && port < MIDI_PORTS) + MusEGlobal::midiPorts[port].updateDrumMaps(); + } + } + } + } + break; + + default: + break; + } + + // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. + // Same code as in MidiPort::sendEvent() + if(!intercepted && port != -1) + // This is the audio thread. Just set directly. + MusEGlobal::midiPorts[port].setHwCtrlState(MidiPlayEvent(ev)); } } - - md->collectMidiEvents(); - - // Take snapshots of the current sizes of the recording fifos, + + // 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) + + 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) + for(int i = 0; i < count; ++i) { - MusECore::MidiRecordEvent event(rf.peek(i)); + const MusECore::MidiRecordEvent& event(rf.peek(i)); int etype = event.type(); if(etype == MusECore::ME_CONTROLLER || etype == MusECore::ME_PITCHBEND || etype == MusECore::ME_PROGRAM) @@ -859,19 +1603,19 @@ ctl = MusECore::CTRL_PITCH; val = event.dataA(); } - else if(etype == MusECore::ME_PROGRAM) + else //if(etype == MusECore::ME_PROGRAM) { ctl = MusECore::CTRL_PROGRAM; val = event.dataA(); } - - // Midi learn! + + // 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) + for (MusECore::iTrack t = MusEGlobal::song->tracks()->begin(); t != MusEGlobal::song->tracks()->end(); ++t) { if((*t)->isMidiTrack()) continue; @@ -883,17 +1627,17 @@ { const MidiAudioCtrlStruct* macs = &imacm->second; int actrl = macs->audioCtrlId(); - - iCtrlList icl = track->controller()->find(actrl); + + 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. + + // 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 @@ -903,74 +1647,82 @@ t = 0; else // Subtract the current audio position frame - t -= pframe; - + 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; - + unsigned int rec_t = playing ? 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 + // 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 + (at == AUTO_READ && !playing) || + (at == AUTO_TOUCH) ) track->enableController(actrl, false); - if(isPlaying()) + if(playing) { if(at == AUTO_WRITE || at == AUTO_TOUCH) - track->recEvents()->push_back(CtrlRecVal(rec_t, actrl, dval)); + track->recEvents()->push_back(CtrlRecVal(rec_t, actrl, dval)); } - else + else { if(at == AUTO_WRITE) - track->recEvents()->push_back(CtrlRecVal(rec_t, actrl, dval)); + 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); + cl->add(rec_t, dval); } } - } + } } - } + } } } - for (MusECore::iMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) + MidiTrackList* mtl = MusEGlobal::song->midis(); + for (iMidiTrack t = mtl->begin(); t != mtl->end(); ++t) { - MusECore::MidiTrack* track = *t; - int port = track->outPort(); - MidiDevice* md = MusEGlobal::midiPorts[port].device(); - if(md) + MidiTrack* track = *t; + const int t_port = track->outPort(); + const int t_channel = track->outChannel(); + MidiPort* mp = 0; + if(t_port >= 0 && t_port < MIDI_PORTS) + mp = &MusEGlobal::midiPorts[t_port]; + MidiDevice* md = 0; + if(mp) + md = mp->device(); + // only add track events if the track is unmuted and turned on + if(!track->isMute() && !track->off()) { - // only add track events if the track is unmuted and turned on - if(!track->isMute() && !track->off()) - { - if(isPlaying() && (curTickPos < nextTickPos)) - collectEvents(track, curTickPos, nextTickPos); - } - } + if(playing && (curTickPos < nextTickPos)) + collectEvents(track, curTickPos, nextTickPos); + } // //----------midi recording // - if (track->recordFlag()) + const bool track_rec_flag = track->recordFlag(); +// REMOVE Tim. monitor. Changed. +// const bool track_rec_monitor = track->recMonitor(); // Separate monitor and record functions. + const bool track_rec_monitor = track->isRecMonitored(); // Separate monitor and record functions. + + if(track_rec_monitor || track_rec_flag) { - MusECore::MPEventList* rl = track->mpevents(); - MusECore::MidiPort* tport = &MusEGlobal::midiPorts[port]; + MPEventList& rl = track->mpevents; RouteList* irl = track->inRoutes(); for(ciRoute r = irl->begin(); r != irl->end(); ++r) { - if(!r->isValid() || (r->type != Route::MIDI_PORT_ROUTE)) + if(!r->isValid() || (r->type != Route::MIDI_PORT_ROUTE)) continue; int devport = r->midiPort; if (devport == -1) @@ -978,76 +1730,134 @@ MidiDevice* dev = MusEGlobal::midiPorts[devport].device(); if(!dev) continue; - int channelMask = r->channel; + +#ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ + + const int r_chan = r->channel; +#else + const int channelMask = r->channel; if(channelMask == -1 || channelMask == 0) continue; - for(int channel = 0; channel < MIDI_CHANNELS; ++channel) +#endif // _USE_MIDI_ROUTE_PER_CHANNEL_ + + for(int channel = 0; channel < MIDI_CHANNELS; ++channel) { + +#ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ + if(r_chan != -1 && channel != r_chan) + continue; +#else // _USE_MIDI_ROUTE_PER_CHANNEL_ if(!(channelMask & (1 << channel))) continue; +#endif // _USE_MIDI_ROUTE_PER_CHANNEL_ + if(!dev->sysexFIFOProcessed()) { // Set to the sysex fifo at first. - MusECore::MidiRecFifo& rf = dev->recordEvents(MIDI_CHANNELS); + MidiRecFifo& rf = dev->recordEvents(MIDI_CHANNELS); // Get the frozen snapshot of the size. int count = dev->tmpRecordCount(MIDI_CHANNELS); - - for(int i = 0; i < count; ++i) + + for(int i = 0; i < count; ++i) { - MusECore::MidiRecordEvent event(rf.peek(i)); - event.setPort(port); - // dont't echo controller changes back to software + MidiRecordEvent event(rf.peek(i)); + event.setPort(t_port); + event.setChannel(t_channel); + // don't echo controller changes back to software // synthesizer: - if(!dev->isSynti() && md && track->recEcho()) + if(md && track_rec_monitor) { - // All recorded events arrived in the previous period. Shift into this period for playback. - unsigned int et = event.time(); + // Do not echo synth events back to the same synth instance under any circumstances, + // not even if monitor (echo) is on. + if(!dev->isSynti() || dev != md) + { + // 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 + // The events arrived in the previous period. Shift into this period for playback. + // The events are already biased with the last frame time. + unsigned int t = et + MusEGlobal::segmentSize; + // Protection from slight errors in estimated frame time. + if(t >= (syncFrame + MusEGlobal::segmentSize)) + { + DEBUG_MIDI(stderr, "Error: Audio::processMidi(): sysex: t:%u >= syncFrame:%u + segmentSize:%u (==%u)\n", + t, syncFrame, MusEGlobal::segmentSize, syncFrame + MusEGlobal::segmentSize); + + t = syncFrame + (MusEGlobal::segmentSize - 1); + } + #endif + event.setTime(t); + md->putEvent(event, MidiDevice::NotLate); + event.setTime(et); // Restore for recording. + } + } + + unsigned int et = event.time(); + // Make sure the event is recorded in units of ticks. + if(extsync) + { + const unsigned int xt = extClockHistoryFrame2Tick(event.time()); + DEBUG_MIDI(stderr, "processMidi: event time:%d dataA:%d dataB:%d curTickPos:%u set time:%u\n", + event.time(), event.dataA(), event.dataB(), curTickPos, xt); + + event.setTime(xt); + } + else + { + // All recorded events arrived in the previous period. Shift into this period for record. #ifdef _AUDIO_USE_TRUE_FRAME_ unsigned int t = et - _previousPos.frame() + _pos.frame() + frameOffset; -#else - unsigned int t = et + frameOffset; +#else + unsigned int t = et + MusEGlobal::segmentSize; + // Protection from slight errors in estimated frame time. + if(t >= (syncFrame + MusEGlobal::segmentSize)) + { + DEBUG_MIDI(stderr, "Error: Audio::processMidi(): record sysex: t:%u >= syncFrame:%u + segmentSize:%u (==%u)\n", + t, syncFrame, MusEGlobal::segmentSize, syncFrame + MusEGlobal::segmentSize); + + t = syncFrame + (MusEGlobal::segmentSize - 1); + } #endif - event.setTime(t); - md->addScheduledEvent(event); - event.setTime(et); // Restore for recording. + // Be sure to allow for some (very) late events, such as + // the first chunk's time in a multi-chunk sysex. + const unsigned int a_fr = pos().frame() + t; + const unsigned int fin_fr = syncFrame > a_fr ? 0 : a_fr - syncFrame; + event.setTime(MusEGlobal::tempomap.frame2tick(fin_fr)); } + + // Is the transport recording, or, is it about to be from external sync? + if((recording || + (MusEGlobal::song->record() && extsync && MusEGlobal::midiSyncContainer.isPlaying())) + && track_rec_flag) + rl.add(event); - // 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); - } + event.setTime(et); // Restore. + } dev->setSysexFIFOProcessed(true); } - - MusECore::MidiRecFifo& rf = dev->recordEvents(channel); + + MidiRecFifo& rf = dev->recordEvents(channel); int count = dev->tmpRecordCount(channel); - for(int i = 0; i < count; ++i) + for(int i = 0; i < count; ++i) { - MusECore::MidiRecordEvent event(rf.peek(i)); + MidiRecordEvent event(rf.peek(i)); int defaultPort = devport; int drumRecPitch=0; //prevent compiler warning: variable used without initialization - MusECore::MidiController *mc = 0; + MidiController *mc = 0; int ctl = 0; - - //Hmmm, hehhh... - // TODO: Clean up a bit around here when it comes to separate events for rec & for playback. - // But not before 0.7 (ml) - int prePitch = 0, preVelo = 0; - - event.setChannel(track->outChannel()); - - if (event.isNote() || event.isNoteOff()) + + event.setPort(t_port); + event.setChannel(t_channel); + + if (event.isNote() || event.isNoteOff()) { // // apply track values // - + //Apply drum inkey: if (track->type() == Track::DRUM) { @@ -1066,14 +1876,26 @@ } else if (track->type() == Track::NEW_DRUM) { - event.setA(track->map_drum_in(event.dataA())); + int pitch = event.dataA(); + int dmindex = track->map_drum_in(pitch); + //Map note that is played according to MusEGlobal::drumInmap + drumRecPitch = track->drummap()[dmindex].enote; + // Default to track port if -1 and track channel if -1. + devport = track->drummap()[dmindex].port; + if(devport == -1) + devport = track->outPort(); + event.setPort(devport); + int mapchan = track->drummap()[dmindex].channel; + if(mapchan != -1) + event.setChannel(mapchan); + event.setA(track->drummap()[dmindex].anote); if (MusEGlobal::config.newDrumRecordCondition & MusECore::DONT_REC_HIDDEN && - track->drummap_hidden()[event.dataA()] ) + track->drummap()[dmindex].hide ) continue; // skip that event, proceed with the next if (MusEGlobal::config.newDrumRecordCondition & MusECore::DONT_REC_MUTED && - track->drummap()[event.dataA()].mute ) + track->drummap()[dmindex].mute ) continue; // skip that event, proceed with the next } else @@ -1086,27 +1908,29 @@ pitch = 0; event.setA(pitch); } - - if (!event.isNoteOff()) - { - preVelo = event.dataB(); - int velo = preVelo + track->velocity; - velo = (velo * track->compression) / 100; - if (velo > 127) - velo = 127; - if (velo < 1) - velo = 1; - event.setB(velo); - } + + // Apply track velocity and compression to note-on AND note-off events. + preVelo = event.dataB(); + int velo = preVelo + track->velocity; + velo = (velo * track->compression) / 100; + if (velo > 127) + velo = 127; + if (velo < 1) + // Zero means zero. Should mean no note at all? + //velo = 1; + velo = 0; // Use zero as a marker to tell the playback (below) not to sound the note. + + event.setB(velo); } else if(event.type() == MusECore::ME_CONTROLLER) { if(track->type() == Track::DRUM) { ctl = event.dataA(); - // Regardless of what port the event came from, is it a drum controller event + // Regardless of what port the event came from, is it a drum controller event // according to the track port's instrument? - mc = tport->drumController(ctl); + if(mp) + mc = mp->drumController(ctl); if(mc) { int pitch = ctl & 0x7f; @@ -1123,123 +1947,386 @@ if(mapchan != -1) event.setChannel(mapchan); event.setA(ctl | MusEGlobal::drumMap[dmindex].anote); - } + } } else if (track->type() == Track::NEW_DRUM) //FINDMICHJETZT TEST { ctl = event.dataA(); - if (tport->drumController(ctl)) // is it a drum controller? + // Regardless of what port the event came from, is it a drum controller event + // according to the track port's instrument? + if(mp) + mc = mp->drumController(ctl); + if(mc) { - int pitch = ctl & 0x7f; // pitch is now the incoming pitch - pitch = track->map_drum_in(pitch); // pitch is now the mapped (recorded) pitch - event.setA((ctl & ~0xff) | pitch); // map the drum ctrl's value accordingly + int pitch = ctl & 0x7f; // pitch is now the incoming pitch + ctl &= ~0xff; + int dmindex = track->map_drum_in(pitch) & 0x7f; + //Map note that is played according to drumInmap + drumRecPitch = track->drummap()[dmindex].enote; + // Default to track port if -1 and track channel if -1. + devport = track->drummap()[dmindex].port; + if(devport == -1) + devport = track->outPort(); + event.setPort(devport); + int mapchan = track->drummap()[dmindex].channel; + if(mapchan != -1) + event.setChannel(mapchan); + event.setA(ctl | track->drummap()[dmindex].anote); if (MusEGlobal::config.newDrumRecordCondition & MusECore::DONT_REC_HIDDEN && - track->drummap_hidden()[pitch] ) + track->drummap()[dmindex].hide ) continue; // skip that event, proceed with the next if (MusEGlobal::config.newDrumRecordCondition & MusECore::DONT_REC_MUTED && - track->drummap()[pitch].mute ) + track->drummap()[dmindex].mute ) continue; // skip that event, proceed with the next } } } - - // MusE uses a fixed clocks per quarternote of 24. - // At standard 384 ticks per quarternote for example, + + // MusE uses a fixed clocks per quarternote of 24. + // At standard 384 ticks per quarternote for example, // 384/24=16 for a division of 16 sub-frames (16 MusE 'ticks'). // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). p3.3.35 // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. - - // dont't echo controller changes back to software + + // don't echo controller changes back to software // synthesizer: - - if (!dev->isSynti()) + + // Zero means zero. Should mean no note at all? + // If the event is marked as a note with zero velocity (above), do not sound the note. + if(!event.isNote() || event.dataB() != 0) { - // All recorded events arrived in previous period. Shift into this period for playback. + + // 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; + // The events arrived in the previous period. Shift into this period for playback. + // The events are already biased with the last frame time. + unsigned int t = et + MusEGlobal::segmentSize; + // Protection from slight errors in estimated frame time. + if(t >= (syncFrame + MusEGlobal::segmentSize)) + { + DEBUG_MIDI(stderr, "Error: Audio::processMidi(): event: t:%u >= syncFrame:%u + segmentSize:%u (==%u)\n", + t, syncFrame, MusEGlobal::segmentSize, syncFrame + MusEGlobal::segmentSize); + + t = syncFrame + (MusEGlobal::segmentSize - 1); + } #endif - event.setTime(t); + event.setTime(t); // Check if we're outputting to another port than default: if (devport == defaultPort) { - event.setPort(port); - if(md && track->recEcho()) - md->addScheduledEvent(event); + event.setPort(t_port); + // REMOVE Tim. monitor. Changed. + //if(md && track_rec_monitor && !track->off() && !track->isMute()) + if(md && track_rec_monitor) + { + // Do not echo synth events back to the same synth instance under any circumstances, + // not even if monitor (echo) is on. + if(!dev->isSynti() || dev != md) + { + MidiInstrument* minstr = MusEGlobal::midiPorts[t_port].instrument(); + const MidiInstrument::NoteOffMode nom = minstr->noteOffMode(); + // If the instrument has no note-off mode, do not use the stuck notes mechanism, send as is. + // This allows drum input triggers (no note offs at all), although it is awkward to + // first have to choose an output instrument with no note-off mode. + if(nom == MidiInstrument::NoteOffNone) + { + if(event.isNoteOff()) + // Try to remove any corresponding stuck live note. + track->removeStuckLiveNote(t_port, event.channel(), event.dataA()); +// md->addScheduledEvent(event); + md->putEvent(event, MidiDevice::NotLate); + } + else if(event.isNoteOff()) + { + // Try to remove any corresponding stuck live note. + // Only if a stuck live note existed do we schedule the note off to play. + if(track->removeStuckLiveNote(t_port, event.channel(), event.dataA())) +// md->addScheduledEvent(event); + md->putEvent(event, MidiDevice::NotLate); + } + else if(event.isNote()) + { + // Check if a stuck live note exists on any track. + ciMidiTrack it_other = mtl->begin(); + for( ; it_other != mtl->end(); ++it_other) + { + if((*it_other)->stuckLiveNoteExists(t_port, event.channel(), event.dataA())) + break; + } + // Only if NO stuck live note existed do we schedule the note on to play. + if(it_other == mtl->end()) + { + if(track->addStuckLiveNote(t_port, event.channel(), event.dataA())) +// md->addScheduledEvent(event); + md->putEvent(event, MidiDevice::NotLate); + } + } + else + { + // TODO Maybe grab the flag from the 'Optimize Controllers' Global Setting, + // which so far was meant for (N)RPN stuff. For now, just force it. + // This is the audio thread. Just set directly. + MusEGlobal::midiPorts[t_port].setHwCtrlState(event); + md->putEvent(event, MidiDevice::NotLate); + } + } } + } else { - // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? MidiDevice* mdAlt = MusEGlobal::midiPorts[devport].device(); - if(mdAlt && track->recEcho()) - mdAlt->addScheduledEvent(event); + // REMOVE Tim. monitor. Changed. + //if(mdAlt && track_rec_monitor && !track->off() && !track->isMute()) + if(mdAlt && track_rec_monitor) + { + // Do not echo synth events back to the same synth instance under any circumstances, + // not even if monitor (echo) is on. + if(!dev->isSynti() || dev != mdAlt) + { + MidiInstrument* minstr = MusEGlobal::midiPorts[devport].instrument(); + MidiInstrument::NoteOffMode nom = minstr->noteOffMode(); + // If the instrument has no note-off mode, do not use the + // stuck notes mechanism, just send as is. + // If the instrument has no note-off mode, do not use the stuck notes mechanism, send as is. + // This allows drum input triggers (no note offs at all), although it is awkward to + // first have to choose an output instrument with no note-off mode. + if(nom == MidiInstrument::NoteOffNone) + { + if(event.isNoteOff()) + // Try to remove any corresponding stuck live note. + track->removeStuckLiveNote(event.port(), event.channel(), event.dataA()); +// mdAlt->addScheduledEvent(event); + mdAlt->putEvent(event, MidiDevice::NotLate); + } + else if(event.isNoteOff()) + { + // Try to remove any corresponding stuck live note. + // Only if a stuck live note existed do we schedule the note off to play. + if(track->removeStuckLiveNote(event.port(), event.channel(), event.dataA())) +// mdAlt->addScheduledEvent(event); + mdAlt->putEvent(event, MidiDevice::NotLate); + } + else if(event.isNote()) + { + // Check if a stuck live note exists on any track. + ciMidiTrack it_other = mtl->begin(); + for( ; it_other != mtl->end(); ++it_other) + { + if((*it_other)->stuckLiveNoteExists(event.port(), event.channel(), event.dataA())) + break; + } + // Only if NO stuck live note existed do we schedule the note on to play. + if(it_other == mtl->end()) + { + if(track->addStuckLiveNote(event.port(), event.channel(), event.dataA())) + mdAlt->putEvent(event, MidiDevice::NotLate); + } + } + else + { + // TODO Maybe grab the flag from the 'Optimize Controllers' Global Setting, + // which so far was meant for (N)RPN stuff. For now, just force it. + // This is the audio thread. Just set directly. + MusEGlobal::midiPorts[devport].setHwCtrlState(event); + mdAlt->putEvent(event, MidiDevice::NotLate); + } + } } + } 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()); - } - - // 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) - { - // In these next steps, it is essential to set the recorded event's port - // to the track port so buildMidiEventList will accept it. Even though - // the port may have no device "". - // - if (track->type() == Track::DRUM) //FINDMICHJETZT no changes. TEST + + // Shall we activate meters even while rec echo is off? Sure, why not... + if(event.isNote() && event.dataB() > track->activity()) + track->setActivity(event.dataB()); + } + + // Is the transport recording, or, is it about to be from external sync? + if((recording || + (MusEGlobal::song->record() && extsync && MusEGlobal::midiSyncContainer.isPlaying())) + && track_rec_flag) + { + unsigned int et = event.time(); + // Make sure the event is recorded in units of ticks. + if(extsync) + { + const unsigned int xt = extClockHistoryFrame2Tick(event.time()); + DEBUG_MIDI(stderr, "processMidi: event time:%d dataA:%d dataB:%d curTickPos:%u set time:%u\n", + event.time(), event.dataA(), event.dataB(), curTickPos, xt); + + event.setTime(xt); + } + else + { + // All recorded events arrived in the previous period. Shift into this period for record. + #ifdef _AUDIO_USE_TRUE_FRAME_ + unsigned int t = et - _previousPos.frame() + _pos.frame() + frameOffset; + #else + unsigned int t = et + MusEGlobal::segmentSize; + // Protection from slight errors in estimated frame time. + if(t >= (syncFrame + MusEGlobal::segmentSize)) { - // Is it a drum controller event? - if(mc) - { - MusECore::MidiPlayEvent drumRecEvent = event; - drumRecEvent.setA(ctl | drumRecPitch); - // In this case, preVelo is simply the controller value. - drumRecEvent.setB(preVelo); - drumRecEvent.setPort(port); //rec-event to current port - drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel - rl->add(drumRecEvent); - } - else - { - MusECore::MidiPlayEvent drumRecEvent = event; - drumRecEvent.setA(drumRecPitch); - drumRecEvent.setB(preVelo); - // Changed to 'port'. Events were not being recorded for a drum map entry pointing to a - // different port. That must have been wrong - buildMidiEventList would ignore that. Tim. - drumRecEvent.setPort(port); //rec-event to current port - drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel - rl->add(drumRecEvent); - } + DEBUG_MIDI(stderr, "Error: Audio::processMidi(): record event: t:%u >= syncFrame:%u + segmentSize:%u (==%u)\n", + t, syncFrame, MusEGlobal::segmentSize, syncFrame + MusEGlobal::segmentSize); + + t = syncFrame + (MusEGlobal::segmentSize - 1); } - else + #endif + // Be sure to allow for some (very) late events, such as + // the first chunk's time in a multi-chunk sysex. + const unsigned int a_fr = pos().frame() + t; + const unsigned int fin_fr = syncFrame > a_fr ? 0 : a_fr - syncFrame; + event.setTime(MusEGlobal::tempomap.frame2tick(fin_fr)); + } + + // In these next steps, it is essential to set the recorded event's port + // to the track port so buildMidiEventList will accept it. Even though + // the port may have no device "". + // + if (track->type() == Track::DRUM || track->type() == Track::NEW_DRUM) + { + // Is it a drum controller event? + if(mc) { - // Restore record-pitch to non-transposed value since we don't want the note transposed twice next - MusECore::MidiPlayEvent recEvent = event; - if (prePitch) - recEvent.setA(prePitch); - if (preVelo) - recEvent.setB(preVelo); - recEvent.setPort(port); - recEvent.setChannel(track->outChannel()); - - rl->add(recEvent); + MusECore::MidiPlayEvent drumRecEvent = event; + drumRecEvent.setA(ctl | drumRecPitch); + // In this case, preVelo is simply the controller value. + drumRecEvent.setB(preVelo); + drumRecEvent.setPort(t_port); //rec-event to current port + drumRecEvent.setChannel(t_channel); //rec-event to current channel + track->mpevents.add(drumRecEvent); } - } - } + else + { + MusECore::MidiPlayEvent drumRecEvent = event; + drumRecEvent.setA(drumRecPitch); + drumRecEvent.setB(preVelo); + // Changed to 'port'. Events were not being recorded for a drum map entry pointing to a + // different port. That must have been wrong - buildMidiEventList would ignore that. Tim. + drumRecEvent.setPort(t_port); //rec-event to current port + drumRecEvent.setChannel(t_channel); //rec-event to current channel + track->mpevents.add(drumRecEvent); + } + } + else + { + // Restore record-pitch to non-transposed value since we don't want the note transposed twice next + MusECore::MidiPlayEvent recEvent = event; + if (prePitch) + recEvent.setA(prePitch); + if (preVelo) + recEvent.setB(preVelo); + recEvent.setPort(t_port); + recEvent.setChannel(t_channel); + + track->mpevents.add(recEvent); + } + // Restore. Not required. + //event.setTime(et); + } } } } + } + + // Must be playing for valid nextTickPos, right? But wasn't checked in Audio::processMidi(). + // MusEGlobal::audio->isPlaying() might not be true during seek right now. + //if(MusEGlobal::audio->isPlaying()) + //if(playing) + //{ + ciMPEvent k; + MidiDevice* mdev; + int mport; + + // If muted or off we want to send all playback note-offs immediately. + if(track->isMute() || track->off()) + { + //--------------------------------------------------- + // Send all track-related playback note-offs (NOT 'live' note-offs) + // which were not put directly to the device + //--------------------------------------------------- + MPEventList& mel = track->stuckNotes; + if(!mel.empty()) + { + for(k = mel.begin(); k != mel.end(); ++k) + { + MidiPlayEvent ev(*k); + mport = ev.port(); + if(mport < 0) + continue; + mdev = MusEGlobal::midiPorts[mport].device(); + if(!mdev) + continue; + ev.setTime(0); // Mark for immediate delivery. + //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(k->time())); + mdev->putEvent(ev, MidiDevice::NotLate); + } + mel.clear(); + } + } + else + // If not muted and not off, we want to schedule all playback note-offs normally. + { + //--------------------------------------------------- + // Schedule all track-related playback note-offs (NOT 'live' note-offs) + // which were not put directly to the device + // To save time this was put here instead of MidiDevice::processStuckNotes() + //--------------------------------------------------- + MPEventList& mel = track->stuckNotes; + if(!mel.empty()) + { + for(k = mel.begin(); k != mel.end(); ++k) + { + if(k->time() >= nextTickPos) + break; + MidiPlayEvent ev(*k); + ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(k->time())); + mport = ev.port(); + if(t_port < 0) + continue; + mdev = MusEGlobal::midiPorts[mport].device(); + if(!mdev) + continue; + // TODO: DECIDE: Hm, we don't want the device to miss any note offs. + // So I guess schedule this as a user event rather than a playback event. + mdev->putEvent(ev, MidiDevice::NotLate); + } + mel.erase(mel.begin(), k); + } + } + + // If no monitor or off, or not rec-armed (or muted), we want to cancel all 'live' (rec) stuck notes immediately. + // REMOVE Tim. monitor. Changed. + //if(!track_rec_monitor || track->off() || track->isMute()) + if(!track_rec_monitor) + { + //------------------------------------------------------------ + // Send all track-related 'live' (rec) note-offs + // which were not put directly to the device + //------------------------------------------------------------ + MPEventList& mel = track->stuckLiveNotes; + if(!mel.empty()) + { + for(k = mel.begin(); k != mel.end(); ++k) + { + MidiPlayEvent ev(*k); + mport = ev.port(); + if(mport < 0) + continue; + mdev = MusEGlobal::midiPorts[mport].device(); + if(!mdev) + continue; + ev.setTime(0); // Mark for immediate delivery. + //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(k->time())); + mdev->putEvent(ev, MidiDevice::NotLate); + } + mel.clear(); + } + } + //} } //--------------------------------------------------- @@ -1249,12 +2336,12 @@ MidiDevice* md = 0; if (MusEGlobal::midiClickFlag) md = MusEGlobal::midiPorts[MusEGlobal::clickPort].device(); - if (MusEGlobal::song->click() && (isPlaying() || state == PRECOUNT)) { + if (MusEGlobal::song->click() && (playing || state == PRECOUNT)) { int bar, beat, z, n; unsigned tick; AudioTickSound audioTickSound = MusECore::beatSound; while (midiClick < nextTickPos) { - if (isPlaying()) { + if (playing) { AL::sigmap.tickValues(midiClick, &bar, &beat, &tick); AL::sigmap.timesig(midiClick, z, n); @@ -1262,28 +2349,29 @@ if (tick == 0 && beat == 0) { audioTickSound = MusECore::measureSound; if (MusEGlobal::debugMsg) - printf("meas: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d div %d\n", midiClick, nextTickPos, bar, beat, tick, z, n, MusEGlobal::config.division); + fprintf(stderr, "meas: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d div %d\n", midiClick, nextTickPos, bar, beat, tick, z, n, MusEGlobal::config.division); } else if (tick == unsigned(MusEGlobal::config.division - (MusEGlobal::config.division/(n*2)))) { audioTickSound = MusECore::accent2Sound; if (MusEGlobal::debugMsg) - printf("acc2: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d div %d\n", midiClick, nextTickPos, bar, beat, tick, z, n, MusEGlobal::config.division); + fprintf(stderr, "acc2: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d div %d\n", midiClick, nextTickPos, bar, beat, tick, z, n, MusEGlobal::config.division); } else if (tick == unsigned(MusEGlobal::config.division - (MusEGlobal::config.division/n))) { audioTickSound = MusECore::accent1Sound; if (MusEGlobal::debugMsg) - printf("acc1: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d div %d\n", midiClick, nextTickPos, bar, beat, tick, z, n, MusEGlobal::config.division); + fprintf(stderr, "acc1: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d div %d\n", midiClick, nextTickPos, bar, beat, tick, z, n, MusEGlobal::config.division); } else { if (MusEGlobal::debugMsg) - printf("beat: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d div %d\n", midiClick, nextTickPos, bar, beat, tick, z, n, MusEGlobal::config.division); + fprintf(stderr, "beat: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d div %d\n", midiClick, nextTickPos, bar, beat, tick, z, n, MusEGlobal::config.division); } } + else if (state == PRECOUNT) { if ((clickno % clicksMeasure) == 0) { audioTickSound = MusECore::measureSound; } } - int evtime = extsync ? midiClick : MusEGlobal::tempomap.tick2frame(midiClick) + frameOffset; // p3.3.25 + unsigned int evtime = MusEGlobal::audio->midiQueueTimeStamp(midiClick); MusECore::MidiPlayEvent ev(evtime, MusEGlobal::clickPort, MusEGlobal::clickChan, MusECore::ME_NOTEON, MusEGlobal::beatClickNote, MusEGlobal::beatClickVelo); if (audioTickSound == MusECore::measureSound) { @@ -1299,20 +2387,27 @@ ev.setB(MusEGlobal::accentClick2Velo); } if (md) { - md->addScheduledEvent(ev); - ev.setB(0); - ev.setTime(midiClick+10); - md->addStuckNote(ev); + MusECore::MidiPlayEvent evmidi = ev; + md->putEvent(evmidi, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); + // Internal midi paths are now all note off aware. Driver handles note offs. Convert. + // Ticksynth has been modified too. + evmidi.setType(MusECore::ME_NOTEOFF); + evmidi.setB(0); + evmidi.setTime(midiClick+10); + md->addStuckNote(evmidi); } if (MusEGlobal::audioClickFlag) { ev.setA(audioTickSound); - metronome->addScheduledEvent(ev); + metronome->putEvent(ev, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); // Built-in metronome synth does not use stuck notes... } - if (isPlaying()) { + if (playing) { // State machine to select next midiClick position. if (MusEGlobal::clickSamples == MusEGlobal::newSamples) { if (tick == 0) {// ON key + if (beat % 2) + midiClick = AL::sigmap.bar2tick(bar, beat+1, 0); + else midiClick = AL::sigmap.bar2tick(bar, beat, MusEGlobal::config.division - ((MusEGlobal::config.division/n))); } else if (tick >= unsigned(MusEGlobal::config.division - (MusEGlobal::config.division/(n*2)))) { // second accent tick @@ -1335,20 +2430,38 @@ } } } - + // // Play all midi events up to curFrame. // - for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) + for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { - // We are done with the 'frozen' recording fifos, remove the events. - (*id)->afterProcess(); + MidiDevice* pl_md = *id; + // We are done with the 'frozen' recording fifos, remove the events. + pl_md->afterProcess(); + + pl_md->processStuckNotes(); // ALSA devices handled by another thread. - if((*id)->deviceType() != MidiDevice::ALSA_MIDI) - (*id)->processMidi(); + const MidiDevice::MidiDeviceType typ = pl_md->deviceType(); + switch(typ) + { + case MidiDevice::ALSA_MIDI: + break; + + case MidiDevice::JACK_MIDI: + case MidiDevice::SYNTH_MIDI: + // The frame is not used by these devices but we pass it along anyway. + // Only ALSA devices need the frame. + pl_md->processMidi(syncFrame); + break; + } } - MusEGlobal::midiBusy=false; + + // Receive hardware state events sent from various threads to this audio thread. + // Update hardware state so gui controls are updated. + // Static. + MidiPort::processGui2AudioEvents(); } } // namespace MusECore diff -Nru muse-2.1.2/muse/midictrl_consts.h muse-3.0.2+ds1/muse/midictrl_consts.h --- muse-2.1.2/muse/midictrl_consts.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/midictrl_consts.h 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,111 @@ +//========================================================= +// MusE +// Linux Music Editor +// midictrl_consts.h +// +// (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) +// (C) Copyright 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 +// 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 __MIDICTRL_CONSTS_H__ +#define __MIDICTRL_CONSTS_H__ + +namespace MusECore { + +const int CTRL_HBANK = 0x00; +const int CTRL_LBANK = 0x20; + +const int CTRL_HDATA = 0x06; +const int CTRL_LDATA = 0x26; + +const int CTRL_DATA_INC = 0x60; +const int CTRL_DATA_DEC = 0x61; + +const int CTRL_HNRPN = 0x63; +const int CTRL_LNRPN = 0x62; + +const int CTRL_HRPN = 0x65; +const int CTRL_LRPN = 0x64; + +const int CTRL_MODULATION = 0x01; +const int CTRL_PORTAMENTO_TIME = 0x05; +const int CTRL_VOLUME = 0x07; +const int CTRL_PANPOT = 0x0a; +const int CTRL_EXPRESSION = 0x0b; +const int CTRL_SUSTAIN = 0x40; +const int CTRL_PORTAMENTO = 0x41; +const int CTRL_SOSTENUTO = 0x42; +const int CTRL_SOFT_PEDAL = 0x43; +const int CTRL_HARMONIC_CONTENT = 0x47; +const int CTRL_RELEASE_TIME = 0x48; +const int CTRL_ATTACK_TIME = 0x49; + +const int CTRL_BRIGHTNESS = 0x4a; +const int CTRL_PORTAMENTO_CONTROL = 0x54; +const int CTRL_REVERB_SEND = 0x5b; +const int CTRL_CHORUS_SEND = 0x5d; +const int CTRL_VARIATION_SEND = 0x5e; + +// Channel Mode controllers: +// Same as other 7-bit controllers, but "implements Mode control and +// special message by using reserved controller numbers 120-127" (MMA). +// +const int CTRL_ALL_SOUNDS_OFF = 0x78; // 120 +const int CTRL_RESET_ALL_CTRL = 0x79; // 121 +// +const int CTRL_LOCAL_OFF = 0x7a; // 122 + + +// controller types 0x10000 - 0x1ffff are 14 bit controller with +// 0x1xxyy +// xx - MSB controller +// yy - LSB controller + +// RPN - registered parameter numbers 0x20000 - +// NRPN - non registered parameter numbers 0x30000 - + +// internal controller types: +const int CTRL_INTERNAL_OFFSET = 0x40000; + +const int CTRL_PITCH = CTRL_INTERNAL_OFFSET; +const int CTRL_PROGRAM = CTRL_INTERNAL_OFFSET + 0x01; +const int CTRL_VELOCITY = CTRL_INTERNAL_OFFSET + 0x02; +const int CTRL_MASTER_VOLUME = CTRL_INTERNAL_OFFSET + 0x03; +const int CTRL_AFTERTOUCH = CTRL_INTERNAL_OFFSET + 0x04; +// NOTE: The range from CTRL_INTERNAL_OFFSET + 0x100 to CTRL_INTERNAL_OFFSET + 0x1ff is reserved +// for this control. (The low byte is reserved because this is a per-note control.) +const int CTRL_POLYAFTER = CTRL_INTERNAL_OFFSET + 0x1FF; // 100 to 1FF ! + +const int CTRL_VAL_UNKNOWN = 0x10000000; // used as unknown hwVal +const int CTRL_PROGRAM_VAL_DONT_CARE = 0xffffff; // High-bank, low-bank, and program are all 0xff don't care. + +const int CTRL_7_OFFSET = 0x00000; +const int CTRL_14_OFFSET = 0x10000; +const int CTRL_RPN_OFFSET = 0x20000; +const int CTRL_NRPN_OFFSET = 0x30000; +const int CTRL_RPN14_OFFSET = 0x50000; +const int CTRL_NRPN14_OFFSET = 0x60000; +const int CTRL_NONE_OFFSET = 0x70000; + +const int CTRL_OFFSET_MASK = 0xf0000; + +} // namespace MusECore + +#endif + + diff -Nru muse-2.1.2/muse/midictrl.cpp muse-3.0.2+ds1/muse/midictrl.cpp --- muse-2.1.2/muse/midictrl.cpp 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/midictrl.cpp 2017-12-17 21:07:38.000000000 +0000 @@ -23,11 +23,22 @@ //========================================================= #include +#include +#include "globaldefs.h" #include "midictrl.h" #include "xml.h" #include "globals.h" +#include "audio.h" +#include "midi.h" +#include "mpevent.h" +#include "midiport.h" +#include "minstrument.h" +#include "muse_math.h" + +#include "track.h" + namespace MusECore { static const char* ctrlName[] = { @@ -82,15 +93,16 @@ // // some global controller which are always available: // -MidiController veloCtrl("Velocity", CTRL_VELOCITY, 0, 127, 0); -MidiController pitchCtrl("PitchBend", CTRL_PITCH, -8192, +8191, 0); -MidiController programCtrl("Program", CTRL_PROGRAM, 0, 0xffffff, 0); -MidiController mastervolCtrl("MasterVolume", CTRL_MASTER_VOLUME, 0, 0x3fff, 0x3000); -MidiController volumeCtrl("MainVolume", CTRL_VOLUME, 0, 127, 100); -MidiController panCtrl("Pan", CTRL_PANPOT, -64, 63, 0); -MidiController reverbSendCtrl("ReverbSend", CTRL_REVERB_SEND, 0, 127, 0); -MidiController chorusSendCtrl("ChorusSend", CTRL_CHORUS_SEND, 0, 127, 0); -MidiController variationSendCtrl("VariationSend", CTRL_VARIATION_SEND, 0, 127, 0); +// Zero note on vel is not allowed now. +MidiController veloCtrl("Velocity", CTRL_VELOCITY, 1, 127, 0, 0); +MidiController pitchCtrl("PitchBend", CTRL_PITCH, -8192, +8191, 0, 0); +MidiController programCtrl("Program", CTRL_PROGRAM, 0, 0xffffff, 0, 0); +MidiController mastervolCtrl("MasterVolume", CTRL_MASTER_VOLUME, 0, 0x3fff, 0x3000, 0x3000); +MidiController volumeCtrl("MainVolume", CTRL_VOLUME, 0, 127, 100, 100); +MidiController panCtrl("Pan", CTRL_PANPOT, -64, 63, 0, 0); +MidiController reverbSendCtrl("ReverbSend", CTRL_REVERB_SEND, 0, 127, 0, 0); +MidiController chorusSendCtrl("ChorusSend", CTRL_CHORUS_SEND, 0, 127, 0, 0); +MidiController variationSendCtrl("VariationSend", CTRL_VARIATION_SEND, 0, 127, 0, 0); //--------------------------------------------------------- // ctrlType2Int @@ -248,16 +260,22 @@ : _name(QString("Velocity")) { _num = CTRL_VELOCITY; - _minVal = 0; + // Zero note on vel is not allowed now. + _minVal = 1; _maxVal = 127; - _initVal = 0; + _initVal = _drumInitVal = 0; _showInTracks = ShowInDrum | ShowInMidi; updateBias(); } -MidiController::MidiController(const QString& s, int n, int min, int max, int init, int show_in_track) +MidiController::MidiController(const QString& s, int n, int min, int max, int init, int drumInit, int show_in_track) : _name(s), _num(n), _minVal(min), _maxVal(max), _initVal(init), _showInTracks(show_in_track) { + // If drumInit was given, use it otherwise set it to the normal init val. + if(drumInit != -1) + _drumInitVal = drumInit; + else + _drumInitVal = _initVal; updateBias(); } @@ -272,12 +290,13 @@ void MidiController::copy(const MidiController &mc) { - _name = mc._name; - _num = mc._num; - _minVal = mc._minVal; - _maxVal = mc._maxVal; - _initVal = mc._initVal; - _bias = mc._bias; + _name = mc._name; + _num = mc._num; + _minVal = mc._minVal; + _maxVal = mc._maxVal; + _initVal = mc._initVal; + _drumInitVal = mc._drumInitVal; + _bias = mc._bias; _showInTracks = mc._showInTracks; } @@ -496,6 +515,8 @@ { if(_initVal != CTRL_VAL_UNKNOWN && _initVal != 0xffffff) xml.nput(" init=\"0x%x\"", _initVal); + if(_drumInitVal != CTRL_VAL_UNKNOWN && _drumInitVal != 0xffffff) + xml.nput(" drumInit=\"0x%x\"", _drumInitVal); } else { @@ -506,6 +527,8 @@ if(_initVal != CTRL_VAL_UNKNOWN) xml.nput(" init=\"%d\"", _initVal); + if(_drumInitVal != CTRL_VAL_UNKNOWN) + xml.nput(" drumInit=\"%d\"", _drumInitVal); } if(_showInTracks != (ShowInDrum | ShowInMidi)) @@ -522,6 +545,7 @@ { ControllerType t = Controller7; _initVal = CTRL_VAL_UNKNOWN; + int drum_init = -1; // -1 = Not set yet. static const int NOT_SET = 0x100000; _minVal = NOT_SET; _maxVal = NOT_SET; @@ -536,6 +560,7 @@ switch (token) { case Xml::Error: case Xml::End: + _drumInitVal = _initVal; return; case Xml::Attribut: { @@ -561,6 +586,8 @@ _maxVal = xml.s2().toInt(&ok, base); else if (tag == "init") _initVal = xml.s2().toInt(&ok, base); + else if (tag == "drumInit") + drum_init = xml.s2().toInt(&ok, base); else if (tag == "showType") _showInTracks = xml.s2().toInt(&ok, base); } @@ -632,6 +659,11 @@ } if (_minVal == NOT_SET) _minVal = 0; + // No drum init val given? Use normal init val. + if(drum_init == -1) + _drumInitVal = _initVal; + else + _drumInitVal = drum_init; updateBias(); return; } @@ -639,6 +671,7 @@ break; } } + _drumInitVal = _initVal; } //--------------------------------------------------------- @@ -681,11 +714,97 @@ MidiCtrlValList::MidiCtrlValList(int c) { ctrlNum = c; - _hwVal = CTRL_VAL_UNKNOWN; - _lastValidHWVal = CTRL_VAL_UNKNOWN; + _hwVal = _lastValidHWVal = _lastValidByte2 = _lastValidByte1 = _lastValidByte0 = CTRL_VAL_UNKNOWN; } //--------------------------------------------------------- +// MidiCtrlValListList +//--------------------------------------------------------- + +MidiCtrlValListList::MidiCtrlValListList() +{ + _RPN_Ctrls_Reserved = false; +} + +// TODO: Finish copy constructor, but first MidiCtrlValList might need one too ? +// MidiCtrlValListList::MidiCtrlValListList(const MidiCtrlValListList& mcvl) : std::map() +// { +// for(ciMidiCtrlValList i = mcvl.begin(); i != mcvl.end(); ++i) +// { +// MidiCtrlValList* mcl = i->second; +// add(new MidiCtrlValList(*mcl)); +// } +// update_RPN_Ctrls_Reserved(); +// } + +void MidiCtrlValListList::add(int channel, MidiCtrlValList* vl, bool update) +{ + // TODO: If per-channel instruments are ever added to MusE, this routine would need changing. + + const int num = vl->num(); + if(!_RPN_Ctrls_Reserved && update) + { + const bool isCtl7 = ((num & CTRL_OFFSET_MASK) == CTRL_7_OFFSET); + const bool isCtl14 = ((num & CTRL_OFFSET_MASK) == CTRL_14_OFFSET); + if(isCtl14 || isCtl7) + { + const int l = num & 0xff; + if(l == CTRL_HDATA || + l == CTRL_LDATA || + l == CTRL_DATA_INC || + l == CTRL_DATA_DEC || + l == CTRL_HNRPN || + l == CTRL_LNRPN || + l == CTRL_HRPN || + l == CTRL_LRPN) + _RPN_Ctrls_Reserved = true; + } + if(!_RPN_Ctrls_Reserved && isCtl14) + { + const int h = (num >> 8) & 0xff; + if(h == CTRL_HDATA || + h == CTRL_LDATA || + h == CTRL_DATA_INC || + h == CTRL_DATA_DEC || + h == CTRL_HNRPN || + h == CTRL_LNRPN || + h == CTRL_HRPN || + h == CTRL_LRPN) + _RPN_Ctrls_Reserved = true; + } + } + insert(std::pair((channel << 24) + num, vl)); +} + +void MidiCtrlValListList::del(iMidiCtrlValList ictl, bool update) +{ + erase(ictl); + if(update) + update_RPN_Ctrls_Reserved(); +} + +MidiCtrlValListList::size_type MidiCtrlValListList::del(int num, bool update) +{ + MidiCtrlValListList::size_type res = erase(num); + if(update) + update_RPN_Ctrls_Reserved(); + return res; +} + +void MidiCtrlValListList::del(iMidiCtrlValList first, iMidiCtrlValList last, bool update) +{ + erase(first, last); + if(update) + update_RPN_Ctrls_Reserved(); +} + +void MidiCtrlValListList::clr() +{ + clear(); + update_RPN_Ctrls_Reserved(); +} + +//--------------------------------------------------------- // clearDelete //--------------------------------------------------------- @@ -701,24 +820,279 @@ } } if(deleteLists) - clear(); + clr(); } - + +//--------------------------------------------------------- +// resetAllHwVals +//--------------------------------------------------------- + +bool MidiCtrlValListList::resetAllHwVals(bool doLastHwValue) +{ + bool changed = false; + for(iMidiCtrlValList imcvl = begin(); imcvl != end(); ++imcvl) + { + if(imcvl->second) + { + if(imcvl->second->resetHwVal(doLastHwValue)) + changed = true; + } + } + return changed; +} + +//--------------------------------------------------------- +// searchControllers +//--------------------------------------------------------- + +iMidiCtrlValList MidiCtrlValListList::searchControllers(int channel, int ctl) +{ + const int type = ctl & CTRL_OFFSET_MASK; + const unsigned ch_bits = (channel << 24); + int n; + + // Looking for Controller7? See if any Controller14 contains the number and should be used instead. + if(type == CTRL_7_OFFSET) + { + const int num = ctl & 0xff; + for(iMidiCtrlValList imc = lower_bound(ch_bits | CTRL_14_OFFSET); imc != end(); ++imc) + { + // There is ->second->num(), but this is only way to get channel. + n = imc->first; + // Stop if we went beyond this channel number or this ctrl14 block. + if((n & 0xff000000) != ch_bits || (n & CTRL_OFFSET_MASK) != CTRL_14_OFFSET) + break; + if(((n >> 8) & 0xff) == num || (n & 0xff) == num) + return imc; + } + } + // Looking for RPN? See if any RPN14 also uses the number and should be used instead. + else if (type == CTRL_RPN_OFFSET) + { + const int num = ctl & 0xffff; + for(iMidiCtrlValList imc = lower_bound(ch_bits | CTRL_RPN14_OFFSET); imc != end(); ++imc) + { + // There is ->second->num(), but this is only way to get channel. + n = imc->first; + // Stop if we went beyond this channel number or this RPN14 block. + if((n & 0xff000000) != ch_bits || (n & CTRL_OFFSET_MASK) != CTRL_RPN14_OFFSET) + break; + if((n & 0xffff) == num) + return imc; + } + } + // Looking for NRPN? See if any NRPN14 also uses the number and should be used instead. + else if (type == CTRL_NRPN_OFFSET) + { + const int num = ctl & 0xffff; + for(iMidiCtrlValList imc = lower_bound(ch_bits | CTRL_NRPN14_OFFSET); imc != end(); ++imc) + { + // There is ->second->num(), but this is only way to get channel. + n = imc->first; + // Stop if we went beyond this channel number or this NRPN14 block. + if((n & 0xff000000) != ch_bits || (n & CTRL_OFFSET_MASK) != CTRL_NRPN14_OFFSET) + break; + if((n & 0xffff) == num) + return imc; + } + } + + // Looking for any other type? Do a regular find. + return std::map >::find(ch_bits | ctl); +} + +//--------------------------------------------------------- +// update_RPN_Ctrls_Reserved +// Manual check and update of the flag. For convenience, returns the flag. +// Cost depends on types and number of list controllers, so it is good for deferring +// an update until AFTER some lengthy list operation. JUST BE SURE to call this! +//--------------------------------------------------------- + +bool MidiCtrlValListList::update_RPN_Ctrls_Reserved() +{ + // TODO: If per-channel instruments are ever added to MusE, this routine would need changing. + + int num, h, l; + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + const unsigned ch_bits = (ch << 24); + + if(find(ch, CTRL_HDATA) != end() || + find(ch, CTRL_LDATA) != end() || + find(ch, CTRL_DATA_INC) != end() || + find(ch, CTRL_DATA_DEC) != end() || + find(ch, CTRL_HNRPN) != end() || + find(ch, CTRL_LNRPN) != end() || + find(ch, CTRL_HRPN) != end() || + find(ch, CTRL_LRPN) != end()) + { + _RPN_Ctrls_Reserved = true; + return true; + } + + // Search: Get a head-start by taking lower bound. + for(iMidiCtrlValList imc = lower_bound(ch_bits | CTRL_14_OFFSET); imc != end(); ++imc) + { + // There is ->second->num(), but this is only way to get channel. + num = imc->first; + // Stop if we went beyond this channel number or its ctrl14 block. + if((num & 0xff000000) != ch_bits || (num & CTRL_OFFSET_MASK) != CTRL_14_OFFSET) + { + _RPN_Ctrls_Reserved = false; + return false; + } + h = (num >> 8) & 0xff; + l = num & 0xff; + if(h == CTRL_HDATA || l == CTRL_HDATA || + h == CTRL_LDATA || l == CTRL_LDATA || + h == CTRL_DATA_INC || l == CTRL_DATA_INC || + h == CTRL_DATA_DEC || l == CTRL_DATA_DEC || + h == CTRL_HNRPN || l == CTRL_HNRPN || + h == CTRL_LNRPN || l == CTRL_LNRPN || + h == CTRL_HRPN || l == CTRL_HRPN || + h == CTRL_LRPN || l == CTRL_LRPN) + { + _RPN_Ctrls_Reserved = true; + return true; + } + } + } + + _RPN_Ctrls_Reserved = false; + return false; +} + +//--------------------------------------------------------- +// Catch all insert, erase, clear etc. +//--------------------------------------------------------- + +MidiCtrlValListList& MidiCtrlValListList::operator=(const MidiCtrlValListList& cl) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiCtrlValListList::operator=\n"); +#endif + _RPN_Ctrls_Reserved = cl._RPN_Ctrls_Reserved; + + // Let map copy the items. + std::map >::operator=(cl); + return *this; +} + +//========================================================= +#ifdef _MIDI_CTRL_METHODS_DEBUG_ + +void MidiCtrlValListList::swap(MidiCtrlValListList& cl) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiCtrlValListList::swap\n"); +#endif + std::map >::swap(cl); +} + +std::pair MidiCtrlValListList::insert(const std::pair& p) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiCtrlValListList::insert num:%d\n", p.second->num()); +#endif + std::pair res = std::map >::insert(p); + return res; +} + +iMidiCtrlValList MidiCtrlValListList::insert(iMidiCtrlValList ic, const std::pair& p) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiCtrlValListList::insertAt num:%d\n", p.second->num()); +#endif + iMidiCtrlValList res = std::map >::insert(ic, p); + return res; +} + +void MidiCtrlValListList::erase(iMidiCtrlValList ictl) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiCtrlValListList::erase iMidiCtrlValList num:%d\n", ictl->second->num()); +#endif + std::map >::erase(ictl); +} + +MidiCtrlValListList::size_type MidiCtrlValListList::erase(int num) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiCtrlValListList::erase num:%d\n", num); +#endif + size_type res = std::map >::erase(num); + return res; +} + +void MidiCtrlValListList::erase(iMidiCtrlValList first, iMidiCtrlValList last) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiCtrlValListList::erase range first num:%d second num:%d\n", + first->second->num(), last->second->num()); +#endif + std::map >::erase(first, last); +} + +void MidiCtrlValListList::clear() +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiCtrlValListList::clear\n"); +#endif + std::map >::clear(); +} + +#endif +// ========================================================= + + +bool MidiCtrlValList::resetHwVal(bool doLastHwValue) +{ + bool changed = false; + if(!hwValIsUnknown()) + { + _hwVal = CTRL_VAL_UNKNOWN; + changed = true; + } + + if(doLastHwValue) + { + if(!lastHwValIsUnknown()) + changed = true; + _lastValidHWVal = _lastValidByte2 = _lastValidByte1 = _lastValidByte0 = CTRL_VAL_UNKNOWN; + } + + return changed; +} + //--------------------------------------------------------- // setHwVal // Returns false if value is already equal, true if value is changed. //--------------------------------------------------------- - -bool MidiCtrlValList::setHwVal(const int v) -{ - if(_hwVal == v) + +bool MidiCtrlValList::setHwVal(const double v) +{ + const double r_v = muse_round2micro(v); + if(_hwVal == r_v) return false; - - _hwVal = v; - if(_hwVal != CTRL_VAL_UNKNOWN) + + _hwVal = r_v; + + const int i_val = MidiController::dValToInt(_hwVal); + if(!MidiController::iValIsUnknown(i_val)) + { _lastValidHWVal = _hwVal; - - return true; + const int hb = (i_val >> 16) & 0xff; + const int lb = (i_val >> 8) & 0xff; + const int pr = i_val & 0xff; + if(hb >= 0 && hb <= 127) + _lastValidByte2 = hb; + if(lb >= 0 && lb <= 127) + _lastValidByte1 = lb; + if(pr >= 0 && pr <= 127) + _lastValidByte0 = pr; + } + + return true; } //--------------------------------------------------------- @@ -729,20 +1103,37 @@ // Returns false if both values are already set, true if either value is changed. //--------------------------------------------------------- -bool MidiCtrlValList::setHwVals(const int v, int const lastv) +bool MidiCtrlValList::setHwVals(const double v, const double lastv) { - if(_hwVal == v && _lastValidHWVal == lastv) + const double r_v = muse_round2micro(v); + const double r_lastv = muse_round2micro(lastv); + + if(_hwVal == r_v && _lastValidHWVal == r_lastv) return false; - - _hwVal = v; + + _hwVal = r_v; // Don't want to break our own rules - _lastValidHWVal can't be unknown while _hwVal is valid... // But _hwVal can be unknown while _lastValidHWVal is valid... - if(lastv == CTRL_VAL_UNKNOWN) + if(MidiController::dValIsUnknown(r_lastv)) _lastValidHWVal = _hwVal; - else - _lastValidHWVal = lastv; - - return true; + else + _lastValidHWVal = r_lastv; + + const int i_lasthwval = MidiController::dValToInt(_lastValidHWVal); + if(!MidiController::iValIsUnknown(i_lasthwval)) + { + const int hb = (i_lasthwval >> 16) & 0xff; + const int lb = (i_lasthwval >> 8) & 0xff; + const int pr = i_lasthwval & 0xff; + if(hb >= 0 && hb <= 127) + _lastValidByte2 = hb; + if(lb >= 0 && lb <= 127) + _lastValidByte1 = lb; + if(pr >= 0 && pr <= 127) + _lastValidByte0 = pr; + } + + return true; } //--------------------------------------------------------- @@ -819,6 +1210,81 @@ return CTRL_VAL_UNKNOWN; } +int MidiCtrlValList::visibleValue(unsigned int tick, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const +{ + // Determine value at tick, using values stored by ANY part, + // ignoring values that are OUTSIDE of their parts, or muted or off parts or tracks... + + // Get the first value found with with a tick equal or greater than specified tick. + ciMidiCtrlVal i = lower_bound(tick); + // Since values from different parts can have the same tick, scan for part in all values at that tick. + for(ciMidiCtrlVal j = i; j != end() && (unsigned int)j->first == tick; ++j) + { + const Part* part = j->second.part; + // Ignore values that are outside of the part. + if(tick < part->tick() || tick >= (part->tick() + part->lenTick())) + continue; + // Ignore if part or track is muted or off. + if(!inclMutedParts && part->mute()) + continue; + const Track* track = part->track(); + if(track && ((!inclMutedTracks && track->isMute()) || (!inclOffTracks && track->off()))) + continue; + return j->second.val; + } + // Scan for part in all previous values, regardless of tick. + while(i != begin()) + { + --i; + const Part* part = i->second.part; + // Ignore values that are outside of the part. + if(tick < part->tick() || tick >= (part->tick() + part->lenTick())) + continue; + // Ignore if part or track is muted or off. + if(!inclMutedParts && part->mute()) + continue; + const Track* track = part->track(); + if(track && ((!inclMutedTracks && track->isMute()) || (!inclOffTracks && track->off()))) + continue; + return i->second.val; + } + // No previous values were found belonging to the specified part. + return CTRL_VAL_UNKNOWN; +} + +int MidiCtrlValList::visibleValue(unsigned int tick, Part* part, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const +{ + // Determine value at tick, using values stored by the SPECIFIC part, + // ignoring values that are OUTSIDE of the part, or muted or off part or track... + + if((!inclMutedParts && part->mute()) || (part->track() && ((!inclMutedTracks && part->track()->isMute()) || (!inclOffTracks && part->track()->off())))) + return CTRL_VAL_UNKNOWN; + + // Get the first value found with with a tick equal or greater than specified tick. + ciMidiCtrlVal i = lower_bound(tick); + // Ignore if part or track is muted or off. + // Since values from different parts can have the same tick, scan for part in all values at that tick. + for(ciMidiCtrlVal j = i; j != end() && (unsigned int)j->first == tick; ++j) + { + if(j->second.part == part) + { + // Ignore values that are outside of the part. + if(tick < part->tick() || tick >= (part->tick() + part->lenTick())) + continue; + return j->second.val; + } + } + // Scan for part in all previous values, regardless of tick. + while(i != begin()) + { + --i; + if(i->second.part == part) + return i->second.val; + } + // No previous values were found belonging to the specified part. + return CTRL_VAL_UNKNOWN; +} + //--------------------------------------------------------- // add // return true if new controller value added or replaced @@ -837,10 +1303,7 @@ return false; } - MidiCtrlVal v; - v.val = val; - v.part = part; - insert(std::pair (tick, v)); + insert(std::pair (tick, MidiCtrlVal(part, val))); return true; } @@ -878,6 +1341,11 @@ // MidiControllerList //--------------------------------------------------------- +MidiControllerList::MidiControllerList() +{ + _RPN_Ctrls_Reserved = false; +} + MidiControllerList::MidiControllerList(const MidiControllerList& mcl) : std::map() { for(ciMidiController i = mcl.begin(); i != mcl.end(); ++i) @@ -885,10 +1353,266 @@ MidiController* mc = i->second; add(new MidiController(*mc)); } + update_RPN_Ctrls_Reserved(); +} + +void MidiControllerList::add(MidiController* mc, bool update) +{ + const int num = mc->num(); + if(!_RPN_Ctrls_Reserved && update) + { + const bool isCtl7 = ((num & CTRL_OFFSET_MASK) == CTRL_7_OFFSET); + const bool isCtl14 = ((num & CTRL_OFFSET_MASK) == CTRL_14_OFFSET); + if(isCtl14 || isCtl7) + { + const int l = num & 0xff; + if(l == CTRL_HDATA || + l == CTRL_LDATA || + l == CTRL_DATA_INC || + l == CTRL_DATA_DEC || + l == CTRL_HNRPN || + l == CTRL_LNRPN || + l == CTRL_HRPN || + l == CTRL_LRPN) + _RPN_Ctrls_Reserved = true; + } + if(!_RPN_Ctrls_Reserved && isCtl14) + { + const int h = (num >> 8) & 0xff; + if(h == CTRL_HDATA || + h == CTRL_LDATA || + h == CTRL_DATA_INC || + h == CTRL_DATA_DEC || + h == CTRL_HNRPN || + h == CTRL_LNRPN || + h == CTRL_HRPN || + h == CTRL_LRPN) + _RPN_Ctrls_Reserved = true; + } + } + insert(std::pair(num, mc)); +} + +void MidiControllerList::del(iMidiController ictl, bool update) +{ + erase(ictl); + if(update) + update_RPN_Ctrls_Reserved(); +} + +MidiControllerList::size_type MidiControllerList::del(int num, bool update) +{ + MidiControllerList::size_type res = erase(num); + if(update) + update_RPN_Ctrls_Reserved(); + return res; +} + +void MidiControllerList::del(iMidiController first, iMidiController last, bool update) +{ + erase(first, last); + if(update) + update_RPN_Ctrls_Reserved(); +} + +void MidiControllerList::clr() +{ + clear(); + update_RPN_Ctrls_Reserved(); +} + +//--------------------------------------------------------- +// update_RPN_Ctrls_Reserved +// Manual check and update of the flag. For convenience, returns the flag. +// Cost depends on types and number of list controllers, so it is good for deferring +// an update until AFTER some lengthy list operation. JUST BE SURE to call this! +//--------------------------------------------------------- + +bool MidiControllerList::update_RPN_Ctrls_Reserved() +{ + if(find(CTRL_HDATA) != end() || + find(CTRL_LDATA) != end() || + find(CTRL_DATA_INC) != end() || + find(CTRL_DATA_DEC) != end() || + find(CTRL_HNRPN) != end() || + find(CTRL_LNRPN) != end() || + find(CTRL_HRPN) != end() || + find(CTRL_LRPN) != end()) + { + _RPN_Ctrls_Reserved = true; + return true; + } + + int num, h, l; + // Search: Get a head-start by taking lower bound. + for(iMidiController imc = lower_bound(CTRL_14_OFFSET); imc != end(); ++imc) + { + num = imc->second->num(); + // Stop if we went beyond this ctrl14 block. + if((num & CTRL_OFFSET_MASK) != CTRL_14_OFFSET) + { + _RPN_Ctrls_Reserved = false; + return false; + } + h = (num >> 8) & 0xff; + l = num & 0xff; + if(h == CTRL_HDATA || l == CTRL_HDATA || + h == CTRL_LDATA || l == CTRL_LDATA || + h == CTRL_DATA_INC || l == CTRL_DATA_INC || + h == CTRL_DATA_DEC || l == CTRL_DATA_DEC || + h == CTRL_HNRPN || l == CTRL_HNRPN || + h == CTRL_LNRPN || l == CTRL_LNRPN || + h == CTRL_HRPN || l == CTRL_HRPN || + h == CTRL_LRPN || l == CTRL_LRPN) + { + _RPN_Ctrls_Reserved = true; + return true; + } + } + _RPN_Ctrls_Reserved = false; + return false; +} + +//--------------------------------------------------------- +// Catch all insert, erase, clear etc. +//--------------------------------------------------------- + +MidiControllerList& MidiControllerList::operator=(const MidiControllerList& cl) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiControllerList::operator=\n"); +#endif + _RPN_Ctrls_Reserved = cl._RPN_Ctrls_Reserved; + + // Let map copy the items. + std::map >::operator=(cl); + return *this; +} + +//========================================================= +#ifdef _MIDI_CTRL_METHODS_DEBUG_ + +void MidiControllerList::swap(MidiControllerList& cl) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiControllerList::swap\n"); +#endif + std::map >::swap(cl); +} + +std::pair MidiControllerList::insert(const std::pair& p) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiControllerList::insert num:%d\n", p.second->num()); +#endif + std::pair res = std::map >::insert(p); + return res; +} + +iMidiController MidiControllerList::insert(iMidiController ic, const std::pair& p) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiControllerList::insertAt num:%d\n", p.second->num()); +#endif + iMidiController res = std::map >::insert(ic, p); + return res; +} + +void MidiControllerList::erase(iMidiController ictl) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiControllerList::erase iMidiController num:%d\n", ictl->second->num()); +#endif + std::map >::erase(ictl); +} + +MidiControllerList::size_type MidiControllerList::erase(int num) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiControllerList::erase num:%d\n", num); +#endif + size_type res = std::map >::erase(num); + return res; +} + +void MidiControllerList::erase(iMidiController first, iMidiController last) +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiControllerList::erase range first num:%d second num:%d\n", + first->second->num(), last->second->num()); +#endif + std::map >::erase(first, last); +} + +void MidiControllerList::clear() +{ +#ifdef _MIDI_CTRL_DEBUG_ + printf("MidiControllerList::clear\n"); +#endif + std::map >::clear(); +} + +#endif +// ========================================================= + +//--------------------------------------------------------- +// searchControllers +//--------------------------------------------------------- + +iMidiController MidiControllerList::searchControllers(int ctl) +{ + const int type = ctl & CTRL_OFFSET_MASK; + int n; + + // Looking for Controller7? See if any Controller14 contains the number and should be used instead. + if(type == CTRL_7_OFFSET) + { + const int num = ctl & 0xff; + for(iMidiController imc = lower_bound(CTRL_14_OFFSET); imc != end(); ++imc) + { + n = imc->second->num(); + // Stop if we went beyond this ctrl14 block. + if((n & CTRL_OFFSET_MASK) != CTRL_14_OFFSET) + break; + if(((n >> 8) & 0xff) == num || (n & 0xff) == num) + return imc; + } + } + // Looking for RPN? See if any RPN14 also uses the number and should be used instead. + else if (type == CTRL_RPN_OFFSET) + { + const int num = ctl & 0xffff; + for(iMidiController imc = lower_bound(CTRL_RPN14_OFFSET); imc != end(); ++imc) + { + n = imc->second->num(); + // Stop if we went beyond this RPN14 block. + if((n & CTRL_OFFSET_MASK) != CTRL_RPN14_OFFSET) + break; + if((n & 0xffff) == num) + return imc; + } + } + // Looking for NRPN? See if any NRPN14 also uses the number and should be used instead. + else if (type == CTRL_NRPN_OFFSET) + { + const int num = ctl & 0xffff; + for(iMidiController imc = lower_bound(CTRL_NRPN14_OFFSET); imc != end(); ++imc) + { + n = imc->second->num(); + // Stop if we went beyond this NRPN14 block. + if((n & CTRL_OFFSET_MASK) != CTRL_NRPN14_OFFSET) + break; + if((n & 0xffff) == num) + return imc; + } + } + + // Looking for any other type? Do a regular find. + return find(ctl); } //--------------------------------------------------------- -// ctrlAvailable (static) +// ctrlAvailable // Check if either a per-note controller, or else a regular controller already exists. //--------------------------------------------------------- @@ -911,6 +1635,450 @@ return imc == end(); } +//--------------------------------------------------------- +// MidiEncoder +//--------------------------------------------------------- + +MidiEncoder::MidiEncoder() +{ + _curMode = EncIdle; + // Try starting with ParamModeUnknown. Requires either RPN or NRPN params at least once. + // Possibly may want to start with ParamModeRPN or ParamModeNRPN, + // possibly make it depend on planned all-encompassing 'Optimizations' Setting, + // and provide reset with 'Panic' button, just as it now resets output optimizations. + _curParamMode = ParamModeUnknown; + _curData = 0; + _curTime = 0; + _timer = 0; + _curCtrl = 0; + _nextCtrl = 0; + _curRPNH = 0; + _curRPNL = 0; + _curNRPNL = 0; + _curNRPNH = 0; +} + +//--------------------------------------------------------- +// encodeEvent +//--------------------------------------------------------- + +void MidiEncoder::encodeEvent(const MidiRecordEvent& ev, int port, int channel) +{ + const int type = ev.type(); + if(type != ME_PITCHBEND || type != ME_AFTERTOUCH || type != ME_POLYAFTER || type != ME_PROGRAM || type != ME_CONTROLLER) + return; + + MidiPort* mp = &MusEGlobal::midiPorts[port]; + + MidiCtrlValListList* mcvll = mp->controller(); + MidiInstrument* instr = mp->instrument(); + MidiControllerList* mcl = instr->controller(); + + int num; + int data; + //int ctrlH; + //int ctrlL; + //const int ch_bits = channel << 24; + + if(_curMode != EncIdle) + { + if(_curMode == EncCtrl14) + num = CTRL_14_OFFSET + ((_curCtrl << 8) | _nextCtrl); + else if(_curMode == EncRPN14) + num = CTRL_RPN14_OFFSET + ((_curRPNH << 8) | _curRPNL); + else if(_curMode == EncNRPN14) + num = CTRL_NRPN14_OFFSET + ((_curNRPNH << 8) | _curNRPNL); + else + { + // Error + _curMode = EncIdle; + return; + } + + iMidiCtrlValList imcvl = mcvll->find(channel, num); + if(imcvl == mcvll->end()) + { + // Error, controller should exist + _curMode = EncIdle; + return; + } + MidiCtrlValList* mcvl = imcvl->second; + + if(type == ME_CONTROLLER && ev.dataA() == _nextCtrl) + { + data = (_curData << 7) | (ev.dataB() & 0x7f); + mcvl->setHwVal(data); + _curMode = EncIdle; + return; + } + else + { + data = (_curData << 7) | (mcvl->hwVal() & 0x7f); + mcvl->setHwVal(data); + //_curMode = EncIdle; + //return; + } + + //return; + } + + if(type == ME_CONTROLLER) + { + num = ev.dataA(); + const int val = ev.dataB(); + // Is it one of the 8 reserved GM controllers for RPN/NRPN? + if(num == CTRL_HDATA || num == CTRL_LDATA || num == CTRL_DATA_INC || num == CTRL_DATA_DEC || + num == CTRL_HNRPN || num == CTRL_LNRPN || num == CTRL_HRPN || num == CTRL_LRPN) + { + // Does the working controller list, and instrument, allow RPN/NRPN? + // (If EITHER the working controller list or instrument defines ANY of these + // 8 controllers as plain 7-bit, then we cannot accept this data as RPN/NRPN.) + const bool rpn_reserved = mcvll->RPN_Ctrls_Reserved() | mcl->RPN_Ctrls_Reserved(); + if(!rpn_reserved) + { + switch(num) + { + case CTRL_HDATA: + { + _curData = val; + switch(_curParamMode) + { + case ParamModeUnknown: + return; // Sorry, we shouldn't accept it without valid (N)RPN numbers. + case ParamModeRPN: + { + const int param_num = (_curRPNH << 8) | _curRPNL; + iMidiCtrlValList imcvl = mcvll->searchControllers(channel, CTRL_RPN_OFFSET | param_num); + if(imcvl == mcvll->end()) + { + // Set mode, _curTime, and _timer to wait for next event. + _curMode = EncDiscoverRPN; + _curTime = MusEGlobal::audio->curFrame(); + _timer = 0; + return; + } + else if((imcvl->first & CTRL_OFFSET_MASK) == CTRL_RPN_OFFSET) + { + // Done. Take _curData and param_num and compose something to return, + // and set the HWval... TODO + _curMode = EncIdle; + return; + } + else if((imcvl->first & CTRL_OFFSET_MASK) == CTRL_RPN14_OFFSET) + { + // Set mode, _curTime, and _timer to wait for next event. + _curMode = EncRPN14; + _curTime = MusEGlobal::audio->curFrame(); + _timer = 0; + return; + } + else + { + fprintf(stderr, "MidiEncoder::encodeEvent unknown type %d\n", imcvl->first & CTRL_OFFSET_MASK); + return; + } + + break; + } + case ParamModeNRPN: + break; + default: + fprintf(stderr, "MidiEncoder::encodeEvent unknown ParamMode %d\n", _curParamMode); + return; + } + + break; + } + + case CTRL_LDATA: + break; + case CTRL_DATA_INC: + break; + case CTRL_DATA_DEC: + break; + case CTRL_HRPN: + _curRPNH = val; + _curParamMode = ParamModeRPN; + return; + case CTRL_LRPN: + _curRPNL = val; + _curParamMode = ParamModeRPN; + return; + case CTRL_HNRPN: + _curNRPNH = val; + _curParamMode = ParamModeNRPN; + return; + case CTRL_LNRPN: + _curNRPNL = val; + _curParamMode = ParamModeNRPN; + return; + default: + break; + } + } + } + +// for(iMidiCtrlValList imcvl = mcvll->begin(); imcvl != mcvll->end(); ++imcvl) +// { +// if(((imcvl->first >> 24) & 0xf) != channel) +// continue; +// MidiCtrlValList* mcvl = imcvl->second; +// } + } + + _curMode = EncIdle; + return; + + +// if(_curMode != EncIdle) +// { +// if(_curMode == EncCtrl14 && type == ME_CONTROLLER && ev.dataA() == _nextCtrl) +// { +// num = CTRL_14_OFFSET + ((_curCtrl << 8) | (_nextCtrl & 0x7f)); +// iMidiCtrlValList imcvl = mcvll->find(channel, num); +// if(imcvl == mcvll->end()) +// return; // Error, controller should exist +// MidiCtrlValList* mcvl = imcvl->second; +// data = (_curData << 7) | (ev.dataB() & 0x7f); +// mcvl->setHwVal(data); +// //_timer = 0; +// _curMode = EncIdle; +// return; +// } +// else if(_curMode == EncRPN14 && type == ME_CONTROLLER && ev.dataA() == CTRL_LDATA) +// { +// +// } +// } + + //mcvl->find(); + +// for(ciMidiCtrlValList imcvl = mcvl->begin(); imcvl != mcvl->end(); ++imcvl) +// { +// const int ch = imcvl->first >> 24; +// if(ch != channel) +// continue; +// MidiCtrlValList* mcvl = imcvl->second; +// num = mcvl->num(); +// ctrlH = (num >> 8) & 0x7f; +// ctrlL = num & 0xff; +// if(type == ME_CONTROLLER) +// { +// const int ev_num = ev.dataA(); +// if(num < CTRL_14_OFFSET) // 7-bit controller 0 - 0x10000 +// { +// if(ev_num == num) +// { +// +// } +// } +// else if(num < CTRL_RPN_OFFSET) // 14-bit controller 0x10000 - 0x20000 +// { +// +// } +// } +// } + + +// int num; +// +// switch(type) +// { +// // TODO +// case ME_PITCHBEND: +// num = CTRL_PITCH; +// break; +// case ME_AFTERTOUCH: +// num = CTRL_AFTERTOUCH; +// break; +// case ME_POLYAFTER: +// num = CTRL_POLYAFTER | (ev.dataA() & 0x7f); +// break; +// case ME_PROGRAM: +// num = CTRL_PROGRAM; +// break; +// +// case ME_CONTROLLER: +// { +// //num = CTRL_; +// } +// break; +// +// default: +// return; +// } + + + +// if(instr) +// { +// int num; +// int ctrlH; +// int ctrlL; +// MidiControllerList* mcl = instr->controller(); +// MidiController* mc; +// +// if (_outputInstrument) { +// MidiControllerList* mcl = _outputInstrument->controller(); +// for (iMidiController i = mcl->begin(); i != mcl->end(); ++i) { +// int cn = i->second->num(); +// if (cn == num) +// return i->second; +// // wildcard? +// if (i->second->isPerNoteController() && ((cn & ~0xff) == (num & ~0xff))) +// return i->second; +// } +// } +// +// for (iMidiController i = defaultMidiController.begin(); i != defaultMidiController.end(); ++i) { +// int cn = i->second->num(); +// if (cn == num) +// return i->second; +// // wildcard? +// if (i->second->isPerNoteController() && ((cn & ~0xff) == (num & ~0xff))) +// return i->second; +// } +// +// +// QString name = midiCtrlName(num); +// int min = 0; +// int max = 127; +// +// MidiController::ControllerType t = midiControllerType(num); +// switch (t) { +// case MidiController::RPN: +// case MidiController::NRPN: +// case MidiController::Controller7: +// case MidiController::PolyAftertouch: +// case MidiController::Aftertouch: +// max = 127; +// break; +// case MidiController::Controller14: +// case MidiController::RPN14: +// case MidiController::NRPN14: +// max = 16383; +// break; +// case MidiController::Program: +// max = 0xffffff; +// break; +// case MidiController::Pitch: +// max = 8191; +// min = -8192; +// break; +// case MidiController::Velo: // cannot happen +// break; +// } +// MidiController* c = new MidiController(name, num, min, max, 0); +// defaultMidiController.add(c); +// return c; +// +// for(ciMidiController imc = mcl->begin(); imc != mcl->end(); ++imc) +// { +// mc = imc->second; +// num = mc->num(); +// ctrlH = (num >> 8) & 0x7f; +// ctrlL = num & 0xff; +// if(num < CTRL_14_OFFSET) // 7-bit controller 0 - 0x10000 +// { +// if(ctrlL == 0xff || ctrlL == a) +// is_7bit = true; +// +// if(ctrlL == 0xff) +// RPNH_reserved = RPNL_reserved = NRPNH_reserved = NRPNL_reserved = DATAH_reserved = DATAL_reserved = true; +// else if(ctrlL == CTRL_HRPN) +// RPNH_reserved = true; +// else if(ctrlL == CTRL_LRPN) +// RPNL_reserved = true; +// else if(ctrlL == CTRL_HNRPN) +// NRPNH_reserved = true; +// else if(ctrlL == CTRL_LNRPN) +// NRPNL_reserved = true; +// else if(ctrlL == CTRL_HDATA) +// DATAH_reserved = true; +// else if(ctrlL == CTRL_LDATA) +// DATAL_reserved = true; +// } +// else if(num < CTRL_RPN_OFFSET) // 14-bit controller 0x10000 - 0x20000 +// { +// if(ctrlH == a) +// { +// //is_14bitH = true; +// is_14bit = true; +// if(!instr->waitForLSB()) +// { +// MidiRecordEvent single_ev; +// single_ev.setChannel(chn); +// single_ev.setType(ME_CONTROLLER); +// single_ev.setA(CTRL_14_OFFSET + (a << 8) + ctrlL); +// single_ev.setB((b << 7) + mcs->ctrls[ctrlL]); +// mdev->recordEvent(single_ev); +// } +// } +// if(ctrlL == 0xff || ctrlL == a) +// { +// //is_14bitL = true; +// is_14bit = true; +// MidiRecordEvent single_ev; +// single_ev.setChannel(chn); +// single_ev.setType(ME_CONTROLLER); +// single_ev.setA(CTRL_14_OFFSET + (ctrlH << 8) + a); +// single_ev.setB((mcs->ctrls[ctrlH] << 7) + b); +// mdev->recordEvent(single_ev); +// } +// +// if(ctrlL == 0xff) +// RPNH_reserved = RPNL_reserved = NRPNH_reserved = NRPNL_reserved = DATAH_reserved = DATAL_reserved = true; +// else if(ctrlL == CTRL_HRPN || ctrlH == CTRL_HRPN) +// RPNH_reserved = true; +// else if(ctrlL == CTRL_LRPN || ctrlH == CTRL_LRPN) +// RPNL_reserved = true; +// else if(ctrlL == CTRL_HNRPN || ctrlH == CTRL_HNRPN) +// NRPNH_reserved = true; +// else if(ctrlL == CTRL_LNRPN || ctrlH == CTRL_LNRPN) +// NRPNL_reserved = true; +// else if(ctrlL == CTRL_HDATA || ctrlH == CTRL_HDATA) +// DATAH_reserved = true; +// else if(ctrlL == CTRL_LDATA || ctrlH == CTRL_LDATA) +// DATAL_reserved = true; +// } +// else if(num < CTRL_NRPN_OFFSET) // RPN 7-Bit Controller 0x20000 - 0x30000 +// { +// //if(a == CTRL_HDATA && mcs->ctrls[CTRL_HRPN] < 128 && mcs->ctrls[CTRL_LRPN] < 128) +// if(a == CTRL_HDATA && !mcs->modeIsNRP && ctrlH == mcs->ctrls[CTRL_HRPN] && (ctrlL == 0xff || ctrlL == mcs->ctrls[CTRL_LRPN])) +// is_RPN = true; +// } +// else if(num < CTRL_INTERNAL_OFFSET) // NRPN 7-Bit Controller 0x30000 - 0x40000 +// { +// //if(a == CTRL_HDATA && mcs->ctrls[CTRL_HNRPN] < 128 && mcs->ctrls[CTRL_LNRPN] < 128) +// if(a == CTRL_HDATA && mcs->modeIsNRP && ctrlH == mcs->ctrls[CTRL_HNRPN] && (ctrlL == 0xff || ctrlL == mcs->ctrls[CTRL_LNRPN])) +// is_NRPN = true; +// } +// else if(num < CTRL_RPN14_OFFSET) // Unaccounted for internal controller 0x40000 - 0x50000 +// continue; +// else if(num < CTRL_NRPN14_OFFSET) // RPN14 Controller 0x50000 - 0x60000 +// { +// //if(a == CTRL_LDATA && mcs->ctrls[CTRL_HRPN] < 128 && mcs->ctrls[CTRL_LRPN] < 128) +// if(a == CTRL_LDATA && !mcs->modeIsNRP && ctrlH == mcs->ctrls[CTRL_HRPN] && (ctrlL == 0xff || ctrlL == mcs->ctrls[CTRL_LRPN])) +// is_RPN14 = true; +// } +// else if(num < CTRL_NONE_OFFSET) // NRPN14 Controller 0x60000 - 0x70000 +// { +// //if(a == CTRL_LDATA && mcs->ctrls[CTRL_HNRPN] < 128 && mcs->ctrls[CTRL_LNRPN] < 128) +// if(a == CTRL_LDATA && mcs->modeIsNRP && ctrlH == mcs->ctrls[CTRL_HNRPN] && (ctrlL == 0xff || ctrlL == mcs->ctrls[CTRL_LNRPN])) +// is_NRPN14 = true; +// } +// } +// } + +} + +//--------------------------------------------------------- +// endCycle +//--------------------------------------------------------- +void MidiEncoder::endCycle(unsigned int /*blockSize*/) +{ + // TODO +} } // namespace MusECore diff -Nru muse-2.1.2/muse/midictrl.h muse-3.0.2+ds1/muse/midictrl.h --- muse-2.1.2/muse/midictrl.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/midictrl.h 2018-01-29 20:07:03.000000000 +0000 @@ -30,74 +30,18 @@ #include +#include "midictrl_consts.h" + +//#define _MIDI_CTRL_DEBUG_ +// For finding exactly who may be calling insert, erase clear etc. in +// the controller list classes. (KDevelop 'Find uses'.) +//#define _MIDI_CTRL_METHODS_DEBUG_ + namespace MusECore { class Xml; class Part; - -const int CTRL_HBANK = 0x00; -const int CTRL_LBANK = 0x20; - -const int CTRL_HDATA = 0x06; -const int CTRL_LDATA = 0x26; - -const int CTRL_HNRPN = 0x63; -const int CTRL_LNRPN = 0x62; - -const int CTRL_HRPN = 0x65; -const int CTRL_LRPN = 0x64; - -const int CTRL_MODULATION = 0x01; -const int CTRL_PORTAMENTO_TIME = 0x05; -const int CTRL_VOLUME = 0x07; -const int CTRL_PANPOT = 0x0a; -const int CTRL_EXPRESSION = 0x0b; -const int CTRL_SUSTAIN = 0x40; -const int CTRL_PORTAMENTO = 0x41; -const int CTRL_SOSTENUTO = 0x42; -const int CTRL_SOFT_PEDAL = 0x43; -const int CTRL_HARMONIC_CONTENT = 0x47; -const int CTRL_RELEASE_TIME = 0x48; -const int CTRL_ATTACK_TIME = 0x49; - -const int CTRL_BRIGHTNESS = 0x4a; -const int CTRL_PORTAMENTO_CONTROL = 0x54; -const int CTRL_REVERB_SEND = 0x5b; -const int CTRL_CHORUS_SEND = 0x5d; -const int CTRL_VARIATION_SEND = 0x5e; - -const int CTRL_ALL_SOUNDS_OFF = 0x78; // 120 -const int CTRL_RESET_ALL_CTRL = 0x79; // 121 -const int CTRL_LOCAL_OFF = 0x7a; // 122 - -// controller types 0x10000 - 0x1ffff are 14 bit controller with -// 0x1xxyy -// xx - MSB controller -// yy - LSB controller - -// RPN - registered parameter numbers 0x20000 - -// NRPN - non registered parameter numbers 0x30000 - - -// internal controller types: -const int CTRL_INTERNAL_OFFSET = 0x40000; - -const int CTRL_PITCH = CTRL_INTERNAL_OFFSET; -const int CTRL_PROGRAM = CTRL_INTERNAL_OFFSET + 0x01; -const int CTRL_VELOCITY = CTRL_INTERNAL_OFFSET + 0x02; -const int CTRL_MASTER_VOLUME = CTRL_INTERNAL_OFFSET + 0x03; -const int CTRL_AFTERTOUCH = CTRL_INTERNAL_OFFSET + 0x04; -// NOTE: The range from CTRL_INTERNAL_OFFSET + 0x100 to CTRL_INTERNAL_OFFSET + 0x1ff is reserved -// for this control. (The low byte is reserved because this is a per-note control.) -const int CTRL_POLYAFTER = CTRL_INTERNAL_OFFSET + 0x1FF; // 100 to 1FF ! - -const int CTRL_VAL_UNKNOWN = 0x10000000; // used as unknown hwVal - -const int CTRL_14_OFFSET = 0x10000; -const int CTRL_RPN_OFFSET = 0x20000; -const int CTRL_NRPN_OFFSET = 0x30000; -const int CTRL_RPN14_OFFSET = 0x50000; -const int CTRL_NRPN14_OFFSET = 0x60000; -const int CTRL_NONE_OFFSET = 0x70000; +class MidiRecordEvent; //--------------------------------------------------------- // MidiController @@ -131,17 +75,33 @@ int _minVal; // controller value range (used in gui) int _maxVal; int _initVal; + // Special for drum mode, for controllers such as program. + int _drumInitVal; int _bias; int _showInTracks; void updateBias(); public: MidiController(); - MidiController(const QString& n, int num, int min, int max, int init, int show_in_track = (ShowInDrum | ShowInMidi)); + // If drumInit = -1, it means don't care - use the init val. + MidiController(const QString& n, int num, int min, int max, int init, int drumInit, int show_in_track = (ShowInDrum | ShowInMidi)); MidiController(const MidiController& mc); void copy(const MidiController &mc); MidiController& operator= (const MidiController &mc); + // Convert given controller double value to integer. + static inline int dValToInt(double v) { + // TODO: Decide best choice here. + //return int(round(v)); + //return lrint(v); + return int(v); + } + + // Whether the given integer value is CTRL_VAL_UNKNOWN. + static inline bool iValIsUnknown(int v) { return v == CTRL_VAL_UNKNOWN; } + // Whether the given double value is CTRL_VAL_UNKNOWN. + static inline bool dValIsUnknown(double v) { return iValIsUnknown(dValToInt(v)); } + const QString& name() const { return _name; } int num() const { return _num; } void setName(const QString& s) { _name = s; } @@ -151,7 +111,11 @@ int minVal() const { return _minVal; } int maxVal() const { return _maxVal; } int initVal() const { return _initVal; } + inline bool initValIsUnknown() const { return iValIsUnknown(_initVal); } void setInitVal(int val) { _initVal = val; } + int drumInitVal() const { return _drumInitVal; } + inline bool drumInitValIsUnknown() const { return iValIsUnknown(_drumInitVal); } + void setDrumInitVal(int val) { _drumInitVal = val; } void setMinVal(int val) { _minVal = val; updateBias(); } void setMaxVal(int val) { _maxVal = val; updateBias(); } int bias() const { return _bias; } @@ -161,13 +125,14 @@ static int genNum(ControllerType, int, int); }; -// Added by T356. struct MidiCtrlVal { // The part containing the event which this value came from. Used for searching and deleting. Part* part; // The stored value. int val; + MidiCtrlVal(Part* p, int v) { part = p; val = v; } + bool operator==(const MidiCtrlVal& mcv) { return part == mcv.part && val == mcv.val; } }; //--------------------------------------------------------- @@ -182,11 +147,20 @@ typedef std::pair MidiCtrlValRange; class MidiCtrlValList : public std::multimap > { + // The controller number. int ctrlNum; - int _lastValidHWVal; - int _hwVal; // current set value in midi hardware - // can be CTRL_VAL_UNKNOWN - + // Current set value in midi hardware. Can be CTRL_VAL_UNKNOWN. + double _hwVal; + // The last value that was not CTRL_VAL_UNKNOWN. Can still be CTRL_VAL_UNKNOWN (typically at startup). + // Note that in the case of PROGRAM for example, HBank/LBank bytes can still be 0xff (OFF). + double _lastValidHWVal; + // The last byte values that were not CTRL_VAL_UNKNOWN or 0xff (Off). + // Can never be 0xff (OFF), but can still be CTRL_VAL_UNKNOWN (typically at startup). + // Special for example PROGRAM controller, has 3 separate values: HBank, LBank and Program. + int _lastValidByte2; + int _lastValidByte1; + int _lastValidByte0; + // Hide built-in finds. iMidiCtrlVal find(const int&) { return end(); }; ciMidiCtrlVal find(const int&) const { return end(); }; @@ -196,19 +170,56 @@ Part* partAtTick(int tick) const; + // Determine value at tick, using values stored by ANY part. iMidiCtrlVal iValue(int tick); + // Determine value at tick, using values stored by ANY part. int value(int tick) const; + // Determine value at tick, using values stored by the SPECIFIC part. int value(int tick, Part* part) const; + // Determine value at tick, using values stored by ANY part, + // ignoring values that are OUTSIDE of their parts, or muted or off parts or tracks. + int visibleValue(unsigned int tick, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const; + // Determine value at tick, using values stored by the SPECIFIC part, + // ignoring values that are OUTSIDE of the part, or muted or off part or track. + int visibleValue(unsigned int tick, Part* part, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const; bool addMCtlVal(int tick, int value, Part* part); void delMCtlVal(int tick, Part* part); iMidiCtrlVal findMCtlVal(int tick, Part* part); + + // Current set value in midi hardware. Can be CTRL_VAL_UNKNOWN. + inline int hwVal() const { return MidiController::dValToInt(_hwVal); } + + double hwDVal() const { return _hwVal; } + inline bool hwValIsUnknown() const { return MidiController::iValIsUnknown(MidiController::dValToInt(_hwVal)); } + + // Resets the current, and optionally the last, hardware value to CTRL_VAL_UNKNOWN. + // Returns true if either value was changed. + bool resetHwVal(bool doLastHwValue = false); - int hwVal() const { return _hwVal; } - bool setHwVal(const int v); - bool setHwVals(const int v, const int lastv); - int num() const { return ctrlNum; } - int lastValidHWVal() const { return _lastValidHWVal; } + // Set current value in midi hardware. Can be CTRL_VAL_UNKNOWN. + // Returns false if value is already equal, true if value is changed. + bool setHwVal(const double v); + // Sets current and last HW values. + // Handy for forcing labels to show 'off' and knobs to show specific values + // without having to send two messages. + // Returns false if both values are already set, true if either value is changed. + bool setHwVals(const double v, const double lastv); + // The controller number. + int num() const { return ctrlNum; } + // The last value that was not CTRL_VAL_UNKNOWN. Can still be CTRL_VAL_UNKNOWN (typically at startup). + // Note that in the case of PROGRAM for example, HBank/LBank bytes can still be 0xff (OFF). + inline int lastValidHWVal() const { return MidiController::dValToInt(_lastValidHWVal); } + + double lastValidHWDVal() const { return _lastValidHWVal; } + inline bool lastHwValIsUnknown() const { return MidiController::iValIsUnknown(MidiController::dValToInt(_lastValidHWVal)); } + + // The last byte values that were not CTRL_VAL_UNKNOWN or 0xff (Off). + // Can never be 0xff (OFF), but can still be CTRL_VAL_UNKNOWN (typically at startup). + // Special for example PROGRAM controller, has 3 separate values: HBank, LBank and Program. + int lastValidByte2() const { return _lastValidByte2; } + int lastValidByte1() const { return _lastValidByte1; } + int lastValidByte0() const { return _lastValidByte0; } }; //--------------------------------------------------------- @@ -219,41 +230,151 @@ // index = (channelNumber << 24) + ctrlNumber //--------------------------------------------------------- -typedef std::map >::iterator iMidiCtrlValList; -typedef std::map >::const_iterator ciMidiCtrlValList; +typedef std::map > MidiCtrlValListList_t; +typedef MidiCtrlValListList_t::iterator iMidiCtrlValList; +typedef MidiCtrlValListList_t::const_iterator ciMidiCtrlValList; -class MidiCtrlValListList : public std::map > { +class MidiCtrlValListList : public MidiCtrlValListList_t { + bool _RPN_Ctrls_Reserved; + public: - void add(int channel, MidiCtrlValList* vl) { - insert(std::pair((channel << 24) + vl->num(), vl)); - } - iMidiCtrlValList find(int channel, int ctrl) { + MidiCtrlValListList(); + //MidiCtrlValListList(const MidiCtrlValListList&); // TODO + + iterator find(int channel, int ctrl) { return std::map >::find((channel << 24) + ctrl); } + const_iterator find(int channel, int ctrl) const { + return ((const MidiCtrlValListList_t*)this)->find((channel << 24) + ctrl); + } void clearDelete(bool deleteLists); + // Like 'find', finds a controller given fully qualified type + number. + // But it returns controller with highest priority if multiple controllers use the + // given number such as {Controller7, num = 0x55} + {Controller14, num = 0x5544}. + // Note if given number is one of the eight reserved General Midi (N)RPN controllers, + // this will only return Controller7 or Controller14, not anything (N)RPN related. + // That is, it will not 'encode' (N)RPNs. Use a MidiEncoder instance for that. + iMidiCtrlValList searchControllers(int channel, int ctl); + // Returns true if any of the EIGHT reserved General Midi (N)RPN control numbers are ALREADY + // defined as Controller7 or part of Controller14. Cached, for speed. + // Used (at least) by midi input encoders to quickly arbitrate new input. + bool RPN_Ctrls_Reserved() { return _RPN_Ctrls_Reserved; } + // Manual check and update of the flag. For convenience, returns the flag. + // Cost depends on types and number of list controllers, so it is good for deferring + // an update until AFTER some lengthy list operation. JUST BE SURE to call this! + bool update_RPN_Ctrls_Reserved(); + + // NOTICE: If update is false or these are bypassed by using insert, erase, clear etc. for speed, + // then BE SURE to call update_RPN_Ctrls_Reserved() later. + void add(int channel, MidiCtrlValList* vl, bool update = true); + void del(iMidiCtrlValList ictl, bool update = true); + size_type del(int num, bool update = true); + void del(iMidiCtrlValList first, iMidiCtrlValList last, bool update = true); + void clr(); + // Convenience method: Resets all current, and optionally the last, hardware controller values to CTRL_VAL_UNKNOWN. + // Equivalent to calling resetAllHwVal() on each MidiCtrlValList. + // Returns true if either value was changed in any controller. + bool resetAllHwVals(bool doLastHwValue); + +#ifdef _MIDI_CTRL_METHODS_DEBUG_ + // Need to catch all insert, erase, clear etc... + void swap(MidiCtrlValListList&); + std::pair insert(const std::pair& p); + iMidiCtrlValList insert(iMidiCtrlValList ic, const std::pair& p); + void erase(iMidiCtrlValList ictl); + size_type erase(int num); + void erase(iMidiCtrlValList first, iMidiCtrlValList last); + void clear(); +#endif + // Some IDEs won't "Find uses" of operators. So, no choice but to trust always catching it here. + MidiCtrlValListList& operator=(const MidiCtrlValListList&); }; + +//--------------------------------------------------------- +// MidiEncoder +//--------------------------------------------------------- + +class MidiEncoder { + public: + enum Mode { EncIdle, EncCtrl14, EncDiscoverRPN, EncDiscoverNRPN, EncRPN, EncNRPN, EncRPN14, EncNRPN14 }; + enum ParamMode { ParamModeUnknown, ParamModeRPN, ParamModeNRPN }; + + private: + Mode _curMode; + ParamMode _curParamMode; + unsigned int _timer; // + unsigned char _curCtrl; // Ctl num of first event + unsigned char _curData; // Data of first event + unsigned int _curTime; // Time of first event + unsigned char _nextCtrl; // Expected next event ctl num (for ctrl14 only) + unsigned char _curRPNH; + unsigned char _curRPNL; + unsigned char _curNRPNH; + unsigned char _curNRPNL; + + public: + MidiEncoder(); + + void encodeEvent(const MidiRecordEvent& ev, int port, int channel); + void endCycle(unsigned int blockSize); +}; //--------------------------------------------------------- // MidiControllerList // this is a list of used midi controllers created -// - excplicit by user +// - explicit by user // - implicit during import of a midi file //--------------------------------------------------------- +typedef std::map >::iterator iMidiController; +typedef std::map >::const_iterator ciMidiController; + class MidiControllerList : public std::map > { + bool _RPN_Ctrls_Reserved; + public: - MidiControllerList() {} + MidiControllerList(); MidiControllerList(const MidiControllerList& mcl); - - void add(MidiController* mc) { insert(std::pair(mc->num(), mc)); } + + // Like 'find', finds a controller given fully qualified type + number. + // But it returns controller with highest priority if multiple controllers use the + // given number such as {Controller7, num = 0x55} + {Controller14, num = 0x5544}. + // Note if given number is one of the eight reserved General Midi (N)RPN controllers, + // this will only return Controller7 or Controller14, not anything (N)RPN related. + // That is, it will not 'encode' (N)RPNs. Use a MidiEncoder instance for that. + iMidiController searchControllers(int ctl); + // Check if either a per-note controller, or else a regular controller already exists. bool ctrlAvailable(int find_num, MidiController* ignore_this = 0); + // Returns true if any of the EIGHT reserved General Midi (N)RPN control numbers are + // ALREADY defined as Controller7 or part of Controller14. Cached, for speed. + // Used (at least) by midi input encoders to quickly arbitrate new input. + bool RPN_Ctrls_Reserved() { return _RPN_Ctrls_Reserved; } + // Manual check and update of the flag. For convenience, returns the flag. + bool update_RPN_Ctrls_Reserved(); + + // NOTICE: If update is false or these are bypassed by using insert, erase, clear etc. for speed, + // then BE SURE to call update_RPN_Ctrls_Reserved() later. + void add(MidiController* mc, bool update = true); + void del(iMidiController ictl, bool update = true); + size_type del(int num, bool update = true); + void del(iMidiController first, iMidiController last, bool update = true); + void clr(); + +#ifdef _MIDI_CTRL_METHODS_DEBUG_ + // Need to catch all insert, erase, clear etc... + void swap(MidiControllerList&); + std::pair insert(const std::pair& p); + iMidiController insert(iMidiController ic, const std::pair& p); + void erase(iMidiController ictl); + size_type erase(int num); + void erase(iMidiController first, iMidiController last); + void clear(); +#endif + // Some IDEs won't "Find uses" of operators. So, no choice but to trust always catching it here. + MidiControllerList& operator=(const MidiControllerList&); }; -typedef MidiControllerList::iterator iMidiController; -typedef MidiControllerList::const_iterator ciMidiController; -typedef MidiControllerList MidiControllerList; - extern MidiControllerList defaultMidiController; extern void initMidiController(); extern MidiController::ControllerType midiControllerType(int num); diff -Nru muse-2.1.2/muse/mididev.cpp muse-3.0.2+ds1/muse/mididev.cpp --- muse-2.1.2/muse/mididev.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/mididev.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: mididev.cpp,v 1.10.2.6 2009/11/05 03:14:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011, 2016 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 @@ -43,8 +43,14 @@ #include "midiseq.h" #include "sync.h" #include "midiitransform.h" +#include "mitplugin.h" #include "part.h" #include "drummap.h" +#include "operations.h" +#include "helper.h" + +// For debugging output: Uncomment the fprintf section. +//#define DEBUG_MIDI_DEVICE(dev, format, args...) //fprintf(dev, format, ##args); namespace MusEGlobal { MusECore::MidiDeviceList midiDevices; @@ -60,6 +66,9 @@ extern void processMidiInputTransformPlugins(MEvent&); +// Static. +const int MidiDevice::extClockHistoryCapacity = 1024; + //--------------------------------------------------------- // initMidiDevices @@ -70,7 +79,9 @@ #ifdef MIDI_DRIVER_MIDI_SERIAL initMidiSerial(); #endif - if(MusEGlobal::useAlsaWithJack || MusEGlobal::audioDevice->deviceType() != AudioDevice::JACK_AUDIO) + if(MusEGlobal::config.enableAlsaMidiDriver || // User setting + MusEGlobal::useAlsaWithJack || // Command line override + MusEGlobal::audioDevice->deviceType() != AudioDevice::JACK_AUDIO) // Jack not running { if(initMidiAlsa()) { @@ -96,9 +107,19 @@ void MidiDevice::init() { - stopPending = false; - seekPending = false; + _extClockHistoryFifo = new LockFreeBuffer(extClockHistoryCapacity); + // TODO: Scale these according to the current audio segment size. + _playbackEventBuffers = new LockFreeMPSCRingBuffer(1024); + _userEventBuffers = new LockFreeMPSCRingBuffer(1024); + + _sysExOutDelayedEvents = new std::vector; + // Initially reserve a fair number of items to hold potentially a lot + // of messages when the sysex processor is busy (in the Sending state). + _sysExOutDelayedEvents->reserve(1024); + _stopFlag.store(false); + + _state = QString("Closed"); _readEnable = false; _writeEnable = false; _rwFlags = 3; @@ -116,7 +137,6 @@ _tmpRecordCount[i] = 0; _sysexFIFOProcessed = false; - _sysexReadingChunks = false; init(); } @@ -128,11 +148,42 @@ _tmpRecordCount[i] = 0; _sysexFIFOProcessed = false; - _sysexReadingChunks = false; init(); } +MidiDevice::~MidiDevice() +{ + if(_sysExOutDelayedEvents) + delete _sysExOutDelayedEvents; + if(_extClockHistoryFifo) + delete _extClockHistoryFifo; + if(_userEventBuffers) + delete _userEventBuffers; + if(_playbackEventBuffers) + delete _playbackEventBuffers; +} + +QString MidiDevice::deviceTypeString() const +{ + switch(deviceType()) + { + case ALSA_MIDI: + return "ALSA"; + case JACK_MIDI: + return "JACK"; + case SYNTH_MIDI: + { + const SynthI* s = dynamic_cast(this); + if(s && s->synth()) + return MusECore::synthType2String(s->synth()->synthType()); + else + return "SYNTH"; + } + } + return "UNKNOWN"; +} + void MidiDevice::setPort(int p) { _port = p; @@ -218,26 +269,31 @@ } //--------------------------------------------------------- +// midiClockInput +// Midi clock (24 ticks / quarter note) +//--------------------------------------------------------- + +void MidiDevice::midiClockInput(unsigned int frame) +{ + // Put a midi clock record event into the clock history fifo. Ignore port and channel. + // Timestamp with the current frame. + const ExtMidiClock ext_clk = MusEGlobal::midiSyncContainer.midiClockInput(midiPort(), frame); + if(ext_clk.isValid() && extClockHistory()) + extClockHistory()->put(ext_clk); +} + +//--------------------------------------------------------- // recordEvent //--------------------------------------------------------- void MidiDevice::recordEvent(MidiRecordEvent& event) { - // 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()); if (MusEGlobal::midiInputTrace) { fprintf(stderr, "MidiInput: "); - event.dump(); + dumpMPEvent(&event); } int typ = event.type(); @@ -257,20 +313,20 @@ if ((p[0] == 0x7f) && ((p[1] == 0x7f) || (idin == 0x7f) || (p[1] == idin))) { if (p[2] == 0x06) { - MusEGlobal::midiSeq->mmcInput(_port, p, n); + MusEGlobal::midiSyncContainer.mmcInput(_port, p, n); return; } if (p[2] == 0x01) { - MusEGlobal::midiSeq->mtcInputFull(_port, p, n); + MusEGlobal::midiSyncContainer.mtcInputFull(_port, p, n); return; } } else if (p[0] == 0x7e) { - MusEGlobal::midiSeq->nonRealtimeSystemSysex(_port, p, n); + MusEGlobal::midiSyncContainer.nonRealtimeSystemSysex(_port, p, n); return; } } - } + } else // Trigger general activity indicator detector. Sysex has no channel, don't trigger. MusEGlobal::midiPorts[_port].syncInfo().trigActDetect(event.channel()); @@ -328,14 +384,6 @@ return 0; } -iMidiDevice MidiDeviceList::find(const MidiDevice* dev) - { - for (iMidiDevice i = begin(); i != end(); ++i) - if (*i == dev) - return i; - return end(); - } - //--------------------------------------------------------- // add //--------------------------------------------------------- @@ -344,26 +392,74 @@ { bool gotUniqueName=false; int increment = 0; - QString origname = dev->name(); + const QString origname = dev->name(); + QString newName = origname; while (!gotUniqueName) { gotUniqueName = true; // check if the name's been taken for (iMidiDevice i = begin(); i != end(); ++i) { const QString s = (*i)->name(); - if (s == dev->name()) + if (s == newName) { - char incstr[4]; - sprintf(incstr,"_%d",++increment); - dev->setName(origname + QString(incstr)); + newName = origname + QString("_%1").arg(++increment); gotUniqueName = false; } } } - + if(origname != newName) + dev->setName(newName); push_back(dev); } //--------------------------------------------------------- +// addOperation +//--------------------------------------------------------- + +void MidiDeviceList::addOperation(MidiDevice* dev, PendingOperationList& ops) +{ + bool gotUniqueName=false; + int increment = 0; + const QString origname = dev->name(); + QString newName = origname; + PendingOperationItem poi(this, dev, PendingOperationItem::AddMidiDevice); + // check if the name's been taken + while(!gotUniqueName) + { + if(increment >= 10000) + { + fprintf(stderr, "MusE Error: MidiDeviceList::addOperation(): Out of 10000 unique midi device names!\n"); + return; + } + gotUniqueName = true; + // In the case of type AddMidiDevice, this searches for the name only. + iPendingOperation ipo = ops.findAllocationOp(poi); + if(ipo != ops.end()) + { + PendingOperationItem& poif = *ipo; + if(poif._midi_device == poi._midi_device) + return; // Device itself is already added! + newName = origname + QString("_%1").arg(++increment); + gotUniqueName = false; + } + + for(iMidiDevice i = begin(); i != end(); ++i) + { + const QString s = (*i)->name(); + if(s == newName) + { + newName = origname + QString("_%1").arg(++increment); + gotUniqueName = false; + } + } + } + + if(origname != newName) + dev->setName(newName); + + ops.add(poi); +} + +//--------------------------------------------------------- // remove //--------------------------------------------------------- @@ -378,168 +474,75 @@ } //--------------------------------------------------------- -// sendNullRPNParams +// resetCurParamNums +// Reset output channel's current parameter numbers to -1. +// All channels if chan = -1. //--------------------------------------------------------- -bool MidiDevice::sendNullRPNParams(unsigned time, int port, int chn, bool nrpn) +void MidiDevice::resetCurOutParamNums(int chan) { - if(_port == -1) - return false; - - int nv = MusEGlobal::midiPorts[_port].nullSendValue(); - if(nv == -1) - return false; - - int nvh = (nv >> 8) & 0xff; - int nvl = nv & 0xff; - if(nvh != 0xff) - { - if(nrpn) - putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f)); - else - putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f)); - } - if(nvl != 0xff) + if(chan == -1) { - if(nrpn) - putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f)); - else - putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f)); + for(int i = 0; i < MIDI_CHANNELS; ++i) + _curOutParamNums[i].resetParamNums(); + return; } - return true; + _curOutParamNums[chan].resetParamNums(); } //--------------------------------------------------------- -// putEventWithRetry +// putEvent // return true if event cannot be delivered -// This method will try to putEvent 'tries' times, waiting 'delayUs' microseconds between tries. -// NOTE: Since it waits, it should not be used in RT or other time-sensitive threads. //--------------------------------------------------------- -bool MidiDevice::putEventWithRetry(const MidiPlayEvent& ev, int tries, long delayUs) +bool MidiDevice::putEvent(const MidiPlayEvent& ev, LatencyType latencyType, EventBufferType bufferType) { - // TODO: Er, probably not the best way to do this. - // Maybe try to correlate with actual audio buffer size instead of blind time delay. - for( ; tries > 0; --tries) - { - if(!putEvent(ev)) // Returns true if event cannot be delivered. - return false; - - bool sleepOk = -1; - while(sleepOk == -1) - sleepOk = usleep(delayUs); // FIXME: usleep is supposed to be depricated! - } - return true; +// TODO: Decide whether we want the driver cached values always updated like this, +// even if not writeable or if error. +// if(!_writeEnable) +// return true; + + // Automatically shift the time forward if specified. + MidiPlayEvent fin_ev = ev; + switch(latencyType) + { + case NotLate: + break; + + case Late: + fin_ev.setTime(fin_ev.time() + pbForwardShiftFrames()); + break; + } + + //DEBUG_MIDI_DEVICE(stderr, "MidiDevice::putUserEvent devType:%d time:%d type:%d ch:%d A:%d B:%d\n", + // deviceType(), fin_ev.time(), fin_ev.type(), fin_ev.channel(), fin_ev.dataA(), fin_ev.dataB()); + if (MusEGlobal::midiOutputTrace) + { + fprintf(stderr, "MidiDevice::putEvent: %s: <%s>: ", deviceTypeString().toLatin1().constData(), name().toLatin1().constData()); + dumpMPEvent(&fin_ev); + } + + bool rv = true; + switch(bufferType) + { + case PlaybackBuffer: + rv = !_playbackEventBuffers->put(fin_ev); + break; + + case UserBuffer: + rv = !_userEventBuffers->put(fin_ev); + break; + } + + if(rv) + fprintf(stderr, "MidiDevice::putEvent: Error: Device buffer overflow. bufferType:%d\n", bufferType); + + return rv; } - -//--------------------------------------------------------- -// putEvent -// return true if event cannot be delivered -// TODO: retry on controller putMidiEvent -// (Note: Since putEvent is virtual and there are different versions, -// a retry facility is now found in putEventWithRetry. ) -//--------------------------------------------------------- - -bool MidiDevice::putEvent(const MidiPlayEvent& ev) - { - if(!_writeEnable) - //return true; - return false; - - unsigned t = ev.time(); - int port = ev.port(); - - if (ev.type() == ME_CONTROLLER) { - int a = ev.dataA(); - int b = ev.dataB(); - int chn = ev.channel(); - if (a == CTRL_PITCH) { - return putMidiEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, b, 0)); - } - if ((a | 0xff) == CTRL_POLYAFTER) { - return putMidiEvent(MidiPlayEvent(t, port, chn, ME_POLYAFTER, a & 0x7f, b & 0x7f)); - } - if (a == CTRL_AFTERTOUCH) { - return putMidiEvent(MidiPlayEvent(t, port, chn, ME_AFTERTOUCH, b, 0)); - } - if (a == CTRL_PROGRAM) { - int hb = (b >> 16) & 0xff; - int lb = (b >> 8) & 0xff; - int pr = b & 0x7f; - if (hb != 0xff) - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb)); - if (lb != 0xff) - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LBANK, lb)); - return putMidiEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, pr, 0)); - } -#if 1 // if ALSA cannot handle RPN NRPN etc. DELETETHIS? remove the wrapping #if #endif - - if (a < CTRL_14_OFFSET) { // 7 Bit Controller - putMidiEvent(ev); - } - else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - int dataH = (b >> 7) & 0x7f; - int dataL = b & 0x7f; - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlH, dataH)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlL, dataL)); - } - else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, b)); - - // Select null parameters so that subsequent data controller - // events do not upset the last *RPN controller. Tim. - sendNullRPNParams(t, port, chn, false); - } - else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, b)); - - sendNullRPNParams(t, port, chn, true); - } - else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - int dataH = (b >> 7) & 0x7f; - int dataL = b & 0x7f; - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); - - sendNullRPNParams(t, port, chn, false); - } - else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller - int ctrlH = (a >> 8) & 0x7f; - int ctrlL = a & 0x7f; - int dataH = (b >> 7) & 0x7f; - int dataL = b & 0x7f; - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); - putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); - - sendNullRPNParams(t, port, chn, true); - } - else { - fprintf(stderr, "putEvent: unknown controller type 0x%x\n", a); - } - return false; -#endif - } - return putMidiEvent(ev); - } //--------------------------------------------------------- // processStuckNotes +// To be called by audio thread only. //--------------------------------------------------------- void MidiDevice::processStuckNotes() @@ -548,26 +551,32 @@ // MusEGlobal::audio->isPlaying() might not be true during seek right now. //if(MusEGlobal::audio->isPlaying()) { - bool extsync = MusEGlobal::extSyncFlag.value(); - int frameOffset = MusEGlobal::audio->getFrameOffset(); - unsigned nextTick = MusEGlobal::audio->nextTick(); - iMPEvent k; + const unsigned nextTick = MusEGlobal::audio->nextTick(); + ciMPEvent k; + + //--------------------------------------------------- + // Play any stuck notes which were put directly to the device + //--------------------------------------------------- + for (k = _stuckNotes.begin(); k != _stuckNotes.end(); ++k) { if (k->time() >= nextTick) break; MidiPlayEvent ev(*k); - if(extsync) // p3.3.25 - ev.setTime(k->time()); - else - ev.setTime(MusEGlobal::tempomap.tick2frame(k->time()) + frameOffset); - _playEvents.add(ev); + ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(k->time())); + _userEventBuffers->put(ev); } _stuckNotes.erase(_stuckNotes.begin(), k); - } + + //------------------------------------------------------------ + // To save time, playing of any track-related playback stuck notes (NOT 'live' notes) + // which were not put directly to the device, is done in Audio::processMidi(). + //------------------------------------------------------------ + } } //--------------------------------------------------------- // handleStop +// To be called by audio thread only. //--------------------------------------------------------- void MidiDevice::handleStop() @@ -606,275 +615,83 @@ } //--------------------------------------------------- - // Clear all notes and handle stuck notes + // Clear all notes and flush out any stuck notes + // which were put directly to the device //--------------------------------------------------- - - _playEvents.clear(); + + setStopFlag(true); for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) { - MidiPlayEvent ev = *i; - ev.setTime(0); - putEvent(ev); + MidiPlayEvent ev(*i); + ev.setTime(0); // Immediate processing. TODO Use curFrame? + //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); + putEvent(ev, MidiDevice::NotLate); } _stuckNotes.clear(); - //--------------------------------------------------- - // reset sustain - //--------------------------------------------------- + //------------------------------------------------------------ + // Flush out any track-related playback stuck notes (NOT 'live' notes) + // which were not put directly to the device + //------------------------------------------------------------ - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + for(ciMidiTrack imt = MusEGlobal::song->midis()->begin(); imt != MusEGlobal::song->midis()->end(); ++imt) { - if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + MPEventList& mel = (*imt)->stuckNotes; + for(iMPEvent i = mel.begin(), i_next = i; i != mel.end(); i = i_next) { - MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); - putEvent(ev); + ++i_next; + + if((*i).port() != _port) + continue; + MidiPlayEvent ev(*i); + ev.setTime(0); // Immediate processing. TODO Use curFrame? + //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); + putEvent(ev, MidiDevice::NotLate); + + mel.erase(i); } } - /* DELETETHIS 23 //--------------------------------------------------- - // send midi stop + // reset sustain //--------------------------------------------------- - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!MusEGlobal::extSyncFlag.value()) + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { - MidiSyncInfo& si = mp->syncInfo(); - if(si.MMCOut()) - mp->sendMMCStop(); - - if(si.MRTOut()) + if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { - // Send STOP - mp->sendStop(); - // Added check of option send continue not start. Hmm, is this required? Seems to make other devices unhappy. - // (Could try now that this is in MidiDevice.) - //if(!si.sendContNotStart()) - // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / MusEGlobal::config.division); + MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); // Immediate processing. TODO Use curFrame? + //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); + putEvent(ev, MidiDevice::NotLate); } - } - */ + } } //--------------------------------------------------------- // handleSeek +// To be called by audio thread only. //--------------------------------------------------------- void MidiDevice::handleSeek() { - // If the device is not in use by a port, don't bother it. - if(_port == -1) - return; - - MidiPort* mp = &MusEGlobal::midiPorts[_port]; - MidiInstrument* instr = mp->instrument(); - MidiCtrlValListList* cll = mp->controller(); - unsigned pos = MusEGlobal::audio->tickPos(); - //--------------------------------------------------- - // Send STOP - //--------------------------------------------------- - - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!MusEGlobal::extSyncFlag.value()) - { - if(mp->syncInfo().MRTOut()) - { - // Shall we check for device write open flag to see if it's ok to send?... - //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) - //if(!(openFlags() & 1)) - // continue; - mp->sendStop(); - } - } - - //--------------------------------------------------- - // If playing, clear all notes and handle stuck notes + // If playing, clear all notes and flush out any + // stuck notes which were put directly to the device //--------------------------------------------------- if(MusEGlobal::audio->isPlaying()) { - _playEvents.clear(); + // TODO: Don't clear, let it play whatever was scheduled ? + //setStopFlag(true); for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) { - MidiPlayEvent ev = *i; - ev.setTime(0); - //_playEvents.add(ev); - putEvent(ev); // For immediate playback try putEvent, putMidiEvent, or sendEvent (for the optimizations) instead. - //mp->sendEvent(ev); + MidiPlayEvent ev(*i); + ev.setTime(0); // Immediate processing. TODO Use curFrame? + //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); + putEvent(ev, MidiDevice::NotLate); } _stuckNotes.clear(); } - - //--------------------------------------------------- - // Send new controller values - //--------------------------------------------------- - - // Find channels on this port used in the song... - bool usedChans[MIDI_CHANNELS]; - int usedChanCount = 0; - for(int i = 0; i < MIDI_CHANNELS; ++i) - usedChans[i] = false; - if(MusEGlobal::song->click() && MusEGlobal::clickPort == _port) - { - usedChans[MusEGlobal::clickChan] = true; - ++usedChanCount; - } - bool drum_found = false; - for(ciMidiTrack imt = MusEGlobal::song->midis()->begin(); imt != MusEGlobal::song->midis()->end(); ++imt) - { - if((*imt)->type() == MusECore::Track::DRUM) - { - if(!drum_found) - { - drum_found = true; - for(int i = 0; i < DRUM_MAPSIZE; ++i) - { - // Default to track port if -1 and track channel if -1. - int mport = MusEGlobal::drumMap[i].port; - if(mport == -1) - mport = (*imt)->outPort(); - int mchan = MusEGlobal::drumMap[i].channel; - if(mchan == -1) - mchan = (*imt)->outChannel(); - if(mport != _port || usedChans[mchan]) - continue; - usedChans[mchan] = true; - ++usedChanCount; - if(usedChanCount >= MIDI_CHANNELS) - break; // All are used, done searching. - } - } - } - else - { - if((*imt)->outPort() != _port || usedChans[(*imt)->outChannel()]) - continue; - usedChans[(*imt)->outChannel()] = true; - ++usedChanCount; - } - - if(usedChanCount >= MIDI_CHANNELS) - break; // All are used. Done searching. - } - - for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) - { - MidiCtrlValList* vl = ivl->second; - int chan = ivl->first >> 24; - if(!usedChans[chan]) // Channel not used in song? - continue; - int ctlnum = vl->num(); - - // Find the first non-muted value at the given tick... - bool values_found = false; - bool found_value = false; - - iMidiCtrlVal imcv = vl->lower_bound(pos); - if(imcv != vl->end() && imcv->first == (int)pos) - { - for( ; imcv != vl->end() && imcv->first == (int)pos; ++imcv) - { - const Part* p = imcv->second.part; - if(!p) - continue; - // Ignore values that are outside of the part. - if(pos < p->tick() || pos >= (p->tick() + p->lenTick())) - continue; - values_found = true; - // Ignore if part or track is muted or off. - if(p->mute()) - continue; - const Track* track = p->track(); - if(track && (track->isMute() || track->off())) - continue; - found_value = true; - break; - } - } - else - { - while(imcv != vl->begin()) - { - --imcv; - const Part* p = imcv->second.part; - if(!p) - continue; - // Ignore values that are outside of the part. - unsigned t = imcv->first; - if(t < p->tick() || t >= (p->tick() + p->lenTick())) - continue; - values_found = true; - // Ignore if part or track is muted or off. - if(p->mute()) - continue; - const Track* track = p->track(); - if(track && (track->isMute() || track->off())) - continue; - found_value = true; - break; - } - } - - if(found_value) - { - // Don't bother sending any sustain values if not playing. Just set the hw state. - if(ctlnum == CTRL_SUSTAIN && !MusEGlobal::audio->isPlaying()) - mp->setHwCtrlState(chan, CTRL_SUSTAIN, imcv->second.val); - else - // Use sendEvent to get the optimizations and limiting. But force if there's a value at this exact position. - // NOTE: Why again was this forced? There was a reason. Think it was RJ in response to bug rep, then I modded. - // A reason not to force: If a straight line is drawn on graph, multiple identical events are stored - // (which must be allowed). So seeking through them here sends them all redundantly, not good. // REMOVE Tim. - mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, imcv->second.val), false); //, imcv->first == pos); - //mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, imcv->second.val), pos == 0 || imcv->first == pos); - } - - // Either no value was found, or they were outside parts, or pos is in the unknown area before the first value. - // Send instrument default initial values. NOT for syntis. Use midiState and/or initParams for that. - //if((imcv == vl->end() || !done) && !MusEGlobal::song->record() && instr && !isSynti()) - // Hmm, without refinement we can only do this at position 0, due to possible 'skipped' values outside parts, above. - if(!values_found && MusEGlobal::config.midiSendCtlDefaults && !MusEGlobal::song->record() && pos == 0 && instr && !isSynti()) - { - MidiControllerList* mcl = instr->controller(); - ciMidiController imc = mcl->find(vl->num()); - if(imc != mcl->end()) - { - MidiController* mc = imc->second; - if(mc->initVal() != CTRL_VAL_UNKNOWN) - // Use sendEvent to get the optimizations and limiting. No force sending. Note the addition of bias. - mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, mc->initVal() + mc->bias()), false); - } - } - } - - //--------------------------------------------------- - // reset sustain - //--------------------------------------------------- - - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) - { - if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) - { - MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); - putEvent(ev); - } - } - - //--------------------------------------------------- - // Send STOP and "set song position pointer" - //--------------------------------------------------- - - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!MusEGlobal::extSyncFlag.value()) - { - if(mp->syncInfo().MRTOut()) - { - //mp->sendStop(); // Moved above - int beat = (pos * 4) / MusEGlobal::config.division; - mp->sendSongpos(beat); - } - } } } // namespace MusECore diff -Nru muse-2.1.2/muse/mididev.h muse-3.0.2+ds1/muse/mididev.h --- muse-2.1.2/muse/mididev.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/mididev.h 2017-12-17 21:07:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: mididev.h,v 1.3.2.4 2009/04/04 01:49:50 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011, 2016 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 @@ -30,6 +30,11 @@ #include "mpevent.h" #include "route.h" #include "globaldefs.h" +#include +#include +#include "lock_free_buffer.h" +#include "sync.h" +#include "evdata.h" #include @@ -37,12 +42,91 @@ namespace MusECore { class Xml; +class PendingOperationList; + +struct MidiOutputParams { + int BANKH; + int BANKL; + int PROG; + int RPNL; + int RPNH; + int NRPNL; + int NRPNH; + int DATAH; + int DATAL; + + MidiOutputParams() { reset(); } + + void reset() { BANKH = BANKL = PROG = 0xff; RPNL = RPNH = NRPNL = NRPNH = DATAH = DATAL = -1; } + void resetParamNums() { RPNL = RPNH = NRPNL = NRPNH = DATAH = DATAL = -1; } + void resetPatch() { BANKH = BANKL = PROG = 0xff; } + void setRPNL(int a) { RPNL = a; NRPNL = NRPNH = -1; } + void setRPNH(int a) { RPNH = a; NRPNL = NRPNH = -1; } + void setNRPNL(int a) { NRPNL = a; RPNL = RPNH = -1; } + void setNRPNH(int a) { NRPNH = a; RPNL = RPNH = -1; } + void setDATAH(int a) { DATAH = a; } + void setDATAL(int a) { DATAL = a; } + void setBANKH(int a) { BANKH = a;} + void setBANKL(int a) { BANKL = a;} + void setPROG(int a) { PROG = a;} + void currentProg(int *prg, int *lbank, int *hbank) + { if(prg) *prg=PROG&0xff; if(lbank) *lbank=BANKL&0xff; if(hbank) *hbank=BANKH&0xff; } + void setCurrentProg(int prg, int lbank, int hbank) + { PROG=prg&0xff; BANKL=lbank&0xff; BANKH=hbank&0xff; } +}; //--------------------------------------------------------- // MidiDevice //--------------------------------------------------------- class MidiDevice { + public: + // Types of MusE midi devices. + enum MidiDeviceType { ALSA_MIDI=0, JACK_MIDI=1, SYNTH_MIDI=2 }; + + // IDs for the various IPC FIFOs that are used. + enum EventFifoIds + { + // Playback queued events put by the audio process thread. + PlayFifo=0, + // Gui events put by our gui thread. + GuiFifo=1, + // OSC events put by the OSC thread. + OSCFifo=2, + // Monitor input passthrough events put by Jack devices (audio process thread). + JackFifo=3, + // Monitor input passthrough events put by ALSA devices (midi seq thread). + ALSAFifo=4 + }; + + // The desired event buffer when putting an event. + enum EventBufferType + { + // Playback queue for events that are scheduled by the playback engine. + PlaybackBuffer=0, + // User queue for non-playback events such as from GUI controls or external hw. + UserBuffer=1 + }; + + // Describes latency of events passed to putEvent(). + enum LatencyType + { + // The given event's time will be used 'as is' without modification. + NotLate = 0, + // The given event's time has some latency. Automatically compensate + // by adding an appropriate number of frames, depending on device type. + // For example, events sent from a GUI control to the GuiFifo are usually + // time-stamped in the past. For Synths and Jack midi (buffer-based systems) + // we add a forward offset (one segment size). For ALSA (a poll-based system), + // no offset is required. Similarly, events sent from OSC handlers to the + // OSCFifo are also usually time-stamped in the past. + // Another example, events sent by the play scheduler to the PlayFifo are + // /already/ scheduled properly for the future and need /no/ further compensation + // for either Jack midi or ALSA devices. + Late = 1 + }; + + private: // Used for multiple reads of fifos during process. int _tmpRecordCount[MIDI_CHANNELS + 1]; bool _sysexFIFOProcessed; @@ -54,38 +138,93 @@ int _openFlags; // configured open flags bool _readEnable; // set when opened/closed. bool _writeEnable; // + QString _state; + std::atomic _stopFlag; - bool _sysexReadingChunks; + // For processing system exclusive input chunks. + SysExInputProcessor _sysExInProcessor; + // For processing system exclusive output chunks. + SysExOutputProcessor _sysExOutProcessor; + // Holds all non-realtime events while the sysex processor is in the Sending state. + // The official midi specs say only realtime messages can be mingled in the middle of a sysex. + std::vector *_sysExOutDelayedEvents; + + MPEventList _stuckNotes; // Playback: Pending note-offs put directly to the device corresponding to currently playing notes + + // Playback IPC buffers. For playback events ONLY. Any thread can use this. + LockFreeMPSCRingBuffer *_playbackEventBuffers; + // Various IPC buffers. NOT for playback events. Any thread can use this. + LockFreeMPSCRingBuffer *_userEventBuffers; - MPEventList _stuckNotes; - MPEventList _playEvents; - - // Fifo for midi events sent from gui direct to midi port: - MidiFifo eventFifo; // Recording fifos. To speed up processing, one per channel plus one special system 'channel' for channel-less events like sysex. MidiRecFifo _recordFifo[MIDI_CHANNELS + 1]; - volatile bool stopPending; - volatile bool seekPending; + // To hold current output program, and RPN/NRPN parameter numbers and values. + MidiOutputParams _curOutParamNums[MIDI_CHANNELS]; RouteList _inRoutes, _outRoutes; + // Fifo holds brief history of incoming external clock messages. + // Timestamped with both tick and frame so that pending play events can + // be scheduled by frame. + // The audio thread processes this fifo and clears it. + LockFreeBuffer *_extClockHistoryFifo; + + // Returns the number of frames to shift forward output event scheduling times when putting events + // into the eventFifos. This is not quite the same as latency (requiring a backwards shift) + // although its requirement is a result of the latency. + // For any driver running in the audio thread (Jack midi, synth, metro etc) this value typically + // will equal one segment size. + // For drivers running in their own thread (ALSA, OSC input) this will typically be near zero: + // 1 ms for ALSA given a standard sequencer timer f = 1000Hz, or near zero for OSC input. + virtual unsigned int pbForwardShiftFrames() const { return 0; } + + // Various IPC buffers. Any thread can use this. + LockFreeMPSCRingBuffer *eventBuffers(EventBufferType bufferType) + { + switch(bufferType) + { + case PlaybackBuffer: + return _playbackEventBuffers; + + case UserBuffer: + return _userEventBuffers; + } + return _userEventBuffers; + } + + // Informs the device to clear (flush) the outEvents and event buffers. + // To be called by audio thread only. Typically from the device's handleStop routine. + void setStopFlag(bool flag) { _stopFlag.store(flag); } + // Returns whether the device is flagged to clear the outEvents and event buffers. + // To be called from the device's thread in the process routine. + bool stopFlag() const { return _stopFlag.load(); } + void init(); - virtual bool putMidiEvent(const MidiPlayEvent&) = 0; - virtual void processStuckNotes(); - - public: - enum { ALSA_MIDI=0, JACK_MIDI=1, SYNTH_MIDI=2 }; + public: MidiDevice(); MidiDevice(const QString& name); - virtual ~MidiDevice() {} + virtual ~MidiDevice(); - virtual int deviceType() const = 0; + SysExInputProcessor* sysExInProcessor() { return &_sysExInProcessor; } + SysExOutputProcessor* sysExOutProcessor() { return &_sysExOutProcessor; } + virtual MidiDeviceType deviceType() const = 0; + virtual QString deviceTypeString() const; + + // The meaning of the returned pointer depends on the driver. + // For Jack it returns the address of a Jack port, for ALSA it return the address of a snd_seq_addr_t. virtual void* inClientPort() { return 0; } virtual void* outClientPort() { return 0; } - + + // These three are generally for ALSA. + virtual void setAddressClient(int) { } + virtual void setAddressPort(int) { } + // We (ab)use the ALSA value SND_SEQ_ADDRESS_UNKNOWN to + // mean 'unavailable' if either client and port equal it. + virtual bool isAddressUnknown() const { return true; } + virtual QString open() = 0; virtual void close() = 0; virtual void writeRouting(int, Xml&) const { }; @@ -96,7 +235,10 @@ bool noOutRoute() const { return _outRoutes.empty(); } const QString& name() const { return _name; } + // setName can be overloaded to do other things like setting port names, while setNameText just sets the text. virtual void setName(const QString& s) { _name = s; } + // setNameText just sets the text, while setName can be overloaded to do other things like setting port names. + void setNameText(const QString& s) { _name = s; } int midiPort() const { return _port; } void setPort(int p); @@ -105,6 +247,8 @@ int openFlags() const { return _openFlags; } void setOpenFlags(int val) { _openFlags = val; } void setrwFlags(int val) { _rwFlags = val; } + const QString& state() const { return _state; } + void setState(const QString& s) { _state = s; } virtual bool isSynti() const { return false; } virtual int selectRfd() { return -1; } @@ -114,23 +258,30 @@ virtual void processInput() {} virtual void discardInput() {} + // Event time and tick must be set by caller beforehand. virtual void recordEvent(MidiRecordEvent&); - // Schedule an event for playback. Returns false if event cannot be delivered. - virtual bool addScheduledEvent(const MidiPlayEvent& ev) { _playEvents.add(ev); return true; } // Add a stuck note. Returns false if event cannot be delivered. virtual bool addStuckNote(const MidiPlayEvent& ev) { _stuckNotes.add(ev); return true; } - // Put an event for immediate playback. - virtual bool putEvent(const MidiPlayEvent&); - // This method will try to putEvent 'tries' times, waiting 'delayUs' microseconds between tries. - // Since it waits, it should not be used in RT or other time-sensitive threads. p4.0.15 - bool putEventWithRetry(const MidiPlayEvent&, int tries = 2, long delayUs = 50000); // 2 tries, 50 mS by default. + // Put either a playback or a user event. Returns true if event cannot be delivered. + virtual bool putEvent(const MidiPlayEvent& ev, + LatencyType latencyType, + EventBufferType bufferType = UserBuffer); + MidiOutputParams* curOutParamNums(int chan) { return &_curOutParamNums[chan]; } + void resetCurOutParamNums(int chan = -1); // Reset channel's current parameter numbers to -1. All channels if chan = -1. virtual void handleStop(); virtual void handleSeek(); + virtual void processStuckNotes(); + virtual void collectMidiEvents() {} - virtual void processMidi() {} + // Process midi events. The frame is used by devices such as ALSA + // that require grabbing a timestamp as early as possible and + // passing it along to all the devices. The other devices don't + // require the frame since they 'compose' a buffer based on the + // frame at cycle start. + virtual void processMidi(unsigned int /*curFrame*/ = 0) {} void beforeProcess(); void afterProcess(); @@ -138,9 +289,10 @@ MidiRecFifo& recordEvents(const unsigned int ch) { return _recordFifo[ch]; } bool sysexFIFOProcessed() { return _sysexFIFOProcessed; } void setSysexFIFOProcessed(bool v) { _sysexFIFOProcessed = v; } - bool sysexReadingChunks() { return _sysexReadingChunks; } - void setSysexReadingChunks(bool v) { _sysexReadingChunks = v; } - bool sendNullRPNParams(unsigned time, int port, int chan, bool); + + static const int extClockHistoryCapacity; + LockFreeBuffer *extClockHistory() { return _extClockHistoryFifo; } + void midiClockInput(unsigned int frame); }; //--------------------------------------------------------- @@ -148,6 +300,7 @@ //--------------------------------------------------------- typedef std::list::iterator iMidiDevice; +typedef std::list::const_iterator ciMidiDevice; class MidiDeviceList : public std::list { @@ -155,7 +308,32 @@ void add(MidiDevice* dev); void remove(MidiDevice* dev); MidiDevice* find(const QString& name, int typeHint = -1); - iMidiDevice find(const MidiDevice* dev); + + iterator find(const MidiDevice* dev) + { + for(iterator i = begin(); i != end(); ++i) + if(*i == dev) + return i; + return end(); + } + + const_iterator find(const MidiDevice* dev) const + { + for(const_iterator i = begin(); i != end(); ++i) + if(*i == dev) + return i; + return end(); + } + + bool contains(const MidiDevice* dev) const + { + for(const_iterator i = begin(); i != end(); ++i) + if(*i == dev) + return true; + return false; + } + + void addOperation(MidiDevice* dev, PendingOperationList& ops); }; extern void initMidiDevices(); diff -Nru muse-2.1.2/muse/midiedit/CMakeLists.txt muse-3.0.2+ds1/muse/midiedit/CMakeLists.txt --- muse-2.1.2/muse/midiedit/CMakeLists.txt 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( midiedit_mocs +QT5_WRAP_CPP ( midiedit_mocs # gatetime.h # midicmd.h # midieditor.h diff -Nru muse-2.1.2/muse/midiedit/dcanvas.cpp muse-3.0.2+ds1/muse/midiedit/dcanvas.cpp --- muse-2.1.2/muse/midiedit/dcanvas.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/dcanvas.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: dcanvas.cpp,v 1.16.2.10 2009/10/15 22:45:50 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -48,10 +49,12 @@ #include "globals.h" #include "midiport.h" #include "audio.h" +#include "midi.h" #include "shortcuts.h" #include "icons.h" #include "functions.h" #include "helper.h" +#include "operations.h" #define CARET 10 #define CARET2 5 @@ -80,7 +83,7 @@ // addItem //--------------------------------------------------------- -CItem* DrumCanvas::addItem(MusECore::Part* part, MusECore::Event& event) +CItem* DrumCanvas::addItem(MusECore::Part* part, const MusECore::Event& event) { if (signed(event.tick())<0) { printf("ERROR: trying to add event before current part!\n"); @@ -162,8 +165,59 @@ delete steprec; } +// //--------------------------------------------------------- +// // moveCanvasItems +// // Return false if invalid index +// //--------------------------------------------------------- +// +// bool DrumCanvas::index2Note(int index, int* port, int* channel, int* note) +// { +// if ((index<0) || (index>=getOurDrumMapSize())) +// return false; +// +// int mport, ch; +// if(old_style_drummap_mode) +// { +// // Default to track port if -1 and track channel if -1. +// mport = ourDrumMap[index].port; +// if(mport == -1) +// { +// if(!curPart || !curPart->track() || !curPart->track()->isMidiTrack()) +// return false; +// MusECore::MidiTrack* mt = static_cast(curPart->track()); +// mport = mt->outPort(); +// } +// ch = ourDrumMap[index].channel; +// if(ch == -1) +// { +// if(!curPart || !curPart->track() || !curPart->track()->isMidiTrack()) +// return false; +// MusECore::MidiTrack* mt = static_cast(curPart->track()); +// ch = mt->outChannel(); +// } +// } +// else +// { +// MusECore::Track* track = *instrument_map[index].tracks.begin(); +// if(!track->isMidiTrack()) +// return false; +// MusECore::MidiTrack* mt = static_cast(track); +// mport = mt->outPort(); +// ch = mt->outChannel(); +// } +// +// if(port) +// *port = mport; +// if(channel) +// *channel = ch; +// if(note) +// *note = old_style_drummap_mode ? ourDrumMap[index].anote : instrument_map[index].pitch; +// +// return true; +// } + //--------------------------------------------------------- -// moveCanvasItems +// index2Note // Return false if invalid index //--------------------------------------------------------- @@ -195,24 +249,46 @@ } else { - MusECore::Track* track = *instrument_map[index].tracks.begin(); - if(!track->isMidiTrack()) - return false; - MusECore::MidiTrack* mt = static_cast(track); - mport = mt->outPort(); - ch = mt->outChannel(); + // Default to track port if -1 and track channel if -1. + MusECore::Track* track = 0; + MusECore::MidiTrack* mt = 0; + if(ourDrumMap[index].port == -1) + { + track = *instrument_map[index].tracks.begin(); + if(!track->isMidiTrack()) + return false; + mt = static_cast(track); + mport = mt->outPort(); + } + else + mport = ourDrumMap[index].port; + + if(ourDrumMap[index].channel == -1) + { + if(!track) + { + track = *instrument_map[index].tracks.begin(); + if(!track->isMidiTrack()) + return false; + mt = static_cast(track); + } + ch = mt->outChannel(); + } + else + ch = ourDrumMap[index].channel; } - + if(port) *port = mport; if(channel) *channel = ch; if(note) - *note = old_style_drummap_mode ? ourDrumMap[index].anote : instrument_map[index].pitch; - + //*note = old_style_drummap_mode ? ourDrumMap[index].anote : instrument_map[index].pitch; + *note = ourDrumMap[index].anote; + return true; } - + //--------------------------------------------------------- // moveCanvasItems //--------------------------------------------------------- @@ -486,7 +562,7 @@ // newItem //--------------------------------------------------------- void DrumCanvas::newItem(CItem* item, bool noSnap) { - newItem(item, noSnap,false); + newItem(item, noSnap,true); } void DrumCanvas::newItem(CItem* item, bool noSnap, bool replace) @@ -514,43 +590,57 @@ return; npitch = instrument_map[npitch].pitch; event.setPitch(npitch); + event.setSelected(true); // check for existing event // if found change command semantic from insert to delete - MusECore::EventList* el = part->events(); - MusECore::iEvent lower = el->lower_bound(event.tick()); - MusECore::iEvent upper = el->upper_bound(event.tick()); - - for (MusECore::iEvent i = lower; i != upper; ++i) { - MusECore::Event ev = i->second; - if(!ev.isNote()) - continue; - if (ev.pitch() == npitch) { - // Indicate do undo, and do not do port controller values and clone parts. - MusEGlobal::audio->msgDeleteEvent(ev, nevent->part(), true, false, false); - if (replace) - break; - else - return; - } - } - MusECore::Undo operations; + std::pair range = + part->events().equal_range(event.type() == MusECore::Wave ? event.frame() : event.tick()); + MusECore::Event oev; + bool found = false; + for(MusECore::ciEvent i = range.first; i != range.second; ++i) + { + oev = i->second; + if(!oev.isNote()) + continue; + if(oev.pitch() == npitch) + { + found = true; + break; + } + } + + // Indicate do undo, and do not do port controller values and clone parts. int diff = event.endTick()-part->lenTick(); - if (! ((diff > 0) && part->hasHiddenEvents()) ) //operation is allowed { - operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent,event, part, false, false)); - + if(found) + { + if(replace) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent,event, oev, part, false, false)); + else + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent,oev, part, false, false)); + } + else + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent,event, part, false, false)); + if (diff > 0) // part must be extended? { schedule_resize_all_same_len_clone_parts(part, event.endTick(), operations); printf("newItem: extending\n"); } } - //else forbid action by not applying it - MusEGlobal::song->applyOperationGroup(operations); - songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is neccessary - //to remove "forbidden" events from the list again + else // forbid action by not applying it + { + if(found) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent,oev, part, false, false)); + } + + if(!operations.empty()) + MusEGlobal::song->applyOperationGroup(operations); + else + songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is necessary + //to remove "forbidden" events from the list again } //--------------------------------------------------------- @@ -576,6 +666,15 @@ MusECore::Event e = ((DEvent*)item)->event(); int index = e.pitch(); // play note: + + if (!old_style_drummap_mode) { + for (int i = 0; i < instrument_map.size(); ++i) { + if (instrument_map.at(i).pitch == index) { + index = i; + break; + } + } + } int pitch, port, channel; if(index2Note(index, &port, &channel, &pitch)) startPlayEvent(pitch, e.velo(), port, channel); @@ -599,22 +698,29 @@ void DrumCanvas::itemMoved(const MusEGui::CItem* item, const QPoint& pos) { - if(!_playEvents) - return; int index = y2pitch(pos.y()); int pitch, port, channel; - if(index2Note(index, &port, &channel, &pitch)) + if(!index2Note(index, &port, &channel, &pitch)) { - if(_playEvents && (port != playedPitchPort || channel != playedPitchChannel || pitch != playedPitch)) - { - MusECore::Event e = ((DEvent*)item)->event(); - // release note: - stopPlayEvent(); - if (moving.size() <= 1) { // items moving or curItem - // play note: - startPlayEvent(pitch, e.velo(), port, channel); - } - } + // Stop any playing notes: + stopPlayEvent(); + return; + } + + // Ignore if the note is already playing. + if(stuckNoteExists(port, channel, pitch)) + return; + + // Stop any playing notes: + stopPlayEvent(); + + if(_playEvents) + { + if (moving.size() <= 1) { // items moving or curItem + const MusECore::Event e = ((DEvent*)item)->event(); + // play note: + startPlayEvent(pitch, e.velo(), port, channel); + } } } @@ -634,7 +740,7 @@ pa.setPoint(2, x + CARET2, y); pa.setPoint(3, x, y + CARET2); QRect r(pa.boundingRect()); - r = r.intersect(rect); + r = r.intersected(rect); if(!r.isValid()) return; @@ -691,7 +797,7 @@ pa.setPoint(2, x+CARET2, y + TH/2); pa.setPoint(3, x, y + (TH-CARET)/2); QRect mr(pa.boundingRect()); - mr = mr.intersect(rect); + mr = mr.intersected(rect); if(!mr.isValid()) return; p.setPen(Qt::black); @@ -917,23 +1023,24 @@ // startDrag //--------------------------------------------------------- -void DrumCanvas::startDrag(CItem* /* item*/, bool copymode) - { - QMimeData* md = selected_events_to_mime(partlist_to_set(editor->parts()), 1); - - if (md) { - // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. - // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can - // clean up after the drag and drop operation has been completed. " - QDrag* drag = new QDrag(this); - drag->setMimeData(md); - - if (copymode) - drag->exec(Qt::CopyAction); - else - drag->exec(Qt::MoveAction); - } - } +void DrumCanvas::startDrag(CItem* /* item*/, DragType t) +{ + QMimeData* md = selected_events_to_mime(partlist_to_set(editor->parts()), 1); + + if (md) + { + // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. + // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can + // clean up after the drag and drop operation has been completed. " + QDrag* drag = new QDrag(this); + drag->setMimeData(md); + + if (t == MOVE_COPY || t == MOVE_CLONE) + drag->exec(Qt::CopyAction); + else + drag->exec(Qt::MoveAction); + } +} //--------------------------------------------------------- // dragEnterEvent @@ -967,6 +1074,12 @@ void DrumCanvas::keyPressed(int index, int velocity) { + // REMOVE Tim. Noteoff. Added. Zero note on vel is not allowed now. + if(velocity > 127) + velocity = 127; + else if(velocity <= 0) + velocity = 1; + if ((index<0) || (index>=getOurDrumMapSize())) return; // called from DList - play event @@ -1003,18 +1116,8 @@ { // release note: if(_playEvents) - { - // REMOVE Tim. - //int pitch, port, channel; - //if(index2Note(index, &port, &channel, &pitch)) - //{ - // MusECore::MidiPlayEvent e(0, port, channel, 0x90, pitch, 0); - // MusEGlobal::audio->msgPlayMidiEvent(&e); - //} stopPlayEvent(); } - //playedPitch=-1; - } //--------------------------------------------------------- // mapChanged @@ -1028,10 +1131,10 @@ if (old_style_drummap_mode) { MusECore::Undo operations; - std::vector< std::pair > delete_events; + std::vector< std::pair > delete_events; std::vector< std::pair > add_events; - typedef std::vector< std::pair >::iterator idel_ev; + typedef std::vector< std::pair >::iterator idel_ev; typedef std::vector< std::pair >::iterator iadd_ev; MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); @@ -1043,9 +1146,9 @@ MusECore::MidiPort* mp = &MusEGlobal::midiPorts[curTrack->outPort()]; MusECore::PartList* parts= curTrack->parts(); for (MusECore::iPart part = parts->begin(); part != parts->end(); ++part) { - MusECore::EventList* events = part->second->events(); + const MusECore::EventList& events = part->second->events(); MusECore::Part* thePart = part->second; - for (MusECore::iEvent i = events->begin(); i != events->end(); ++i) { + for (MusECore::ciEvent i = events.begin(); i != events.end(); ++i) { MusECore::Event event = i->second; if(event.type() != MusECore::Controller && event.type() != MusECore::Note) continue; @@ -1059,9 +1162,9 @@ } if (pitch == spitch) { - MusECore::Event* spitch_event = &(i->second); - delete_events.push_back(std::pair(thePart, spitch_event)); - MusECore::Event newEvent = spitch_event->clone(); + const MusECore::Event& spitch_event = i->second; + delete_events.push_back(std::pair(thePart, spitch_event)); + MusECore::Event newEvent = spitch_event.clone(); if(drc) newEvent.setA((newEvent.dataA() & ~0xff) | dpitch); else @@ -1069,9 +1172,9 @@ add_events.push_back(std::pair(thePart, newEvent)); } else if (pitch == dpitch) { - MusECore::Event* dpitch_event = &(i->second); - delete_events.push_back(std::pair(thePart, dpitch_event)); - MusECore::Event newEvent = dpitch_event->clone(); + const MusECore::Event& dpitch_event = i->second; + delete_events.push_back(std::pair(thePart, dpitch_event)); + MusECore::Event newEvent = dpitch_event.clone(); if(drc) newEvent.setA((newEvent.dataA() & ~0xff) | spitch); else @@ -1084,8 +1187,8 @@ for (idel_ev i = delete_events.begin(); i != delete_events.end(); i++) { MusECore::Part* thePart = (*i).first; - MusECore::Event* theEvent = (*i).second; - operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, *theEvent, thePart, true, false)); + const MusECore::Event& theEvent = (*i).second; + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, theEvent, thePart, true, false)); } MusECore::DrumMap dm = MusEGlobal::drumMap[spitch]; @@ -1103,7 +1206,7 @@ } MusEGlobal::song->applyOperationGroup(operations, false); // do not indicate undo - MusEGlobal::song->update(SC_DRUMMAP); //this update is neccessary, as it's not handled by applyOperationGroup() + MusEGlobal::song->update(SC_DRUMMAP); //this update is necessary, as it's not handled by applyOperationGroup() } else // if (!old_style_drummap_mode) { @@ -1213,9 +1316,8 @@ void DrumCanvas::modifySelected(NoteInfo::ValType type, int val, bool delta_mode) { - QList< QPair > already_done; - MusEGlobal::audio->msgIdle(true); - MusEGlobal::song->startUndo(); + QList< QPair > already_done; + MusECore::Undo operations; for (iCItem i = items.begin(); i != items.end(); ++i) { if (!(i->second->isSelected())) continue; @@ -1226,7 +1328,7 @@ MusECore::MidiPart* part = (MusECore::MidiPart*)(e->part()); - if (already_done.contains(QPair(part->events(), event))) + if (already_done.contains(QPair(part->clonemaster_sn(), event))) continue; MusECore::Event newEvent = event.clone(); @@ -1262,7 +1364,9 @@ if (velo > 127) velo = 127; else if (velo < 0) - velo = 0; + // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now. +// velo = 0; + velo = 1; newEvent.setVelo(velo); } break; @@ -1304,14 +1408,12 @@ } break; } - MusEGlobal::song->changeEvent(event, newEvent, part); - // Indicate do not do port controller values and clone parts. - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); - already_done.append(QPair(part->events(), event)); + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); + + already_done.append(QPair(part->clonemaster_sn(), event)); } - MusEGlobal::song->endUndo(SC_EVENT_MODIFIED); - MusEGlobal::audio->msgIdle(false); + MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- @@ -1482,18 +1584,17 @@ //--------------------------------------------------------- // getEventAtCursorPos //--------------------------------------------------------- -MusECore::Event *DrumCanvas::getEventAtCursorPos() +const MusECore::Event* DrumCanvas::getEventAtCursorPos() { if (_tool != CursorTool) return 0; if (instrument_map[cursorPos.y()].tracks.contains(curPart->track())) { - MusECore::EventList* el = curPart->events(); - MusECore::iEvent lower = el->lower_bound(cursorPos.x()-curPart->tick()); - MusECore::iEvent upper = el->upper_bound(cursorPos.x()-curPart->tick()); + MusECore::ciEvent lower = curPart->events().lower_bound(cursorPos.x()-curPart->tick()); + MusECore::ciEvent upper = curPart->events().upper_bound(cursorPos.x()-curPart->tick()); int curPitch = instrument_map[cursorPos.y()].pitch; - for (MusECore::iEvent i = lower; i != upper; ++i) { - MusECore::Event &ev = i->second; + for (MusECore::ciEvent i = lower; i != upper; ++i) { + const MusECore::Event& ev = i->second; if (ev.isNote() && ev.pitch() == curPitch) return &ev; } @@ -1504,7 +1605,7 @@ //--------------------------------------------------------- // selectCursorEvent //--------------------------------------------------------- -void DrumCanvas::selectCursorEvent(MusECore::Event *ev) +void DrumCanvas::selectCursorEvent(const MusECore::Event* ev) { for (iCItem i = items.begin(); i != items.end(); ++i) { @@ -1633,23 +1734,111 @@ return -1; } -void DrumCanvas::propagate_drummap_change(int instr, bool update_druminmap) +void DrumCanvas::propagate_drummap_change(int instrument, int fields, bool isReset, bool includeDefault, bool isInstrumentMod, bool doWholeMap) +{ + //fprintf(stderr, "DrumCanvas::propagate_track_drummap_change instrument:%d fields:%x isReset:%d isInstrumentMod:%d\n", + // instrument, fields, isReset, isInstrumentMod); + const QSet& tracks=instrument_map[instrument].tracks; + int index=instrument_map[instrument].pitch; + + MusECore::DrumMapTrackOperation* dmop = new MusECore::DrumMapTrackOperation; + dmop->_isReset = isReset; + dmop->_includeDefault = includeDefault; + dmop->_doWholeMap = doWholeMap; + dmop->_isInstrumentMod = isInstrumentMod; + + MusECore::PendingOperationList operations; + MusECore::Track* t; + for(QSet::const_iterator it = tracks.begin(); it != tracks.end(); it++) + { + t = *it; + if(!t->isDrumTrack()) + continue; + MusECore::MidiTrack* mt = static_cast(t); + dmop->_tracks.push_back(mt); + } + + if(isReset) + dmop->_workingItemList.add(index, MusECore::WorkingDrumMapEntry(MusECore::DrumMap(), fields)); // Fixme: Dummy map. Should just be fields. + else + dmop->_workingItemList.add(index, MusECore::WorkingDrumMapEntry(ourDrumMap[instrument], fields)); + + operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ModifyTrackDrumMapItem)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); +} + +int DrumCanvas::isWorkingMapInstrument(int instr, int fields) const { const QSet& tracks=instrument_map[instr].tracks; int index=instrument_map[instr].pitch; - + + MusECore::Track* t; + MusECore::MidiTrack* mt; + int ret = MusECore::WorkingDrumMapEntry::NoOverride; for (QSet::const_iterator it = tracks.begin(); it != tracks.end(); it++) { - MusECore::MidiTrack* mt=dynamic_cast(*it); - // if we're not only changing "mute" state... - if (!mt->drummap()[index].almost_equals(ourDrumMap[instr])) - mt->set_drummap_tied_to_patch(false); - mt->drummap()[index] = ourDrumMap[instr]; - if (update_druminmap) - mt->update_drum_in_map(); + t = *it; + if(!t->isDrumTrack()) + continue; + mt = static_cast(t); + // Don't pass a patch - ask it to take care of patch number for us. + ret |= mt->isWorkingMapItem(index, fields); + } + return ret; +} + +bool DrumCanvas::hasOverrides(int instr) const +{ + const QSet& tracks=instrument_map[instr].tracks; + MusECore::Track* t; + MusECore::MidiTrack* mt; + for (QSet::const_iterator it = tracks.begin(); it != tracks.end(); it++) + { + t = *it; + if(!t->isDrumTrack()) + continue; + mt = static_cast(t); + if(!mt->workingDrumMap()->empty()) + return true; } + return false; } +void DrumCanvas::resetOverridesForAllPatches(int instr) +{ + if(QMessageBox::warning(this, tr("Drum map"), + tr("Reset the track's drum map with instrument defaults?"), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok) + { + MusECore::PendingOperationList operations; + const QSet& tracks=instrument_map[instr].tracks; + MusECore::Track* t; + MusECore::MidiTrack* mt; + MusECore::WorkingDrumMapPatchList* new_wdmpl; + MusECore::DrumMapTrackPatchReplaceOperation* dmop; + for (QSet::const_iterator it = tracks.begin(); it != tracks.end(); it++) + { + t = *it; + if(!t->isDrumTrack()) + continue; + mt = static_cast(t); + if(!mt->workingDrumMap()->empty()) + { + // Completely blank replacement list. + new_wdmpl = new MusECore::WorkingDrumMapPatchList(); + // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the + // original lists will be deleted, in the operation following. + dmop = new MusECore::DrumMapTrackPatchReplaceOperation; + dmop->_isInstrumentMod = false; // Not instrument operation. + dmop->_workingItemPatchList = new_wdmpl; + dmop->_track = static_cast(t); + operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList)); + } + } + if(!operations.empty()) + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } +} void DrumCanvas::rebuildOurDrumMap() { @@ -1666,6 +1855,7 @@ if (!old_style_drummap_mode) { + //fprintf(stderr, "DrumCanvas::rebuildOurDrumMap\n"); bool need_update = false; TrackList* tl=MusEGlobal::song->tracks(); @@ -1755,8 +1945,7 @@ { if (dynamic_cast(*track)->drummap()[pitch].mute == false) mute=false; - - if (dynamic_cast(*track)->drummap_hidden()[pitch] == false) + if (dynamic_cast(*track)->drummap()[pitch].hide == false) hidden=false; } @@ -1771,10 +1960,6 @@ need_update = true; } } - - if (dynamic_cast(*group->begin())->drummap()[pitch].anote != pitch) - printf("THIS SHOULD NEVER HAPPEN: track's_drummap[pitch].anote (%i)!= pitch (%i) !!!\n",dynamic_cast(*group->begin())->drummap()[pitch].anote,pitch); - instrument_map.append(instrument_number_mapping_t(*group, pitch)); } @@ -1794,8 +1979,18 @@ ourDrumMap=new DrumMap[size]; must_delete_our_drum_map=true; + Track* t; + MidiTrack* mt; + int index; for (int i=0;i(*instrument_map[i].tracks.begin())->drummap()[instrument_map[i].pitch]; + { + t = *instrument_map[i].tracks.begin(); + if(!t->isMidiTrack()) + continue; + mt = static_cast(t); + index = instrument_map[i].pitch; + ourDrumMap[i] = mt->drummap()[index]; + } if (instrument_map!=old_instrument_map) { diff -Nru muse-2.1.2/muse/midiedit/dcanvas.h muse-3.0.2+ds1/muse/midiedit/dcanvas.h --- muse-2.1.2/muse/midiedit/dcanvas.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/dcanvas.h 2017-12-04 21:01:18.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: dcanvas.h,v 1.8.2.2 2009/02/02 21:38:00 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -72,7 +73,7 @@ pitch=p; } - bool operator==(const instrument_number_mapping_t& that) //TODO maybe compare the Track* serial numbers, not the pointers themselves? + bool operator==(const instrument_number_mapping_t& that) const //TODO maybe compare the Track* serial numbers, not the pointers themselves? { return (this->tracks == that.tracks && this->pitch==that.pitch); } @@ -126,11 +127,11 @@ int pitch2y(int pitch) const; inline int y2height(int) const { return TH; } inline int yItemOffset() const { return -TH/2; } - void startDrag(CItem*, bool copymode); + void startDrag(CItem*, DragType); void dragEnterEvent(QDragEnterEvent* event); void dragMoveEvent(QDragMoveEvent*); void dragLeaveEvent(QDragLeaveEvent*); - virtual CItem* addItem(MusECore::Part*, MusECore::Event&); + virtual CItem* addItem(MusECore::Part*, const MusECore::Event&); virtual void resizeEvent(QResizeEvent*); virtual void curPartChanged(); int getNextStep(unsigned int pos, int basicStep, int stepSize=1); @@ -167,15 +168,24 @@ virtual void modifySelected(NoteInfo::ValType type, int val, bool delta_mode = true); virtual void keyPress(QKeyEvent* event); virtual void keyRelease(QKeyEvent* event); - MusECore::Event *getEventAtCursorPos(); - void selectCursorEvent(MusECore::Event *ev); + const MusECore::Event* getEventAtCursorPos(); + void selectCursorEvent(const MusECore::Event* ev); int pitch_and_track_to_instrument(int pitch, MusECore::Track* track); + // Returns OR'd WorkingDrumMapEntry::OverrideType flags indicating whether a map item's members, + // given by 'fields' (OR'd WorkingDrumMapEntry::Fields), are either the original or working map item. + // Here in DrumCanvas the flags can be NoOverride, TrackOverride, and InstrumentOverride. + int isWorkingMapInstrument(int instr, int fields) const; + // Returns true if any of the instrument's group tracks have non-empty working lists. + bool hasOverrides(int instr) const; + // Resets all overrides in all patches (clears all override lists), in the instrument's group tracks. + void resetOverridesForAllPatches(int instr); + MusECore::DrumMap* getOurDrumMap() { return ourDrumMap; } int getOurDrumMapSize() { return instrument_map.size(); } QVector& get_instrument_map() { return instrument_map; } - void propagate_drummap_change(int instrument, bool update_druminmap); + void propagate_drummap_change(int instrument, int fields, bool isReset, bool includeDefault, bool isInstrumentMod, bool doWholeMap); void rebuildOurDrumMap(); DrumEdit* drumEdit() { return drumEditor; } }; diff -Nru muse-2.1.2/muse/midiedit/dlist.cpp muse-3.0.2+ds1/muse/midiedit/dlist.cpp --- muse-2.1.2/muse/midiedit/dlist.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/dlist.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: dlist.cpp,v 1.9.2.7 2009/10/16 21:50:16 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -43,15 +44,219 @@ #include "dlist.h" #include "song.h" #include "dcanvas.h" +#include "minstrument.h" namespace MusEGui { //--------------------------------------------------------- +// DLineEdit +//--------------------------------------------------------- + +DLineEdit::DLineEdit(QWidget* parent) : QLineEdit(parent) +{ + // Reset these since our parent will typically turn them on for speed. + setAutoFillBackground(true); + setAttribute(Qt::WA_NoSystemBackground, false); + setAttribute(Qt::WA_StaticContents, false); + setAttribute(Qt::WA_OpaquePaintEvent, false); + + setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + setContentsMargins(0, 0, 0, 0); +} + +bool DLineEdit::event(QEvent* e) +{ + switch(e->type()) + { + case QEvent::KeyPress: + { + QKeyEvent* ke = static_cast(e); + switch(ke->key()) + { + // For return, we want to close the editor but don't want the + // parent to receive the event which will just open the box again. + case Qt::Key_Return: + case Qt::Key_Enter: + e->accept(); + emit returnPressed(); + return true; + break; + + case Qt::Key_Escape: + { + e->accept(); + emit escapePressed(); + return true; + } + break; + + default: + break; + } + } + break; + + case QEvent::NonClientAreaMouseButtonPress: + // FIXME: Doesn't work. + //fprintf(stderr, "DLineEdit::event NonClientAreaMouseButtonPress\n"); + case QEvent::FocusOut: + e->accept(); + emit returnPressed(); + return true; + break; + + default: + break; + } + + // Do not pass ANY events on to the parent. + QLineEdit::event(e); + e->accept(); + return true; +} + +//--------------------------------------------------------- +// DrumListSpinBox +//--------------------------------------------------------- + +DrumListSpinBox::DrumListSpinBox(QWidget* parent) : QSpinBox(parent) +{ + // Reset these since our parent will typically turn them on for speed. + setAutoFillBackground(true); + setAttribute(Qt::WA_NoSystemBackground, false); + setAttribute(Qt::WA_StaticContents, false); + setAttribute(Qt::WA_OpaquePaintEvent, false); + + setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + setContentsMargins(0, 0, 0, 0); +} + +bool DrumListSpinBox::event(QEvent* e) +{ + switch(e->type()) + { + case QEvent::KeyPress: + { + QKeyEvent* ke = static_cast(e); + switch(ke->key()) + { + // For return, we want to close the editor but don't want the + // parent to receive the event which will just open the box again. + case Qt::Key_Return: + case Qt::Key_Enter: + e->accept(); + emit returnPressed(); + return true; + break; + + case Qt::Key_Escape: + { + e->accept(); + emit escapePressed(); + return true; + } + break; + + default: + break; + } + } + break; + + case QEvent::NonClientAreaMouseButtonPress: + // FIXME: Doesn't work. + //fprintf(stderr, "DLineEdit::event NonClientAreaMouseButtonPress\n"); + case QEvent::FocusOut: + e->accept(); + emit returnPressed(); + return true; + break; + + default: + break; + } + + // Do not pass ANY events on to the parent. + QSpinBox::event(e); + e->accept(); + return true; +} + +//--------------------------------------------------------- +// DPitchEdit +//--------------------------------------------------------- + +DPitchEdit::DPitchEdit(QWidget* parent) : PitchEdit(parent) +{ + // Reset these since our parent will typically turn them on for speed. + setAutoFillBackground(true); + setAttribute(Qt::WA_NoSystemBackground, false); + setAttribute(Qt::WA_StaticContents, false); + setAttribute(Qt::WA_OpaquePaintEvent, false); + + setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); + setContentsMargins(0, 0, 0, 0); +} + +bool DPitchEdit::event(QEvent* e) +{ + switch(e->type()) + { + case QEvent::KeyPress: + { + QKeyEvent* ke = static_cast(e); + switch(ke->key()) + { + // For return, we want to close the editor but don't want the + // parent to receive the event which will just open the box again. + case Qt::Key_Return: + case Qt::Key_Enter: + e->accept(); + emit returnPressed(); + return true; + break; + + case Qt::Key_Escape: + { + e->accept(); + emit escapePressed(); + return true; + } + break; + + default: + break; + } + } + break; + + case QEvent::NonClientAreaMouseButtonPress: + // FIXME: Doesn't work. + //fprintf(stderr, "DPitchEdit::event NonClientAreaMouseButtonPress\n"); + case QEvent::FocusOut: + e->accept(); + emit returnPressed(); + return true; + break; + + default: + break; + } + + // Do not pass ANY events on to the parent. + Awl::PitchEdit::event(e); + e->accept(); + return true; +} + +//--------------------------------------------------------- // draw //--------------------------------------------------------- void DList::draw(QPainter& p, const QRect& rect) { + using MusECore::WorkingDrumMapEntry; + int x = rect.x(); int y = rect.y(); int w = rect.width(); @@ -62,6 +267,10 @@ //--------------------------------------------------- p.setPen(Qt::black); + QColor override_col(Qt::gray); + override_col.setAlpha(64); + + QFont fnt(p.font()); for (int instrument = 0; instrument < ourDrumMapSize; ++instrument) { int yy = instrument * TH; @@ -80,8 +289,9 @@ for (int k = 0; k < h->count(); ++k) { if (h->isSectionHidden(k)) continue; - - + + int isWorkingItem = MusECore::WorkingDrumMapEntry::NoOverride; + int x = h->sectionPosition(k); int w = h->sectionSize(k); //QRect r = p.combinedTransform().mapRect(QRect(x+2, yy, w-4, TH)); // Gives inconsistent positions. Source shows wrong operation for our needs. @@ -89,74 +299,119 @@ QString s; int align = Qt::AlignVCenter | Qt::AlignHCenter; + bool doOverrideFill = true; + switch (k) { case COL_VOLUME: s.setNum(dm->vol); + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::VolField); break; case COL_QUANT: s.setNum(dm->quant); + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::QuantField); break; case COL_NOTELENGTH: s.setNum(dm->len); + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::LenField); break; case COL_NOTE: s = MusECore::pitch2string(dm->anote); + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::ANoteField); break; case COL_INPUTTRIGGER: s = MusECore::pitch2string(dm->enote); + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::ENoteField); break; case COL_LEVEL1: s.setNum(dm->lv1); + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::Lv1Field); break; case COL_LEVEL2: s.setNum(dm->lv2); + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::Lv2Field); break; case COL_LEVEL3: s.setNum(dm->lv3); + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::Lv3Field); break; case COL_LEVEL4: s.setNum(dm->lv4); + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::Lv4Field); break; case COL_HIDE: { - bool hidden=false; - bool shown=false; - QSet* group = &dcanvas->get_instrument_map()[instrument].tracks; - int pitch = dcanvas->get_instrument_map()[instrument].pitch; - - for (QSet::iterator track=group->begin(); track!=group->end() && !(hidden&&shown); track++) - if (dynamic_cast(*track)->drummap_hidden()[pitch]) - hidden=true; - else - shown=true; - - if (!hidden && !shown) - printf("THIS SHOULD NEVER HAPPEN: in DList::draw(): instrument %i's track group is empty. strange...\n", instrument); - - const QPixmap* pm = NULL; - - if (shown && !hidden) - pm = eyeIcon; - else if (!shown && hidden) - pm = eyeCrossedIcon; - else if (shown && hidden) - pm = eyeGrayIcon; - else //if (!shown && !hidden) - pm = NULL; - - if (pm) + // isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, VolField); + doOverrideFill = false; + if(dcanvas) + { + bool hidden=false; + bool shown=false; + QSet* group = &dcanvas->get_instrument_map()[instrument].tracks; + int pitch = dcanvas->get_instrument_map()[instrument].pitch; + + for (QSet::iterator track=group->begin(); track!=group->end() && !(hidden&&shown); track++) + // if (dynamic_cast(*track)->drummap_hidden()[pitch]) + if (dynamic_cast(*track)->drummap()[pitch].hide) + hidden=true; + else + shown=true; + + if (!hidden && !shown) + printf("THIS SHOULD NEVER HAPPEN: in DList::draw(): instrument %i's track group is empty. strange...\n", instrument); + + const QPixmap* pm = NULL; + + if (shown && !hidden) + pm = eyeIcon; + else if (!shown && hidden) + pm = eyeCrossedIcon; + else if (shown && hidden) + pm = eyeGrayIcon; + else //if (!shown && !hidden) + pm = NULL; + + if (pm) + { + // p.setPen(Qt::red); + p.drawPixmap( + r.x() + r.width()/2 - pm->width()/2, + r.y() + r.height()/2 - pm->height()/2, + *pm); + // p.setPen(Qt::black); + } + } + else { - // p.setPen(Qt::red); + const QPixmap* pm = dm->hide ? eyeCrossedIcon : eyeIcon; + if (dm->hide) + pm = eyeCrossedIcon; + else + pm = eyeIcon; + // p.setPen(Qt::red); p.drawPixmap( - r.x() + r.width()/2 - pm->width()/2, - r.y() + r.height()/2 - pm->height()/2, - *pm); - // p.setPen(Qt::black); + r.x() + r.width()/2 - pm->width()/2, + r.y() + r.height()/2 - pm->height()/2, + *pm); + // p.setPen(Qt::black); } break; } case COL_MUTE: + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::MuteField); + doOverrideFill = false; + if(isWorkingItem == MusECore::WorkingDrumMapEntry::NoOverride) + p.fillRect(r, override_col); if (dm->mute) { p.setPen(Qt::red); const QPixmap& pm = *muteIcon; @@ -169,6 +424,11 @@ break; case COL_NAME: { + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::NameField); + doOverrideFill = false; + if(isWorkingItem == MusECore::WorkingDrumMapEntry::NoOverride) + p.fillRect(r, override_col); if(dcanvas && dcanvas->part()) { MusECore::Part* cur_part = dcanvas->part(); @@ -208,8 +468,7 @@ continue; found = true; - MusECore::EventList* el = cur_part->events(); - for(MusECore::iEvent ie = el->begin(); ie != el->end(); ++ie) + for(MusECore::ciEvent ie = cur_part->events().begin(); ie != cur_part->events().end(); ++ie) { MusECore::Event e = ie->second; if(e.type() != MusECore::Controller) @@ -303,14 +562,14 @@ continue; found = true; - MusECore::EventList* el = cur_part->events(); + const MusECore::EventList& el = cur_part->events(); //MusECore::PartList* part_list = dcanvas->drumEdit()->parts(); //for(MusECore::ciPart ip = part_list->cbegin(); ip != part_list->cend(); ++ip) { //MusECore::Part* part = ip->second; ///if(part->track() != - //MusECore::EventList* el = part->events(); - for(MusECore::iEvent ie = el->begin(); ie != el->end(); ++ie) + //const MusECore::EventList& el = part->events(); + for(MusECore::ciEvent ie = el.begin(); ie != el.end(); ++ie) { MusECore::Event e = ie->second; if(e.type() != MusECore::Controller) @@ -356,24 +615,76 @@ } QString str = dm->name; align = Qt::AlignVCenter | Qt::AlignLeft; + fnt.setItalic(false); + fnt.setBold(false); + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + if(isWorkingItem & + (MusECore::WorkingDrumMapEntry::TrackOverride | MusECore::WorkingDrumMapEntry::TrackDefaultOverride)) + fnt.setBold(true); + else if(isWorkingItem & + (MusECore::WorkingDrumMapEntry::InstrumentOverride | MusECore::WorkingDrumMapEntry::InstrumentDefaultOverride)) + fnt.setItalic(true); +#else + if(isWorkingItem & (MusECore::WorkingDrumMapEntry::TrackOverride | + MusECore::WorkingDrumMapEntry::TrackDefaultOverride)) + fnt.setBold(true); + if( (isWorkingItem & MusECore::WorkingDrumMapEntry::TrackDefaultOverride) && + !(isWorkingItem & MusECore::WorkingDrumMapEntry::TrackOverride)) + fnt.setItalic(true); +#endif + + p.setFont(fnt); p.drawText(r.x() + 8, r.y(), r.width() - 8, r.height(), align, str); } break; // Default to track port if -1 and track channel if -1. case COL_OUTCHANNEL: + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::ChanField); if(dm->channel != -1) s.setNum(dm->channel+1); break; case COL_OUTPORT: + if(dcanvas) + isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::PortField); if(dm->port != -1) - s.sprintf("%d:%s", dm->port+1, MusEGlobal::midiPorts[dm->port].portname().toLatin1().constData()); + s = QString("%1:%2") + .arg(dm->port + 1) + .arg(MusEGlobal::midiPorts[dm->port].portname()); align = Qt::AlignVCenter | Qt::AlignLeft; break; } + + if(doOverrideFill && isWorkingItem == MusECore::WorkingDrumMapEntry::NoOverride) + p.fillRect(r, override_col); + if (!s.isEmpty()) + { + fnt.setItalic(false); + fnt.setBold(false); + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + if(isWorkingItem & + (MusECore::WorkingDrumMapEntry::TrackOverride | MusECore::WorkingDrumMapEntry::TrackDefaultOverride)) + fnt.setBold(true); + else if(isWorkingItem & + (MusECore::WorkingDrumMapEntry::InstrumentOverride | MusECore::WorkingDrumMapEntry::InstrumentDefaultOverride)) + fnt.setItalic(true); +#else + if(isWorkingItem & (MusECore::WorkingDrumMapEntry::TrackOverride | + MusECore::WorkingDrumMapEntry::TrackDefaultOverride)) + fnt.setBold(true); + if( (isWorkingItem & MusECore::WorkingDrumMapEntry::TrackDefaultOverride) && + !(isWorkingItem & MusECore::WorkingDrumMapEntry::TrackOverride)) + fnt.setItalic(true); +#endif + + p.setFont(fnt); p.drawText(r, align, s); } + } p.restore(); } @@ -415,23 +726,18 @@ // devicesPopupMenu //--------------------------------------------------------- -void DList::devicesPopupMenu(MusECore::DrumMap* t, int x, int y, bool changeAll) - { - if (!old_style_drummap_mode) +bool DList::devicesPopupMenu(MusECore::DrumMap* t, int x, int y) { - printf("THIS SHOULD NEVER HAPPEN: devicesPopupMenu() called in new style mode!\n"); - return; - } + // Include a "" entry. Do not pass parent! Causes accelerators to be returned in QAction::text() ! + QMenu* p = MusEGui::midiPortsPopup(0, t->port, true); - QMenu* p = MusECore::midiPortsPopup(this, t->port, true); // Include a "" entry. QAction* act = p->exec(mapToGlobal(QPoint(x, y)), 0); - bool doemit = false; if(!act) { delete p; - return; - } - + return false; + } + int n = act->data().toInt(); delete p; @@ -439,60 +745,24 @@ const int defaultId = MIDI_PORTS + 1; if(n < 0 || n > defaultId) // Invalid item. - return; + return false; if(n == openConfigId) // Show port config dialog. { MusEGlobal::muse->configMidiPorts(); - return; + return false; } if(n == defaultId) // Means the -1 n = -1; - - if (!changeAll) - { - if(n != t->port) - { - int mport = n; - // Default to track port if -1 and track channel if -1. - if(mport == -1) - { - if(!dcanvas || !dcanvas->part()) - return; - MusECore::Part* cur_part = dcanvas->part(); - if(!cur_part->track() || !cur_part->track()->isMidiTrack()) - return; - MusECore::MidiTrack* cur_track = static_cast(cur_part->track()); - mport = cur_track->outPort(); - } - MusEGlobal::audio->msgIdle(true); - MusEGlobal::song->remapPortDrumCtrlEvents(getSelectedInstrument(), -1, -1, mport); - MusEGlobal::audio->msgIdle(false); - t->port = n; // -1 is allowed - doemit = true; - } - } - else { - MusEGlobal::audio->msgIdle(true); - // Delete all port controller events. - MusEGlobal::song->changeAllPortDrumCtrlEvents(false); - - for (int i = 0; i < ourDrumMapSize; i++) - ourDrumMap[i].port = n; - // Add all port controller events. - MusEGlobal::song->changeAllPortDrumCtrlEvents(true); - - MusEGlobal::audio->msgIdle(false); - doemit = true; - } - if(doemit) + bool changed = false; + if(n != t->port) { - int instr = getSelectedInstrument(); - if(instr != -1) - MusEGlobal::song->update(SC_DRUMMAP); - } + t->port = n; // -1 is allowed + changed = true; + } + return changed; } //--------------------------------------------------------- @@ -501,6 +771,8 @@ void DList::viewMousePressEvent(QMouseEvent* ev) { + ev->accept(); + int x = ev->x(); int y = ev->y(); int button = ev->button(); @@ -520,271 +792,335 @@ DrumColumn col = DrumColumn(x2col(x)); - int val; - int incVal = 0; - if (button == Qt::RightButton) - incVal = 1; - else if (button == Qt::MidButton) - incVal = -1; - - // Check if we're already editing anything and have pressed the mouse - // elsewhere - // In that case, treat it as if a return was pressed - - if (button == Qt::LeftButton) { - if (editEntry && (editEntry != dm || col != selectedColumn)) { - returnPressed(); - } - } + if(button == Qt::RightButton) + { + if(dcanvas && !old_style_drummap_mode) + { + enum MapPopupIDs { HideInstrumentID = 0, ShowInstrumentID, + ResetFieldID, ResetItemID, ResetColumnID, ResetMapID, + SetFieldID, SetItemID, SetColumnID, SetMapID, + SetDefaultFieldID, SetDefaultItemID, SetDefaultColumnID, SetDefaultMapID, + ResetAllPatchMapsID, +#ifdef _USE_INSTRUMENT_OVERRIDES_ + ResetInstrumentFieldID, ResetInstrumentItemID, ResetInstrumentMapID, + SetInstrumentFieldID, SetInstrumentItemID, SetInstrumentMapID +#endif + }; + + const int field = col2Field(col); + const int overrides = dcanvas->isWorkingMapInstrument(instrument, field); + const bool has_overrides = dcanvas->hasOverrides(instrument); + + const bool track_override = overrides & MusECore::WorkingDrumMapEntry::TrackOverride; + const bool track_def_override = overrides & MusECore::WorkingDrumMapEntry::TrackDefaultOverride; +#ifdef _USE_INSTRUMENT_OVERRIDES_ + const bool instr_override = overrides & MusECore::WorkingDrumMapEntry::InstrumentOverride; + const bool instr_def_override = overrides & MusECore::WorkingDrumMapEntry::InstrumentDefaultOverride; +#endif + + const int all_fields_overrides = dcanvas->isWorkingMapInstrument(instrument, MusECore::WorkingDrumMapEntry::AllFields); + const bool all_fields_track_override = all_fields_overrides & MusECore::WorkingDrumMapEntry::TrackOverride; + const bool all_fields_track_def_override = all_fields_overrides & MusECore::WorkingDrumMapEntry::TrackDefaultOverride; +#ifdef _USE_INSTRUMENT_OVERRIDES_ + const bool all_fields_instr_override = all_fields_overrides & MusECore::WorkingDrumMapEntry::InstrumentOverride; + const bool all_fields_instr_def_override = all_fields_overrides & MusECore::WorkingDrumMapEntry::InstrumentDefaultOverride; +#endif + + bool hidden=false; + bool shown=false; + QSet* group = &dcanvas->get_instrument_map()[instrument].tracks; + int pitch = dcanvas->get_instrument_map()[instrument].pitch; + + for (QSet::iterator track=group->begin(); track!=group->end() && !(hidden&&shown); track++) + if (dynamic_cast(*track)->drummap()[pitch].hide) + hidden=true; + else + shown=true; + + QMenu* popup = new QMenu(NULL /* intendedly not "this" */); + + popup->setToolTipsVisible(true); + + QAction* act = popup->addAction(tr("Hide this instrument")); + if(!shown) + act->setEnabled(false); + act->setData(HideInstrumentID); + act->setToolTip(tr("This turns a blue eye into a crossed eye")); + + act = popup->addAction(tr("Show this instrument")); + if(!hidden) + act->setEnabled(false); + act->setData(ShowInstrumentID); + act->setToolTip(tr("This turns a crossed eye into a blue eye")); + + popup->addSeparator(); + + act = popup->addAction(tr("Set field")); + //act->setEnabled(field != MusECore::WorkingDrumMapEntry::ENoteField); + act->setData(SetFieldID); + act->setToolTip(tr("Sets a field")); + + act = popup->addAction(tr("Set row")); + //act->setEnabled(field != MusECore::WorkingDrumMapEntry::ENoteField); + act->setData(SetItemID); + act->setToolTip(tr("Sets a row")); + + act = popup->addAction(tr("Set column")); + act->setEnabled(field != MusECore::WorkingDrumMapEntry::ENoteField); + act->setData(SetColumnID); + act->setToolTip(tr("Sets a whole column to the field")); + + act = popup->addAction(tr("Set list")); + //act->setEnabled(field != MusECore::WorkingDrumMapEntry::ENoteField); + act->setData(SetMapID); + act->setToolTip(tr("Sets the whole list")); + + popup->addSeparator(); + + act = popup->addAction(tr("Reset field")); + act->setEnabled(track_override || track_def_override); + act->setData(ResetFieldID); + act->setToolTip(tr("Resets a field in a row to default patch or instrument value")); + + act = popup->addAction(tr("Reset row")); + act->setEnabled(all_fields_track_override || all_fields_track_def_override); + act->setData(ResetItemID); + act->setToolTip(tr("Resets a row to the instrument values")); + + act = popup->addAction(tr("Reset column")); + //act->setEnabled(!all_fields_track_def_override); + act->setData(ResetColumnID); + act->setToolTip(tr("Resets a whole column to the instrument values")); + + act = popup->addAction(tr("Reset list")); + act->setEnabled(has_overrides); + act->setData(ResetMapID); + act->setToolTip(tr("Resets the whole list to the instrument values")); + + popup->addSeparator(); + + act = popup->addAction(tr("Reset track's drum list")); + act->setEnabled(dcanvas->hasOverrides(instrument)); + act->setData(ResetAllPatchMapsID); + act->setToolTip(tr("Resets all lists on all patches to the instrument values")); + + popup->addSeparator(); + + act = popup->addAction(tr("Promote field to default patch")); + act->setData(SetDefaultFieldID); + act->setToolTip(tr("Promotes a field in a row to the default patch")); + + act = popup->addAction(tr("Promote row to default patch")); + act->setData(SetDefaultItemID); + act->setToolTip(tr("Promotes a row to the default patch")); + + act = popup->addAction(tr("Promote column to default patch")); + act->setData(SetDefaultColumnID); + act->setToolTip(tr("Promotes a column to the default patch")); + + act = popup->addAction(tr("Promote list to default patch")); + act->setData(SetDefaultMapID); + act->setToolTip(tr("Promotes the whole list to the default patch")); + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + popup->addSeparator(); + + act = popup->addAction(tr("Reset instrument field")); + act->setEnabled(instr_override || instr_def_override); + act->setData(ResetInstrumentFieldID); + + act = popup->addAction(tr("Reset instrument row")); + act->setEnabled(all_fields_instr_override || all_fields_instr_def_override); + act->setData(ResetInstrumentItemID); + + act = popup->addAction(tr("Reset instrument list")); + act->setEnabled(all_fields_instr_override || all_fields_instr_def_override); + act->setData(ResetInstrumentMapID); + + popup->addSeparator(); + + act = popup->addAction(tr("Set instrument field")); + act->setEnabled(track_override); + act->setData(SetInstrumentFieldID); + + act = popup->addAction(tr("Set instrument row")); + act->setEnabled(track_override); + act->setData(SetInstrumentItemID); + + act = popup->addAction(tr("Set instrument list")); + act->setEnabled(track_override); + act->setData(SetInstrumentMapID); +#endif + + int id = -1; + QAction* result = popup->exec(ev->globalPos()); + if(result) + id = result->data().toInt(); + + delete popup; + + switch(id) + { + case HideInstrumentID: + dm->hide = true; + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::HideField, false, false, false, false); + break; + + case ShowInstrumentID: + dm->hide = false; + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::HideField, false, false, false, false); + break; + + case ResetFieldID: + // If there is a track override do not include defaults. This way a track override will be removed + // first, then if reset field is clicked again any track default override will removed. + dcanvas->propagate_drummap_change(instrument, field, true, !track_override, false, false); + //update(); + break; + + case ResetItemID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, true, true, false, false); + //update(); + break; + + case ResetMapID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, true, true, false, true); + break; + + case SetDefaultFieldID: + dcanvas->propagate_drummap_change(instrument, field, false, true, false, false); + break; + + case SetDefaultItemID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, true, false, false); + break; + + case SetDefaultMapID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, true, false, true); + break; + + case SetFieldID: + dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); + break; + + case SetItemID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, false, false, false); + break; + + case SetColumnID: + dcanvas->propagate_drummap_change(instrument, field, false, false, false, true); + break; + + case SetMapID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, false, false, true); + break; + + case SetDefaultColumnID: + dcanvas->propagate_drummap_change(instrument, field, false, true, false, true); + break; + + case ResetColumnID: + dcanvas->propagate_drummap_change(instrument, field, true, true, false, true); + break; + + case ResetAllPatchMapsID: + dcanvas->resetOverridesForAllPatches(instrument); + break; + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + case ResetInstrumentFieldID: + // If there is an instrument override do not include defaults. This way an instrument override will be removed + // first, then if reset instrument field is clicked again any instrument default override will removed. + dcanvas->propagate_drummap_change(instrument, field, true, false, true, false); + //update(); + break; + + case ResetInstrumentItemID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, true, true, true, false); + //update(); + break; + + case ResetInstrumentMapID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, true, true, true, true); + break; + + case SetInstrumentFieldID: + dcanvas->propagate_drummap_change(instrument, field, false, false, true, false); + //update(); + break; + + case SetInstrumentItemID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, false, true, false); + //update(); + break; + + case SetInstrumentMapID: + dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, false, true, true); + break; +#endif + + default: + return; + } + } + return; + } + int field = MusECore::WorkingDrumMapEntry::NoField; switch (col) { case COL_NONE: + case COL_VOLUME: + case COL_QUANT: + case COL_INPUTTRIGGER: + case COL_NOTELENGTH: + case COL_NOTE: + case COL_OUTCHANNEL: // this column isn't visible in new style drum mode + case COL_LEVEL1: + case COL_LEVEL2: + case COL_LEVEL3: + case COL_LEVEL4: break; case COL_HIDE: if (button == Qt::LeftButton) { - bool hidden=true; - QSet* group = &dcanvas->get_instrument_map()[instrument].tracks; - int pitch = dcanvas->get_instrument_map()[instrument].pitch; - - for (QSet::iterator track=group->begin(); track!=group->end(); track++) - if (dynamic_cast(*track)->drummap_hidden()[pitch] == false) - { - hidden=false; - break; - } - - for (QSet::iterator track=group->begin(); track!=group->end(); track++) - dynamic_cast(*track)->drummap_hidden()[pitch] = !hidden; + field = MusECore::WorkingDrumMapEntry::HideField; + dm->hide = !dm->hide; } break; case COL_MUTE: + field = MusECore::WorkingDrumMapEntry::MuteField; if (button == Qt::LeftButton) dm->mute = !dm->mute; break; case COL_OUTPORT: // this column isn't visible in new style drum mode + field = MusECore::WorkingDrumMapEntry::PortField; if ((button == Qt::RightButton) || (button == Qt::LeftButton)) { - bool changeAll = ev->modifiers() & Qt::ControlModifier; - devicesPopupMenu(dm, mapx(x), mapy(instrument * TH), changeAll); + devicesPopupMenu(dm, mapx(x), mapy(instrument * TH)); } break; - case COL_VOLUME: - val = dm->vol + incVal; - if (val < 0) - val = 0; - else if (val > 999) //changed from 200 to 999 by flo93 - val = 999; - dm->vol = (unsigned char)val; - break; - case COL_QUANT: - dm->quant += incVal; - // ?? range - break; - case COL_INPUTTRIGGER: - val = dm->enote + incVal; - if (val < 0) - val = 0; - else if (val > 127) - val = 127; - - if (old_style_drummap_mode) - { - //Check if there is any other drumMap with the same inmap value (there should be one (and only one):-) - //If so, switch the inmap between the instruments - for (int i=0; ienote)] = i; - ourDrumMap[i].enote = dm->enote; - break; - } - } - //TODO: Set all the notes on the track with instrument=dm->enote to instrument=val - MusEGlobal::drumInmap[val] = instrument; - } - else - { - if (dcanvas) - { - //Check if there is any other drumMap with the same inmap value (there should be one (and only one):-) - //If so, switch the inmap between the instruments - for (QSet::iterator it = dcanvas->get_instrument_map()[instrument].tracks.begin(); it!=dcanvas->get_instrument_map()[instrument].tracks.end(); it++) - { - MusECore::MidiTrack* mt = dynamic_cast(*it); - mt->drummap()[mt->map_drum_in(val)].enote=dm->enote; - mt->set_drummap_tied_to_patch(false); - } - // propagating this is unneccessary as it's already done. - // updating the drumInmap is unneccessary, as the propagate call below - // does this for us. - // updating ourDrumMap is unneccessary because the song->update(SC_DRUMMAP) - // does this for us. - } - else - { - for (int i=0;i<128;i++) - if (ourDrumMap[i].enote==val) - { - ourDrumMap[i].enote=dm->enote; - break; - } - } - } - - dm->enote = val; - break; - - case COL_NOTELENGTH: - val = dm->len + incVal; - if (val < 0) - val = 0; - dm->len = val; - break; - case COL_NOTE: - if (old_style_drummap_mode) //only allow changing in old style mode - { - val = dm->anote + incVal; - if (val < 0) - val = 0; - else if (val > 127) - val = 127; - if(val != dm->anote) - { - MusEGlobal::audio->msgIdle(true); - MusEGlobal::song->remapPortDrumCtrlEvents(instrument, val, -1, -1); - MusEGlobal::audio->msgIdle(false); - dm->anote = val; - MusEGlobal::song->update(SC_DRUMMAP); - } - } - - emit keyPressed(instrument, 100); - break; - case COL_OUTCHANNEL: // this column isn't visible in new style drum mode - val = dm->channel + incVal; - // Default to track port if -1 and track channel if -1. - if (val < -1) - val = -1; - else if (val > 127) - val = 127; - - if (ev->modifiers() & Qt::ControlModifier) { - MusEGlobal::audio->msgIdle(true); - // Delete all port controller events. - MusEGlobal::song->changeAllPortDrumCtrlEvents(false, true); - - for (int i = 0; i < ourDrumMapSize; i++) - ourDrumMap[i].channel = val; - // Add all port controller events. - MusEGlobal::song->changeAllPortDrumCtrlEvents(true, true); - MusEGlobal::audio->msgIdle(false); - MusEGlobal::song->update(SC_DRUMMAP); - } - else - { - if(val != dm->channel) - { - MusEGlobal::audio->msgIdle(true); - int mchan = val; - if(mchan == -1 && dcanvas && dcanvas->part() && dcanvas->part()->track() && dcanvas->part()->track()->isMidiTrack()) - mchan = static_cast(dcanvas->part()->track())->outChannel(); - if(val != -1) - MusEGlobal::song->remapPortDrumCtrlEvents(instrument, -1, val, -1); - MusEGlobal::audio->msgIdle(false); - dm->channel = val; - MusEGlobal::song->update(SC_DRUMMAP); - } - } - break; - case COL_LEVEL1: - val = dm->lv1 + incVal; - if (val < 0) - val = 0; - else if (val > 127) - val = 127; - dm->lv1 = val; - break; - case COL_LEVEL2: - val = dm->lv2 + incVal; - if (val < 0) - val = 0; - else if (val > 127) - val = 127; - dm->lv2 = val; - break; - case COL_LEVEL3: - val = dm->lv3 + incVal; - if (val < 0) - val = 0; - else if (val > 127) - val = 127; - dm->lv3 = val; - break; - case COL_LEVEL4: - val = dm->lv4 + incVal; - if (val < 0) - val = 0; - else if (val > 127) - val = 127; - dm->lv4 = val; - break; case COL_NAME: + field = MusECore::WorkingDrumMapEntry::NameField; if (button == Qt::LeftButton) { int velo = 127 * (ev->x() - header->sectionPosition(COL_NAME)) / (header->sectionSize(COL_NAME) - 10); - if (velo < 0) velo = 0; - if (velo > 127 ) velo = 127; + if (velo <= 0) velo = 1; // Zero note on vel is not allowed. + else if (velo > 127 ) velo = 127; emit keyPressed(instrument, velo); //Mapping done on other side, send index } else if (button == Qt::MidButton && dcanvas) // hide that instrument { - QSet* group = &dcanvas->get_instrument_map()[instrument].tracks; - int pitch = dcanvas->get_instrument_map()[instrument].pitch; - for (QSet::iterator track=group->begin(); track!=group->end(); track++) - dynamic_cast(*track)->drummap_hidden()[pitch] = true; - } - else if (button == Qt::RightButton && dcanvas) - { - bool hidden=false; - bool shown=false; - QSet* group = &dcanvas->get_instrument_map()[instrument].tracks; - int pitch = dcanvas->get_instrument_map()[instrument].pitch; - - for (QSet::iterator track=group->begin(); track!=group->end() && !(hidden&&shown); track++) - if (dynamic_cast(*track)->drummap_hidden()[pitch]) - hidden=true; - else - shown=true; - - QMenu* popup = new QMenu(NULL /* intendedly not "this" */); - QAction* hideAction = popup->addAction(tr("hide this instrument")); - QAction* showAction = popup->addAction(tr("show this instrument")); - showAction->setToolTip(tr("this turns a grayed out eye into a blue eye")); - - if (!hidden) - showAction->setEnabled(false); - if (!shown) - hideAction->setEnabled(false); - - QAction* result = popup->exec(ev->globalPos()); - if (result==hideAction) - for (QSet::iterator track=group->begin(); track!=group->end(); track++) - dynamic_cast(*track)->drummap_hidden()[pitch] = true; - else if (result==showAction) - for (QSet::iterator track=group->begin(); track!=group->end(); track++) - dynamic_cast(*track)->drummap_hidden()[pitch] = false; - - delete popup; + dm->hide = true; } break; default: break; } - + + update(); if (!old_style_drummap_mode && dm_old != *dm && dcanvas) //something changed and we're in new style mode? - dcanvas->propagate_drummap_change(instrument, (dm_old.enote != dm->enote)); - - MusEGlobal::song->update(SC_DRUMMAP); - //redraw(); //this is done by the songChanged slot + dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); + + MusEGlobal::song->update(SC_DRUM_SELECTION); } //--------------------------------------------------------- @@ -799,13 +1135,17 @@ int section = header->logicalIndexAt(x); - if ((section == COL_NAME || section == COL_VOLUME || section == COL_NOTELENGTH || section == COL_LEVEL1 || - section == COL_LEVEL2 || section == COL_LEVEL3 || section == COL_LEVEL4 || section == COL_QUANT || - (section == COL_OUTCHANNEL && old_style_drummap_mode) ) && (ev->button() == Qt::LeftButton)) + if (section == COL_NAME && (ev->button() == Qt::LeftButton)) { lineEdit(instrument, section); } - else if (((section == COL_NOTE && old_style_drummap_mode) || section == COL_INPUTTRIGGER) && (ev->button() == Qt::LeftButton)) + else if ((section == COL_VOLUME || section == COL_NOTELENGTH || section == COL_LEVEL1 || + section == COL_LEVEL2 || section == COL_LEVEL3 || section == COL_LEVEL4 || section == COL_QUANT || + section == COL_OUTCHANNEL ) && (ev->button() == Qt::LeftButton)) + { + valEdit(instrument, section); + } + else if ((section == COL_NOTE || section == COL_INPUTTRIGGER) && (ev->button() == Qt::LeftButton)) pitchEdit(instrument, section); else viewMousePressEvent(ev); @@ -828,6 +1168,8 @@ editor = new DLineEdit(this); connect(editor, SIGNAL(returnPressed()), SLOT(returnPressed())); + connect(editor, SIGNAL(escapePressed()), + SLOT(escapePressed())); editor->setFrame(true); } int colx = mapx(header->sectionPosition(section)); @@ -839,52 +1181,87 @@ case COL_NAME: editor->setText(dm->name); break; + } - case COL_VOLUME: { - editor->setText(QString::number(dm->vol)); - break; + editor->end(false); + editor->setGeometry(colx, coly, colw, colh); + editor->show(); + editor->setFocus(); + } + +//--------------------------------------------------------- +// valEdit +//--------------------------------------------------------- +void DList::valEdit(int line, int section) + { + if (line >= ourDrumMapSize) line=ourDrumMapSize-1; + if (line < 0) line=0; + if (ourDrumMapSize==0) return; + + MusECore::DrumMap* dm = &ourDrumMap[line]; + editEntry = dm; + if (val_editor == 0) { + val_editor = new DrumListSpinBox(this); + connect(val_editor, SIGNAL(returnPressed()), + SLOT(valEdited())); + connect(val_editor, SIGNAL(escapePressed()), + SLOT(escapePressed())); + val_editor->setFrame(true); } - - case COL_NOTELENGTH: { - editor->setText(QString::number(dm->len)); + int colx = mapx(header->sectionPosition(section)); + int colw = rmapx(header->sectionSize(section)); + int coly = mapy(line * TH); + int colh = rmapy(TH); + selectedColumn = section; //Store selected column to have an idea of which one was selected when return is pressed + switch (section) { + case COL_VOLUME: + val_editor->setRange(0, 250); + val_editor->setValue(dm->vol); + break; + + case COL_NOTELENGTH: + val_editor->setRange(1, 1000000); + val_editor->setValue(dm->len); break; - } case COL_LEVEL1: - editor->setText(QString::number(dm->lv1)); + // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now. + val_editor->setRange(1, 127); + val_editor->setValue(dm->lv1); break; case COL_LEVEL2: - editor->setText(QString::number(dm->lv2)); + val_editor->setRange(1, 127); + val_editor->setValue(dm->lv2); break; case COL_LEVEL3: - editor->setText(QString::number(dm->lv3)); + val_editor->setRange(1, 127); + val_editor->setValue(dm->lv3); break; case COL_LEVEL4: - editor->setText(QString::number(dm->lv4)); + val_editor->setRange(1, 127); + val_editor->setValue(dm->lv4); break; case COL_QUANT: - editor->setText(QString::number(dm->quant)); + val_editor->setRange(0, 1000000); + val_editor->setValue(dm->quant); break; case COL_OUTCHANNEL: + val_editor->setRange(0, MIDI_CHANNELS); // Default to track port if -1 and track channel if -1. - if(dm->channel != -1) - editor->setText(QString::number(dm->channel+1)); + if(dm->channel != -1) + val_editor->setValue(dm->channel+1); break; } - editor->end(false); - editor->setGeometry(colx, coly, colw, colh); - // In all cases but the column name, select all text: - if (section != COL_NAME) - editor->selectAll(); - editor->show(); - editor->setFocus(); - + val_editor->setGeometry(colx, coly, colw, colh); + val_editor->selectAll(); + val_editor->show(); + val_editor->setFocus(); } //--------------------------------------------------------- @@ -900,8 +1277,10 @@ editEntry = dm; if (pitch_editor == 0) { pitch_editor = new DPitchEdit(this); - connect(pitch_editor, SIGNAL(editingFinished()), + connect(pitch_editor, SIGNAL(returnPressed()), SLOT(pitchEdited())); + connect(pitch_editor, SIGNAL(escapePressed()), + SLOT(escapePressed())); pitch_editor->setFrame(true); } int colx = mapx(header->sectionPosition(section)); @@ -944,6 +1323,83 @@ return header->logicalIndex(col); } +int DList::col2Field(int col) const +{ + switch(col) + { + case COL_NONE: + return MusECore::WorkingDrumMapEntry::NoField; + case COL_HIDE: + return MusECore::WorkingDrumMapEntry::HideField; + case COL_MUTE: + return MusECore::WorkingDrumMapEntry::MuteField; + case COL_NAME: + return MusECore::WorkingDrumMapEntry::NameField; + case COL_VOLUME: + return MusECore::WorkingDrumMapEntry::VolField; + case COL_QUANT: + return MusECore::WorkingDrumMapEntry::QuantField; + case COL_INPUTTRIGGER: + return MusECore::WorkingDrumMapEntry::ENoteField; + case COL_NOTELENGTH: + return MusECore::WorkingDrumMapEntry::LenField; + case COL_NOTE: + return MusECore::WorkingDrumMapEntry::ANoteField; + case COL_OUTCHANNEL: + return MusECore::WorkingDrumMapEntry::ChanField; + case COL_OUTPORT: + return MusECore::WorkingDrumMapEntry::PortField; + case COL_LEVEL1: + return MusECore::WorkingDrumMapEntry::Lv1Field; + case COL_LEVEL2: + return MusECore::WorkingDrumMapEntry::Lv2Field; + case COL_LEVEL3: + return MusECore::WorkingDrumMapEntry::Lv3Field; + case COL_LEVEL4: + return MusECore::WorkingDrumMapEntry::Lv4Field; + } + return MusECore::WorkingDrumMapEntry::NoField; +} + +int DList::field2Col(int field) const +{ + switch(field) + { + case MusECore::WorkingDrumMapEntry::NoField: + return COL_NONE; + case MusECore::WorkingDrumMapEntry::HideField: + return COL_HIDE; + case MusECore::WorkingDrumMapEntry::MuteField: + return COL_MUTE; + case MusECore::WorkingDrumMapEntry::NameField: + return COL_NAME; + case MusECore::WorkingDrumMapEntry::VolField: + return COL_VOLUME; + case MusECore::WorkingDrumMapEntry::QuantField: + return COL_QUANT; + case MusECore::WorkingDrumMapEntry::ENoteField: + return COL_INPUTTRIGGER; + case MusECore::WorkingDrumMapEntry::LenField: + return COL_NOTELENGTH; + case MusECore::WorkingDrumMapEntry::ANoteField: + return COL_NOTE; + case MusECore::WorkingDrumMapEntry::ChanField: + return COL_OUTCHANNEL; + case MusECore::WorkingDrumMapEntry::PortField: + return COL_OUTPORT; + case MusECore::WorkingDrumMapEntry::Lv1Field: + return COL_LEVEL1; + case MusECore::WorkingDrumMapEntry::Lv2Field: + return COL_LEVEL2; + case MusECore::WorkingDrumMapEntry::Lv3Field: + return COL_LEVEL3; + case MusECore::WorkingDrumMapEntry::Lv4Field: + return COL_LEVEL4; + } + return COL_NONE; +} + + //--------------------------------------------------------- // setCurDrumInstrument //--------------------------------------------------------- @@ -965,10 +1421,59 @@ // sizeChange //--------------------------------------------------------- -void DList::sizeChange(int, int, int) - { - redraw(); - } +void DList::sizeChange(int section, int, int) +{ + redraw(); + + if(editEntry==NULL) + return; + + const int line = (editEntry-ourDrumMap); + + int colx = mapx(header->sectionPosition(section)); + int colw = rmapx(header->sectionSize(section)); + int coly = mapy(line * TH); + int colh = rmapy(TH); + + if(editor && editor->isVisible()) + editor->setGeometry(colx, coly, colw, colh); + + if(val_editor && val_editor->isVisible()) + val_editor->setGeometry(colx, coly, colw, colh); + + if(pitch_editor && pitch_editor->isVisible()) + pitch_editor->setGeometry(colx, coly, colw, colh); +} + +//--------------------------------------------------------- +// pitchValueChanged +//--------------------------------------------------------- + +void DList::escapePressed() +{ + selectedColumn = -1; + if(editor) + { + editor->blockSignals(true); + editor->hide(); + editor->blockSignals(false); + } + if(val_editor) + { + val_editor->blockSignals(true); + val_editor->hide(); + val_editor->blockSignals(false); + } + if(pitch_editor) + { + pitch_editor->blockSignals(true); + pitch_editor->hide(); + pitch_editor->blockSignals(false); + } + editEntry = 0; + setFocus(); + update(); +} //--------------------------------------------------------- // returnPressed @@ -976,138 +1481,215 @@ void DList::returnPressed() { + if (editor==NULL) + { + printf("THIS SHOULD NEVER HAPPEN: editor is NULL in DList::returnPressed()!\n"); + return; + } + if (editEntry==NULL) { printf("THIS SHOULD NEVER HAPPEN: editEntry is NULL in DList::returnPressed()!\n"); + selectedColumn = -1; + editor->blockSignals(true); + editor->hide(); + editor->blockSignals(false); + setFocus(); + update(); return; } - - int val = -1; - if (selectedColumn != COL_NAME) - { - val = atoi(editor->text().toAscii().constData()); - - switch (selectedColumn) - { - case COL_VOLUME: - if (val > 999) //changed from 200 to 999 by flo93 - val = 999; - if (val < 0) - val = 0; - break; - - case COL_LEVEL1: - case COL_LEVEL2: - case COL_LEVEL3: - case COL_LEVEL4: - if (val > 127) //Check bounds for lv1-lv4 values - val = 127; - if (val < 0) - val = 0; - break; - - case COL_OUTCHANNEL: - // Default to track port if -1 and track channel if -1. - if(val <= 0) - val = -1; - else - val--; - if (val >= MIDI_CHANNELS) - val = MIDI_CHANNELS - 1; - break; - - default: break; - } - } - + + const int instrument = (editEntry-ourDrumMap); + + int field = MusECore::WorkingDrumMapEntry::NoField; MusECore::DrumMap editEntryOld = *editEntry; switch(selectedColumn) { case COL_NAME: editEntry->name = editor->text(); + field = MusECore::WorkingDrumMapEntry::NameField; break; + default: + printf("Return pressed in unknown column\n"); + break; + } + + const bool do_prop = (editEntryOld != *editEntry && dcanvas); + + // Clear these before the operations. + selectedColumn = -1; + editor->blockSignals(true); + editor->hide(); + editor->blockSignals(false); + editEntry = 0; + setFocus(); + update(); + + if(do_prop) + dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); + } + +//--------------------------------------------------------- +// valEdited +//--------------------------------------------------------- + +void DList::valEdited() + { + if (val_editor==NULL) + { + printf("THIS SHOULD NEVER HAPPEN: val_editor is NULL in DList::returnPressed()!\n"); + return; + } + + if (editEntry==NULL) + { + printf("THIS SHOULD NEVER HAPPEN: editEntry is NULL in DList::returnPressed()!\n"); + selectedColumn = -1; + val_editor->blockSignals(true); + val_editor->hide(); + val_editor->blockSignals(false); + setFocus(); + update(); + return; + } + + const int instrument = (editEntry-ourDrumMap); + int val = val_editor->value(); + + switch (selectedColumn) + { + case COL_VOLUME: + if (val > 250) + val = 250; + if (val < 0) + val = 0; + break; + + case COL_LEVEL1: + case COL_LEVEL2: + case COL_LEVEL3: + case COL_LEVEL4: + if (val > 127) //Check bounds for lv1-lv4 values + val = 127; + // Zero note on vel is not allowed now. + else if (val <= 0) + val = 1; + break; + + case COL_OUTCHANNEL: + // Default to track port if -1 and track channel if -1. + if(val <= 0) + val = -1; + else + val--; + if (val >= MIDI_CHANNELS) + val = MIDI_CHANNELS - 1; + break; + + default: break; + } + + int field = MusECore::WorkingDrumMapEntry::NoField; + MusECore::DrumMap editEntryOld = *editEntry; + switch(selectedColumn) { case COL_NOTELENGTH: - editEntry->len = atoi(editor->text().toAscii().constData()); + editEntry->len = val; + field = MusECore::WorkingDrumMapEntry::LenField; break; case COL_VOLUME: editEntry->vol = val; + field = MusECore::WorkingDrumMapEntry::VolField; break; case COL_LEVEL1: editEntry->lv1 = val; + field = MusECore::WorkingDrumMapEntry::Lv1Field; break; case COL_LEVEL2: editEntry->lv2 = val; + field = MusECore::WorkingDrumMapEntry::Lv2Field; break; case COL_LEVEL3: editEntry->lv3 = val; + field = MusECore::WorkingDrumMapEntry::Lv3Field; break; case COL_LEVEL4: editEntry->lv4 = val; + field = MusECore::WorkingDrumMapEntry::Lv4Field; break; case COL_QUANT: editEntry->quant = val; + field = MusECore::WorkingDrumMapEntry::QuantField; break; case COL_OUTCHANNEL: - editEntry->channel = val; + editEntry->channel = val; + field = MusECore::WorkingDrumMapEntry::ChanField; break; default: - printf("Return pressed in unknown column\n"); + printf("Value edited in unknown column\n"); break; } - - if (editEntryOld != *editEntry && dcanvas) - dcanvas->propagate_drummap_change(editEntry-ourDrumMap, false); - + + const bool do_prop = (editEntryOld != *editEntry && dcanvas); + + // Clear these before the operations. selectedColumn = -1; - editor->hide(); + val_editor->blockSignals(true); + val_editor->hide(); + val_editor->blockSignals(false); editEntry = 0; setFocus(); - MusEGlobal::song->update(SC_DRUMMAP); - //redraw(); //this is done by the songChanged slot + update(); + + if(do_prop) + dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); } //--------------------------------------------------------- -// pitchValueChanged +// pitchEdited //--------------------------------------------------------- void DList::pitchEdited() { + if (pitch_editor==NULL) + { + printf("THIS SHOULD NEVER HAPPEN: pitch_editor is NULL in DList::pitchEdited()!\n"); + return; + } + if (editEntry==NULL) { printf("THIS SHOULD NEVER HAPPEN: editEntry is NULL in DList::pitchEdited()!\n"); + selectedColumn = -1; + pitch_editor->blockSignals(true); + pitch_editor->hide(); + pitch_editor->blockSignals(false); + setFocus(); + update(); return; } - int val=pitch_editor->value(); - int instrument=(editEntry-ourDrumMap); - + const int val=pitch_editor->value(); + const int instrument=(editEntry-ourDrumMap); + + int field = MusECore::WorkingDrumMapEntry::NoField; MusECore::DrumMap editEntryOld=*editEntry; switch(selectedColumn) { case COL_NOTE: - if (old_style_drummap_mode) //should actually be always true, but to be sure... - { - if(val != editEntry->anote) - { - MusEGlobal::audio->msgIdle(true); - MusEGlobal::song->remapPortDrumCtrlEvents(instrument, val, -1, -1); - MusEGlobal::audio->msgIdle(false); - editEntry->anote = val; - MusEGlobal::song->update(SC_DRUMMAP); - } - } - else - printf("ERROR: THIS SHOULD NEVER HAPPEN: pitch edited of anote in new style mode!\n"); + field = MusECore::WorkingDrumMapEntry::ANoteField; + if(val != editEntry->anote) + editEntry->anote = val; break; case COL_INPUTTRIGGER: + field = MusECore::WorkingDrumMapEntry::ENoteField; if (old_style_drummap_mode) { //Check if there is any other MusEGlobal::drumMap with the same inmap value (there should be one (and only one):-) @@ -1126,19 +1708,23 @@ { if (dcanvas) { - //Check if there is any other drumMap with the same inmap value (there should be one (and only one):-) - //If so, switch the inmap between the instruments - for (QSet::iterator it = dcanvas->get_instrument_map()[instrument].tracks.begin(); it!=dcanvas->get_instrument_map()[instrument].tracks.end(); it++) + // Clear these before the operations. + selectedColumn = -1; + pitch_editor->blockSignals(true); + pitch_editor->hide(); + pitch_editor->blockSignals(false); + setFocus(); + update(); + + if(editEntry->enote != val) { - MusECore::MidiTrack* mt = dynamic_cast(*it); - mt->drummap()[mt->map_drum_in(val)].enote=editEntry->enote; - mt->set_drummap_tied_to_patch(false); + editEntry->enote = val; + editEntry = 0; + dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); } - // propagating this is unneccessary as it's already done. - // updating the drumInmap is unneccessary, as the propagate call below - // does this for us. - // updating ourDrumMap is unneccessary because the song->update(SC_DRUMMAP) - // does this for us. + else + editEntry = 0; + return; } else { @@ -1149,7 +1735,7 @@ break; } } - } + } editEntry->enote = val; break; @@ -1157,26 +1743,51 @@ printf("ERROR: THIS SHOULD NEVER HAPPEN: Value changed in unknown column\n"); break; } - - if (editEntryOld != *editEntry && dcanvas) - dcanvas->propagate_drummap_change(editEntry-ourDrumMap, (editEntryOld.enote!=editEntry->enote)); - + + + const bool do_prop = (editEntryOld != *editEntry && dcanvas); + + // Clear these before the operations. selectedColumn = -1; + pitch_editor->blockSignals(true); pitch_editor->hide(); + pitch_editor->blockSignals(false); editEntry = 0; setFocus(); - MusEGlobal::song->update(SC_DRUMMAP); - //redraw(); //this is done by the songChanged slot + update(); + + if(do_prop) + dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); } + //--------------------------------------------------------- // moved //--------------------------------------------------------- -void DList::moved(int, int, int) - { - redraw(); - } +void DList::moved(int section, int, int) +{ + redraw(); + + if(editEntry==NULL) + return; + + const int line = (editEntry-ourDrumMap); + + int colx = mapx(header->sectionPosition(section)); + int colw = rmapx(header->sectionSize(section)); + int coly = mapy(line * TH); + int colh = rmapy(TH); + + if(editor && editor->isVisible()) + editor->setGeometry(colx, coly, colw, colh); + + if(val_editor && val_editor->isVisible()) + val_editor->setGeometry(colx, coly, colw, colh); + + if(pitch_editor && pitch_editor->isVisible()) + pitch_editor->setGeometry(colx, coly, colw, colh); +} //--------------------------------------------------------- // tracklistChanged @@ -1208,13 +1819,13 @@ h = new QHeaderView(Qt::Horizontal, parent); header = h; - //ORCAN- CHECK if really needed: header->setTracking(true); DELETETHIS seems like it's unneeded ;) connect(header, SIGNAL(sectionResized(int,int,int)), SLOT(sizeChange(int,int,int))); connect(header, SIGNAL(sectionMoved(int, int,int)), SLOT(moved(int,int,int))); setFocusPolicy(Qt::StrongFocus); drag = NORMAL; editor = 0; + val_editor = 0; pitch_editor = 0; editEntry = 0; @@ -1321,9 +1932,6 @@ emit mapChanged(sInstrument, (unsigned)dInstrument); //Track instrument change done in canvas } drag = NORMAL; -//?? redraw(); //commented out NOT by flo93; was already commented out. DELETETHIS? not the below, only this single line! -// if (editEntry) //removed by flo93; seems to work without it -// editor->setFocus(); //and causes segfaults after adding the pitchedits int x = ev->x(); int y = ev->y(); bool shift = ev->modifiers() & Qt::ShiftModifier; @@ -1348,9 +1956,253 @@ //--------------------------------------------------------- void DList::wheelEvent(QWheelEvent* ev) - { - emit redirectWheelEvent(ev); - } +{ + ev->accept(); + + Qt::MouseButtons buttons = ev->buttons(); + int keyState = ev->modifiers(); + //bool shift = keyState & Qt::ShiftModifier; + bool ctrl = keyState & Qt::ControlModifier; + int x = ev->x(); + int y = ev->y(); + + DrumColumn col = DrumColumn(x2col(x)); + + if(buttons != Qt::LeftButton || ourDrumMapSize == 0 || col == COL_NONE) + { + //ev->ignore(); + //View::wheelEvent(ev); + //ev->accept(); + emit redirectWheelEvent(ev); + return; + } + + int instrument = y / TH; + if (instrument >= ourDrumMapSize) + instrument = ourDrumMapSize-1; + if (instrument < 0) + instrument = 0; + +// setCurDrumInstrument(instrument); + + MusECore::DrumMap* dm = &ourDrumMap[instrument]; + MusECore::DrumMap dm_old = *dm; + + //startY = y; + //sInstrument = instrument; + //drag = START_DRAG; + + int val; +// int incVal = 0; +// if (button == Qt::RightButton) +// incVal = 1; +// else if (button == Qt::MidButton) +// incVal = -1; + + + const QPoint pixelDelta = ev->pixelDelta(); + const QPoint angleDegrees = ev->angleDelta() / 8; + int delta = 0; + if(!pixelDelta.isNull()) + delta = pixelDelta.y(); + else if(!angleDegrees.isNull()) + delta = angleDegrees.y() / 15; + + + + int field = MusECore::WorkingDrumMapEntry::NoField; + switch (col) + { + case COL_NONE: + case COL_HIDE: + case COL_MUTE: + case COL_NAME: + break; + + case COL_OUTPORT: // this column isn't visible in new style drum mode +// TODO +// field = MusECore::WorkingDrumMapEntry::PortField; +// if ((button == Qt::RightButton) || (button == Qt::LeftButton)) { +// bool changeAll = ev->modifiers() & Qt::ControlModifier; +// devicesPopupMenu(dm, mapx(x), mapy(instrument * TH), changeAll); +// } + break; + case COL_VOLUME: + field = MusECore::WorkingDrumMapEntry::VolField; + val = dm->vol + delta; + if (val < 0) + val = 0; + else if (val > 250) + val = 250; + dm->vol = (unsigned char)val; + break; + case COL_QUANT: + field = MusECore::WorkingDrumMapEntry::QuantField; + dm->quant += delta; + // ?? range + break; + case COL_INPUTTRIGGER: + field = MusECore::WorkingDrumMapEntry::ENoteField; + val = dm->enote + delta; + if (val < 0) + val = 0; + else if (val > 127) + val = 127; + + if (old_style_drummap_mode) + { + //Check if there is any other drumMap with the same inmap value (there should be one (and only one):-) + //If so, switch the inmap between the instruments + for (int i=0; ienote)] = i; + ourDrumMap[i].enote = dm->enote; + break; + } + } + //TODO: Set all the notes on the track with instrument=dm->enote to instrument=val + MusEGlobal::drumInmap[val] = instrument; + } + else + { + if (dcanvas) + { + if(dm->enote != val) + { + dm->enote = val; + update(); + dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); + } + return; + + } + else + { + for (int i=0;i<128;i++) + if (ourDrumMap[i].enote==val) + { + ourDrumMap[i].enote=dm->enote; + break; + } + } + } + + dm->enote = val; + break; + + case COL_NOTELENGTH: + field = MusECore::WorkingDrumMapEntry::LenField; + val = dm->len + delta; + if (val < 0) + val = 0; + dm->len = val; + break; + case COL_NOTE: + field = MusECore::WorkingDrumMapEntry::ANoteField; + if (old_style_drummap_mode) //only allow changing in old style mode + { + val = dm->anote + delta; + if (val < 0) + val = 0; + else if (val > 127) + val = 127; + if(val != dm->anote) + { + MusEGlobal::audio->msgIdle(true); + MusEGlobal::song->remapPortDrumCtrlEvents(instrument, val, -1, -1); + MusEGlobal::audio->msgIdle(false); + dm->anote = val; + MusEGlobal::song->update(SC_DRUMMAP); + } + } + + emit keyPressed(instrument, 100); + break; + case COL_OUTCHANNEL: // this column isn't visible in new style drum mode + field = MusECore::WorkingDrumMapEntry::ChanField; + val = dm->channel + delta; + // Default to track port if -1 and track channel if -1. + if (val < -1) + val = -1; + else if (val > 127) + val = 127; + + if (ctrl) { + MusEGlobal::audio->msgIdle(true); + // Delete all port controller events. + MusEGlobal::song->changeAllPortDrumCtrlEvents(false, true); + + for (int i = 0; i < ourDrumMapSize; i++) + ourDrumMap[i].channel = val; + // Add all port controller events. + MusEGlobal::song->changeAllPortDrumCtrlEvents(true, true); + MusEGlobal::audio->msgIdle(false); + MusEGlobal::song->update(SC_DRUMMAP); + } + else + { + if(val != dm->channel) + { + MusEGlobal::audio->msgIdle(true); + int mchan = val; + if(mchan == -1 && dcanvas && dcanvas->part() && dcanvas->part()->track() && dcanvas->part()->track()->isMidiTrack()) + mchan = static_cast(dcanvas->part()->track())->outChannel(); + if(val != -1) + MusEGlobal::song->remapPortDrumCtrlEvents(instrument, -1, val, -1); + MusEGlobal::audio->msgIdle(false); + dm->channel = val; + MusEGlobal::song->update(SC_DRUMMAP); + } + } + break; + case COL_LEVEL1: + field = MusECore::WorkingDrumMapEntry::Lv1Field; + val = dm->lv1 + delta; + // Zero note on vel is not allowed now. + if (val <= 0) + val = 1; + else if (val > 127) + val = 127; + dm->lv1 = val; + break; + case COL_LEVEL2: + field = MusECore::WorkingDrumMapEntry::Lv2Field; + val = dm->lv2 + delta; + // Zero note on vel is not allowed now. + if (val <= 0) + val = 1; + else if (val > 127) + val = 127; + dm->lv2 = val; + break; + case COL_LEVEL3: + field = MusECore::WorkingDrumMapEntry::Lv3Field; + val = dm->lv3 + delta; + // Zero note on vel is not allowed now. + if (val <= 0) + val = 1; + else if (val > 127) + val = 127; + dm->lv3 = val; + break; + case COL_LEVEL4: + field = MusECore::WorkingDrumMapEntry::Lv4Field; + val = dm->lv4 + delta; + // Zero note on vel is not allowed now. + if (val <= 0) + val = 1; + else if (val > 127) + val = 127; + dm->lv4 = val; + break; + default: + break; + } + + update(); + if (!old_style_drummap_mode && dm_old != *dm && dcanvas) //something changed and we're in new style mode? + dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); +} //--------------------------------------------------------- // getSelectedInstrument diff -Nru muse-2.1.2/muse/midiedit/dlist.h muse-3.0.2+ds1/muse/midiedit/dlist.h --- muse-2.1.2/muse/midiedit/dlist.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/dlist.h 2018-01-26 21:59:38.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: dlist.h,v 1.5.2.3 2009/10/16 21:50:16 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -25,6 +26,7 @@ #include #include +#include #include "type_defs.h" #include "awl/pitchedit.h" @@ -32,15 +34,19 @@ #define TH 18 // normal Track-hight +// REMOVE Tim. newdrums. Added. +// Adds the ability to override at instrument level. +// But it just makes things too complex for the user. +// And in a way is unnecessary and overkill, since we +// already allow modifying an instrument. +//#define _USE_INSTRUMENT_OVERRIDES_ + class QHeaderView; -class QLineEdit; class QMouseEvent; class QPainter; -class Device; -class QLineEdit; namespace MusECore { -class DrumMap; +struct DrumMap; } namespace MusEGui { @@ -48,7 +54,6 @@ class ScrollScale; class DrumCanvas; - enum DrumColumn { COL_HIDE = 0, COL_MUTE, @@ -71,40 +76,57 @@ //--------------------------------------------------------- // DLineEdit //--------------------------------------------------------- + class DLineEdit: public QLineEdit { - public: - DLineEdit(QWidget* parent) : QLineEdit(parent) {} - virtual ~DLineEdit() {}; - - virtual void keyPressEvent(QKeyEvent* keyItem) { - if(keyItem->key() == Qt::Key_Escape) { - parentWidget()->setFocus(); - hide(); - } - else - QLineEdit::keyPressEvent(keyItem); + Q_OBJECT + + protected: + virtual bool event(QEvent*); + + signals: + void returnPressed(); + void escapePressed(); - } + public: + DLineEdit(QWidget* parent); +}; + +//--------------------------------------------------------- +// DrumListSpinBox +//--------------------------------------------------------- + +class DrumListSpinBox : public QSpinBox { + Q_OBJECT + + protected: + virtual bool event(QEvent*); + + signals: + void returnPressed(); + void escapePressed(); + + public: + DrumListSpinBox(QWidget* parent=0); }; //--------------------------------------------------------- // DPitchEdit //--------------------------------------------------------- + class DPitchEdit: public Awl::PitchEdit { - public: - DPitchEdit(QWidget* parent) : PitchEdit(parent) {} - virtual ~DPitchEdit() {}; - - virtual void keyPressEvent(QKeyEvent* keyItem) { - if ((keyItem->key() == Qt::Key_Escape) || (keyItem->key() == Qt::Key_Return)) { - parentWidget()->setFocus(); - hide(); - } - else - PitchEdit::keyPressEvent(keyItem); - } + Q_OBJECT + + protected: + virtual bool event(QEvent*); + + //signals: + //void returnPressed(); + //void escapePressed(); + + public: + DPitchEdit(QWidget* parent); }; //--------------------------------------------------------- @@ -121,6 +143,7 @@ QHeaderView* header; QLineEdit* editor; + DrumListSpinBox* val_editor; DPitchEdit* pitch_editor; MusECore::DrumMap* editEntry; MusECore::DrumMap* currentlySelected; @@ -140,13 +163,19 @@ virtual void wheelEvent(QWheelEvent* e); int x2col(int x) const; - void devicesPopupMenu(MusECore::DrumMap* t, int x, int y, bool changeAll); - + // Returns -1 if invalid. + int col2Field(int col) const; + // Returns -1 if invalid. + int field2Col(int field) const; + bool devicesPopupMenu(MusECore::DrumMap* t, int x, int y); + void init(QHeaderView*, QWidget*); private slots: void sizeChange(int, int, int); + void escapePressed(); void returnPressed(); + void valEdited(); void pitchEdited(); void moved(int, int, int); @@ -165,11 +194,12 @@ public: void lineEdit(int line, int section); + void valEdit(int line, int section); void pitchEdit(int line, int section); void setCurDrumInstrument(int n); DList(QHeaderView*, QWidget* parent, int ymag, DrumCanvas* dcanvas, bool oldstyle); DList(QHeaderView* h, QWidget* parent, int ymag, MusECore::DrumMap* dm, int dmSize=128); - ~DList(); + virtual ~DList(); int getSelectedInstrument(); }; diff -Nru muse-2.1.2/muse/midiedit/drumedit.cpp muse-3.0.2+ds1/muse/midiedit/drumedit.cpp --- muse-2.1.2/muse/midiedit/drumedit.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/drumedit.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -42,7 +42,9 @@ #include #include #include +#include +#include "globaldefs.h" #include "drumedit.h" #include "dcanvas.h" #include "mtscale.h" @@ -68,8 +70,8 @@ #include "helper.h" #include "popupmenu.h" #include "menutitleitem.h" +#include "operations.h" #include "widgets/function_dialogs/quantize.h" -#include "editinstrument.h" namespace MusEGui { @@ -161,7 +163,8 @@ tickValue = 0; lenValue = 0; pitchValue = 0; - veloOnValue = 0; + // Zero note on vel is not allowed now. + veloOnValue = 1; veloOffValue = 0; firstValueSet = false; tickOffset = 0; @@ -386,28 +389,18 @@ // Toolbars //--------------------------------------------------- - if (old_style_drummap_mode()) - { - QToolBar* maptools = addToolBar(tr("Drum map tools")); - maptools->setObjectName("Drum map tools"); - - QToolButton *ldm = new QToolButton(); - ldm->setToolTip(tr("Load Drummap")); - ldm->setIcon(*openIcon); - ldm->setFocusPolicy(Qt::NoFocus); - connect(ldm, SIGNAL(clicked()), SLOT(load())); - maptools->addWidget(ldm); - - QToolButton *sdm = new QToolButton(); - sdm->setToolTip(tr("Store Drummap")); - sdm->setIcon(*saveIcon); - sdm->setFocusPolicy(Qt::NoFocus); - connect(sdm, SIGNAL(clicked()), SLOT(save())); - maptools->addWidget(sdm); - - maptools->addAction(QWhatsThis::createAction()); - } + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + addToolBarBreak(); + + // Already has an object name. + tools2 = new MusEGui::EditToolBar(this, drumeditTools); + addToolBar(tools2); tools = addToolBar(tr("Drum tools")); tools->setObjectName("Drum tools"); @@ -436,11 +429,39 @@ tools->addAction(QWhatsThis::createAction(this)); - tools2 = new MusEGui::EditToolBar(this, drumeditTools); - addToolBar(tools2); + if (old_style_drummap_mode()) + { + QToolBar* maptools = addToolBar(tr("Drum map tools")); + maptools->setObjectName("Drum map tools"); + + QToolButton *ldm = new QToolButton(); + ldm->setToolTip(tr("Load Drummap")); + ldm->setIcon(*openIcon); + ldm->setFocusPolicy(Qt::NoFocus); + connect(ldm, SIGNAL(clicked()), SLOT(load())); + maptools->addWidget(ldm); + + QToolButton *sdm = new QToolButton(); + sdm->setToolTip(tr("Store Drummap")); + sdm->setIcon(*saveIcon); + sdm->setFocusPolicy(Qt::NoFocus); + connect(sdm, SIGNAL(clicked()), SLOT(save())); + maptools->addWidget(sdm); + } + + // don't show pitch value in toolbar + toolbar = new MusEGui::Toolbar1(this, _rasterInit, false); + toolbar->setObjectName("Drum Pos/Snap/Solo-tools"); + addToolBar(toolbar); + + addToolBarBreak(); + + info = new MusEGui::NoteInfo(this); + info->setObjectName("Drum Note Info"); + addToolBar(info); QToolBar* cursorToolbar = addToolBar(tr("cursor tools")); - cursorToolbar->setObjectName("cursor"); + cursorToolbar->setObjectName("Cursor step tools"); QLabel *stepStr = new QLabel(tr("Cursor step:")); cursorToolbar->addWidget(stepStr); stepLenWidget = new QComboBox(); @@ -459,15 +480,6 @@ connect(stepLenWidget, SIGNAL(currentIndexChanged(QString)), SLOT(setStep(QString))); cursorToolbar->addWidget(stepLenWidget); - addToolBarBreak(); - // don't show pitch value in toolbar - toolbar = new MusEGui::Toolbar1(this, _rasterInit, false); - addToolBar(toolbar); - - addToolBarBreak(); - info = new MusEGui::NoteInfo(this); - addToolBar(info); - //--------------------------------------------------- // split //--------------------------------------------------- @@ -496,6 +508,19 @@ split2 = new MusEGui::Splitter(Qt::Horizontal, split1, "split2"); split1w1 = new QWidget(split2); QWidget* split1w2 = new QWidget(split2); + + split2->setStretchFactor(split2->indexOf(split1w1), 0); + QSizePolicy tipolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + tipolicy.setHorizontalStretch(0); + tipolicy.setVerticalStretch(100); + split1w1->setSizePolicy(tipolicy); + + split2->setStretchFactor(split2->indexOf(split1w2), 1); + QSizePolicy epolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + epolicy.setHorizontalStretch(255); + epolicy.setVerticalStretch(100); + split1w2->setSizePolicy(epolicy); + QGridLayout* gridS1 = new QGridLayout(split1w1); QGridLayout* gridS2 = new QGridLayout(split1w2); gridS1->setContentsMargins(0, 0, 0, 0); @@ -539,9 +564,9 @@ header->setColumnLabel(tr("M"), COL_MUTE, 20); header->setColumnLabel(tr("Sound"), COL_NAME, 120); header->setColumnLabel(tr("Vol"), COL_VOLUME); - header->setColumnLabel(tr("QNT"), COL_QUANT, 30); + header->setColumnLabel(tr("QNT"), COL_QUANT, 40); header->setColumnLabel(tr("E-Note"), COL_INPUTTRIGGER, 50); - header->setColumnLabel(tr("Len"), COL_NOTELENGTH); + header->setColumnLabel(tr("Len"), COL_NOTELENGTH, 40); header->setColumnLabel(tr("A-Note"), COL_NOTE, 50); header->setColumnLabel(tr("Ch"), COL_OUTCHANNEL); header->setColumnLabel(tr("Port"), COL_OUTPORT, 70); @@ -553,12 +578,6 @@ setHeaderToolTips(); setHeaderWhatsThis(); - if (!old_style_drummap_mode()) - { - header->hideSection(COL_OUTPORT); - header->hideSection(COL_OUTCHANNEL); - } - if (!old_style_drummap_mode() && _ignore_hide) header->showSection(COL_HIDE); else @@ -622,6 +641,8 @@ connect(ctrl, SIGNAL(clicked()), SLOT(addCtrlClicked())); + connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), SLOT(midiNote(int,int))); + QClipboard* cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged())); @@ -645,7 +666,6 @@ if(canvas->track()) toolbar->setSolo(canvas->track()->solo()); - initTopwinState(); finalizeInit(); } @@ -661,8 +681,8 @@ if (bits & SC_SOLO) { - toolbar->setSolo(canvas->track()->solo()); - return; + if(canvas->track()) + toolbar->setSolo(canvas->track()->solo()); } if ( !old_style_drummap_mode() && ( bits & (SC_DRUMMAP | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | @@ -673,6 +693,38 @@ } //--------------------------------------------------------- +// midiNote +//--------------------------------------------------------- +void DrumEdit::midiNote(int pitch, int velo) +{ + if (MusEGlobal::debugMsg) + printf("DrumEdit::midiNote: pitch=%i, velo=%i\n", pitch, velo); + int index=0; + + // *note = old_style_drummap_mode ? ourDrumMap[index].anote : instrument_map[index].pitch; + + if ((DrumCanvas*)(canvas)->midiin()) + { + if (old_style_drummap_mode()) { + MusECore::DrumMap *dmap= ((DrumCanvas*)canvas)->getOurDrumMap(); + for (index = 0; index < ((DrumCanvas*)canvas)->getOurDrumMapSize(); ++index) { + + if ((&dmap[index])->anote == pitch) + break; + } + + } + else + { + for (index = 0; index < get_instrument_map().size(); ++index) { + if (get_instrument_map().at(index).pitch == pitch) + break; + } + } + dlist->setCurDrumInstrument(index); + } +} +//--------------------------------------------------------- // horizontalZoom //--------------------------------------------------------- @@ -809,6 +861,12 @@ lenValue = e.lenTick(); pitchValue = e.pitch(); veloOnValue = e.velo(); + // Zero note on vel is not allowed now. + if(veloOnValue == 0) + { + veloOnValue = 1; + fprintf(stderr, "DrumEdit::setSelection: Warning: Zero note on velocity!\n"); + } veloOffValue = e.veloOff(); firstValueSet = true; } @@ -822,12 +880,14 @@ } else { info->setEnabled(false); - info->setValues(0, 0, 0, 0, 0); + // Zero note on vel is not allowed now. + info->setValues(0, 0, 0, deltaMode ? 0 : 1, 0); firstValueSet = false; tickValue = 0; lenValue = 0; pitchValue = 0; - veloOnValue = 0; + // Zero note on vel is not allowed now. + veloOnValue = 1; veloOffValue = 0; tickOffset = 0; lenOffset = 0; @@ -883,8 +943,13 @@ void DrumEdit::soloChanged(bool flag) { - MusEGlobal::audio->msgSetSolo(canvas->track(), flag); - MusEGlobal::song->update(SC_SOLO); + if(canvas->track()) + { + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(canvas->track(), flag, MusECore::PendingOperationItem::SetTrackSolo)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } } //--------------------------------------------------------- @@ -986,10 +1051,10 @@ xml.intTag(level, "midiin", canvas->midiin()); xml.intTag(level, "tool", int(canvas->tool())); xml.intTag(level, "playEvents", _playEvents); - xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "xmag", hscroll->mag()); - xml.intTag(level, "ypos", vscroll->pos()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", vscroll->mag()); + xml.intTag(level, "ypos", vscroll->pos()); xml.intTag(level, "ignore_hide", _ignore_hide); xml.tag(level, "/drumedit"); } @@ -1219,6 +1284,11 @@ void DrumEdit::cmd(int cmd) { + // Don't process if user is dragging or has clicked on the events. + // Causes crashes later in Canvas::viewMouseMoveEvent and viewMouseReleaseEvent. + if(canvas->getCurrentDrag()) + return; + switch(cmd) { case DrumCanvas::CMD_CUT: copy_notes(partlist_to_set(parts()), 1); @@ -1316,7 +1386,7 @@ } else if (rv == edit_ins) { // edit instrument MusECore::MidiInstrument* instr = port->instrument(); - MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrument::Controllers); + MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrumentControllers); } else { // Select a control if(cll->find(channel, rv) == cll->end()) @@ -1646,6 +1716,34 @@ else return; } + else if (key == shortcuts[SHRT_MOVE_PLAY_TO_NOTE].key){ + movePlayPointerToSelectedEvent(); + return; + } + else if (key == shortcuts[SHRT_STEP_RECORD].key) { + canvas->setSteprec(!srec->isChecked()); + srec->setChecked(!srec->isChecked()); + return; + } + else if (key == shortcuts[SHRT_MIDI_INPUT].key) { + canvas->setMidiin(!midiin->isChecked()); + midiin->setChecked(!midiin->isChecked()); + return; + + } + else if (key == shortcuts[SHRT_PLAY_EVENTS].key) { + canvas->playEvents(!speaker->isChecked()); + speaker->setChecked(!speaker->isChecked()); + return; + } + else if (key == shortcuts[SHRT_INC_VELOCITY].key) { + modify_velocity(partlist_to_set(parts()), 1, 100, 1); + return; + } + else if (key == shortcuts[SHRT_DEC_VELOCITY].key) { + modify_velocity(partlist_to_set(parts()), 1, 100, -1); + return; + } else { //Default: event->ignore(); return; @@ -1701,7 +1799,7 @@ void DrumEdit::execDeliveredScript(int id) { QString scriptfile = MusEGlobal::song->getScriptPath(id, true); - MusEGlobal::song->executeScript(scriptfile.toLatin1().constData(), parts(), raster(), true); + MusEGlobal::song->executeScript(this, scriptfile.toLatin1().constData(), parts(), raster(), true); } //--------------------------------------------------------- @@ -1710,7 +1808,7 @@ void DrumEdit::execUserScript(int id) { QString scriptfile = MusEGlobal::song->getScriptPath(id, false); - MusEGlobal::song->executeScript(scriptfile.toLatin1().constData(), parts(), raster(), true); + MusEGlobal::song->executeScript(this, scriptfile.toLatin1().constData(), parts(), raster(), true); } void DrumEdit::setStep(QString v) @@ -1770,7 +1868,7 @@ MidiTrack* track=*it; for (int i=0;i<128;i++) - track->drummap_hidden()[i]=false; + track->drummap()[i].hide=false; } MusEGlobal::song->update(SC_DRUMMAP); @@ -1789,7 +1887,7 @@ MidiTrack* track=*it; for (int i=0;i<128;i++) - track->drummap_hidden()[i]=true; + track->drummap()[i].hide=true; } MusEGlobal::song->update(SC_DRUMMAP); @@ -1816,13 +1914,13 @@ for (MusECore::ciPart p = parts()->begin(); p != parts()->end(); ++p) if (p->second->track() == track) { - const EventList* el = p->second->cevents(); - for (ciEvent ev=el->begin(); ev!=el->end(); ev++) + const EventList& el = p->second->events(); + for (ciEvent ev=el.begin(); ev!=el.end(); ev++) hide[ev->second.pitch()]=false; } for (int i=0;i<128;i++) - track->drummap_hidden()[i]=hide[i]; + track->drummap()[i].hide=hide[i]; } MusEGlobal::song->update(SC_DRUMMAP); @@ -1849,13 +1947,13 @@ for (MusECore::ciPart p = parts()->begin(); p != parts()->end(); ++p) if (p->second->track() == track) { - const EventList* el = p->second->cevents(); - for (ciEvent ev=el->begin(); ev!=el->end(); ev++) + const EventList& el = p->second->events(); + for (ciEvent ev=el.begin(); ev!=el.end(); ev++) hide[ev->second.pitch()]=false; } for (int i=0;i<128;i++) - track->drummap_hidden()[i]=hide[i]; + track->drummap()[i].hide=hide[i]; } MusEGlobal::song->update(SC_DRUMMAP); diff -Nru muse-2.1.2/muse/midiedit/drumedit.h muse-3.0.2+ds1/muse/midiedit/drumedit.h --- muse-2.1.2/muse/midiedit/drumedit.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/drumedit.h 2017-12-04 21:01:18.000000000 +0000 @@ -169,6 +169,7 @@ void display_old_new_conflict_message(); void deltaModeChanged(bool); + void midiNote(int pitch, int velo); public slots: void setSelection(int tick, MusECore::Event&, MusECore::Part*, bool update); diff -Nru muse-2.1.2/muse/midiedit/drummap.cpp muse-3.0.2+ds1/muse/midiedit/drummap.cpp --- muse-2.1.2/muse/midiedit/drummap.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/drummap.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: drummap.cpp,v 1.3.2.6 2009/10/29 02:14:37 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -26,6 +27,8 @@ #include "xml.h" #include "song.h" +#include "helper.h" + #include namespace MusEGlobal { @@ -42,9 +45,9 @@ //--------------------------------------------------------- // Default to track port if -1 and track channel if -1. (These used to say 9, 0 for chan, port). -const DrumMap blankdm = { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 127, 127, false }; +const DrumMap blankdm = { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 127, 127, false, false }; -// this map should have 128 entries, as it's used for initalising iNewDrumMap as well. +// this map should have 128 entries, as it's used for initialising iNewDrumMap as well. // iNewDrumMap only has 128 entries. also, the every "out-note" ("anote") should be // represented exactly once in idrumMap, and there shall be no duplicate or unused // "out-notes". @@ -52,141 +55,141 @@ // iNewDrumMap[ idrumMap[i].anote ] = idrumMap[i] // if you ever want to change this, you will need to fix the initNewDrumMap() function. const DrumMap idrumMap[DRUM_MAPSIZE] = { - { QString("Acoustic Bass Drum"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 35, 35, false }, - { QString("Bass Drum 1"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 36, 36, false }, - { QString("Side Stick"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 37, 37, false }, - { QString("Acoustic Snare"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 38, 38, false }, - { QString("Hand Clap"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 39, 39, false }, - { QString("Electric Snare"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 40, 40, false }, - { QString("Low Floor Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 41, 41, false }, - { QString("Closed Hi-Hat"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 42, 42, false }, - { QString("High Floor Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 43, 43, false }, - { QString("Pedal Hi-Hat"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 44, 44, false }, - { QString("Low Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 45, 45, false }, - { QString("Open Hi-Hat"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 46, 46, false }, - { QString("Low-Mid Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 47, 47, false }, - { QString("Hi-Mid Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 48, 48, false }, - { QString("Crash Cymbal 1"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 49, 49, false }, - { QString("High Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 50, 50, false }, - - { QString("Ride Cymbal 1"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 51, 51, false }, - { QString("Chinese Cymbal"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 52, 52, false }, - { QString("Ride Bell"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 53, 53, false }, - { QString("Tambourine"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 54, 54, false }, - { QString("Splash Cymbal"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 55, 55, false }, - { QString("Cowbell"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 56, 56, false }, - { QString("Crash Cymbal 2"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 57, 57, false }, - { QString("Vibraslap"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 58, 58, false }, - { QString("Ride Cymbal 2"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 59, 59, false }, - { QString("Hi Bongo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 60, 60, false }, - { QString("Low Bongo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 61, 61, false }, - { QString("Mute Hi Conga"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 62, 62, false }, - { QString("Open Hi Conga"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 63, 63, false }, - { QString("Low Conga"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 64, 64, false }, - { QString("High Timbale"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 65, 65, false }, - { QString("Low Timbale"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 66, 66, false }, - - { QString("High Agogo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 67, 67, false }, - { QString("Low Agogo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 68, 68, false }, - { QString("Cabasa"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 69, 69, false }, - { QString("Maracas"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 70, 70, false }, - { QString("Short Whistle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 71, 71, false }, - { QString("Long Whistle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 72, 72, false }, - { QString("Short Guiro"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 73, 73, false }, - { QString("Long Guiro"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 74, 74, false }, - { QString("Claves"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 75, 75, false }, - { QString("Hi Wood Block"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 76, 76, false }, - { QString("Low Wood Block"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 77, 77, false }, - { QString("Mute Cuica"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 78, 78, false }, - { QString("Open Cuica"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 79, 79, false }, - { QString("Mute Triangle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 80, 80, false }, - { QString("Open Triangle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 81, 81, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 82, 82, false }, - - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 83, 83, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 84, 84, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 85, 85, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 86, 86, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 87, 87, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 88, 88, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 89, 89, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 90, 90, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 91, 91, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 92, 92, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 93, 93, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 94, 94, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 95, 95, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 96, 96, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 97, 97, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 98, 98, false }, - - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 99, 99, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 100, 100, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 101, 101, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 102, 102, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 103, 103, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 104, 104, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 105, 105, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 106, 106, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 107, 107, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 108, 108, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 109, 109, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 110, 110, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 111, 111, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 112, 112, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 113, 113, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 114, 114, false }, - - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 115, 115, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 116, 116, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 117, 117, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 118, 118, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 119, 119, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 120, 120, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 121, 121, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 122, 122, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 123, 123, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 124, 124, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 125, 125, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 126, 126, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 127, 127, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 0, 0, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 1, 1, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 2, 2, false }, - - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 3, 3, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 4, 4, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 5, 5, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 6, 6, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 7, 7, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 8, 8, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 9, 9, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 10, 10, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 11, 11, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 12, 12, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 13, 13, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 14, 14, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 15, 15, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 16, 16, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 17, 17, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 18, 18, false }, - - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 19, 19, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 20, 20, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 21, 21, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 22, 22, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 23, 23, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 24, 24, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 25, 25, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 26, 26, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 27, 27, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 28, 28, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 29, 29, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 30, 30, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 31, 31, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 32, 32, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 33, 33, false }, - { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 34, 34, false } + { QString("Acoustic Bass Drum"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 35, 35, false, false }, + { QString("Bass Drum 1"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 36, 36, false, false }, + { QString("Side Stick"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 37, 37, false, false }, + { QString("Acoustic Snare"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 38, 38, false, false }, + { QString("Hand Clap"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 39, 39, false, false }, + { QString("Electric Snare"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 40, 40, false, false }, + { QString("Low Floor Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 41, 41, false, false }, + { QString("Closed Hi-Hat"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 42, 42, false, false }, + { QString("High Floor Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 43, 43, false, false }, + { QString("Pedal Hi-Hat"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 44, 44, false, false }, + { QString("Low Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 45, 45, false, false }, + { QString("Open Hi-Hat"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 46, 46, false, false }, + { QString("Low-Mid Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 47, 47, false, false }, + { QString("Hi-Mid Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 48, 48, false, false }, + { QString("Crash Cymbal 1"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 49, 49, false, false }, + { QString("High Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 50, 50, false, false }, + + { QString("Ride Cymbal 1"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 51, 51, false, false }, + { QString("Chinese Cymbal"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 52, 52, false, false }, + { QString("Ride Bell"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 53, 53, false, false }, + { QString("Tambourine"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 54, 54, false, false }, + { QString("Splash Cymbal"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 55, 55, false, false }, + { QString("Cowbell"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 56, 56, false, false }, + { QString("Crash Cymbal 2"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 57, 57, false, false }, + { QString("Vibraslap"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 58, 58, false, false }, + { QString("Ride Cymbal 2"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 59, 59, false, false }, + { QString("Hi Bongo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 60, 60, false, false }, + { QString("Low Bongo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 61, 61, false, false }, + { QString("Mute Hi Conga"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 62, 62, false, false }, + { QString("Open Hi Conga"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 63, 63, false, false }, + { QString("Low Conga"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 64, 64, false, false }, + { QString("High Timbale"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 65, 65, false, false }, + { QString("Low Timbale"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 66, 66, false, false }, + + { QString("High Agogo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 67, 67, false, false }, + { QString("Low Agogo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 68, 68, false, false }, + { QString("Cabasa"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 69, 69, false, false }, + { QString("Maracas"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 70, 70, false, false }, + { QString("Short Whistle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 71, 71, false, false }, + { QString("Long Whistle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 72, 72, false, false }, + { QString("Short Guiro"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 73, 73, false, false }, + { QString("Long Guiro"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 74, 74, false, false }, + { QString("Claves"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 75, 75, false, false }, + { QString("Hi Wood Block"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 76, 76, false, false }, + { QString("Low Wood Block"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 77, 77, false, false }, + { QString("Mute Cuica"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 78, 78, false, false }, + { QString("Open Cuica"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 79, 79, false, false }, + { QString("Mute Triangle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 80, 80, false, false }, + { QString("Open Triangle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 81, 81, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 82, 82, false, false }, + + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 83, 83, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 84, 84, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 85, 85, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 86, 86, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 87, 87, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 88, 88, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 89, 89, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 90, 90, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 91, 91, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 92, 92, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 93, 93, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 94, 94, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 95, 95, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 96, 96, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 97, 97, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 98, 98, false, false }, + + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 99, 99, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 100, 100, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 101, 101, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 102, 102, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 103, 103, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 104, 104, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 105, 105, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 106, 106, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 107, 107, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 108, 108, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 109, 109, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 110, 110, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 111, 111, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 112, 112, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 113, 113, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 114, 114, false, false }, + + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 115, 115, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 116, 116, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 117, 117, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 118, 118, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 119, 119, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 120, 120, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 121, 121, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 122, 122, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 123, 123, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 124, 124, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 125, 125, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 126, 126, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 127, 127, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 0, 0, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 1, 1, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 2, 2, false, false }, + + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 3, 3, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 4, 4, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 5, 5, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 6, 6, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 7, 7, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 8, 8, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 9, 9, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 10, 10, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 11, 11, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 12, 12, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 13, 13, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 14, 14, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 15, 15, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 16, 16, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 17, 17, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 18, 18, false, false }, + + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 19, 19, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 20, 20, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 21, 21, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 22, 22, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 23, 23, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 24, 24, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 25, 25, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 26, 26, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 27, 27, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 28, 28, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 29, 29, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 30, 30, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 31, 31, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 32, 32, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 33, 33, false, false }, + { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 34, 34, false, false } }; DrumMap iNewDrumMap[128]; @@ -205,7 +208,7 @@ { if (done[idx]==true) { - printf("ERROR: iNewDrumMap[%i] is already initalized!\n" + printf("ERROR: iNewDrumMap[%i] is already initialized!\n" " this will be probably not a problem, but some programmer didn't read\n" " flo's comment at drummap.cpp, above idrumMap[].\n", idx); } @@ -221,7 +224,7 @@ { if (done[i]==false) { - printf("ERROR: iNewDrumMap[%i] is uninitalized!\n" + printf("ERROR: iNewDrumMap[%i] is uninitialized!\n" " this will be probably not a problem, but some programmer didn't read\n" " flo's comment at drummap.cpp, above idrumMap[].\n", i); iNewDrumMap[i].name=""; @@ -234,6 +237,8 @@ iNewDrumMap[i].lv4=110; iNewDrumMap[i].enote=i; iNewDrumMap[i].anote=i; + iNewDrumMap[i].mute=false; + iNewDrumMap[i].hide=false; } } } @@ -247,7 +252,8 @@ { for (int i = 0; i < DRUM_MAPSIZE; ++i) { DrumMap& d = MusEGlobal::drumMap[i]; - d.vol = d.len = d.channel = d.port = d.lv1 = d.lv2 = d.lv3 = d.lv4 = d.enote = d.anote = d.mute = 0; + d.vol = d.len = d.channel = d.port = d.lv1 = d.lv2 = d.lv3 = d.lv4 = d.enote = d.anote = 0; + d.mute = d.hide = false; } } //--------------------------------------------------------- @@ -261,7 +267,7 @@ DrumMap& d = MusEGlobal::drumMap[i]; //Make sure we're not overwriting any values loaded //On init, all these values are zero. If so, just set the drummap entry to the initial drummap entry. - if (!(d.vol || d.len || d.channel || d.port || d.lv1 || d.lv2 || d.lv3 || d.lv4 || d.enote || d.anote || d.mute)) + if (!(d.vol || d.len || d.channel || d.port || d.lv1 || d.lv2 || d.lv3 || d.lv4 || d.enote || d.anote || d.mute || d.hide)) MusEGlobal::drumMap[i] = idrumMap[i]; } //Finally, setup the inMap, outMap-values @@ -301,6 +307,15 @@ bool DrumMap::operator==(const DrumMap& map) const { + // Everything equal, including mute and hide settings? + return almost_equals(map) && + mute == map.mute && + hide == map.hide; + } + +bool DrumMap::almost_equals(const DrumMap& map) const +{ + // Disregarding mute and hide settings. return name == map.name && vol == map.vol @@ -313,18 +328,25 @@ && lv3 == map.lv3 && lv4 == map.lv4 && enote == map.enote - && anote == map.anote - && mute == map.mute; - } + && anote == map.anote; +} -bool DrumMap::almost_equals(const DrumMap& map) const +void DrumMap::dump() { - DrumMap tmp=map; - tmp.mute=this->mute; - return tmp==*this; + fprintf(stderr, "%s\t\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d(%s)\t\t%d(%s)\t\t%d\t%d\t\n", + name.toLatin1().constData(), + vol, + quant, + len, + channel, + port, + lv1, lv2, lv3, lv4, + enote, pitch2string(enote).toLatin1().constData(), + anote, pitch2string(anote).toLatin1().constData(), + mute, + hide); } - //--------------------------------------------------------- // writeDrumMap //--------------------------------------------------------- @@ -350,6 +372,7 @@ xml.intTag(level, "lv4", dm->lv4); xml.intTag(level, "enote", dm->enote); xml.intTag(level, "anote", dm->anote); + xml.intTag(level, "hide", dm->hide); } else { // write only, if entry is different from initial entry @@ -382,6 +405,8 @@ xml.intTag(level, "anote", dm->anote); if (dm->mute != idm->mute) xml.intTag(level, "mute", dm->mute); + if (dm->hide != idm->hide) + xml.intTag(level, "hide", dm->hide); } xml.tag(level--, "/entry"); } @@ -429,11 +454,13 @@ dm->anote = xml.parseInt(); else if (tag == "mute") dm->mute = xml.parseInt(); + else if (tag == "hide") + dm->hide = xml.parseInt(); else if (tag == "selected") //; // dm->selected = xml.parseInt(); xml.skip(tag); else - xml.unknown("DrumMapEntry"); + xml.unknown("entry"); break; case Xml::Attribut: if (tag == "idx") { @@ -652,5 +679,5 @@ return entry; } - + } // namespace MusEGlobal diff -Nru muse-2.1.2/muse/midiedit/drummap.h muse-3.0.2+ds1/muse/midiedit/drummap.h --- muse-2.1.2/muse/midiedit/drummap.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/drummap.h 2017-12-04 21:01:18.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: drummap.h,v 1.3.2.3 2009/10/29 02:14:37 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -26,6 +27,7 @@ #include #include +#include namespace MusECore { @@ -48,10 +50,30 @@ char lv1, lv2, lv3, lv4; // velocities char enote, anote; // input note - output note bool mute; + bool hide; + + // Initializes the structure. NOTE: Be sure the enote value + // does not conflict with another map item's enote value. + void init() { + vol = 100; + quant = 16; + len = 32; + channel = -1; + port = -1; + lv1 = 70; + lv2 = 90; + lv3 = 110; + lv4 = 127; + enote = 0; + anote = 0; + mute = false; + }; bool operator==(const DrumMap& map) const; bool operator!=(const DrumMap& map) const { return !operator==(map); } bool almost_equals(const DrumMap& map) const; + + void dump(); }; // please let this at "128". idrumMap should have length 128 (see drummap.cpp for details) diff -Nru muse-2.1.2/muse/midiedit/ecanvas.cpp muse-3.0.2+ds1/muse/midiedit/ecanvas.cpp --- muse-2.1.2/muse/midiedit/ecanvas.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/ecanvas.cpp 2017-12-17 21:07:38.000000000 +0000 @@ -34,6 +34,7 @@ #include #include #include +#include #include "xml.h" #include "midieditor.h" @@ -63,10 +64,6 @@ _playEvents = true; _setCurPartIfOnlyOneEventIsSelected = true; curVelo = 70; - playedPitch = -1; - playedPitchChannel = -1; - playedPitchPort = -1; - playedVelocity = 0; setBg(MusEGlobal::config.midiCanvasBg); setAcceptDrops(true); @@ -152,17 +149,28 @@ MusEGlobal::song->update(SC_SELECTION); } +bool EventCanvas::stuckNoteExists(int port, int channel, int pitch) const +{ + const int sz = _stuckNotes.size(); + for(int i = 0; i < sz; ++i) + { + MusECore::MidiPlayEvent s_ev(_stuckNotes.at(i)); + if(s_ev.type() == MusECore::ME_NOTEON && + port == s_ev.port() && + channel == s_ev.channel() && + pitch == s_ev.dataA()) + return true; + } + return false; +} + //--------------------------------------------------------- // songChanged(type) //--------------------------------------------------------- void EventCanvas::songChanged(MusECore::SongChangedFlags_t flags) { - // Is it simply a midi controller value adjustment? Forget it. - if(flags == SC_MIDI_CONTROLLER) - return; - - if (flags & ~SC_SELECTION) { + if (flags & ~(SC_SELECTION | SC_PART_SELECTION | SC_TRACK_SELECTION)) { // TODO FIXME: don't we actually only want SC_PART_*, and maybe SC_TRACK_DELETED? // (same in waveview.cpp) bool curItemNeedsRestore=false; @@ -192,8 +200,7 @@ if (etick > end_tick) end_tick = etick; - MusECore::EventList* el = part->events(); - for (MusECore::iEvent i = el->begin(); i != el->end(); ++i) { + for (MusECore::ciEvent i = part->events().begin(); i != part->events().end(); ++i) { MusECore::Event e = i->second; // Do not add events which are past the end of the part. if(e.tick() > len) @@ -222,14 +229,12 @@ int n = 0; // count selections for (iCItem k = items.begin(); k != items.end(); ++k) { MusECore::Event ev = k->second->event(); - bool selected = ev.selected(); - if (selected) { - k->second->setSelected(true); + + if (ev.selected()) { ++n; if (!nevent) { nevent = k->second; - MusECore::Event mi = nevent->event(); - curVelo = mi.velo(); + curVelo = ev.velo(); } } } @@ -265,37 +270,39 @@ // selectAtTick //--------------------------------------------------------- void EventCanvas::selectAtTick(unsigned int tick) - { - //Select note nearest tick, if none selected and there are any - if (!items.empty() && selectionSize() == 0) { - iCItem i = items.begin(); - CItem* nearest = i->second; - - while (i != items.end()) { - CItem* cur=i->second; - unsigned int curtk=abs(cur->x() + cur->part()->tick() - tick); - unsigned int neartk=abs(nearest->x() + nearest->part()->tick() - tick); - - if (curtk < neartk) { - nearest=cur; - } - - i++; - } - - if (!nearest->isSelected()) { - selectItem(nearest, true); - songChanged(SC_SELECTION); - } - } +{ + //Select note nearest tick, if none selected and there are any + if (!items.empty() && selectionSize() == 0) { + iCItem i = items.begin(); + CItem* nearest = i->second; + + while (i != items.end()) { + CItem* cur=i->second; + unsigned int curtk=abs(cur->x() + (int)cur->part()->tick() - (int)tick); + unsigned int neartk=abs(nearest->x() + (int)nearest->part()->tick() - (int)tick); + + if (curtk < neartk) { + nearest=cur; + } + + i++; } + if (!nearest->isSelected()) { + selectItem(nearest, true); + songChanged(SC_SELECTION); + } + } +} + //--------------------------------------------------------- // track //--------------------------------------------------------- MusECore::MidiTrack* EventCanvas::track() const { + if(!curPart) + return 0; return ((MusECore::MidiPart*)curPart)->track(); } @@ -490,29 +497,63 @@ } //--------------------------------------------------------- +// deselectAll +//--------------------------------------------------------- + +void EventCanvas::deselectAll() +{ + QSet already_done; + MusECore::Part* p; + for(iCItem i = items.begin(); i != items.end(); ++i) + { + p = i->second->part(); + if(already_done.contains(p) || !p) + continue; + MusEGlobal::song->selectAllEvents(p, false); + already_done.insert(p); + } +} + +//--------------------------------------------------------- // startPlayEvent //--------------------------------------------------------- void EventCanvas::startPlayEvent(int note, int velocity, int port, int channel) { + // REMOVE Tim. Noteoff. Added. Zero note on vel is not allowed now. + if(velocity == 0) + { + fprintf(stderr, "EventCanvas::startPlayEvent: Warning: Zero note on velocity!\n"); + velocity = 1; + } + if (MusEGlobal::debugMsg) printf("EventCanvas::startPlayEvent %d %d %d %d\n", note, velocity, port, channel); // Release any current note. stopPlayEvent(); - playedPitch = note + track()->transposition; - playedVelocity = velocity; - playedPitchPort = port; - playedPitchChannel = channel; + if(!track()) + return; + + int playedPitch = note; + // Apply track transposition, but only for midi tracks, not drum tracks. + if(track()->isMidiTrack() && !track()->isDrumTrack()) + playedPitch += track()->transposition; // play note: - MusECore::MidiPlayEvent e(0, port, channel, MusECore::ME_NOTEON, playedPitch, velocity); - MusEGlobal::audio->msgPlayMidiEvent(&e); + if(stuckNoteExists(port, channel, playedPitch)) + return; + const MusECore::MidiPlayEvent e(MusEGlobal::audio->curFrame(), port, channel, MusECore::ME_NOTEON, playedPitch, velocity); + _stuckNotes.push_back(e); + // Send to the port and device. + MusEGlobal::midiPorts[port].putEvent(e); } void EventCanvas::startPlayEvent(int note, int velocity) { + if(!track()) + return; int port = track()->outPort(); int channel = track()->outChannel(); startPlayEvent(note, velocity, port, channel); @@ -524,14 +565,26 @@ void EventCanvas::stopPlayEvent() { - if(playedPitch == -1 || playedPitchPort == -1 || playedPitchChannel == -1) - return; - // release note: - //MusECore::MidiPlayEvent ev(0, playedPitchPort, playedPitchChannel, 0x90, playedPitch, 0); // REMOVE Tim. - MusECore::MidiPlayEvent ev(0, playedPitchPort, playedPitchChannel, MusECore::ME_NOTEOFF, playedPitch, playedVelocity); - MusEGlobal::audio->msgPlayMidiEvent(&ev); - playedPitch = playedPitchPort = playedPitchChannel = -1; - playedVelocity = 0; + // Stop all currently playing notes. + unsigned int frame = MusEGlobal::audio->curFrame(); + int port; + const int sz = _stuckNotes.size(); + for(int i = 0; i < sz; ++i) + { + MusECore::MidiPlayEvent ev(_stuckNotes.at(i)); + port = ev.port(); + if(port < 0 || port >= MIDI_PORTS) + continue; + ev.setType(MusECore::ME_NOTEOFF); + ev.setTime(frame); + if(ev.dataB() == 0) + ev.setB(64); + + // Send to the port and device. + MusEGlobal::midiPorts[port].putEvent(ev); + } + // Clear the stuck notes list. + _stuckNotes.clear(); } } // namespace MusEGui diff -Nru muse-2.1.2/muse/midiedit/ecanvas.h muse-3.0.2+ds1/muse/midiedit/ecanvas.h --- muse-2.1.2/muse/midiedit/ecanvas.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/ecanvas.h 2017-12-17 21:07:38.000000000 +0000 @@ -26,8 +26,10 @@ #include "type_defs.h" #include "canvas.h" #include "noteinfo.h" +#include "mpevent.h" #include #include +#include #define KH 13 @@ -67,10 +69,6 @@ virtual void mouseMove(QMouseEvent* event); protected: - int playedPitch; - int playedVelocity; - int playedPitchPort; - int playedPitchChannel; bool _playEvents; MidiEditor* editor; unsigned start_tick, end_tick; @@ -78,13 +76,17 @@ bool _steprec; bool _midiin; bool _setCurPartIfOnlyOneEventIsSelected; + // Notes that are currently being played in the piano or drum list etc. + QVector _stuckNotes; + bool stuckNoteExists(int port, int channel, int pitch) const; void updateSelection(); - virtual CItem* addItem(MusECore::Part*, MusECore::Event&) = 0; + virtual CItem* addItem(MusECore::Part*, const MusECore::Event&) = 0; virtual QPoint raster(const QPoint&) const; virtual MusECore::Undo moveCanvasItems(CItemList&, int, int, DragType, bool rasterize = true) = 0; virtual bool moveItem(MusECore::Undo&, CItem*, const QPoint&, DragType, bool rasterize = true) = 0; virtual void endMoveItems(const QPoint&, DragType, int dir, bool rasterize = true); + virtual void deselectAll(); virtual void startPlayEvent(int note, int velocity); virtual void startPlayEvent(int note, int velocity, int port, int channel); virtual void stopPlayEvent(); diff -Nru muse-2.1.2/muse/midiedit/piano.cpp muse-3.0.2+ds1/muse/midiedit/piano.cpp --- muse-2.1.2/muse/midiedit/piano.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/piano.cpp 2017-12-04 22:07:38.000000000 +0000 @@ -137,6 +137,22 @@ ".......................................#", }; + +static const char *mk1_xpmC0[] = { + "40 9 2 1", + ". c none", + "# c #000000", + ".......................................#", + "..........................###.....##...#", + ".........................#...#...#..#..#", + ".........................#.......#..#..#", + ".........................#.......#..#..#", + ".........................#...#...#..#..#", // 0 + "..........................###.....##...#", + ".......................................#", + ".......................................#", + }; + static const char *mk1_xpmC1[] = { "40 9 2 1", ". c none", @@ -146,11 +162,13 @@ ".........................#...#...##....#", ".........................#........#....#", ".........................#........#....#", - ".........................#...#....#....#", // 9 + ".........................#...#....#....#", // 1 "..........................###....###...#", ".......................................#", ".......................................#", }; + + static const char *mk1_xpmC2[] = { "40 9 2 1", ". c none", @@ -160,7 +178,7 @@ ".........................#...#..#..#...#", ".........................#........#....#", ".........................#.......#.....#", - ".........................#...#..#......#", // 9 + ".........................#...#..#......#", // 2 "..........................###...####...#", ".......................................#", ".......................................#", @@ -174,7 +192,7 @@ ".........................#...#..#..#...#", ".........................#........#....#", ".........................#.........#...#", - ".........................#...#..#..#...#", // 9 + ".........................#...#..#..#...#", // 3 "..........................###....##....#", ".......................................#", ".......................................#", @@ -188,7 +206,7 @@ ".........................#...#..#..#...#", ".........................#......####...#", ".........................#.........#...#", - ".........................#...#.....#...#", // 9 + ".........................#...#.....#...#", // 4 "..........................###......#...#", ".......................................#", ".......................................#", @@ -202,7 +220,7 @@ ".........................#...#..#......#", ".........................#......###....#", ".........................#.........#...#", - ".........................#...#.....#...#", // 9 + ".........................#...#.....#...#", // 5 "..........................###...###....#", ".......................................#", ".......................................#", @@ -217,7 +235,7 @@ ".........................#...#..#......#", ".........................#......###....#", ".........................#......#..#...#", - ".........................#...#..#..#...#", // 9 + ".........................#...#..#..#...#", // 6 "..........................###...###....#", ".......................................#", ".......................................#", @@ -232,7 +250,7 @@ ".........................#...#.....#...#", ".........................#........#....#", ".........................#.......#.....#", - ".........................#...#..#......#", // 9 + ".........................#...#..#......#", // 7 "..........................###...#......#", ".......................................#", ".......................................#", @@ -246,12 +264,29 @@ ".........................#...#..#..#...#", ".........................#.......##....#", ".........................#......#..#...#", + ".........................#...#..#..#...#", // 8 + "..........................###....##....#", + ".......................................#", + ".......................................#", + }; + +/* +static const char *mk1_xpmC9[] = { + "40 9 2 1", + ". c none", + "# c #000000", + ".......................................#", + "..........................###....##....#", + ".........................#...#..#..#...#", + ".........................#.......###...#", + ".........................#.........#...#", ".........................#...#..#..#...#", // 9 "..........................###....##....#", ".......................................#", ".......................................#", }; +*/ static const char *mk1_xpm[] = { "40 13 2 1", ". c #ff0000", @@ -408,7 +443,7 @@ 0 1 2 3 4 5 6 7 8 9 10 c-2 c-1 C0 C1 C2 C3 C4 C5 C6 C7 C8 - G8 - Grid über Oktave: + Grid ÑŒber Oktave: +------------+ ------------------------------ 11 | | @@ -461,6 +496,7 @@ c_keys[5] = new QPixmap(mk1_xpmC3); c_keys[6] = new QPixmap(mk1_xpmC2); c_keys[7] = new QPixmap(mk1_xpmC1); + c_keys[8] = new QPixmap(mk1_xpmC0); mk1 = new QPixmap(mk1_xpm); mk2 = new QPixmap(mk2_xpm); @@ -536,7 +572,7 @@ } // draw C notes - for (int drawKey = 0; drawKey < 8;drawKey++) { + for (int drawKey = 0; drawKey < 9;drawKey++) { int octaveSize=91; int drawY = octaveSize * drawKey + 82 - KH*2; @@ -550,7 +586,7 @@ return; MusECore::PartList* part_list = _midiEditor->parts(); MusECore::Part* cur_part = _midiEditor->curCanvasPart(); - if(!part_list || !cur_part) + if(!part_list || !cur_part || !cur_part->track()->isMidiTrack()) return; MusECore::MidiTrack* track = (MusECore::MidiTrack*)(cur_part->track()); @@ -570,8 +606,7 @@ int num = cl->num(); int pitch = num & 0x7f; bool used = false; - MusECore::EventList* el = cur_part->events(); - for (MusECore::ciEvent ie = el->begin(); ie != el->end(); ++ie) + for (MusECore::ciEvent ie = cur_part->events().begin(); ie != cur_part->events().end(); ++ie) { MusECore::Event e = ie->second; if(e.type() != MusECore::Controller) @@ -717,7 +752,13 @@ } else { int velocity = event->x()*127/40; - emit keyPressed(keyDown, velocity>127 ? 127 : velocity, shift); //emit keyPressed(keyDown, shift); + // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now. +// emit keyPressed(keyDown, velocity>127 ? 127 : velocity, shift); //emit keyPressed(keyDown, shift); + if(velocity > 127) + velocity = 127; + else if(velocity <= 0) + velocity = 1; // Zero note on vel is not allowed. + emit keyPressed(keyDown, velocity, shift); //emit keyPressed(keyDown, shift); } if (keyDown != -1 && keyDown != _curSelectedPitch) { diff -Nru muse-2.1.2/muse/midiedit/pianoroll.cpp muse-3.0.2+ds1/muse/midiedit/pianoroll.cpp --- muse-2.1.2/muse/midiedit/pianoroll.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/pianoroll.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // $Id: pianoroll.cpp,v 1.25.2.15 2009/11/16 11:29:33 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012-2016 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 @@ -46,14 +46,20 @@ #include #include #include +#include +#include +#include +#include #include +#include "globaldefs.h" #include "xml.h" #include "mtscale.h" #include "prcanvas.h" #include "pianoroll.h" #include "scrollscale.h" +#include "scrollbar.h" #include "piano.h" #include "../ctrl/ctrledit.h" #include "splitter.h" @@ -69,17 +75,24 @@ #include "helper.h" #include "popupmenu.h" #include "menutitleitem.h" -#include "editinstrument.h" - +#include "operations.h" #include "cmd.h" #include "shortcuts.h" +#include "mstrip.h" +#include "widget_stack.h" +#include "trackinfo_layout.h" + +#ifdef _USE_TRACKINFO_ALT #include "mtrackinfo.h" +#endif namespace MusEGui { int PianoRoll::_rasterInit = 96; +int PianoRoll::_trackInfoWidthInit = 50; +int PianoRoll::_canvasWidthInit = 300; int PianoRoll::colorModeInit = 0; static const int xscale = -10; @@ -99,7 +112,7 @@ tickValue = 0; lenValue = 0; pitchValue = 0; - veloOnValue = 0; + veloOnValue = 1; veloOffValue = 0; firstValueSet = false; tickOffset = 0; @@ -276,6 +289,20 @@ //---------ToolBar---------------------------------- + + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + + addToolBarBreak(); + + // Already has an object name. + tools2 = new MusEGui::EditToolBar(this, pianorollTools); + addToolBar(tools2); + tools = addToolBar(tr("Pianoroll tools")); tools->setObjectName("Pianoroll tools"); @@ -303,15 +330,14 @@ tools->addAction(QWhatsThis::createAction(this)); - tools2 = new MusEGui::EditToolBar(this, pianorollTools); - addToolBar(tools2); - - addToolBarBreak(); toolbar = new MusEGui::Toolbar1(this, _rasterInit); + toolbar->setObjectName("Pianoroll Pos/Snap/Solo-tools"); addToolBar(toolbar); addToolBarBreak(); + info = new MusEGui::NoteInfo(this); + info->setObjectName("Pianoroll Note Info"); addToolBar(info); //--------------------------------------------------- @@ -323,7 +349,7 @@ hsplitter = new MusEGui::Splitter(Qt::Horizontal, mainw, "hsplitter"); hsplitter->setChildrenCollapsible(true); - hsplitter->setHandleWidth(2); + hsplitter->setHandleWidth(4); ctrl = new QPushButton(tr("ctrl"), mainw); ctrl->setObjectName("Ctrl"); @@ -333,30 +359,40 @@ // Increased scale to -1. To resolve/select/edit 1-tick-wide (controller graph) events. hscroll = new MusEGui::ScrollScale(-25, -1 /* formerly -2 */, xscale, 20000, Qt::Horizontal, mainw); ctrl->setFixedSize(pianoWidth, hscroll->sizeHint().height()); - //ctrl->setFixedSize(pianoWidth / 2, hscroll->sizeHint().height()); // DELETETHIS? QSizeGrip* corner = new QSizeGrip(mainw); - midiTrackInfo = new MusEGui::MidiTrackInfo(mainw); - int mtiw = midiTrackInfo->width(); // Save this. - midiTrackInfo->setMinimumWidth(100); - //midiTrackInfo->setMaximumWidth(150); DELETETHIS ? - - midiTrackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding)); - infoScroll = new QScrollArea; - infoScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - infoScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - infoScroll->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding)); - infoScroll->setWidget(midiTrackInfo); - infoScroll->setWidgetResizable(true); - infoScroll->setFocusPolicy(Qt::NoFocus); - //infoScroll->setVisible(false); DELETETHIS 4? - //infoScroll->setEnabled(false); - - //hsplitter->addWidget(midiTrackInfo); - hsplitter->addWidget(infoScroll); // Tim. - hsplitter->addWidget(splitter); +#ifdef _USE_TRACKINFO_ALT +// midiTrackInfo = new MusEGui::MidiTrackInfo(mainw); +// int mtiw = midiTrackInfo->width(); // Save this. +// midiTrackInfo->setMinimumWidth(100); +// //midiTrackInfo->setMaximumWidth(150); DELETETHIS ? +// midiTrackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding)); +// int mtiw = 0; + midiTrackInfo = 0; +#endif + + midiStrip = 0; + + trackInfoWidget = new TrackInfoWidget(hsplitter); + + genTrackInfo(trackInfoWidget); + + if(hsplitter) + hsplitter->addWidget(splitter); + hsplitter->setStretchFactor(hsplitter->indexOf(trackInfoWidget), 0); + QSizePolicy tipolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + tipolicy.setHorizontalStretch(0); + tipolicy.setVerticalStretch(100); + trackInfoWidget->setSizePolicy(tipolicy); + + hsplitter->setStretchFactor(hsplitter->indexOf(splitter), 1); + QSizePolicy epolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + epolicy.setHorizontalStretch(255); + epolicy.setVerticalStretch(100); + splitter->setSizePolicy(epolicy); + mainGrid->setRowStretch(0, 100); mainGrid->setColumnStretch(1, 100); mainGrid->addWidget(hsplitter, 0, 1, 1, 3); @@ -406,11 +442,6 @@ piano->setFixedWidth(pianoWidth); - QList mops; - mops.append(mtiw + 30); // 30 for possible scrollbar - mops.append(width() - mtiw - 30); - hsplitter->setSizes(mops); - connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int))); connect(ctrl, SIGNAL(clicked()), SLOT(addCtrlClicked())); @@ -446,8 +477,13 @@ connect(info, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(info, SIGNAL(escapePressed()), SLOT(focusCanvas())); - connect(midiTrackInfo, SIGNAL(returnPressed()), SLOT(focusCanvas())); - connect(midiTrackInfo, SIGNAL(escapePressed()), SLOT(focusCanvas())); +#ifdef _USE_TRACKINFO_ALT + if(midiTrackInfo) + { + connect(midiTrackInfo, SIGNAL(returnPressed()), SLOT(focusCanvas())); + connect(midiTrackInfo, SIGNAL(escapePressed()), SLOT(focusCanvas())); + } +#endif connect(hscroll, SIGNAL(scaleChanged(int)), SLOT(updateHScrollRange())); piano->setYPos(KH * 30); @@ -493,10 +529,15 @@ if(canvas->track()) { - updateTrackInfo(); + updateTrackInfo(-1); toolbar->setSolo(canvas->track()->solo()); } + QList mops; + mops.append(_trackInfoWidthInit); + mops.append(_canvasWidthInit); + hsplitter->setSizes(mops); + initTopwinState(); finalizeInit(); } @@ -509,15 +550,57 @@ { if(_isDeleting) // Ignore while while deleting to prevent crash. return; + + // We must catch this first and be sure to update the strips. + if(bits & SC_TRACK_REMOVED) + { +#ifdef _USE_TRACKINFO_ALT + const int idx = midiTrackInfo ? 2 : 1; +#else + const int idx = 1; +#endif + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(idx)); + if(w) + { + MusECore::Track* t = w->getTrack(); + if(t) + { + MusECore::MidiTrackList* tl = MusEGlobal::song->midis(); + MusECore::iMidiTrack it = tl->find(t); + if(it == tl->end()) + { + delete w; + trackInfoWidget->addWidget(0, idx); + selected = 0; + switchInfo(0); + } + } + } + } + } if (bits & SC_SOLO) + { + if(canvas->track()) toolbar->setSolo(canvas->track()->solo()); + } songChanged(bits); // We'll receive SC_SELECTION if a different part is selected. - if (bits & SC_SELECTION) - updateTrackInfo(); + // Addition - also need to respond here to moving part to another track. (Tim) + if (bits & (SC_SELECTION | SC_PART_INSERTED | SC_PART_REMOVED)) + updateTrackInfo(-1); + + // We must marshall song changed instead of connecting to the strip's song changed + // otherwise it crashes when loading another song because track is no longer valid + // and the strip's songChanged() seems to be called before Pianoroll songChanged() + // gets called and has a chance to stop the crash. + // Also, calling updateTrackInfo() from here is too heavy, it destroys and recreates + // the strips each time no matter what the flags are ! + else + trackInfoSongChange(bits); } //--------------------------------------------------------- @@ -530,6 +613,146 @@ } //--------------------------------------------------------- +// genTrackInfo +//--------------------------------------------------------- + +void PianoRoll::genTrackInfo(TrackInfoWidget* trackInfo) + { + noTrackInfo = new QWidget(trackInfo); + noTrackInfo->setAutoFillBackground(true); + QPixmap *noInfoPix = new QPixmap(160, 1000); + const QPixmap *logo = new QPixmap(*museLeftSideLogo); + noInfoPix->fill(noTrackInfo->palette().color(QPalette::Window) ); + QPainter p(noInfoPix); + p.drawPixmap(10, 0, *logo, 0,0, logo->width(), logo->height()); + + QPalette palette; + palette.setBrush(noTrackInfo->backgroundRole(), QBrush(*noInfoPix)); + noTrackInfo->setPalette(palette); + noTrackInfo->setGeometry(0, 0, 65, 200); + noTrackInfo->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding)); + +// midiTrackInfo = new MidiTrackInfo(trackInfo); + + trackInfo->addWidget(noTrackInfo, 0); +#ifdef _USE_TRACKINFO_ALT + if(midiTrackInfo) + { + trackInfo->addWidget(midiTrackInfo, 1); + trackInfo->addWidget(0, 2); + } + else +#endif + trackInfo->addWidget(0, 1); + } + +void PianoRoll::updateTrackInfo(MusECore::SongChangedFlags_t /*flags*/) +{ + MusECore::Part* part = curCanvasPart(); + if(part) + selected = part->track(); + else + selected = 0; + + if (selected == 0) { + switchInfo(0); + return; + } + if (selected->isMidiTrack()) + { + switchInfo(1); +#ifdef _USE_TRACKINFO_ALT + if(midiTrackInfo) + { + // If a different part was selected + if(midiTrackInfo->track() != selected) + // Set a new track and do a complete update. + midiTrackInfo->setTrack(selected); + else + // Otherwise just regular update with specific flags. + midiTrackInfo->updateTrackInfo(flags); + } +#endif + } +} + +//--------------------------------------------------------- +// switchInfo +//--------------------------------------------------------- + +void PianoRoll::switchInfo(int n) + { +#ifdef _USE_TRACKINFO_ALT + const int idx = midiTrackInfo ? 2 : 1; +#else + const int idx = 1; +#endif + if(n == idx) { + MidiStrip* w = (MidiStrip*)(trackInfoWidget->getWidget(idx)); + if (w == 0 || selected != w->getTrack()) { + if (w) + { + //fprintf(stderr, "PianoRoll::switchInfo deleting strip\n"); + delete w; + //w->deleteLater(); + } + w = new MidiStrip(trackInfoWidget, static_cast (selected)); + // Leave broadcasting changes to other selected tracks off. + + // Set focus yielding to the canvas. + if(MusEGlobal::config.smartFocus) + { + w->setFocusYieldWidget(canvas); + //w->setFocusPolicy(Qt::WheelFocus); + } + + // We must marshall song changed instead of connecting to the strip's song changed + // otherwise it crashes when loading another song because track is no longer valid + // and the strip's songChanged() seems to be called before Pianoroll songChanged() + // gets called and has a chance to stop the crash. + //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), w, SLOT(songChanged(MusECore::SongChangedFlags_t))); + + connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged())); + w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); + trackInfoWidget->addWidget(w, idx); + w->show(); + //setTabOrder(midiTrackInfo, w); + } + } + if (trackInfoWidget->curIdx() == n) + return; + trackInfoWidget->raiseWidget(n); + } + +//--------------------------------------------------------- +// trackInfoSongChange +//--------------------------------------------------------- + +void PianoRoll::trackInfoSongChange(MusECore::SongChangedFlags_t flags) +{ + if(!selected) // || !showTrackinfoFlag) + return; + + if(selected->isMidiTrack()) + { +#ifdef _USE_TRACKINFO_ALT + if(midiTrackInfo) // && showTrackinfoAltFlag) + { + MidiTrackInfo* w = static_cast(trackInfoWidget->getWidget(1)); + if(w) + w->songChanged(flags); + } + else +#endif + { + MidiStrip* w = static_cast(trackInfoWidget->getWidget(1)); + if(w) + w->songChanged(flags); + } + } +} + +//--------------------------------------------------------- // horizontalZoom //--------------------------------------------------------- @@ -583,14 +806,6 @@ hscroll->setRange(s, e); } -void PianoRoll::updateTrackInfo() -{ - selected = curCanvasPart()->track(); - if (selected->isMidiTrack()) { - midiTrackInfo->setTrack(selected); - } -} - //--------------------------------------------------------- // follow //--------------------------------------------------------- @@ -631,6 +846,11 @@ void PianoRoll::cmd(int cmd) { + // Don't process if user is dragging or has clicked on the events. + // Causes crashes later in Canvas::viewMouseMoveEvent and viewMouseReleaseEvent. + if(canvas->getCurrentDrag()) + return; + switch (cmd) { case PianoCanvas::CMD_CUT: @@ -713,6 +933,11 @@ lenValue = e.lenTick(); pitchValue = e.pitch(); veloOnValue = e.velo(); + if(veloOnValue == 0) + { + veloOnValue = 1; + fprintf(stderr, "PianoRoll::setSelection: Warning: Zero note on velocity!\n"); + } veloOffValue = e.veloOff(); firstValueSet = true; } @@ -726,12 +951,12 @@ } else { info->setEnabled(false); - info->setValues(0, 0, 0, 0, 0); + info->setValues(0, 0, 0, deltaMode ? 0 : 1, 0); firstValueSet = false; tickValue = 0; lenValue = 0; pitchValue = 0; - veloOnValue = 0; + veloOnValue = 1; veloOffValue = 0; tickOffset = 0; lenOffset = 0; @@ -868,7 +1093,7 @@ } else if (rv == edit_ins) { // edit instrument MusECore::MidiInstrument* instr = port->instrument(); - MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrument::Controllers); + MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrumentControllers); } else { // Select a control if(cll->find(channel, rv) == cll->end()) @@ -975,6 +1200,13 @@ QSettings settings("MusE", "MusE-qt"); settings.setValue("Pianoroll/windowState", saveState()); + //Store values of the horizontal splitter + QList sizes = hsplitter->sizes(); + QList::iterator it = sizes.begin(); + _trackInfoWidthInit = *it; //There are only 2 values stored in the sizelist, size of trackinfo widget and canvas widget + it++; + _canvasWidthInit = *it; + emit isDeleting(static_cast(this)); e->accept(); } @@ -994,6 +1226,10 @@ case MusECore::Xml::TagStart: if (tag == "raster") _rasterInit = xml.parseInt(); + else if (tag == "trackinfowidth") + _trackInfoWidthInit = xml.parseInt(); + else if (tag == "canvaswidth") + _canvasWidthInit = xml.parseInt(); else if (tag == "colormode") colorModeInit = xml.parseInt(); else if (tag == "topwin") @@ -1018,6 +1254,8 @@ { xml.tag(level++, "pianoroll"); xml.intTag(level, "raster", _rasterInit); + xml.intTag(level, "trackinfowidth", _trackInfoWidthInit); + xml.intTag(level, "canvaswidth", _canvasWidthInit); xml.intTag(level, "colormode", colorModeInit); TopWin::writeConfiguration(PIANO_ROLL, level, xml); xml.etag(level, "pianoroll"); @@ -1030,8 +1268,13 @@ void PianoRoll::soloChanged(bool flag) { - MusEGlobal::audio->msgSetSolo(canvas->track(), flag); - MusEGlobal::song->update(SC_SOLO); + if(canvas->track()) + { + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(canvas->track(), flag, MusECore::PendingOperationItem::SetTrackSolo)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } } //--------------------------------------------------------- @@ -1056,7 +1299,8 @@ xml.tag(level++, "pianoroll"); MidiEditor::writeStatus(level, xml); splitter->writeStatus(level, xml); - hsplitter->writeStatus(level, xml); + if(hsplitter) + hsplitter->writeStatus(level, xml); for (std::list::const_iterator i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) { @@ -1067,10 +1311,10 @@ xml.intTag(level, "midiin", canvas->midiin()); xml.intTag(level, "tool", int(canvas->tool())); xml.intTag(level, "playEvents", _playEvents); - xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "xmag", hscroll->mag()); - xml.intTag(level, "ypos", vscroll->pos()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", vscroll->mag()); + xml.intTag(level, "ypos", vscroll->pos()); xml.tag(level, "/pianoroll"); } @@ -1110,7 +1354,7 @@ } else if (tag == splitter->objectName()) splitter->readStatus(xml); - else if (tag == hsplitter->objectName()) + else if (hsplitter && tag == hsplitter->objectName()) hsplitter->readStatus(xml); else if (tag == "playEvents") { _playEvents = xml.parseInt(); @@ -1311,6 +1555,34 @@ else return; } + else if (key == shortcuts[SHRT_MOVE_PLAY_TO_NOTE].key){ + movePlayPointerToSelectedEvent(); + return; + } + else if (key == shortcuts[SHRT_STEP_RECORD].key) { + canvas->setSteprec(!srec->isChecked()); + srec->setChecked(!srec->isChecked()); + return; + } + else if (key == shortcuts[SHRT_MIDI_INPUT].key) { + canvas->setMidiin(!midiin->isChecked()); + midiin->setChecked(!midiin->isChecked()); + return; + + } + else if (key == shortcuts[SHRT_PLAY_EVENTS].key) { + canvas->playEvents(!speaker->isChecked()); + speaker->setChecked(!speaker->isChecked()); + return; + } + else if (key == shortcuts[SHRT_INC_VELOCITY].key) { + modify_velocity(partlist_to_set(parts()), 1, 100, 1); + return; + } + else if (key == shortcuts[SHRT_DEC_VELOCITY].key) { + modify_velocity(partlist_to_set(parts()), 1, 100, -1); + return; + } else { //Default: event->ignore(); return; @@ -1434,7 +1706,7 @@ void PianoRoll::execDeliveredScript(int id) { QString scriptfile = MusEGlobal::song->getScriptPath(id, true); - MusEGlobal::song->executeScript(scriptfile.toAscii().data(), parts(), raster(), true); + MusEGlobal::song->executeScript(this, scriptfile.toLatin1().data(), parts(), raster(), true); } //--------------------------------------------------------- @@ -1443,7 +1715,7 @@ void PianoRoll::execUserScript(int id) { QString scriptfile = MusEGlobal::song->getScriptPath(id, false); - MusEGlobal::song->executeScript(scriptfile.toAscii().data(), parts(), raster(), true); + MusEGlobal::song->executeScript(this, scriptfile.toLatin1().data(), parts(), raster(), true); } //--------------------------------------------------------- @@ -1474,9 +1746,12 @@ void PianoRoll::toggleTrackInfo() { - bool vis = midiTrackInfo->isVisible(); - infoScroll->setVisible(!vis); - infoScroll->setEnabled(!vis); +// bool vis = (midiTrackInfo && midiTrackInfo->isVisible()) || (midiStrip && midiStrip->isVisible()); +// if(infoScroll) +// { +// infoScroll->setVisible(!vis); +// infoScroll->setEnabled(!vis); +// } } } // namespace MusEGui diff -Nru muse-2.1.2/muse/midiedit/pianoroll.h muse-3.0.2+ds1/muse/midiedit/pianoroll.h --- muse-2.1.2/muse/midiedit/pianoroll.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/pianoroll.h 2017-12-04 21:01:18.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // $Id: pianoroll.h,v 1.5.2.4 2009/11/16 11:29:33 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012-2016 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,11 +24,6 @@ #ifndef __PIANOROLL_H__ #define __PIANOROLL_H__ -#include -#include -#include -#include - #include #include "type_defs.h" #include "noteinfo.h" @@ -38,16 +33,23 @@ #include "event.h" #include "midictrl.h" +// Whether to show an additional 'Alt' button beside +// the trackinfo button, to show the old midi trackinfo panel. +// TODO: TO BE REMOVED. The midi trackinfo panel is obsolete and disabled now. +//#define _USE_TRACKINFO_ALT 1; + +class QSplitter; class QAction; -class QLabel; class QMenu; class QPushButton; -class QScrollArea; -class QScrollBar; class QToolBar; class QToolButton; class QWidget; class QPoint; +class QHBoxLayout; +class QResizeEvent; +class QCloseEvent; +class QKeyEvent; namespace MusECore { class MidiPart; @@ -59,8 +61,9 @@ namespace MusEGui { + +class MidiStrip; class CtrlEdit; -class MidiTrackInfo; class PianoCanvas; class PitchLabel; class SNode; @@ -69,6 +72,11 @@ class TimeLabel; class Toolbar1; class Piano; +class TrackInfoToolBar; +class TrackInfoWidget; +#ifdef _USE_TRACKINFO_ALT +class MidiTrackInfo; +#endif //--------------------------------------------------------- // PianoRoll @@ -78,7 +86,12 @@ Q_OBJECT QMenu *menuEdit, *menuFunctions, *menuSelect, *menuConfig, *eventColor, *menuPlugins; + MusEGui::MidiStrip *midiStrip; +#ifdef _USE_TRACKINFO_ALT MusEGui::MidiTrackInfo *midiTrackInfo; +#endif + TrackInfoWidget* trackInfoWidget; + QWidget* noTrackInfo; MusECore::Track* selected; QAction* editCutAction; @@ -139,22 +152,25 @@ QToolButton* speaker; QToolBar* tools; MusEGui::EditToolBar* tools2; + TrackInfoToolBar* trackInfoBar; int colorMode; static int _rasterInit; - + static int _trackInfoWidthInit; + static int _canvasWidthInit; static int colorModeInit; bool _playEvents; - QScrollArea* infoScroll; - - void initShortcuts(); void setupNewCtrl(CtrlEdit* ctrlEdit); void setEventColorMode(int); QWidget* genToolbar(QWidget* parent); + void genTrackInfo(TrackInfoWidget* trackInfo); + void switchInfo(int); + void trackInfoSongChange(MusECore::SongChangedFlags_t flags); + virtual void closeEvent(QCloseEvent*); virtual void keyPressEvent(QKeyEvent*); @@ -176,7 +192,7 @@ void configChanged(); void newCanvasWidth(int); void toggleTrackInfo(); - void updateTrackInfo(); + void updateTrackInfo(MusECore::SongChangedFlags_t); void deltaModeChanged(bool); void addCtrlClicked(); void ctrlPopupTriggered(QAction* act); diff -Nru muse-2.1.2/muse/midiedit/prcanvas.cpp muse-3.0.2+ds1/muse/midiedit/prcanvas.cpp --- muse-2.1.2/muse/midiedit/prcanvas.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/prcanvas.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -50,6 +50,7 @@ #include "audio.h" #include "functions.h" #include "gconfig.h" +#include "helper.h" #define CHORD_TIMEOUT 75 @@ -59,7 +60,7 @@ // NEvent //--------------------------------------------------------- -NEvent::NEvent(MusECore::Event& e, MusECore::Part* p, int y) : MusEGui::CItem(e, p) +NEvent::NEvent(const MusECore::Event& e, MusECore::Part* p, int y) : MusEGui::CItem(e, p) { y = y - KH/4; unsigned tick = e.tick() + p->tick(); @@ -72,7 +73,7 @@ // addItem //--------------------------------------------------------- -CItem* PianoCanvas::addItem(MusECore::Part* part, MusECore::Event& event) +CItem* PianoCanvas::addItem(MusECore::Part* part, const MusECore::Event& event) { if (signed(event.tick())<0) { printf("ERROR: trying to add event before current part!\n"); @@ -99,6 +100,7 @@ { colorMode = 0; for (int i=0;i<128;i++) noteHeldDown[i]=false; + supportsResizeToTheLeft = true; steprec=new MusECore::StepRec(noteHeldDown); @@ -245,8 +247,8 @@ int meh = mer.height(); color.setAlpha(MusEGlobal::config.globalAlphaBlend); QBrush brush(color); - p.fillRect(mr, brush); - + p.fillRect(mr, brush); + if(mex >= mx && mex <= mx + mw) p.drawLine(mex, my, mex, my + mh - 1); // The left edge if(mex + mew >= mx && mex + mew <= mx + mw) @@ -255,7 +257,29 @@ p.drawLine(mx, mey, mx + mw - 1, mey); // The top edge if(mey + meh >= my && mey + meh <= my + mh) p.drawLine(mx, mey + meh - 1, mx + mw - 1, mey + meh - 1); // The bottom edge - + + // print note name on the drawn notes + if (MusEGlobal::config.showNoteNamesInPianoRoll) { + QFont f(MusEGlobal::config.fonts[1]); + + f.setPointSize(f.pointSize() * 0.85); + p.setFont(f); + + if (color.lightnessF() > 0.6f) { + + p.setPen(Qt::black); + + } else { + + p.setPen(Qt::white); + + } + QString noteStr = MusECore::pitch2string(event.pitch()); + + p.drawText(mer,Qt::AlignHCenter|Qt::AlignCenter, noteStr.toUpper()); + } + + p.setWorldMatrixEnabled(wmtxen); } @@ -430,7 +454,7 @@ NEvent* nevent = (NEvent*) item; MusECore::Event event = nevent->event(); int npitch = y2pitch(pos.y()); - MusECore::Event newEvent = event.clone(); + MusECore::Event newEvent = (dtype == MOVE_COPY) ? event.duplicate() : event.clone(); int x = pos.x(); if (x < 0) x = 0; @@ -504,6 +528,7 @@ event.setLenTick(w); event.setPitch(y2pitch(item->y())); + event.setSelected(true); MusECore::Undo operations; int diff = event.endTick()-part->lenTick(); @@ -521,7 +546,7 @@ MusEGlobal::song->applyOperationGroup(operations); } else // forbid action by not applying it - songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is neccessary + songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is necessary //to remove "forbidden" events from the list again } @@ -529,7 +554,7 @@ // resizeItem //--------------------------------------------------------- -void PianoCanvas::resizeItem(MusEGui::CItem* item, bool noSnap, bool) // experimental changes to try dynamically extending parts +void PianoCanvas::resizeItem(MusEGui::CItem* item, bool noSnap, bool rasterize) // experimental changes to try dynamically extending parts { NEvent* nevent = (NEvent*) item; MusECore::Event event = nevent->event(); @@ -550,6 +575,15 @@ MusECore::Undo operations; int diff = event.tick()+len-part->lenTick(); + if((nevent->mp() != nevent->pos()) && (resizeDirection == RESIZE_TO_THE_LEFT)) + { + int x = nevent->mp().x(); + int ntick = (rasterize ? editor->rasterVal(x) : x) - part->tick(); + if (ntick < 0) + ntick = 0; + newEvent.setTick(ntick); + } + if (! ((diff > 0) && part->hasHiddenEvents()) ) //operation is allowed { newEvent.setLenTick(len); @@ -561,9 +595,10 @@ printf("resizeItem: extending\n"); } } + //else forbid action by not performing it MusEGlobal::song->applyOperationGroup(operations); - songChanged(SC_EVENT_MODIFIED); //this forces an update of the itemlist, which is neccessary + songChanged(SC_EVENT_MODIFIED); //this forces an update of the itemlist, which is necessary //to remove "forbidden" events from the list again } @@ -635,11 +670,10 @@ if (part == 0) break; - MusECore::EventList* el = part->events(); MusECore::Undo operations; std::list elist; - for (MusECore::iEvent e = el->lower_bound(pos[0] - part->tick()); e != el->end(); ++e) + for (MusECore::ciEvent e = part->events().lower_bound(pos[0] - part->tick()); e != part->events().end(); ++e) elist.push_back((MusECore::Event)e->second); for (std::list::iterator i = elist.begin(); i != elist.end(); ++i) { MusECore::Event event = *i; @@ -663,10 +697,8 @@ break; MusECore::Undo operations; - MusECore::EventList* el = part->events(); - std::list elist; - for (MusECore::iEvent e = el->lower_bound(pos[0]); e != el->end(); ++e) + for (MusECore::ciEvent e = part->events().lower_bound(pos[0]); e != part->events().end(); ++e) elist.push_back((MusECore::Event)e->second); for (std::list::iterator i = elist.begin(); i != elist.end(); ++i) { MusECore::Event event = *i; @@ -689,6 +721,12 @@ void PianoCanvas::pianoPressed(int pitch, int velocity, bool shift) { + // REMOVE Tim. Noteoff. Added. Zero note on vel is not allowed now. + if(velocity > 127) + velocity = 127; + else if(velocity <= 0) + velocity = 1; + // play note: if(_playEvents) startPlayEvent(pitch, velocity); @@ -887,23 +925,24 @@ // startDrag //--------------------------------------------------------- -void PianoCanvas::startDrag(MusEGui::CItem* /* item*/, bool copymode) - { - QMimeData* md = selected_events_to_mime(partlist_to_set(editor->parts()), 1); - - if (md) { - // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. - // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can - // clean up after the drag and drop operation has been completed. " - QDrag* drag = new QDrag(this); - drag->setMimeData(md); - - if (copymode) - drag->exec(Qt::CopyAction); - else - drag->exec(Qt::MoveAction); - } - } +void PianoCanvas::startDrag(MusEGui::CItem* /* item*/, DragType t) +{ + QMimeData* md = selected_events_to_mime(partlist_to_set(editor->parts()), 1); + + if (md) + { + // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. + // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can + // clean up after the drag and drop operation has been completed. " + QDrag* drag = new QDrag(this); + drag->setMimeData(md); + + if (t == MOVE_COPY || t == MOVE_CLONE) + drag->exec(Qt::CopyAction); + else + drag->exec(Qt::MoveAction); + } +} //--------------------------------------------------------- // dragEnterEvent @@ -961,17 +1000,30 @@ void PianoCanvas::itemMoved(const MusEGui::CItem* item, const QPoint& pos) { int npitch = y2pitch(pos.y()); - if ((playedPitch != -1) && (playedPitch != npitch)) { - NEvent* nevent = (NEvent*) item; - MusECore::Event event = nevent->event(); - // release note: - stopPlayEvent(); - if (moving.size() <= 1) { // items moving or curItem - // play note: - startPlayEvent(npitch, event.velo()); - } + if(!track()) + { + // Stop any playing notes: + stopPlayEvent(); + return; + } + const int port = track()->outPort(); + const int channel = track()->outChannel(); + // Ignore if the note is already playing. + if(stuckNoteExists(port, channel, npitch)) + return; + + // Stop any playing notes: + stopPlayEvent(); + + if(_playEvents) + { + if (moving.size() <= 1) { // items moving or curItem + const MusECore::Event e = ((NEvent*)item)->event(); + // play note: + startPlayEvent(npitch, e.velo()); } } + } //--------------------------------------------------------- // curPartChanged @@ -989,9 +1041,9 @@ void PianoCanvas::modifySelected(MusEGui::NoteInfo::ValType type, int val, bool delta_mode) { - QList< QPair > already_done; - MusEGlobal::audio->msgIdle(true); - MusEGlobal::song->startUndo(); + QList< QPair > already_done; + MusECore::Undo operations; + for (MusEGui::iCItem i = items.begin(); i != items.end(); ++i) { if (!(i->second->isSelected())) continue; @@ -1002,7 +1054,7 @@ MusECore::MidiPart* part = (MusECore::MidiPart*)(e->part()); - if (already_done.contains(QPair(part->events(), event))) + if (already_done.contains(QPair(part->clonemaster_sn(), event))) continue; MusECore::Event newEvent = event.clone(); @@ -1038,7 +1090,9 @@ if (velo > 127) velo = 127; else if (velo < 0) - velo = 0; + // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now. +// velo = 0; + velo = 1; newEvent.setVelo(velo); } break; @@ -1067,15 +1121,10 @@ } break; } - - MusEGlobal::song->changeEvent(event, newEvent, part); - // Indicate do not do port controller values and clone parts. - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); - - already_done.append(QPair(part->events(), event)); + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); + already_done.append(QPair(part->clonemaster_sn(), event)); } - MusEGlobal::song->endUndo(SC_EVENT_MODIFIED); - MusEGlobal::audio->msgIdle(false); + MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/midiedit/prcanvas.h muse-3.0.2+ds1/muse/midiedit/prcanvas.h --- muse-2.1.2/muse/midiedit/prcanvas.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/prcanvas.h 2017-12-04 21:01:18.000000000 +0000 @@ -46,7 +46,7 @@ class NEvent : public CItem { public: - NEvent(MusECore::Event& e, MusECore::Part* p, int y); + NEvent(const MusECore::Event& e, MusECore::Part* p, int y); }; class ScrollScale; @@ -76,11 +76,11 @@ virtual void resizeItem(CItem*, bool noSnap, bool); virtual void newItem(CItem*, bool noSnap); virtual bool deleteItem(CItem*); - virtual void startDrag(CItem* item, bool copymode); + virtual void startDrag(CItem* item, DragType t); virtual void dragEnterEvent(QDragEnterEvent* event); virtual void dragMoveEvent(QDragMoveEvent*); virtual void dragLeaveEvent(QDragLeaveEvent*); - virtual CItem* addItem(MusECore::Part*, MusECore::Event&); + virtual CItem* addItem(MusECore::Part*, const MusECore::Event&); int y2pitch(int) const; int pitch2y(int) const; diff -Nru muse-2.1.2/muse/midiedit/scoreedit.cpp muse-3.0.2+ds1/muse/midiedit/scoreedit.cpp --- muse-2.1.2/muse/midiedit/scoreedit.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/scoreedit.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -73,6 +73,8 @@ using MusEGlobal::debugMsg; using MusEGlobal::heavyDebugMsg; +using MusECore::UndoOp; +using MusECore::Undo; namespace MusEGui { @@ -135,12 +137,12 @@ QString create_random_string(int len=8) { - string result; - - for (int i=0;isetLayout(mainGrid); - mainGrid = new QGridLayout(); - mainw->setLayout(mainGrid); + mainGrid->setContentsMargins(0, 0, 0, 0); + mainGrid->setSpacing(0); + setCentralWidget(mainw); + + + apply_velo=false; + + + score_canvas=new ScoreCanvas(this, mainw); + xscroll = new QScrollBar(Qt::Horizontal, mainw); + yscroll = new QScrollBar(Qt::Vertical, mainw); + time_bar = new MusEGui::MTScaleFlo(score_canvas, mainw); + + connect(xscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(x_scroll_event(int))); + connect(score_canvas, SIGNAL(xscroll_changed(int)), xscroll, SLOT(setValue(int))); + + connect(yscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(y_scroll_event(int))); + connect(score_canvas, SIGNAL(yscroll_changed(int)), yscroll, SLOT(setValue(int))); + + connect(score_canvas, SIGNAL(canvas_width_changed(int)), SLOT(canvas_width_changed(int))); + connect(score_canvas, SIGNAL(viewport_width_changed(int)), SLOT(viewport_width_changed(int))); + connect(score_canvas, SIGNAL(canvas_height_changed(int)), SLOT(canvas_height_changed(int))); + connect(score_canvas, SIGNAL(viewport_height_changed(int)), SLOT(viewport_height_changed(int))); + + connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), score_canvas, SLOT(song_changed(MusECore::SongChangedFlags_t))); + + connect(xscroll, SIGNAL(valueChanged(int)), time_bar, SLOT(set_xpos(int))); + connect(score_canvas, SIGNAL(pos_add_changed()), time_bar, SLOT(pos_add_changed())); + connect(score_canvas, SIGNAL(preamble_width_changed(int)), time_bar, SLOT(set_xoffset(int))); + + mainGrid->addWidget(time_bar, 0,0); + mainGrid->addWidget(score_canvas, 1,0); + mainGrid->addWidget(xscroll,2,0); + mainGrid->addWidget(yscroll,1,1); + + xscroll->setMinimum(0); + yscroll->setMinimum(0); + xscroll->setValue(0); + yscroll->setValue(0); + + menu_mapper=new QSignalMapper(this); + connect(menu_mapper, SIGNAL(mapped(int)), SLOT(menu_command(int))); + + + + // Toolbars --------------------------------------------------------- + + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + + addToolBarBreak(); + + edit_tools = new MusEGui::EditToolBar(this, MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool); + addToolBar(edit_tools); + edit_tools->set(MusEGui::PointerTool); + score_canvas->set_tool(MusEGui::PointerTool); + connect(edit_tools, SIGNAL(toolChanged(int)), score_canvas, SLOT(set_tool(int))); + + QToolBar* steprec_tools=addToolBar(tr("Step recording tools")); + steprec_tools->setObjectName("Score tools"); + srec = new QToolButton(); + srec->setToolTip(tr("Step Record")); + srec->setIcon(*steprecIcon); + srec->setCheckable(true); + srec->setFocusPolicy(Qt::NoFocus); + steprec_tools->addWidget(srec); + connect(srec, SIGNAL(toggled(bool)), score_canvas, SLOT(set_steprec(bool))); + + QToolBar* quant_toolbar = addToolBar(tr("Quantisation settings")); + quant_toolbar->setObjectName("Score quantisation toolbar"); + quant_toolbar->addWidget(new QLabel(tr("Quantisation:"), quant_toolbar)); + quant_combobox = new QComboBox(this); + quant_combobox->addItem("2"); // if you add or remove items from + quant_combobox->addItem("4"); // here, also change all code regarding + quant_combobox->addItem("8"); // _quant_power2 and _quant_power2_init + quant_combobox->addItem("16"); // and MAX_QUANT_POWER (must be log2(largest_value)) + quant_combobox->addItem("32"); + quant_combobox->setFocusPolicy(Qt::TabFocus); + quant_combobox->setCurrentIndex(score_canvas->quant_power2()-1); + // the above is intendedly executed BEFORE connecting. otherwise this would + // destroy pixels_per_whole_init! + //connect(quant_combobox, SIGNAL(currentIndexChanged(int)), score_canvas, SLOT(set_quant(int))); + connect(quant_combobox, SIGNAL(activated(int)), SLOT(quant_combobox_changed(int))); // Tim + quant_toolbar->addWidget(quant_combobox); + + + quant_toolbar->addSeparator(); + + quant_toolbar->addWidget(new QLabel(tr("Pixels per whole:"), quant_toolbar)); + px_per_whole_spinbox = new SpinBox(this); + px_per_whole_spinbox->setFocusPolicy(Qt::StrongFocus); + px_per_whole_spinbox->setRange(10, 1200); + px_per_whole_spinbox->setSingleStep(50); + connect(px_per_whole_spinbox, SIGNAL(valueChanged(int)), score_canvas, SLOT(set_pixels_per_whole(int))); + connect(score_canvas, SIGNAL(pixels_per_whole_changed(int)), px_per_whole_spinbox, SLOT(setValue(int))); + connect(px_per_whole_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas())); + connect(px_per_whole_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas())); + quant_toolbar->addWidget(px_per_whole_spinbox); + px_per_whole_spinbox->setValue(ScoreCanvas::_pixels_per_whole_init); + + addToolBarBreak(); + + QToolBar* note_settings_toolbar = addToolBar(tr("Note settings")); + //don't change that name, or you will lose toolbar settings + note_settings_toolbar->setObjectName("New note settings"); + note_settings_toolbar->addWidget(new QLabel(tr("Note length:"), note_settings_toolbar)); + len_actions=new QActionGroup(this); + n1_action = note_settings_toolbar->addAction("1", menu_mapper, SLOT(map())); + n2_action = note_settings_toolbar->addAction("2", menu_mapper, SLOT(map())); + n4_action = note_settings_toolbar->addAction("4", menu_mapper, SLOT(map())); + n8_action = note_settings_toolbar->addAction("8", menu_mapper, SLOT(map())); + n16_action = note_settings_toolbar->addAction("16", menu_mapper, SLOT(map())); + n32_action = note_settings_toolbar->addAction("32", menu_mapper, SLOT(map())); + nlast_action = note_settings_toolbar->addAction(tr("last"), menu_mapper, SLOT(map())); + menu_mapper->setMapping(n1_action, CMD_NOTELEN_1); + menu_mapper->setMapping(n2_action, CMD_NOTELEN_2); + menu_mapper->setMapping(n4_action, CMD_NOTELEN_4); + menu_mapper->setMapping(n8_action, CMD_NOTELEN_8); + menu_mapper->setMapping(n16_action, CMD_NOTELEN_16); + menu_mapper->setMapping(n32_action, CMD_NOTELEN_32); + menu_mapper->setMapping(nlast_action, CMD_NOTELEN_LAST); + n1_action->setCheckable(true); + n2_action->setCheckable(true); + n4_action->setCheckable(true); + n8_action->setCheckable(true); + n16_action->setCheckable(true); + n32_action->setCheckable(true); + nlast_action->setCheckable(true); + len_actions->addAction(n1_action); + len_actions->addAction(n2_action); + len_actions->addAction(n4_action); + len_actions->addAction(n8_action); + len_actions->addAction(n16_action); + len_actions->addAction(n32_action); + len_actions->addAction(nlast_action); + + switch (ScoreCanvas::new_len_init) + { + case 0: nlast_action->setChecked(true); menu_command(CMD_NOTELEN_LAST); break; + case 1: n1_action->setChecked(true); menu_command(CMD_NOTELEN_1); break; + case 2: n2_action->setChecked(true); menu_command(CMD_NOTELEN_2); break; + case 4: n4_action->setChecked(true); menu_command(CMD_NOTELEN_4); break; + case 8: n8_action->setChecked(true); menu_command(CMD_NOTELEN_8); break; + case 16: n16_action->setChecked(true); menu_command(CMD_NOTELEN_16); break; + case 32: n32_action->setChecked(true); menu_command(CMD_NOTELEN_32); break; + default: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN. newLen is invalid in ScoreEdit::ScoreEdit.\n" << + " (newLen="<setChecked(true); + menu_command(CMD_NOTELEN_LAST); + } + + note_settings_toolbar->addSeparator(); + + apply_velo_to_label = new QLabel(tr("Apply to new notes:"), note_settings_toolbar); + int w1 = apply_velo_to_label->fontMetrics().width(tr("Apply to new notes:")); + int w2 = apply_velo_to_label->fontMetrics().width(tr("Apply to selected notes:")); + if (w1>w2) + apply_velo_to_label->setFixedWidth(w1+5); + else + apply_velo_to_label->setFixedWidth(w2+5); + + note_settings_toolbar->addWidget(apply_velo_to_label); + note_settings_toolbar->addWidget(new QLabel(tr("Velocity:"), note_settings_toolbar)); + velo_spinbox = new SpinBox(this); + velo_spinbox->setRange(0, 127); + velo_spinbox->setSingleStep(1); + //we do not use the valueChanged signal, because that would generate + //many many undos when using the spin buttons. + connect(velo_spinbox, SIGNAL(editingFinished()), SLOT(velo_box_changed())); + connect(this,SIGNAL(velo_changed(int)), score_canvas, SLOT(set_velo(int))); + connect(velo_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas())); + connect(velo_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas())); + note_settings_toolbar->addWidget(velo_spinbox); + velo_spinbox->setValue(ScoreCanvas::note_velo_init); + + note_settings_toolbar->addWidget(new QLabel(tr("Off-Velocity:"), note_settings_toolbar)); + velo_off_spinbox = new SpinBox(this); + velo_off_spinbox->setRange(0, 127); + velo_off_spinbox->setSingleStep(1); + //we do not use the valueChanged signal, because that would generate + //many many undos when using the spin buttons. + connect(velo_off_spinbox, SIGNAL(editingFinished()), SLOT(velo_off_box_changed())); + connect(this,SIGNAL(velo_off_changed(int)), score_canvas, SLOT(set_velo_off(int))); + connect(velo_off_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas())); + connect(velo_off_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas())); + note_settings_toolbar->addWidget(velo_off_spinbox); + velo_off_spinbox->setValue(ScoreCanvas::note_velo_off_init); + + + QMenu* edit_menu = menuBar()->addMenu(tr("&Edit")); + + edit_menu->addActions(MusEGlobal::undoRedo->actions()); + edit_menu->addSeparator(); + + cut_action = edit_menu->addAction(QIcon(*editcutIconSet), tr("C&ut")); + menu_mapper->setMapping(cut_action, CMD_CUT); + connect(cut_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + copy_action = edit_menu->addAction(QIcon(*editcopyIconSet), tr("&Copy")); + menu_mapper->setMapping(copy_action, CMD_COPY); + connect(copy_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + copy_range_action = edit_menu->addAction(QIcon(*editcopyIconSet), tr("Copy events in range")); + menu_mapper->setMapping(copy_range_action, CMD_COPY_RANGE); + connect(copy_range_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + paste_action = edit_menu->addAction(QIcon(*editpasteIconSet), tr("&Paste")); + menu_mapper->setMapping(paste_action, CMD_PASTE); + connect(paste_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + paste_dialog_action = edit_menu->addAction(QIcon(*editpasteIconSet), tr("Paste (with dialog)")); + menu_mapper->setMapping(paste_dialog_action, CMD_PASTE_DIALOG); + connect(paste_dialog_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + edit_menu->addSeparator(); + + del_action = edit_menu->addAction(tr("Delete &Events")); + menu_mapper->setMapping(del_action, CMD_DEL); + connect(del_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + edit_menu->addSeparator(); + + QMenu* select_menu = edit_menu->addMenu(QIcon(*selectIcon), tr("&Select")); + + select_all_action = select_menu->addAction(QIcon(*select_allIcon), tr("Select &All")); + menu_mapper->setMapping(select_all_action, CMD_SELECT_ALL); + connect(select_all_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_none_action = select_menu->addAction(QIcon(*select_deselect_allIcon), tr("&Deselect All")); + menu_mapper->setMapping(select_none_action, CMD_SELECT_NONE); + connect(select_none_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_invert_action = select_menu->addAction(QIcon(*select_invert_selectionIcon), tr("Invert &Selection")); + menu_mapper->setMapping(select_invert_action, CMD_SELECT_INVERT); + connect(select_invert_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_menu->addSeparator(); + + select_iloop_action = select_menu->addAction(QIcon(*select_inside_loopIcon), tr("&Inside Loop")); + menu_mapper->setMapping(select_iloop_action, CMD_SELECT_ILOOP); + connect(select_iloop_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + select_oloop_action = select_menu->addAction(QIcon(*select_outside_loopIcon), tr("&Outside Loop")); + menu_mapper->setMapping(select_oloop_action, CMD_SELECT_OLOOP); + connect(select_oloop_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); + + + QMenu* functions_menu = menuBar()->addMenu(tr("Fu&nctions")); + + func_quantize_action = functions_menu->addAction(tr("&Quantize"), menu_mapper, SLOT(map())); + func_notelen_action = functions_menu->addAction(tr("Change note &length"), menu_mapper, SLOT(map())); + func_velocity_action = functions_menu->addAction(tr("Change note &velocity"), menu_mapper, SLOT(map())); + func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo"), menu_mapper, SLOT(map())); + func_transpose_action = functions_menu->addAction(tr("Transpose"), menu_mapper, SLOT(map())); + func_erase_action = functions_menu->addAction(tr("Erase Events"), menu_mapper, SLOT(map())); + func_move_action = functions_menu->addAction(tr("Move Notes"), menu_mapper, SLOT(map())); + func_fixed_len_action = functions_menu->addAction(tr("Set Fixed Length"), menu_mapper, SLOT(map())); + func_del_overlaps_action = functions_menu->addAction(tr("Delete Overlaps"), menu_mapper, SLOT(map())); + func_legato_action = functions_menu->addAction(tr("Legato"), menu_mapper, SLOT(map())); + menu_mapper->setMapping(func_quantize_action, CMD_QUANTIZE); + menu_mapper->setMapping(func_notelen_action, CMD_NOTELEN); + menu_mapper->setMapping(func_velocity_action, CMD_VELOCITY); + menu_mapper->setMapping(func_cresc_action, CMD_CRESCENDO); + menu_mapper->setMapping(func_transpose_action, CMD_TRANSPOSE); + menu_mapper->setMapping(func_erase_action, CMD_ERASE); + menu_mapper->setMapping(func_move_action, CMD_MOVE); + menu_mapper->setMapping(func_fixed_len_action, CMD_FIXED_LEN); + menu_mapper->setMapping(func_del_overlaps_action, CMD_DELETE_OVERLAPS); + menu_mapper->setMapping(func_legato_action, CMD_LEGATO); + + + QMenu* settings_menu = menuBar()->addMenu(tr("Window &Config")); + + color_menu = settings_menu->addMenu(tr("Note head &colors")); + color_actions = new QActionGroup(this); + color_black_action = color_menu->addAction(tr("&Black"), menu_mapper, SLOT(map())); + color_velo_action = color_menu->addAction(tr("&Velocity"), menu_mapper, SLOT(map())); + color_part_action = color_menu->addAction(tr("&Part"), menu_mapper, SLOT(map())); + color_black_action->setCheckable(true); + color_velo_action->setCheckable(true); + color_part_action->setCheckable(true); + color_actions->addAction(color_black_action); + color_actions->addAction(color_velo_action); + color_actions->addAction(color_part_action); + menu_mapper->setMapping(color_black_action, CMD_COLOR_BLACK); + menu_mapper->setMapping(color_velo_action, CMD_COLOR_VELO); + menu_mapper->setMapping(color_part_action, CMD_COLOR_PART); + + switch (ScoreCanvas::coloring_mode_init) + { + case 0: color_black_action->setChecked(true); menu_command(CMD_COLOR_BLACK); break; + case 1: color_velo_action->setChecked(true); menu_command(CMD_COLOR_VELO); break; + case 2: color_part_action->setChecked(true); menu_command(CMD_COLOR_PART); break; + default: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN. noteColor is invalid in ScoreEdit::ScoreEdit.\n" << + " (noteColor="<setChecked(true); + menu_command(CMD_COLOR_BLACK); + } + + QMenu* preamble_menu = settings_menu->addMenu(tr("Set up &preamble")); + preamble_keysig_action = preamble_menu->addAction(tr("Display &key signature")); + preamble_timesig_action = preamble_menu->addAction(tr("Display &time signature")); + connect(preamble_keysig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_keysig_slot(bool))); + connect(preamble_timesig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_timesig_slot(bool))); + + preamble_keysig_action->setCheckable(true); + preamble_timesig_action->setCheckable(true); + + preamble_keysig_action->setChecked(ScoreCanvas::preamble_contains_keysig_init); + preamble_timesig_action->setChecked(ScoreCanvas::preamble_contains_timesig_init); + + QAction* set_name_action = settings_menu->addAction(tr("Set Score &name"), menu_mapper, SLOT(map())); + menu_mapper->setMapping(set_name_action, CMD_SET_NAME); + + settings_menu->addSeparator(); + settings_menu->addAction(subwinAction); + settings_menu->addAction(shareAction); + settings_menu->addAction(fullscreenAction); + + + init_shortcuts(); + + connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(init_shortcuts())); + + QClipboard* cb = QApplication::clipboard(); + connect(cb, SIGNAL(dataChanged()), SLOT(clipboard_changed())); + + clipboard_changed(); + selection_changed(); + + connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(song_changed(MusECore::SongChangedFlags_t))); + connect(MusEGlobal::song, SIGNAL(newPartsCreated(const std::map< const MusECore::Part*, std::set >&)), score_canvas, SLOT(add_new_parts(const std::map< const MusECore::Part*, std::set >&))); + + score_canvas->fully_recalculate(); + score_canvas->goto_tick(initPos,true); + + if (name!=NULL) + set_name(name, false, true); + else + init_name(); - mainGrid->setContentsMargins(0, 0, 0, 0); - mainGrid->setSpacing(0); - setCentralWidget(mainw); - - - apply_velo=false; - - - score_canvas=new ScoreCanvas(this, mainw); - xscroll = new QScrollBar(Qt::Horizontal, mainw); - yscroll = new QScrollBar(Qt::Vertical, mainw); - time_bar = new MusEGui::MTScaleFlo(score_canvas, mainw); - - connect(xscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(x_scroll_event(int))); - connect(score_canvas, SIGNAL(xscroll_changed(int)), xscroll, SLOT(setValue(int))); - - connect(yscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(y_scroll_event(int))); - connect(score_canvas, SIGNAL(yscroll_changed(int)), yscroll, SLOT(setValue(int))); - - connect(score_canvas, SIGNAL(canvas_width_changed(int)), SLOT(canvas_width_changed(int))); - connect(score_canvas, SIGNAL(viewport_width_changed(int)), SLOT(viewport_width_changed(int))); - connect(score_canvas, SIGNAL(canvas_height_changed(int)), SLOT(canvas_height_changed(int))); - connect(score_canvas, SIGNAL(viewport_height_changed(int)), SLOT(viewport_height_changed(int))); - - connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), score_canvas, SLOT(song_changed(MusECore::SongChangedFlags_t))); - - connect(xscroll, SIGNAL(valueChanged(int)), time_bar, SLOT(set_xpos(int))); - connect(score_canvas, SIGNAL(pos_add_changed()), time_bar, SLOT(pos_add_changed())); - connect(score_canvas, SIGNAL(preamble_width_changed(int)), time_bar, SLOT(set_xoffset(int))); - - mainGrid->addWidget(time_bar, 0,0); - mainGrid->addWidget(score_canvas, 1,0); - mainGrid->addWidget(xscroll,2,0); - mainGrid->addWidget(yscroll,1,1); - - xscroll->setMinimum(0); - yscroll->setMinimum(0); - xscroll->setValue(0); - yscroll->setValue(0); - - menu_mapper=new QSignalMapper(this); - connect(menu_mapper, SIGNAL(mapped(int)), SLOT(menu_command(int))); - - - - // Toolbars --------------------------------------------------------- - QToolBar* steprec_tools=addToolBar(tr("Step recording tools")); - steprec_tools->setObjectName("Step recording tools"); - srec = new QToolButton(); - srec->setToolTip(tr("Step Record")); - srec->setIcon(*steprecIcon); - srec->setCheckable(true); - srec->setFocusPolicy(Qt::NoFocus); - steprec_tools->addWidget(srec); - connect(srec, SIGNAL(toggled(bool)), score_canvas, SLOT(set_steprec(bool))); - - - edit_tools = new MusEGui::EditToolBar(this, MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool); - addToolBar(edit_tools); - edit_tools->set(MusEGui::PointerTool); - score_canvas->set_tool(MusEGui::PointerTool); - connect(edit_tools, SIGNAL(toolChanged(int)), score_canvas, SLOT(set_tool(int))); - - addToolBarBreak(); - - QToolBar* note_settings_toolbar = addToolBar(tr("Note settings")); - //don't change that name, or you will lose toolbar settings - note_settings_toolbar->setObjectName("New note settings"); - note_settings_toolbar->addWidget(new QLabel(tr("Note length:"), note_settings_toolbar)); - len_actions=new QActionGroup(this); - n1_action = note_settings_toolbar->addAction("1", menu_mapper, SLOT(map())); - n2_action = note_settings_toolbar->addAction("2", menu_mapper, SLOT(map())); - n4_action = note_settings_toolbar->addAction("4", menu_mapper, SLOT(map())); - n8_action = note_settings_toolbar->addAction("8", menu_mapper, SLOT(map())); - n16_action = note_settings_toolbar->addAction("16", menu_mapper, SLOT(map())); - n32_action = note_settings_toolbar->addAction("32", menu_mapper, SLOT(map())); - nlast_action = note_settings_toolbar->addAction(tr("last"), menu_mapper, SLOT(map())); - menu_mapper->setMapping(n1_action, CMD_NOTELEN_1); - menu_mapper->setMapping(n2_action, CMD_NOTELEN_2); - menu_mapper->setMapping(n4_action, CMD_NOTELEN_4); - menu_mapper->setMapping(n8_action, CMD_NOTELEN_8); - menu_mapper->setMapping(n16_action, CMD_NOTELEN_16); - menu_mapper->setMapping(n32_action, CMD_NOTELEN_32); - menu_mapper->setMapping(nlast_action, CMD_NOTELEN_LAST); - n1_action->setCheckable(true); - n2_action->setCheckable(true); - n4_action->setCheckable(true); - n8_action->setCheckable(true); - n16_action->setCheckable(true); - n32_action->setCheckable(true); - nlast_action->setCheckable(true); - len_actions->addAction(n1_action); - len_actions->addAction(n2_action); - len_actions->addAction(n4_action); - len_actions->addAction(n8_action); - len_actions->addAction(n16_action); - len_actions->addAction(n32_action); - len_actions->addAction(nlast_action); - - switch (ScoreCanvas::new_len_init) - { - case 0: nlast_action->setChecked(true); menu_command(CMD_NOTELEN_LAST); break; - case 1: n1_action->setChecked(true); menu_command(CMD_NOTELEN_1); break; - case 2: n2_action->setChecked(true); menu_command(CMD_NOTELEN_2); break; - case 4: n4_action->setChecked(true); menu_command(CMD_NOTELEN_4); break; - case 8: n8_action->setChecked(true); menu_command(CMD_NOTELEN_8); break; - case 16: n16_action->setChecked(true); menu_command(CMD_NOTELEN_16); break; - case 32: n32_action->setChecked(true); menu_command(CMD_NOTELEN_32); break; - default: - cerr << "ERROR: THIS SHOULD NEVER HAPPEN. newLen is invalid in ScoreEdit::ScoreEdit.\n" << - " (newLen="<setChecked(true); - menu_command(CMD_NOTELEN_LAST); - } - - note_settings_toolbar->addSeparator(); - - apply_velo_to_label = new QLabel(tr("Apply to new notes:"), note_settings_toolbar); - int w1 = apply_velo_to_label->fontMetrics().width(tr("Apply to new notes:")); - int w2 = apply_velo_to_label->fontMetrics().width(tr("Apply to selected notes:")); - if (w1>w2) - apply_velo_to_label->setFixedWidth(w1+5); - else - apply_velo_to_label->setFixedWidth(w2+5); - - note_settings_toolbar->addWidget(apply_velo_to_label); - note_settings_toolbar->addWidget(new QLabel(tr("Velocity:"), note_settings_toolbar)); - velo_spinbox = new SpinBox(this); - velo_spinbox->setRange(0, 127); - velo_spinbox->setSingleStep(1); - //we do not use the valueChanged signal, because that would generate - //many many undos when using the spin buttons. - connect(velo_spinbox, SIGNAL(editingFinished()), SLOT(velo_box_changed())); - connect(this,SIGNAL(velo_changed(int)), score_canvas, SLOT(set_velo(int))); - connect(velo_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas())); - connect(velo_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas())); - note_settings_toolbar->addWidget(velo_spinbox); - velo_spinbox->setValue(ScoreCanvas::note_velo_init); - - note_settings_toolbar->addWidget(new QLabel(tr("Off-Velocity:"), note_settings_toolbar)); - velo_off_spinbox = new SpinBox(this); - velo_off_spinbox->setRange(0, 127); - velo_off_spinbox->setSingleStep(1); - //we do not use the valueChanged signal, because that would generate - //many many undos when using the spin buttons. - connect(velo_off_spinbox, SIGNAL(editingFinished()), SLOT(velo_off_box_changed())); - connect(this,SIGNAL(velo_off_changed(int)), score_canvas, SLOT(set_velo_off(int))); - connect(velo_off_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas())); - connect(velo_off_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas())); - note_settings_toolbar->addWidget(velo_off_spinbox); - velo_off_spinbox->setValue(ScoreCanvas::note_velo_off_init); - - - - QToolBar* quant_toolbar = addToolBar(tr("Quantisation settings")); - quant_toolbar->setObjectName("Quantisation settings"); - quant_toolbar->addWidget(new QLabel(tr("Quantisation:"), quant_toolbar)); - quant_combobox = new QComboBox(this); - quant_combobox->addItem("2"); // if you add or remove items from - quant_combobox->addItem("4"); // here, also change all code regarding - quant_combobox->addItem("8"); // _quant_power2 and _quant_power2_init - quant_combobox->addItem("16"); // and MAX_QUANT_POWER (must be log2(largest_value)) - quant_combobox->addItem("32"); - quant_combobox->setFocusPolicy(Qt::TabFocus); - quant_combobox->setCurrentIndex(score_canvas->quant_power2()-1); - // the above is intendedly executed BEFORE connecting. otherwise this would - // destroy pixels_per_whole_init! - //connect(quant_combobox, SIGNAL(currentIndexChanged(int)), score_canvas, SLOT(set_quant(int))); - connect(quant_combobox, SIGNAL(activated(int)), SLOT(quant_combobox_changed(int))); // Tim - quant_toolbar->addWidget(quant_combobox); - - - quant_toolbar->addSeparator(); - - quant_toolbar->addWidget(new QLabel(tr("Pixels per whole:"), quant_toolbar)); - px_per_whole_spinbox = new SpinBox(this); - px_per_whole_spinbox->setFocusPolicy(Qt::StrongFocus); - px_per_whole_spinbox->setRange(10, 1200); - px_per_whole_spinbox->setSingleStep(50); - connect(px_per_whole_spinbox, SIGNAL(valueChanged(int)), score_canvas, SLOT(set_pixels_per_whole(int))); - connect(score_canvas, SIGNAL(pixels_per_whole_changed(int)), px_per_whole_spinbox, SLOT(setValue(int))); - connect(px_per_whole_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas())); - connect(px_per_whole_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas())); - quant_toolbar->addWidget(px_per_whole_spinbox); - px_per_whole_spinbox->setValue(ScoreCanvas::_pixels_per_whole_init); - - QMenu* edit_menu = menuBar()->addMenu(tr("&Edit")); - - edit_menu->addActions(MusEGlobal::undoRedo->actions()); - edit_menu->addSeparator(); - - cut_action = edit_menu->addAction(QIcon(*editcutIconSet), tr("C&ut")); - menu_mapper->setMapping(cut_action, CMD_CUT); - connect(cut_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - copy_action = edit_menu->addAction(QIcon(*editcopyIconSet), tr("&Copy")); - menu_mapper->setMapping(copy_action, CMD_COPY); - connect(copy_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - copy_range_action = edit_menu->addAction(QIcon(*editcopyIconSet), tr("Copy events in range")); - menu_mapper->setMapping(copy_range_action, CMD_COPY_RANGE); - connect(copy_range_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - paste_action = edit_menu->addAction(QIcon(*editpasteIconSet), tr("&Paste")); - menu_mapper->setMapping(paste_action, CMD_PASTE); - connect(paste_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - paste_dialog_action = edit_menu->addAction(QIcon(*editpasteIconSet), tr("Paste (with dialog)")); - menu_mapper->setMapping(paste_dialog_action, CMD_PASTE_DIALOG); - connect(paste_dialog_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - edit_menu->addSeparator(); - - del_action = edit_menu->addAction(tr("Delete &Events")); - menu_mapper->setMapping(del_action, CMD_DEL); - connect(del_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - edit_menu->addSeparator(); - - QMenu* select_menu = edit_menu->addMenu(QIcon(*selectIcon), tr("&Select")); - - select_all_action = select_menu->addAction(QIcon(*select_allIcon), tr("Select &All")); - menu_mapper->setMapping(select_all_action, CMD_SELECT_ALL); - connect(select_all_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - select_none_action = select_menu->addAction(QIcon(*select_deselect_allIcon), tr("&Deselect All")); - menu_mapper->setMapping(select_none_action, CMD_SELECT_NONE); - connect(select_none_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - select_invert_action = select_menu->addAction(QIcon(*select_invert_selectionIcon), tr("Invert &Selection")); - menu_mapper->setMapping(select_invert_action, CMD_SELECT_INVERT); - connect(select_invert_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - select_menu->addSeparator(); - - select_iloop_action = select_menu->addAction(QIcon(*select_inside_loopIcon), tr("&Inside Loop")); - menu_mapper->setMapping(select_iloop_action, CMD_SELECT_ILOOP); - connect(select_iloop_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - select_oloop_action = select_menu->addAction(QIcon(*select_outside_loopIcon), tr("&Outside Loop")); - menu_mapper->setMapping(select_oloop_action, CMD_SELECT_OLOOP); - connect(select_oloop_action, SIGNAL(triggered()), menu_mapper, SLOT(map())); - - - QMenu* functions_menu = menuBar()->addMenu(tr("Fu&nctions")); - - func_quantize_action = functions_menu->addAction(tr("&Quantize"), menu_mapper, SLOT(map())); - func_notelen_action = functions_menu->addAction(tr("Change note &length"), menu_mapper, SLOT(map())); - func_velocity_action = functions_menu->addAction(tr("Change note &velocity"), menu_mapper, SLOT(map())); - func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo"), menu_mapper, SLOT(map())); - func_transpose_action = functions_menu->addAction(tr("Transpose"), menu_mapper, SLOT(map())); - func_erase_action = functions_menu->addAction(tr("Erase Events"), menu_mapper, SLOT(map())); - func_move_action = functions_menu->addAction(tr("Move Notes"), menu_mapper, SLOT(map())); - func_fixed_len_action = functions_menu->addAction(tr("Set Fixed Length"), menu_mapper, SLOT(map())); - func_del_overlaps_action = functions_menu->addAction(tr("Delete Overlaps"), menu_mapper, SLOT(map())); - func_legato_action = functions_menu->addAction(tr("Legato"), menu_mapper, SLOT(map())); - menu_mapper->setMapping(func_quantize_action, CMD_QUANTIZE); - menu_mapper->setMapping(func_notelen_action, CMD_NOTELEN); - menu_mapper->setMapping(func_velocity_action, CMD_VELOCITY); - menu_mapper->setMapping(func_cresc_action, CMD_CRESCENDO); - menu_mapper->setMapping(func_transpose_action, CMD_TRANSPOSE); - menu_mapper->setMapping(func_erase_action, CMD_ERASE); - menu_mapper->setMapping(func_move_action, CMD_MOVE); - menu_mapper->setMapping(func_fixed_len_action, CMD_FIXED_LEN); - menu_mapper->setMapping(func_del_overlaps_action, CMD_DELETE_OVERLAPS); - menu_mapper->setMapping(func_legato_action, CMD_LEGATO); - - - QMenu* settings_menu = menuBar()->addMenu(tr("Window &Config")); - - color_menu = settings_menu->addMenu(tr("Note head &colors")); - color_actions = new QActionGroup(this); - color_black_action = color_menu->addAction(tr("&Black"), menu_mapper, SLOT(map())); - color_velo_action = color_menu->addAction(tr("&Velocity"), menu_mapper, SLOT(map())); - color_part_action = color_menu->addAction(tr("&Part"), menu_mapper, SLOT(map())); - color_black_action->setCheckable(true); - color_velo_action->setCheckable(true); - color_part_action->setCheckable(true); - color_actions->addAction(color_black_action); - color_actions->addAction(color_velo_action); - color_actions->addAction(color_part_action); - menu_mapper->setMapping(color_black_action, CMD_COLOR_BLACK); - menu_mapper->setMapping(color_velo_action, CMD_COLOR_VELO); - menu_mapper->setMapping(color_part_action, CMD_COLOR_PART); - - switch (ScoreCanvas::coloring_mode_init) - { - case 0: color_black_action->setChecked(true); menu_command(CMD_COLOR_BLACK); break; - case 1: color_velo_action->setChecked(true); menu_command(CMD_COLOR_VELO); break; - case 2: color_part_action->setChecked(true); menu_command(CMD_COLOR_PART); break; - default: - cerr << "ERROR: THIS SHOULD NEVER HAPPEN. noteColor is invalid in ScoreEdit::ScoreEdit.\n" << - " (noteColor="<setChecked(true); - menu_command(CMD_COLOR_BLACK); - } - - QMenu* preamble_menu = settings_menu->addMenu(tr("Set up &preamble")); - preamble_keysig_action = preamble_menu->addAction(tr("Display &key signature")); - preamble_timesig_action = preamble_menu->addAction(tr("Display &time signature")); - connect(preamble_keysig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_keysig_slot(bool))); - connect(preamble_timesig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_timesig_slot(bool))); - - preamble_keysig_action->setCheckable(true); - preamble_timesig_action->setCheckable(true); - - preamble_keysig_action->setChecked(ScoreCanvas::preamble_contains_keysig_init); - preamble_timesig_action->setChecked(ScoreCanvas::preamble_contains_timesig_init); - - QAction* set_name_action = settings_menu->addAction(tr("Set Score &name"), menu_mapper, SLOT(map())); - menu_mapper->setMapping(set_name_action, CMD_SET_NAME); - - settings_menu->addSeparator(); - settings_menu->addAction(subwinAction); - settings_menu->addAction(shareAction); - settings_menu->addAction(fullscreenAction); - - - init_shortcuts(); - - connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(init_shortcuts())); - - QClipboard* cb = QApplication::clipboard(); - connect(cb, SIGNAL(dataChanged()), SLOT(clipboard_changed())); - - clipboard_changed(); - selection_changed(); - - connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(song_changed(MusECore::SongChangedFlags_t))); - connect(MusEGlobal::song, SIGNAL(newPartsCreated(const std::map< MusECore::Part*, std::set >&)), score_canvas, SLOT(add_new_parts(const std::map< MusECore::Part*, std::set >&))); - - score_canvas->fully_recalculate(); - score_canvas->goto_tick(initPos,true); - - if (name!=NULL) - set_name(name, false, true); - else - init_name(); - - - apply_velo=true; - - initTopwinState(); - finalizeInit(); + + apply_velo=true; + + initTopwinState(); + finalizeInit(); } void ScoreEdit::init_shortcuts() { - cut_action->setShortcut(shortcuts[SHRT_CUT].key); - copy_action->setShortcut(shortcuts[SHRT_COPY].key); - copy_range_action->setShortcut(shortcuts[SHRT_COPY_RANGE].key); - paste_action->setShortcut(shortcuts[SHRT_PASTE].key); - paste_dialog_action->setShortcut(shortcuts[SHRT_PASTE_DIALOG].key); - del_action->setShortcut(shortcuts[SHRT_DELETE].key); - - select_all_action->setShortcut(shortcuts[SHRT_SELECT_ALL].key); - select_none_action->setShortcut(shortcuts[SHRT_SELECT_NONE].key); - select_invert_action->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); - select_iloop_action->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); - select_oloop_action->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); - - color_menu->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key); - - func_quantize_action->setShortcut(shortcuts[SHRT_QUANTIZE].key); - func_notelen_action->setShortcut(shortcuts[SHRT_MODIFY_GATE_TIME].key); - func_velocity_action->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key); - func_transpose_action->setShortcut(shortcuts[SHRT_TRANSPOSE].key); - func_erase_action->setShortcut(shortcuts[SHRT_ERASE_EVENT].key); - func_move_action->setShortcut(shortcuts[SHRT_NOTE_SHIFT].key); - func_fixed_len_action->setShortcut(shortcuts[SHRT_FIXED_LEN].key); - func_del_overlaps_action->setShortcut(shortcuts[SHRT_DELETE_OVERLAPS].key); + cut_action->setShortcut(shortcuts[SHRT_CUT].key); + copy_action->setShortcut(shortcuts[SHRT_COPY].key); + copy_range_action->setShortcut(shortcuts[SHRT_COPY_RANGE].key); + paste_action->setShortcut(shortcuts[SHRT_PASTE].key); + paste_dialog_action->setShortcut(shortcuts[SHRT_PASTE_DIALOG].key); + del_action->setShortcut(shortcuts[SHRT_DELETE].key); + + select_all_action->setShortcut(shortcuts[SHRT_SELECT_ALL].key); + select_none_action->setShortcut(shortcuts[SHRT_SELECT_NONE].key); + select_invert_action->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); + select_iloop_action->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); + select_oloop_action->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); + + color_menu->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key); + + func_quantize_action->setShortcut(shortcuts[SHRT_QUANTIZE].key); + func_notelen_action->setShortcut(shortcuts[SHRT_MODIFY_GATE_TIME].key); + func_velocity_action->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key); + func_transpose_action->setShortcut(shortcuts[SHRT_TRANSPOSE].key); + func_erase_action->setShortcut(shortcuts[SHRT_ERASE_EVENT].key); + func_move_action->setShortcut(shortcuts[SHRT_NOTE_SHIFT].key); + func_fixed_len_action->setShortcut(shortcuts[SHRT_FIXED_LEN].key); + func_del_overlaps_action->setShortcut(shortcuts[SHRT_DELETE_OVERLAPS].key); } void ScoreEdit::add_parts(MusECore::PartList* pl, bool all_in_one) { - score_canvas->add_staves(pl, all_in_one); + score_canvas->add_staves(pl, all_in_one); } void ScoreEdit::init_name() { - int no=1; - QString temp; - - while (true) - { - temp="Score "+IntToQStr(no); - if (set_name(temp, false, false)) - break; - else - no++; - } + int no=1; + QString temp; + + while (true) + { + temp="Score "+IntToQStr(no); + if (set_name(temp, false, false)) + break; + else + no++; + } } bool ScoreEdit::set_name(QString newname, bool emit_signal, bool emergency_name) { - if (names.find(newname)==names.end()) - { - names.erase(name); - names.insert(newname); - - name=newname; - - setWindowTitle("MusE: Score \""+name+"\""); - - if (emit_signal) - emit name_changed(); - - return true; - } - else - { - if (emergency_name) - { - while (set_name(create_random_string(), emit_signal, false) == false) ; - return true; - } - else - return false; - } + if (names.find(newname)==names.end()) + { + names.erase(name); + names.insert(newname); + + name=newname; + + setWindowTitle("MusE: Score \""+name+"\""); + + if (emit_signal) + emit name_changed(); + + return true; + } + else + { + if (emergency_name) + { + while (set_name(create_random_string(), emit_signal, false) == false) ; + return true; + } + else + return false; + } } //--------------------------------------------------------- @@ -618,297 +628,297 @@ ScoreEdit::~ScoreEdit() { - names.erase(name); + names.erase(name); } void ScoreEdit::focusCanvas() { - if(MusEGlobal::config.smartFocus) - { - score_canvas->setFocus(); - score_canvas->activateWindow(); - } + if(MusEGlobal::config.smartFocus) + { + score_canvas->setFocus(); + score_canvas->activateWindow(); + } } void ScoreEdit::velo_box_changed() { - emit velo_changed(velo_spinbox->value()); + emit velo_changed(velo_spinbox->value()); } void ScoreEdit::velo_off_box_changed() { - emit velo_off_changed(velo_off_spinbox->value()); + emit velo_off_changed(velo_off_spinbox->value()); } void ScoreEdit::quant_combobox_changed(int idx) { - score_canvas->set_quant(idx); - focusCanvas(); + score_canvas->set_quant(idx); + focusCanvas(); } void ScoreEdit::song_changed(MusECore::SongChangedFlags_t flags) { - if(_isDeleting) // Ignore while while deleting to prevent crash. - return; - - if (flags & (SC_SELECTION | SC_EVENT_MODIFIED | SC_EVENT_REMOVED)) - { - map selection=get_events(score_canvas->get_all_parts(),1); - if (selection.empty()) - { - apply_velo_to_label->setText(tr("Apply to new notes:")); - } - else - { - apply_velo_to_label->setText(tr("Apply to selected notes:")); - - int velo=-1; - int velo_off=-1; - for (map::iterator it=selection.begin(); it!=selection.end(); it++) - if (it->first->type()==MusECore::Note) - { - if (velo==-1) velo=it->first->velo(); - else if ((velo>=0) && (velo!=it->first->velo())) velo=-2; - - if (velo_off==-1) velo_off=it->first->veloOff(); - else if ((velo_off>=0) && (velo_off!=it->first->veloOff())) velo_off=-2; - } - - if (velo>=0) velo_spinbox->setValue(velo); - if (velo_off>=0) velo_off_spinbox->setValue(velo_off); - } - - selection_changed(); - } + if(_isDeleting) // Ignore while while deleting to prevent crash. + return; + + if (flags & (SC_SELECTION | SC_EVENT_MODIFIED | SC_EVENT_REMOVED)) + { + map selection=get_events(score_canvas->get_all_parts(),1); + if (selection.empty()) + { + apply_velo_to_label->setText(tr("Apply to new notes:")); + } + else + { + apply_velo_to_label->setText(tr("Apply to selected notes:")); + + int velo=-1; + int velo_off=-1; + for (map::iterator it=selection.begin(); it!=selection.end(); it++) + if (it->first->type()==MusECore::Note) + { + if (velo==-1) velo=it->first->velo(); + else if ((velo>=0) && (velo!=it->first->velo())) velo=-2; + + if (velo_off==-1) velo_off=it->first->veloOff(); + else if ((velo_off>=0) && (velo_off!=it->first->veloOff())) velo_off=-2; + } + + if (velo>=0) velo_spinbox->setValue(velo); + if (velo_off>=0) velo_off_spinbox->setValue(velo_off); + } + + selection_changed(); + } } void ScoreEdit::canvas_width_changed(int width) { - xscroll->setMaximum(width); + xscroll->setMaximum(width); } void ScoreEdit::viewport_width_changed(int width) { - xscroll->setPageStep(width * PAGESTEP); + xscroll->setPageStep(width * PAGESTEP); } void ScoreEdit::canvas_height_changed(int height) { - int val=height - score_canvas->viewport_height(); - if (val<=0) val=0; - - yscroll->setMaximum(val); - - if (val==0) - yscroll->hide(); - else - yscroll->show(); + int val=height - score_canvas->viewport_height(); + if (val<=0) val=0; + + yscroll->setMaximum(val); + + if (val==0) + yscroll->hide(); + else + yscroll->show(); } void ScoreEdit::viewport_height_changed(int height) { - int val=score_canvas->canvas_height() - height; - // FINDMICHJETZT canvas_height() is uninitalized! - if (val<0) val=0; - yscroll->setPageStep(height * PAGESTEP); - yscroll->setMaximum(val); - - if (val==0) - yscroll->hide(); - else - yscroll->show(); + int val=score_canvas->canvas_height() - height; + // FINDMICHJETZT canvas_height() is uninitialized! + if (val<0) val=0; + yscroll->setPageStep(height * PAGESTEP); + yscroll->setMaximum(val); + + if (val==0) + yscroll->hide(); + else + yscroll->show(); } void ScoreEdit::closeEvent(QCloseEvent* e) { - _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. - names.erase(name); + _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. + names.erase(name); - QSettings settings("MusE", "MusE-qt"); - //settings.setValue("ScoreEdit/geometry", saveGeometry()); - settings.setValue("ScoreEdit/windowState", saveState()); + QSettings settings("MusE", "MusE-qt"); + //settings.setValue("ScoreEdit/geometry", saveGeometry()); + settings.setValue("ScoreEdit/windowState", saveState()); - emit isDeleting(static_cast(this)); - e->accept(); + emit isDeleting(static_cast(this)); + e->accept(); } void ScoreEdit::menu_command(int cmd) { - switch (cmd) - { - case CMD_SET_NAME: - { - bool ok; - QString newname = QInputDialog::getText(this, tr("Enter the new score title"), - tr("Enter the new score title"), QLineEdit::Normal, - name, &ok); - if (ok) - { - if (!set_name(newname)) - QMessageBox::warning(this, tr("Error"), tr("Changing score title failed:\nthe selected title is not unique")); - } - } - break; - - case CMD_SELECT_ALL: select_all(score_canvas->get_all_parts()); break; - case CMD_SELECT_NONE: select_none(score_canvas->get_all_parts()); break; - case CMD_SELECT_INVERT: select_invert(score_canvas->get_all_parts()); break; - case CMD_SELECT_ILOOP: select_in_loop(score_canvas->get_all_parts()); break; - case CMD_SELECT_OLOOP: select_not_in_loop(score_canvas->get_all_parts()); break; - - case CMD_CUT: - copy_notes(score_canvas->get_all_parts(), 1); - erase_notes(score_canvas->get_all_parts(), 1); - break; - case CMD_COPY: copy_notes(score_canvas->get_all_parts(), 1); break; - case CMD_COPY_RANGE: copy_notes(score_canvas->get_all_parts(), MusECore::any_event_selected(score_canvas->get_all_parts()) ? 3 : 2); break; - case CMD_PASTE: - menu_command(CMD_SELECT_NONE); - MusECore::paste_notes(3072, false, true, score_canvas->get_selected_part()); - break; - case CMD_PASTE_DIALOG: - menu_command(CMD_SELECT_NONE); - MusECore::paste_notes(score_canvas->get_selected_part()); - break; - case CMD_QUANTIZE: quantize_notes(score_canvas->get_all_parts()); break; - case CMD_VELOCITY: modify_velocity(score_canvas->get_all_parts()); break; - case CMD_CRESCENDO: crescendo(score_canvas->get_all_parts()); break; - case CMD_NOTELEN: modify_notelen(score_canvas->get_all_parts()); break; - case CMD_TRANSPOSE: transpose_notes(score_canvas->get_all_parts()); break; - case CMD_ERASE: erase_notes(score_canvas->get_all_parts()); break; - case CMD_DEL: erase_notes(score_canvas->get_all_parts(),1); break; - case CMD_MOVE: move_notes(score_canvas->get_all_parts()); break; - case CMD_FIXED_LEN: set_notelen(score_canvas->get_all_parts()); break; - case CMD_DELETE_OVERLAPS: delete_overlaps(score_canvas->get_all_parts()); break; - case CMD_LEGATO: legato(score_canvas->get_all_parts()); break; - - default: - score_canvas->menu_command(cmd); - } + switch (cmd) + { + case CMD_SET_NAME: + { + bool ok; + QString newname = QInputDialog::getText(this, tr("Enter the new score title"), + tr("Enter the new score title"), QLineEdit::Normal, + name, &ok); + if (ok) + { + if (!set_name(newname)) + QMessageBox::warning(this, tr("Error"), tr("Changing score title failed:\nthe selected title is not unique")); + } + } + break; + + case CMD_SELECT_ALL: select_all(score_canvas->get_all_parts()); break; + case CMD_SELECT_NONE: select_none(score_canvas->get_all_parts()); break; + case CMD_SELECT_INVERT: select_invert(score_canvas->get_all_parts()); break; + case CMD_SELECT_ILOOP: select_in_loop(score_canvas->get_all_parts()); break; + case CMD_SELECT_OLOOP: select_not_in_loop(score_canvas->get_all_parts()); break; + + case CMD_CUT: + copy_notes(score_canvas->get_all_parts(), 1); + erase_notes(score_canvas->get_all_parts(), 1); + break; + case CMD_COPY: copy_notes(score_canvas->get_all_parts(), 1); break; + case CMD_COPY_RANGE: copy_notes(score_canvas->get_all_parts(), MusECore::any_event_selected(score_canvas->get_all_parts()) ? 3 : 2); break; + case CMD_PASTE: + menu_command(CMD_SELECT_NONE); + MusECore::paste_notes(3072, false, true, score_canvas->get_selected_part()); + break; + case CMD_PASTE_DIALOG: + menu_command(CMD_SELECT_NONE); + MusECore::paste_notes(score_canvas->get_selected_part()); + break; + case CMD_QUANTIZE: quantize_notes(score_canvas->get_all_parts()); break; + case CMD_VELOCITY: modify_velocity(score_canvas->get_all_parts()); break; + case CMD_CRESCENDO: crescendo(score_canvas->get_all_parts()); break; + case CMD_NOTELEN: modify_notelen(score_canvas->get_all_parts()); break; + case CMD_TRANSPOSE: transpose_notes(score_canvas->get_all_parts()); break; + case CMD_ERASE: erase_notes(score_canvas->get_all_parts()); break; + case CMD_DEL: erase_notes(score_canvas->get_all_parts(),1); break; + case CMD_MOVE: move_notes(score_canvas->get_all_parts()); break; + case CMD_FIXED_LEN: set_notelen(score_canvas->get_all_parts()); break; + case CMD_DELETE_OVERLAPS: delete_overlaps(score_canvas->get_all_parts()); break; + case CMD_LEGATO: legato(score_canvas->get_all_parts()); break; + + default: + score_canvas->menu_command(cmd); + } } void ScoreEdit::clipboard_changed() { - paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists"))); - paste_dialog_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists"))); + paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists"))); + paste_dialog_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists"))); } void ScoreEdit::selection_changed() { - bool flag = !get_events(score_canvas->get_all_parts(),1).empty(); - cut_action->setEnabled(flag); - copy_action->setEnabled(flag); - del_action->setEnabled(flag); + bool flag = !get_events(score_canvas->get_all_parts(),1).empty(); + cut_action->setEnabled(flag); + copy_action->setEnabled(flag); + del_action->setEnabled(flag); } -//duplicated from songfile.cpp's MusE::readPart(); the only differences: +//duplicated from songfile.cpp's MusE::readPart(); the only differences: //"none" is supported and tag_name is settable -MusECore::Part* read_part(MusECore::Xml& xml, QString tag_name="part") +MusECore::Part* read_part(MusECore::Xml& xml, QString tag_name="part") { - MusECore::Part* part = 0; + MusECore::Part* part = 0; - for (;;) - { - MusECore::Xml::Token token = xml.parse(); - const QString& tag = xml.s1(); - - switch (token) - { - case MusECore::Xml::Error: - case MusECore::Xml::End: - return part; - - case MusECore::Xml::Text: - { - int trackIdx, partIdx; - if (tag=="none") - part=NULL; - else - { - sscanf(tag.toLatin1().constData(), "%d:%d", &trackIdx, &partIdx); - if (debugMsg) cout << "read_part: trackIdx="<::iterator part=parts.begin(); part!=parts.end(); part++) - { - MusECore::Track* track = (*part)->track(); - int trkIdx = MusEGlobal::song->tracks()->index(track); - int partIdx = track->parts()->index(*part); - - if((trkIdx == -1) || (partIdx == -1)) - cerr << "ERROR: staff_t::write_status: trkIdx:"<%d:%d", trkIdx, partIdx); - } - xml.tag(level, "/staff"); + xml.tag(level++, "staff"); + xml.intTag(level, "type", type); + xml.intTag(level, "clef", clef); + for (set::iterator part=parts.begin(); part!=parts.end(); part++) + { + MusECore::Track* track = (*part)->track(); + int trkIdx = MusEGlobal::song->tracks()->index(track); + int partIdx = track->parts()->index(*part); + + if((trkIdx == -1) || (partIdx == -1)) + cerr << "ERROR: staff_t::write_status: trkIdx:"<%d:%d", trkIdx, partIdx); + } + xml.tag(level, "/staff"); } //--------------------------------------------------------- @@ -917,78 +927,78 @@ void ScoreEdit::writeStatus(int level, MusECore::Xml& xml) const { - xml.tag(level++, "scoreedit"); - TopWin::writeStatus(level, xml); + xml.tag(level++, "scoreedit"); + TopWin::writeStatus(level, xml); - xml.strTag(level, "name", name); - xml.intTag(level, "tool", edit_tools->curTool()); - xml.intTag(level, "steprec", srec->isChecked()); - xml.intTag(level, "quantPower", score_canvas->quant_power2()); - xml.intTag(level, "pxPerWhole", score_canvas->pixels_per_whole()); - xml.intTag(level, "newNoteVelo", velo_spinbox->value()); - xml.intTag(level, "newNoteVeloOff", velo_off_spinbox->value()); - xml.intTag(level, "lastLen", score_canvas->get_last_len()); - - int len=0; - if (n1_action->isChecked()) - len=1; - else if (n2_action->isChecked()) - len=2; - else if (n4_action->isChecked()) - len=4; - else if (n8_action->isChecked()) - len=8; - else if (n16_action->isChecked()) - len=16; - else if (n32_action->isChecked()) - len=32; - else if (nlast_action->isChecked()) - len=0; // means "last" - - xml.intTag(level, "newLen", len); - - int color=0; - if (color_black_action->isChecked()) - color=0; - else if (color_velo_action->isChecked()) - color=1; - else if (color_part_action->isChecked()) - color=2; - - xml.intTag(level, "noteColor", color); - - xml.intTag(level, "xscroll", xscroll->value()); - xml.intTag(level, "yscroll", yscroll->value()); - xml.intTag(level, "preambleContainsKeysig", preamble_keysig_action->isChecked()); - xml.intTag(level, "preambleContainsTimesig", preamble_timesig_action->isChecked()); - - MusECore::Part* selected_part=score_canvas->get_selected_part(); - if (selected_part==NULL) - { - xml.put(level, "none"); - } - else - { - MusECore::Track* track = selected_part->track(); - int trkIdx = MusEGlobal::song->tracks()->index(track); - int partIdx = track->parts()->index(selected_part); - - if((trkIdx == -1) || (partIdx == -1)) - cerr << "ERROR: ScoreEdit::write_status: trkIdx:"<curTool()); + xml.intTag(level, "steprec", srec->isChecked()); + xml.intTag(level, "quantPower", score_canvas->quant_power2()); + xml.intTag(level, "pxPerWhole", score_canvas->pixels_per_whole()); + xml.intTag(level, "newNoteVelo", velo_spinbox->value()); + xml.intTag(level, "newNoteVeloOff", velo_off_spinbox->value()); + xml.intTag(level, "lastLen", score_canvas->get_last_len()); + + int len=0; + if (n1_action->isChecked()) + len=1; + else if (n2_action->isChecked()) + len=2; + else if (n4_action->isChecked()) + len=4; + else if (n8_action->isChecked()) + len=8; + else if (n16_action->isChecked()) + len=16; + else if (n32_action->isChecked()) + len=32; + else if (nlast_action->isChecked()) + len=0; // means "last" + + xml.intTag(level, "newLen", len); + + int color=0; + if (color_black_action->isChecked()) + color=0; + else if (color_velo_action->isChecked()) + color=1; + else if (color_part_action->isChecked()) + color=2; + + xml.intTag(level, "noteColor", color); + + xml.intTag(level, "xscroll", xscroll->value()); + xml.intTag(level, "yscroll", yscroll->value()); + xml.intTag(level, "preambleContainsKeysig", preamble_keysig_action->isChecked()); + xml.intTag(level, "preambleContainsTimesig", preamble_timesig_action->isChecked()); + + const MusECore::Part* selected_part=score_canvas->get_selected_part(); + if (selected_part==NULL) + { + xml.put(level, "none"); + } + else + { + MusECore::Track* track = selected_part->track(); + int trkIdx = MusEGlobal::song->tracks()->index(track); + int partIdx = track->parts()->index(selected_part); + + if((trkIdx == -1) || (partIdx == -1)) + cerr << "ERROR: ScoreEdit::write_status: trkIdx:"<%d:%d", trkIdx, partIdx); - } + xml.put(level, "%d:%d", trkIdx, partIdx); + } - score_canvas->write_staves(level,xml); + score_canvas->write_staves(level,xml); - xml.tag(level, "/scoreedit"); + xml.tag(level, "/scoreedit"); } void ScoreCanvas::write_staves(int level, MusECore::Xml& xml) const { - for (list::const_iterator staff=staves.begin(); staff!=staves.end(); staff++) - staff->write_status(level, xml); + for (list::const_iterator staff=staves.begin(); staff!=staves.end(); staff++) + staff->write_status(level, xml); } @@ -998,170 +1008,170 @@ void ScoreEdit::readStatus(MusECore::Xml& xml) { - // never "return;" inside that function. - // instead, goto end_of_scoreedit_read_status; - // (there is a command which must be executed!) - - bool apply_velo_temp=apply_velo; - apply_velo=false; - - for (;;) - { - MusECore::Xml::Token token = xml.parse(); - if (token == MusECore::Xml::Error || token == MusECore::Xml::End) - break; - - const QString& tag = xml.s1(); - switch (token) - { - case MusECore::Xml::TagStart: - if (tag == "name") - set_name(xml.parse1()); - else if (tag == "tool") - edit_tools->set(xml.parseInt()); - else if (tag == "steprec") - srec->setChecked(xml.parseInt()); - else if (tag == "quantPower") - quant_combobox->setCurrentIndex(xml.parseInt()-1); - else if (tag == "pxPerWhole") - px_per_whole_spinbox->setValue(xml.parseInt()); - else if (tag == "newNoteVelo") - velo_spinbox->setValue(xml.parseInt()); - else if (tag == "newNoteVeloOff") - velo_off_spinbox->setValue(xml.parseInt()); - else if (tag == "lastLen") - score_canvas->set_last_len(xml.parseInt()); - else if (tag == "newLen") - { - int val=xml.parseInt(); - switch (val) - { - case 0: nlast_action->setChecked(true); break; - case 1: n1_action->setChecked(true); break; - case 2: n2_action->setChecked(true); break; - case 4: n4_action->setChecked(true); break; - case 8: n8_action->setChecked(true); break; - case 16: n16_action->setChecked(true); break; - case 32: n32_action->setChecked(true); break; - default: - cerr << "ERROR: THIS SHOULD NEVER HAPPEN. newLen is invalid in ScoreEdit::readStatus.\n" << - " (newLen="<setChecked(true); - } - } - else if (tag == "noteColor") - { - int val=xml.parseInt(); - switch (val) - { - case 0: color_black_action->setChecked(true); break; - case 1: color_velo_action->setChecked(true); break; - case 2: color_part_action->setChecked(true); break; - default: - cerr << "ERROR: THIS SHOULD NEVER HAPPEN. noteColor is invalid in ScoreEdit::readStatus.\n" << - " (noteColor="<setChecked(true); - } - } - else if (tag == "xscroll") - xscroll->setValue(xml.parseInt()); - else if (tag == "yscroll") - yscroll->setValue(xml.parseInt()); - else if (tag == "preambleContainsKeysig") - preamble_keysig_action->setChecked(xml.parseInt()); - else if (tag == "preambleContainsTimesig") - preamble_timesig_action->setChecked(xml.parseInt()); - else if (tag == "topwin") - TopWin::readStatus(xml); - else if (tag == "selectedPart") - score_canvas->set_selected_part(read_part(xml, "selectedPart")); - else if (tag == "staff") - { - staff_t staff(score_canvas); - staff.read_status(xml); - score_canvas->push_back_staff(staff); - } - else - xml.unknown("ScoreEdit"); - break; - - case MusECore::Xml::TagEnd: - if (tag == "scoreedit") - goto end_of_scoreedit_read_status; - - default: - break; - } - } + // never "return;" inside that function. + // instead, goto end_of_scoreedit_read_status; + // (there is a command which must be executed!) + + bool apply_velo_temp=apply_velo; + apply_velo=false; + + for (;;) + { + MusECore::Xml::Token token = xml.parse(); + if (token == MusECore::Xml::Error || token == MusECore::Xml::End) + break; + + const QString& tag = xml.s1(); + switch (token) + { + case MusECore::Xml::TagStart: + if (tag == "name") + set_name(xml.parse1()); + else if (tag == "tool") + edit_tools->set(xml.parseInt()); + else if (tag == "steprec") + srec->setChecked(xml.parseInt()); + else if (tag == "quantPower") + quant_combobox->setCurrentIndex(xml.parseInt()-1); + else if (tag == "pxPerWhole") + px_per_whole_spinbox->setValue(xml.parseInt()); + else if (tag == "newNoteVelo") + velo_spinbox->setValue(xml.parseInt()); + else if (tag == "newNoteVeloOff") + velo_off_spinbox->setValue(xml.parseInt()); + else if (tag == "lastLen") + score_canvas->set_last_len(xml.parseInt()); + else if (tag == "newLen") + { + int val=xml.parseInt(); + switch (val) + { + case 0: nlast_action->setChecked(true); break; + case 1: n1_action->setChecked(true); break; + case 2: n2_action->setChecked(true); break; + case 4: n4_action->setChecked(true); break; + case 8: n8_action->setChecked(true); break; + case 16: n16_action->setChecked(true); break; + case 32: n32_action->setChecked(true); break; + default: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN. newLen is invalid in ScoreEdit::readStatus.\n" << + " (newLen="<setChecked(true); + } + } + else if (tag == "noteColor") + { + int val=xml.parseInt(); + switch (val) + { + case 0: color_black_action->setChecked(true); break; + case 1: color_velo_action->setChecked(true); break; + case 2: color_part_action->setChecked(true); break; + default: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN. noteColor is invalid in ScoreEdit::readStatus.\n" << + " (noteColor="<setChecked(true); + } + } + else if (tag == "xscroll") + xscroll->setValue(xml.parseInt()); + else if (tag == "yscroll") + yscroll->setValue(xml.parseInt()); + else if (tag == "preambleContainsKeysig") + preamble_keysig_action->setChecked(xml.parseInt()); + else if (tag == "preambleContainsTimesig") + preamble_timesig_action->setChecked(xml.parseInt()); + else if (tag == "topwin") + TopWin::readStatus(xml); + else if (tag == "selectedPart") + score_canvas->set_selected_part(read_part(xml, "selectedPart")); + else if (tag == "staff") + { + staff_t staff(score_canvas); + staff.read_status(xml); + score_canvas->push_back_staff(staff); + } + else + xml.unknown("ScoreEdit"); + break; + + case MusECore::Xml::TagEnd: + if (tag == "scoreedit") + goto end_of_scoreedit_read_status; + + default: + break; + } + } end_of_scoreedit_read_status: - - apply_velo=apply_velo_temp; + + apply_velo=apply_velo_temp; } void ScoreEdit::read_configuration(MusECore::Xml& xml) { - for (;;) - { - MusECore::Xml::Token token = xml.parse(); - if (token == MusECore::Xml::Error || token == MusECore::Xml::End) - break; - - const QString& tag = xml.s1(); - switch (token) - { - case MusECore::Xml::TagStart: - if (tag=="quantPowerInit") - ScoreCanvas::_quant_power2_init=xml.parseInt(); - else if (tag=="pxPerWholeInit") - ScoreCanvas::_pixels_per_whole_init=xml.parseInt(); - else if (tag=="newNoteVeloInit") - ScoreCanvas::note_velo_init=xml.parseInt(); - else if (tag=="newNoteVeloOffInit") - ScoreCanvas::note_velo_off_init=xml.parseInt(); - else if (tag=="newLenInit") - ScoreCanvas::new_len_init=xml.parseInt(); - else if (tag=="noteColorInit") - ScoreCanvas::coloring_mode_init=(ScoreCanvas::coloring_mode_t)xml.parseInt(); - else if (tag=="preambleContainsKeysig") - ScoreCanvas::preamble_contains_keysig_init=xml.parseInt(); - else if (tag=="preambleContainsTimesig") - ScoreCanvas::preamble_contains_timesig_init=xml.parseInt(); - else if (tag == "topwin") - TopWin::readConfiguration(SCORE, xml); - else - xml.unknown("ScoreEdit"); - break; - - case MusECore::Xml::TagEnd: - if (tag == "scoreedit") - return; - - default: - break; - } - } + for (;;) + { + MusECore::Xml::Token token = xml.parse(); + if (token == MusECore::Xml::Error || token == MusECore::Xml::End) + break; + + const QString& tag = xml.s1(); + switch (token) + { + case MusECore::Xml::TagStart: + if (tag=="quantPowerInit") + ScoreCanvas::_quant_power2_init=xml.parseInt(); + else if (tag=="pxPerWholeInit") + ScoreCanvas::_pixels_per_whole_init=xml.parseInt(); + else if (tag=="newNoteVeloInit") + ScoreCanvas::note_velo_init=xml.parseInt(); + else if (tag=="newNoteVeloOffInit") + ScoreCanvas::note_velo_off_init=xml.parseInt(); + else if (tag=="newLenInit") + ScoreCanvas::new_len_init=xml.parseInt(); + else if (tag=="noteColorInit") + ScoreCanvas::coloring_mode_init=(ScoreCanvas::coloring_mode_t)xml.parseInt(); + else if (tag=="preambleContainsKeysig") + ScoreCanvas::preamble_contains_keysig_init=xml.parseInt(); + else if (tag=="preambleContainsTimesig") + ScoreCanvas::preamble_contains_timesig_init=xml.parseInt(); + else if (tag == "topwin") + TopWin::readConfiguration(SCORE, xml); + else + xml.unknown("ScoreEdit"); + break; + + case MusECore::Xml::TagEnd: + if (tag == "scoreedit") + return; + + default: + break; + } + } } void ScoreEdit::write_configuration(int level, MusECore::Xml& xml) { - xml.tag(level++, "scoreedit"); + xml.tag(level++, "scoreedit"); - xml.intTag(level, "quantPowerInit", ScoreCanvas::_quant_power2_init); - xml.intTag(level, "pxPerWholeInit", ScoreCanvas::_pixels_per_whole_init); - xml.intTag(level, "newNoteVeloInit", ScoreCanvas::note_velo_init); - xml.intTag(level, "newNoteVeloOffInit", ScoreCanvas::note_velo_off_init); - xml.intTag(level, "newLenInit", ScoreCanvas::new_len_init); - xml.intTag(level, "noteColorInit", ScoreCanvas::coloring_mode_init); - xml.intTag(level, "preambleContainsKeysig", ScoreCanvas::preamble_contains_keysig_init); - xml.intTag(level, "preambleContainsTimesig", ScoreCanvas::preamble_contains_timesig_init); - - TopWin::writeConfiguration(SCORE, level, xml); + xml.intTag(level, "quantPowerInit", ScoreCanvas::_quant_power2_init); + xml.intTag(level, "pxPerWholeInit", ScoreCanvas::_pixels_per_whole_init); + xml.intTag(level, "newNoteVeloInit", ScoreCanvas::note_velo_init); + xml.intTag(level, "newNoteVeloOffInit", ScoreCanvas::note_velo_off_init); + xml.intTag(level, "newLenInit", ScoreCanvas::new_len_init); + xml.intTag(level, "noteColorInit", ScoreCanvas::coloring_mode_init); + xml.intTag(level, "preambleContainsKeysig", ScoreCanvas::preamble_contains_keysig_init); + xml.intTag(level, "preambleContainsTimesig", ScoreCanvas::preamble_contains_timesig_init); - xml.etag(level, "scoreedit"); + TopWin::writeConfiguration(SCORE, level, xml); + + xml.etag(level, "scoreedit"); } @@ -1169,574 +1179,574 @@ void ScoreCanvas::add_staves(MusECore::PartList* pl, bool all_in_one) { - if (!pl->empty()) - { - staff_t staff(this); - - if (all_in_one) - { - clefTypes clef=((MusECore::MidiTrack*)pl->begin()->second->track())->getClef(); - - staff.parts.clear(); - for (MusECore::ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) - { - if (((MusECore::MidiTrack*)part_it->second->track())->getClef() != clef) - clef=grandStaff; - - staff.parts.insert(part_it->second); - } - staff.cleanup_parts(); - staff.update_part_indices(); - - switch (clef) - { - case trebleClef: - staff.type=NORMAL; - staff.clef=VIOLIN; - staves.push_back(staff); - break; - - case bassClef: - staff.type=NORMAL; - staff.clef=BASS; - staves.push_back(staff); - break; - - case grandStaff: - staff.type=GRAND_TOP; - staff.clef=VIOLIN; - staves.push_back(staff); - - staff.type=GRAND_BOTTOM; - staff.clef=BASS; - staves.push_back(staff); - break; - } - } - else - { - set tracks; - for (MusECore::ciPart it=pl->begin(); it!=pl->end(); it++) - tracks.insert(it->second->track()); - - MusECore::TrackList* tracklist = MusEGlobal::song->tracks(); - // this loop is used for inserting track-staves in the - // correct order. simply iterating through tracks's contents - // would sort after the pointer values, i.e. randomly - for (MusECore::ciTrack track_it=tracklist->begin(); track_it!=tracklist->end(); track_it++) - if (tracks.find(*track_it)!=tracks.end()) - { - staff.parts.clear(); - for (MusECore::ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) - if (part_it->second->track() == *track_it) - staff.parts.insert(part_it->second); - staff.cleanup_parts(); - staff.update_part_indices(); - - switch (((MusECore::MidiTrack*)(*track_it))->getClef()) - { - case trebleClef: - staff.type=NORMAL; - staff.clef=VIOLIN; - staves.push_back(staff); - break; - - case bassClef: - staff.type=NORMAL; - staff.clef=BASS; - staves.push_back(staff); - break; - - case grandStaff: - staff.type=GRAND_TOP; - staff.clef=VIOLIN; - staves.push_back(staff); - - staff.type=GRAND_BOTTOM; - staff.clef=BASS; - staves.push_back(staff); - break; - } - } - } - - cleanup_staves(); - fully_recalculate(); - recalc_staff_pos(); - } + if (!pl->empty()) + { + staff_t staff(this); + + if (all_in_one) + { + clefTypes clef=((MusECore::MidiTrack*)pl->begin()->second->track())->getClef(); + + staff.parts.clear(); + for (MusECore::ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) + { + if (((MusECore::MidiTrack*)part_it->second->track())->getClef() != clef) + clef=grandStaff; + + staff.parts.insert(part_it->second); + } + staff.cleanup_parts(); + staff.update_part_indices(); + + switch (clef) + { + case trebleClef: + staff.type=NORMAL; + staff.clef=VIOLIN; + staves.push_back(staff); + break; + + case bassClef: + staff.type=NORMAL; + staff.clef=BASS; + staves.push_back(staff); + break; + + case grandStaff: + staff.type=GRAND_TOP; + staff.clef=VIOLIN; + staves.push_back(staff); + + staff.type=GRAND_BOTTOM; + staff.clef=BASS; + staves.push_back(staff); + break; + } + } + else + { + set tracks; + for (MusECore::ciPart it=pl->begin(); it!=pl->end(); it++) + tracks.insert(it->second->track()); + + MusECore::TrackList* tracklist = MusEGlobal::song->tracks(); + // this loop is used for inserting track-staves in the + // correct order. simply iterating through tracks's contents + // would sort after the pointer values, i.e. randomly + for (MusECore::ciTrack track_it=tracklist->begin(); track_it!=tracklist->end(); track_it++) + if (tracks.find(*track_it)!=tracks.end()) + { + staff.parts.clear(); + for (MusECore::ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) + if (part_it->second->track() == *track_it) + staff.parts.insert(part_it->second); + staff.cleanup_parts(); + staff.update_part_indices(); + + switch (((MusECore::MidiTrack*)(*track_it))->getClef()) + { + case trebleClef: + staff.type=NORMAL; + staff.clef=VIOLIN; + staves.push_back(staff); + break; + + case bassClef: + staff.type=NORMAL; + staff.clef=BASS; + staves.push_back(staff); + break; + + case grandStaff: + staff.type=GRAND_TOP; + staff.clef=VIOLIN; + staves.push_back(staff); + + staff.type=GRAND_BOTTOM; + staff.clef=BASS; + staves.push_back(staff); + break; + } + } + } + + cleanup_staves(); + fully_recalculate(); + recalc_staff_pos(); + } } ScoreCanvas::ScoreCanvas(ScoreEdit* pr, QWidget* parent_widget) : View(parent_widget, 1, 1) { - parent = pr; - setFocusPolicy(Qt::StrongFocus); + parent = pr; + setFocusPolicy(Qt::StrongFocus); setBg(MusEGlobal::config.midiCanvasBg); - - setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); - - init_pixmaps(); - - srec=false; - for (int i=0;i<128;i++) held_notes[i]=false; - steprec=new MusECore::StepRec(held_notes); - connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), SLOT(midi_note(int,int))); - - x_pos=0; - x_left=0; - y_pos=0; - have_lasso=false; - inserting=false; - dragging=false; - drag_cursor_changed=false; - mouse_erases_notes=false; - mouse_inserts_notes=true; - undo_started=false; - - selected_part=NULL; - dragged_event_part=NULL; - - last_len=384; - new_len=-1; // will be initalized with new_len_init by ScoreEdit::ScoreEdit(); - - _quant_power2=_quant_power2_init; // ScoreEdit relies on this to be done! - _pixels_per_whole_init = _pixels_per_whole_init; - - note_velo=note_velo_init; - note_velo_off=note_velo_off_init; - - dragging_staff=false; - - - coloring_mode=coloring_mode_init; - preamble_contains_keysig=preamble_contains_keysig_init; - preamble_contains_timesig=preamble_contains_timesig_init; - - - x_scroll_speed=0; - x_scroll_pos=0; - y_scroll_speed=0; - y_scroll_pos=0; - connect (MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat_timer_event())); - - connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(pos_changed(int,unsigned,bool))); - connect(MusEGlobal::song, SIGNAL(playChanged(bool)), SLOT(play_changed(bool))); - connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(config_changed())); - - - staff_menu=new QMenu(this); - - staffmode_treble_action = staff_menu->addAction(tr("Treble")); - connect(staffmode_treble_action, SIGNAL(triggered()), SLOT(staffmode_treble_slot())); - - staffmode_bass_action = staff_menu->addAction(tr("Bass")); - connect(staffmode_bass_action, SIGNAL(triggered()), SLOT(staffmode_bass_slot())); - staffmode_both_action = staff_menu->addAction(tr("Grand Staff")); - connect(staffmode_both_action, SIGNAL(triggered()), SLOT(staffmode_both_slot())); + setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); + + init_pixmaps(); + + srec=false; + for (int i=0;i<128;i++) held_notes[i]=false; + steprec=new MusECore::StepRec(held_notes); + connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), SLOT(midi_note(int,int))); + + x_pos=0; + x_left=0; + y_pos=0; + have_lasso=false; + inserting=false; + dragging=false; + drag_cursor_changed=false; + mouse_erases_notes=false; + mouse_inserts_notes=true; + undo_started=false; + + selected_part=NULL; + dragged_event_part=NULL; + + last_len=384; + new_len=-1; // will be initialized with new_len_init by ScoreEdit::ScoreEdit(); + + _quant_power2=_quant_power2_init; // ScoreEdit relies on this to be done! + _pixels_per_whole = _pixels_per_whole_init; + + note_velo=note_velo_init; + note_velo_off=note_velo_off_init; + + dragging_staff=false; + + + coloring_mode=coloring_mode_init; + preamble_contains_keysig=preamble_contains_keysig_init; + preamble_contains_timesig=preamble_contains_timesig_init; + - remove_staff_action = staff_menu->addAction(tr("Remove staff")); - connect(remove_staff_action, SIGNAL(triggered()), SLOT(remove_staff_slot())); + x_scroll_speed=0; + x_scroll_pos=0; + y_scroll_speed=0; + y_scroll_pos=0; + connect (MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat_timer_event())); - unsetCursor(); + connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(pos_changed(int,unsigned,bool))); + connect(MusEGlobal::song, SIGNAL(playChanged(bool)), SLOT(play_changed(bool))); + connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(config_changed())); + + + staff_menu=new QMenu(this); + + staffmode_treble_action = staff_menu->addAction(tr("Treble")); + connect(staffmode_treble_action, SIGNAL(triggered()), SLOT(staffmode_treble_slot())); + + staffmode_bass_action = staff_menu->addAction(tr("Bass")); + connect(staffmode_bass_action, SIGNAL(triggered()), SLOT(staffmode_bass_slot())); + + staffmode_both_action = staff_menu->addAction(tr("Grand Staff")); + connect(staffmode_both_action, SIGNAL(triggered()), SLOT(staffmode_both_slot())); + + remove_staff_action = staff_menu->addAction(tr("Remove staff")); + connect(remove_staff_action, SIGNAL(triggered()), SLOT(remove_staff_slot())); + + unsetCursor(); } ScoreCanvas::~ScoreCanvas() { - delete steprec; + delete steprec; } void ScoreCanvas::staffmode_treble_slot() { - set_staffmode(current_staff, MODE_TREBLE); + set_staffmode(current_staff, MODE_TREBLE); } void ScoreCanvas::staffmode_bass_slot() { - set_staffmode(current_staff, MODE_BASS); + set_staffmode(current_staff, MODE_BASS); } void ScoreCanvas::staffmode_both_slot() { - set_staffmode(current_staff, MODE_BOTH); + set_staffmode(current_staff, MODE_BOTH); } void ScoreCanvas::remove_staff_slot() { - remove_staff(current_staff); + remove_staff(current_staff); } void ScoreCanvas::set_staffmode(list::iterator it, staff_mode_t mode) { - if (it->type == GRAND_BOTTOM) - { - it--; - if (it->type!=GRAND_TOP) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type==GRAND_TOP) - { - list::iterator tmp=it; - tmp++; - if (tmp->type!=GRAND_BOTTOM) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<type=NORMAL; - it->clef=VIOLIN; - break; - - case MODE_BASS: - it->type=NORMAL; - it->clef=BASS; - break; - - case MODE_BOTH: - it->type=GRAND_BOTTOM; - it->clef=BASS; - - staves.insert(it, staff_t(this, GRAND_TOP, VIOLIN, it->parts)); - break; - - default: - cerr << "ERROR: ILLEGAL FUNCTION CALL: invalid mode in set_staffmode" << endl; - } - - fully_recalculate(); - recalc_staff_pos(); + if (it->type == GRAND_BOTTOM) + { + it--; + if (it->type!=GRAND_TOP) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type==GRAND_TOP) + { + list::iterator tmp=it; + tmp++; + if (tmp->type!=GRAND_BOTTOM) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<type=NORMAL; + it->clef=VIOLIN; + break; + + case MODE_BASS: + it->type=NORMAL; + it->clef=BASS; + break; + + case MODE_BOTH: + it->type=GRAND_BOTTOM; + it->clef=BASS; + + staves.insert(it, staff_t(this, GRAND_TOP, VIOLIN, it->parts)); + break; + + default: + cerr << "ERROR: ILLEGAL FUNCTION CALL: invalid mode in set_staffmode" << endl; + } + + fully_recalculate(); + recalc_staff_pos(); } void ScoreCanvas::remove_staff(list::iterator it) { - if (it->type == GRAND_BOTTOM) - { - it--; - if (it->type!=GRAND_TOP) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type == NORMAL) - { - staves.erase(it); - } - else if (it->type == GRAND_TOP) - { - staves.erase(it++); - if (it->type!=GRAND_BOTTOM) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<type == GRAND_BOTTOM) + { + it--; + if (it->type!=GRAND_TOP) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type == NORMAL) + { + staves.erase(it); + } + else if (it->type == GRAND_TOP) + { + staves.erase(it++); + if (it->type!=GRAND_BOTTOM) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<::iterator dest, list::iterator src) { - if (dest->type == GRAND_BOTTOM) - { - dest--; - if (dest->type!=GRAND_TOP) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type == GRAND_BOTTOM) - { - src--; - if (src->type!=GRAND_TOP) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<parts.insert(src->parts.begin(), src->parts.end()); - - if (dest->type == GRAND_TOP) - { - dest++; - if (dest->type != GRAND_BOTTOM) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<parts.insert(src->parts.begin(), src->parts.end()); - } - - dest->update_part_indices(); - - remove_staff(src); + if (dest->type == GRAND_BOTTOM) + { + dest--; + if (dest->type!=GRAND_TOP) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type == GRAND_BOTTOM) + { + src--; + if (src->type!=GRAND_TOP) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<parts.insert(src->parts.begin(), src->parts.end()); + + if (dest->type == GRAND_TOP) + { + dest++; + if (dest->type != GRAND_BOTTOM) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<parts.insert(src->parts.begin(), src->parts.end()); + } + + dest->update_part_indices(); - fully_recalculate(); - recalc_staff_pos(); + remove_staff(src); + + fully_recalculate(); + recalc_staff_pos(); } void ScoreCanvas::move_staff_above(list::iterator dest, list::iterator src) { - if (dest->type == GRAND_BOTTOM) - { - dest--; - if (dest->type!=GRAND_TOP) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type == GRAND_BOTTOM) - { - src--; - if (src->type!=GRAND_TOP) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<::iterator src_end=src; - src_end++; //point _after_ src - if (src->type==GRAND_TOP) //if this is a grand staff, we need to splice two list-entries - src_end++; - - staves.splice(dest, staves, src, src_end); + if (dest->type == GRAND_BOTTOM) + { + dest--; + if (dest->type!=GRAND_TOP) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type == GRAND_BOTTOM) + { + src--; + if (src->type!=GRAND_TOP) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<::iterator src_end=src; + src_end++; //point _after_ src + if (src->type==GRAND_TOP) //if this is a grand staff, we need to splice two list-entries + src_end++; + + staves.splice(dest, staves, src, src_end); - fully_recalculate(); - recalc_staff_pos(); + fully_recalculate(); + recalc_staff_pos(); } void ScoreCanvas::move_staff_below(list::iterator dest, list::iterator src) { - if (dest->type == GRAND_TOP) - { - dest++; - if (dest->type!=GRAND_BOTTOM) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"< ScoreCanvas::get_all_parts() -{ - set result; - - for (list::iterator it=staves.begin(); it!=staves.end(); it++) - result.insert(it->parts.begin(), it->parts.end()); - - return result; + if (dest->type == GRAND_TOP) + { + dest++; + if (dest->type!=GRAND_BOTTOM) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"< ScoreCanvas::get_all_parts() +{ + set result; + + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + result.insert(it->parts.begin(), it->parts.end()); + + return result; } void ScoreCanvas::fully_recalculate() { - song_changed(SC_EVENT_MODIFIED); + song_changed(SC_EVENT_MODIFIED); } void ScoreCanvas::song_changed(MusECore::SongChangedFlags_t flags) { - if(parent && parent->deleting()) // Ignore while while deleting to prevent crash. - return; + if(parent && parent->deleting()) // Ignore while while deleting to prevent crash. + return; + + if (flags & (SC_PART_MODIFIED | SC_PART_REMOVED | SC_PART_INSERTED | SC_TRACK_REMOVED)) + { + update_parts(); + + if (flags & (SC_PART_REMOVED | SC_TRACK_REMOVED)) + { + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + it->cleanup_parts(); + + cleanup_staves(); + + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + it->recalculate(); + + recalc_staff_pos(); + + redraw(); + } + } + + if (flags & (SC_PART_MODIFIED | + SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED | + SC_SIG | SC_KEY) ) + { + calc_pos_add_list(); - if (flags & (SC_PART_MODIFIED | SC_PART_REMOVED | SC_PART_INSERTED | SC_TRACK_REMOVED)) - { - update_parts(); - - if (flags & (SC_PART_REMOVED | SC_TRACK_REMOVED)) - { - for (list::iterator it=staves.begin(); it!=staves.end(); it++) - it->cleanup_parts(); - - cleanup_staves(); - - for (list::iterator it=staves.begin(); it!=staves.end(); it++) - it->recalculate(); - - recalc_staff_pos(); - - redraw(); - } - } - - if (flags & (SC_PART_MODIFIED | - SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED | - SC_SIG | SC_KEY) ) - { - calc_pos_add_list(); - - for (list::iterator it=staves.begin(); it!=staves.end(); it++) - it->recalculate(); - - recalc_staff_pos(); - - redraw(); - emit canvas_width_changed(canvas_width()); - } - - if (flags & SC_SELECTION) - { - redraw(); - } + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + it->recalculate(); + + recalc_staff_pos(); + + redraw(); + emit canvas_width_changed(canvas_width()); + } + + if (flags & SC_SELECTION) + { + redraw(); + } } int ScoreCanvas::canvas_width() { - //return tick_to_x(staves.begin()->itemlist.rbegin()->first); - return tick_to_x(SONG_LENGTH); + //return tick_to_x(staves.begin()->itemlist.rbegin()->first); + return tick_to_x(SONG_LENGTH); } int ScoreCanvas::canvas_height() { - return staves.empty() ? 0 : staves.rbegin()->y_bottom; + return staves.empty() ? 0 : staves.rbegin()->y_bottom; } int ScoreCanvas::viewport_width() { - return (width() - x_left); + return (width() - x_left); } int ScoreCanvas::viewport_height() { - return height(); + return height(); } string IntToStr(int i) { - ostringstream s; - s<load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest1.png"); - pix_r2->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest2.png"); - pix_r4->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest4.png"); - pix_r8->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest8.png"); - pix_r16->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest16.png"); - pix_r32->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest32.png"); - pix_flag_up[0].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags8u.png"); - pix_flag_up[1].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags16u.png"); - pix_flag_up[2].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags32u.png"); - pix_flag_up[3].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags64u.png"); - pix_flag_down[0].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags8d.png"); - pix_flag_down[1].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags16d.png"); - pix_flag_down[2].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags32d.png"); - pix_flag_down[3].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags64d.png"); - - pix_clef_violin->load(MusEGlobal::museGlobalShare + "/scoreglyphs/clef_violin_big.png"); - pix_clef_bass->load(MusEGlobal::museGlobalShare + "/scoreglyphs/clef_bass_big.png"); - - for (int i=0;i<10;i++) - pix_num[i].load(MusEGlobal::museGlobalShare + "/scoreglyphs/"+IntToQStr(i)+".png"); - - pixmaps_initalized=true; - - if (debugMsg) cout << "done" << endl; - } + if (!pixmaps_initalized) + { + if (debugMsg) cout << "initalizing colors..." << endl; + + mycolors=new QColor[NUM_MYCOLORS]; + + mycolors[0]=Qt::black; + for (int i=1;iload(MusEGlobal::museGlobalShare + "/scoreglyphs/rest1.png"); + pix_r2->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest2.png"); + pix_r4->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest4.png"); + pix_r8->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest8.png"); + pix_r16->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest16.png"); + pix_r32->load(MusEGlobal::museGlobalShare + "/scoreglyphs/rest32.png"); + pix_flag_up[0].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags8u.png"); + pix_flag_up[1].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags16u.png"); + pix_flag_up[2].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags32u.png"); + pix_flag_up[3].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags64u.png"); + pix_flag_down[0].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags8d.png"); + pix_flag_down[1].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags16d.png"); + pix_flag_down[2].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags32d.png"); + pix_flag_down[3].load(MusEGlobal::museGlobalShare + "/scoreglyphs/flags64d.png"); + + pix_clef_violin->load(MusEGlobal::museGlobalShare + "/scoreglyphs/clef_violin_big.png"); + pix_clef_bass->load(MusEGlobal::museGlobalShare + "/scoreglyphs/clef_bass_big.png"); + + for (int i=0;i<10;i++) + pix_num[i].load(MusEGlobal::museGlobalShare + "/scoreglyphs/"+IntToQStr(i)+".png"); + + pixmaps_initalized=true; + + if (debugMsg) cout << "done" << endl; + } } int modulo(int a, int b) // similar to a % b { - return (((a%b)+b)%b); + return (((a%b)+b)%b); } int divide_floor(int a, int b) // similar to a / b { - return int(floor(float(a)/float(b))); + return int(floor(float(a)/float(b))); } #define DEFAULT_REST_HEIGHT 6 @@ -1744,24 +1754,24 @@ bool operator< (const note_pos_t& a, const note_pos_t& b) { - if (a.heightb.height) return false; - return a.vorzeichenb.height) return false; + return a.vorzeichen::const_iterator part_it=parts.begin(); part_it!=parts.end(); part_it++) - { - MusECore::Part* part=*part_it; - MusECore::EventList* el=part->events(); - - for (MusECore::iEvent it=el->begin(); it!=el->end(); it++) - { - MusECore::Event& event=it->second; - - if ( ( event.isNote() && !event.isNoteOff() && - // (event.endTick() <= part->lenTick()) ) && - (event.tick() <= part->lenTick()) ) && // changed to accord to prcanvas.cpp and others (flo93) - ( ((type==GRAND_TOP) && (event.pitch() >= SPLIT_NOTE)) || - ((type==GRAND_BOTTOM) && (event.pitch() < SPLIT_NOTE)) || - (type==NORMAL) ) ) - { - unsigned begin, end; - begin=flo_quantize(event.tick()+part->tick(), parent->quant_ticks()); - end=flo_quantize(event.endTick()+part->tick(), parent->quant_ticks()); - if (end==begin) - { - if (heavyDebugMsg) cout << "note len would be quantized to zero. using minimal possible length" << endl; - end=begin+parent->quant_ticks(); - } - - if (heavyDebugMsg) cout << "inserting note on at "<second->sig.z<<"/"<second->sig.n<<"; ticks per measure = "<(from, FloEvent(from, FloEvent::TIME_SIG, it->second->sig.z, it->second->sig.n) ) ); - for (unsigned t=from; t(t, FloEvent(t,0,0,ticks_per_measure,FloEvent::BAR) ) ); - } - - //insert key changes - for (MusECore::iKeyEvent it=MusEGlobal::keymap.begin(); it!=MusEGlobal::keymap.end(); it++) - eventlist.insert(pair(it->second.tick, FloEvent(it->second.tick,FloEvent::KEY_CHANGE, it->second.key ) ) ); - - - // phase two: deal with overlapping notes --------------------------- - ScoreEventList::iterator it, it2; - - //iterate through all note_on - events - for (it=eventlist.begin(); it!=eventlist.end(); it++) - if (it->second.type==FloEvent::NOTE_ON) - { - unsigned end_tick=it->first + it->second.len; - - //iterate though all (relevant) later note_ons which are - //at the same pitch. if there's a collision, shorten it's len - for (it2=it, it2++; it2!=eventlist.end() && it2->first < end_tick; it2++) - if ((it2->second.type==FloEvent::NOTE_ON) && (it2->second.pitch == it->second.pitch)) - it->second.len=it2->first - it->first; - } - - - // phase three: eliminate zero-length-notes ------------------------- - for (it=eventlist.begin(); it!=eventlist.end();) - if ((it->second.type==FloEvent::NOTE_ON) && (it->second.len<=0)) - eventlist.erase(it++); - else - it++; + //insert note on events + for (set::const_iterator part_it=parts.begin(); part_it!=parts.end(); part_it++) + { + const MusECore::Part* part=*part_it; + + for (MusECore::ciEvent it=part->events().begin(); it!=part->events().end(); it++) + { + const MusECore::Event& event=it->second; + + if ( ( event.isNote() && !event.isNoteOff() && + // (event.endTick() <= part->lenTick()) ) && + (event.tick() <= part->lenTick()) ) && // changed to accord to prcanvas.cpp and others (flo93) + ( ((type==GRAND_TOP) && (event.pitch() >= SPLIT_NOTE)) || + ((type==GRAND_BOTTOM) && (event.pitch() < SPLIT_NOTE)) || + (type==NORMAL) ) ) + { + unsigned begin, end; + begin=flo_quantize(event.tick()+part->tick(), parent->quant_ticks()); + end=flo_quantize(event.endTick()+part->tick(), parent->quant_ticks()); + if (end==begin) + { + if (heavyDebugMsg) cout << "note len would be quantized to zero. using minimal possible length" << endl; + end=begin+parent->quant_ticks(); + } + + if (heavyDebugMsg) cout << "inserting note on at "<second->sig.z<<"/"<second->sig.n<<"; ticks per measure = "<(from, FloEvent(from, FloEvent::TIME_SIG, it->second->sig.z, it->second->sig.n) ) ); + for (unsigned t=from; t(t, FloEvent(t,0,0,ticks_per_measure,FloEvent::BAR) ) ); + } + + //insert key changes + for (MusECore::iKeyEvent it=MusEGlobal::keymap.begin(); it!=MusEGlobal::keymap.end(); it++) + eventlist.insert(pair(it->second.tick, FloEvent(it->second.tick,FloEvent::KEY_CHANGE, it->second.key ) ) ); + + + // phase two: deal with overlapping notes --------------------------- + ScoreEventList::iterator it, it2; + + //iterate through all note_on - events + for (it=eventlist.begin(); it!=eventlist.end(); it++) { + if (it->second.type==FloEvent::NOTE_ON) + { + unsigned end_tick=it->first + it->second.len; + + //iterate though all (relevant) later note_ons which are + //at the same pitch. if there's a collision, shorten it's len + for (it2=it, it2++; it2!=eventlist.end() && it2->first < end_tick; it2++) + if ((it2->second.type==FloEvent::NOTE_ON) && (it2->second.pitch == it->second.pitch)) + it->second.len=it2->first - it->first; + } + } + + + // phase three: eliminate zero-length-notes ------------------------- + for (it=eventlist.begin(); it!=eventlist.end();) { + if ((it->second.type==FloEvent::NOTE_ON) && (it->second.len<=0)) + eventlist.erase(it++); + else + it++; + } } bool is_sharp_key(MusECore::key_enum t) { - return ((t>=MusECore::KEY_SHARP_BEGIN) && (t<=MusECore::KEY_SHARP_END)); + return ((t>=MusECore::KEY_SHARP_BEGIN) && (t<=MusECore::KEY_SHARP_END)); } bool is_b_key(MusECore::key_enum t) { - return ((t>=MusECore::KEY_B_BEGIN) && (t<=MusECore::KEY_B_END)); + return ((t>=MusECore::KEY_B_BEGIN) && (t<=MusECore::KEY_B_END)); } int n_accidentials(MusECore::key_enum t) { - if (is_sharp_key(t)) - return t-MusECore::KEY_SHARP_BEGIN-1; - else - return t-MusECore::KEY_B_BEGIN-1; + if (is_sharp_key(t)) + return t-MusECore::KEY_SHARP_BEGIN-1; + else + return t-MusECore::KEY_B_BEGIN-1; } @@ -1886,53 +1897,53 @@ //only for internal use note_pos_t note_pos_(int note, MusECore::key_enum key) { - note_pos_t result; - //C CIS D DIS E F FIS G GIS A AIS H - int foo[12]={0,-1, 1,-1, 2,3,-1, 4,-1, 5, -1,6}; - - if ((note<0) || (note>=12)) - cerr << "ERROR: ILLEGAL FUNCTION CALL (note_pos, note out of range)" << endl; - - if (foo[note]!=-1) - { - result.height=foo[note]; - result.vorzeichen=NONE; - } - else - { - if (is_sharp_key(key)) - { - result.height=foo[note-1]; - result.vorzeichen=SHARP; - } - else // if is_b_key - { - result.height=foo[note+1]; - result.vorzeichen=B; - } - } - - // Special cases for GES / FIS keys - if (key==MusECore::KEY_GES) - { - // convert a H to a Ces - if (note==11) - { - result.height=12; - result.vorzeichen=B; - } - } - else if (key==MusECore::KEY_FIS) - { - // convert a F to an Eis - if (note==5) - { - result.height=2; - result.vorzeichen=SHARP; - } - } + note_pos_t result; + //C CIS D DIS E F FIS G GIS A AIS H + int foo[12]={0,-1, 1,-1, 2,3,-1, 4,-1, 5, -1,6}; + + if ((note<0) || (note>=12)) + cerr << "ERROR: ILLEGAL FUNCTION CALL (note_pos, note out of range)" << endl; + + if (foo[note]!=-1) + { + result.height=foo[note]; + result.vorzeichen=NONE; + } + else + { + if (is_sharp_key(key)) + { + result.height=foo[note-1]; + result.vorzeichen=SHARP; + } + else // if is_b_key + { + result.height=foo[note+1]; + result.vorzeichen=B; + } + } + + // Special cases for GES / FIS keys + if (key==MusECore::KEY_GES) + { + // convert a H to a Ces + if (note==11) + { + result.height=12; + result.vorzeichen=B; + } + } + else if (key==MusECore::KEY_FIS) + { + // convert a F to an Eis + if (note==5) + { + result.height=2; + result.vorzeichen=SHARP; + } + } - return result; + return result; } @@ -1953,121 +1964,121 @@ note_pos_t note_pos (unsigned note, MusECore::key_enum key, clef_t clef) { - int octave=(note/12)-1; //integer division. note is unsigned - note=note%12; - - //now octave contains the octave the note is in - //(A4 is the 440Hz tone. C4 is the "low C" in the violin clef - //and the "high C" in the bass clef. - //note contains 0 for C, 1 for Cis, ..., 11 for H (or B if you're not german) - - note_pos_t pos=note_pos_(note,key); - - switch (clef) //CLEF_MARKER - { - case VIOLIN: - pos.height=pos.height + (octave-4)*7; - break; - - case BASS: - pos.height=pos.height + (octave-3)*7 + 5; - break; - } - - return pos; + int octave=(note/12)-1; //integer division. note is unsigned + note=note%12; + + //now octave contains the octave the note is in + //(A4 is the 440Hz tone. C4 is the "low C" in the violin clef + //and the "high C" in the bass clef. + //note contains 0 for C, 1 for Cis, ..., 11 for H (or B if you're not german) + + note_pos_t pos=note_pos_(note,key); + + switch (clef) //CLEF_MARKER + { + case VIOLIN: + pos.height=pos.height + (octave-4)*7; + break; + + case BASS: + pos.height=pos.height + (octave-3)*7 + 5; + break; + } + + return pos; } int calc_len(int l, int d) { - // l=0,1,2 -> whole, half, quarter (think of 2^0, 2^1, 2^2) - // d=number of dots - - int tmp=0; - for (int i=0;i<=d;i++) - tmp+=TICKS_PER_WHOLE / (1 << (l+i)); - - return tmp; + // l=0,1,2 -> whole, half, quarter (think of 2^0, 2^1, 2^2) + // d=number of dots + + int tmp=0; + for (int i=0;i<=d;i++) + tmp+=TICKS_PER_WHOLE / (1 << (l+i)); + + return tmp; } int calc_measure_len(const list& nums, int denom) { - int sum=0; - - for (list::const_iterator it=nums.begin(); it!=nums.end(); it++) - sum+=*it; - - return 64* sum/denom; + int sum=0; + + for (list::const_iterator it=nums.begin(); it!=nums.end(); it++) + sum+=*it; + + return 64* sum/denom; } vector create_emphasize_list(const list& nums, int denom) { - if (heavyDebugMsg) - { - cout << "creating emphasize list for "; - for (list::const_iterator it=nums.begin(); it!=nums.end(); it++) - cout << *it << " "; - cout << "/ "< result(len); - - for (int i=0;i::const_iterator it=nums.begin(); it!=nums.end(); it++) - { - result[pos]=1; - for (int i=1;i<*it;i++) - result[pos + i*64/denom]=2; - pos+= *it * 64 / denom; - } - - result[0]=0; - - if (heavyDebugMsg) - { - for (int i=0;i::const_iterator it=nums.begin(); it!=nums.end(); it++) + cout << *it << " "; + cout << "/ "< result(len); + + for (int i=0;i::const_iterator it=nums.begin(); it!=nums.end(); it++) + { + result[pos]=1; + for (int i=1;i<*it;i++) + result[pos + i*64/denom]=2; + pos+= *it * 64 / denom; + } + + result[0]=0; + + if (heavyDebugMsg) + { + for (int i=0;i create_emphasize_list(int num, int denom) { - list nums; - - if (num%3 ==0) - { - for (int i=0;i nums; + + if (num%3 ==0) + { + for (int i=0;i parse_note_len(int len_ticks, int begin_tick, vector& foo, bool allow_dots, bool allow_normal) { - list retval; - - if (len_ticks<0) - cerr << "ERROR: ILLEGAL FUNCTION CALL in parse_note_len: len_ticks < 0" << endl; - if (begin_tick<0) - cerr << "ERROR: ILLEGAL FUNCTION CALL in parse_note_len: begin_tick < 0" << endl; - - if (allow_normal) - { - int dot_max = allow_dots ? MAX_QUANT_POWER : 0; - - for (int i=0;i<=MAX_QUANT_POWER;i++) - for (int j=0;j<=dot_max-i;j++) - if (calc_len(i,j) == len_ticks) - { - retval.push_back(note_len_t (i,j)); - return retval; - } - } - - //if !allow_normal or if the above failed - - int begin=begin_tick * 64 / TICKS_PER_WHOLE; - int len=len_ticks * 64 / TICKS_PER_WHOLE; - - unsigned pos=begin; - int len_done=0; - - while (len_donefirst, it->second.type, it->second.pitch, it->second.vel, it->second.len); - cout << "\tline="<quant_ticks())/2; - if (heavyDebugMsg) cout << "\tend-of-measure: this was an empty measure. inserting rest in between at t="< lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS); - unsigned tmppos=lastevent; - for (list::iterator x=lens.begin(); x!=lens.end(); x++) - { - if (heavyDebugMsg) cout << "\t\tpartial rest with len="<len<<", dots="<dots<len,x->dots) ); - tmppos+=calc_len(x->len,x->dots); - itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); - } - } - } - } - - lastevent=t; - last_measure=t; - next_measure=t+len; - - itemlist[t].insert( FloItem(FloItem::BAR,no_notepos,0,0) ); - } - else if (type==FloEvent::NOTE_ON) - { - int rest=t-lastevent; - if (rest) - { - if (heavyDebugMsg) printf("\tset rest at %i with len %i\n",lastevent,rest); - // no need to check if the rest crosses measure boundaries; - // it can't. - - list lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS); - unsigned tmppos=lastevent; - for (list::iterator x=lens.begin(); x!=lens.end(); x++) - { - if (heavyDebugMsg) cout << "\t\tpartial rest with len="<len<<", dots="<dots<len,x->dots) ); - tmppos+=calc_len(x->len,x->dots); - itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); - } - } - - - - if (heavyDebugMsg) printf("\tset note at %i with len=%i\n", t, len); - - int tmplen; - bool tied_note; - - // if the note exceeds the current measure, split it. - if (t+len>next_measure) - { - tmplen=next_measure-t; - tied_note=true; - - //append the "remainder" of the note to our EventList, so that - //it gets processed again when entering the new measure - int newlen=len-tmplen; - eventlist.insert(pair(next_measure, FloEvent(actual_tick,pitch, velo,0,FloEvent::NOTE_OFF, it->second.source_part, it->second.source_event))); - eventlist.insert(pair(next_measure, FloEvent(actual_tick,pitch, velo,newlen,FloEvent::NOTE_ON, it->second.source_part, it->second.source_event))); - - if (heavyDebugMsg) cout << "\t\tnote was split to length "<(t+len, FloEvent(t+len,pitch, velo,0,FloEvent::NOTE_OFF,it->second.source_part, it->second.source_event))); - } - - list lens=parse_note_len(tmplen,t-last_measure,emphasize_list,true,true); - unsigned tmppos=t; - int n_lens=lens.size(); - int count=0; - for (list::iterator x=lens.begin(); x!=lens.end(); x++) - { - if (heavyDebugMsg) cout << "\t\tpartial note with len="<len<<", dots="<dots<len,x->dots, tie, actual_tick, it->second.source_part, it->second.source_event) ); - tmppos+=calc_len(x->len,x->dots); - itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,notepos,0,0) ); - } - } - else if (type==FloEvent::NOTE_OFF) - { - lastevent=t; - } - else if (type==FloEvent::TIME_SIG) - { - if (heavyDebugMsg) cout << "inserting TIME SIGNATURE "<second.num<<"/"<second.denom<<" at "<second.num, it->second.denom) ); - - emphasize_list=create_emphasize_list(it->second.num, it->second.denom); - } - else if (type==FloEvent::KEY_CHANGE) - { - if (heavyDebugMsg) cout << "inserting KEY CHANGE ("<second.key<<") at "<second.key) ); - tmp_key=it->second.key; - } - } + MusECore::key_enum tmp_key=MusECore::KEY_C; + int lastevent=0; + int next_measure=-1; + int last_measure=-1; + vector emphasize_list=create_emphasize_list(4,4); //actually unnecessary, for safety + + itemlist.clear(); + + for (ScoreEventList::iterator it=eventlist.begin(); it!=eventlist.end(); it++) + { + int t, pitch, len, velo, actual_tick; + FloEvent::typeEnum type; + t=it->first; + pitch=it->second.pitch; + velo=it->second.vel; + len=it->second.len; + type=it->second.type; + actual_tick=it->second.tick; + if (actual_tick==-1) actual_tick=t; + + note_pos_t notepos=note_pos(pitch,tmp_key,clef); + + if (heavyDebugMsg) + { + printf("FLO: t=%i\ttype=%i\tpitch=%i\tvel=%i\tlen=%i\n",it->first, it->second.type, it->second.pitch, it->second.vel, it->second.len); + cout << "\tline="<quant_ticks())/2; + if (heavyDebugMsg) cout << "\tend-of-measure: this was an empty measure. inserting rest in between at t="< lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS); + unsigned tmppos=lastevent; + for (list::iterator x=lens.begin(); x!=lens.end(); x++) + { + if (heavyDebugMsg) cout << "\t\tpartial rest with len="<len<<", dots="<dots<len,x->dots) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); + } + } + } + } + + lastevent=t; + last_measure=t; + next_measure=t+len; + + itemlist[t].insert( FloItem(FloItem::BAR,no_notepos,0,0) ); + } + else if (type==FloEvent::NOTE_ON) + { + int rest=t-lastevent; + if (rest) + { + if (heavyDebugMsg) printf("\tset rest at %i with len %i\n",lastevent,rest); + // no need to check if the rest crosses measure boundaries; + // it can't. + + list lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS); + unsigned tmppos=lastevent; + for (list::iterator x=lens.begin(); x!=lens.end(); x++) + { + if (heavyDebugMsg) cout << "\t\tpartial rest with len="<len<<", dots="<dots<len,x->dots) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); + } + } + + + + if (heavyDebugMsg) printf("\tset note at %i with len=%i\n", t, len); + + int tmplen; + bool tied_note; + + // if the note exceeds the current measure, split it. + if (t+len>next_measure) + { + tmplen=next_measure-t; + tied_note=true; + + //append the "remainder" of the note to our EventList, so that + //it gets processed again when entering the new measure + int newlen=len-tmplen; + eventlist.insert(pair(next_measure, FloEvent(actual_tick,pitch, velo,0,FloEvent::NOTE_OFF, it->second.source_part, it->second.source_event))); + eventlist.insert(pair(next_measure, FloEvent(actual_tick,pitch, velo,newlen,FloEvent::NOTE_ON, it->second.source_part, it->second.source_event))); + + if (heavyDebugMsg) cout << "\t\tnote was split to length "<(t+len, FloEvent(t+len,pitch, velo,0,FloEvent::NOTE_OFF,it->second.source_part, it->second.source_event))); + } + + list lens=parse_note_len(tmplen,t-last_measure,emphasize_list,true,true); + unsigned tmppos=t; + int n_lens=lens.size(); + int count=0; + for (list::iterator x=lens.begin(); x!=lens.end(); x++) + { + if (heavyDebugMsg) cout << "\t\tpartial note with len="<len<<", dots="<dots<len,x->dots, tie, actual_tick, it->second.source_part, it->second.source_event) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,notepos,0,0) ); + } + } + else if (type==FloEvent::NOTE_OFF) + { + lastevent=t; + } + else if (type==FloEvent::TIME_SIG) + { + if (heavyDebugMsg) cout << "inserting TIME SIGNATURE "<second.num<<"/"<second.denom<<" at "<second.num, it->second.denom) ); + + emphasize_list=create_emphasize_list(it->second.num, it->second.denom); + } + else if (type==FloEvent::KEY_CHANGE) + { + if (heavyDebugMsg) cout << "inserting KEY CHANGE ("<second.key<<") at "<second.key) ); + tmp_key=it->second.key; + } + } } void staff_t::process_itemlist() { - map occupied; - int last_measure=0; - vector emphasize_list=create_emphasize_list(4,4); //unneccessary, only for safety - - //iterate through all times with items - for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) - { - set& curr_items=it2->second; - - if (heavyDebugMsg) cout << "at t="<first<::iterator it=curr_items.begin(); it!=curr_items.end(); it++) - { - if ((it->type==FloItem::NOTE) || (it->type==FloItem::REST)) - occupied[it->pos.height]++; - else if ((it->type==FloItem::NOTE_END) || (it->type==FloItem::REST_END)) - occupied[it->pos.height]--; - else if (it->type==FloItem::BAR) - last_measure=it2->first; - else if (it->type==FloItem::TIME_SIG) - emphasize_list=create_emphasize_list(it->num, it->denom); - } - - if (heavyDebugMsg) - { - cout << "occupied: "; - for (map::iterator i=occupied.begin(); i!=occupied.end(); i++) - if (i->second) cout << i->first << "("<second<<") "; - cout << endl; - } - - - - - - // phase 1: group rests together ----------------------------------- - int n_groups=0; - bool dont_group=false; - - //iterate through all rests R at that time - // iterate through all rests X at that time below R - // if something is between X and R ("barrier"), stop - // else: group them together - for (set::iterator it=curr_items.begin(); it!=curr_items.end();) - { - //only operate on rests; ignore rests which are created by this code - //(can be seen on already_grouped) - if ((it->type==FloItem::REST) && (it->already_grouped==false)) - { - if (heavyDebugMsg) cout << "trying to group" << endl; - - int lastheight; - int height_cumulative=0; - int counter=0; - - lastheight=it->pos.height; - - set::iterator tmp; - for (tmp=it; tmp!=curr_items.end();) - { - if (heavyDebugMsg) cout << "checking if we can proceed with an item at height="<pos.height<pos.height-1; i++) - if (occupied[i]!=0) - { - if (heavyDebugMsg) cout << "we can NOT, because occ["<pos.height; - - // the current item is a rest with equal len? cool! - if (tmp->type==FloItem::REST && tmp->len==it->len && tmp->dots==it->dots) - { - // füge diese pause zur gruppe dazu und entferne sie von diesem set hier - // entfernen aber nur, wenn sie nicht it, also die erste pause ist, die brauchen wir noch! - if (heavyDebugMsg) cout << "\tgrouping rest at height="<pos.height<pos.height; - counter++; - if (tmp!=it) - curr_items.erase(tmp++); - else - tmp++; - } - else //it's something else? well, we can stop grouping that rest then - { - if (heavyDebugMsg) cout << "we can NOT, because that item is not a rest" << endl; - //stop grouping that rest - goto get_out_here; - } - } - if (heavyDebugMsg) cout << "no items to proceed on left, continuing" << endl; - get_out_here: - - n_groups++; - - // entferne it vom set und - // füge eine pause mit dem "mittelwert" ein. - // occupied und die "_END"-events bleiben unberührt - - FloItem temp=*it; - temp.already_grouped=true; - - // have we grouped all available rests into one single? - if ( (n_groups==1) && (tmp==curr_items.end()) && !dont_group) - { - if (heavyDebugMsg) cout << "wow, we were able to group all rests into one single" << endl; - if (temp.len==0) //the whole rest is shifted one line (one space and one line) - temp.pos.height=DEFAULT_REST_HEIGHT+2; - else - temp.pos.height=DEFAULT_REST_HEIGHT; - } - else - { - if (heavyDebugMsg) cout << "creating group #"<height, the set considers temp and it equal (it doesn't - // take already_grouped into account) - // the result of this: insert does nothing, and erase erases - // the item. effect: you don't have the rest at all - curr_items.erase(it++); - - if (heavyDebugMsg) cout << "replacing all grouped rests with a rest at height="<type==FloItem::NOTE) - dont_group=true; - - it++; - } - } - - - - - - // phase 2: avoid collisions of items ------------------------------ - set::iterator lastit, groupbegin, invalid; - invalid=curr_items.end(); - lastit=invalid; - groupbegin=invalid; - int count; - - //TODO: is "grouping" notes and rests together okay? - // or is it better to ignore rests when grouping? - for (set::iterator it=curr_items.begin(); it!=curr_items.end(); it++) - if ( (it->type==FloItem::NOTE) || (it->type==FloItem::REST) ) - { - if (lastit != invalid) - { - if (it->pos.height == lastit->pos.height+1) // they would collide? - { - if (groupbegin==invalid) // we have no group atm? - { - groupbegin=lastit; // start a new group - count=1; // because lastit has to be taken into account. - // for "it", there's a count++ later - } - - // the following will work even on start-new-group, - // because lastit will be "untouched", and that's why - // still be initalized to "false" - it->ausweich=!lastit->ausweich; - - count++; - } - else - { - if (groupbegin!=invalid) //this is the first item which - { //doesn't belong to the previous group any more - if (count%2 == 0) //count is even? - if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) - for (set::iterator tmp=groupbegin; tmp!=it; tmp++) - tmp->ausweich=!tmp->ausweich; - - groupbegin=invalid; - } - // else: everything is ok :) - } - } - - lastit=it; - } - - // this could be the case if the last processed item before end() - // still belonged to a group. finalize this last group as well: - if (groupbegin!=invalid) - { - if (count%2 == 0) //count is even? - if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) - for (set::iterator tmp=groupbegin; tmp!=curr_items.end(); tmp++) - tmp->ausweich=!tmp->ausweich; - } - // else: everything is ok :) - - - - - - // phase 3: group notes by their length and ------------------------ - // find out appropriate stem directions + map occupied; + int last_measure=0; + vector emphasize_list=create_emphasize_list(4,4); //unnecessary, only for safety + + //iterate through all times with items + for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) + { + set& curr_items=it2->second; + + if (heavyDebugMsg) cout << "at t="<first<::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + { + if ((it->type==FloItem::NOTE) || (it->type==FloItem::REST)) + occupied[it->pos.height]++; + else if ((it->type==FloItem::NOTE_END) || (it->type==FloItem::REST_END)) + occupied[it->pos.height]--; + else if (it->type==FloItem::BAR) + last_measure=it2->first; + else if (it->type==FloItem::TIME_SIG) + emphasize_list=create_emphasize_list(it->num, it->denom); + } + + if (heavyDebugMsg) + { + cout << "occupied: "; + for (map::iterator i=occupied.begin(); i!=occupied.end(); i++) + if (i->second) cout << i->first << "("<second<<") "; + cout << endl; + } + + + + + + // phase 1: group rests together ----------------------------------- + int n_groups=0; + bool dont_group=false; + + //iterate through all rests R at that time + // iterate through all rests X at that time below R + // if something is between X and R ("barrier"), stop + // else: group them together + for (set::iterator it=curr_items.begin(); it!=curr_items.end();) + { + //only operate on rests; ignore rests which are created by this code + //(can be seen on already_grouped) + if ((it->type==FloItem::REST) && (it->already_grouped==false)) + { + if (heavyDebugMsg) cout << "trying to group" << endl; + + int lastheight; + int height_cumulative=0; + int counter=0; + + lastheight=it->pos.height; + + set::iterator tmp; + for (tmp=it; tmp!=curr_items.end();) + { + if (heavyDebugMsg) cout << "checking if we can proceed with an item at height="<pos.height<pos.height-1; i++) + if (occupied[i]!=0) + { + if (heavyDebugMsg) cout << "we can NOT, because occ["<pos.height; + + // the current item is a rest with equal len? cool! + if (tmp->type==FloItem::REST && tmp->len==it->len && tmp->dots==it->dots) + { + // füge diese pause zur gruppe dazu und entferne sie von diesem set hier + // entfernen aber nur, wenn sie nicht it, also die erste pause ist, die brauchen wir noch! + if (heavyDebugMsg) cout << "\tgrouping rest at height="<pos.height<pos.height; + counter++; + if (tmp!=it) + curr_items.erase(tmp++); + else + tmp++; + } + else //it's something else? well, we can stop grouping that rest then + { + if (heavyDebugMsg) cout << "we can NOT, because that item is not a rest" << endl; + //stop grouping that rest + goto get_out_here; + } + } + if (heavyDebugMsg) cout << "no items to proceed on left, continuing" << endl; + get_out_here: + + n_groups++; + + // entferne it vom set und + // füge eine pause mit dem "mittelwert" ein. + // occupied und die "_END"-events bleiben unberührt + + FloItem temp=*it; + temp.already_grouped=true; + + // have we grouped all available rests into one single? + if ( (n_groups==1) && (tmp==curr_items.end()) && !dont_group) + { + if (heavyDebugMsg) cout << "wow, we were able to group all rests into one single" << endl; + if (temp.len==0) //the whole rest is shifted one line (one space and one line) + temp.pos.height=DEFAULT_REST_HEIGHT+2; + else + temp.pos.height=DEFAULT_REST_HEIGHT; + } + else + { + if (heavyDebugMsg) cout << "creating group #"<height, the set considers temp and it equal (it doesn't + // take already_grouped into account) + // the result of this: insert does nothing, and erase erases + // the item. effect: you don't have the rest at all + curr_items.erase(it++); + + if (heavyDebugMsg) cout << "replacing all grouped rests with a rest at height="<type==FloItem::NOTE) + dont_group=true; + + it++; + } + } + + + + + + // phase 2: avoid collisions of items ------------------------------ + set::iterator lastit, groupbegin, invalid; + invalid=curr_items.end(); + lastit=invalid; + groupbegin=invalid; + int count = 0; + + //TODO: is "grouping" notes and rests together okay? + // or is it better to ignore rests when grouping? + for (set::iterator it=curr_items.begin(); it!=curr_items.end(); it++) { + if ( (it->type==FloItem::NOTE) || (it->type==FloItem::REST) ) + { + if (lastit != invalid) + { + if (it->pos.height == lastit->pos.height+1) // they would collide? + { + if (groupbegin==invalid) // we have no group atm? + { + groupbegin=lastit; // start a new group + count=1; // because lastit has to be taken into account. + // for "it", there's a count++ later + } + + // the following will work even on start-new-group, + // because lastit will be "untouched", and that's why + // still be initialized to "false" + it->ausweich=!lastit->ausweich; + + count++; + } + else + { + if (groupbegin!=invalid) //this is the first item which + { //doesn't belong to the previous group any more + if (count%2 == 0) //count is even? + if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) + for (set::iterator tmp=groupbegin; tmp!=it; tmp++) + tmp->ausweich=!tmp->ausweich; + + groupbegin=invalid; + } + // else: everything is ok :) + } + } + + lastit=it; + } + } + + // this could be the case if the last processed item before end() + // still belonged to a group. finalize this last group as well: + if (groupbegin!=invalid) + { + if (count%2 == 0) //count is even? + if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) + for (set::iterator tmp=groupbegin; tmp!=curr_items.end(); tmp++) + tmp->ausweich=!tmp->ausweich; + } + // else: everything is ok :) + + + + + + // phase 3: group notes by their length and ------------------------ + // find out appropriate stem directions group_them_again: - map lengths; - bool has_whole=false; - - // find out which note lengths are present at that time - for (set::iterator it=curr_items.begin(); it!=curr_items.end(); it++) - if (it->type==FloItem::NOTE) - lengths[it->len].add(it->pos.height); - - if (heavyDebugMsg) - { - cout << "note lengths at that time are:"; - for (map::iterator it=lengths.begin(); it!=lengths.end(); it++) - cout << it->first << "("<< it->second.mean() <<") "; - cout << endl; - } - - if (lengths.erase(0)) // in case "0" is in the set, erase it - has_whole=true; // but remember there were whole notes - - if (lengths.size()==0) - { - if (heavyDebugMsg) cout << "no notes other than wholes, or no notes at all. we can relax" << endl; - } - else if (lengths.size()==1) - { - pair& group=*(lengths.begin()); - stem_t stem; - int shift=0; - if (heavyDebugMsg) cout << "only one non-whole note group (len="<::iterator it=curr_items.begin(); it!=curr_items.end();) - if (it->type==FloItem::NOTE) - { - //if *it belongs to group1 and has not already its destination length - if (heavyDebugMsg) cout << "\tprocessing note-item with len="<len<lenfirst+group1_len_ticks; - - itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); - - list lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true); - unsigned tmppos=t; - int n_lens=lens.size(); - int count=0; - for (list::iterator x=lens.begin(); x!=lens.end(); x++) - { - if (heavyDebugMsg) cout << "\t\twhile regrouping: partial note with len="<len<<", dots="<dots<len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); - tmppos+=calc_len(x->len,x->dots); - itemlist[tmppos].insert( FloItem(FloItem::NOTE_END, tmp.pos,0,0) ); - } - - } - //else if *it belongs to group2 and has not already its destination length - else if ((it->lenlen>group1_len)) - { - if (heavyDebugMsg) cout << "\t\thas to be changed to fit into group 2" << endl; - - FloItem tmp=*it; - curr_items.erase(it++); - - int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group2_len_ticks; - bool tied_note=tmp.tied; - - - //shorten the current item to it's group's length - tmp.len=group2_len; - tmp.dots=0; - tmp.tied=true; - curr_items.insert(tmp); - - //create items for the remaining lengths (and a note_END for the just created shortened note) - int t=it2->first+group2_len_ticks; - - itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); - - list lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true); - unsigned tmppos=t; - int n_lens=lens.size(); - int count=0; - for (list::iterator x=lens.begin(); x!=lens.end(); x++) - { - if (heavyDebugMsg) cout << "\t\twhile regrouping: partial note with len="<len<<", dots="<dots<len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); - tmppos+=calc_len(x->len,x->dots); - itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); - } - - } - else //nothing to do? - { - if (heavyDebugMsg) cout << "\t\tnothing to do" << endl; - it++; - } - } - else - it++; - - goto group_them_again; //do it again - } + map lengths; + bool has_whole=false; + + // find out which note lengths are present at that time + for (set::iterator it=curr_items.begin(); it!=curr_items.end(); it++) + if (it->type==FloItem::NOTE) + lengths[it->len].add(it->pos.height); + + if (heavyDebugMsg) + { + cout << "note lengths at that time are:"; + for (map::iterator it=lengths.begin(); it!=lengths.end(); it++) + cout << it->first << "("<< it->second.mean() <<") "; + cout << endl; + } + + if (lengths.erase(0)) // in case "0" is in the set, erase it + has_whole=true; // but remember there were whole notes + + if (lengths.size()==0) + { + if (heavyDebugMsg) cout << "no notes other than wholes, or no notes at all. we can relax" << endl; + } + else if (lengths.size()==1) + { + pair& group=*(lengths.begin()); + stem_t stem; + int shift=0; + if (heavyDebugMsg) cout << "only one non-whole note group (len="<::iterator it=curr_items.begin(); it!=curr_items.end();) + if (it->type==FloItem::NOTE) + { + //if *it belongs to group1 and has not already its destination length + if (heavyDebugMsg) cout << "\tprocessing note-item with len="<len<lenfirst+group1_len_ticks; + + itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); + + list lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true); + unsigned tmppos=t; + int n_lens=lens.size(); + int count=0; + for (list::iterator x=lens.begin(); x!=lens.end(); x++) + { + if (heavyDebugMsg) cout << "\t\twhile regrouping: partial note with len="<len<<", dots="<dots<len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::NOTE_END, tmp.pos,0,0) ); + } + + } + //else if *it belongs to group2 and has not already its destination length + else if ((it->lenlen>group1_len)) + { + if (heavyDebugMsg) cout << "\t\thas to be changed to fit into group 2" << endl; + + FloItem tmp=*it; + curr_items.erase(it++); + + int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group2_len_ticks; + bool tied_note=tmp.tied; + + + //shorten the current item to it's group's length + tmp.len=group2_len; + tmp.dots=0; + tmp.tied=true; + curr_items.insert(tmp); + + //create items for the remaining lengths (and a note_END for the just created shortened note) + int t=it2->first+group2_len_ticks; + + itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); + + list lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true); + unsigned tmppos=t; + int n_lens=lens.size(); + int count=0; + for (list::iterator x=lens.begin(); x!=lens.end(); x++) + { + if (heavyDebugMsg) cout << "\t\twhile regrouping: partial note with len="<len<<", dots="<dots<len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); + tmppos+=calc_len(x->len,x->dots); + itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); + } + + } + else //nothing to do? + { + if (heavyDebugMsg) cout << "\t\tnothing to do" << endl; + it++; + } + } + else + it++; - } + goto group_them_again; //do it again + } + + } } //draw a pixmap centered void ScoreCanvas::draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm) { - if (heavyDebugMsg) cout << "drawing pixmap with size="<size()); + return bbox_center(x,y,pix->size()); } void ScoreCanvas::draw_note_lines(QPainter& p, int y, bool reserve_akkolade_space) { - int xbegin = reserve_akkolade_space ? AKKOLADE_LEFTMARGIN+AKKOLADE_WIDTH+AKKOLADE_RIGHTMARGIN : 0; - int xend=width(); - // FINDMICHJETZT y is uninitalized! - - p.setPen(Qt::black); - - for (int i=0;i<5;i++) - p.drawLine(xbegin, y + i*YLEN - 2*YLEN, xend, y + i*YLEN - 2*YLEN); + int xbegin = reserve_akkolade_space ? AKKOLADE_LEFTMARGIN+AKKOLADE_WIDTH+AKKOLADE_RIGHTMARGIN : 0; + int xend=width(); + // FINDMICHJETZT y is uninitialized! + + p.setPen(Qt::black); + + for (int i=0;i<5;i++) + p.drawLine(xbegin, y + i*YLEN - 2*YLEN, xend, y + i*YLEN - 2*YLEN); } void staff_t::calc_item_pos() { - //this has to be KEY_C or KEY_C_B and nothing else, - //because only with these two keys the next (initial) - //key signature is properly drawn. - MusECore::key_enum curr_key=MusECore::KEY_C; - - int pos_add=0; - - max_y_coord=0; - min_y_coord=0; - - for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) - { - for (set::iterator it=it2->second.begin(); it!=it2->second.end();it++) - { - it->x=it2->first * parent->pixels_per_whole()/TICKS_PER_WHOLE +pos_add; - //if this changes, also change the line(s) with YLEN (but not all). don't change it. - it->y=2*YLEN - (it->pos.height-2)*YLEN/2; - - if (it->type==FloItem::NOTE) - { - if (it->y > max_y_coord) max_y_coord=it->y; - if (it->y < min_y_coord) min_y_coord=it->y; - - it->x+=parent->note_x_indent() + it->shift*NOTE_SHIFT; - - switch (it->len) - { - case 0: it->pix=pix_whole; break; - case 1: it->pix=pix_half; break; - default: it->pix=pix_quarter; break; - } - - it->stem_x=it->x; - - if (it->ausweich) - { - if ((it->stem==UPWARDS) || (it->len==0)) - it->x += it->pix->width()-1; //AUSWEICH_X - else - it->x -= it->pix->width()-1; //AUSWEICH_X - } - - //if there's a tie, try to find the tie's destination and set is_tie_dest - if (it->tied) - { - set::iterator dest; - set& desttime = itemlist[it2->first+calc_len(it->len,it->dots)]; - for (dest=desttime.begin(); dest!=desttime.end();dest++) - if ((dest->type==FloItem::NOTE) && (dest->pos==it->pos)) - { - dest->is_tie_dest=true; - dest->tie_from_x=it->x; - break; - } - - if (dest==desttime.end()) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: did not find destination note for tie!" << endl; - } - } - else if (it->type==FloItem::REST) - { - switch (it->len) - { - case 0: it->pix=pix_r1; break; - case 1: it->pix=pix_r2; break; - case 2: it->pix=pix_r4; break; - case 3: it->pix=pix_r8; break; - case 4: it->pix=pix_r16; break; - case 5: it->pix=pix_r32; break; - } - - it->x+=parent->note_x_indent() + (it->ausweich ? REST_AUSWEICH_X : 0); //AUSWEICH_X - } - else if (it->type==FloItem::BAR) - { - //nothing to do :) - } - else if (it->type==FloItem::TIME_SIG) - { - int add=calc_timesig_width(it->num, it->denom); - pos_add+=add; - } - else if (it->type==FloItem::KEY_CHANGE) - { - MusECore::key_enum new_key=it->key; - - list aufloes_list=calc_accidentials(curr_key, clef, new_key); - list new_acc_list=calc_accidentials(new_key, clef); - - int n_acc_drawn=aufloes_list.size() + new_acc_list.size(); - pos_add+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; - - curr_key=new_key; - } - } - } + //this has to be KEY_C or KEY_C_B and nothing else, + //because only with these two keys the next (initial) + //key signature is properly drawn. + MusECore::key_enum curr_key=MusECore::KEY_C; + + int pos_add=0; + + max_y_coord=0; + min_y_coord=0; + + for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) + { + for (set::iterator it=it2->second.begin(); it!=it2->second.end();it++) + { + it->x=it2->first * parent->pixels_per_whole()/TICKS_PER_WHOLE +pos_add; + //if this changes, also change the line(s) with YLEN (but not all). don't change it. + it->y=2*YLEN - (it->pos.height-2)*YLEN/2; + + if (it->type==FloItem::NOTE) + { + if (it->y > max_y_coord) max_y_coord=it->y; + if (it->y < min_y_coord) min_y_coord=it->y; + + it->x+=parent->note_x_indent() + it->shift*NOTE_SHIFT; + + switch (it->len) + { + case 0: it->pix=pix_whole; break; + case 1: it->pix=pix_half; break; + default: it->pix=pix_quarter; break; + } + + it->stem_x=it->x; + + if (it->ausweich) + { + if ((it->stem==UPWARDS) || (it->len==0)) + it->x += it->pix->width()-1; //AUSWEICH_X + else + it->x -= it->pix->width()-1; //AUSWEICH_X + } + + //if there's a tie, try to find the tie's destination and set is_tie_dest + if (it->tied) + { + set::iterator dest; + set& desttime = itemlist[it2->first+calc_len(it->len,it->dots)]; + for (dest=desttime.begin(); dest!=desttime.end();dest++) + if ((dest->type==FloItem::NOTE) && (dest->pos==it->pos)) + { + dest->is_tie_dest=true; + dest->tie_from_x=it->x; + break; + } + + if (dest==desttime.end()) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: did not find destination note for tie!" << endl; + } + } + else if (it->type==FloItem::REST) + { + switch (it->len) + { + case 0: it->pix=pix_r1; break; + case 1: it->pix=pix_r2; break; + case 2: it->pix=pix_r4; break; + case 3: it->pix=pix_r8; break; + case 4: it->pix=pix_r16; break; + case 5: it->pix=pix_r32; break; + } + + it->x+=parent->note_x_indent() + (it->ausweich ? REST_AUSWEICH_X : 0); //AUSWEICH_X + } + else if (it->type==FloItem::BAR) + { + //nothing to do :) + } + else if (it->type==FloItem::TIME_SIG) + { + int add=calc_timesig_width(it->num, it->denom); + pos_add+=add; + } + else if (it->type==FloItem::KEY_CHANGE) + { + MusECore::key_enum new_key=it->key; + + list aufloes_list=calc_accidentials(curr_key, clef, new_key); + list new_acc_list=calc_accidentials(new_key, clef); + + int n_acc_drawn=aufloes_list.size() + new_acc_list.size(); + pos_add+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; + + curr_key=new_key; + } + } + } - max_y_coord+= (pix_quarter->height()/2 +NOTE_YDIST/2); - min_y_coord-= (pix_quarter->height()/2 +NOTE_YDIST/2); + max_y_coord+= (pix_quarter->height()/2 +NOTE_YDIST/2); + min_y_coord-= (pix_quarter->height()/2 +NOTE_YDIST/2); } void ScoreCanvas::calc_pos_add_list() { - using AL::sigmap; - using AL::iSigEvent; + using AL::sigmap; + using AL::iSigEvent; + + + pos_add_list.clear(); + + //process time signatures + for (iSigEvent it=sigmap.begin(); it!=sigmap.end(); it++) + pos_add_list[it->second->tick]+=calc_timesig_width(it->second->sig.z, it->second->sig.n); - - pos_add_list.clear(); - - //process time signatures - for (iSigEvent it=sigmap.begin(); it!=sigmap.end(); it++) - pos_add_list[it->second->tick]+=calc_timesig_width(it->second->sig.z, it->second->sig.n); - - - //process key changes - - //this has to be KEY_C or KEY_C_B and nothing else, - //because only with these two keys the next (initial) - //key signature is properly calculated. - MusECore::key_enum curr_key=MusECore::KEY_C; - - - for (MusECore::iKeyEvent it=MusEGlobal::keymap.begin(); it!=MusEGlobal::keymap.end(); it++) - { - MusECore::key_enum new_key=it->second.key; - list aufloes_list=calc_accidentials(curr_key, VIOLIN, new_key); //clef argument is unneccessary - list new_acc_list=calc_accidentials(new_key, VIOLIN); //in this case - int n_acc_drawn=aufloes_list.size() + new_acc_list.size(); - pos_add_list[it->second.tick]+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; - - curr_key=new_key; - } - emit pos_add_changed(); + //process key changes + + //this has to be KEY_C or KEY_C_B and nothing else, + //because only with these two keys the next (initial) + //key signature is properly calculated. + MusECore::key_enum curr_key=MusECore::KEY_C; + + + for (MusECore::iKeyEvent it=MusEGlobal::keymap.begin(); it!=MusEGlobal::keymap.end(); it++) + { + MusECore::key_enum new_key=it->second.key; + list aufloes_list=calc_accidentials(curr_key, VIOLIN, new_key); //clef argument is unnecessary + list new_acc_list=calc_accidentials(new_key, VIOLIN); //in this case + int n_acc_drawn=aufloes_list.size() + new_acc_list.size(); + pos_add_list[it->second.tick]+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; + + curr_key=new_key; + } + + emit pos_add_changed(); } void ScoreCanvas::draw_items(QPainter& p, int y, staff_t& staff, int x1, int x2) { - int from_tick, to_tick; - ScoreItemList::iterator from_it, to_it; + int from_tick, to_tick; + ScoreItemList::iterator from_it, to_it; - //drawing too much isn't bad. drawing too few is. + //drawing too much isn't bad. drawing too few is. - from_tick=x_to_tick(x1); - from_it=staff.itemlist.lower_bound(from_tick); - //from_it now contains the first time which is fully drawn - //however, the previous beat could still be relevant, when it's - //partly drawn. so we decrement from_it - if (from_it!=staff.itemlist.begin()) from_it--; - - //decrement until we're at a time with a bar - //otherwise, drawing accidentials will be broken - while (from_it!=staff.itemlist.begin() && from_it->second.find(FloItem(FloItem::BAR))==from_it->second.end()) - from_it--; - - - to_tick=x_to_tick(x2); - to_it=staff.itemlist.upper_bound(to_tick); - //to_it now contains the first time which is not drawn at all any more - //however, a tie from 1:04 to 2:01 is stored in 2:01, not in 1:04, - //so for drawing ties, we need to increment to_it, so that the - //"first time not drawn at all any more" is the last which gets - //actually drawn. - if (to_it!=staff.itemlist.end()) to_it++; //do one tick more than neccessary. this will draw ties + from_tick=x_to_tick(x1); + from_it=staff.itemlist.lower_bound(from_tick); + //from_it now contains the first time which is fully drawn + //however, the previous beat could still be relevant, when it's + //partly drawn. so we decrement from_it + if (from_it!=staff.itemlist.begin()) from_it--; + + //decrement until we're at a time with a bar + //otherwise, drawing accidentials will be broken + while (from_it!=staff.itemlist.begin() && from_it->second.find(FloItem(FloItem::BAR))==from_it->second.end()) + from_it--; + + + to_tick=x_to_tick(x2); + to_it=staff.itemlist.upper_bound(to_tick); + //to_it now contains the first time which is not drawn at all any more + //however, a tie from 1:04 to 2:01 is stored in 2:01, not in 1:04, + //so for drawing ties, we need to increment to_it, so that the + //"first time not drawn at all any more" is the last which gets + //actually drawn. + if (to_it!=staff.itemlist.end()) to_it++; //do one tick more than necessary. this will draw ties - draw_items(p,y, staff, from_it, to_it); + draw_items(p,y, staff, from_it, to_it); } void ScoreCanvas::draw_items(QPainter& p, int y, staff_t& staff) { - draw_items(p,y, staff,x_pos,x_pos+width()-x_left); + draw_items(p,y, staff,x_pos,x_pos+width()-x_left); } void ScoreCanvas::draw_items(QPainter& p, int y_offset, staff_t& staff, ScoreItemList::iterator from_it, ScoreItemList::iterator to_it) { - // init accidentials properly - vorzeichen_t curr_accidential[7]; - vorzeichen_t default_accidential[7]; - MusECore::key_enum curr_key; - - curr_key=key_at_tick(from_it->first); - list new_acc_list=calc_accidentials(curr_key, staff.clef); - vorzeichen_t new_accidential = is_sharp_key(curr_key) ? SHARP : B; - - for (int i=0;i<7;i++) - curr_accidential[i]=default_accidential[i]=NONE; - - for (list::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++) - default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential; - - - - for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++) - { - if (heavyDebugMsg) cout << "at t="<first << endl; - - int upstem_y1 = -1, upstem_y2=-1, upstem_x=-1, upflag=-1; - int downstem_y1 = -1, downstem_y2=-1, downstem_x=-1, downflag=-1; - - for (set::iterator it=it2->second.begin(); it!=it2->second.end();it++) - { - if (it->type==FloItem::NOTE) - { - if (heavyDebugMsg) - { - cout << "\tNOTE at line"<pos.height<<" with acc.="<pos.vorzeichen<<", len="<len); - for (int i=0;idots;i++) cout << "."; - cout << " , stem="; - if (it->stem==UPWARDS) - cout << "UPWARDS"; - else - cout << "DOWNWARDS"; - - cout << " , shift="<shift<<", ausweich="<ausweich<<", "; - if (!it->tied) cout << "un"; - cout << "tied, is_tie_dest="<is_tie_dest<len!=0) //only for non-whole notes the stems are relevant! - { - if (it->stem==UPWARDS) - { - if (upstem_y1 == -1) - upstem_y1=it->y; - - upstem_y2=it->y; - - - if ((upflag!=-1) && (upflag!=it->len)) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: upflag != this->flag" << endl; - upflag=it->len; - - if ((upstem_x!=-1) && (upstem_x!=it->stem_x )) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: upstem_x != x_result" << endl; - upstem_x=it->stem_x; - } - else - { - if (downstem_y1 == -1) - downstem_y1=it->y; - - downstem_y2=it->y; - - - if ((downflag!=-1) && (downflag!=it->len)) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: downflag != this->flag" << endl; - downflag=it->len; - - if ((downstem_x!=-1) && (downstem_x!=it->stem_x)) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: downstem_x != x_result" << endl; - downstem_x=it->stem_x; //important: before the below calculation! - } - } - - - if (it->pos.height <= 0) //we need auxiliary lines on the bottom? - { - p.setPen(Qt::black); - for (int i=0; i>=it->pos.height; i-=2) - p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2); - } - else if (it->pos.height >= 12) //we need auxiliary lines on the top? - { - p.setPen(Qt::black); - for (int i=12; i<=it->pos.height; i+=2) - p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2); - } - - it->is_active= ( (MusEGlobal::song->cpos() >= it->source_event->tick() + it->source_part->tick()) && - (MusEGlobal::song->cpos() < it->source_event->endTick() + it->source_part->tick()) ); - - - int color_index; - switch (coloring_mode) - { - case COLOR_MODE_BLACK: - color_index=BLACK_PIXMAP; - break; - - case COLOR_MODE_PART: - color_index=it->source_part->colorIndex(); - break; - - case COLOR_MODE_VELO: - color_index=VELO_PIXMAP_BEGIN + it->source_event->velo(); - break; - - default: - cerr << "ERROR: THIS CANNOT HAPPEN: coloring_mode (="<source_event->selected()) - color_index=SELECTED_PIXMAP; - - if (MusEGlobal::audio->isPlaying() && it->is_active) - color_index=HIGHLIGHTED_PIXMAP; - - - draw_pixmap(p,it->x -x_pos+x_left,y_offset + it->y,it->pix[color_index]); - - //draw dots - - int x_dot=DOT_XBEGIN; - int y_dot; - if (modulo(it->pos.height, 2) == 0) //note is on a line? - y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space - else //note is between two lines? - y_dot=YLEN * 0.1; - - if (it->stem==DOWNWARDS) - y_dot=-y_dot; - //else y_dot=y_dot; - - for (int i=0;idots;i++) - { - draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[color_index]); - x_dot+=DOT_XDIST; - } - - - - //draw accidentials - if (it->pos.vorzeichen != curr_accidential[modulo(it->pos.height,7)]) - { - QPixmap* acc_pix; - switch (it->pos.vorzeichen) - { - case NONE: acc_pix=pix_noacc; break; - case SHARP: acc_pix=pix_sharp; break; - case B: acc_pix=pix_b; break; - default: cerr << "ERROR: THIS CANNOT HAPPEN: it->pos.vorzeichen (="<pos.vorzeichen<<") is invalid! defaulting to NONE." << endl; - acc_pix=pix_noacc; break; - } - draw_pixmap(p,it->x-ACCIDENTIAL_DIST -x_pos+x_left,y_offset + it->y, acc_pix[color_index]); - - curr_accidential[modulo(it->pos.height,7)]=it->pos.vorzeichen; - } - - - //if needed, draw tie - if (it->is_tie_dest) - { - if (heavyDebugMsg) cout << "drawing tie" << endl; - draw_tie(p,it->tie_from_x-x_pos+x_left,it->x -x_pos+x_left,y_offset + it->y, (it->len==0) ? true : (it->stem==DOWNWARDS) , mycolors[color_index]); - // in english: "if it's a whole note, tie is upwards (true). if not, tie is upwards if - // stem is downwards and vice versa" - } - } - else if (it->type==FloItem::REST) - { - if (heavyDebugMsg) - { - cout << "\tREST at line"<pos.height<<" with len="<len); - for (int i=0;idots;i++) cout << "."; - cout << " , ausweich="<ausweich<x -x_pos+x_left,y_offset + it->y,*it->pix); - - - //draw dots - - int x_dot=DOT_XBEGIN_REST; - int y_dot; - if (modulo(it->pos.height, 2) == 0) //rest is on a line? - y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space - else //note is between two lines? - y_dot=YLEN * 0.1; - - if (it->len!=0) // all rests except the whole are treated as - y_dot=-y_dot; // if they had a downwards stem - - for (int i=0;idots;i++) - { - draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[BLACK_PIXMAP]); - x_dot+=DOT_XDIST; - } - } - else if (it->type==FloItem::BAR) - { - if (heavyDebugMsg) cout << "\tBAR" << endl; - - p.setPen(Qt::black); - p.drawLine(it->x -x_pos+x_left,y_offset -2*YLEN,it->x -x_pos+x_left,y_offset +2*YLEN); - - for (int i=0;i<7;i++) - curr_accidential[i]=default_accidential[i]; - } - else if (it->type==FloItem::TIME_SIG) - { - if (heavyDebugMsg) cout << "\tTIME SIGNATURE: "<num<<"/"<denom<x - x_pos+x_left, y_offset, it->num, it->denom); - } - else if (it->type==FloItem::KEY_CHANGE) - { - MusECore::key_enum new_key=it->key; - if (heavyDebugMsg) cout << "\tKEY CHANGE: from "< aufloes_list=calc_accidentials(curr_key, staff.clef, new_key); - list new_acc_list=calc_accidentials(new_key, staff.clef); - - // cancel accidentials from curr_key - draw_accidentials(p, it->x + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, aufloes_list, pix_noacc[BLACK_PIXMAP]); - - // draw all accidentials from new_key - QPixmap* pix = is_sharp_key(new_key) ? &pix_sharp[BLACK_PIXMAP] : &pix_b[BLACK_PIXMAP]; - vorzeichen_t new_accidential = is_sharp_key(new_key) ? SHARP : B; - - draw_accidentials(p, it->x + aufloes_list.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, new_acc_list, *pix); - - for (int i=0;i<7;i++) - curr_accidential[i]=default_accidential[i]=NONE; - - for (list::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++) - default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential; - - curr_key=new_key; - } - } - - p.setPen(Qt::black); - //note: y1 is bottom, y2 is top! - if (upstem_x!=-1) - { - upstem_x=upstem_x-pix_quarter[0].width()/2 +pix_quarter[0].width() -1; - p.drawLine(upstem_x -x_pos+x_left, y_offset + upstem_y1, upstem_x -x_pos+x_left, y_offset + upstem_y2-STEM_LEN); - - if (upflag>=3) //if the note needs a flag - p.drawPixmap(upstem_x -x_pos+x_left,y_offset + upstem_y2-STEM_LEN,pix_flag_up[upflag-3]); - } - if (downstem_x!=-1) - { - downstem_x=downstem_x-pix_quarter[0].width()/2; - p.drawLine(downstem_x -x_pos+x_left, y_offset + downstem_y1+STEM_LEN, downstem_x -x_pos+x_left, y_offset + downstem_y2); - - if (downflag>=3) //if the note needs a flag - p.drawPixmap(downstem_x -x_pos+x_left,y_offset + downstem_y1+STEM_LEN-pix_flag_down[downflag-3].height(),pix_flag_down[downflag-3]); - } - } + // init accidentials properly + vorzeichen_t curr_accidential[7]; + vorzeichen_t default_accidential[7]; + MusECore::key_enum curr_key; + + curr_key=key_at_tick(from_it->first); + list new_acc_list=calc_accidentials(curr_key, staff.clef); + vorzeichen_t new_accidential = is_sharp_key(curr_key) ? SHARP : B; + + for (int i=0;i<7;i++) + curr_accidential[i]=default_accidential[i]=NONE; + + for (list::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++) + default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential; + + + + for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++) + { + if (heavyDebugMsg) cout << "at t="<first << endl; + + int upstem_y1 = -1, upstem_y2=-1, upstem_x=-1, upflag=-1; + int downstem_y1 = -1, downstem_y2=-1, downstem_x=-1, downflag=-1; + + for (set::iterator it=it2->second.begin(); it!=it2->second.end();it++) + { + if (it->type==FloItem::NOTE) + { + if (heavyDebugMsg) + { + cout << "\tNOTE at line"<pos.height<<" with acc.="<pos.vorzeichen<<", len="<len); + for (int i=0;idots;i++) cout << "."; + cout << " , stem="; + if (it->stem==UPWARDS) + cout << "UPWARDS"; + else + cout << "DOWNWARDS"; + + cout << " , shift="<shift<<", ausweich="<ausweich<<", "; + if (!it->tied) cout << "un"; + cout << "tied, is_tie_dest="<is_tie_dest<len!=0) //only for non-whole notes the stems are relevant! + { + if (it->stem==UPWARDS) + { + if (upstem_y1 == -1) + upstem_y1=it->y; + + upstem_y2=it->y; + + + if ((upflag!=-1) && (upflag!=it->len)) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: upflag != this->flag" << endl; + upflag=it->len; + + if ((upstem_x!=-1) && (upstem_x!=it->stem_x )) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: upstem_x != x_result" << endl; + upstem_x=it->stem_x; + } + else + { + if (downstem_y1 == -1) + downstem_y1=it->y; + + downstem_y2=it->y; + + + if ((downflag!=-1) && (downflag!=it->len)) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: downflag != this->flag" << endl; + downflag=it->len; + + if ((downstem_x!=-1) && (downstem_x!=it->stem_x)) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: downstem_x != x_result" << endl; + downstem_x=it->stem_x; //important: before the below calculation! + } + } + + + if (it->pos.height <= 0) //we need auxiliary lines on the bottom? + { + p.setPen(Qt::black); + for (int i=0; i>=it->pos.height; i-=2) + p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2); + } + else if (it->pos.height >= 12) //we need auxiliary lines on the top? + { + p.setPen(Qt::black); + for (int i=12; i<=it->pos.height; i+=2) + p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2); + } + + it->is_active= ( (MusEGlobal::song->cpos() >= it->source_event->tick() + it->source_part->tick()) && + (MusEGlobal::song->cpos() < it->source_event->endTick() + it->source_part->tick()) ); + + + int color_index; + switch (coloring_mode) + { + case COLOR_MODE_BLACK: + color_index=BLACK_PIXMAP; + break; + + case COLOR_MODE_PART: + color_index=it->source_part->colorIndex(); + break; + + case COLOR_MODE_VELO: + color_index=VELO_PIXMAP_BEGIN + it->source_event->velo(); + break; + + default: + cerr << "ERROR: THIS CANNOT HAPPEN: coloring_mode (="<source_event->selected()) + color_index=SELECTED_PIXMAP; + + if (MusEGlobal::audio->isPlaying() && it->is_active) + color_index=HIGHLIGHTED_PIXMAP; + + + draw_pixmap(p,it->x -x_pos+x_left,y_offset + it->y,it->pix[color_index]); + + //draw dots + + int x_dot=DOT_XBEGIN; + int y_dot; + if (modulo(it->pos.height, 2) == 0) //note is on a line? + y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space + else //note is between two lines? + y_dot=YLEN * 0.1; + + if (it->stem==DOWNWARDS) + y_dot=-y_dot; + //else y_dot=y_dot; + + for (int i=0;idots;i++) + { + draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[color_index]); + x_dot+=DOT_XDIST; + } + + + + //draw accidentials + if (it->pos.vorzeichen != curr_accidential[modulo(it->pos.height,7)]) + { + QPixmap* acc_pix; + switch (it->pos.vorzeichen) + { + case NONE: acc_pix=pix_noacc; break; + case SHARP: acc_pix=pix_sharp; break; + case B: acc_pix=pix_b; break; + default: cerr << "ERROR: THIS CANNOT HAPPEN: it->pos.vorzeichen (="<pos.vorzeichen<<") is invalid! defaulting to NONE." << endl; + acc_pix=pix_noacc; break; + } + draw_pixmap(p,it->x-ACCIDENTIAL_DIST -x_pos+x_left,y_offset + it->y, acc_pix[color_index]); + + curr_accidential[modulo(it->pos.height,7)]=it->pos.vorzeichen; + } + + + //if needed, draw tie + if (it->is_tie_dest) + { + if (heavyDebugMsg) cout << "drawing tie" << endl; + draw_tie(p,it->tie_from_x-x_pos+x_left,it->x -x_pos+x_left,y_offset + it->y, (it->len==0) ? true : (it->stem==DOWNWARDS) , mycolors[color_index]); + // in english: "if it's a whole note, tie is upwards (true). if not, tie is upwards if + // stem is downwards and vice versa" + } + } + else if (it->type==FloItem::REST) + { + if (heavyDebugMsg) + { + cout << "\tREST at line"<pos.height<<" with len="<len); + for (int i=0;idots;i++) cout << "."; + cout << " , ausweich="<ausweich<x -x_pos+x_left,y_offset + it->y,*it->pix); + + + //draw dots + + int x_dot=DOT_XBEGIN_REST; + int y_dot; + if (modulo(it->pos.height, 2) == 0) //rest is on a line? + y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space + else //note is between two lines? + y_dot=YLEN * 0.1; + + if (it->len!=0) // all rests except the whole are treated as + y_dot=-y_dot; // if they had a downwards stem + + for (int i=0;idots;i++) + { + draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[BLACK_PIXMAP]); + x_dot+=DOT_XDIST; + } + } + else if (it->type==FloItem::BAR) + { + if (heavyDebugMsg) cout << "\tBAR" << endl; + + p.setPen(Qt::black); + p.drawLine(it->x -x_pos+x_left,y_offset -2*YLEN,it->x -x_pos+x_left,y_offset +2*YLEN); + + for (int i=0;i<7;i++) + curr_accidential[i]=default_accidential[i]; + } + else if (it->type==FloItem::TIME_SIG) + { + if (heavyDebugMsg) cout << "\tTIME SIGNATURE: "<num<<"/"<denom<x - x_pos+x_left, y_offset, it->num, it->denom); + } + else if (it->type==FloItem::KEY_CHANGE) + { + MusECore::key_enum new_key=it->key; + if (heavyDebugMsg) cout << "\tKEY CHANGE: from "< aufloes_list=calc_accidentials(curr_key, staff.clef, new_key); + list new_acc_list=calc_accidentials(new_key, staff.clef); + + // cancel accidentials from curr_key + draw_accidentials(p, it->x + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, aufloes_list, pix_noacc[BLACK_PIXMAP]); + + // draw all accidentials from new_key + QPixmap* pix = is_sharp_key(new_key) ? &pix_sharp[BLACK_PIXMAP] : &pix_b[BLACK_PIXMAP]; + vorzeichen_t new_accidential = is_sharp_key(new_key) ? SHARP : B; + + draw_accidentials(p, it->x + aufloes_list.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, new_acc_list, *pix); + + for (int i=0;i<7;i++) + curr_accidential[i]=default_accidential[i]=NONE; + + for (list::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++) + default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential; + + curr_key=new_key; + } + } + + p.setPen(Qt::black); + //note: y1 is bottom, y2 is top! + if (upstem_x!=-1) + { + upstem_x=upstem_x-pix_quarter[0].width()/2 +pix_quarter[0].width() -1; + p.drawLine(upstem_x -x_pos+x_left, y_offset + upstem_y1, upstem_x -x_pos+x_left, y_offset + upstem_y2-STEM_LEN); + + if (upflag>=3) //if the note needs a flag + p.drawPixmap(upstem_x -x_pos+x_left,y_offset + upstem_y2-STEM_LEN,pix_flag_up[upflag-3]); + } + if (downstem_x!=-1) + { + downstem_x=downstem_x-pix_quarter[0].width()/2; + p.drawLine(downstem_x -x_pos+x_left, y_offset + downstem_y1+STEM_LEN, downstem_x -x_pos+x_left, y_offset + downstem_y2); + + if (downflag>=3) //if the note needs a flag + p.drawPixmap(downstem_x -x_pos+x_left,y_offset + downstem_y1+STEM_LEN-pix_flag_down[downflag-3].height(),pix_flag_down[downflag-3]); + } + } } bool ScoreCanvas::need_redraw_for_hilighting() { - for (list::iterator it=staves.begin(); it!=staves.end(); it++) - if (need_redraw_for_hilighting(it->itemlist)) return true; - - return false; + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + if (need_redraw_for_hilighting(it->itemlist)) return true; + + return false; } bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist) { - return need_redraw_for_hilighting(itemlist, x_pos,x_pos+width()-x_left); + return need_redraw_for_hilighting(itemlist, x_pos,x_pos+width()-x_left); } bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist, int x1, int x2) { - int from_tick, to_tick; - ScoreItemList::iterator from_it, to_it; + int from_tick, to_tick; + ScoreItemList::iterator from_it, to_it; - from_tick=x_to_tick(x1); - from_it=itemlist.lower_bound(from_tick); - //from_it now contains the first time which is fully drawn - //however, the previous beat could still be relevant, when it's - //partly drawn. so we decrement from_it - if (from_it!=itemlist.begin()) from_it--; - - to_tick=x_to_tick(x2); - to_it=itemlist.upper_bound(to_tick); - //to_it now contains the first time which is not drawn at all any more + from_tick=x_to_tick(x1); + from_it=itemlist.lower_bound(from_tick); + //from_it now contains the first time which is fully drawn + //however, the previous beat could still be relevant, when it's + //partly drawn. so we decrement from_it + if (from_it!=itemlist.begin()) from_it--; + + to_tick=x_to_tick(x2); + to_it=itemlist.upper_bound(to_tick); + //to_it now contains the first time which is not drawn at all any more - return need_redraw_for_hilighting(from_it, to_it); + return need_redraw_for_hilighting(from_it, to_it); } bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList::iterator from_it, ScoreItemList::iterator to_it) { - //if we aren't playing, there will never be a need for redrawing due to highlighting things - if (MusEGlobal::audio->isPlaying()==false) - return false; - - for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++) - for (set::iterator it=it2->second.begin(); it!=it2->second.end();it++) - if (it->type==FloItem::NOTE) - { - bool is_active= ( (MusEGlobal::song->cpos() >= it->source_event->tick() + it->source_part->tick()) && - (MusEGlobal::song->cpos() < it->source_event->endTick() + it->source_part->tick()) ); - if (it->is_active != is_active) - return true; - } - - return false; + //if we aren't playing, there will never be a need for redrawing due to highlighting things + if (MusEGlobal::audio->isPlaying()==false) + return false; + + for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++) + for (set::iterator it=it2->second.begin(); it!=it2->second.end();it++) + if (it->type==FloItem::NOTE) + { + bool is_active= ( (MusEGlobal::song->cpos() >= it->source_event->tick() + it->source_part->tick()) && + (MusEGlobal::song->cpos() < it->source_event->endTick() + it->source_part->tick()) ); + if (it->is_active != is_active) + return true; + } + + return false; } int clef_height(clef_t clef) { - switch (clef) //CLEF_MARKER - { - case VIOLIN: return 4; - case BASS: return 8; - default: - cerr << "ERROR: ILLEGAL FUNCTION CALL in clef_height()" << endl; - return 6; - } + switch (clef) //CLEF_MARKER + { + case VIOLIN: return 4; + case BASS: return 8; + default: + cerr << "ERROR: ILLEGAL FUNCTION CALL in clef_height()" << endl; + return 6; + } } #define TIMESIG_LEFTMARGIN 5 @@ -3413,177 +3425,177 @@ void ScoreCanvas::draw_preamble(QPainter& p, int y_offset, clef_t clef, bool reserve_akkolade_space, bool with_akkolade) { - int x_left_old=x_left; - int tick=x_to_tick(x_pos); - - // maybe draw akkolade ---------------------------------------------- - if (reserve_akkolade_space) - { - if (with_akkolade) - draw_akkolade(p, AKKOLADE_LEFTMARGIN, y_offset+GRANDSTAFF_DISTANCE/2); - - x_left= AKKOLADE_LEFTMARGIN + AKKOLADE_WIDTH + AKKOLADE_RIGHTMARGIN; - } - else - x_left=0; - - - // draw clef -------------------------------------------------------- - QPixmap* pix_clef= (clef==BASS) ? pix_clef_bass : pix_clef_violin; - int y_coord=2*YLEN - ( clef_height(clef) -2)*YLEN/2; - - draw_pixmap(p,x_left + CLEF_LEFTMARGIN + pix_clef->width()/2,y_offset + y_coord,*pix_clef); - - x_left+= CLEF_LEFTMARGIN + pix_clef->width() + CLEF_RIGHTMARGIN; - - - // draw accidentials ------------------------------------------------ - if (preamble_contains_keysig) - { - x_left+=KEYCHANGE_ACC_LEFTDIST; - - MusECore::key_enum key=key_at_tick(tick); - QPixmap* pix_acc=is_sharp_key(key) ? &pix_sharp[BLACK_PIXMAP] : &pix_b[BLACK_PIXMAP]; - list acclist=calc_accidentials(key,clef); - - draw_accidentials(p,x_left, y_offset, acclist ,*pix_acc); - - x_left+=acclist.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_RIGHTDIST; - } - - - // draw time signature ---------------------------------------------- - if (preamble_contains_timesig) - { - x_left+=TIMESIG_LEFTMARGIN; - - timesig_t timesig=timesig_at_tick(tick); - - draw_timesig(p, x_left, y_offset, timesig.num, timesig.denom); - - x_left+=calc_timesig_width(timesig.num, timesig.denom)+TIMESIG_RIGHTMARGIN; - } - - // draw bar --------------------------------------------------------- - p.setPen(Qt::black); - p.drawLine(x_left,y_offset -2*YLEN,x_left,y_offset +2*YLEN); - - - if (x_left_old!=x_left) - { - emit viewport_width_changed(viewport_width()); - emit preamble_width_changed(x_left); - } + int x_left_old=x_left; + int tick=x_to_tick(x_pos); + + // maybe draw akkolade ---------------------------------------------- + if (reserve_akkolade_space) + { + if (with_akkolade) + draw_akkolade(p, AKKOLADE_LEFTMARGIN, y_offset+GRANDSTAFF_DISTANCE/2); + + x_left= AKKOLADE_LEFTMARGIN + AKKOLADE_WIDTH + AKKOLADE_RIGHTMARGIN; + } + else + x_left=0; + + + // draw clef -------------------------------------------------------- + QPixmap* pix_clef= (clef==BASS) ? pix_clef_bass : pix_clef_violin; + int y_coord=2*YLEN - ( clef_height(clef) -2)*YLEN/2; + + draw_pixmap(p,x_left + CLEF_LEFTMARGIN + pix_clef->width()/2,y_offset + y_coord,*pix_clef); + + x_left+= CLEF_LEFTMARGIN + pix_clef->width() + CLEF_RIGHTMARGIN; + + + // draw accidentials ------------------------------------------------ + if (preamble_contains_keysig) + { + x_left+=KEYCHANGE_ACC_LEFTDIST; + + MusECore::key_enum key=key_at_tick(tick); + QPixmap* pix_acc=is_sharp_key(key) ? &pix_sharp[BLACK_PIXMAP] : &pix_b[BLACK_PIXMAP]; + list acclist=calc_accidentials(key,clef); + + draw_accidentials(p,x_left, y_offset, acclist ,*pix_acc); + + x_left+=acclist.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_RIGHTDIST; + } + + + // draw time signature ---------------------------------------------- + if (preamble_contains_timesig) + { + x_left+=TIMESIG_LEFTMARGIN; + + timesig_t timesig=timesig_at_tick(tick); + + draw_timesig(p, x_left, y_offset, timesig.num, timesig.denom); + + x_left+=calc_timesig_width(timesig.num, timesig.denom)+TIMESIG_RIGHTMARGIN; + } + + // draw bar --------------------------------------------------------- + p.setPen(Qt::black); + p.drawLine(x_left,y_offset -2*YLEN,x_left,y_offset +2*YLEN); + + + if (x_left_old!=x_left) + { + emit viewport_width_changed(viewport_width()); + emit preamble_width_changed(x_left); + } } void ScoreCanvas::draw_timesig(QPainter& p, int x, int y_offset, int num, int denom) { - int num_width=calc_number_width(num); - int denom_width=calc_number_width(denom); - int width=((num_width > denom_width) ? num_width : denom_width); - int num_indent=(width-num_width)/2 + TIMESIG_LEFTMARGIN; - int denom_indent=(width-denom_width)/2 + TIMESIG_LEFTMARGIN; - - draw_number(p, x+num_indent, y_offset -DIGIT_YDIST, num); - draw_number(p, x+denom_indent, y_offset +DIGIT_YDIST, denom); + int num_width=calc_number_width(num); + int denom_width=calc_number_width(denom); + int width=((num_width > denom_width) ? num_width : denom_width); + int num_indent=(width-num_width)/2 + TIMESIG_LEFTMARGIN; + int denom_indent=(width-denom_width)/2 + TIMESIG_LEFTMARGIN; + + draw_number(p, x+num_indent, y_offset -DIGIT_YDIST, num); + draw_number(p, x+denom_indent, y_offset +DIGIT_YDIST, denom); } int calc_timesig_width(int num, int denom) { - int num_width=calc_number_width(num); - int denom_width=calc_number_width(denom); - int width=((num_width > denom_width) ? num_width : denom_width); - return width+TIMESIG_LEFTMARGIN+TIMESIG_RIGHTMARGIN; + int num_width=calc_number_width(num); + int denom_width=calc_number_width(denom); + int width=((num_width > denom_width) ? num_width : denom_width); + return width+TIMESIG_LEFTMARGIN+TIMESIG_RIGHTMARGIN; } int calc_number_width(int n) { - string str=IntToStr(n); - return (str.length()*DIGIT_WIDTH); + string str=IntToStr(n); + return (str.length()*DIGIT_WIDTH); } void ScoreCanvas::draw_number(QPainter& p, int x, int y, int n) { - string str=IntToStr(n); - int curr_x=x+DIGIT_WIDTH/2; - - for (size_t i=0;i::iterator it=staves.begin(); it!=staves.end(); it++) - if (it->type==GRAND_TOP) - { - reserve_akkolade_space=true; - break; - } - - for (list::iterator it=staves.begin(); it!=staves.end(); it++) - { - //TODO: maybe only draw visible staves? - draw_note_lines(p,it->y_draw - y_pos, reserve_akkolade_space); - draw_preamble(p,it->y_draw - y_pos, it->clef, reserve_akkolade_space, (it->type==GRAND_TOP)); - p.setClipRect(x_left+1,0,p.device()->width(),p.device()->height()); - draw_items(p,it->y_draw - y_pos, *it); - p.setClipping(false); - } - - if (have_lasso) - { - p.setPen(Qt::blue); - p.setBrush(Qt::NoBrush); - p.drawRect(lasso); - } - - if (debugMsg) cout << "drawing done." << endl; + bool reserve_akkolade_space=false; + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + if (it->type==GRAND_TOP) + { + reserve_akkolade_space=true; + break; + } + + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + { + //TODO: maybe only draw visible staves? + draw_note_lines(p,it->y_draw - y_pos, reserve_akkolade_space); + draw_preamble(p,it->y_draw - y_pos, it->clef, reserve_akkolade_space, (it->type==GRAND_TOP)); + p.setClipRect(x_left+1,0,p.device()->width(),p.device()->height()); + draw_items(p,it->y_draw - y_pos, *it); + p.setClipping(false); + } + + if (have_lasso) + { + p.setPen(Qt::blue); + p.setBrush(Qt::NoBrush); + p.drawRect(lasso); + } + + if (debugMsg) cout << "drawing done." << endl; } list calc_accidentials(MusECore::key_enum key, clef_t clef, MusECore::key_enum next_key) { - list result; - - int violin_sharp_pos[]={10,7,11,8,5,9,6}; //CLEF_MARKER - int violin_b_pos[]={6,9,5,8,4,7,3}; - int bass_sharp_pos[]={8,5,9,6,3,7,4}; - int bass_b_pos[]={4,7,3,6,2,5,1}; - - int* accidential_pos; - - switch (clef) - { - case VIOLIN: accidential_pos = is_sharp_key(key) ? violin_sharp_pos : violin_b_pos; break; - case BASS: accidential_pos = is_sharp_key(key) ? bass_sharp_pos : bass_b_pos; break; - } - - int begin=0; - - if (is_sharp_key(key)==is_sharp_key(next_key)) //same kind of key (both b or both #)? - begin=n_accidentials(next_key); - else - begin=0; - - - int end=n_accidentials(key); - - for (int i=begin; i result; + + int violin_sharp_pos[]={10,7,11,8,5,9,6}; //CLEF_MARKER + int violin_b_pos[]={6,9,5,8,4,7,3}; + int bass_sharp_pos[]={8,5,9,6,3,7,4}; + int bass_b_pos[]={4,7,3,6,2,5,1}; + + int* accidential_pos = NULL; + + switch (clef) + { + case VIOLIN: accidential_pos = is_sharp_key(key) ? violin_sharp_pos : violin_b_pos; break; + case BASS: accidential_pos = is_sharp_key(key) ? bass_sharp_pos : bass_b_pos; break; + } + + int begin=0; + + if (is_sharp_key(key)==is_sharp_key(next_key)) //same kind of key (both b or both #)? + begin=n_accidentials(next_key); + else + begin=0; + + + int end=n_accidentials(key); + + for (int i=begin; i::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<=t; it++) - x+=it->second; - - return x; + int x=t*pixels_per_whole()/TICKS_PER_WHOLE; + + for (std::map::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<=t; it++) + x+=it->second; + + return x; } int ScoreCanvas::delta_tick_to_delta_x(int t) { - return t*pixels_per_whole()/TICKS_PER_WHOLE; + return t*pixels_per_whole()/TICKS_PER_WHOLE; } int ScoreCanvas::calc_posadd(int t) { - int result=0; + int result=0; - for (std::map::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->firstsecond; + for (std::map::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->firstsecond; - return result; + return result; } //doesn't round mathematically correct, but i don't think this //will be a problem, because a tick is pretty small int ScoreCanvas::x_to_tick(int x) { - int t=TICKS_PER_WHOLE * x/pixels_per_whole(); - int min_t=0; - - for (std::map::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->firstfirst; - x-=it->second; - t=TICKS_PER_WHOLE * x/pixels_per_whole(); - } - - return t > min_t ? t : min_t; + int t=TICKS_PER_WHOLE * x/pixels_per_whole(); + int min_t=0; + + for (std::map::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->firstfirst; + x-=it->second; + t=TICKS_PER_WHOLE * x/pixels_per_whole(); + } + + return t > min_t ? t : min_t; } MusECore::key_enum ScoreCanvas::key_at_tick(int t_) { - unsigned int t= (t_>=0) ? t_ : 0; - - return MusEGlobal::keymap.keyAtTick(t); + unsigned int t= (t_>=0) ? t_ : 0; + + return MusEGlobal::keymap.keyAtTick(t); } timesig_t ScoreCanvas::timesig_at_tick(int t_) { - timesig_t tmp; - unsigned int t= (t_>=0) ? t_ : 0; + timesig_t tmp; + unsigned int t= (t_>=0) ? t_ : 0; - AL::sigmap.timesig(t, tmp.num, tmp.denom); + AL::sigmap.timesig(t, tmp.num, tmp.denom); - return tmp; + return tmp; } int ScoreCanvas::height_to_pitch(int h, clef_t clef) { - int foo[]={0,2,4,5,7,9,11}; - - switch(clef) //CLEF_MARKER - { - case VIOLIN: return foo[modulo(h,7)] + ( divide_floor(h,7)*12 ) + 60; - case BASS: return foo[modulo((h-5),7)] + ( divide_floor(h-5,7)*12 ) + 48; - default: - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: unknown clef in height_to_pitch" << endl; - return 60; - } + int foo[]={0,2,4,5,7,9,11}; + + switch(clef) //CLEF_MARKER + { + case VIOLIN: return foo[modulo(h,7)] + ( divide_floor(h,7)*12 ) + 60; + case BASS: return foo[modulo((h-5),7)] + ( divide_floor(h-5,7)*12 ) + 48; + default: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: unknown clef in height_to_pitch" << endl; + return 60; + } } int ScoreCanvas::height_to_pitch(int h, clef_t clef, MusECore::key_enum key) { - int add=0; - - list accs=calc_accidentials(key,clef); - - for (list::iterator it=accs.begin(); it!=accs.end(); it++) - { - if (modulo(*it,7) == modulo(h,7)) - { - add=is_sharp_key(key) ? 1 : -1; - break; - } - } - - return height_to_pitch(h,clef)+add; + int add=0; + + list accs=calc_accidentials(key,clef); + + for (list::iterator it=accs.begin(); it!=accs.end(); it++) + { + if (modulo(*it,7) == modulo(h,7)) + { + add=is_sharp_key(key) ? 1 : -1; + break; + } + } + + return height_to_pitch(h,clef)+add; } int ScoreCanvas::y_to_height(int y) { - return int(nearbyint(float(2*YLEN - y)*2.0/YLEN))+2 ; + return int(nearbyint(float(2*YLEN - y)*2.0/YLEN))+2 ; } int ScoreCanvas::y_to_pitch(int y, int t, clef_t clef) { - return height_to_pitch(y_to_height(y), clef, key_at_tick(t)); + return height_to_pitch(y_to_height(y), clef, key_at_tick(t)); } @@ -3695,292 +3707,290 @@ void ScoreCanvas::mousePressEvent (QMouseEvent* event) { - keystate=event->modifiers(); - bool ctrl=keystate & Qt::ControlModifier; + keystate=event->modifiers(); + bool ctrl=keystate & Qt::ControlModifier; - // always round DOWN. - // because the "area" of a beat goes from "beat_begin" to "nextbeat_begin-1", - // but notes are drawn in the middle of that area! - - list::iterator staff_it=staff_at_y(event->y() + y_pos); - - int y=event->y() + y_pos - staff_it->y_draw; - int x=event->x()+x_pos-x_left; - int tick=flo_quantize_floor(x_to_tick(x), quant_ticks()); - - if (staff_it!=staves.end()) - { - if (event->x() <= x_left) //clicked in the preamble? - { - if (event->button() == Qt::RightButton) //right-click? - { - current_staff=staff_it; - staff_menu->popup(event->globalPos()); - } - else if (event->button() == Qt::MidButton) //middle click? - { - remove_staff(staff_it); - } - else if (event->button() == Qt::LeftButton) //left click? - { - current_staff=staff_it; - setCursor(Qt::SizeAllCursor); - dragging_staff=true; - } - } - else - { - ScoreItemList& itemlist=staff_it->itemlist; - - if (debugMsg) cout << "mousePressEvent at "<len, found->dots); - } - } while (found->tied); - - int total_begin=set_it->begin_tick; - int total_end=t; - - int this_begin=tick; - int this_end=this_begin+calc_len(set_it->len, set_it->dots); - - set_selected_part(set_it->source_part); - - //that's the only note corresponding to the event? - if (this_begin==total_begin && this_end==total_end) - { - if (x < set_it->x) - mouse_x_drag_operation=BEGIN; - else - mouse_x_drag_operation=LENGTH; - } - //that's NOT the only note? - else - { - if (this_begin==total_begin) - mouse_x_drag_operation=BEGIN; - else if (this_end==total_end) - mouse_x_drag_operation=LENGTH; - else - mouse_x_drag_operation=NO_OP; - } - - if (debugMsg) - cout << "you clicked at a note with begin at "<begin_tick<<" and end at "<source_part << endl; - - if (set_it->source_part == NULL) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: set_it->source_part is NULL!" << endl; - - - - clicked_event_ptr=set_it->source_event; - dragged_event=*set_it->source_event; - original_dragged_event=dragged_event.clone(); - set_dragged_event_part(set_it->source_part); - - if ((mouse_erases_notes) || (event->button()==Qt::MidButton)) //erase? - { - MusEGlobal::audio->msgDeleteEvent(dragged_event, dragged_event_part, true, false, false); - } - else if (event->button()==Qt::LeftButton) //edit? - { - setMouseTracking(true); - dragging=true; - drag_cursor_changed=false; - } - } - else //we found nothing? - if (event->button()==Qt::LeftButton) - { - if (mouse_inserts_notes) - { - MusECore::Part* curr_part = NULL; - set possible_dests=staff_it->parts_at_tick(tick); - - if (!possible_dests.empty()) - { - if (possible_dests.size()==1) - curr_part=*possible_dests.begin(); - else - { - if (possible_dests.find(selected_part)!=possible_dests.end()) - curr_part=selected_part; - else - QMessageBox::information(this, tr("Ambiguous part"), tr("There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part.")); - } - } - else - QMessageBox::information(this, tr("No part"), tr("There are no parts you could add the note to.")); - - if (curr_part!=NULL) - { - signed int relative_tick=(signed) tick - curr_part->tick(); - if (relative_tick<0) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: relative_tick is negative!" << endl; - - if (!ctrl) - deselect_all(); - - MusECore::Event newevent(MusECore::Note); - newevent.setPitch(y_to_pitch(y,tick, staff_it->clef)); - newevent.setVelo(note_velo); - newevent.setVeloOff(note_velo_off); - newevent.setTick(relative_tick); - newevent.setLenTick((new_len>0)?new_len:last_len); - newevent.setSelected(true); - - if (flo_quantize(newevent.lenTick(), quant_ticks()) <= 0) - { - newevent.setLenTick(quant_ticks()); - if (debugMsg) cout << "inserted note's length would be invisible after quantisation (too short)." << endl << - " setting it to " << newevent.lenTick() << endl; - } - - if (newevent.endTick() > curr_part->lenTick()) - { - if (debugMsg) cout << "clipping inserted note from len="<::iterator staff_it=staff_at_y(event->y() + y_pos); + + int y=event->y() + y_pos - staff_it->y_draw; + int x=event->x()+x_pos-x_left; + int tick=flo_quantize_floor(x_to_tick(x), quant_ticks()); + + if (staff_it!=staves.end()) + { + if (event->x() <= x_left) //clicked in the preamble? + { + if (event->button() == Qt::RightButton) //right-click? + { + current_staff=staff_it; + staff_menu->popup(event->globalPos()); + } + else if (event->button() == Qt::MidButton) //middle click? + { + remove_staff(staff_it); + } + else if (event->button() == Qt::LeftButton) //left click? + { + current_staff=staff_it; + setCursor(Qt::SizeAllCursor); + dragging_staff=true; + } + } + else + { + ScoreItemList& itemlist=staff_it->itemlist; + + if (debugMsg) cout << "mousePressEvent at "<len, found->dots); + } + } while (found->tied); + + int total_begin=set_it->begin_tick; + int total_end=t; + + int this_begin=tick; + int this_end=this_begin+calc_len(set_it->len, set_it->dots); + + set_selected_part(set_it->source_part); + + //that's the only note corresponding to the event? + if (this_begin==total_begin && this_end==total_end) + { + if (x < set_it->x) + mouse_x_drag_operation=BEGIN; + else + mouse_x_drag_operation=LENGTH; + } + //that's NOT the only note? + else + { + if (this_begin==total_begin) + mouse_x_drag_operation=BEGIN; + else if (this_end==total_end) + mouse_x_drag_operation=LENGTH; + else + mouse_x_drag_operation=NO_OP; + } + + if (debugMsg) + cout << "you clicked at a note with begin at "<begin_tick<<" and end at "<source_part << endl; + + if (set_it->source_part == NULL) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: set_it->source_part is NULL!" << endl; + + + + clicked_event_ptr=set_it->source_event; + dragged_event=*set_it->source_event; + original_dragged_event=dragged_event.clone(); + set_dragged_event_part(set_it->source_part); + + if ((mouse_erases_notes) || (event->button()==Qt::MidButton)) //erase? + { + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent,dragged_event, dragged_event_part, false, false)); + } + else if (event->button()==Qt::LeftButton) //edit? + { + setMouseTracking(true); + dragging=true; + drag_cursor_changed=false; + } + } + else //we found nothing? + if (event->button()==Qt::LeftButton) + { + if (mouse_inserts_notes) + { + const MusECore::Part* curr_part = NULL; + set possible_dests=staff_it->parts_at_tick(tick); + + if (!possible_dests.empty()) + { + if (possible_dests.size()==1) + curr_part=*possible_dests.begin(); + else + { + if (possible_dests.find(selected_part)!=possible_dests.end()) + curr_part=selected_part; + else + QMessageBox::information(this, tr("Ambiguous part"), tr("There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part.")); + } + } + else + QMessageBox::information(this, tr("No part"), tr("There are no parts you could add the note to.")); + + if (curr_part!=NULL) + { + signed int relative_tick=(signed) tick - curr_part->tick(); + if (relative_tick<0) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: relative_tick is negative!" << endl; + + if (!ctrl) + deselect_all(); + + MusECore::Event newevent(MusECore::Note); + newevent.setPitch(y_to_pitch(y,tick, staff_it->clef)); + newevent.setVelo(note_velo); + newevent.setVeloOff(note_velo_off); + newevent.setTick(relative_tick); + newevent.setLenTick((new_len>0)?new_len:last_len); + newevent.setSelected(true); // No need to select clones, AddEvent operation below will take care of that. + + if (flo_quantize(newevent.lenTick(), quant_ticks()) <= 0) + { + newevent.setLenTick(quant_ticks()); + if (debugMsg) cout << "inserted note's length would be invisible after quantisation (too short)." << endl << + " setting it to " << newevent.lenTick() << endl; + } + + if (newevent.endTick() > curr_part->lenTick()) + { + if (debugMsg) cout << "clipping inserted note from len="<undo(); - MusEGlobal::audio->msgDeleteEvent(dragged_event, dragged_event_part, true, false, false); - } - else - { - last_len=flo_quantize(dragged_event.lenTick(), quant_ticks()); - } - } - - - if (mouse_operation==NO_OP && !inserting) - { - if (event->button()==Qt::LeftButton) - if (!ctrl) - deselect_all(); - - clicked_event_ptr->setSelected(!clicked_event_ptr->selected()); - - MusEGlobal::song->update(SC_SELECTION); - } - - setMouseTracking(false); - unsetCursor(); - inserting=false; - dragging=false; - drag_cursor_changed=false; - undo_started=false; - - x_scroll_speed=0; x_scroll_pos=0; - } - - if (dragging_staff && event->button()==Qt::LeftButton) - { - int y=event->y()+y_pos; - list::iterator mouse_staff=staff_at_y(y); - - if (mouse_staff!=staves.end()) - { - if ( ((mouse_staff->type==NORMAL) && (y >= mouse_staff->y_draw-2*YLEN) && (y <= mouse_staff->y_draw+2*YLEN)) || - ((mouse_staff->type==GRAND_TOP) && (y >= mouse_staff->y_draw-2*YLEN)) || - ((mouse_staff->type==GRAND_BOTTOM) && (y <= mouse_staff->y_draw+2*YLEN)) ) - merge_staves(mouse_staff, current_staff); - else if (y >= mouse_staff->y_draw+2*YLEN) //will never happen when mouse_staff->type==GRAND_TOP - move_staff_below(mouse_staff, current_staff); - else if (y <= mouse_staff->y_draw-2*YLEN) //will never happen when mouse_staff->type==GRAND_BOTTOM - move_staff_above(mouse_staff, current_staff); - } - - dragging_staff=false; - unsetCursor(); - - y_scroll_speed=0; y_scroll_pos=0; - } - - if (have_lasso && event->button()==Qt::LeftButton) - { - if (!ctrl) - deselect_all(); - - set already_processed; - - for (list::iterator it=staves.begin(); it!=staves.end(); it++) - it->apply_lasso(lasso.translated(x_pos-x_left, y_pos - it->y_draw), already_processed); - - MusEGlobal::song->update(SC_SELECTION); - - have_lasso=false; - redraw(); - } + keystate=event->modifiers(); + bool ctrl=keystate & Qt::ControlModifier; + + if (dragging && event->button()==Qt::LeftButton) + { + if (mouse_operation==LENGTH) + { + if (flo_quantize(dragged_event.lenTick(), quant_ticks()) <= 0) + { + if (debugMsg) cout << "new length <= 0, erasing item" << endl; + if (undo_started) MusEGlobal::song->undo(); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent,dragged_event, dragged_event_part, false, false)); + } + else + { + last_len=flo_quantize(dragged_event.lenTick(), quant_ticks()); + } + } + + + if (mouse_operation==NO_OP && !inserting) + { + if (event->button()==Qt::LeftButton) + if (!ctrl) + deselect_all(); + + MusEGlobal::song->applyOperation(UndoOp(UndoOp::SelectEvent, *clicked_event_ptr, selected_part, !clicked_event_ptr->selected(), clicked_event_ptr->selected())); + } + + setMouseTracking(false); + unsetCursor(); + inserting=false; + dragging=false; + drag_cursor_changed=false; + undo_started=false; + + x_scroll_speed=0; x_scroll_pos=0; + } + + if (dragging_staff && event->button()==Qt::LeftButton) + { + int y=event->y()+y_pos; + list::iterator mouse_staff=staff_at_y(y); + + if (mouse_staff!=staves.end()) + { + if ( ((mouse_staff->type==NORMAL) && (y >= mouse_staff->y_draw-2*YLEN) && (y <= mouse_staff->y_draw+2*YLEN)) || + ((mouse_staff->type==GRAND_TOP) && (y >= mouse_staff->y_draw-2*YLEN)) || + ((mouse_staff->type==GRAND_BOTTOM) && (y <= mouse_staff->y_draw+2*YLEN)) ) + merge_staves(mouse_staff, current_staff); + else if (y >= mouse_staff->y_draw+2*YLEN) //will never happen when mouse_staff->type==GRAND_TOP + move_staff_below(mouse_staff, current_staff); + else if (y <= mouse_staff->y_draw-2*YLEN) //will never happen when mouse_staff->type==GRAND_BOTTOM + move_staff_above(mouse_staff, current_staff); + } + + dragging_staff=false; + unsetCursor(); + + y_scroll_speed=0; y_scroll_pos=0; + } + + if (have_lasso && event->button()==Qt::LeftButton) + { + if (!ctrl) + deselect_all(); + + set already_processed; + + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + it->apply_lasso(lasso.translated(x_pos-x_left, y_pos - it->y_draw), already_processed); + + MusEGlobal::song->update(SC_SELECTION); + + have_lasso=false; + redraw(); + } } #define PITCH_DELTA 5 @@ -3988,249 +3998,247 @@ void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) { - keystate=event->modifiers(); - bool ctrl=keystate & Qt::ControlModifier; + keystate=event->modifiers(); + bool ctrl=keystate & Qt::ControlModifier; - if (dragging) - { - int dx=event->x()-mouse_down_pos.x(); - int dy=event->y()-mouse_down_pos.y(); - - int x=event->x()+x_pos-x_left; - - int tick=flo_quantize_floor(x_to_tick(x), quant_ticks()); - - - if ((drag_cursor_changed==false) && ((dx!=0) || (dy!=0))) - { - setCursor(Qt::SizeAllCursor); - drag_cursor_changed=true; - } - - if (mouse_operation==NO_OP) - { - if ((abs(dx)>DRAG_INIT_DISTANCE) && (mouse_x_drag_operation!=NO_OP)) - { - if (debugMsg) cout << "mouse-operation is now "<DRAG_INIT_DISTANCE) - { - if (debugMsg) cout << "mouse-operation is now PITCH" << endl; - mouse_operation=PITCH; - setCursor(Qt::SizeVerCursor); - } - - if (mouse_operation!=NO_OP) - { - if (!inserting && clicked_event_ptr->selected()==false) - { - if (!ctrl) - deselect_all(); - - clicked_event_ptr->setSelected(true); - - MusEGlobal::song->update(SC_SELECTION); - } - - old_pitch=-1; - old_dest_tick=INT_MAX; - old_len=-1; - } - } - - int new_pitch; - - switch (mouse_operation) - { - case NONE: - break; - - case PITCH: - if (heavyDebugMsg) cout << "trying to change pitch, delta="<<-nearbyint((float)dy/PITCH_DELTA)< 127) new_pitch=127; - - if (new_pitch != old_pitch) - { - if (debugMsg) cout << "changing pitch, delta="<undo(); - undo_started=transpose_notes(part_to_set(dragged_event_part),1, new_pitch-original_dragged_event.pitch()); - old_pitch=new_pitch; - } - - break; - - case BEGIN: - { - signed relative_tick=tick-signed(dragged_event_part->tick()); - unsigned dest_tick; - - if (relative_tick >= 0) - dest_tick=relative_tick; - else - { - dest_tick=0; - if (debugMsg) cout << "not moving note before begin of part; setting it directly to the begin" << endl; - } - - if (dest_tick != old_dest_tick) - { - if (undo_started) MusEGlobal::song->undo(); //FINDMICH EXTEND - undo_started=move_notes(part_to_set(dragged_event_part),1, (signed)dest_tick-original_dragged_event.tick()); - old_dest_tick=dest_tick; - } - } - - break; - - case LENGTH: - tick+=quant_ticks(); - if (dragged_event.tick()+old_len + dragged_event_part->tick() != unsigned(tick)) - { - MusECore::Event tmp=dragged_event.clone(); - signed relative_tick=tick-signed(dragged_event_part->tick()); - signed new_len=relative_tick-dragged_event.tick(); - - if (new_len>=0) - tmp.setLenTick(new_len); - else - { - tmp.setLenTick(0); - if (debugMsg) cout << "not setting len to a negative value. using 0 instead" << endl; - } - - unsigned newpartlen=dragged_event_part->lenTick(); - if (tmp.endTick() > dragged_event_part->lenTick()) - { - if (dragged_event_part->hasHiddenEvents()) // do not allow autoexpand - { - tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); - if (debugMsg) cout << "resized note would exceed its part; limiting length to " << tmp.lenTick() << endl; - } - else - { - newpartlen=tmp.endTick(); - if (debugMsg) cout << "resized note would exceeds its part; expanding the part..." << endl; - } - } - - if (undo_started) MusEGlobal::song->undo(); - MusECore::Undo operations; - operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, tmp, dragged_event, dragged_event_part, false, false)); - if (newpartlen != dragged_event_part->lenTick()) - schedule_resize_all_same_len_clone_parts(dragged_event_part, newpartlen, operations); - undo_started=MusEGlobal::song->applyOperationGroup(operations); - - old_len=new_len; - } - - break; - } - - - if ((mouse_operation==LENGTH) || (mouse_operation==BEGIN)) //x-scrolling enabled? - { - int win_x=event->x(); - - if (win_x < x_left + SCROLL_MARGIN) - { - x_scroll_speed=(win_x - (x_left + SCROLL_MARGIN)) * SCROLL_SPEED; - if (x_scroll_speed < -SCROLL_SPEED_MAX) x_scroll_speed=-SCROLL_SPEED_MAX; - } - else if (win_x > width() - SCROLL_MARGIN) - { - x_scroll_speed=(win_x - (width() - SCROLL_MARGIN)) * SCROLL_SPEED; - if (x_scroll_speed > SCROLL_SPEED_MAX) x_scroll_speed=SCROLL_SPEED_MAX; - } - else - x_scroll_speed=0; - } - else - { - x_scroll_speed=0; - } - } - - if (dragging_staff) //y-scrolling enabled? - { - int win_y=event->y(); - - if (win_y < SCROLL_MARGIN) - { - y_scroll_speed=(win_y - SCROLL_MARGIN) * SCROLL_SPEED; - if (y_scroll_speed < -SCROLL_SPEED_MAX) y_scroll_speed=-SCROLL_SPEED_MAX; - } - else if (win_y > height() - SCROLL_MARGIN) - { - y_scroll_speed=(win_y - (height() - SCROLL_MARGIN)) * SCROLL_SPEED; - if (y_scroll_speed > SCROLL_SPEED_MAX) y_scroll_speed=SCROLL_SPEED_MAX; - } - else - y_scroll_speed=0; - } - else - { - y_scroll_speed=0; - } - - if (have_lasso) - { - lasso=QRect(lasso_start, event->pos()).normalized(); - redraw(); - } + if (dragging) + { + int dx=event->x()-mouse_down_pos.x(); + int dy=event->y()-mouse_down_pos.y(); + + int x=event->x()+x_pos-x_left; + + int tick=flo_quantize_floor(x_to_tick(x), quant_ticks()); + + + if ((drag_cursor_changed==false) && ((dx!=0) || (dy!=0))) + { + setCursor(Qt::SizeAllCursor); + drag_cursor_changed=true; + } + + if (mouse_operation==NO_OP) + { + if ((abs(dx)>DRAG_INIT_DISTANCE) && (mouse_x_drag_operation!=NO_OP)) + { + if (debugMsg) cout << "mouse-operation is now "<DRAG_INIT_DISTANCE) + { + if (debugMsg) cout << "mouse-operation is now PITCH" << endl; + mouse_operation=PITCH; + setCursor(Qt::SizeVerCursor); + } + + if (mouse_operation!=NO_OP) + { + if (!inserting && clicked_event_ptr->selected()==false) + { + if (!ctrl) + deselect_all(); + + MusEGlobal::song->applyOperation(UndoOp(UndoOp::SelectEvent, *clicked_event_ptr, selected_part, true, clicked_event_ptr->selected())); + } + + old_pitch=-1; + old_dest_tick=INT_MAX; + old_len=-1; + } + } + + int new_pitch; + + switch (mouse_operation) + { + case NONE: + break; + + case PITCH: + if (heavyDebugMsg) cout << "trying to change pitch, delta="<<-nearbyint((float)dy/PITCH_DELTA)< 127) new_pitch=127; + + if (new_pitch != old_pitch) + { + if (debugMsg) cout << "changing pitch, delta="<undo(); + undo_started=transpose_notes(part_to_set(dragged_event_part),1, new_pitch-original_dragged_event.pitch()); + old_pitch=new_pitch; + } + + break; + + case BEGIN: + { + signed relative_tick=tick-signed(dragged_event_part->tick()); + unsigned dest_tick; + + if (relative_tick >= 0) + dest_tick=relative_tick; + else + { + dest_tick=0; + if (debugMsg) cout << "not moving note before begin of part; setting it directly to the begin" << endl; + } + + if (dest_tick != old_dest_tick) + { + if (undo_started) MusEGlobal::song->undo(); //FINDMICH EXTEND + undo_started=move_notes(part_to_set(dragged_event_part),1, (signed)dest_tick-original_dragged_event.tick()); + old_dest_tick=dest_tick; + } + } + + break; + + case LENGTH: + tick+=quant_ticks(); + if (dragged_event.tick()+old_len + dragged_event_part->tick() != unsigned(tick)) + { + MusECore::Event tmp=dragged_event.clone(); + signed relative_tick=tick-signed(dragged_event_part->tick()); + signed new_len=relative_tick-dragged_event.tick(); + + if (new_len>=0) + tmp.setLenTick(new_len); + else + { + tmp.setLenTick(0); + if (debugMsg) cout << "not setting len to a negative value. using 0 instead" << endl; + } + + unsigned newpartlen=dragged_event_part->lenTick(); + if (tmp.endTick() > dragged_event_part->lenTick()) + { + if (dragged_event_part->hasHiddenEvents()) // do not allow autoexpand + { + tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); + if (debugMsg) cout << "resized note would exceed its part; limiting length to " << tmp.lenTick() << endl; + } + else + { + newpartlen=tmp.endTick(); + if (debugMsg) cout << "resized note would exceeds its part; expanding the part..." << endl; + } + } + + if (undo_started) MusEGlobal::song->undo(); + MusECore::Undo operations; + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, tmp, dragged_event, dragged_event_part, false, false)); + if (newpartlen != dragged_event_part->lenTick()) + schedule_resize_all_same_len_clone_parts(dragged_event_part, newpartlen, operations); + undo_started=MusEGlobal::song->applyOperationGroup(operations); + + old_len=new_len; + } + + break; + } + + + if ((mouse_operation==LENGTH) || (mouse_operation==BEGIN)) //x-scrolling enabled? + { + int win_x=event->x(); + + if (win_x < x_left + SCROLL_MARGIN) + { + x_scroll_speed=(win_x - (x_left + SCROLL_MARGIN)) * SCROLL_SPEED; + if (x_scroll_speed < -SCROLL_SPEED_MAX) x_scroll_speed=-SCROLL_SPEED_MAX; + } + else if (win_x > width() - SCROLL_MARGIN) + { + x_scroll_speed=(win_x - (width() - SCROLL_MARGIN)) * SCROLL_SPEED; + if (x_scroll_speed > SCROLL_SPEED_MAX) x_scroll_speed=SCROLL_SPEED_MAX; + } + else + x_scroll_speed=0; + } + else + { + x_scroll_speed=0; + } + } + + if (dragging_staff) //y-scrolling enabled? + { + int win_y=event->y(); + + if (win_y < SCROLL_MARGIN) + { + y_scroll_speed=(win_y - SCROLL_MARGIN) * SCROLL_SPEED; + if (y_scroll_speed < -SCROLL_SPEED_MAX) y_scroll_speed=-SCROLL_SPEED_MAX; + } + else if (win_y > height() - SCROLL_MARGIN) + { + y_scroll_speed=(win_y - (height() - SCROLL_MARGIN)) * SCROLL_SPEED; + if (y_scroll_speed > SCROLL_SPEED_MAX) y_scroll_speed=SCROLL_SPEED_MAX; + } + else + y_scroll_speed=0; + } + else + { + y_scroll_speed=0; + } + + if (have_lasso) + { + lasso=QRect(lasso_start, event->pos()).normalized(); + redraw(); + } } void ScoreCanvas::heartbeat_timer_event() { - if (x_scroll_speed) - { - int old_xpos=x_pos; - - x_scroll_pos+=x_scroll_speed*MusEGlobal::heartBeatTimer->interval()/1000.0; - int tmp=int(x_scroll_pos); - if (tmp!=0) - x_pos+=tmp; - x_scroll_pos-=tmp; - - if (x_pos<0) x_pos=0; - if (x_pos>canvas_width()) x_pos=canvas_width(); - - if (old_xpos!=x_pos) emit xscroll_changed(x_pos); - } - - if (y_scroll_speed) - { - int old_ypos=y_pos; - - y_scroll_pos+=y_scroll_speed*MusEGlobal::heartBeatTimer->interval()/1000.0; - int tmp=int(y_scroll_pos); - if (tmp!=0) - y_pos+=tmp; - y_scroll_pos-=tmp; - - if (y_pos<0) y_pos=0; - if (y_pos>canvas_height()) y_pos=canvas_height(); + if (x_scroll_speed) + { + int old_xpos=x_pos; + + x_scroll_pos+=x_scroll_speed*MusEGlobal::heartBeatTimer->interval()/1000.0; + int tmp=int(x_scroll_pos); + if (tmp!=0) + x_pos+=tmp; + x_scroll_pos-=tmp; + + if (x_pos<0) x_pos=0; + if (x_pos>canvas_width()) x_pos=canvas_width(); + + if (old_xpos!=x_pos) emit xscroll_changed(x_pos); + } + + if (y_scroll_speed) + { + int old_ypos=y_pos; + + y_scroll_pos+=y_scroll_speed*MusEGlobal::heartBeatTimer->interval()/1000.0; + int tmp=int(y_scroll_pos); + if (tmp!=0) + y_pos+=tmp; + y_scroll_pos-=tmp; + + if (y_pos<0) y_pos=0; + if (y_pos>canvas_height()) y_pos=canvas_height(); - if (old_ypos!=y_pos) emit yscroll_changed(y_pos); - } + if (old_ypos!=y_pos) emit yscroll_changed(y_pos); + } } void ScoreCanvas::x_scroll_event(int x) { - if (debugMsg) cout << "SCROLL EVENT: x="<y_bottom; - } - - emit canvas_height_changed( canvas_height() ); + int y=0; + + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + { + it->y_top=y; + switch (it->type) + { + case NORMAL: + it->y_draw = it->y_top + STAFF_DISTANCE/2; + if (it->min_y_coord < -STAFF_DISTANCE/2) + it->y_draw+= (-it->min_y_coord - STAFF_DISTANCE/2); + + it->y_bottom = it->y_draw + STAFF_DISTANCE/2; + if (it->max_y_coord > STAFF_DISTANCE/2) + it->y_bottom+= (it->max_y_coord - STAFF_DISTANCE/2); + + break; + + case GRAND_TOP: + it->y_draw = it->y_top + STAFF_DISTANCE/2; + if (it->min_y_coord < -STAFF_DISTANCE/2) + it->y_draw+= (-it->min_y_coord - STAFF_DISTANCE/2); + + it->y_bottom = it->y_draw + GRANDSTAFF_DISTANCE/2; + + break; + + case GRAND_BOTTOM: + it->y_draw = it->y_top + GRANDSTAFF_DISTANCE/2; + + it->y_bottom = it->y_draw + STAFF_DISTANCE/2; + if (it->max_y_coord > STAFF_DISTANCE/2) + it->y_bottom+= (it->max_y_coord - STAFF_DISTANCE/2); + + break; + + default: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: invalid staff type!" << endl; + } + y=it->y_bottom; + } + + emit canvas_height_changed( canvas_height() ); } list::iterator ScoreCanvas::staff_at_y(int y) { - for (list::iterator it=staves.begin(); it!=staves.end(); it++) - if ((y >= it->y_top) && (y < it->y_bottom)) - return it; + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + if ((y >= it->y_top) && (y < it->y_bottom)) + return it; - return staves.end(); + return staves.end(); } void ScoreCanvas::play_changed(bool) { - redraw(); + redraw(); } void ScoreCanvas::config_changed() { - redraw(); + redraw(); } void ScoreCanvas::set_tool(int tool) { - switch (tool) - { - case MusEGui::PointerTool: mouse_erases_notes=false; mouse_inserts_notes=false; break; - case MusEGui::RubberTool: mouse_erases_notes=true; mouse_inserts_notes=false; break; - case MusEGui::PencilTool: mouse_erases_notes=false; mouse_inserts_notes=true; break; - default: - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: set_tool called with unknown tool ("<=0) && (val<5)) - { - int old_len=quant_len(); - - _quant_power2=val+1; - _quant_power2_init=_quant_power2; - - set_pixels_per_whole(pixels_per_whole() * quant_len() / old_len ); - - fully_recalculate(); - } - else - { - cerr << "ERROR: ILLEGAL FUNCTION CALL: set_quant called with invalid value of "<=0) && (val<5)) + { + int old_len=quant_len(); + + _quant_power2=val+1; + _quant_power2_init=_quant_power2; + + set_pixels_per_whole(pixels_per_whole() * quant_len() / old_len ); + + fully_recalculate(); + } + else + { + cerr << "ERROR: ILLEGAL FUNCTION CALL: set_quant called with invalid value of "<::iterator it=staves.begin(); it!=staves.end(); it++) - it->calc_item_pos(); - - emit pixels_per_whole_changed(val); - - if (old_xpos!=0) - { - x_pos=tick_to_x(tick); - if (debugMsg) cout << "x_pos was not zero, readjusting to " << x_pos << endl; - emit xscroll_changed(x_pos); - } - - redraw(); + if (debugMsg) cout << "setting px per whole to " << val << endl; + + int tick = 0; + int old_xpos=x_pos; + if (x_pos!=0) tick=x_to_tick(x_pos); + // the above saves us from a division by zero when initalizing + // ScoreCanvas; then x_pos will be 0 and x_to_tick (causing the + // division by zero) won't be called. also, when x_pos=0, and the + // above would not be done, after that function, x_pos will be + // not zero, but at the position of the first note (which isn't + // zero!) + + _pixels_per_whole=val; + _pixels_per_whole_init=val; + + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + it->calc_item_pos(); + + emit pixels_per_whole_changed(val); + + if (old_xpos!=0) + { + x_pos=tick_to_x(tick); + if (debugMsg) cout << "x_pos was not zero, readjusting to " << x_pos << endl; + emit xscroll_changed(x_pos); + } + + redraw(); } void ScoreCanvas::cleanup_staves() { - for (list::iterator it=staves.begin(); it!=staves.end();) - { - if (it->parts.empty()) - staves.erase(it++); - else - it++; - } - - maybe_close_if_empty(); + for (list::iterator it=staves.begin(); it!=staves.end();) + { + if (it->parts.empty()) + staves.erase(it++); + else + it++; + } + + maybe_close_if_empty(); } void ScoreCanvas::maybe_close_if_empty() { - if (staves.empty()) - { - if (!parent->close()) - cerr << "ERROR: THIS SHOULD NEVER HAPPEN: tried to close, but event hasn't been accepted!" << endl; - } + if (staves.empty()) + { + if (!parent->close()) + cerr << "ERROR: THIS SHOULD NEVER HAPPEN: tried to close, but event hasn't been accepted!" << endl; + } } void ScoreCanvas::set_velo(int velo) { - note_velo=velo; - note_velo_init=velo; + note_velo=velo; + note_velo_init=velo; - if (parent->get_apply_velo()) - modify_velocity(get_all_parts(),1, 0,velo); + if (parent->get_apply_velo()) + modify_velocity(get_all_parts(),1, 0,velo); } void ScoreCanvas::set_velo_off(int velo) { - note_velo_off=velo; - note_velo_off_init=velo; + note_velo_off=velo; + note_velo_off_init=velo; - if (parent->get_apply_velo()) - modify_off_velocity(get_all_parts(),1, 0,velo); + if (parent->get_apply_velo()) + modify_off_velocity(get_all_parts(),1, 0,velo); } void ScoreCanvas::deselect_all() { - set all_parts=get_all_parts(); + set all_parts=get_all_parts(); + + Undo operations; + operations.combobreaker=true; + + for (set::iterator part=all_parts.begin(); part!=all_parts.end(); part++) + for (MusECore::ciEvent event=(*part)->events().begin(); event!=(*part)->events().end(); event++) + operations.push_back(UndoOp(UndoOp::SelectEvent, event->second, *part, false, event->second.selected())); - for (set::iterator part=all_parts.begin(); part!=all_parts.end(); part++) - for (MusECore::iEvent event=(*part)->events()->begin(); event!=(*part)->events()->end(); event++) - event->second.setSelected(false); - - MusEGlobal::song->update(SC_SELECTION); + MusEGlobal::song->applyOperationGroup(operations); } bool staff_t::cleanup_parts() { - bool did_something=false; - - for (set::iterator it=parts.begin(); it!=parts.end();) - { - bool valid=false; - - for (MusECore::iTrack track=MusEGlobal::song->tracks()->begin(); track!=MusEGlobal::song->tracks()->end(); track++) - if ((*track)->type() == MusECore::Track::MIDI) - { - MusECore::PartList* pl=(*track)->parts(); - for (MusECore::iPart part=pl->begin(); part!=pl->end(); part++) - if (*it == part->second) - { - valid=true; - goto get_out_here2; - } - } - - get_out_here2: - if (!valid) - { - parts.erase(it++); - - did_something=true; - } - else - it++; - } - - if (did_something) update_part_indices(); - return did_something; -} - -set staff_t::parts_at_tick(unsigned tick) -{ - set result; - - for (set::iterator it=parts.begin(); it!=parts.end(); it++) - if ((tick >= (*it)->tick()) && (tick<=(*it)->endTick())) - result.insert(*it); - - return result; -} - -void staff_t::apply_lasso(QRect rect, set& already_processed) -{ - for (ScoreItemList::iterator it=itemlist.begin(); it!=itemlist.end(); it++) - for (set::iterator it2=it->second.begin(); it2!=it->second.end(); it2++) - if (it2->type==FloItem::NOTE) - { - if (rect.contains(it2->x, it2->y)) - if (already_processed.find(it2->source_event)==already_processed.end()) - { - it2->source_event->setSelected(!it2->source_event->selected()); - already_processed.insert(it2->source_event); - } - } + bool did_something=false; + + for (set::iterator it=parts.begin(); it!=parts.end();) + { + bool valid=false; + + for (MusECore::iTrack track=MusEGlobal::song->tracks()->begin(); track!=MusEGlobal::song->tracks()->end(); track++) + if ((*track)->type() == MusECore::Track::MIDI) + { + MusECore::PartList* pl=(*track)->parts(); + for (MusECore::iPart part=pl->begin(); part!=pl->end(); part++) + if (*it == part->second) + { + valid=true; + goto get_out_here2; + } + } + + get_out_here2: + if (!valid) + { + parts.erase(it++); + + did_something=true; + } + else + it++; + } + + if (did_something) update_part_indices(); + return did_something; +} + +set staff_t::parts_at_tick(unsigned tick) +{ + set result; + + for (set::iterator it=parts.begin(); it!=parts.end(); it++) + if ((tick >= (*it)->tick()) && (tick<=(*it)->endTick())) + result.insert(*it); + + return result; +} + +void staff_t::apply_lasso(QRect rect, set& already_processed) +{ + Undo operations; + for (ScoreItemList::iterator it=itemlist.begin(); it!=itemlist.end(); it++) + for (set::iterator it2=it->second.begin(); it2!=it->second.end(); it2++) + if (it2->type==FloItem::NOTE) + { + if (rect.contains(it2->x, it2->y)) + if (already_processed.find(it2->source_event)==already_processed.end()) + { + operations.push_back(UndoOp(UndoOp::SelectEvent,*it2->source_event,it2->source_part,!it2->source_event->selected(),it2->source_event->selected())); + already_processed.insert(it2->source_event); + } + } + MusEGlobal::song->applyOperationGroup(operations); } void ScoreCanvas::set_steprec(bool flag) { - srec=flag; + srec=flag; } void ScoreCanvas::midi_note(int pitch, int velo) { - if (velo) - held_notes[pitch]=true; - else - held_notes[pitch]=false; + if (velo) + held_notes[pitch]=true; + else + held_notes[pitch]=false; - if ( srec && selected_part && !MusEGlobal::audio->isPlaying() && velo ) - steprec->record(selected_part,pitch,quant_ticks(),quant_ticks(),velo,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier); + if ( srec && selected_part && !MusEGlobal::audio->isPlaying() && velo ) + steprec->record(selected_part,pitch,quant_ticks(),quant_ticks(),velo,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier); } void ScoreCanvas::update_parts() { - if (selected_part!=NULL) //if it's null, let it be null - selected_part=MusECore::partFromSerialNumber(selected_part_index); - - if (dragged_event_part!=NULL) //same thing here - dragged_event_part=MusECore::partFromSerialNumber(dragged_event_part_index); - - for (list::iterator it=staves.begin(); it!=staves.end(); it++) - it->update_parts(); + if (selected_part!=NULL) //if it's null, let it be null + selected_part=MusECore::partFromSerialNumber(selected_part_index); + + if (dragged_event_part!=NULL) //same thing here + dragged_event_part=MusECore::partFromSerialNumber(dragged_event_part_index); + + for (list::iterator it=staves.begin(); it!=staves.end(); it++) + it->update_parts(); } void staff_t::update_parts() { - parts.clear(); - - for (set::iterator it=part_indices.begin(); it!=part_indices.end(); it++) - parts.insert(MusECore::partFromSerialNumber(*it)); + parts.clear(); + + for (set::iterator it=part_indices.begin(); it!=part_indices.end(); it++) + parts.insert(MusECore::partFromSerialNumber(*it)); } void staff_t::update_part_indices() { - part_indices.clear(); - - for (set::iterator it=parts.begin(); it!=parts.end(); it++) - part_indices.insert((*it)->sn()); + part_indices.clear(); + + for (set::iterator it=parts.begin(); it!=parts.end(); it++) + part_indices.insert((*it)->sn()); } void ScoreEdit::keyPressEvent(QKeyEvent* event) { - int key = event->key(); + int key = event->key(); - if (key == Qt::Key_Escape) - { - close(); - return; - } - else if (key == shortcuts[SHRT_TOOL_POINTER].key) - { - edit_tools->set(MusEGui::PointerTool); - return; - } - else if (key == shortcuts[SHRT_TOOL_PENCIL].key) - { - edit_tools->set(MusEGui::PencilTool); - return; - } - else if (key == shortcuts[SHRT_TOOL_RUBBER].key) - { - edit_tools->set(MusEGui::RubberTool); - return; - } - else //Default: - { - event->ignore(); - return; - } + if (key == Qt::Key_Escape) + { + close(); + return; + } + else if (key == shortcuts[SHRT_TOOL_POINTER].key) + { + edit_tools->set(MusEGui::PointerTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_PENCIL].key) + { + edit_tools->set(MusEGui::PencilTool); + return; + } + else if (key == shortcuts[SHRT_TOOL_RUBBER].key) + { + edit_tools->set(MusEGui::RubberTool); + return; + } + else //Default: + { + event->ignore(); + return; + } } -void ScoreCanvas::add_new_parts(const std::map< MusECore::Part*, std::set >& param) -{ - for (list::iterator staff=staves.begin(); staff!=staves.end(); staff++) - { - for (std::map< MusECore::Part*, set >::const_iterator it = param.begin(); it!=param.end(); it++) - if (staff->parts.find(it->first)!=staff->parts.end()) - staff->parts.insert(it->second.begin(), it->second.end()); - - //staff->cleanup_parts(); // don't cleanup here, because at this point, the parts may only exist - // in the operation group. cleanup could remove them immediately - staff->update_part_indices(); - } - - fully_recalculate(); +void ScoreCanvas::add_new_parts(const std::map< const MusECore::Part*, std::set >& param) +{ + for (list::iterator staff=staves.begin(); staff!=staves.end(); staff++) + { + for (std::map< const MusECore::Part*, set >::const_iterator it = param.begin(); it!=param.end(); it++) + if (staff->parts.find(it->first)!=staff->parts.end()) + staff->parts.insert(it->second.begin(), it->second.end()); + + //staff->cleanup_parts(); // don't cleanup here, because at this point, the parts might exist only + // in the operation group. cleanup could remove them immediately + staff->update_part_indices(); + } + + fully_recalculate(); } } // namespace MusEGui @@ -4685,7 +4698,7 @@ //note: recalculating event- and itemlists "from zero" // could happen in realtime, as it is pretty fast. -// however, this adds unneccessary cpu usage. +// however, this adds unnecessary cpu usage. // it is NO problem to recalc the stuff "from zero" // every time something changes. @@ -4708,7 +4721,7 @@ * o rename E- and A-note to I- and O- * o storing into a template file seems to ignore * the arranger's "MDI-ness", sets is at subwin all the time! - * + * * o add controller editor "like search-and-replace": * acts on all specified type's events, and edits the value: * - apply some MIN or MAX on it @@ -4722,7 +4735,7 @@ * song only contains logical information: not some "synth port", * but a "synth name", like "Yamaha DX7" or "TR-808". * the global config contains physical information, like - * "'Yamaha DX7' is on 'USB Midi Bridge, Output two'", + * "'Yamaha DX7' is on 'USB Midi Bridge, Output two'", * "'TR-808' is on 'jack-midi port foo, connected to bar'" * if loading a song with unknown synth name, ask the user. * o prerecord feature. @@ -4730,7 +4743,7 @@ * * o find and fix FINDMICHJETZT * o fix valgrind problems (the two "FINDMICHJETZT" lines in scoreedit.cpp) - * + * * IMPORTANT TODO * o allow steprec-insert-rest-note to be set to "off" / "unused" * o all places where i added doubleclick-edits: only react on left-click double clicks! @@ -4753,7 +4766,7 @@ * o implement borland-style maximize: free windows do not cover the main menu, even when maximized * o smart range selection: if range markers have been used recently (that is, a dialog with * "range" setting, or they've been modified), default to "in range" or "selected in range" - * + * * o rename stuff with F2 key * * o shrink a part from its beginning as well! watch out for clones! @@ -4771,7 +4784,7 @@ * calc_pos_add_list must be called before calc_item_pos then, * and calc_item_pos must respect the pos_add_list instead of * keeping its own pos_add variable (which is only an optimisation) - * + * * really unimportant nice-to-haves * o support in-song clef-changes * o draw measure numbers @@ -4779,9 +4792,9 @@ * o refuse to resize so that width gets smaller or equal than x_left * o draw a margin around notes which are in a bright color * o support drum tracks in the score editor (x-note-heads etc.) - * o drum list: scroll while dragging: probably unneccessary with the "reorder list" function - * - * + * o drum list: scroll while dragging: probably unnecessary with the "reorder list" function + * + * * stuff for the other muse developers * o process accurate timesignatures from muse's list (has to be implemented first in muse) * ( (2+2+3)/4 or (3+2+2)/4 instead of 7/4 ) @@ -4790,48 +4803,48 @@ /* R O A D M A P * ============= - * + * * 1. finish the score editor, without transposing instruments and * with only a global keymap - * + * * REASON: a score editor with few functions is better than * no score editor at all - * - * + * + * * 2. support transposing by octave-steps - * + * * REASON: the main problem with transposing is, that the * editor needs different key signatures and needs * to align them against each other. this problem * doesn't exist when only transposing by octaves - * - * + * + * * 3. support transposing instruments, but only one - * transposing-setting per score window. that is, you won't be - * able to display your (C-)strings in the same window as your + * transposing-setting per score window. that is, you won't be + * able to display your (C-)strings in the same window as your * B-trumpet. this will be very easy to implement - * + * * REASON: the above problem still exists, but is circumvented * by simply not having to align them against each other * (because they're in different windows) - * - * + * + * * 4. support different transposing instruments in the same score * window. this will be some hassle, because we need to align - * the scores properly. for example, when the C-violin has + * the scores properly. for example, when the C-violin has * C-major (no accidentials), then the B-trumpet need some * accidentials. we now must align the staves so that the * "note-after-keychange"s of both staves are again at the * same x-position - * + * * REASON: some solution for that problem must be written. * this is a large step, which atm isn't very important - * - * + * + * * 5. support different keys per track. this wouldn't be that * hard, when 4) is already done; because we then already have * the "align it properly" functionality, and can use it - * + * * REASON: this is only a nice-to-have, which can however be * easily implemented when 4) is done */ diff -Nru muse-2.1.2/muse/midiedit/scoreedit.h muse-3.0.2+ds1/muse/midiedit/scoreedit.h --- muse-2.1.2/muse/midiedit/scoreedit.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiedit/scoreedit.h 2018-01-26 21:59:38.000000000 +0000 @@ -257,8 +257,8 @@ enum typeEnum { NOTE_ON = 30, NOTE_OFF = 10, BAR = 20, KEY_CHANGE=23, TIME_SIG=26 }; //the order matters! typeEnum type; unsigned tick; - MusECore::Part* source_part; - MusECore::Event* source_event; + const MusECore::Part* source_part; + const MusECore::Event* source_event; int pitch; mutable int vel; @@ -270,7 +270,7 @@ MusECore::key_enum key; - FloEvent(unsigned ti, int p,int v,int l,typeEnum t, MusECore::Part* part=NULL, MusECore::Event* event=NULL) + FloEvent(unsigned ti, int p,int v,int l,typeEnum t, const MusECore::Part* part=NULL, const MusECore::Event* event=NULL) { pitch=p; vel=v; @@ -312,8 +312,8 @@ enum typeEnum { NOTE=21, REST=22, NOTE_END=01, REST_END=02, BAR =10, KEY_CHANGE=13, TIME_SIG=16}; //the order matters! typeEnum type; unsigned begin_tick; - MusECore::Event* source_event; - MusECore::Part* source_part; + const MusECore::Event* source_event; + const MusECore::Part* source_part; note_pos_t pos; int len; @@ -343,7 +343,7 @@ - FloItem(typeEnum t, note_pos_t p, int l=0,int d=0, bool ti=false, unsigned beg=0, MusECore::Part* part=NULL, MusECore::Event* event=NULL) + FloItem(typeEnum t, note_pos_t p, int l=0,int d=0, bool ti=false, unsigned beg=0, const MusECore::Part* part=NULL, const MusECore::Event* event=NULL) { pos=p; dots=d; @@ -417,7 +417,7 @@ case REST_END: return (this->pos == that.pos); - //the following may only occurr once in a set + //the following may only occur once in a set //so we don't search for "the time signature with 4/4 //at t=0", but only for "some time signature at t=0" //that's why true is returned, and not some conditional @@ -454,7 +454,7 @@ case FloItem::REST_END: return (a.pos < b.pos); - //the following may only occurr once in a set + //the following may only occur once in a set //so we don't search for "the time signature with 4/4 //at t=0", but only for "some time signature at t=0" //that's why true is returned, and not some conditional @@ -546,7 +546,7 @@ struct staff_t { - set parts; + set parts; set part_indices; ScoreEventList eventlist; ScoreItemList itemlist; @@ -568,7 +568,7 @@ void process_itemlist(); void calc_item_pos(); - void apply_lasso(QRect rect, set& already_processed); + void apply_lasso(QRect rect, set& already_processed); void recalculate() { @@ -585,7 +585,7 @@ parent=parent_; } - staff_t (ScoreCanvas* parent_, staff_type_t type_, clef_t clef_, set parts_) + staff_t (ScoreCanvas* parent_, staff_type_t type_, clef_t clef_, set parts_) { type=type_; clef=clef_; @@ -596,7 +596,7 @@ bool cleanup_parts(); - set parts_at_tick(unsigned tick); + set parts_at_tick(unsigned tick); void read_status(MusECore::Xml& xml); void write_status(int level, MusECore::Xml& xml) const; @@ -718,7 +718,7 @@ float y_scroll_speed; float y_scroll_pos; - MusECore::Part* selected_part; + const MusECore::Part* selected_part; int selected_part_index; int last_len; @@ -742,11 +742,11 @@ bool inserting; bool dragging; bool drag_cursor_changed; - MusECore::Part* dragged_event_part; + const MusECore::Part* dragged_event_part; int dragged_event_part_index; MusECore::Event dragged_event; MusECore::Event original_dragged_event; - MusECore::Event* clicked_event_ptr; + const MusECore::Event* clicked_event_ptr; int old_pitch; unsigned old_dest_tick; @@ -790,7 +790,7 @@ void deselect_all(); void midi_note(int pitch, int velo); - void add_new_parts(const std::map< MusECore::Part*, std::set >&); + void add_new_parts(const std::map< const MusECore::Part*, std::set >&); public slots: void x_scroll_event(int); @@ -855,12 +855,12 @@ int get_last_len() {return last_len;} void set_last_len(int l) {last_len=l;} - MusECore::Part* get_selected_part() {return selected_part;} - void set_selected_part(MusECore::Part* p) {selected_part=p; if (selected_part) selected_part_index=selected_part->sn();} - MusECore::Part* get_dragged_event_part() {return dragged_event_part;} - void set_dragged_event_part(MusECore::Part* p) {dragged_event_part=p; if (dragged_event_part) dragged_event_part_index=dragged_event_part->sn();} + const MusECore::Part* get_selected_part() const {return selected_part;} + void set_selected_part(const MusECore::Part* p) {selected_part=p; if (selected_part) selected_part_index=selected_part->sn();} + const MusECore::Part* get_dragged_event_part() const {return dragged_event_part;} + void set_dragged_event_part(const MusECore::Part* p) {dragged_event_part=p; if (dragged_event_part) dragged_event_part_index=dragged_event_part->sn();} - set get_all_parts(); + set get_all_parts(); void write_staves(int level, MusECore::Xml& xml) const; diff -Nru muse-2.1.2/muse/midieditor.cpp muse-3.0.2+ds1/muse/midieditor.cpp --- muse-2.1.2/muse/midieditor.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/midieditor.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -62,7 +62,7 @@ mainGrid->setSpacing(0); setCentralWidget(mainw); - connect(MusEGlobal::song, SIGNAL(newPartsCreated(const std::map< MusECore::Part*, std::set >&)), SLOT(addNewParts(const std::map< MusECore::Part*, std::set >&))); + connect(MusEGlobal::song, SIGNAL(newPartsCreated(const std::map< const MusECore::Part*, std::set >&)), SLOT(addNewParts(const std::map< const MusECore::Part*, std::set >&))); } //--------------------------------------------------------- @@ -71,6 +71,9 @@ void MidiEditor::genPartlist() { + if(!_pl) + return; + _pl->clear(); for (std::set::iterator i = _parts.begin(); i != _parts.end(); ++i) { MusECore::TrackList* tl = MusEGlobal::song->tracks(); @@ -92,9 +95,32 @@ //--------------------------------------------------------- // addPart //--------------------------------------------------------- +void MidiEditor::movePlayPointerToSelectedEvent() +{ + const MusECore::EventList & evl = curCanvasPart()->events(); + int tickPos = -1; + for (MusECore::ciEvent ev=evl.begin(); ev!=evl.end(); ev++) { + if (ev->second.selected()) { + tickPos = ev->second.tick(); + printf("found selected event, moving to pos %d\n", tickPos); + break; + } + } + if (tickPos > -1) + { + MusECore::Pos p0(curCanvasPart()->tick() + tickPos, true); + MusEGlobal::song->setPos(0, p0); + } +} + +//--------------------------------------------------------- +// addPart +//--------------------------------------------------------- void MidiEditor::addPart(MusECore::Part* p) { + if(!_pl || !p) + return; _pl->add(p); _parts.insert(p->sn()); } @@ -150,6 +176,8 @@ void MidiEditor::writePartList(int level, MusECore::Xml& xml) const { + if(!_pl) + return; for (MusECore::ciPart p = _pl->begin(); p != _pl->end(); ++p) { MusECore::Part* part = p->second; MusECore::Track* track = part->track(); @@ -245,15 +273,18 @@ canvas->setCurrentPart(part); } -void MidiEditor::addNewParts(const std::map< MusECore::Part*, std::set >& param) +void MidiEditor::addNewParts(const std::map< const MusECore::Part*, std::set >& param) { + if(!_pl) + return; + using std::map; using std::set; - for (map< MusECore::Part*, set >::const_iterator it = param.begin(); it!=param.end(); it++) + for (map< const MusECore::Part*, set >::const_iterator it = param.begin(); it!=param.end(); it++) if (_pl->index(it->first) != -1) - for (set::const_iterator it2=it->second.begin(); it2!=it->second.end(); it2++) - addPart(*it2); + for (set::const_iterator it2=it->second.begin(); it2!=it->second.end(); it2++) + addPart(const_cast(*it2)); // FIXME make this const-correct! } } // namespace MusEGui diff -Nru muse-2.1.2/muse/midieditor.h muse-3.0.2+ds1/muse/midieditor.h --- muse-2.1.2/muse/midieditor.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/midieditor.h 2017-12-04 21:01:18.000000000 +0000 @@ -59,6 +59,7 @@ std::set _parts; int _curDrumInstrument; // currently selected instrument if drum // editor + protected: MusEGui::ScrollScale* hscroll; MusEGui::ScrollScale* vscroll; @@ -74,9 +75,10 @@ virtual void writeStatus(int, MusECore::Xml&) const; void writePartList(int, MusECore::Xml&) const; void genPartlist(); + void movePlayPointerToSelectedEvent(); private slots: - void addNewParts(const std::map< MusECore::Part*, std::set >&); + void addNewParts(const std::map< const MusECore::Part*, std::set >&); public slots: void songChanged(MusECore::SongChangedFlags_t type); diff -Nru muse-2.1.2/muse/midievent.cpp muse-3.0.2+ds1/muse/midievent.cpp --- muse-2.1.2/muse/midievent.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/midievent.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -41,11 +41,60 @@ c = 0; } +MidiEventBase::MidiEventBase(const MidiEventBase& ev, bool duplicate_not_clone) + : EventBase(ev, duplicate_not_clone) +{ + a = ev.a; + b = ev.b; + c = ev.c; + if(duplicate_not_clone) + edata.setData(ev.data(), ev.dataLen()); // Makes a copy. + else + // NOTE: Even non-shared clone events ALWAYS share edata. edata does NOT currently require + // separate instances, unlike wave events which absolutely do. + // Be aware when iterating or modifying clones for example. (It can save time.) + edata = ev.edata; +} + +//--------------------------------------------------------- +// assign +//--------------------------------------------------------- + +void MidiEventBase::assign(const EventBase& ev) +{ + if(ev.type() != type()) + return; + EventBase::assign(ev); + a = ev.dataA(); + b = ev.dataB(); + c = ev.dataC(); + // NOTE: Even non-shared clone events ALWAYS share edata. edata does NOT currently require + // separate instances, unlike wave events which absolutely do. + // Be aware when iterating or modifying clones for example. (It can save time.) + if(edata.data != ev.data()) + edata.setData(ev.data(), ev.dataLen()); // Makes a copy. +} + +bool MidiEventBase::isSimilarTo(const EventBase& other_) const +{ + const MidiEventBase* other = dynamic_cast(&other_); + if (other==NULL) // dynamic cast hsa failed: "other_" is not of type MidiEventBase. + return false; + + if ((a==other->a && b==other->b && c==other->c && edata.dataLen==other->edata.dataLen && this->PosLen::operator==(*other)) == false) + return false; + + if (edata.dataLen > 0) + return (memcmp(edata.data, other->edata.data, edata.dataLen) == 0); + else + return true; // no data equals no data. +} + //--------------------------------------------------------- // MidiEventBase::mid //--------------------------------------------------------- -EventBase* MidiEventBase::mid(unsigned b, unsigned e) +EventBase* MidiEventBase::mid(unsigned b, unsigned e) const { if (tick() < b || tick() >= e) return 0; diff -Nru muse-2.1.2/muse/midievent.h muse-3.0.2+ds1/muse/midievent.h --- muse-2.1.2/muse/midievent.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/midievent.h 2017-12-04 21:01:18.000000000 +0000 @@ -34,13 +34,26 @@ class MidiEventBase : public EventBase { int a, b, c; // pitch, velo-on, velo-off + // Sysex event data. + // NOTE: Even non-shared clone events ALWAYS share edata. edata does NOT currently require + // separate instances, unlike wave events which absolutely do. + // Be aware when iterating or modifying clones for example. (It can save time.) EvData edata; - virtual EventBase* clone() { return new MidiEventBase(*this); } + // Creates a non-shared clone (copies event base), including the same 'group' id. + virtual EventBase* clone() const { return new MidiEventBase(*this); } + // Creates a copy of the event base, excluding the 'group' _id. + virtual EventBase* duplicate() const { return new MidiEventBase(*this, true); } public: MidiEventBase(EventType t); + // Creates a non-shared clone with same id, or duplicate with unique id, and 0 ref count and invalid Pos sn. + MidiEventBase(const MidiEventBase& ev, bool duplicate_not_clone = false); virtual ~MidiEventBase() {} + + virtual void assign(const EventBase& ev); // Assigns to this event, excluding the _id. + + virtual bool isSimilarTo(const EventBase& other) const; virtual bool isNote() const { return type() == Note; } virtual bool isNoteOff() const; @@ -71,7 +84,7 @@ virtual void dump(int n = 0) const; virtual void read(Xml&); virtual void write(int, Xml&, const Pos& offset, bool forcePath = false) const; - virtual EventBase* mid(unsigned, unsigned); + virtual EventBase* mid(unsigned, unsigned) const; }; } // namespace MusECore diff -Nru muse-2.1.2/muse/midifile.cpp muse-3.0.2+ds1/muse/midifile.cpp --- muse-2.1.2/muse/midifile.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midifile.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -55,8 +55,9 @@ MF_READ, MF_WRITE, MF_MTRK, + //MF_MTRK_ZERO_DATA, MF_MTHD, - MF_FORMAT, + MF_FORMAT }; //--------------------------------------------------------- @@ -84,10 +85,27 @@ MidiFile::~MidiFile() { - delete _tracks; + if(_tracks) + { + _tracks->clearDelete(); + delete _tracks; + _tracks = 0; + } delete _usedPortMap; } +void MidiFile::setTrackList(MidiFileTrackList* tr, int n) +{ + if(_tracks) + { + _tracks->clearDelete(); + delete _tracks; + _tracks = 0; + } + _tracks = tr; + ntracks = n; +} + //--------------------------------------------------------- // read // return true on error @@ -239,6 +257,9 @@ return true; } int len = readLong(); // len + if(len <= 0) + return false; + int endPos = curPos + len; status = -1; sstatus = -1; // running status, not reset scanning meta or sysex @@ -345,7 +366,7 @@ channel = event.channel(); el->add(event); } - + int end = curPos; if (end != endPos) { printf("MidiFile::readTrack(): TRACKLEN does not fit %d+%d != %d, %d too much\n", @@ -419,6 +440,10 @@ lastMtype = MT_GM; return -1; } + if (((unsigned)len == gm2OnMsgLen) && memcmp(buffer, gm2OnMsg, gm2OnMsgLen) == 0) { + lastMtype = MT_GM2; + return -1; + } if (((unsigned)len == gsOnMsgLen) && memcmp(buffer, gsOnMsg, gsOnMsgLen) == 0) { lastMtype = MT_GS; return -1; @@ -466,7 +491,7 @@ } } if(MusEGlobal::debugMsg) - printf("MidiFile::readEvent: unknown Sysex 0x%02x unabsorbed, passing thru intead\n", me & 0xff); + printf("MidiFile::readEvent: unknown Sysex 0x%02x unabsorbed, passing thru instead\n", me & 0xff); return 3; } if (me == 0xff) { @@ -644,7 +669,7 @@ int c = event->channel(); int nstat = event->type(); - // we dont save meta data into smf type 0 files: DELETETHIS 4 ??? + // we don't save meta data into smf type 0 files: DELETETHIS 4 ??? // Oct 16, 2011: Apparently it is legal to do that. Part of fix for bug tracker 3293339. //if (MusEGlobal::config.smfFormat == 0 && nstat == ME_META) // return; @@ -653,7 +678,7 @@ // // running status; except for Sysex- and Meta Events // - if (((nstat & 0xf0) != 0xf0) && (nstat != status)) { + if (((nstat & 0xf0) != 0xf0) && ((nstat != status) || !MusEGlobal::config.expRunningStatus)) { status = nstat; put(nstat); } @@ -771,24 +796,44 @@ case 0: { MidiFileTrack* t = new MidiFileTrack; - _tracks->push_back(t); if (readTrack(t)) - return true; + { + delete t; + return true; + } + else + _tracks->push_back(t); } break; case 1: - for (i = 0; i < ntracks; i++) { - MidiFileTrack* t = new MidiFileTrack; - _tracks->push_back(t); - if (readTrack(t)) - return true; - } + for (i = 0; i < ntracks; ++i) + { + MidiFileTrack* t = new MidiFileTrack; + if (readTrack(t)) + { + delete t; + return true; + } + else + _tracks->push_back(t); + } break; default: _error = MF_FORMAT; return true; } + return false; } +void MidiFileTrackList::clearDelete() +{ + for(iterator i = begin(); i != end(); ++i) + { + if(*i) + delete *i; + } + clear(); +} + } // namespace MusECore diff -Nru muse-2.1.2/muse/midifile.h muse-3.0.2+ds1/muse/midifile.h --- muse-2.1.2/muse/midifile.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/midifile.h 2017-12-17 21:07:38.000000000 +0000 @@ -35,7 +35,7 @@ namespace MusECore { -struct MPEventList; +class MPEventList; class MidiPlayEvent; class MidiInstrument; @@ -71,7 +71,11 @@ } }; -typedef std::list MidiFileTrackList; +class MidiFileTrackList : public std::list +{ + public: + void clearDelete(); +}; typedef MidiFileTrackList::iterator iMidiFileTrack; typedef MidiFileTrackList::const_iterator ciMidiFileTrack; @@ -125,10 +129,8 @@ MidiFilePortMap* usedPortMap() { return _usedPortMap; } MidiFileTrackList* trackList() { return _tracks; } int tracks() const { return ntracks; } - void setTrackList(MidiFileTrackList* tr, int n) { - _tracks = tr; - ntracks = n; - } + // Takes ownership of list and its contents. + void setTrackList(MidiFileTrackList* tr, int n); void setDivision(int d) { _division = d; } int division() const { return _division; } }; diff -Nru muse-2.1.2/muse/midi.h muse-3.0.2+ds1/muse/midi.h --- muse-2.1.2/muse/midi.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midi.h 2018-01-29 20:07:03.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: midi.h,v 1.4.2.2 2009/11/09 20:28:28 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) -// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2016 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,66 +29,14 @@ #include #include +#include "midi_consts.h" + class QString; namespace MusECore { class EventList; -enum { - ME_NOTEOFF = 0x80, - ME_NOTEON = 0x90, - ME_POLYAFTER = 0xa0, - ME_CONTROLLER = 0xb0, - ME_PROGRAM = 0xc0, - ME_AFTERTOUCH = 0xd0, - ME_PITCHBEND = 0xe0, - ME_SYSEX = 0xf0, - ME_META = 0xff, - ME_MTC_QUARTER = 0xf1, - ME_SONGPOS = 0xf2, - ME_SONGSEL = 0xf3, - ME_TUNE_REQ = 0xf6, - ME_SYSEX_END = 0xf7, - ME_CLOCK = 0xf8, - ME_TICK = 0xf9, - ME_START = 0xfa, - ME_CONTINUE = 0xfb, - ME_STOP = 0xfc, - ME_SENSE = 0xfe - }; - -//-------------------------------------- -// Recognized / transmitted meta events: -//-------------------------------------- -enum { - ME_META_TEXT_0_SEQUENCE_NUMBER = 0x00, - ME_META_TEXT_1_COMMENT = 0x01, - ME_META_TEXT_2_COPYRIGHT = 0x02, - ME_META_TEXT_3_TRACK_NAME = 0x03, - ME_META_TEXT_4_INSTRUMENT_NAME = 0x04, - ME_META_TEXT_5_LYRIC = 0x05, - ME_META_TEXT_6_MARKER = 0x06, - ME_META_TEXT_7_CUE_POINT = 0x07, - ME_META_TEXT_8 = 0x08, - ME_META_TEXT_9_DEVICE_NAME = 0x09, - ME_META_TEXT_A = 0x0a, - ME_META_TEXT_B = 0x0b, - ME_META_TEXT_C = 0x0c, - ME_META_TEXT_D = 0x0d, - ME_META_TEXT_E = 0x0e, - ME_META_TEXT_F_TRACK_COMMENT = 0x0f, - ME_META_CHANNEL_CHANGE = 0x20, - ME_META_PORT_CHANGE = 0x21, - ME_META_END_OF_TRACK = 0x2f, - ME_META_SET_TEMPO = 0x51, - ME_META_SMPTE_OFFSET = 0x54, - ME_META_TIME_SIGNATURE = 0x58, - ME_META_KEY_SIGNATURE = 0x59, - ME_META_SEQ_SPECIFIC_1 = 0x74, - ME_META_SEQ_SPECIFIC_2 = 0x7f -}; - enum AudioTickSound { beatSound, measureSound, @@ -96,37 +44,14 @@ accent2Sound }; -extern const unsigned char gmOnMsg[]; - -extern const unsigned char gsOnMsg[]; -extern const unsigned char gsOnMsg2[]; -extern const unsigned char gsOnMsg3[]; -extern const unsigned char xgOnMsg[]; -extern const unsigned char mmcDeferredPlayMsg[]; -extern const unsigned char mmcStopMsg[]; -extern const unsigned char mmcLocateMsg[]; - -extern const unsigned int gmOnMsgLen; -extern const unsigned int gsOnMsgLen; -extern const unsigned int gsOnMsg2Len; -extern const unsigned int gsOnMsg3Len; -extern const unsigned int xgOnMsgLen; -extern const unsigned int mmcDeferredPlayMsgLen; -extern const unsigned int mmcStopMsgLen; -extern const unsigned int mmcLocateMsgLen; - -QString nameSysex(unsigned int len, const unsigned char* buf); -QString midiMetaName(int); - -// Use these in all the synths and their guis. -// Did this here for ease, since they all include this file. -// -// A special MusE soft synth sysex manufacturer ID. -#define MUSE_SYNTH_SYSEX_MFG_ID 0x7c +class MidiInstrument; +extern QString nameSysex(unsigned int len, const unsigned char* buf, MidiInstrument* instr = 0); +extern QString sysexComment(unsigned int len, const unsigned char* buf, MidiInstrument* instr = 0); +extern QString midiMetaName(int meta); class MPEventList; class MidiTrack; -extern void buildMidiEventList(EventList* mel, const MPEventList* el, MidiTrack* track, int division, bool addSysexMeta, bool doLoops); +extern void buildMidiEventList(EventList* mel, const MPEventList& el, MidiTrack* track, int division, bool addSysexMeta, bool doLoops); } // namespace MusECore diff -Nru muse-2.1.2/muse/midiport.cpp muse-3.0.2+ds1/muse/midiport.cpp --- muse-2.1.2/muse/midiport.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiport.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: midiport.cpp,v 1.21.2.15 2009/12/07 20:11:51 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012, 2017 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,13 +24,13 @@ #include -#include -#include +#include #include "mididev.h" #include "midiport.h" #include "midictrl.h" #include "midi.h" +#include "midiseq.h" #include "minstrument.h" #include "xml.h" #include "gconfig.h" @@ -44,6 +44,9 @@ #include "icons.h" #include "track.h" #include "drummap.h" +#include "audio.h" +#include "muse_math.h" +#include "sysex_helper.h" namespace MusEGlobal { MusECore::MidiPort midiPorts[MIDI_PORTS]; @@ -51,9 +54,11 @@ namespace MusECore { - MidiControllerList defaultManagedMidiController; +LockFreeMPSCRingBuffer *MidiPort::_eventBuffers = + new LockFreeMPSCRingBuffer(16384); + //--------------------------------------------------------- // initMidiPorts //--------------------------------------------------------- @@ -77,12 +82,20 @@ // port->addDefaultControllers(); - ///port->setInstrument(genericMidiInstrument); - port->setInstrument(registerMidiInstrument("GM")); // Changed by Tim. + port->changeInstrument(registerMidiInstrument("GM")); port->syncInfo().setPort(i); - // p4.0.17 Set the first channel on the first port to auto-connect to midi track outputs. + // Set the first channel on the first port to auto-connect to midi track outputs. if(i == 0) - port->setDefaultOutChannels(1); + { + + // robert: removing the default init on several places to allow for the case + // where you rather want the midi track to default to the last created port + // this can only happen if there is _no_ default set + // + // port->setDefaultOutChannels(1); + + port->setDefaultInChannels(1); + } } } @@ -94,7 +107,7 @@ : _state("not configured") { _initializationsSent = false; - _defaultInChannels = (1 << MIDI_CHANNELS) -1; // p4.0.17 Default is now to connect to all channels. + _defaultInChannels = 0; _defaultOutChannels = 0; _device = 0; _instrument = 0; @@ -117,16 +130,46 @@ bool MidiPort::guiVisible() const { - return _instrument ? _instrument->guiVisible() : false; + if(!_device) + return false; + SynthI* synth = 0; + if(_device->isSynti()) + synth = static_cast(_device); + if(!synth) + return false; + return synth->guiVisible(); } //--------------------------------------------------------- +// showGui +//--------------------------------------------------------- + +void MidiPort::showGui(bool v) +{ + if(!_device) + return; + SynthI* synth = 0; + if(_device->isSynti()) + synth = static_cast(_device); + if(!synth) + return; + synth->showGui(v); +} + +//--------------------------------------------------------- // hasGui //--------------------------------------------------------- bool MidiPort::hasGui() const { - return _instrument ? _instrument->hasGui() : false; + if(!_device) + return false; + SynthI* synth = 0; + if(_device->isSynti()) + synth = static_cast(_device); + if(!synth) + return false; + return synth->hasGui(); } //--------------------------------------------------------- @@ -135,16 +178,46 @@ bool MidiPort::nativeGuiVisible() const { - return _instrument ? _instrument->nativeGuiVisible() : false; + if(!_device) + return false; + SynthI* synth = 0; + if(_device->isSynti()) + synth = static_cast(_device); + if(!synth) + return false; + return synth->nativeGuiVisible(); } //--------------------------------------------------------- +// showNativeGui +//--------------------------------------------------------- + +void MidiPort::showNativeGui(bool v) +{ + if(!_device) + return; + SynthI* synth = 0; + if(_device->isSynti()) + synth = static_cast(_device); + if(!synth) + return; + synth->showNativeGui(v); +} + +//--------------------------------------------------------- // hasNativeGui //--------------------------------------------------------- bool MidiPort::hasNativeGui() const { - return _instrument ? _instrument->hasNativeGui() : false; + if(!_device) + return false; + SynthI* synth = 0; + if(_device->isSynti()) + synth = static_cast(_device); + if(!synth) + return false; + return synth->hasNativeGui(); } //--------------------------------------------------------- @@ -159,13 +232,15 @@ _device->setPort(-1); _device->close(); _initializationsSent = false; + // Wait until upcoming process call has finished. Otherwise Jack may crash! + MusEGlobal::audio->msgAudioWait(); } if (dev) { for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; if (mp->device() == dev) { if(dev->isSynti()) - mp->setInstrument(genericMidiInstrument); + mp->changeInstrument(genericMidiInstrument); // move device _state = mp->state(); mp->clearDevice(); @@ -189,6 +264,7 @@ //--------------------------------------------------------- // sendPendingInitializations // Return true if success. +// To be called from realtime audio thread only. //--------------------------------------------------------- bool MidiPort::sendPendingInitializations(bool force) @@ -203,7 +279,8 @@ // test for explicit instrument initialization // - unsigned last_tick = 0; +// unsigned last_tick = 0; + unsigned last_frame = 0; MusECore::MidiInstrument* instr = instrument(); if(instr && MusEGlobal::config.midiSendInit && (force || !_initializationsSent)) { @@ -213,20 +290,19 @@ { for(iEvent ie = events->begin(); ie != events->end(); ++ie) { - unsigned tick = ie->second.tick(); - if(tick > last_tick) - last_tick = tick; - MusECore::MidiPlayEvent ev(tick, port, 0, ie->second); - _device->putEvent(ev); + if(ie->second.type() == Sysex) + last_frame += sysexDuration(ie->second.dataLen(), MusEGlobal::sampleRate); + MusECore::MidiPlayEvent ev = ie->second.asMidiPlayEvent(last_frame + MusEGlobal::audio->curSyncFrame(), port, 0); + _device->putEvent(ev, MidiDevice::NotLate); } // Give a bit of time for the last Init sysex to settle? - last_tick += 100; + last_frame += 100; } _initializationsSent = true; // Mark as having been sent. } // Send the Instrument controller default values. - sendInitialControllers(last_tick); + sendInitialControllers(last_frame); return rv; } @@ -277,6 +353,25 @@ } } } + else if((*imt)->type() == MusECore::Track::NEW_DRUM) + { + for(int i = 0; i < DRUM_MAPSIZE; ++i) + { + // Default to track port if -1 and track channel if -1. + int mport = (*imt)->drummap()[i].port; + if(mport == -1) + mport = (*imt)->outPort(); + int mchan = (*imt)->drummap()[i].channel; + if(mchan == -1) + mchan = (*imt)->outChannel(); + if(mport != port || usedChans[mchan]) + continue; + usedChans[mchan] = true; + ++usedChanCount; + if(usedChanCount >= MIDI_CHANNELS) + break; // All are used, done searching. + } + } else { if((*imt)->outPort() != port || usedChans[(*imt)->outChannel()]) @@ -319,9 +414,8 @@ { int ctl = mc->num(); // Note the addition of bias! - // Retry added. Use default attempts and delay. - _device->putEventWithRetry(MidiPlayEvent(start_time, port, chan, - ME_CONTROLLER, ctl, mc->initVal() + mc->bias())); + _device->putEvent(MidiPlayEvent(start_time, port, chan, + ME_CONTROLLER, ctl, mc->initVal() + mc->bias()), MidiDevice::NotLate); // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... // Set it again so that control labels show 'off'... setHwCtrlStates(chan, ctl, CTRL_VAL_UNKNOWN, mc->initVal() + mc->bias()); @@ -341,9 +435,8 @@ int val = i->second->hwVal(); if (val != CTRL_VAL_UNKNOWN) { - // Retry added. Use default attempts and delay. - _device->putEventWithRetry(MidiPlayEvent(start_time, port, channel, - ME_CONTROLLER, cntrl, val)); + _device->putEvent(MidiPlayEvent(start_time, port, channel, + ME_CONTROLLER, cntrl, val), MidiDevice::NotLate); // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... setHwCtrlState(channel, cntrl, val); } @@ -353,13 +446,17 @@ } //--------------------------------------------------------- -// setInstrument +// changeInstrument +// If audio is running (and not idle) this should only be called by the rt audio thread. //--------------------------------------------------------- -void MidiPort::setInstrument(MidiInstrument* i) +void MidiPort::changeInstrument(MidiInstrument* i) { + if(_instrument == i) + return; _instrument = i; _initializationsSent = false; + updateDrumMaps(); } //--------------------------------------------------------- @@ -387,80 +484,6 @@ } //--------------------------------------------------------- -// midiPortsPopup -//--------------------------------------------------------- - -QMenu* midiPortsPopup(QWidget* parent, int checkPort, bool includeDefaultEntry) - { - QMenu* p = new QMenu(parent); - QMenu* subp = 0; - QAction *act = 0; - QString name; - const int openConfigId = MIDI_PORTS; - const int defaultId = MIDI_PORTS + 1; - - // Warn if no devices available. Add an item to open midi config. - int pi = 0; - for( ; pi < MIDI_PORTS; ++pi) - { - MusECore::MidiDevice* md = MusEGlobal::midiPorts[pi].device(); - if(md && (md->rwFlags() & 1)) - break; - } - if(pi == MIDI_PORTS) - { - act = p->addAction(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Warning: No output devices!"))); - act->setCheckable(false); - act->setData(-1); - p->addSeparator(); - } - act = p->addAction(QIcon(*MusEGui::settings_midiport_softsynthsIcon), qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Open midi config..."))); - act->setCheckable(false); - act->setData(openConfigId); - p->addSeparator(); - - p->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Output port/device")), p)); - - if(includeDefaultEntry) - { - act = p->addAction(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "default"))); - act->setCheckable(false); - act->setData(defaultId); - } - - for (int i = 0; i < MIDI_PORTS; ++i) { - MidiPort* port = &MusEGlobal::midiPorts[i]; - MusECore::MidiDevice* md = port->device(); - if(md && !(md->rwFlags() & 1) && (i != checkPort)) // Only writeable ports, or current one. - continue; - name.sprintf("%d:%s", port->portno()+1, port->portname().toLatin1().constData()); - if(md || (i == checkPort)) - { - act = p->addAction(name); - act->setData(i); - act->setCheckable(true); - act->setChecked(i == checkPort); - } - - if(!md) - { - if(!subp) // No submenu yet? Create it now. - { - subp = new QMenu(p); - subp->setTitle(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Empty ports"))); - } - act = subp->addAction(QString().setNum(i+1)); - act->setData(i); - act->setCheckable(true); - act->setChecked(i == checkPort); - } - } - if(subp) - p->addMenu(subp); - return p; - } - -//--------------------------------------------------------- // portname //--------------------------------------------------------- @@ -475,6 +498,7 @@ //--------------------------------------------------------- // tryCtrlInitVal +// To be called from realtime audio thread only. //--------------------------------------------------------- void MidiPort::tryCtrlInitVal(int chan, int ctl, int val) @@ -487,7 +511,7 @@ if(v != CTRL_VAL_UNKNOWN) { if(_device) - _device->putEventWithRetry(MidiPlayEvent(0, portno(), chan, ME_CONTROLLER, ctl, v)); + _device->putEvent(MidiPlayEvent(0, portno(), chan, ME_CONTROLLER, ctl, v), MidiDevice::NotLate); // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... setHwCtrlState(chan, ctl, v); @@ -512,9 +536,7 @@ if(_device) { MidiPlayEvent ev(0, portno(), chan, ME_CONTROLLER, ctl, initval + mc->bias()); - _device->putEvent(ev); - // Retry added. Use default attempts and delay. p4.0.15 - //_device->putEventWithRetry(ev); + _device->putEvent(ev, MidiDevice::NotLate); } setHwCtrlStates(chan, ctl, CTRL_VAL_UNKNOWN, initval + mc->bias()); @@ -527,7 +549,7 @@ if(_device) { MidiPlayEvent ev(0, portno(), chan, ME_CONTROLLER, ctl, val); - _device->putEvent(ev); + _device->putEvent(ev, MidiDevice::NotLate); } setHwCtrlStates(chan, ctl, CTRL_VAL_UNKNOWN, val); } @@ -631,7 +653,7 @@ { if (_device) { MidiPlayEvent event(0, 0, ME_SYSEX, p, n); - _device->putEvent(event); + _device->putEvent(event, MidiDevice::NotLate); } } @@ -693,7 +715,7 @@ { if (_device) { MidiPlayEvent event(0, 0, 0, ME_START, 0, 0); - _device->putEvent(event); + _device->putEvent(event, MidiDevice::NotLate); } } @@ -705,7 +727,7 @@ { if (_device) { MidiPlayEvent event(0, 0, 0, ME_STOP, 0, 0); - _device->putEvent(event); + _device->putEvent(event, MidiDevice::NotLate); } } @@ -717,7 +739,7 @@ { if (_device) { MidiPlayEvent event(0, 0, 0, ME_CLOCK, 0, 0); - _device->putEvent(event); + _device->putEvent(event, MidiDevice::NotLate); } } @@ -729,7 +751,7 @@ { if (_device) { MidiPlayEvent event(0, 0, 0, ME_CONTINUE, 0, 0); - _device->putEvent(event); + _device->putEvent(event, MidiDevice::NotLate); } } @@ -741,7 +763,7 @@ { if (_device) { MidiPlayEvent event(0, 0, 0, ME_SONGPOS, pos, 0); - _device->putEvent(event); + _device->putEvent(event, MidiDevice::NotLate); } } @@ -829,84 +851,634 @@ return val; } - + +double MidiPort::limitValToInstrCtlRange(MidiController* mc, double val) +{ + if(!_instrument || !mc || int(val) == CTRL_VAL_UNKNOWN) + return val; + + const double mn = double(mc->minVal()); + const double mx = double(mc->maxVal()); + const double bias = double(mc->bias()); + + // Subtract controller bias from value. + val -= double(bias); + + // Limit value to controller range. + if(val < mn) + val = mn; + else + if(val > mx) + val = mx; + + // Re-add controller bias to value. + val += bias; + + return val; +} + +double MidiPort::limitValToInstrCtlRange(int ctl, double val) +{ + if(!_instrument || int(val) == CTRL_VAL_UNKNOWN) + return val; + + MidiControllerList* cl = _instrument->controller(); + + // FIXME: This might be optimized by calling midiController instead, + // and simply asking if it's a drum controller. Saves one list iteration. + // Is it a drum controller? + MidiController *mc = drumController(ctl); + if(!mc) + { + // It's not a drum controller. Find it as a regular controller instead. + iMidiController imc = cl->find(ctl); + if(imc != cl->end()) + mc = imc->second; + } + + // If it's a valid controller, limit the value to the instrument controller range. + if(mc) + return limitValToInstrCtlRange(mc, val); + + return val; +} + //--------------------------------------------------------- -// sendHwCtrlState -// Return true if it is OK to go ahead and deliver the event. +// createController +// Creates a controller in this port's controller list. +// Returns true if the controller was created. +// To be called by gui thread only. //--------------------------------------------------------- -bool MidiPort::sendHwCtrlState(const MidiPlayEvent& ev, bool forceSend) +bool MidiPort::createController(int chan, int ctrl) +{ + if(ctrl < 0 || chan < 0 || chan >= MIDI_CHANNELS) + return false; + + PendingOperationList operations; + + // Make sure the controller exists, create it if not. + iMidiCtrlValList cl = _controller->find(chan, ctrl); + if(cl != _controller->end()) + return false; + + PendingOperationItem poi(_controller, 0, chan, ctrl, PendingOperationItem::AddMidiCtrlValList); + + // This step is intended in case we ever pass an operations list to this function. + if(operations.findAllocationOp(poi) != operations.end()) + return false; + + MidiCtrlValList* mcvl = new MidiCtrlValList(ctrl); + poi._mcvl = mcvl; + operations.add(poi); + // This waits for audio process thread to execute it. + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + + return true; +} + +//--------------------------------------------------------- +// putHwCtrlEvent +// To be called from gui thread only. +// Returns true if event cannot be delivered. +//--------------------------------------------------------- + +bool MidiPort::putHwCtrlEvent(const MidiPlayEvent& ev) +{ + const int ctrl = ev.translateCtrlNum(); + + // Event not translatable to a controller? + if(ctrl < 0) + return true; + + // Make sure to create the controller if necessary. + //createController(chan, ctrl); + + // Make sure the controller exists, create it if not. + const int chan = ev.channel(); + ciMidiCtrlValList cl = _controller->find(chan, ctrl); + // Controller does not exist? + if(cl == _controller->end()) + { + // Tell the gui thread to create and add a new controller. + // It will store and re-deliver the events directly to the buffers + // after the controller is created. + MusEGlobal::song->putIpcInEvent(ev); + // Technically the event is being delivered. + return false; + } + + if(!eventBuffers()->put(ev)) + { + fprintf(stderr, "MidiPort::putHwCtrlEvent: Error: gui2AudioFifo fifo overflow\n"); + return true; + } + + return false; +} + +//--------------------------------------------------------- +// putEvent +// To be called from gui thread only. +// Returns true if event cannot be delivered. +//--------------------------------------------------------- + +bool MidiPort::putEvent(const MidiPlayEvent& ev) +{ + // Send the event to the device first so that current parameters could be updated on process. + bool res = false; + if(_device) + { + res = !_device->putEvent(ev, MidiDevice::Late); + } + putHwCtrlEvent(ev); + return res; +} + +//--------------------------------------------------------- +// putControllerValue +// To be called from gui thread only. +// Returns true if event cannot be delivered. +//--------------------------------------------------------- + +bool MidiPort::putControllerValue(int port, int chan, int ctlnum, double val, bool isDb) +{ + iMidiCtrlValList imcvl = _controller->find(chan, ctlnum); + if(imcvl == _controller->end()) + return true; + + // Don't create if not found. + MusECore::MidiController* mc = midiController(ctlnum, false); + if(!mc) + return true; + const int max = mc->maxVal(); + + if(isDb) + val = double(max) * muse_db2val(val / 2.0); + + const int i_new_val = MidiController::dValToInt(val); + + // Time-stamp the event. + MidiPlayEvent ev(MusEGlobal::audio->curFrame(), port, chan, MusECore::ME_CONTROLLER, ctlnum, i_new_val); + bool res = false; + if(_device) + { + res = !_device->putEvent(ev, MidiDevice::Late); + } + + putHwCtrlEvent(ev); + return res; +} + +//--------------------------------------------------------- +// handleGui2AudioEvent +// To be called from audio thread only. +// Returns true on success. +// If createAsNeeded is true, automatically send a message to the gui thread to +// create items such as controllers, and cache the events sent to it and re-put +// them after the controller has been created. +//--------------------------------------------------------- + +bool MidiPort::handleGui2AudioEvent(const MidiPlayEvent& ev, bool createAsNeeded) +{ + const int chn = ev.channel(); + const int type = ev.type(); + const int i_dataA = ev.dataA(); + const double d_dataB = ev.dataB(); + const int i_dataB = MidiController::dValToInt(d_dataB); + + int fin_db = i_dataB; + switch(type) + { + case ME_CONTROLLER: + switch(i_dataA) { - if (ev.type() == ME_CONTROLLER) { + case CTRL_HBANK: + { + // Does the CTRL_PROGRAM controller exist? + iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PROGRAM); + if(imcvl == _controller->end()) + { + // Tell the gui to create the controller and add the value. + if(createAsNeeded) + return MusEGlobal::song->putIpcInEvent(ev); + return false; + } + + int hb = 0xff; + if(!MidiController::iValIsUnknown(i_dataB)) + hb = i_dataB & 0xff; + if(hb != 0xff) + hb = limitValToInstrCtlRange(i_dataA, hb); + int lb = 0xff; + int pr = 0xff; + + MidiCtrlValList* mcvl = imcvl->second; + if(!mcvl->hwValIsUnknown()) + { + const int hw_val = mcvl->hwVal(); + lb = (hw_val >> 8) & 0xff; + pr = hw_val & 0xff; + } + + if((hb != 0xff || lb != 0xff) && pr == 0xff) + pr = 0x01; + if(hb == 0xff && lb == 0xff && pr == 0xff) + fin_db = CTRL_VAL_UNKNOWN; + else + fin_db = (hb << 16) | (lb << 8) | pr; + + // Set the value. Be sure to update drum maps (and inform the gui). + if(mcvl->setHwVal(fin_db)) + updateDrumMaps(chn, fin_db); + + return true; + } + break; + + case CTRL_LBANK: + { + // Does the CTRL_PROGRAM controller exist? + iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PROGRAM); + if(imcvl == _controller->end()) + { + // Tell the gui to create the controller and add the value. + if(createAsNeeded) + return MusEGlobal::song->putIpcInEvent(ev); + return false; + } + + int hb = 0xff; + int lb = 0xff; + if(!MidiController::iValIsUnknown(i_dataB)) + lb = i_dataB & 0xff; + if(lb != 0xff) + lb = limitValToInstrCtlRange(i_dataA, lb); + int pr = 0xff; + + MidiCtrlValList* mcvl = imcvl->second; + if(!mcvl->hwValIsUnknown()) + { + const int hw_val = mcvl->hwVal(); + hb = (hw_val >> 16) & 0xff; + pr = hw_val & 0xff; + } + + if((hb != 0xff || lb != 0xff) && pr == 0xff) + pr = 0x01; + if(hb == 0xff && lb == 0xff && pr == 0xff) + fin_db = CTRL_VAL_UNKNOWN; + else + fin_db = (hb << 16) | (lb << 8) | pr; + + // Set the value. Be sure to update drum maps (and inform the gui). + if(mcvl->setHwVal(fin_db)) + updateDrumMaps(chn, fin_db); + + return true; + } + break; + + case CTRL_PROGRAM: + { + // TODO: Maybe update CTRL_HBANK/CTRL_LBANK - but ONLY if they are specifically + // defined by the user in the controller list. + + // Does the CTRL_PROGRAM controller exist? + iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PROGRAM); + if(imcvl == _controller->end()) + { + // Tell the gui to create the controller and add the value. + if(createAsNeeded) + return MusEGlobal::song->putIpcInEvent(ev); + return false; + } + + // Set the value. Be sure to update drum maps (and inform the gui). + if(imcvl->second->setHwVal(fin_db)) + updateDrumMaps(chn, fin_db); + + return true; + } + break; + + default: + { + // Does the controller exist? + iMidiCtrlValList imcvl = _controller->find(chn, i_dataA); + if(imcvl == _controller->end()) + { + // Tell the gui to create the controller and add the value. + if(createAsNeeded) + return MusEGlobal::song->putIpcInEvent(ev); + return false; + } + + fin_db = limitValToInstrCtlRange(i_dataA, i_dataB); + // Set the value. + imcvl->second->setHwVal(fin_db); + + return true; + } + break; + } + break; + + case ME_POLYAFTER: + { + const int pitch = i_dataA & 0x7f; + const int fin_da = (CTRL_POLYAFTER & ~0xff) | pitch; - int da = ev.dataA(); - int db = ev.dataB(); - db = limitValToInstrCtlRange(da, db); - - if(!setHwCtrlState(ev.channel(), da, db)) { - if (MusEGlobal::debugMsg && forceSend) - printf("sendHwCtrlState: State already set. Forcing anyway...\n"); - if (!forceSend) - return false; - } - } - else - if (ev.type() == ME_POLYAFTER) // REMOVE Tim. Or keep? ... - { - int pitch = ev.dataA() & 0x7f; - int ctl = (CTRL_POLYAFTER & ~0xff) | pitch; - int db = limitValToInstrCtlRange(ctl, ev.dataB()); - if(!setHwCtrlState(ev.channel(), ctl, db)) { - if (!forceSend) - return false; - } + // Does the controller exist? + iMidiCtrlValList imcvl = _controller->find(chn, fin_da); + if(imcvl == _controller->end()) + { + // Tell the gui to create the controller and add the value. + if(createAsNeeded) + return MusEGlobal::song->putIpcInEvent(ev); + return false; } - else - if (ev.type() == ME_AFTERTOUCH) // ... Same - { - int da = limitValToInstrCtlRange(CTRL_AFTERTOUCH, ev.dataA()); - if(!setHwCtrlState(ev.channel(), CTRL_AFTERTOUCH, da)) { - if (!forceSend) - return false; - } + + fin_db = limitValToInstrCtlRange(fin_da, i_dataB); + // Set the value. + imcvl->second->setHwVal(fin_db); + + return true; + } + break; + + case ME_AFTERTOUCH: + { + // Does the controller exist? + iMidiCtrlValList imcvl = _controller->find(chn, CTRL_AFTERTOUCH); + if(imcvl == _controller->end()) + { + // Tell the gui to create the controller and add the value. + if(createAsNeeded) + return MusEGlobal::song->putIpcInEvent(ev); + return false; } - else - if (ev.type() == ME_PITCHBEND) - { - int da = limitValToInstrCtlRange(CTRL_PITCH, ev.dataA()); - if(!setHwCtrlState(ev.channel(), CTRL_PITCH, da)) { - if (!forceSend) - return false; - } + + fin_db = limitValToInstrCtlRange(CTRL_AFTERTOUCH, i_dataA); + // Set the value. + imcvl->second->setHwVal(fin_db); + + return true; + } + break; + + case ME_PITCHBEND: + { + // Does the controller exist? + iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PITCH); + if(imcvl == _controller->end()) + { + // Tell the gui to create the controller and add the value. + if(createAsNeeded) + return MusEGlobal::song->putIpcInEvent(ev); + return false; } - else - if (ev.type() == ME_PROGRAM) + + fin_db = limitValToInstrCtlRange(CTRL_PITCH, i_dataA); + // Set the value. + imcvl->second->setHwVal(fin_db); + + return true; + } + break; + + case ME_PROGRAM: + { + // Does the controller exist? + iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PROGRAM); + if(imcvl == _controller->end()) + { + // Tell the gui to create the controller and add the value. + if(createAsNeeded) + return MusEGlobal::song->putIpcInEvent(ev); + return false; + } + + int hb = 0xff; + int lb = 0xff; + int pr = 0xff; + if(!MidiController::iValIsUnknown(i_dataA)) + pr = i_dataA & 0xff; + //if(pr != 0xff) + // pr = limitValToInstrCtlRange(da, pr); + + MidiCtrlValList* mcvl = imcvl->second; + if(!mcvl->hwValIsUnknown()) { - if(!setHwCtrlState(ev.channel(), CTRL_PROGRAM, ev.dataA())) { - if (!forceSend) - return false; - } + const int hw_val = mcvl->hwVal(); + hb = (hw_val >> 16) & 0xff; + lb = (hw_val >> 8) & 0xff; } + if((hb != 0xff || lb != 0xff) && pr == 0xff) + pr = 0x01; + if(hb == 0xff && lb == 0xff && pr == 0xff) + fin_db = CTRL_VAL_UNKNOWN; + else + fin_db = (hb << 16) | (lb << 8) | pr; + + // Set the value. Be sure to update drum maps (and inform the gui). + if(mcvl->setHwVal(fin_db)) + updateDrumMaps(chn, fin_db); + return true; - } + } + break; + + default: + break; + } + + return false; +} //--------------------------------------------------------- -// sendEvent -// return true, if event cannot be delivered +// processGui2AudioEvents +// To be called from audio thread only. +// Return true if error. //--------------------------------------------------------- -bool MidiPort::sendEvent(const MidiPlayEvent& ev, bool forceSend) +// Static. +bool MidiPort::processGui2AudioEvents() +{ + // Receive hardware state events sent from various threads to this audio thread. + // Update hardware state so gui controls are updated. + // False = don't use the size snapshot, but update it. + const int sz = eventBuffers()->getSize(false); + MidiPlayEvent ev; + for(int i = 0; i < sz; ++i) + { + if(!eventBuffers()->get(ev)) + continue; + const int port = ev.port(); + if(port < 0 || port >= MIDI_PORTS) + continue; + // Handle the event. Tell the gui NOT to create controllers as needed, + // that should be done before it ever gets here. + MusEGlobal::midiPorts[port].handleGui2AudioEvent(ev, false); + } + return false; +} + +//--------------------------------------------------------- +// sendHwCtrlState +// Return true if it is OK to go ahead and deliver the event. +//--------------------------------------------------------- + +bool MidiPort::sendHwCtrlState(const MidiPlayEvent& ev, bool forceSend) { - if(!sendHwCtrlState(ev, forceSend)) - return false; + const int type = ev.type(); + const int chn = ev.channel(); + const int da = ev.dataA(); + int fin_da = da; + const int db = ev.dataB(); + + switch(type) + { + case ME_CONTROLLER: + switch(da) + { + case CTRL_HBANK: + { + int hb = 0xff; + if(!MidiController::iValIsUnknown(db)) + hb = db & 0xff; + if(hb != 0xff) + hb = limitValToInstrCtlRange(da, hb); + int lb = 0xff; + int pr = 0xff; + + fin_da = CTRL_PROGRAM; + // This will create a new value list if necessary, otherwise it returns the existing list. + // FIXME: This is not realtime safe because it may allocate. + MidiCtrlValList* mcvl = addManagedController(chn, fin_da); + if(!mcvl->hwValIsUnknown()) + { + const int hw_val = mcvl->hwVal(); + lb = (hw_val >> 8) & 0xff; + pr = hw_val & 0xff; + } + + if((hb != 0xff || lb != 0xff) && pr == 0xff) + pr = 0x01; + } + break; - if (!_device) { - if (MusEGlobal::debugMsg) - printf("no device for this midi port\n"); - return true; + case CTRL_LBANK: + { + int hb = 0xff; + int lb = 0xff; + if(!MidiController::iValIsUnknown(db)) + lb = db & 0xff; + if(lb != 0xff) + lb = limitValToInstrCtlRange(da, lb); + int pr = 0xff; + + fin_da = CTRL_PROGRAM; + // This will create a new value list if necessary, otherwise it returns the existing list. + // FIXME: This is not realtime safe because it may allocate. + MidiCtrlValList* mcvl = addManagedController(chn, fin_da); + if(!mcvl->hwValIsUnknown()) + { + const int hw_val = mcvl->hwVal(); + hb = (hw_val >> 16) & 0xff; + pr = hw_val & 0xff; + } + + if((hb != 0xff || lb != 0xff) && pr == 0xff) + pr = 0x01; + } + break; + + case CTRL_PROGRAM: + // This will create a new value list if necessary, otherwise it returns the existing list. + // FIXME: This is not realtime safe because it may allocate. + addManagedController(chn, da); + // TODO: Maybe update CTRL_HBANK/CTRL_LBANK - but ONLY if they are specifically + // defined by the user in the controller list. + break; + + default: + // This will create a new value list if necessary, otherwise it returns the existing list. + // FIXME: This is not realtime safe because it may allocate. + addManagedController(chn, da); + break; } - return _device->putEvent(ev); + break; + + case ME_POLYAFTER: + { + const int pitch = da & 0x7f; + fin_da = (CTRL_POLYAFTER & ~0xff) | pitch; + // This will create a new value list if necessary, otherwise it returns the existing list. + // FIXME: This is not realtime safe because it may allocate. + addManagedController(chn, fin_da); + } + break; + + case ME_AFTERTOUCH: + { + fin_da = CTRL_AFTERTOUCH; + // This will create a new value list if necessary, otherwise it returns the existing list. + // FIXME: This is not realtime safe because it may allocate. + addManagedController(chn, fin_da); + } + break; + + case ME_PITCHBEND: + { + fin_da = CTRL_PITCH; + // This will create a new value list if necessary, otherwise it returns the existing list. + // FIXME: This is not realtime safe because it may allocate. + addManagedController(chn, fin_da); + } + break; + + case ME_PROGRAM: + { + int hb = 0xff; + int lb = 0xff; + int pr = 0xff; + if(!MidiController::iValIsUnknown(da)) + pr = da & 0xff; + //if(pr != 0xff) + // pr = limitValToInstrCtlRange(da, pr); + + fin_da = CTRL_PROGRAM; + // This will create a new value list if necessary, otherwise it returns the existing list. + // FIXME: This is not realtime safe because it may allocate. + MidiCtrlValList* mcvl = addManagedController(chn, fin_da); + if(!mcvl->hwValIsUnknown()) + { + const int hw_val = mcvl->hwVal(); + hb = (hw_val >> 16) & 0xff; + lb = (hw_val >> 8) & 0xff; + } + + if((hb != 0xff || lb != 0xff) && pr == 0xff) + pr = 0x01; + } + break; + + default: + // For all other event types, just return true. + return true; + break; + } + + if(!setHwCtrlState(chn, da, db)) { + if (MusEGlobal::debugMsg && forceSend) + printf("sendHwCtrlState: State already set. Forcing anyway...\n"); + if (!forceSend) + return false; + } + + return true; } //--------------------------------------------------------- @@ -916,7 +1488,7 @@ int MidiPort::lastValidHWCtrlState(int ch, int ctrl) const { ch &= 0xff; - iMidiCtrlValList cl = _controller->find(ch, ctrl); + ciMidiCtrlValList cl = ((const MidiCtrlValListList*)_controller)->find(ch, ctrl); if (cl == _controller->end()) { return CTRL_VAL_UNKNOWN; } @@ -925,13 +1497,28 @@ } //--------------------------------------------------------- +// lastValidHWCtrlState +//--------------------------------------------------------- + +double MidiPort::lastValidHWDCtrlState(int ch, int ctrl) const +{ + ch &= 0xff; + ciMidiCtrlValList cl = ((const MidiCtrlValListList*)_controller)->find(ch, ctrl); + if (cl == _controller->end()) { + return CTRL_VAL_UNKNOWN; + } + MidiCtrlValList* vl = cl->second; + return vl->lastValidHWDVal(); +} + +//--------------------------------------------------------- // hwCtrlState //--------------------------------------------------------- int MidiPort::hwCtrlState(int ch, int ctrl) const { ch &= 0xff; - iMidiCtrlValList cl = _controller->find(ch, ctrl); + ciMidiCtrlValList cl = ((const MidiCtrlValListList*)_controller)->find(ch, ctrl); if (cl == _controller->end()) return CTRL_VAL_UNKNOWN; MidiCtrlValList* vl = cl->second; @@ -939,20 +1526,64 @@ } //--------------------------------------------------------- +// hwDCtrlState +//--------------------------------------------------------- + +double MidiPort::hwDCtrlState(int ch, int ctrl) const + { + ch &= 0xff; + ciMidiCtrlValList cl = ((const MidiCtrlValListList*)_controller)->find(ch, ctrl); + if (cl == _controller->end()) + return CTRL_VAL_UNKNOWN; + MidiCtrlValList* vl = cl->second; + return vl->hwDVal(); + } + +//--------------------------------------------------------- // setHwCtrlState +// If audio is running (and not idle) this should only be called by the rt audio thread. // Returns false if value is already equal, true if value is set. //--------------------------------------------------------- +bool MidiPort::setHwCtrlState(const MidiPlayEvent& ev) +{ + const int port = ev.port(); + if(port < 0 || port >= MIDI_PORTS) + return false; + + // Handle the event. Tell the gui to create controllers as needed. + return MusEGlobal::midiPorts[port].handleGui2AudioEvent(ev, true); +} + bool MidiPort::setHwCtrlState(int ch, int ctrl, int val) { - // By T356. This will create a new value list if necessary, otherwise it returns the existing list. + // This will create a new value list if necessary, otherwise it returns the existing list. MidiCtrlValList* vl = addManagedController(ch, ctrl); - return vl->setHwVal(val); + bool res = vl->setHwVal(val); + // If program controller be sure to update drum maps (and inform the gui). + if(res && ctrl == CTRL_PROGRAM) + updateDrumMaps(ch, val); + + return res; + } + +bool MidiPort::setHwCtrlState(int ch, int ctrl, double val) + { + // This will create a new value list if necessary, otherwise it returns the existing list. + MidiCtrlValList* vl = addManagedController(ch, ctrl); + + bool res = vl->setHwVal(val); + // If program controller be sure to update drum maps (and inform the gui). + if(res && ctrl == CTRL_PROGRAM) + updateDrumMaps(ch, val); + + return res; } //--------------------------------------------------------- // setHwCtrlStates +// If audio is running (and not idle) this should only be called by the rt audio thread. // Sets current and last HW values. // Handy for forcing labels to show 'off' and knobs to show specific values // without having to send two messages. @@ -964,9 +1595,28 @@ // This will create a new value list if necessary, otherwise it returns the existing list. MidiCtrlValList* vl = addManagedController(ch, ctrl); - return vl->setHwVals(val, lastval); + // This is not perfectly ideal, drum maps should not have to (check) change if last hw val changed. + bool res = vl->setHwVals(val, lastval); + // If program controller be sure to update drum maps (and inform the gui). + if(res && ctrl == CTRL_PROGRAM) + updateDrumMaps(ch, val); + + return res; } +bool MidiPort::setHwCtrlStates(int ch, int ctrl, double val, double lastval) + { + // This will create a new value list if necessary, otherwise it returns the existing list. + MidiCtrlValList* vl = addManagedController(ch, ctrl); + + // This is not perfectly ideal, drum maps should not have to (check) change if last hw val changed. + bool res = vl->setHwVals(val, lastval); + // If program controller be sure to update drum maps (and inform the gui). + if(res && ctrl == CTRL_PROGRAM) + updateDrumMaps(ch, val); + + return res; + } //--------------------------------------------------------- // setControllerVal @@ -991,6 +1641,98 @@ } //--------------------------------------------------------- +// updateDrumMaps +// If audio is running (and not idle) this should only be called by the rt audio thread. +// Returns true if maps were changed. +//--------------------------------------------------------- + +bool MidiPort::updateDrumMaps(int chan, int patch) +{ + int port; + int tpatch; + int tchan; + bool map_changed = false; + MidiTrack* mt; + for(iMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) + { + mt = *t; + if(mt->type() != Track::NEW_DRUM) + continue; + port = mt->outPort(); + if(port < 0 || port >= MIDI_PORTS || &MusEGlobal::midiPorts[port] != this) + continue; + tchan = mt->outChannel(); + if(tchan != chan) + continue; + tpatch = hwCtrlState(tchan, CTRL_PROGRAM); + if(tpatch != patch) + continue; + if(mt->updateDrummap(false)) // false = don't signal gui thread, we'll do that here. + map_changed = true; + } + + if(map_changed) + { + // It is possible we are being called from gui thread already, in audio idle mode. + // Will this still work, and not conflict with audio sending the same message? + // Are we are not supposed to write to an fd from different threads? + if(!MusEGlobal::audio || MusEGlobal::audio->isIdle()) + // Directly emit SC_DRUMMAP song changed signal. + MusEGlobal::song->update(SC_DRUMMAP); + else + // Tell the gui to emit SC_DRUMMAP song changed signal. + MusEGlobal::audio->sendMsgToGui('D'); // Drum map changed. + + return true; + } + + return false; +} + +//--------------------------------------------------------- +// updateDrumMaps +// If audio is running (and not idle) this should only be called by the rt audio thread. +// Returns true if maps were changed. +//--------------------------------------------------------- + +bool MidiPort::updateDrumMaps() +{ + int port; + bool map_changed; + MidiTrack* mt; + for(iMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) + { + mt = *t; + if(mt->type() != Track::NEW_DRUM) + continue; + port = mt->outPort(); + if(port < 0 || port >= MIDI_PORTS || &MusEGlobal::midiPorts[port] != this) + continue; + if(mt->updateDrummap(false)) // false = don't signal gui thread, we'll do that here. + map_changed = true; + } + + if(map_changed) + { + // It is possible we are being called from gui thread already, in audio idle mode. + // Will this still work, and not conflict with audio sending the same message? + // Are we are not supposed to write to an fd from different threads? + //if(MusEGlobal::audio && MusEGlobal::audio->isIdle() && MusEGlobal::midiSeq && MusEGlobal::midiSeq->isIdle()) + if(!MusEGlobal::audio || MusEGlobal::audio->isIdle()) + // Directly emit SC_DRUMMAP song changed signal. + //MusEGlobal::song->update(SC_DRUMMAP, true); + MusEGlobal::song->update(SC_DRUMMAP); + else + // Tell the gui to emit SC_DRUMMAP song changed signal. + MusEGlobal::audio->sendMsgToGui('D'); // Drum map changed. + + return true; + } + + return false; +} + +//--------------------------------------------------------- // getCtrl //--------------------------------------------------------- @@ -1011,6 +1753,25 @@ return cl->second->value(tick, part); } + +int MidiPort::getVisibleCtrl(int ch, int tick, int ctrl, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const + { + iMidiCtrlValList cl = _controller->find(ch, ctrl); + if (cl == _controller->end()) + return CTRL_VAL_UNKNOWN; + + return cl->second->visibleValue(tick, inclMutedParts, inclMutedTracks, inclOffTracks); + } + +int MidiPort::getVisibleCtrl(int ch, int tick, int ctrl, Part* part, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const + { + iMidiCtrlValList cl = _controller->find(ch, ctrl); + if (cl == _controller->end()) + return CTRL_VAL_UNKNOWN; + + return cl->second->visibleValue(tick, part, inclMutedParts, inclMutedTracks, inclOffTracks); + } + //--------------------------------------------------------- // deleteController //--------------------------------------------------------- @@ -1032,7 +1793,7 @@ // midiController //--------------------------------------------------------- -MidiController* MidiPort::midiController(int num) const +MidiController* MidiPort::midiController(int num, bool createIfNotFound) const { if (_instrument) { MidiControllerList* mcl = _instrument->controller(); @@ -1055,7 +1816,9 @@ return i->second; } - + if(!createIfNotFound) + return NULL; + QString name = midiCtrlName(num); int min = 0; int max = 127; @@ -1084,7 +1847,7 @@ case MidiController::Velo: // cannot happen break; } - MidiController* c = new MidiController(name, num, min, max, 0); + MidiController* c = new MidiController(name, num, min, max, 0, 0); defaultMidiController.add(c); return c; } @@ -1119,17 +1882,6 @@ return 0; } -int MidiPort::nullSendValue() -{ - return _instrument ? _instrument->nullSendValue() : -1; -} - -void MidiPort::setNullSendValue(int v) -{ - if(_instrument) - _instrument->setNullSendValue(v); -} - //--------------------------------------------------------- // writeRouting //--------------------------------------------------------- @@ -1153,8 +1905,8 @@ continue; s = QT_TRANSLATE_NOOP("@default", "Route"); - if(r->channel != -1 && r->channel != 0) - s += QString(QT_TRANSLATE_NOOP("@default", " channelMask=\"%1\"")).arg(r->channel); // Use new channel mask. + if(r->channel != -1) + s += QString(QT_TRANSLATE_NOOP("@default", " channel=\"%1\"")).arg(r->channel); xml.tag(level++, s.toLatin1().constData()); xml.tag(level, "source mport=\"%d\"/", portno()); diff -Nru muse-2.1.2/muse/midiport.h muse-3.0.2+ds1/muse/midiport.h --- muse-2.1.2/muse/midiport.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiport.h 2017-12-17 21:07:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: midiport.h,v 1.9.2.6 2009/11/17 22:08:22 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012, 2017 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 @@ -28,27 +28,44 @@ #include "globaldefs.h" #include "sync.h" #include "route.h" +#include "mpevent.h" +#include "lock_free_buffer.h" +#include "mididev.h" -class QMenu; -class QWidget; +class QString; namespace MusECore { class MidiDevice; class Part; -//class MidiSyncInfo; class MidiController; class MidiControllerList; class MidiCtrlValListList; class MidiCtrlValList; class MidiInstrument; -class MidiPlayEvent; //--------------------------------------------------------- // MidiPort //--------------------------------------------------------- class MidiPort { + public: + // IDs for the various IPC FIFOs that are used. + enum EventFifoIds + { + // Playback queued events put by the audio process thread. + PlayFifo=0, + // Gui events put by our gui thread. + GuiFifo=1, + // OSC events put by the OSC thread. + OSCFifo=2, + // Monitor input passthrough events put by Jack devices (audio process thread). + JackFifo=3, + // Monitor input passthrough events put by ALSA devices (midi seq thread). + ALSAFifo=4 + }; + + private: MidiCtrlValListList* _controller; MidiDevice* _device; QString _state; // result of device open @@ -56,7 +73,7 @@ AutomationType _automationType[MIDI_CHANNELS]; // Holds sync settings and detection monitors. MidiSyncInfo _syncInfo; - // p3.3.50 Just a flag to say the port was found in the song file, even if it has no device right now. + // Just a flag to say the port was found in the song file, even if it has no device right now. bool _foundInSongFile; // When creating a new midi track, add these global default channel routes to/from this port. Ignored if 0. int _defaultInChannels; // These are bit-wise channel masks. @@ -64,11 +81,24 @@ // Whether Init sysexes and default controller values have been sent. To be reset whenever // something about the port changes like device, Jack routes, or instrument. bool _initializationsSent; - + + static LockFreeMPSCRingBuffer *_eventBuffers; + RouteList _inRoutes, _outRoutes; void clearDevice(); + // Creates a controller in this port's controller list. + // Returns true if the controller was created. + // To be called by gui thread only. + bool createController(int chan, int ctrl); + + // To be called from audio thread only. Returns true on success. + // If createAsNeeded is true, automatically send a message to the gui thread to + // create items such as controllers, and cache the events sent to it and re-put + // them after the controller has been created. + bool handleGui2AudioEvent(const MidiPlayEvent&, bool createAsNeeded); + public: MidiPort(); ~MidiPort(); @@ -77,20 +107,35 @@ // manipulate active midi controller // MidiCtrlValListList* controller() { return _controller; } + // Determine controller value at tick on channel, using values stored by ANY part. int getCtrl(int ch, int tick, int ctrl) const; + // Determine controller value at tick on channel, using values stored by the SPECIFIC part. int getCtrl(int ch, int tick, int ctrl, Part* part) const; + // Determine controller value at tick on channel, using values stored by ANY part, + // ignoring values that are OUTSIDE of their parts, or muted or off parts or tracks. + int getVisibleCtrl(int ch, int tick, int ctrl, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const; + // Determine controller value at tick on channel, using values stored by the SPECIFIC part, + // ignoring values that are OUTSIDE of the part, or muted or off part or track. + int getVisibleCtrl(int ch, int tick, int ctrl, Part* part, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const; bool setControllerVal(int ch, int tick, int ctrl, int val, Part* part); // Can be CTRL_VAL_UNKNOWN until a valid state is set int lastValidHWCtrlState(int ch, int ctrl) const; + double lastValidHWDCtrlState(int ch, int ctrl) const; int hwCtrlState(int ch, int ctrl) const; + double hwDCtrlState(int ch, int ctrl) const; bool setHwCtrlState(int ch, int ctrl, int val); + bool setHwCtrlState(int ch, int ctrl, double val); bool setHwCtrlStates(int ch, int ctrl, int val, int lastval); + bool setHwCtrlStates(int ch, int ctrl, double val, double lastval); + bool setHwCtrlState(const MidiPlayEvent&); void deleteController(int ch, int tick, int ctrl, Part* part); void addDefaultControllers(); bool guiVisible() const; + void showGui(bool); bool hasGui() const; bool nativeGuiVisible() const; + void showNativeGui(bool); bool hasNativeGui() const; int portno() const; @@ -102,16 +147,26 @@ void setState(const QString& s) { _state = s; } void setMidiDevice(MidiDevice* dev); const QString& portname() const; - MidiInstrument* instrument() const { return _instrument; } - void setInstrument(MidiInstrument* i); - MidiController* midiController(int num) const; + MidiInstrument* instrument() const { return _instrument; } + void setInstrument(MidiInstrument* i) { _instrument = i; } + void changeInstrument(MidiInstrument* i); + MidiController* midiController(int num, bool createIfNotFound = true) const; MidiCtrlValList* addManagedController(int channel, int ctrl); + // To be called from realtime audio thread only. void tryCtrlInitVal(int chan, int ctl, int val); int limitValToInstrCtlRange(int ctl, int val); + double limitValToInstrCtlRange(int ctl, double val); int limitValToInstrCtlRange(MidiController* mc, int val); + double limitValToInstrCtlRange(MidiController* mc, double val); MidiController* drumController(int ctl); - int nullSendValue(); - void setNullSendValue(int v); + // Update drum maps when patch is known. + // If audio is running (and not idle) this should only be called by the rt audio thread. + // Returns true if maps were changed. + bool updateDrumMaps(int chan, int patch); + // Update drum maps when patch is not known. + // If audio is running (and not idle) this should only be called by the rt audio thread. + // Returns true if maps were changed. + bool updateDrumMaps(); int defaultInChannels() const { return _defaultInChannels; } int defaultOutChannels() const { return _defaultOutChannels; } @@ -142,15 +197,37 @@ void sendMMCStop(int devid = -1); void sendMMCDeferredPlay(int devid = -1); - // Send Instrument Init sequences and controller defaults etc. + // Send Instrument Init sequences and controller defaults etc. Return true if success. + // To be called from realtime audio thread only. bool sendPendingInitializations(bool force = true); // Per port // Send initial controller values. Called by above method, and elsewhere. bool sendInitialControllers(unsigned start_time = 0); bool initSent() const { return _initializationsSent; } void clearInitSent() { _initializationsSent = false; } + // Put an event into the gui2audio fifo for playback. Calls stageEvent(). + // Called from gui thread only. Returns true if event cannot be delivered. + bool putHwCtrlEvent(const MidiPlayEvent&); + // Put an event into both the device and the gui2audio fifos for playback. Calls stageEvent(). + // Called from gui thread only. Returns true if event cannot be delivered. + bool putEvent(const MidiPlayEvent&); + // Special method for incrementing a value: Handles getting the current hw value, + // incrementing it (as dB if specified), and sending it and setting the current hw value. + // Called from gui thread only. Returns true if event cannot be delivered. + // NOTE: Caller should use the Audio::msgAudioWait() to wait for the current value + // to change in the audio thread before calling again, especially rapidly. + // This method looks at the current value, so the current value must be up to date. + // It will not call Audio::msgAudioWait(), to allow caller to optimize multiple calls. +// TODO: An increment method seems possible: Wait for gui2audio to increment, then send to driver, +// which incurs up to one extra segment delay (if Jack midi). + bool putControllerValue(int port, int chan, int ctlnum, double val, bool isDb); + // Process the gui2AudioFifo. Called from audio thread only. + static bool processGui2AudioEvents(); + + // Various IPC FIFOs. + static LockFreeMPSCRingBuffer *eventBuffers() { return _eventBuffers; } + bool sendHwCtrlState(const MidiPlayEvent&, bool forceSend = false ); - bool sendEvent(const MidiPlayEvent&, bool forceSend = false ); AutomationType automationType(int channel) { return _automationType[channel]; } void setAutomationType(int channel, AutomationType t) { _automationType[channel] = t; @@ -165,7 +242,6 @@ extern void setPortExclusiveDefOutChan(int /*port*/, int /*chan*/); #endif -extern QMenu* midiPortsPopup(QWidget* parent = 0, int checkPort = -1, bool includeDefaultEntry = false); extern MidiControllerList defaultManagedMidiController; } // namespace MusECore diff -Nru muse-2.1.2/muse/midiseq.cpp muse-3.0.2+ds1/muse/midiseq.cpp --- muse-2.1.2/muse/midiseq.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiseq.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -6,6 +6,7 @@ // high priority task for scheduling midi events // // (C) Copyright 2003 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge.net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -31,26 +32,27 @@ #include #include #include +#include +#include "config.h" #include "app.h" #include "globals.h" +#include "driver/alsatimer.h" +#include "driver/rtctimer.h" #include "midi.h" #include "midiseq.h" #include "midiport.h" #include "mididev.h" -#include "midictrl.h" #include "audio.h" +#include "audiodev.h" #include "driver/alsamidi.h" -#include "driver/jackmidi.h" #include "sync.h" -#include "synth.h" #include "song.h" #include "gconfig.h" #include "warn_bad_timing.h" namespace MusEGlobal { -MusECore::MidiSeq* midiSeq; -volatile bool midiBusy=false; +MusECore::MidiSeq* midiSeq = NULL; } namespace MusECore { @@ -59,7 +61,19 @@ void initMidiSequencer() { - MusEGlobal::midiSeq = new MidiSeq("Midi"); + if(!MusEGlobal::midiSeq) + MusEGlobal::midiSeq = new MidiSeq("Midi"); +} + +void exitMidiSequencer() +{ + // Sequencer should be stopped before calling exitMidiSequencer(). Todo: maybe check that here. + if(MusEGlobal::midiSeq) + { + //MusEGlobal::midiSeqRunning = false; // Done in stop. + delete MusEGlobal::midiSeq; + MusEGlobal::midiSeq = NULL; + } } //--------------------------------------------------------- @@ -81,11 +95,6 @@ { MusECore::AudioMsg* msg = (MusECore::AudioMsg*)m; switch(msg->id) { - // This does not appear to be used anymore. Was sent in Audio::process1, DELETETHIS 5 ?? - // now Audio::processMidi is called directly. p4.0.15 Tim. - //case MusECore::MS_PROCESS: - // audio->processMidi(); - // break; case MusECore::SEQM_SEEK: processSeek(); @@ -104,100 +113,40 @@ break; - // Moved into Song::processMsg p4.0.34 ... - case MusECore::SEQM_ADD_TRACK: - MusEGlobal::song->insertTrack2(msg->track, msg->ival); - updatePollFd(); - break; - case MusECore::SEQM_REMOVE_TRACK: - MusEGlobal::song->cmdRemoveTrack(msg->track); - updatePollFd(); - break; - //case MusECore::SEQM_CHANGE_TRACK: DELETETHIS 4 - // MusEGlobal::song->changeTrack((Track*)(msg->p1), (Track*)(msg->p2)); - // updatePollFd(); - // break; - case MusECore::SEQM_ADD_PART: - MusEGlobal::song->cmdAddPart((Part*)msg->p1); - break; - case MusECore::SEQM_REMOVE_PART: - MusEGlobal::song->cmdRemovePart((Part*)msg->p1); - break; - case MusECore::SEQM_CHANGE_PART: - MusEGlobal::song->cmdChangePart((Part*)msg->p1, (Part*)msg->p2, msg->a, msg->b); - break; - - - case MusECore::SEQM_SET_TRACK_OUT_CHAN: - { - MidiTrack* track = (MidiTrack*)(msg->p1); - track->setOutChanAndUpdate(msg->a); - } - break; - case MusECore::SEQM_SET_TRACK_OUT_PORT: - { - MidiTrack* track = (MidiTrack*)(msg->p1); - track->setOutPortAndUpdate(msg->a); - } - break; - case MusECore::SEQM_REMAP_PORT_DRUM_CTL_EVS: - MusEGlobal::song->remapPortDrumCtrlEvents(msg->ival, msg->a, msg->b, msg->c); - break; - case MusECore::SEQM_CHANGE_ALL_PORT_DRUM_CTL_EVS: - MusEGlobal::song->changeAllPortDrumCtrlEvents((bool)msg->a, (bool)msg->b); - break; - case MusECore::SEQM_SET_MIDI_DEVICE: - ((MidiPort*)(msg->p1))->setMidiDevice((MidiDevice*)(msg->p2)); - updatePollFd(); - break; case MusECore::SEQM_IDLE: idle = msg->a; break; default: - printf("MidiSeq::processMsg() unknown id %d\n", msg->id); + fprintf(stderr, "MidiSeq::processMsg() unknown id %d\n", msg->id); break; } } -#if 1 // DELETETHIS the #if and #endif? //--------------------------------------------------------- // processStop //--------------------------------------------------------- void MidiSeq::processStop() { - // TODO Try to move this into Audio::stopRolling(). - playStateExt = false; // not playing - - // - // clear Alsa midi device notes and stop stuck notes - // - for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) + // Clear Alsa midi device notes and stop stuck notes. + for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { - //MidiDevice* md = *id; DELETETHIS 3 - // Only ALSA devices are handled by this thread. - //if((*id)->deviceType() == MidiDevice::ALSA_MIDI) - (*id)->handleStop(); - /* DELETETHIS 14 - if (md->midiPort() == -1) - continue; - MPEventList* pel = md->playEvents(); - MPEventList* sel = md->stuckNotes(); - pel->clear(); - for(iMPEvent i = sel->begin(); i != sel->end(); ++i) + MidiDevice* md = *id; + const MidiDevice::MidiDeviceType type = md->deviceType(); + // Only for ALSA devices. + switch(type) { - MidiPlayEvent ev = *i; - ev.setTime(0); - pel->add(ev); + case MidiDevice::ALSA_MIDI: + md->handleStop(); + break; + + case MidiDevice::JACK_MIDI: + case MidiDevice::SYNTH_MIDI: + break; } - sel->clear(); - //md->setNextPlayEvent(pel->begin()); // Removed p4.0.15 - */ } } -#endif -#if 1 //DELETETHIS #if and #endif //--------------------------------------------------------- // processSeek //--------------------------------------------------------- @@ -205,66 +154,27 @@ void MidiSeq::processSeek() { //--------------------------------------------------- - // set all controller + // Set all controllers //--------------------------------------------------- - for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { - //MidiDevice* md = *i; DELETETHIS 3 - // Only ALSA devices are handled by this thread. - //if((*i)->deviceType() == MidiDevice::ALSA_MIDI) - (*i)->handleSeek(); - /* DELETETHIS 47 - int port = md->midiPort(); - if (port == -1) - continue; - MidiPort* mp = &MusEGlobal::midiPorts[port]; - MidiCtrlValListList* cll = mp->controller(); - - MPEventList* el = md->playEvents(); - - if (MusEGlobal::audio->isPlaying()) + MidiDevice* md = *i; + const MidiDevice::MidiDeviceType type = md->deviceType(); + // Only for ALSA devices. + switch(type) { - // stop all notes - el->clear(); - MPEventList* sel = dev->stuckNotes(); - for (iMPEvent i = sel->begin(); i != sel->end(); ++i) - { - MidiPlayEvent ev = *i; - ev.setTime(0); - el->add(ev); - } - sel->clear(); - } - //else - // Removed p4.0.15 Device now leaves beginning pointing at next event, - // immediately after playing some notes. - // NOTE: This removal needs testing. I'm not sure about this. - //el->erase(el->begin(), dev->nextPlayEvent()); - - for (iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) - { - MidiCtrlValList* vl = ivl->second; - //int val = vl->value(pos); - //if (val != CTRL_VAL_UNKNOWN) { - // int channel = ivl->first >> 24; - // el->add(MidiPlayEvent(0, port, channel, ME_CONTROLLER, vl->num(), val)); - // } - iMidiCtrlVal imcv = vl->iValue(pos); - if(imcv != vl->end()) - { - Part* p = imcv->second.part; - unsigned t = (unsigned)imcv->first; - // Do not add values that are outside of the part. - if(p && t >= p->tick() && t < (p->tick() + p->lenTick()) ) - el->add(MidiPlayEvent(0, port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); - } + case MidiDevice::ALSA_MIDI: + md->handleSeek(); + break; + + case MidiDevice::JACK_MIDI: + case MidiDevice::SYNTH_MIDI: + break; } - //dev->setNextPlayEvent(el->begin()); // Removed p4.0.15 - */ } } -#endif + //--------------------------------------------------------- // MidiSeq @@ -276,29 +186,11 @@ prio = 0; idle = false; - midiClock = 0; - mclock1 = 0.0; - mclock2 = 0.0; - songtick1 = songtick2 = 0; - 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(); + MusEGlobal::undoSetuid(); } //--------------------------------------------------------- @@ -307,8 +199,8 @@ MidiSeq::~MidiSeq() { - delete timer; - delete _clockAveragerStages; + if(timer) + delete timer; } //--------------------------------------------------------- @@ -319,56 +211,59 @@ signed int MidiSeq::selectTimer() { int tmrFd; - + printf("Trying RTC timer...\n"); timer = new RtcTimer(); - tmrFd = timer->initTimer(); + tmrFd = timer->initTimer(MusEGlobal::config.rtcTicks); if (tmrFd != -1) { // ok! printf("got timer = %d\n", tmrFd); return tmrFd; } delete timer; - printf("Trying ALSA timer...\n"); +#ifdef ALSA_SUPPORT + fprintf(stderr, "Trying ALSA timer...\n"); timer = new AlsaTimer(); - tmrFd = timer->initTimer(); + tmrFd = timer->initTimer(MusEGlobal::config.rtcTicks); if ( tmrFd!= -1) { // ok! - printf("got timer = %d\n", tmrFd); + fprintf(stderr, "got timer = %d\n", tmrFd); return tmrFd; } delete timer; +#endif + timer=NULL; QMessageBox::critical( 0, /*tr*/(QString("Failed to start timer!")), /*tr*/(QString("No functional timer was available.\n" "RTC timer not available, check if /dev/rtc is available and readable by current user\n" "Alsa timer not available, check if module snd_timer is available and /dev/snd/timer is available"))); - printf("No functional timer available!!!\n"); + fprintf(stderr, "No functional timer available!!!\n"); exit(1); } //--------------------------------------------------------- +// deleteTimer() +// Destroy timer if valid. +//--------------------------------------------------------- + +bool MidiSeq::deleteTimer() +{ + if(timer) + { + delete timer; + timer = NULL; + return true; + } + return false; +} + +//--------------------------------------------------------- // threadStart // called from loop() //--------------------------------------------------------- void MidiSeq::threadStart(void*) { - // Removed by Tim. p3.3.17 DELETETHIS 13 - /* - struct sched_param rt_param; - memset(&rt_param, 0, sizeof(rt_param)); - int prio_min = sched_get_priority_min(SCHED_FIFO); - int prio_max = sched_get_priority_max(SCHED_FIFO); - - if (prio < prio_min) prio = prio_min; - else if (prio > prio_max) prio = prio_max; - - rt_param.sched_priority = prio; - int rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, &rt_param); - if (rv != 0) - perror("set realtime scheduler"); - */ - int policy; if ((policy = sched_getscheduler (0)) < 0) { printf("Cannot get current client scheduler: %s\n", strerror(errno)); @@ -398,19 +293,6 @@ dev->processInput(); } -// DELETETHIS 12 -//--------------------------------------------------------- -// synthIRead -//--------------------------------------------------------- - -#if 0 -static void synthIRead(void*, void* d) - { - SynthI* syn = (SynthI*) d; - syn->processInput(); - } -#endif - //--------------------------------------------------------- // midiWrite //--------------------------------------------------------- @@ -421,6 +303,20 @@ dev->flush(); } +void MidiSeq::addAlsaPollFd() +{ + // special handling for alsa midi: + // (one fd for all devices) + // this allows for processing of some alsa events + // even if no alsa driver is active (assigned to a port) + addPollFd(alsaSelectRfd(), POLLIN, MusECore::alsaMidiRead, this, 0); +} + +void MidiSeq::removeAlsaPollFd() +{ + removePollFd(alsaSelectRfd(), POLLIN); +} + //--------------------------------------------------------- // updatePollFd //--------------------------------------------------------- @@ -460,7 +356,7 @@ // (one fd for all devices) // this allows for processing of some alsa events // even if no alsa driver is active (assigned to a port) - addPollFd(alsaSelectRfd(), POLLIN, MusECore::alsaMidiRead, this, 0); + addAlsaPollFd(); } //--------------------------------------------------------- @@ -481,9 +377,14 @@ int MidiSeq::setRtcTicks() { int gotTicks = timer->setTimerFreq(MusEGlobal::config.rtcTicks); + if(gotTicks == 0) + return 0; if (MusEGlobal::config.rtcTicks-24 > gotTicks) { - printf("INFO: Could not get the wanted frequency %d, got %d, still it should suffice.\n", MusEGlobal::config.rtcTicks, gotTicks); + fprintf(stderr, "INFO: Could not get the wanted frequency %d, got %d, still it should suffice.\n", MusEGlobal::config.rtcTicks, gotTicks); } + else + fprintf(stderr, "INFO: Requested timer frequency:%d actual:%d\n", MusEGlobal::config.rtcTicks, gotTicks); + timer->startTimer(); return gotTicks; } @@ -493,15 +394,76 @@ // return true on error //--------------------------------------------------------- -void MidiSeq::start(int priority) - { - prio = priority; - - MusEGlobal::doSetuid(); - setRtcTicks(); - MusEGlobal::undoSetuid(); - Thread::start(priority); - } +void MidiSeq::start(int /*priority*/, void*) +{ + // Already running? + if(isRunning()) + return; + + if(!MusEGlobal::audioDevice) + { + fprintf(stderr, "MusE::seqStartMidi: audioDevice is NULL\n"); + return; + } + + if(!MusEGlobal::audio->isRunning()) + { + fprintf(stderr, "MusE::seqStartMidi: audio is not running\n"); + return; + } + + int midiprio = 0; + + // NOTE: MusEGlobal::realTimeScheduling can be true (gotten using jack_is_realtime()), + // while the determined MusEGlobal::realTimePriority can be 0. + // MusEGlobal::realTimePriority is gotten using pthread_getschedparam() on the client thread + // in JackAudioDevice::realtimePriority() which is a bit flawed - it reports there's no RT... + if(MusEGlobal::realTimeScheduling) + { + if(MusEGlobal::realTimePriority - 1 >= 0) + midiprio = MusEGlobal::realTimePriority - 1; + } + + if(MusEGlobal::midiRTPrioOverride > 0) + midiprio = MusEGlobal::midiRTPrioOverride; + + // FIXME: The MusEGlobal::realTimePriority of the Jack thread seems to always be 5 less than the value passed to jackd command. + + prio = midiprio; + + MusEGlobal::doSetuid(); + int freq = setRtcTicks(); + MusEGlobal::undoSetuid(); + + if(freq == 0) + { + fprintf(stderr, "Error setting timer frequency! Midi playback will not work!\n"); + } + + Thread::start(prio); + + int counter=0; + while (++counter) { + if (counter > 1000) { + fprintf(stderr,"midi sequencer thread does not start!? Exiting...\n"); +// exit(33); + break; + } + MusEGlobal::midiSeqRunning = MusEGlobal::midiSeq->isRunning(); + if (MusEGlobal::midiSeqRunning) + break; + usleep(1000); + if(MusEGlobal::debugMsg) + printf("looping waiting for sequencer thread to start\n"); + } + if(!MusEGlobal::midiSeqRunning) + { +// fprintf(stderr, "midiSeq is not running! Exiting...\n"); +// exit(33); + fprintf(stderr, "midiSeq is still not running!\n"); + } +} + //--------------------------------------------------------- // checkAndReportTimingResolution @@ -509,7 +471,7 @@ void MidiSeq::checkAndReportTimingResolution() { int freq = timer->getTimerFreq(); - printf("Aquired timer frequency: %d\n", freq); + fprintf(stderr, "Acquired timer frequency: %d\n", freq); if (freq < 500) { if(MusEGlobal::config.warnIfBadTiming) { @@ -525,6 +487,7 @@ if(warn != MusEGlobal::config.warnIfBadTiming) { MusEGlobal::config.warnIfBadTiming = warn; + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //MusEGlobal::muse->changeConfig(true); // Save settings? No, wait till close. } } @@ -532,102 +495,6 @@ } //--------------------------------------------------------- -// 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 -//--------------------------------------------------------- - -void MidiSeq::processMidiClock() - { - // DELETETHIS 30, maybe remove entire function? -// if (genMCSync) { -// MusEGlobal::midiPorts[txSyncPort].sendClock(); -// } - -/* if (state == START_PLAY) { - // start play on sync - state = PLAY; - _midiTick = playTickPos; - midiClock = playTickPos; - - int bar, beat, tick; - sigmap.tickValues(_midiTick, &bar, &beat, &tick); - midiClick = sigmap.bar2tick(bar, beat+1, 0); - - double cpos = MusEGlobal::tempomap.tick2time(playTickPos); - samplePosStart = samplePos - lrint(cpos * MusEGlobal::sampleRate); - rtcTickStart = rtcTick - lrint(cpos * realRtcTicks); - - endSlice = playTickPos; - recTick = playTickPos; - lastTickPos = playTickPos; - - tempoSN = MusEGlobal::tempomap.tempoSN(); - - startRecordPos.setPosTick(playTickPos); - } -*/ -// midiClock += MusEGlobal::config.division/24; - } - -//--------------------------------------------------------- // midiTick //--------------------------------------------------------- @@ -639,7 +506,7 @@ { if(MidiSeq::ticker++ > 100) { - printf("tick!\n"); + fprintf(stderr, "ticker:%i\n", MidiSeq::ticker); MidiSeq::ticker=0; } } @@ -665,23 +532,21 @@ if (idle) return; - if (MusEGlobal::midiBusy) { - // we hit MusEGlobal::audio: MusEGlobal::midiSeq->msgProcess (actually this has been MusEGlobal::audio->processMidi for some time now - Tim) - // miss this timer tick - return; - } - unsigned curFrame = MusEGlobal::audio->curFrame(); if (!MusEGlobal::extSyncFlag.value()) { int curTick = lrint((double(curFrame)/double(MusEGlobal::sampleRate)) * double(MusEGlobal::tempomap.globalTempo()) * double(MusEGlobal::config.division) * 10000.0 / double(MusEGlobal::tempomap.tempo(MusEGlobal::song->cpos()))); - if(midiClock > curTick) - midiClock = curTick; - + int mclock = MusEGlobal::midiSyncContainer.midiClock(); + if(mclock > curTick) + { + mclock = curTick; + MusEGlobal::midiSyncContainer.setMidiClock(mclock); + } + int div = MusEGlobal::config.division/24; - if(curTick >= midiClock + div) { - int perr = (curTick - midiClock) / div; + if(curTick >= mclock + div) { + int perr = (curTick - mclock) / div; bool used = false; @@ -689,7 +554,7 @@ { MidiPort* mp = &MusEGlobal::midiPorts[port]; - // No device? Clock out not turned on? DELETETHIS 3 + // No device? Clock out not turned on? if(!mp->device() || !mp->syncInfo().MCOut()) continue; @@ -698,126 +563,31 @@ mp->sendClock(); } - // DELETETHIS 35 ?? - /* - for(iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) - { - MidiDevice* dev = *imd; - - if(!dev->syncInfo().MCOut()) - continue; - - // Shall we check open flags? - //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) - //if(!(dev->openFlags() & 1)) - // continue; - - int port = dev->midiPort(); - // Without this -1 check, interesting sync things can be done by the user without ever - // assigning any devices to ports ! - //if(port < 0 || port > MIDI_PORTS) - if(port < -1 || port > MIDI_PORTS) - continue; - - if(port == -1) - // Send straight to the device... Copied from MidiPort. - { - MidiPlayEvent event(0, 0, 0, ME_CLOCK, 0, 0); - dev->putEvent(event); - } - else - // Go through the port... - MusEGlobal::midiPorts[port].sendClock(); - } - */ - if(MusEGlobal::debugMsg && used && perr > 1) - printf("Dropped %d midi out clock(s). curTick:%d midiClock:%d div:%d\n", perr, curTick, midiClock, div); - //} DELETETHIS and maybe the below ??? - - // Increment as if we had caught the timer exactly on the mark, even if the timer - // has passed beyond the mark, or even beyond 2 * div. - // If we missed some chances to send clock, resume the count where it would have been, - // had we not missed chances. - // We can't do anything about missed chances except send right away, and make up - // for gained time by losing time in the next count... - // In other words, use equalization periods to counter gained/lost time, so that - // ultimately, over time, the receiver remains in phase, despite any short dropouts / phase glitches. - // (midiClock only increments by div units). - // - // Tested: With midi thread set to high priority, very few clock dropouts ocurred (P4 1.6Ghz). - // But target device tick drifts out of phase with muse tick slowly over time, say 20 bars or so. - // May need more tweaking, possibly use round with/instead of lrint (above), and/or - // do not use equalization periods - set midiClock to fractions of div. - // Tested: With RTC resolution at 1024, stability was actually better than with 8192! - // It stayed in sync more than 64 bars... - // - // + printf("Dropped %d midi out clock(s). curTick:%d midiClock:%d div:%d\n", perr, curTick, MusEGlobal::midiSyncContainer.midiClock(), div); + // Using equalization periods... - midiClock += (perr * div); - //midiClock += perr; DELETETHIS - // - // No equalization periods... TODO: or DELETETHIS? - //midiClock += (perr * div); + MusEGlobal::midiSyncContainer.setMidiClock(mclock + (perr * div)); } } - // play all events upto curFrame - for (iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { - //MidiDevice* md = *id; DELETETHIS 10 - // Is it a Jack midi device? They are iterated in Audio::processMidi. p3.3.36 - //MidiJackDevice* mjd = dynamic_cast(md); - //if(mjd) - //if(md->deviceType() == MidiDevice::JACK_MIDI) - // continue; - //if(md->isSynti()) // syntis are handled by audio thread - // continue; - // Only ALSA midi devices are handled by this thread. - if((*id)->deviceType() == MidiDevice::ALSA_MIDI) - (*id)->processMidi(); - - // Moved into MidiAlsaDevice. p4.0.34 DELETETHIS 40 - /* - int port = md->midiPort(); - MidiPort* mp = port != -1 ? &MusEGlobal::midiPorts[port] : 0; - MPEventList* el = md->playEvents(); - if (el->empty()) - continue; - - ///iMPEvent i = md->nextPlayEvent(); - iMPEvent i = el->begin(); // p4.0.15 Tim. - - for (; i != el->end(); ++i) { - // p3.3.25 - // If syncing to external midi sync, we cannot use the tempo map. - // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. - //if (i->time() > curFrame) { - if (i->time() > (extsync ? tickpos : curFrame)) { - //printf(" curT %d frame %d\n", i->time(), curFrame); - break; // skip this event - } - - if (mp) { - if (mp->sendEvent(*i)) - break; - } - else { - if (md->putEvent(*i)) - break; - } - } - ///md->setNextPlayEvent(i); - // p4.0.15 We are done with these events. Let us erase them here instead of Audio::processMidi. - // That way we can simply set the next play event to the beginning. - // This also allows other events to be inserted without the problems caused by the next play event - // being at the 'end' iterator and not being *easily* set to some new place beginning of the newer insertions. - // The way that MPEventList sorts made it difficult to predict where the iterator of the first newly inserted items was. - // The erasure in Audio::processMidi was missing some events because of that. - el->erase(el->begin(), i); - //md->setNextPlayEvent(el->begin()); // Removed p4.0.15 - */ - - } + // Play all events up to curFrame. + for (iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) + { + MidiDevice* md = *id; + const MidiDevice::MidiDeviceType type = md->deviceType(); + // Only for ALSA devices. + switch(type) + { + case MidiDevice::ALSA_MIDI: + md->processMidi(curFrame); + break; + + case MidiDevice::JACK_MIDI: + case MidiDevice::SYNTH_MIDI: + break; + } + } } //--------------------------------------------------------- @@ -831,27 +601,6 @@ Thread::sendMsg(&msg); } -//--------------------------------------------------------- -// msgSetMidiDevice -// to avoid timeouts in the RT-thread, setMidiDevice -// is done in GUI context after setting the midi thread -// into idle mode -//--------------------------------------------------------- - -void MidiSeq::msgSetMidiDevice(MidiPort* port, MidiDevice* device) - { - MusECore::AudioMsg msg; - msg.id = MusECore::SEQM_IDLE; - msg.a = true; - Thread::sendMsg(&msg); - - port->setMidiDevice(device); - - msg.id = MusECore::SEQM_IDLE; - msg.a = false; - Thread::sendMsg(&msg); - } - void MidiSeq::msgSeek() { msgMsg(MusECore::SEQM_SEEK); } void MidiSeq::msgStop() { msgMsg(MusECore::MS_STOP); } void MidiSeq::msgSetRtc() { msgMsg(MusECore::MS_SET_RTC); } diff -Nru muse-2.1.2/muse/midiseq.h muse-3.0.2+ds1/muse/midiseq.h --- muse-2.1.2/muse/midiseq.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/midiseq.h 2017-12-17 21:07:38.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: midiseq.h,v 1.6.2.11 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge.net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,10 +26,6 @@ #define __MIDISEQ_H__ #include "thread.h" -#include "mpevent.h" -#include "driver/alsatimer.h" -#include "driver/rtctimer.h" -#include "sync.h" namespace MusECore { @@ -37,6 +34,7 @@ class MPEventList; class MTC; class SynthI; +class Timer; //--------------------------------------------------------- // MidiSeq @@ -46,83 +44,50 @@ int timerFd; int idle; int prio; // realtime priority - int midiClock; static int ticker; - -/* Testing */ - bool playStateExt; // used for keeping play state in sync functions - int recTick; // ext sync tick position - double mclock1, mclock2; - double songtick1, songtick2; - int recTick1, recTick2; - int lastTempo; - 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 */ - Timer *timer; - signed int selectTimer(); int setRtcTicks(); static void midiTick(void* p, void*); void processTimerTick(); void processSeek(); void processStop(); - void processMidiClock(); virtual void processMsg(const ThreadMsg*); void updatePollFd(); - void mtcSyncMsg(const MTC&, int, bool); - public: MidiSeq(const char* name); ~MidiSeq(); - virtual void start(int); + virtual void start(int, void* ptr=0); virtual void threadStop(); virtual void threadStart(void*); + signed int selectTimer(); + // Destroy timer if valid. Returns true if successful. + bool deleteTimer(); + void addAlsaPollFd(); + void removeAlsaPollFd(); + bool isIdle() const { return idle; } - bool externalPlayState() const { return playStateExt; } - void setExternalPlayState(bool v) { playStateExt = v; } - 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(); void msgStop(); void msgSetRtc(); void msgUpdatePollFd(); - void msgAddSynthI(SynthI* synth); - void msgRemoveSynthI(SynthI* synth); - void msgSetMidiDevice(MidiPort*, MidiDevice*); }; + +extern void initMidiSequencer(); +extern void exitMidiSequencer(); + } //namespace MusECore namespace MusEGlobal { extern MusECore::MidiSeq* midiSeq; -extern volatile bool midiBusy; } // namespace MusEGlobal #endif diff -Nru muse-2.1.2/muse/miditransform.cpp muse-3.0.2+ds1/muse/miditransform.cpp --- muse-2.1.2/muse/miditransform.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/miditransform.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -24,7 +24,6 @@ #include #include - #include #include @@ -41,11 +40,14 @@ #include "gconfig.h" #include "midictrl.h" +using MusECore::Undo; +using MusECore::UndoOp; + namespace MusECore { // // Order of events: -// Note, Poly Pressure, Control, AfterTouch, Pitch Bend, NRPN, RPN +// Note, Poly Pressure, Control, AfterTouch, Pitch Bend, NRPN, RPN, Program // #define MIDITRANSFORM_NOTE 0 #define MIDITRANSFORM_POLY 1 @@ -54,11 +56,12 @@ #define MIDITRANSFORM_PITCHBEND 4 #define MIDITRANSFORM_NRPN 5 #define MIDITRANSFORM_RPN 6 +#define MIDITRANSFORM_PROGRAM 7 static int eventTypeTable[] = { MIDITRANSFORM_NOTE, MIDITRANSFORM_POLY, MIDITRANSFORM_CTRL, MIDITRANSFORM_ATOUCH, - MIDITRANSFORM_PITCHBEND, MIDITRANSFORM_NRPN, MIDITRANSFORM_RPN + MIDITRANSFORM_PITCHBEND, MIDITRANSFORM_NRPN, MIDITRANSFORM_RPN, MIDITRANSFORM_PROGRAM }; static int procVal2Map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 10, 11 }; @@ -367,7 +370,7 @@ // buttonNew buttonDelete buttonApply buttonOk //--------------------------------------------------------- -MidiTransformerDialog::MidiTransformerDialog(QDialog* parent, Qt::WFlags fl) +MidiTransformerDialog::MidiTransformerDialog(QDialog* parent, Qt::WindowFlags fl) : QDialog(parent, fl) { setupUi(this); @@ -483,8 +486,7 @@ // subfunction of processEvent() //--------------------------------------------------------- -void MidiTransformerDialog::transformEvent(MusECore::Event& event, MusECore::MidiPart* part, - MusECore::MidiPart* newPart) +void MidiTransformerDialog::transformEvent(MusECore::Event& event, MusECore::MidiPart* part, MusECore::MidiPart* newPart, MusECore::Undo& operations) { MusECore::MidiTransformation* cmt = data->cmt; MusECore::Event newEvent = event.clone(); @@ -543,6 +545,8 @@ val = cmt->procVal1a; } break; + default: + break; } if (val < 0) val = 0; @@ -596,6 +600,7 @@ case MusECore::ScaleMap: case MusECore::Keep: case MusECore::Flip: + case MusECore::Toggle: break; } if (val < 0) @@ -617,10 +622,10 @@ len -= cmt->procLenA; break; case MusECore::Multiply: - len = int(val * (cmt->procLenA/100.0) + .5); + len = int(len * (cmt->procLenA/100.0) + .5); break; case MusECore::Divide: - len = int(val / (cmt->procLenA/100.0) + .5); + len = int(len / (cmt->procLenA/100.0) + .5); break; case MusECore::Fix: len = cmt->procLenA; @@ -632,6 +637,7 @@ case MusECore::Keep: case MusECore::Flip: case MusECore::Value: + case MusECore::Toggle: break; } if (len < 0) @@ -651,52 +657,44 @@ pos -= cmt->procPosA; break; case MusECore::Multiply: - pos = int(val * (cmt->procPosA/100.0) + .5); + pos = int(pos * (cmt->procPosA/100.0) + .5); break; case MusECore::Divide: - pos = int(val / (cmt->procPosA/100.0) + .5); + pos = int(pos / (cmt->procPosA/100.0) + .5); break; case MusECore::Fix: case MusECore::Invert: case MusECore::ScaleMap: case MusECore::Dynamic: case MusECore::Random: + { + int range = cmt->procPosA; + int val = rand() % (2 * range) - range; + pos = pos + val; + } + break; case MusECore::Keep: case MusECore::Flip: case MusECore::Value: + case MusECore::Toggle: break; } if (pos < 0) pos = 0; newEvent.setTick(pos); - MusECore::Event dummy; switch(data->cmt->funcOp) { case MusECore::Transform: - // Indicate do clone parts. - removePortCtrlEvents(event, part, true); - MusEGlobal::song->changeEvent(event, newEvent, part); - // Indicate do clone parts. - addPortCtrlEvents(newEvent, part, true); - // Indicate do port controller values and clone parts. - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, true, true)); - MusEGlobal::song->addUpdateFlags(SC_EVENT_MODIFIED); + operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, true, true)); break; case MusECore::Insert: // Indicate do port controller values and clone parts. - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::AddEvent, dummy, newEvent, part, true, true)); - MusEGlobal::song->addEvent(newEvent, part); - // Indicate do clone parts. - addPortCtrlEvents(newEvent, part, true); - MusEGlobal::song->addUpdateFlags(SC_EVENT_INSERTED); + operations.push_back(UndoOp(UndoOp::AddEvent, newEvent, part, true, true)); break; case MusECore::Extract: - // Indicate do port controller values and clone parts. - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, dummy, event, part, true, true)); - // Indicate do clone parts. - removePortCtrlEvents(event, part, true); - MusEGlobal::song->deleteEvent(event, part); - MusEGlobal::song->addUpdateFlags(SC_EVENT_REMOVED); + operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, true, true)); + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH case MusECore::Copy: newPart->addEvent(newEvent); break; @@ -709,7 +707,7 @@ // processEvent //--------------------------------------------------------- -void MidiTransformerDialog::processEvent(MusECore::Event& event, MusECore::MidiPart* part, MusECore::MidiPart* newPart) +void MidiTransformerDialog::processEvent(MusECore::Event& event, MusECore::MidiPart* part, MusECore::MidiPart* newPart, MusECore::Undo& operations) { switch(data->cmt->funcOp) { case MusECore::Select: @@ -719,35 +717,22 @@ int tick = event.tick(); int rt = AL::sigmap.raster(tick, data->cmt->quantVal) - tick; if (tick != rt) { - // Indicate do clone parts. - removePortCtrlEvents(event, part, true); MusECore::Event newEvent = event.clone(); newEvent.setTick(rt); - MusEGlobal::song->changeEvent(event, newEvent, part); - // Indicate do clone parts. - addPortCtrlEvents(newEvent, part, true); - // Indicate do port controller values and clone parts. - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, true, true)); - MusEGlobal::song->addUpdateFlags(SC_EVENT_MODIFIED); + operations.push_back(UndoOp(UndoOp::ModifyEvent,newEvent,event,part,true,true)); } } break; case MusECore::Delete: { - MusECore::Event ev; - // Indicate do port controller values and clone parts. - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, ev, event, part, true, true)); - // Indicate do clone parts. - removePortCtrlEvents(event, part, true); - MusEGlobal::song->deleteEvent(event, part); - MusEGlobal::song->addUpdateFlags(SC_EVENT_REMOVED); + operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, true, true)); } break; case MusECore::Transform: case MusECore::Insert: case MusECore::Copy: case MusECore::Extract: - transformEvent(event, part, newPart); + transformEvent(event, part, newPart, operations); break; } } @@ -758,7 +743,7 @@ // return true if event is selected //--------------------------------------------------------- -bool MidiTransformerDialog::isSelected(MusECore::Event& event, MusECore::MidiPart*) +bool MidiTransformerDialog::isSelected(const MusECore::Event& event) { MusECore::MidiTransformation* cmt = data->cmt; @@ -921,13 +906,13 @@ void MidiTransformerDialog::apply() { MusECore::SongChangedFlags_t flags = 0; - MusEGlobal::song->startUndo(); - MusEGlobal::audio->msgIdle(true); + + Undo operations; bool copyExtract = (data->cmt->funcOp == MusECore::Copy) || (data->cmt->funcOp == MusECore::Extract); - std::vector< MusECore::EventList* > doneList; - typedef std::vector< MusECore::EventList* >::iterator iDoneList; + QSet< int > doneList; + typedef std::set< int >::iterator iDoneList; iDoneList idl; MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); @@ -938,24 +923,21 @@ MusECore::MidiTrack* newTrack = 0; MusECore::PartList *pl = (*t)->parts(); if (copyExtract) { - // check wether we must generate a new track + // check whether we must generate a new track for (MusECore::iPart p = pl->begin(); p != pl->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart *) p->second; - MusECore::EventList* el = part->events(); + const MusECore::EventList& el = part->events(); // Check if the event list has already been done. Skip repeated clones. - for(idl = doneList.begin(); idl != doneList.end(); ++idl) - if(*idl == el) - break; - if(idl != doneList.end()) - break; - doneList.push_back(el); + if (doneList.contains(part->clonemaster_sn())) + continue; + doneList.insert(part->clonemaster_sn()); - for (MusECore::iEvent i = el->begin(); i != el->end(); ++i) { - MusECore::Event event = i->second; + for (MusECore::ciEvent i = el.begin(); i != el.end(); ++i) { + const MusECore::Event& event = i->second; unsigned tick = event.tick(); if (data->cmt->insideLoop && (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())) continue; - if (isSelected(event, part)) { + if (isSelected(event)) { newTrack = new MusECore::MidiTrack(); tl.push_back(newTrack); break; @@ -969,82 +951,56 @@ for (MusECore::iPart p = pl->begin(); p != pl->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart *) p->second; MusECore::MidiPart* newPart = 0; - MusECore::EventList* el = part->events(); + const MusECore::EventList& el = part->events(); // Check if the event list has already been done. Skip repeated clones. - for(idl = doneList.begin(); idl != doneList.end(); ++idl) - if(*idl == el) - break; - if(idl != doneList.end()) - break; - doneList.push_back(el); + if (doneList.contains(part->clonemaster_sn())) + continue; + doneList.insert(part->clonemaster_sn()); if (copyExtract) { - // check wether we must generate a new part - for (MusECore::iEvent i = el->begin(); i != el->end(); ++i) { - MusECore::Event event = i->second; + // check whether we must generate a new part + for (MusECore::ciEvent i = el.begin(); i != el.end(); ++i) { + const MusECore::Event& event = i->second; unsigned tick = event.tick(); if (data->cmt->insideLoop && (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())) continue; - if (isSelected(event, part)) { + if (isSelected(event)) { newPart = new MusECore::MidiPart(newTrack); newPart->setName(part->name()); newPart->setColorIndex(part->colorIndex()); newPart->setTick(part->tick()); newPart->setLenTick(part->lenTick()); - MusEGlobal::song->addPart(newPart); + operations.push_back(UndoOp(UndoOp::AddPart,newPart)); flags |= SC_PART_INSERTED; break; } } } MusECore::EventList pel; - for (MusECore::iEvent i = el->begin(); i != el->end(); ++i) { - MusECore::Event event = i->second; + for (MusECore::ciEvent i = el.begin(); i != el.end(); ++i) { + const MusECore::Event& event = i->second; unsigned tick = event.tick(); if (data->cmt->insideLoop && (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())) continue; - int flag = isSelected(event, part); + int flag = isSelected(event); if (data->cmt->funcOp == MusECore::Select) - event.setSelected(flag); + operations.push_back(UndoOp(UndoOp::SelectEvent, event, part, flag, event.selected())); else if (flag) - pel.add(event); + pel.add(const_cast(event)); // ough, FIXME, what an ugly hack. } for (MusECore::iEvent i = pel.begin(); i != pel.end(); ++i) { MusECore::Event event = i->second; - processEvent(event, part, newPart); + processEvent(event, part, newPart, operations); } } } if (!tl.empty()) { flags |= SC_TRACK_INSERTED; - for (MusECore::iTrack t = tl.begin(); t != tl.end(); ++t) { - MusEGlobal::song->insertTrack0(*t, -1); - } - } + for (MusECore::iTrack t = tl.begin(); t != tl.end(); ++t) + operations.push_back(UndoOp(UndoOp::AddTrack, -1, *t)); + } - switch(data->cmt->funcOp) { - case MusECore::Select: - flags |= SC_SELECTION; - break; - case MusECore::Quantize: - flags |= SC_EVENT_MODIFIED; - break; - case MusECore::Delete: - flags |= SC_EVENT_REMOVED; - break; - case MusECore::Transform: - flags |= SC_EVENT_MODIFIED; - break; - case MusECore::Insert: - flags |= SC_EVENT_INSERTED; - break; - case MusECore::Copy: - flags |= SC_EVENT_INSERTED; - case MusECore::Extract: - break; - } - MusEGlobal::audio->msgIdle(false); - MusEGlobal::song->endUndo(flags); + MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- @@ -1196,6 +1152,8 @@ procVal1a->setEnabled(true); procVal1b->setEnabled(true); break; + default: + break; } procVal1aChanged(data->cmt->procVal1a); procVal1bChanged(data->cmt->procVal1b); @@ -1209,7 +1167,15 @@ { MusECore::TransformOperator op = MusECore::TransformOperator(MusECore::procVal2Map[val]); data->cmt->procVal2 = op; + procVal2OpUpdate(op); + } + +//--------------------------------------------------------- +// procVal2OpUpdate +//--------------------------------------------------------- +void MidiTransformerDialog::procVal2OpUpdate(MusECore::TransformOperator op) + { switch (op) { case MusECore::Keep: case MusECore::Invert: @@ -1277,7 +1243,21 @@ void MidiTransformerDialog::procPosOpSel(int val) { - MusECore::TransformOperator op = MusECore::TransformOperator(val); + // Added the randomize functionality for the + // MIDI note position. Since we left out the + // other operators, random would be 5 (as + // we get it from the combo box but in the + // enum used it would be 11, so we do this + // conditionally + MusECore::TransformOperator op; + if(val == 5) + { + op = MusECore::Random; + } + else + { + op = MusECore::TransformOperator(val); + } data->cmt->procPos = op; switch (op) { @@ -1290,6 +1270,7 @@ procPosA->setDecimals(2); procPosA->setEnabled(true); break; + case MusECore::Random: case MusECore::Plus: case MusECore::Minus: procPosA->setDecimals(0); @@ -1339,8 +1320,8 @@ { QString name; for (int i = 0;; ++i) { - name.sprintf("New-%d", i); - MusECore::iMidiTransformation imt; + name = QString("New-") + QString::number(i); + MusECore::iMidiTransformation imt; for (imt = MusECore::mtlist.begin(); imt != MusECore::mtlist.end(); ++imt) { if (name == (*imt)->name) break; @@ -1705,9 +1686,9 @@ /*! - \fn MidiTransformerDialog::typesMatch(MusECore::MidiEvent e, unsigned t) + \fn MidiTransformerDialog::typesMatch(const MusECore::MidiEvent e, unsigned t) */ -bool MidiTransformerDialog::typesMatch(MusECore::Event& e, unsigned selType) +bool MidiTransformerDialog::typesMatch(const MusECore::Event& e, unsigned selType) { bool matched = false; switch (selType) @@ -1748,6 +1729,7 @@ MusECore::MidiController::ControllerType c = MusECore::midiControllerType(e.dataA()); matched = (c == MusECore::MidiController::NRPN); } + break; } case MIDITRANSFORM_RPN: { @@ -1755,6 +1737,15 @@ MusECore::MidiController::ControllerType c = MusECore::midiControllerType(e.dataA()); matched = (c == MusECore::MidiController::RPN); } + break; + } + case MIDITRANSFORM_PROGRAM: + { + if (e.type() == MusECore::Controller) { + MusECore::MidiController::ControllerType c = MusECore::midiControllerType(e.dataA()); + matched = (c == MusECore::MidiController::Program); + } + break; } default: fprintf(stderr, "Error matching type in MidiTransformerDialog: unknown eventtype!\n"); diff -Nru muse-2.1.2/muse/miditransform.h muse-3.0.2+ds1/muse/miditransform.h --- muse-2.1.2/muse/miditransform.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/miditransform.h 2017-12-04 21:01:18.000000000 +0000 @@ -32,6 +32,7 @@ namespace MusECore { +class Undo; class Event; class MidiPart; class MidiTransformation; @@ -49,7 +50,21 @@ enum TransformOperator { Keep, Plus, Minus, Multiply, Divide, Fix, Value, Invert, - ScaleMap, Flip, Dynamic, Random + ScaleMap, Flip, Dynamic, Random, Toggle + }; + +class TransformToggleState { + private: + bool _ctrlStates[128]; + + public: + TransformToggleState() + { + for(int i = 0; i < 128; ++i) + _ctrlStates[i] = false; + } + bool ctrlState(int ctl_num) { return _ctrlStates[ctl_num & 0x7f]; } + void setCtrlState(int ctl_num, bool state) { _ctrlStates[ctl_num & 0x7f] = state; } }; extern void writeMidiTransforms(int level, Xml& xml); @@ -70,10 +85,10 @@ virtual void accept(); void setValOp(QWidget* a, QWidget* b, MusECore::ValOp op); - void processEvent(MusECore::Event&, MusECore::MidiPart*, MusECore::MidiPart*); - bool isSelected(MusECore::Event&, MusECore::MidiPart*); - void transformEvent(MusECore::Event&, MusECore::MidiPart*, MusECore::MidiPart*); - bool typesMatch(MusECore::Event& e, unsigned selType); + void processEvent(MusECore::Event&, MusECore::MidiPart*, MusECore::MidiPart*, MusECore::Undo& operations); + bool isSelected(const MusECore::Event&); + void transformEvent(MusECore::Event&, MusECore::MidiPart*, MusECore::MidiPart*, MusECore::Undo& operations); + bool typesMatch(const MusECore::Event& e, unsigned selType); void updatePresetList(); @@ -92,6 +107,7 @@ void procEventTypeSel(int); void procVal1OpSel(int); void procVal2OpSel(int); + void procVal2OpUpdate(MusECore::TransformOperator op); void procLenOpSel(int); void procPosOpSel(int); void funcOpSel(int); @@ -121,7 +137,7 @@ void songChanged(MusECore::SongChangedFlags_t); public: - MidiTransformerDialog(QDialog* parent = 0, Qt::WFlags fl = 0); + MidiTransformerDialog(QDialog* parent = 0, Qt::WindowFlags fl = 0); ~MidiTransformerDialog(); }; diff -Nru muse-2.1.2/muse/mixer/amixer.cpp muse-3.0.2+ds1/muse/mixer/amixer.cpp --- muse-2.1.2/muse/mixer/amixer.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/amixer.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -33,21 +33,27 @@ #include #include #include +#include #include "app.h" #include "helper.h" #include "icons.h" #include "amixer.h" #include "song.h" +#include "audio.h" #include "astrip.h" #include "mstrip.h" +#include "track.h" #include "gconfig.h" #include "xml.h" #define __WIDTH_COMPENSATION 4 +// For debugging output: Uncomment the fprintf section. +#define DEBUG_MIXER(dev, format, args...) // fprintf(dev, format, ##args); + //typedef std::list StripList; //static StripList stripList; @@ -143,14 +149,17 @@ bool ScrollArea::viewportEvent(QEvent* event) { + DEBUG_MIXER(stderr, "viewportEvent type %d\n", (int)event->type()); +// event->ignore(); // Let it do the layout now, before we emit. - QScrollArea::viewportEvent(event); + const bool res = QScrollArea::viewportEvent(event); if(event->type() == QEvent::LayoutRequest) emit layoutRequest(); - //return false; - return true; +// return false; +// return true; + return res; } //--------------------------------------------------------- @@ -161,7 +170,8 @@ AudioMixerApp::AudioMixerApp(QWidget* parent, MusEGlobal::MixerConfig* c) : QMainWindow(parent) - { +{ + _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; cfg = c; oldAuxsSize = 0; routingDialog = 0; @@ -169,11 +179,16 @@ setWindowTitle(cfg->name); setWindowIcon(*museIcon); + //cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW; + QMenu* menuConfig = menuBar()->addMenu(tr("&Create")); MusEGui::populateAddTrack(menuConfig,true); connect(menuConfig, SIGNAL(triggered(QAction *)), MusEGlobal::song, SLOT(addNewTrack(QAction *))); QMenu* menuView = menuBar()->addMenu(tr("&View")); + menuStrips = menuView->addMenu(tr("Strips")); + connect(menuStrips, SIGNAL(aboutToShow()), SLOT(stripsMenu())); + routingId = menuView->addAction(tr("Routing"), this, SLOT(toggleRouteDialog())); routingId->setCheckable(true); @@ -197,6 +212,8 @@ showAuxTracksId = new QAction(tr("Show Auxs"), actionItems); showSyntiTracksId = new QAction(tr("Show Synthesizers"), actionItems); + showDrumTracksId->setVisible(false); + showMidiTracksId->setCheckable(true); showDrumTracksId->setCheckable(true); showNewDrumTracksId->setCheckable(true); @@ -207,11 +224,9 @@ showAuxTracksId->setCheckable(true); showSyntiTracksId->setCheckable(true); - //connect(menuView, SIGNAL(triggered(QAction*)), SLOT(showTracksChanged(QAction*))); - //connect(actionItems, SIGNAL(selected(QAction*)), this, SLOT(showTracksChanged(QAction*))); connect(showMidiTracksId, SIGNAL(triggered(bool)), SLOT(showMidiTracksChanged(bool))); - connect(showDrumTracksId, SIGNAL(triggered(bool)), SLOT(showDrumTracksChanged(bool))); - connect(showNewDrumTracksId, SIGNAL(triggered(bool)), SLOT(showNewDrumTracksChanged(bool))); + connect(showDrumTracksId, SIGNAL(triggered(bool)), SLOT(showDrumTracksChanged(bool))); + connect(showNewDrumTracksId, SIGNAL(triggered(bool)), SLOT(showNewDrumTracksChanged(bool))); connect(showWaveTracksId, SIGNAL(triggered(bool)), SLOT(showWaveTracksChanged(bool))); connect(showInputTracksId, SIGNAL(triggered(bool)), SLOT(showInputTracksChanged(bool))); connect(showOutputTracksId, SIGNAL(triggered(bool)), SLOT(showOutputTracksChanged(bool))); @@ -223,32 +238,388 @@ ///view = new QScrollArea(); view = new ScrollArea(); -// view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view->setFocusPolicy(Qt::NoFocus); + view->setContentsMargins(0, 0, 0, 0); + //view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setCentralWidget(view); central = new QWidget(view); - layout = new QHBoxLayout(); - central->setLayout(layout); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); + central->setFocusPolicy(Qt::NoFocus); + central->setContentsMargins(0, 0, 0, 0); + //splitter = new QSplitter(view); + mixerLayout = new QHBoxLayout(); + central->setLayout(mixerLayout); + mixerLayout->setSpacing(0); + mixerLayout->setContentsMargins(0, 0, 0, 0); view->setWidget(central); + //view->setWidget(splitter); view->setWidgetResizable(true); connect(view, SIGNAL(layoutRequest()), SLOT(setSizing())); - ///connect(this, SIGNAL(layoutRequest()), SLOT(setSizing())); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); - //MusEGlobal::song->update(); // calls update mixer - updateMixer(UPDATE_ALL); // Build the mixer, add the strips. p4.0.45 + initMixer(); + redrawMixer(); + + central->installEventFilter(this); + mixerLayout->installEventFilter(this); + view ->installEventFilter(this); + +} + +void AudioMixerApp::stripsMenu() +{ + menuStrips->clear(); + connect(menuStrips, SIGNAL(triggered(QAction*)), SLOT(handleMenu(QAction*))); + QAction *act; + + act = menuStrips->addAction(tr("Traditional order")); + act->setData(MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW); + act->setCheckable(true); + if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW) + act->setChecked(true); + + act = menuStrips->addAction(tr("Arranger order")); + act->setData(MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW); + act->setCheckable(true); + if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW) + act->setChecked(true); + + act = menuStrips->addAction(tr("User order")); + act->setData(MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW); + act->setCheckable(true); + if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW) + act->setChecked(true); + + menuStrips->addSeparator(); + act = menuStrips->addAction(tr("Show all hidden strips")); + act->setData(UNHIDE_STRIPS); + menuStrips->addSeparator(); + + // loop through all tracks and show the hidden ones + int i=0,h=0; + foreach (Strip *s, stripList) { + if (!s->getStripVisible()){ + act = menuStrips->addAction(tr("Unhide strip: ") + s->getTrack()->name()); + act->setData(i); + h++; + } + i++; + } + if (h==0) { + act = menuStrips->addAction(tr("(no hidden strips)")); + act->setData(UNHANDLED_NUMBER); + } +} + +void AudioMixerApp::handleMenu(QAction *act) +{ + DEBUG_MIXER(stderr, "handleMenu %d\n", act->data().toInt()); + int operation = act->data().toInt(); + if (operation >= 0) { + stripList.at(act->data().toInt())->setStripVisible(true); + } else if (operation == UNHIDE_STRIPS) { + foreach (Strip *s, stripList) { + s->setStripVisible(true); + } + } else if (operation == MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW) { + cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW; + } else if (operation == MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW) { + cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW; + } else if (operation == MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW) { + cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW; + } + redrawMixer(); +} + +bool AudioMixerApp::stripIsVisible(Strip* s) +{ + if (!s->getStripVisible()) + return false; + + MusECore::Track *t = s->getTrack(); + switch (t->type()) + { + case MusECore::Track::AUDIO_SOFTSYNTH: + if (!cfg->showSyntiTracks) + return false; + break; + case MusECore::Track::AUDIO_INPUT: + if (!cfg->showInputTracks) + return false; + break; + case MusECore::Track::AUDIO_OUTPUT: + if (!cfg->showOutputTracks) + return false; + break; + case MusECore::Track::AUDIO_AUX: + if (!cfg->showAuxTracks) + return false; + break; + case MusECore::Track::AUDIO_GROUP: + if (!cfg->showGroupTracks) + return false; + break; + case MusECore::Track::WAVE: + if (!cfg->showWaveTracks) + return false; + break; + case MusECore::Track::MIDI: + case MusECore::Track::DRUM: + case MusECore::Track::NEW_DRUM: + if (!cfg->showMidiTracks) + return false; + break; + + } + return true; +} + +void AudioMixerApp::redrawMixer() +{ + DEBUG_MIXER(stderr, "redrawMixer type %d, mixerLayout count %d\n", cfg->displayOrder, mixerLayout->count()); + // empty layout + while (mixerLayout->count() > 0) { + mixerLayout->removeItem(mixerLayout->itemAt(0)); + } + + switch (cfg->displayOrder) { + case MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW: + { + DEBUG_MIXER(stderr, "Draw strips with arranger view\n"); + MusECore::TrackList *tl = MusEGlobal::song->tracks(); + MusECore::TrackList::iterator tli = tl->begin(); + for (; tli != tl->end(); tli++) { + DEBUG_MIXER(stderr, "Adding strip %s\n", (*tli)->name().toLatin1().data()); + StripList::iterator si = stripList.begin(); + for (; si != stripList.end(); si++) { + if((*si)->getTrack() == *tli) { + addStripToLayoutIfVisible(*si); + } + } + } } + break; + case MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW: + { + DEBUG_MIXER(stderr, "Draw strips with edited view\n"); + // add them back in the selected order + StripList::iterator si = stripList.begin(); + for (; si != stripList.end(); ++si) { + DEBUG_MIXER(stderr, "Adding strip %s\n", (*si)->getTrack()->name().toLatin1().data()); + addStripToLayoutIfVisible(*si); + } + DEBUG_MIXER(stderr, "mixerLayout count is now %d\n", mixerLayout->count()); + } + break; + case MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW: + { + DEBUG_MIXER(stderr, "TRADITIONAL VIEW mixerLayout count is now %d\n", mixerLayout->count()); + addStripsTraditionalLayout(); + } + + break; + } + + // Setup all tabbing for each strip, and between strips. + setupComponentTabbing(); + + update(); +} + +Strip* AudioMixerApp::findStripForTrack(StripList &sl, MusECore::Track *t) +{ + StripList::iterator si = sl.begin(); + for (;si != sl.end(); si++) { + if ((*si)->getTrack() == t) + return *si; + } + DEBUG_MIXER(stderr, "AudioMixerApp::findStripForTrack - ERROR: there was no strip for this track!\n"); + return NULL; +} + +void AudioMixerApp::fillStripListTraditional() +{ + StripList oldList = stripList; + stripList.clear(); + MusECore::TrackList *tl = MusEGlobal::song->tracks(); + + // add Input Strips + MusECore::TrackList::iterator tli = tl->begin(); + for (; tli != tl->end(); ++tli) { + if ((*tli)->type() == MusECore::Track::AUDIO_INPUT) + stripList.append(findStripForTrack(oldList,*tli)); + } + + // Synthesizer Strips + tli = tl->begin(); + for (; tli != tl->end(); ++tli) { + if ((*tli)->type() == MusECore::Track::AUDIO_SOFTSYNTH) + stripList.append(findStripForTrack(oldList,*tli)); + } + + // generate Wave Track Strips + tli = tl->begin(); + for (; tli != tl->end(); ++tli) { + if ((*tli)->type() == MusECore::Track::WAVE) + stripList.append(findStripForTrack(oldList,*tli)); + } + + // generate Midi channel/port Strips + tli = tl->begin(); + for (; tli != tl->end(); ++tli) { + if ((*tli)->type() == MusECore::Track::MIDI || + (*tli)->type() == MusECore::Track::DRUM || + (*tli)->type() == MusECore::Track::NEW_DRUM) + stripList.append(findStripForTrack(oldList,*tli)); + } + + // Groups + tli = tl->begin(); + for (; tli != tl->end(); ++tli) { + if ((*tli)->type() == MusECore::Track::AUDIO_GROUP) + stripList.append(findStripForTrack(oldList,*tli)); + } + + // Aux + tli = tl->begin(); + for (; tli != tl->end(); ++tli) { + if ((*tli)->type() == MusECore::Track::AUDIO_AUX) + stripList.append(findStripForTrack(oldList,*tli)); + } + + // Master + tli = tl->begin(); + for (; tli != tl->end(); ++tli) { + if ((*tli)->type() == MusECore::Track::AUDIO_OUTPUT) + stripList.append(findStripForTrack(oldList,*tli)); + } +} + + +void AudioMixerApp::moveStrip(Strip *s) +{ + DEBUG_MIXER(stderr, "Recreate stripList\n"); + if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW) { + + for (int i=0; i< stripList.size(); i++) + { + Strip *s2 = stripList.at(i); + if (s2 == s) continue; + + DEBUG_MIXER(stderr, "loop loop %d %d width %d\n", s->pos().x(),s2->pos().x(), s2->width()); + if (s->pos().x()+s->width()/2 < s2->pos().x()+s2->width() // upper limit + && s->pos().x()+s->width()/2 > s2->pos().x() ) // lower limit + { + // found relevant pos. + int sTrack = MusEGlobal::song->tracks()->index(s->getTrack()); + int dTrack = MusEGlobal::song->tracks()->index(s2->getTrack()); + MusEGlobal::audio->msgMoveTrack(sTrack, dTrack); + } + } + + } else if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW) + { + fillStripListTraditional(); + cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW; + } + DEBUG_MIXER(stderr, "moveStrip %s! stripList.size = %d\n", s->getLabelText().toLatin1().data(), stripList.size()); + + for (int i=0; i< stripList.size(); i++) + { + Strip *s2 = stripList.at(i); + if (s2 == s) continue; + + DEBUG_MIXER(stderr, "loop loop %d %d width %d\n", s->pos().x(),s2->pos().x(), s2->width()); + if (s->pos().x()+s->width()/2 < s2->pos().x()+s2->width() // upper limit + && s->pos().x()+s->width()/2 > s2->pos().x() ) // lower limit + { + DEBUG_MIXER(stderr, "got new pos: %d\n", i); +#if DEBUG_MIXER + bool isSuccess = stripList.removeOne(s); + DEBUG_MIXER(stderr, "Removed strip %d", isSuccess); +#else + stripList.removeOne(s); +#endif + stripList.insert(i,s); + DEBUG_MIXER(stderr, "Inserted strip at %d", i); + break; + } + } + redrawMixer(); + update(); +} + +void AudioMixerApp::addStripToLayoutIfVisible(Strip *s) +{ + if (stripIsVisible(s)) { + s->setVisible(true); + mixerLayout->addWidget(s); + } else { + s->setVisible(false); + } +} + +void AudioMixerApp::addStripsTraditionalLayout() +{ + // generate Input Strips + StripList::iterator si = stripList.begin(); + for (; si != stripList.end(); ++si) { + if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_INPUT) + addStripToLayoutIfVisible(*si); + } + + // Synthesizer Strips + si = stripList.begin(); + for (; si != stripList.end(); ++si) { + if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_SOFTSYNTH) + addStripToLayoutIfVisible(*si); + } + + // generate Wave Track Strips + si = stripList.begin(); + for (; si != stripList.end(); ++si) { + if ((*si)->getTrack()->type() == MusECore::Track::WAVE) + addStripToLayoutIfVisible(*si); + } + + // generate Midi channel/port Strips + si = stripList.begin(); + for (; si != stripList.end(); ++si) { + if ((*si)->getTrack()->type() == MusECore::Track::MIDI || + (*si)->getTrack()->type() == MusECore::Track::DRUM || + (*si)->getTrack()->type() == MusECore::Track::NEW_DRUM) + addStripToLayoutIfVisible(*si); + } + + // Groups + si = stripList.begin(); + for (; si != stripList.end(); ++si) { + if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_GROUP) + addStripToLayoutIfVisible(*si); + } + + // Aux + si = stripList.begin(); + for (; si != stripList.end(); ++si) { + if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_AUX) + addStripToLayoutIfVisible(*si); + } + + // Master + si = stripList.begin(); + for (; si != stripList.end(); ++si) { + if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_OUTPUT) + addStripToLayoutIfVisible(*si); + } +} /* bool AudioMixerApp::event(QEvent* event) { - printf("AudioMixerApp::event type:%d\n", event->type()); + DEBUG_MIXER(stderr, "AudioMixerApp::event type:%d\n", event->type()); // Let it do the layout now, before we emit. QMainWindow::event(event); @@ -260,283 +631,130 @@ } */ -//void AudioMixerApp::addNewTrack(QAction* action) -//{ - //MusEGlobal::song->addNewTrack(action, MusEGlobal::muse->arranger()->curTrack()); // Insert at current selected track. -// MusEGlobal::song->addNewTrack(action); // Add at end. -//} - void AudioMixerApp::setSizing() { + DEBUG_MIXER(stderr, "setSizing\n"); int w = 0; - StripList::iterator si = stripList.begin(); - for (; si != stripList.end(); ++si) + + w = mixerLayout->minimumSize().width(); + + if(const QStyle* st = style()) { - //w += (*si)->frameGeometry().width(); - //Strip* s = *si; - //printf("AudioMixerApp::setSizing width:%d frame width:%d\n", s->width(), s->frameWidth()); - //w += s->width() + 2 * (s->frameWidth() + s->lineWidth() + s->midLineWidth()); - //w += s->width() + 2 * s->frameWidth(); - w += (*si)->width(); + st = st->proxy(); + w += 2 * st->pixelMetric(QStyle::PM_DefaultFrameWidth); } - //w += 2* style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - // FIXME: When mixer first opened, frameSize is not correct yet, done after main window shown. - w += frameSize().width() - width(); - if(w < 40) w = 40; - setMaximumWidth(w); + view->setUpdatesEnabled(false); + setUpdatesEnabled(false); if(stripList.size() <= 6) - view->setMinimumWidth(w); + setMinimumWidth(w); + + setMaximumWidth(w); + + setUpdatesEnabled(true); + view->setUpdatesEnabled(true); + } //--------------------------------------------------------- // addStrip //--------------------------------------------------------- -void AudioMixerApp::addStrip(MusECore::Track* t, int idx) - { - StripList::iterator si = stripList.begin(); - for (int i = 0; i < idx; ++i) { - if (si != stripList.end()) - ++si; - } - if (si != stripList.end() && (*si)->getTrack() == t) - return; +void AudioMixerApp::addStrip(MusECore::Track* t, bool visible) +{ + DEBUG_MIXER(stderr, "addStrip\n"); + Strip* strip; - std::list::iterator nsi = si; - ++nsi; - if (si != stripList.end() - && nsi != stripList.end() - && (*nsi)->getTrack() == t) { - layout->removeWidget(*si); - delete *si; - stripList.erase(si); - } - else { - Strip* strip; - if (t->isMidiTrack()) - strip = new MidiStrip(central, (MusECore::MidiTrack*)t); - else - strip = new AudioStrip(central, (MusECore::AudioTrack*)t); - layout->insertWidget(idx, strip); - stripList.insert(si, strip); - strip->show(); - } - } + // Make them non-embedded: Moveable, hideable, and with an expander handle. + if (t->isMidiTrack()) + strip = new MidiStrip(central, (MusECore::MidiTrack*)t, true, false); + else + strip = new AudioStrip(central, (MusECore::AudioTrack*)t, true, false); + + // Broadcast changes to other selected tracks. + strip->setBroadcastChanges(true); + + // Set focus yielding to the mixer window. + if(MusEGlobal::config.smartFocus) + { + strip->setFocusYieldWidget(this); + //strip->setFocusPolicy(Qt::WheelFocus); + } + + connect(strip, SIGNAL(clearStripSelection()),this,SLOT(clearStripSelection())); + connect(strip, SIGNAL(moveStrip(Strip*)),this,SLOT(moveStrip(Strip*))); + + DEBUG_MIXER(stderr, "putting new strip [%s] at end\n", t->name().toLatin1().data()); + stripList.append(strip); + strip->setVisible(visible); + strip->setStripVisible(visible); +} //--------------------------------------------------------- -// clear +// clearAndDelete //--------------------------------------------------------- -void AudioMixerApp::clear() - { - StripList::iterator si = stripList.begin(); - for (; si != stripList.end(); ++si) { - layout->removeWidget(*si); - delete *si; - } - stripList.clear(); - oldAuxsSize = -1; - } +void AudioMixerApp::clearAndDelete() +{ + DEBUG_MIXER(stderr, "clearAndDelete\n"); + StripList::iterator si = stripList.begin(); + for (; si != stripList.end(); ++si) + { + mixerLayout->removeWidget(*si); + //(*si)->deleteLater(); + delete (*si); + } + + stripList.clear(); + cfg->stripOrder.clear(); + oldAuxsSize = -1; +} //--------------------------------------------------------- -// updateMixer +// initMixer //--------------------------------------------------------- -void AudioMixerApp::updateMixer(UpdateAction action) - { - //printf("AudioMixerApp::updateMixer action:%d\n", action); - - //name = cfg->name; - //setCaption(name); - setWindowTitle(cfg->name); - - showMidiTracksId->setChecked(cfg->showMidiTracks); - showDrumTracksId->setChecked(cfg->showDrumTracks); - showNewDrumTracksId->setChecked(cfg->showNewDrumTracks); - showInputTracksId->setChecked(cfg->showInputTracks); - showOutputTracksId->setChecked(cfg->showOutputTracks); - showWaveTracksId->setChecked(cfg->showWaveTracks); - showGroupTracksId->setChecked(cfg->showGroupTracks); - showAuxTracksId->setChecked(cfg->showAuxTracks); - showSyntiTracksId->setChecked(cfg->showSyntiTracks); - - int auxsSize = MusEGlobal::song->auxs()->size(); - if ((action == UPDATE_ALL) || (auxsSize != oldAuxsSize)) { - clear(); - oldAuxsSize = auxsSize; - } - else if (action == STRIP_REMOVED) - { - StripList::iterator si = stripList.begin(); - for (; si != stripList.end();) { - MusECore::Track* track = (*si)->getTrack(); - MusECore::TrackList* tl = MusEGlobal::song->tracks(); - MusECore::iTrack it; - for (it = tl->begin(); it != tl->end(); ++it) { - if (*it == track) - break; - } - StripList::iterator ssi = si; - ++si; - if (it != tl->end()) - continue; - layout->removeWidget(*ssi); - delete *ssi; - stripList.erase(ssi); - } - - //printf("AudioMixerApp::updateMixer STRIP_REMOVED\n"); - - //setMaximumWidth(STRIP_WIDTH * stripList.size() + __WIDTH_COMPENSATION); -/// int w = computeWidth(); -/// setMaximumWidth(w); -/// if (stripList.size() < 8) - // view->setMinimumWidth(stripList.size() * STRIP_WIDTH + __WIDTH_COMPENSATION); -/// view->setMinimumWidth(w); - - return; - } - else if (action == UPDATE_MIDI) - { - int i = 0; - int idx = -1; - StripList::iterator si = stripList.begin(); - for (; si != stripList.end(); ++i) - { - MusECore::Track* track = (*si)->getTrack(); - if(!track->isMidiTrack()) - { - ++si; - continue; - } - - if(idx == -1) - idx = i; - - StripList::iterator ssi = si; - ++si; - layout->removeWidget(*ssi); - delete *ssi; - stripList.erase(ssi); - } - - if(idx == -1) - idx = 0; - - //--------------------------------------------------- - // generate Midi channel/port Strips - //--------------------------------------------------- - - MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); - for (MusECore::iMidiTrack i = mtl->begin(); i != mtl->end(); ++i) - { - MusECore::MidiTrack* mt = *i; - if((mt->type() == MusECore::Track::MIDI && cfg->showMidiTracks) || (mt->type() == MusECore::Track::DRUM && cfg->showDrumTracks) || (mt->type() == MusECore::Track::NEW_DRUM && cfg->showNewDrumTracks)) - addStrip(*i, idx++); - } - - //printf("AudioMixerApp::updateMixer UPDATE_MIDI\n"); - - //setMaximumWidth(STRIP_WIDTH * stripList.size() + __WIDTH_COMPENSATION); -/// int w = computeWidth(); -/// setMaximumWidth(w); -/// if (stripList.size() < 8) - // view->setMinimumWidth(stripList.size() * STRIP_WIDTH + __WIDTH_COMPENSATION); -/// view->setMinimumWidth(w); - return; - } - - int idx = 0; - //--------------------------------------------------- - // generate Input Strips - //--------------------------------------------------- - - if(cfg->showInputTracks) - { - MusECore::InputList* itl = MusEGlobal::song->inputs(); - for (MusECore::iAudioInput i = itl->begin(); i != itl->end(); ++i) - addStrip(*i, idx++); - } - - //--------------------------------------------------- - // Synthesizer Strips - //--------------------------------------------------- - - if(cfg->showSyntiTracks) - { - MusECore::SynthIList* sl = MusEGlobal::song->syntis(); - for (MusECore::iSynthI i = sl->begin(); i != sl->end(); ++i) - addStrip(*i, idx++); - } - - //--------------------------------------------------- - // generate Wave Track Strips - //--------------------------------------------------- - - if(cfg->showWaveTracks) - { - MusECore::WaveTrackList* wtl = MusEGlobal::song->waves(); - for (MusECore::iWaveTrack i = wtl->begin(); i != wtl->end(); ++i) - addStrip(*i, idx++); - } - - //--------------------------------------------------- - // generate Midi channel/port Strips - //--------------------------------------------------- - - MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); - for (MusECore::iMidiTrack i = mtl->begin(); i != mtl->end(); ++i) - { - MusECore::MidiTrack* mt = *i; - if((mt->type() == MusECore::Track::MIDI && cfg->showMidiTracks) || (mt->type() == MusECore::Track::DRUM && cfg->showDrumTracks) || (mt->type() == MusECore::Track::NEW_DRUM && cfg->showNewDrumTracks)) - addStrip(*i, idx++); - } - - //--------------------------------------------------- - // Groups - //--------------------------------------------------- - - if(cfg->showGroupTracks) - { - MusECore::GroupList* gtl = MusEGlobal::song->groups(); - for (MusECore::iAudioGroup i = gtl->begin(); i != gtl->end(); ++i) - addStrip(*i, idx++); - } - - //--------------------------------------------------- - // Aux - //--------------------------------------------------- - - if(cfg->showAuxTracks) - { - MusECore::AuxList* al = MusEGlobal::song->auxs(); - for (MusECore::iAudioAux i = al->begin(); i != al->end(); ++i) - addStrip(*i, idx++); - } - - //--------------------------------------------------- - // Master - //--------------------------------------------------- - - if(cfg->showOutputTracks) - { - MusECore::OutputList* otl = MusEGlobal::song->outputs(); - for (MusECore::iAudioOutput i = otl->begin(); i != otl->end(); ++i) - addStrip(*i, idx++); - } - - //printf("AudioMixerApp::updateMixer other\n"); - - //setMaximumWidth(STRIP_WIDTH * idx + __WIDTH_COMPENSATION); -/// int w = computeWidth(); -/// setMaximumWidth(w); -/// if (idx < 8) - // view->setMinimumWidth(idx * STRIP_WIDTH + __WIDTH_COMPENSATION); -/// view->setMinimumWidth(w); - } +void AudioMixerApp::initMixer() +{ + DEBUG_MIXER(stderr, "initMixer %d\n", cfg->stripOrder.size()); + setWindowTitle(cfg->name); + //clearAndDelete(); + + showMidiTracksId->setChecked(cfg->showMidiTracks); + showDrumTracksId->setChecked(cfg->showDrumTracks); + showNewDrumTracksId->setChecked(cfg->showNewDrumTracks); + showInputTracksId->setChecked(cfg->showInputTracks); + showOutputTracksId->setChecked(cfg->showOutputTracks); + showWaveTracksId->setChecked(cfg->showWaveTracks); + showGroupTracksId->setChecked(cfg->showGroupTracks); + showAuxTracksId->setChecked(cfg->showAuxTracks); + showSyntiTracksId->setChecked(cfg->showSyntiTracks); + + int auxsSize = MusEGlobal::song->auxs()->size(); + oldAuxsSize = auxsSize; + MusECore::TrackList *tl = MusEGlobal::song->tracks(); + MusECore::TrackList::iterator tli = tl->begin(); + + if (cfg->stripOrder.size() > 0) { + for (int i=0; i < cfg->stripOrder.size(); i++) { + MusECore::TrackList::iterator tli = tl->begin(); + DEBUG_MIXER(stderr, "processing strip [%s][%d]\n", cfg->stripOrder.at(i).toLatin1().data(), cfg->stripVisibility.at(i)); + for (;tli != tl->end(); tli++) { + if ((*tli)->name() == cfg->stripOrder.at(i)) { + addStrip(*tli, cfg->stripVisibility.at(i)); + break; + } + } + } + } + else { + for (;tli != tl->end(); tli++) { + addStrip(*tli); + } + } +} //--------------------------------------------------------- // configChanged @@ -544,10 +762,106 @@ void AudioMixerApp::configChanged() { - //songChanged(-1); // SC_CONFIG // Catch when fonts change, do full rebuild. + DEBUG_MIXER(stderr, "configChanged\n"); StripList::iterator si = stripList.begin(); // Catch when fonts change, viewable tracks, etc. No full rebuild. p4.0.45 for (; si != stripList.end(); ++si) (*si)->configChanged(); + + // Detect when knobs are preferred. + // TODO: Later if added: Detect when a rack component is added or removed. + // Or other stuff requiring this retabbing. + if(_preferKnobs != MusEGlobal::config.preferKnobsVsSliders) + { + _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; + // Now set up all tabbing on all strips. + setupComponentTabbing(); + } +} + +//--------------------------------------------------------- +// updateStripList +//--------------------------------------------------------- + +void AudioMixerApp::updateStripList() +{ + DEBUG_MIXER(stderr, "updateStripList stripList %d tracks %zd\n", stripList.size(), MusEGlobal::song->tracks()->size()); + + if (stripList.size() == 0 && cfg->stripOrder.size() > 0) { + return initMixer(); + } + + MusECore::TrackList *tl = MusEGlobal::song->tracks(); + // check for superfluous strips + for (StripList::iterator si = stripList.begin(); si != stripList.end(); ) { + MusECore::TrackList::iterator tli = tl->begin(); + bool found = false; + for (; tli != tl->end();++tli) { + if ((*si)->getTrack() == (*tli)) { + found = true; + break; + } + } + if (!found) { + DEBUG_MIXER(stderr, "Did not find track for strip %s - Removing\n", (*si)->getLabelText().toLatin1().data()); + //(*si)->deleteLater(); + delete (*si); + si = stripList.erase(si); + } + else + ++si; + } + + // check for new tracks + MusECore::TrackList::iterator tli = tl->begin(); + for (; tli != tl->end();++tli) { + bool found = false; + StripList::iterator si = stripList.begin(); + for (; si != stripList.end(); ++si) { + if ((*si)->getTrack() == (*tli)) { + found = true; + break; + } + } + if (!found) { + DEBUG_MIXER(stderr, "Did not find strip for track %s - Adding\n", (*tli)->name().toLatin1().data()); + addStrip((*tli)); // TODO: be intelligent about where strip is inserted + } + } +} + +void AudioMixerApp::updateSelectedStrips() +{ + foreach(Strip *s, stripList) + { + if(MusECore::Track* t = s->getTrack()) + { + if(s->isSelected() != t->selected()) + s->setSelected(t->selected()); + } + } +} + +QWidget* AudioMixerApp::setupComponentTabbing(QWidget* previousWidget) +{ + QWidget* prev = previousWidget; + QLayoutItem* li; + QWidget* widget; + Strip* strip; + const int cnt = mixerLayout->count(); + for(int i = 0; i < cnt; ++i) + { + li = mixerLayout->itemAt(i); + if(!li) + continue; + widget = li->widget(); + if(!widget) + continue; + strip = qobject_cast(widget); + if(!strip) + continue; + prev = strip->setupComponentTabbing(prev); + } + return prev; } //--------------------------------------------------------- @@ -555,34 +869,33 @@ //--------------------------------------------------------- void AudioMixerApp::songChanged(MusECore::SongChangedFlags_t flags) - { - // Is it simply a midi controller value adjustment? Forget it. - if(flags == SC_MIDI_CONTROLLER) - return; +{ + DEBUG_MIXER(stderr, "AudioMixerApp::songChanged %llX\n", (long long)flags); + if (flags & SC_TRACK_REMOVED) { + updateStripList(); + } + else if (flags & SC_TRACK_INSERTED) { + updateStripList(); + } + DEBUG_MIXER(stderr, "songChanged action = %ld\n", (long int)flags); - UpdateAction action = NO_UPDATE; - - if (flags == -1) - action = UPDATE_ALL; - else if (flags & SC_TRACK_REMOVED) - action = STRIP_REMOVED; - else if (flags & SC_TRACK_INSERTED) - action = STRIP_INSERTED; - else if (flags & SC_MIDI_TRACK_PROP) - action = UPDATE_MIDI; - - //if (action != NO_UPDATE) - if (action != NO_UPDATE && action != UPDATE_MIDI) // Fix for very slow track prop adjusting. - updateMixer(action); - - if (action != UPDATE_ALL) - { - StripList::iterator si = stripList.begin(); - for (; si != stripList.end(); ++si) { - (*si)->songChanged(flags); - } - } - } + + // This costly to do every time. Try to filter it according to required flags. + // The only flags which would require a redraw, which is very costly, are the following: + if (flags & (SC_TRACK_REMOVED | SC_TRACK_INSERTED | SC_TRACK_MOVED + //| SC_CHANNELS // Channels due to one/two meters and peak labels can change the strip width. + //| SC_AUX + )) + redrawMixer(); + + StripList::iterator si = stripList.begin(); + for (; si != stripList.end(); ++si) { + (*si)->songChanged(flags); + } + + if(flags & SC_TRACK_SELECTION) + updateSelectedStrips(); +} //--------------------------------------------------------- // closeEvent @@ -624,124 +937,227 @@ //--------------------------------------------------------- void AudioMixerApp::routingDialogClosed() - { +{ routingId->setChecked(false); - } +} //--------------------------------------------------------- -// showTracksChanged +// show hide track groups //--------------------------------------------------------- -/* -void AudioMixerApp::showTracksChanged(QAction* id) - { - bool val = id->isOn(); - if (id == showMidiTracksId) - cfg->showMidiTracks = val; - else if (id == showDrumTracksId) - cfg->showDrumTracks = val; - else if (id == showNewDrumTracksId) - cfg->showNewDrumTracks = val; - else if (id == showInputTracksId) - cfg->showInputTracks = val; - else if (id == showOutputTracksId) - cfg->showOutputTracks = val; - else if (id == showWaveTracksId) - cfg->showWaveTracks = val; - else if (id == showGroupTracksId) - cfg->showGroupTracks = val; - else if (id == showAuxTracksId) - cfg->showAuxTracks = val; - else if (id == showSyntiTracksId) - cfg->showSyntiTracks = val; - updateMixer(UPDATE_ALL); - } -*/ - void AudioMixerApp::showMidiTracksChanged(bool v) { - cfg->showMidiTracks = v; - updateMixer(UPDATE_ALL); + cfg->showMidiTracks = v; + redrawMixer(); } - void AudioMixerApp::showDrumTracksChanged(bool v) { - cfg->showDrumTracks = v; - updateMixer(UPDATE_ALL); + cfg->showDrumTracks = v; + redrawMixer(); } - void AudioMixerApp::showNewDrumTracksChanged(bool v) { - cfg->showNewDrumTracks = v; - updateMixer(UPDATE_ALL); + cfg->showNewDrumTracks = v; + redrawMixer(); } - void AudioMixerApp::showWaveTracksChanged(bool v) { - cfg->showWaveTracks = v; - updateMixer(UPDATE_ALL); + cfg->showWaveTracks = v; + redrawMixer(); } - void AudioMixerApp::showInputTracksChanged(bool v) { - cfg->showInputTracks = v; - updateMixer(UPDATE_ALL); + cfg->showInputTracks = v; + redrawMixer(); } - void AudioMixerApp::showOutputTracksChanged(bool v) { - cfg->showOutputTracks = v; - updateMixer(UPDATE_ALL); + cfg->showOutputTracks = v; + redrawMixer(); } - void AudioMixerApp::showGroupTracksChanged(bool v) { - cfg->showGroupTracks = v; - updateMixer(UPDATE_ALL); + cfg->showGroupTracks = v; + redrawMixer(); } - void AudioMixerApp::showAuxTracksChanged(bool v) { - cfg->showAuxTracks = v; - updateMixer(UPDATE_ALL); + cfg->showAuxTracks = v; + redrawMixer(); } - void AudioMixerApp::showSyntiTracksChanged(bool v) { - cfg->showSyntiTracks = v; - updateMixer(UPDATE_ALL); + cfg->showSyntiTracks = v; + redrawMixer(); } //--------------------------------------------------------- // write //--------------------------------------------------------- -//void AudioMixerApp::write(MusECore::Xml& xml, const char* name) void AudioMixerApp::write(int level, MusECore::Xml& xml) -//void AudioMixerApp::write(int level, MusECore::Xml& xml, const char* name) +{ + DEBUG_MIXER(stderr, "AudioMixerApp:;write\n"); + xml.tag(level++, "Mixer"); + + xml.strTag(level, "name", cfg->name); + + xml.qrectTag(level, "geometry", geometry()); + + xml.intTag(level, "showMidiTracks", cfg->showMidiTracks); + xml.intTag(level, "showDrumTracks", cfg->showDrumTracks); + xml.intTag(level, "showNewDrumTracks", cfg->showNewDrumTracks); + xml.intTag(level, "showInputTracks", cfg->showInputTracks); + xml.intTag(level, "showOutputTracks", cfg->showOutputTracks); + xml.intTag(level, "showWaveTracks", cfg->showWaveTracks); + xml.intTag(level, "showGroupTracks", cfg->showGroupTracks); + xml.intTag(level, "showAuxTracks", cfg->showAuxTracks); + xml.intTag(level, "showSyntiTracks", cfg->showSyntiTracks); + + xml.intTag(level, "displayOrder", cfg->displayOrder); + + // specific to store made to song file - this is not part of MixerConfig::write + StripList::iterator si = stripList.begin(); + for (; si != stripList.end(); ++si) { + xml.strTag(level, "StripName", (*si)->getTrack()->name()); + xml.intTag(level, "StripVisible", (*si)->getStripVisible()); + } + + xml.etag(level, "Mixer"); + } + +void AudioMixerApp::keyPressEvent(QKeyEvent *ev) +{ + bool moveEnabled=false; + const bool shift = ev->modifiers() & Qt::ShiftModifier; + const bool alt = ev->modifiers() & Qt::AltModifier; + const bool ctl = ev->modifiers() & Qt::ControlModifier; + if (ctl && alt) { + moveEnabled=true; + } + + switch (ev->key()) { + case Qt::Key_Left: + if (moveEnabled) { - //xml.stag(QString(name)); - //xml.tag(level++, name.toLatin1()); - xml.tag(level++, "Mixer"); - - xml.strTag(level, "name", cfg->name); - - //xml.tag("geometry", geometry()); - xml.qrectTag(level, "geometry", geometry()); - - xml.intTag(level, "showMidiTracks", cfg->showMidiTracks); - xml.intTag(level, "showDrumTracks", cfg->showDrumTracks); - xml.intTag(level, "showNewDrumTracks", cfg->showNewDrumTracks); - xml.intTag(level, "showInputTracks", cfg->showInputTracks); - xml.intTag(level, "showOutputTracks", cfg->showOutputTracks); - xml.intTag(level, "showWaveTracks", cfg->showWaveTracks); - xml.intTag(level, "showGroupTracks", cfg->showGroupTracks); - xml.intTag(level, "showAuxTracks", cfg->showAuxTracks); - xml.intTag(level, "showSyntiTracks", cfg->showSyntiTracks); - - //xml.etag(name); - //xml.etag(level, name.toLatin1()); - xml.etag(level, "Mixer"); + selectNextStrip(false, !shift); + ev->accept(); + return; } + break; + + case Qt::Key_Right: + if (moveEnabled) + { + selectNextStrip(true, !shift); + ev->accept(); + return; + } + break; + + default: + break; + } + + ev->ignore(); + return QMainWindow::keyPressEvent(ev); +} + +void AudioMixerApp::clearStripSelection() +{ + foreach (Strip *s, stripList) + s->setSelected(false); +} + +void AudioMixerApp::selectNextStrip(bool isRight, bool /*clearAll*/) +{ + Strip *prev = NULL; + + for (int i = 0; i < mixerLayout->count(); i++) + { + QWidget *w = mixerLayout->itemAt(i)->widget(); + if (w) + { + if (prev && !prev->isEmbedded() && prev->isSelected() && isRight) // got it + { + Strip* st = static_cast(w); + //if(clearAll) // TODO + { + MusEGlobal::song->selectAllTracks(false); + clearStripSelection(); + } + st->setSelected(true); + if(st->getTrack()) + st->getTrack()->setSelected(true); + MusEGlobal::song->update(SC_TRACK_SELECTION); + return; + } + else if( !static_cast(w)->isEmbedded() && static_cast(w)->isSelected() && prev && !prev->isEmbedded() && !isRight) + { + //if(clearAll) // TODO + { + MusEGlobal::song->selectAllTracks(false); + clearStripSelection(); + } + prev->setSelected(true); + if(prev->getTrack()) + prev->getTrack()->setSelected(true); + MusEGlobal::song->update(SC_TRACK_SELECTION); + return; + } + else { + prev = static_cast(w); + } + } + } + + QWidget *w; + if (isRight) + w = mixerLayout->itemAt(0)->widget(); + else + w = mixerLayout->itemAt(mixerLayout->count()-1)->widget(); + Strip* st = static_cast(w); + if(st && !st->isEmbedded()) + { + //if(clearAll) // TODO + { + MusEGlobal::song->selectAllTracks(false); + clearStripSelection(); + } + st->setSelected(true); + if(st->getTrack()) + st->getTrack()->setSelected(true); + MusEGlobal::song->update(SC_TRACK_SELECTION); + } +} + +bool AudioMixerApp::eventFilter(QObject *obj, + QEvent *event) +{ + DEBUG_MIXER(stderr, "eventFilter type %d\n", (int)event->type()); + QKeyEvent *keyEvent = NULL;//event data, if this is a keystroke event + bool result = false;//return true to consume the keystroke + + if (event->type() == QEvent::KeyPress) + { + keyEvent = dynamic_cast(event); + this->keyPressEvent(keyEvent); + result = true; + }//if type() + + else if (event->type() == QEvent::KeyRelease) + { + keyEvent = dynamic_cast(event); + this->keyReleaseEvent(keyEvent); + result = true; + }//else if type() + + //### Standard event processing ### + else + result = QObject::eventFilter(obj, event); + + return result; +}//eventFilter + } // namespace MusEGui diff -Nru muse-2.1.2/muse/mixer/amixer.h muse-3.0.2+ds1/muse/mixer/amixer.h --- muse-2.1.2/muse/mixer/amixer.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/amixer.h 2017-12-04 21:01:18.000000000 +0000 @@ -39,6 +39,8 @@ class QMenu; class QToolButton; class QWidget; +class QShowEvent; +class QCloseEvent; namespace MusECore { class Xml; @@ -58,7 +60,8 @@ class RouteDialog; class Slider; class Strip; -typedef std::list StripList; + +typedef QList StripList; //--------------------------------------------------------- // ScrollArea @@ -85,15 +88,17 @@ class AudioMixerApp : public QMainWindow { Q_OBJECT - //QString name; + enum StripMenuOperations { + UNHIDE_STRIPS = -1000, + UNHANDLED_NUMBER = -1001 + }; MusEGlobal::MixerConfig* cfg; StripList stripList; + QScrollArea* view; QWidget* central; - QHBoxLayout* lbox; - //Strip* master; - QHBoxLayout* layout; - QMenu* menuView; + QHBoxLayout* mixerLayout; + QMenu* menuStrips; MusEGui::RouteDialog* routingDialog; QAction* routingId; int oldAuxsSize; @@ -108,30 +113,41 @@ QAction* showAuxTracksId; QAction* showSyntiTracksId; - - - virtual void closeEvent(QCloseEvent*); - void addStrip(MusECore::Track*, int); + bool mixerClicked; + // Current local state of knobs versus sliders preference global setting. + bool _preferKnobs; + + bool stripIsVisible(Strip* s); + void redrawMixer(); + void addStrip(MusECore::Track* t, bool visible=true); void showRouteDialog(bool); + void updateStripList(); + void fillStripListTraditional(); + Strip* findStripForTrack(StripList &s, MusECore::Track *t); + void updateSelectedStrips(); + enum UpdateAction { NO_UPDATE, UPDATE_ALL, UPDATE_MIDI, STRIP_INSERTED, STRIP_REMOVED }; - void updateMixer(UpdateAction); - + void initMixer(); + void addStripsTraditionalLayout(); + void addStripToLayoutIfVisible(Strip *s); + void selectNextStrip(bool isRight, bool clearAll = true); + + + bool eventFilter(QObject *obj,QEvent *event); + + signals: void closed(); - //void layoutRequest(); private slots: void songChanged(MusECore::SongChangedFlags_t); - //void configChanged() { songChanged(-1); } void configChanged(); - //void addNewTrack(QAction*); void setSizing(); void toggleRouteDialog(); void routingDialogClosed(); - //void showTracksChanged(QAction*); void showMidiTracksChanged(bool); void showDrumTracksChanged(bool); void showNewDrumTracksChanged(bool); @@ -141,17 +157,25 @@ void showGroupTracksChanged(bool); void showAuxTracksChanged(bool); void showSyntiTracksChanged(bool); + void stripsMenu(); + void handleMenu(QAction *); + void clearStripSelection(); + void moveStrip(Strip*); + + protected: + //virtual bool event(QEvent* event); + virtual void closeEvent(QCloseEvent*); + virtual void keyPressEvent(QKeyEvent*); - //protected: - // virtual bool event(QEvent* event); - public: - //AudioMixerApp(QWidget* parent); AudioMixerApp(QWidget* parent, MusEGlobal::MixerConfig* c); - //void write(Xml&, const char* name); - //void write(int level, Xml& xml, const char* name); void write(int level, MusECore::Xml& xml); - void clear(); + void clearAndDelete(); + + // Sets up tabbing for the entire mixer. Strip by strip. + // Accepts a previousWidget which can be null and returns the last widget in the last strip, + // which allows chaining other widgets. + virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/mixer/astrip.cpp muse-3.0.2+ds1/muse/mixer/astrip.cpp --- muse-2.1.2/muse/mixer/astrip.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/astrip.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: astrip.cpp,v 1.23.2.17 2009/11/16 01:55:55 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,19 +22,16 @@ // //========================================================= -#include #include #include #include +#include #include -//#include #include -#include #include #include #include -//#include #include #include #include @@ -44,77 +41,821 @@ #include #include #include -//#include #include "app.h" #include "globals.h" #include "audio.h" -//#include "driver/audiodev.h" +#include "midi.h" #include "song.h" #include "slider.h" -#include "knob.h" +#include "compact_knob.h" +#include "compact_slider.h" #include "combobox.h" #include "meter.h" #include "astrip.h" #include "track.h" #include "synth.h" -//#include "route.h" #include "doublelabel.h" #include "rack.h" #include "node.h" #include "amixer.h" #include "icons.h" #include "gconfig.h" -#include "ttoolbutton.h" +#include "pixmap_button.h" #include "menutitleitem.h" -//#include "popupmenu.h" #include "routepopup.h" +#include "ctrl.h" +#include "utils.h" +#include "muse_math.h" +#include "operations.h" + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_AUDIO_STRIP(dev, format, args...) //fprintf(dev, format, ##args); + namespace MusEGui { + +const double AudioStrip::volSliderStep = 0.5; +const double AudioStrip::volSliderMax = 10.0; +const int AudioStrip::volSliderPrec = 1; + +const double AudioStrip::auxSliderStep = 1.0; +const double AudioStrip::auxSliderMax = 10.0; +const int AudioStrip::auxSliderPrec = 0; + +const double AudioStrip::gainSliderStep = 0.1; +const double AudioStrip::gainSliderMin = 0.5; +const double AudioStrip::gainSliderMax = 20.0; +const int AudioStrip::gainSliderPrec = 1; + +const int AudioStrip::xMarginHorSlider = 1; +const int AudioStrip::yMarginHorSlider = 1; +const int AudioStrip::upperRackSpacerHeight = 2; +const int AudioStrip::rackFrameWidth = 1; -/* //--------------------------------------------------------- -// minimumSizeHint +// AudioComponentRack //--------------------------------------------------------- -QSize AudioStrip::minimumSizeHint () const +AudioComponentRack::AudioComponentRack(MusECore::AudioTrack* track, int id, bool manageAuxs, QWidget* parent, Qt::WindowFlags f) + : ComponentRack(id, parent, f), _track(track), _manageAuxs(manageAuxs) +{ + +} + +void AudioComponentRack::newComponent( ComponentDescriptor* desc, const ComponentWidget& before ) +{ + double min = 0.0; + double max = 0.0; + double val = 0.0; + int prec = 0.0; + double step = 0.0; + bool showval = MusEGlobal::config.showControlValues;; + + switch(desc->_componentType) + { + case aStripAuxComponent: + { + val = _track->auxSend(desc->_index); + //if(val == 0.0) + if(val < MusEGlobal::config.minSlider) + val = MusEGlobal::config.minSlider; + else + { + val = muse_val2dbr(val); + if(val < MusEGlobal::config.minSlider) + val = MusEGlobal::config.minSlider; + } + min = MusEGlobal::config.minSlider; + max = AudioStrip::auxSliderMax; + prec = AudioStrip::auxSliderPrec; + step = AudioStrip::auxSliderStep; + + // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! + // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. + desc->_enabled = _track->auxRefCount() == 0; + + if(!desc->_color.isValid()) + desc->_color = MusEGlobal::config.auxSliderColor; + + if(desc->_label.isEmpty()) + { + // the thought was to acquire the correct Aux name for each Aux + // now they are only called Aux1, Aux2, which isn't too usable. + desc->_label = ((MusECore::AudioAux*)(MusEGlobal::song->auxs()->at(desc->_index)))->auxName(); + if (desc->_label.length() > 8) { // shorten name + desc->_label = desc->_label.mid(0,8) + "."; + } + } + if(desc->_toolTipText.isEmpty()) + desc->_toolTipText = tr("Aux send level (dB)"); + } + break; + + case controllerComponent: + { + MusECore::iCtrlList ic = _track->controller()->find(desc->_index); + if(ic == _track->controller()->end()) + return; + MusECore::CtrlList* cl = ic->second; + val = _track->pluginCtrlVal(desc->_index); + cl->range(&min, &max); + prec = 2; + step = 0.01; + + if(desc->_label.isEmpty()) + { + switch(desc->_index) + { + case MusECore::AC_VOLUME: + desc->_label = tr("Vol"); + break; + + case MusECore::AC_PAN: + desc->_label = tr("Pan"); + break; + + case MusECore::AC_MUTE: + desc->_label = tr("Mute"); + break; + + default: + desc->_label = cl->name(); + break; + } + } + + if(desc->_toolTipText.isEmpty()) + { + switch(desc->_index) + { + case MusECore::AC_VOLUME: + desc->_toolTipText = tr("Volume/gain"); + break; + + case MusECore::AC_PAN: + desc->_toolTipText = tr("Panorama/Balance"); + break; + + case MusECore::AC_MUTE: + desc->_toolTipText = tr("Mute"); + break; + + default: + desc->_toolTipText = cl->name(); + break; + } + } + + if(!desc->_color.isValid()) + { + switch(desc->_index) + { + case MusECore::AC_PAN: + desc->_color = MusEGlobal::config.panSliderColor; + break; + + default: + desc->_color = MusEGlobal::config.audioControllerSliderDefaultColor; + break; + } + } + } + break; + + case propertyComponent: + { + switch(desc->_index) + { + case aStripGainProperty: + { + val = _track->gain(); + min = AudioStrip::gainSliderMin; + max = AudioStrip::gainSliderMax; + prec = AudioStrip::gainSliderPrec; + step = AudioStrip::gainSliderStep; + if(desc->_label.isEmpty()) + desc->_label = tr("Gain"); + if(desc->_toolTipText.isEmpty()) + desc->_toolTipText = tr("Calibration gain"); + if(!desc->_color.isValid()) + desc->_color = MusEGlobal::config.gainSliderColor; + } + break; + + default: + if(!desc->_color.isValid()) + desc->_color = MusEGlobal::config.audioPropertySliderDefaultColor; + break; + } + } + break; + } + + switch(desc->_widgetType) + { + case CompactKnobComponentWidget: + { + CompactKnobComponentDescriptor* d = static_cast(desc); + d->_min = min; + d->_max = max; + d->_precision = prec; + d->_step = step; + d->_initVal = val; + d->_showValue = showval; + if(!d->_color.isValid()) + d->_color = MusEGlobal::config.sliderDefaultColor; + + // Adds a component. Creates a new component using the given desc values if the desc widget is not given. + // Connects known widget types' signals to slots. + newComponentWidget(d, before); + + // Handle special slots for audio strip. + switch(desc->_componentType) + { + case aStripAuxComponent: + { + if(d->_compactKnob->specialValueText().isEmpty()) + { + d->_compactKnob->setSpecialValueText(QString('-') + QString(QChar(0x221e))); // The infinity character + } + + connect(d->_compactKnob, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(auxChanged(double,bool,int,int))); + connect(d->_compactKnob, SIGNAL(sliderMoved(double,int,bool)), SLOT(auxMoved(double,int,bool))); + connect(d->_compactKnob, SIGNAL(sliderPressed(double, int)), SLOT(auxPressed(double, int))); + connect(d->_compactKnob, SIGNAL(sliderReleased(double, int)), SLOT(auxReleased(double, int))); + connect(d->_compactKnob, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(auxRightClicked(QPoint,int))); + } + break; + } + } + break; + + case CompactSliderComponentWidget: + { + CompactSliderComponentDescriptor* d = static_cast(desc); + d->_min = min; + d->_max = max; + d->_precision = prec; + d->_step = step; + d->_initVal = val; + d->_showValue = showval; + if(!d->_color.isValid()) + d->_color = MusEGlobal::config.sliderDefaultColor; + // Set the bar color the same. + if(!d->_barColor.isValid()) + //d->_barColor = d->_color; + d->_barColor = MusEGlobal::config.sliderBarDefaultColor; + + // Adds a component. Creates a new component using the given desc values if the desc widget is not given. + // Connects known widget types' signals to slots. + newComponentWidget(d, before); + + // Handle special slots for audio strip. + switch(desc->_componentType) + { + case aStripAuxComponent: + { + if(d->_compactSlider->specialValueText().isEmpty()) + { + d->_compactSlider->setSpecialValueText(QString('-') + QString(QChar(0x221e))); // The infinity character + } + + connect(d->_compactSlider, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(auxChanged(double,bool,int,int))); + connect(d->_compactSlider, SIGNAL(sliderMoved(double,int,bool)), SLOT(auxMoved(double,int,bool))); + connect(d->_compactSlider, SIGNAL(sliderPressed(double, int)), SLOT(auxPressed(double, int))); + connect(d->_compactSlider, SIGNAL(sliderReleased(double, int)), SLOT(auxReleased(double, int))); + connect(d->_compactSlider, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(auxRightClicked(QPoint,int))); + } + break; + } + } + break; + } +} + +void AudioComponentRack::scanControllerComponents() +{ + std::vector to_be_erased; + for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + ComponentWidget& cw = *ic; + if(!cw._widget) + continue; + + switch(cw._componentType) + { + case controllerComponent: + { + MusECore::iCtrlList ictrl = _track->controller()->find(cw._index); + if(ictrl == _track->controller()->end()) + to_be_erased.push_back(ic); + } + break; + } + } + for(std::vector::iterator i = to_be_erased.begin(); i != to_be_erased.end(); ++i) + { + iComponentWidget icw = *i; + ComponentWidget& cw = *icw; + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::scanControllerComponents: deleting controller component index:%d\n", cw._index); + if(cw._widget) + delete cw._widget; + _components.erase(icw); + } +} + +void AudioComponentRack::scanAuxComponents() +{ + std::vector to_be_erased; + for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + ComponentWidget& cw = *ic; + if(!cw._widget) + continue; + + switch(cw._componentType) + { + case aStripAuxComponent: + { + // TODO: This is just brute-force deletion and recreation of all the auxs. + // Make this more efficient by only removing what's necessary and updating/re-using the rest. + to_be_erased.push_back(ic); + } + break; + } + } + for(std::vector::iterator i = to_be_erased.begin(); i != to_be_erased.end(); ++i) + { + iComponentWidget icw = *i; + ComponentWidget& cw = *icw; + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::scanAuxComponents: deleting aux component index:%d\n", cw._index); + if(cw._widget) + delete cw._widget; + _components.erase(icw); + } + + // Add auxs, only if we want this rack to manage auxs. + if(_manageAuxs) + { + int auxsSize = MusEGlobal::song->auxs()->size(); + if(_track->hasAuxSend()) + { + for (int idx = 0; idx < auxsSize; ++idx) + { + // the thought was to acquire the correct Aux name for each Aux + // now they are only called Aux1, Aux2, which isn't too usable. +// QString title = ((MusECore::AudioAux*)(MusEGlobal::song->auxs()->at(idx)))->auxName(); +// if (title.length() > 8) { // shorten name +// title = title.mid(0,8) + "."; +// } + + // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! + // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. +// const bool enable = _track->auxRefCount() == 0; + + if(MusEGlobal::config.preferKnobsVsSliders) + { + CompactKnobComponentDescriptor aux_desc + ( + aStripAuxComponent, + "MixerStripAudioAux", + idx + ); + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::scanAuxComponents: adding aux component index:%d\n", idx); + newComponent(&aux_desc); + } + else + { + CompactSliderComponentDescriptor aux_desc + ( + aStripAuxComponent, + "MixerStripAudioAux", + idx + ); + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::scanAuxComponents: adding aux component index:%d\n", idx); + newComponent(&aux_desc); + } + } + } + } +} + +void AudioComponentRack::updateComponents() +{ + for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + ComponentWidget& cw = *ic; + if(!cw._widget) + continue; + + switch(cw._componentType) + { + case controllerComponent: + { + // Inhibit the controller stream if control is currently pressed. + // Note _pressed operates differently than simply checking if the control is pressed! + if(cw._pressed) + continue; + const double val = _track->pluginCtrlVal(cw._index); + setComponentValue(cw, val); // Signals blocked. Redundant ignored. + } + break; + + case propertyComponent: + { + switch(cw._index) + { + case aStripGainProperty: + { + const double val = _track->gain(); + setComponentValue(cw, val); // Signals blocked. Redundant ignored. + } + break; + } + } + break; + + case aStripAuxComponent: + { + double val = _track->auxSend(cw._index); + if(val == 0.0) + val = MusEGlobal::config.minSlider; + else + { + val = muse_val2dbr(val); + if(val < MusEGlobal::config.minSlider) + val = MusEGlobal::config.minSlider; + } + setComponentValue(cw, val); // Signals blocked. Redundant ignored. + } + break; + } + } +} + +void AudioComponentRack::setAuxEnabled(bool enable) { - // We force the width of the size hint to be what we want - //return QWidget::minimumSizeHint(); - ///return QSize(66,QWidget::minimumSizeHint().height()); + for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + ComponentWidget& cw = *ic; + switch(cw._componentType) + { + case aStripAuxComponent: + setComponentEnabled(cw, enable); + break; + } + } +} + + +void AudioComponentRack::controllerChanged(double val, bool off, int id, int scrollMode) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerChanged id:%d val:%.20f scrollMode:%d\n", id, val, scrollMode); + // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. + // ScrDirect mode is one-time only on press with modifier. + if(scrollMode != SliderBase::ScrDirect) + _track->recordAutomation(id, val); + _track->setParam(id, val); // Schedules a timed control change. + //_track->setPluginCtrlVal(id, val); // TODO Try this instead. setParam gives a slight jump at release, in tracking off temp mode. + _track->enableController(id, false); + + emit componentChanged(controllerComponent, val, off, id, scrollMode); +} + +void AudioComponentRack::controllerMoved(double val, int id, bool shift_pressed) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerMoved id:%d val:%.20f\n", id, val); + emit componentMoved(controllerComponent, val, id, shift_pressed); +} + +void AudioComponentRack::controllerPressed(double v, int id) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerPressed id:%d\n", id); + double val = 0.0; + iComponentWidget ic = _components.find(controllerComponent, -1, id); + if(ic != _components.end()) + { + ComponentWidget& cw = *ic; + cw._pressed = true; + val = componentValue(cw); + DEBUG_AUDIO_STRIP(stderr, " val:%.20f\n", val); + } + _track->startAutoRecord(id, val); + _track->setPluginCtrlVal(id, val); + //_track->setParam(id, val); // Schedules a timed control change. // TODO Try this instead + DEBUG_AUDIO_STRIP(stderr, " calling enableController(false)\n"); + _track->enableController(id, false); + + emit componentPressed(controllerComponent, v, id); +} + +void AudioComponentRack::controllerReleased(double v, int id) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerReleased id:%d\n", id); + AutomationType at = _track->automationType(); + double val = 0.0; + iComponentWidget ic = _components.find(controllerComponent, -1, id); + if(ic != _components.end()) + { + ComponentWidget& cw = *ic; + val = componentValue(cw); + DEBUG_AUDIO_STRIP(stderr, " val:%.20f\n", val); + cw._pressed = false; + } + _track->stopAutoRecord(id, val); + if(at == AUTO_OFF || at == AUTO_TOUCH) + { + DEBUG_AUDIO_STRIP(stderr, " calling enableController(true)\n"); + _track->enableController(id, true); + } + + emit componentReleased(controllerComponent, v, id); +} + +void AudioComponentRack::controllerRightClicked(QPoint p, int id) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerRightClicked id:%d\n", id); + MusEGlobal::song->execAutomationCtlPopup(_track, p, id); +} + + +void AudioComponentRack::propertyChanged(double val, bool off, int id, int scrollMode) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::propertyChanged id:%d val:%.20f\n", id, val); + switch(id) + { + case aStripGainProperty: + if(_track->gain() != val) + _track->setGain(val); // FIXME: Realtime safe? + break; + } + + emit componentChanged(propertyComponent, val, off, id, scrollMode); +} + +void AudioComponentRack::propertyMoved(double val, int id, bool shift_pressed) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::propertyMoved id:%d val:%.20f\n", id, val); + emit componentMoved(propertyComponent, val, id, shift_pressed); +} + +void AudioComponentRack::propertyPressed(double val, int id) +{ + emit componentPressed(propertyComponent, val, id); +} + +void AudioComponentRack::propertyReleased(double val, int id) +{ + emit componentReleased(propertyComponent, val, id); +} + +void AudioComponentRack::propertyRightClicked(QPoint, int) +{ + +} + +void AudioComponentRack::auxChanged(double val, bool off, int id, int scrollMode) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::auxChanged id:%d val:%.20f\n", id, val); + double vol; + if (val <= MusEGlobal::config.minSlider) + vol = 0.0; + else + vol = muse_db2val(val); + MusEGlobal::audio->msgSetAux(_track, id, vol); + + emit componentChanged(aStripAuxComponent, val, off, id, scrollMode); +} + +void AudioComponentRack::auxMoved(double val, int id, bool shift_pressed) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::auxMoved id:%d val:%.20f\n", id, val); + emit componentMoved(aStripAuxComponent, val, id, shift_pressed); +} + +void AudioComponentRack::auxPressed(double val, int id) +{ + emit componentPressed(aStripAuxComponent, val, id); +} + +void AudioComponentRack::auxReleased(double val, int id) +{ + emit componentReleased(aStripAuxComponent, val, id); +} + +void AudioComponentRack::auxRightClicked(QPoint, int) +{ + } //--------------------------------------------------------- -// sizeHint +// songChanged //--------------------------------------------------------- -QSize AudioStrip::sizeHint () const +void AudioComponentRack::songChanged(MusECore::SongChangedFlags_t flags) { - // We force the width of the size hint to be what we want - //return QWidget::minimumSizeHint(); - //return QSize(66,QWidget::minimumSizeHint().height()); - return minimumSizeHint(); + // Scan controllers. + if(flags & (SC_RACK | SC_AUDIO_CONTROLLER_LIST)) + { + scanControllerComponents(); + } + + // Take care of scanning aux before setting aux enabled below. + if(flags & SC_AUX) + { + scanAuxComponents(); + } + + if(flags & SC_ROUTE) { + // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! + // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. + setAuxEnabled(_track->auxRefCount() == 0); + } } -*/ //--------------------------------------------------------- -// heartBeat +// configChanged +// Catch when label font, or configuration min slider and meter values change, or viewable tracks etc. //--------------------------------------------------------- -void AudioStrip::heartBeat() +void AudioComponentRack::configChanged() +{ + // Handle font changes etc. + ComponentRack::configChanged(); + + for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + ComponentWidget& cw = *ic; + + // Whether to show values along with labels for certain controls. + setComponentShowValue(cw, MusEGlobal::config.showControlValues); + + switch(cw._componentType) + { + // Special for Aux controls. + case aStripAuxComponent: + // Adjust aux minimum value. + setComponentRange(cw, MusEGlobal::config.minSlider, AudioStrip::auxSliderMax, true, AudioStrip::auxSliderStep); + break; + } + } + setComponentColors(); +} + +//--------------------------------------------------------- +// setComponentColors +//--------------------------------------------------------- + +void AudioComponentRack::setComponentColors() +{ + for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + const ComponentWidget& cw = *ic; + if(!cw._widget) + continue; + + QColor color = MusEGlobal::config.sliderDefaultColor; + switch(cw._componentType) + { + case aStripAuxComponent: + color = MusEGlobal::config.auxSliderColor; + break; + + case controllerComponent: { - for (int ch = 0; ch < track->channels(); ++ch) { - if (meter[ch]) { - //int meterVal = track->meter(ch); - //int peak = track->peak(ch); - //meter[ch]->setVal(meterVal, peak, false); - meter[ch]->setVal(track->meter(ch), track->peak(ch), false); - } + switch(cw._index) + { + case MusECore::AC_PAN: + color = MusEGlobal::config.panSliderColor; + break; + + default: + color = MusEGlobal::config.audioControllerSliderDefaultColor; + break; } - Strip::heartBeat(); - updateVolume(); - updatePan(); - } + } + break; + + case propertyComponent: + { + switch(cw._index) + { + case aStripGainProperty: + color = MusEGlobal::config.gainSliderColor; + break; + + default: + color = MusEGlobal::config.audioPropertySliderDefaultColor; + break; + } + } + break; + } + + switch(cw._widgetType) + { + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + w->setFaceColor(color); + } + break; + + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + w->setBorderColor(color); + //w->setBarColor(color); + w->setBarColor(MusEGlobal::config.sliderBarDefaultColor); + } + break; + } + } +} + +//--------------------------------------------------------- +// AudioStrip +//--------------------------------------------------------- + +//--------------------------------------------------------- +// heartBeat +//--------------------------------------------------------- + +void AudioStrip::heartBeat() +{ + const int tch = track->channels(); + for (int ch = 0; ch < tch; ++ch) { + if (meter[ch]) { + meter[ch]->setVal(track->meter(ch), track->peak(ch), false); + } + if(_clipperLabel[ch]) + { + _clipperLabel[ch]->setVal(track->peak(ch)); + _clipperLabel[ch]->setClipped(track->isClipped(ch)); + } + } + updateVolume(); + _upperRack->updateComponents(); + _infoRack->updateComponents(); + _lowerRack->updateComponents(); + +// if(_recMonitor && _recMonitor->isChecked() && MusEGlobal::blinkTimerPhase != _recMonitor->blinkPhase()) +// _recMonitor->setBlinkPhase(MusEGlobal::blinkTimerPhase); + + Strip::heartBeat(); +} + +void AudioStrip::updateRackSizes(bool upper, bool lower) +{ +// const QFontMetrics fm = fontMetrics(); + if(upper) + { + // Make room for 3 CompactSliders and one CompactPatchEdit. + // TODO: Add the instrument select label height! + +// // const int csh = CompactSlider::getMinimumSizeHint(fm, +// // Qt::Horizontal, +// // CompactSlider::None, +// // xMarginHorSlider, yMarginHorSlider).height(); +// // const int cpeh = CompactPatchEdit::getMinimumSizeHint(fm, +// // Qt::Horizontal, +// // CompactSlider::None, +// // xMarginHorSlider, yMarginHorSlider).height(); +// // const int ilh = _instrLabel->sizeHint().height(); +// +// // DEBUG_AUDIO_STRIP(stderr, "MidiStrip::updateRackSizes: CompactSlider h:%d CompactPatchEdit h:%d instrLabel h:%d upper frame w:%d \n", +// // csh, cpeh, ilh, _upperRack->frameWidth()); + +// _upperRack->setMinimumHeight( +// 3 * CompactSlider::getMinimumSizeHint(fm, +// Qt::Horizontal, +// CompactSlider::None, +// xMarginHorSlider, yMarginHorSlider).height() + +// 1 * CompactPatchEdit::getMinimumSizeHint(fm, +// Qt::Horizontal, +// CompactSlider::None, +// xMarginHorSlider, yMarginHorSlider).height() + +// upperRackSpacerHeight + +// +// _instrLabel->sizeHint().height() + +// +// 2 * rackFrameWidth); + } + if(lower) + { + // Make room for 1 CompactSlider (Pan, so far). + + //DEBUG_AUDIO_STRIP(stderr, "MidiStrip::updateRackSizes: lower frame w:%d \n", _lowerRack->frameWidth()); + +// _lowerRack->setMinimumHeight( +// 1 * CompactSlider::getMinimumSizeHint(fm, +// Qt::Horizontal, +// CompactSlider::None, +// xMarginHorSlider, yMarginHorSlider).height() + +// 2 * rackFrameWidth); + } +} //--------------------------------------------------------- // configChanged @@ -123,49 +864,58 @@ void AudioStrip::configChanged() { + // Detect when knobs are preferred and rebuild. + if(_preferKnobs != MusEGlobal::config.preferKnobsVsSliders) + { + _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; + // Rebuild the strip components. + buildStrip(); + // Now set up all tabbing on the strip. + // Don't bother if the strip is part of the mixer (not embedded), + // the non-embedding parent (mixer) should set up all the tabs and make this call. + if(isEmbedded()) + setupComponentTabbing(); + } + // Set the whole strip's font, except for the label. if(font() != MusEGlobal::config.fonts[1]) + { setFont(MusEGlobal::config.fonts[1]); + DEBUG_AUDIO_STRIP(stderr, "AudioStrip::configChanged changing font: current size:%d\n", font().pointSize()); + setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); + } // Set the strip label's font. - setLabelFont(); setLabelText(); - + + slider->setFillColor(MusEGlobal::config.audioVolumeSliderColor); // Adjust minimum volume slider and label values. - slider->setRange(MusEGlobal::config.minSlider-0.1, 10.0); - sl->setRange(MusEGlobal::config.minSlider, 10.0); - - // Adjust minimum aux knob and label values. - int n = auxKnob.size(); - for (int idx = 0; idx < n; ++idx) - { - auxKnob[idx]->blockSignals(true); - auxLabel[idx]->blockSignals(true); - auxKnob[idx]->setRange(MusEGlobal::config.minSlider-0.1, 10.0); - auxLabel[idx]->setRange(MusEGlobal::config.minSlider, 10.1); - auxKnob[idx]->blockSignals(false); - auxLabel[idx]->blockSignals(false); - } + slider->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep); + slider->setScale(MusEGlobal::config.minSlider, volSliderMax, 6.0, false); + + sl->setRange(MusEGlobal::config.minSlider, volSliderMax); + sl->setOff(MusEGlobal::config.minSlider); + // Enable special hack for line edits. + if(sl->enableStyleHack() != MusEGlobal::config.lineEditStyleHack) + sl->setEnableStyleHack(MusEGlobal::config.lineEditStyleHack); + + _upperRack->configChanged(); + _infoRack->configChanged(); + _lowerRack->configChanged(); - // Adjust minimum meter values. + // Adjust minimum meter values, and colours. for(int c = 0; c < channel; ++c) - meter[c]->setRange(MusEGlobal::config.minMeter, 10.0); -} - -void AudioStrip::updateRouteButtons() -{ - if (iR) - { - if (track->noInRoute()) - iR->setStyleSheet("background-color:darkgray;"); - else - iR->setStyleSheet(""); - } + { + meter[c]->setRange(MusEGlobal::config.minMeter, volSliderMax); + meter[c]->setPrimaryColor(MusEGlobal::config.audioMeterPrimaryColor); + meter[c]->setRefreshRate(MusEGlobal::config.guiRefresh); + } - if (track->noOutRoute()) - oR->setStyleSheet("background-color:red;"); - else - oR->setStyleSheet(""); + // If smart focus is on redirect strip focus to slider label. +// if(MusEGlobal::config.smartFocus) +// setFocusProxy(sl); +// else +// setFocusProxy(0); } //--------------------------------------------------------- @@ -174,14 +924,7 @@ void AudioStrip::songChanged(MusECore::SongChangedFlags_t val) { - // Is it simply a midi controller value adjustment? Forget it. - if (val == SC_MIDI_CONTROLLER) - return; - - updateRouteButtons(); - - MusECore::AudioTrack* src = (MusECore::AudioTrack*)track; - gain->setValue(src->gain()); + MusECore::AudioTrack* src = static_cast(track); // Do channels before MusEGlobal::config... if (val & SC_CHANNELS) @@ -197,59 +940,51 @@ mute->blockSignals(true); mute->setChecked(src->mute()); mute->blockSignals(false); - mute->setIcon(src->mute() ? QIcon(*muteIconOff) : QIcon(*muteIconOn)); - //mute->setIconSize(muteIconOn->size()); + updateMuteIcon(); updateOffState(); } - if (solo && (val & SC_SOLO)) { + if (solo && (val & (SC_SOLO | SC_ROUTE))) { solo->blockSignals(true); solo->setChecked(track->solo()); solo->blockSignals(false); - if(track->internalSolo()) - solo->setIcon(track->solo() ? QIcon(*soloblksqIconOn) : QIcon(*soloblksqIconOff)); - else - solo->setIcon(track->solo() ? QIcon(*soloIconOn) : QIcon(*soloIconOff)); - //solo->setIconSize(soloIconOn->size()); + solo->setIconSetB(track->internalSolo()); + updateMuteIcon(); } if (val & SC_RECFLAG) + { setRecordFlag(track->recordFlag()); + } if (val & SC_TRACK_MODIFIED) { setLabelText(); - setLabelFont(); - } //if (val & SC_CHANNELS) // updateChannels(); if (val & SC_ROUTE) { + updateRouteButtons(); if (pre) { pre->blockSignals(true); pre->setChecked(src->prefader()); pre->blockSignals(false); } - - // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! - // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. - int rc = track->auxRefCount(); - int n = auxKnob.size(); - for (int idx = 0; idx < n; ++idx) - { - auxKnob[idx]->setEnabled( rc == 0 ); - auxLabel[idx]->setEnabled( rc == 0 ); - } } - if (val & SC_AUX) { - int n = auxKnob.size(); - for (int idx = 0; idx < n; ++idx) { - double val = MusECore::fast_log10(src->auxSend(idx)) * 20.0; - auxKnob[idx]->blockSignals(true); - auxLabel[idx]->blockSignals(true); - auxKnob[idx]->setValue(val); - auxLabel[idx]->setValue(val); - auxKnob[idx]->blockSignals(false); - auxLabel[idx]->blockSignals(false); - } - } + + if(val & SC_TRACK_REC_MONITOR) + { + // Set record monitor. + if(_recMonitor) // && (_recMonitor->isChecked() != track->recMonitor())) + { + _recMonitor->blockSignals(true); + _recMonitor->setChecked(track->recMonitor()); + _recMonitor->blockSignals(false); + } + } + // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! + // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. + _upperRack->songChanged(val); + _infoRack->songChanged(val); + _lowerRack->songChanged(val); + if (autoType && (val & SC_AUTOMATION)) { autoType->blockSignals(true); autoType->setCurrentItem(track->automationType()); @@ -258,40 +993,16 @@ if(track->automationType() == AUTO_TOUCH || track->automationType() == AUTO_WRITE) { palette.setColor(QPalette::Button, QColor(215, 76, 39)); // red - //palette.setColor(QPalette::Window, QColor(215, 76, 39)); // red - /*QLinearGradient gradient(autoType->geometry().topLeft(), autoType->geometry().bottomLeft()); - QColor c(Qt::red); - //QColor c(215, 76, 39); // red - gradient.setColorAt(0, c.darker()); - gradient.setColorAt(0.5, c); - gradient.setColorAt(1, c.darker()); - palette.setBrush(QPalette::Button, gradient); - //palette.setBrush(autoType->backgroundRole(), gradient); - //palette.setBrush(QPalette::Window, gradient); */ autoType->setPalette(palette); } else if(track->automationType() == AUTO_READ) { palette.setColor(QPalette::Button, QColor(100, 172, 49)); // green - //palette.setColor(QPalette::Window, QColor(100, 172, 49)); // green - /*QLinearGradient gradient(autoType->geometry().topLeft(), autoType->geometry().bottomLeft()); - QColor c(Qt::green); - //QColor c(100, 172, 49); // green - gradient.setColorAt(0, c.darker()); - gradient.setColorAt(0.5, c); - gradient.setColorAt(1, c.darker()); - palette.setBrush(QPalette::Button, gradient); - //palette.setBrush(autoType->backgroundRole(), gradient); - //palette.setBrush(QPalette::Window, gradient); */ autoType->setPalette(palette); } else { palette.setColor(QPalette::Button, qApp->palette().color(QPalette::Active, QPalette::Background)); - //QColor c(qApp->palette().color(QPalette::Active, QPalette::Background)); - //gradient.setColorAt(0, c); - //gradient.setColorAt(1, c.darker()); - //palette.setBrush(QPalette::Button, gradient); autoType->setPalette(palette); } @@ -305,51 +1016,44 @@ void AudioStrip::updateVolume() { - double vol = ((MusECore::AudioTrack*)track)->volume(); - if (vol != volume) - { - //printf("AudioStrip::updateVolume setting slider and label\n"); + if(_volPressed) // Inhibit the controller stream if control is currently pressed. + return; + double vol = static_cast(track)->volume(); + if (vol != volume) + { + double val; + if(vol == 0.0) + val = MusEGlobal::config.minSlider; + else + { + val = muse_val2dbr(vol); + if(val < MusEGlobal::config.minSlider) + val = MusEGlobal::config.minSlider; + } - slider->blockSignals(true); - sl->blockSignals(true); - double val = MusECore::fast_log10(vol) * 20.0; - slider->setValue(val); - sl->setValue(val); - sl->blockSignals(false); - slider->blockSignals(false); - volume = vol; - } + slider->blockSignals(true); + sl->blockSignals(true); + // Slider::fitValue should not be required since the log function is accurate but rounded to the nearest .000001 + slider->setValue(val); + sl->setValue(val); + sl->blockSignals(false); + slider->blockSignals(false); + volume = vol; + } } //--------------------------------------------------------- -// updatePan -//--------------------------------------------------------- - -void AudioStrip::updatePan() -{ - double v = ((MusECore::AudioTrack*)track)->pan(); - if (v != panVal) - { - //printf("AudioStrip::updatePan setting slider and label\n"); - - pan->blockSignals(true); - panl->blockSignals(true); - pan->setValue(v); - panl->setValue(v); - panl->blockSignals(false); - pan->blockSignals(false); - panVal = v; - } -} - -//--------------------------------------------------------- // offToggled //--------------------------------------------------------- void AudioStrip::offToggled(bool val) { - track->setOff(val); - MusEGlobal::song->update(SC_MUTE); + if(!track) + return; + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackOff)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- @@ -361,22 +1065,24 @@ bool val = !track->off(); slider->setEnabled(val); sl->setEnabled(val); - pan->setEnabled(val); - panl->setEnabled(val); + + _upperRack->setEnabled(val); + _infoRack->setEnabled(val); + _lowerRack->setEnabled(val); + if (track->type() != MusECore::Track::AUDIO_SOFTSYNTH) stereo->setEnabled(val); label->setEnabled(val); // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. - bool ae = track->auxRefCount() == 0 && val; - int n = auxKnob.size(); - for (int i = 0; i < n; ++i) - { - auxKnob[i]->setEnabled(ae); - auxLabel[i]->setEnabled(ae); - } + const bool ae = track->auxRefCount() == 0 && val; + _upperRack->setAuxEnabled(ae); + _infoRack->setAuxEnabled(ae); + _lowerRack->setAuxEnabled(ae); + if (_recMonitor) + _recMonitor->setEnabled(val); if (pre) pre->setEnabled(val); if (record) @@ -385,18 +1091,10 @@ solo->setEnabled(val); if (mute) mute->setEnabled(val); - //if (autoType) - // autoType->setEnabled(val); - //if (iR) - // iR->setEnabled(val); - //if (oR) - // oR->setEnabled(val); if (off) { off->blockSignals(true); off->setChecked(track->off()); off->blockSignals(false); - off->setIcon(track->off() ? QIcon(*exit1Icon) : QIcon(*exitIcon)); - //off->setIconSize(exit1Icon->size()); } } @@ -406,7 +1104,9 @@ void AudioStrip::preToggled(bool val) { - MusEGlobal::audio->msgSetPrefader((MusECore::AudioTrack*)track, val); + if(!track) + return; + MusEGlobal::audio->msgSetPrefader(static_cast(track), val); resetPeaks(); MusEGlobal::song->update(SC_ROUTE); } @@ -417,121 +1117,127 @@ void AudioStrip::stereoToggled(bool val) { - int oc = track->channels(); - int nc = val ? 2 : 1; -// stereo->setIcon(nc == 2 ? *stereoIcon : *monoIcon); + if(!track) + return; + const int nc = val ? 2 : 1; + const int oc = track->channels(); if (oc == nc) return; - MusEGlobal::audio->msgSetChannels((MusECore::AudioTrack*)track, nc); + MusEGlobal::audio->msgSetChannels(static_cast(track), nc); MusEGlobal::song->update(SC_CHANNELS); } //--------------------------------------------------------- -// auxChanged +// recMonitorToggled //--------------------------------------------------------- -void AudioStrip::auxChanged(double val, int idx) - { - double vol; - if (val <= MusEGlobal::config.minSlider) { - vol = 0.0; - val -= 1.0; // display special value "off" - } - else - vol = pow(10.0, val/20.0); - MusEGlobal::audio->msgSetAux((MusECore::AudioTrack*)track, idx, vol); - MusEGlobal::song->update(SC_AUX); - } +void AudioStrip::recMonitorToggled(bool v) +{ + if(!track) + return; + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(track, v, MusECore::PendingOperationItem::SetTrackRecMonitor)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); +} //--------------------------------------------------------- -// gainChanged +// volumeMoved //--------------------------------------------------------- -void AudioStrip::gainChanged(double val) +void AudioStrip::volumeMoved(double val, int id, bool shift_pressed) { - ((MusECore::AudioTrack*)track)->setGain(val); + DEBUG_AUDIO_STRIP(stderr, "AudioStrip::volumeMoved id:%d val:%.20f\n", id, val); + componentMoved(ComponentRack::controllerComponent, val, id, shift_pressed); } //--------------------------------------------------------- -// auxLabelChanged +// volumeChanged //--------------------------------------------------------- -void AudioStrip::auxLabelChanged(double val, unsigned int idx) - { - if(idx >= auxKnob.size()) - return; - auxKnob[idx]->setValue(val); - } +void AudioStrip::volumeChanged(double val, int id, int scrollMode) +{ + DEBUG_AUDIO_STRIP(stderr, "AudioStrip::volumeChanged id:%d val:%.20f scrollMode:%d\n", id, val, scrollMode); -//--------------------------------------------------------- -// volumeChanged -//--------------------------------------------------------- + if(!track || track->isMidiTrack()) + return; -void AudioStrip::volumeChanged(double val, int, bool shift_pressed) - { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - track->enableVolumeController(false); - - double vol; - if (val <= MusEGlobal::config.minSlider) { - vol = 0.0; - val -= 1.0; // display special value "off" - } - else - vol = pow(10.0, val/20.0); - volume = vol; - //MusEGlobal::audio->msgSetVolume((MusECore::AudioTrack*)track, vol); - // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? - ((MusECore::AudioTrack*)track)->setVolume(vol); - if (!shift_pressed) ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_VOLUME, vol); - } + double vol; + if (val <= MusEGlobal::config.minSlider) + vol = 0.0; + else + vol = muse_db2val(val); + volume = vol; + + MusECore::AudioTrack* at = static_cast(track); + // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. + // ScrDirect mode is one-time only on press with modifier. + if(scrollMode != SliderBase::ScrDirect) + at->recordAutomation(id, vol); + at->setParam(id, vol); // Schedules a timed control change. + at->enableController(id, false); + + componentChanged(ComponentRack::controllerComponent, val, false, id, scrollMode); +} //--------------------------------------------------------- // volumePressed //--------------------------------------------------------- -void AudioStrip::volumePressed() +void AudioStrip::volumePressed(double val, int id) { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - track->enableVolumeController(false); - - double val = slider->value(); + DEBUG_AUDIO_STRIP(stderr, "AudioStrip::volumePressed\n"); + if(!track || track->isMidiTrack()) + return; + _volPressed = true; double vol; - if (val <= MusEGlobal::config.minSlider) { - vol = 0.0; - //val -= 1.0; // display special value "off" - } + if (val <= MusEGlobal::config.minSlider) + vol = 0.0; else - vol = pow(10.0, val/20.0); + vol = muse_db2val(val); volume = vol; - //MusEGlobal::audio->msgSetVolume((MusECore::AudioTrack*)track, volume); - // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? - ((MusECore::AudioTrack*)track)->setVolume(volume); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_VOLUME, volume); + DEBUG_AUDIO_STRIP(stderr, " val:%.20f\n", volume); + + MusECore::AudioTrack* at = static_cast(track); + at->startAutoRecord(id, vol); + at->setVolume(vol); + //at->setParam(MusECore::AC_VOLUME, val); // Schedules a timed control change. // TODO Try this instead + DEBUG_AUDIO_STRIP(stderr, " calling enableController(false)\n"); + at->enableController(id, false); + + componentPressed(ComponentRack::controllerComponent, val, id); } //--------------------------------------------------------- // volumeReleased //--------------------------------------------------------- -void AudioStrip::volumeReleased() +void AudioStrip::volumeReleased(double val, int id) { - 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); + DEBUG_AUDIO_STRIP(stderr, "AudioStrip::volumeReleased\n"); + if(!track || track->isMidiTrack()) + return; + + MusECore::AudioTrack* at = static_cast(track); + AutomationType atype = at->automationType(); + DEBUG_AUDIO_STRIP(stderr, " val:%.20f\n", volume); + at->stopAutoRecord(id, volume); + if(atype == AUTO_OFF || atype == AUTO_TOUCH) + { + DEBUG_AUDIO_STRIP(stderr, " calling enableController(true)\n"); + at->enableController(id, true); + } + + componentReleased(ComponentRack::controllerComponent, val, id); + _volPressed = false; } //--------------------------------------------------------- // volumeRightClicked //--------------------------------------------------------- -void AudioStrip::volumeRightClicked(const QPoint &p) +void AudioStrip::volumeRightClicked(QPoint p) { - MusEGlobal::song->execAutomationCtlPopup((MusECore::AudioTrack*)track, p, MusECore::AC_VOLUME); + MusEGlobal::song->execAutomationCtlPopup(static_cast(track), p, MusECore::AC_VOLUME); } //--------------------------------------------------------- @@ -540,125 +1246,74 @@ void AudioStrip::volLabelChanged(double val) { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - track->enableVolumeController(false); - + if(!track || track->isMidiTrack()) + return; + MusECore::AudioTrack* t = static_cast(track); + double v = val; double vol; - if (val <= MusEGlobal::config.minSlider) { + if (v <= MusEGlobal::config.minSlider) { vol = 0.0; - val -= 1.0; // display special value "off" + v = MusEGlobal::config.minSlider; } else - vol = pow(10.0, val/20.0); + vol = muse_db2val(v); volume = vol; - slider->setValue(val); - //audio->msgSetVolume((MusECore::AudioTrack*)track, vol); - // p4.0.21 audio->msgXXX waits. Do we really need to? - ((MusECore::AudioTrack*)track)->setVolume(vol); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_VOLUME, vol); - } - -//--------------------------------------------------------- -// panChanged -//--------------------------------------------------------- - -void AudioStrip::panChanged(double val, int, bool shift_pressed) - { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - 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); - if (!shift_pressed) ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_PAN, val); - } - -//--------------------------------------------------------- -// panPressed -//--------------------------------------------------------- - -void AudioStrip::panPressed() - { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - 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); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_PAN, panVal); - } - -//--------------------------------------------------------- -// panReleased -//--------------------------------------------------------- + slider->blockSignals(true); + slider->setValue(v); + slider->blockSignals(false); + t->startAutoRecord(MusECore::AC_VOLUME, vol); + t->setParam(MusECore::AC_VOLUME, vol); // Schedules a timed control change. + t->enableController(MusECore::AC_VOLUME, false); -void AudioStrip::panReleased() - { - 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); + componentChanged(ComponentRack::controllerComponent, val, false, MusECore::AC_VOLUME, SliderBase::ScrNone); } -//--------------------------------------------------------- -// panRightClicked -//--------------------------------------------------------- -void AudioStrip::panRightClicked(const QPoint &p) +void AudioStrip::resetClipper() { - MusEGlobal::song->execAutomationCtlPopup((MusECore::AudioTrack*)track, p, MusECore::AC_PAN); + if(track) + { + track->resetClipper(); + resetPeaks(); + } } //--------------------------------------------------------- -// panLabelChanged -//--------------------------------------------------------- - -void AudioStrip::panLabelChanged(double val) - { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - track->enablePanController(false); - - panVal = val; - pan->setValue(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); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_PAN, val); - } - -//--------------------------------------------------------- // updateChannels //--------------------------------------------------------- void AudioStrip::updateChannels() { - MusECore::AudioTrack* t = (MusECore::AudioTrack*)track; + MusECore::AudioTrack* t = static_cast(track); int c = t->channels(); - //printf("AudioStrip::updateChannels track channels:%d current channels:%d\n", c, channel); + DEBUG_AUDIO_STRIP(stderr, "AudioStrip::updateChannels track channels:%d current channels:%d\n", c, channel); if (c > channel) { for (int cc = channel; cc < c; ++cc) { - meter[cc] = new MusEGui::Meter(this); - //meter[cc]->setRange(MusEGlobal::config.minSlider, 10.0); - meter[cc]->setRange(MusEGlobal::config.minMeter, 10.0); - meter[cc]->setFixedWidth(15); - connect(meter[cc], SIGNAL(mousePress()), this, SLOT(resetPeaks())); - sliderGrid->addWidget(meter[cc], 0, cc+1, Qt::AlignLeft); - sliderGrid->setColumnStretch(cc, 50); + _clipperLabel[cc] = new ClipperLabel(); + _clipperLabel[cc]->setContentsMargins(0, 0, 0, 0); + _clipperLabel[cc]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setClipperTooltip(cc); + _clipperLayout->addWidget(_clipperLabel[cc]); + connect(_clipperLabel[cc], SIGNAL(clicked()), SLOT(resetClipper())); + + meter[cc] = new Meter(this, Meter::DBMeter, Qt::Vertical, MusEGlobal::config.minMeter, volSliderMax); + meter[cc]->setRefreshRate(MusEGlobal::config.guiRefresh); + meter[cc]->setFixedWidth(FIXED_METER_WIDTH); + meter[cc]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + meter[cc]->setPrimaryColor(MusEGlobal::config.audioMeterPrimaryColor); + connect(meter[cc], SIGNAL(mousePress()), this, SLOT(resetClipper())); + sliderGrid->addWidget(meter[cc], 2, cc+1, Qt::AlignLeft); meter[cc]->show(); } } else if (c < channel) { for (int cc = channel-1; cc >= c; --cc) { - delete meter[cc]; + if(_clipperLabel[cc]) + delete _clipperLabel[cc]; + _clipperLabel[cc] = 0; + + if(meter[cc]) + delete meter[cc]; meter[cc] = 0; } } @@ -666,91 +1321,6 @@ stereo->blockSignals(true); stereo->setChecked(channel == 2); stereo->blockSignals(false); - stereo->setIcon(channel == 2 ? QIcon(*stereoIcon) : QIcon(*monoIcon)); - //stereo->setIconSize(stereoIcon->size()); - } - -//--------------------------------------------------------- -// addKnob -// type = 0 - panorama -// 1 - aux send -//--------------------------------------------------------- - -MusEGui::Knob* AudioStrip::addKnob(Knob::KnobType type, int id, MusEGui::DoubleLabel** dlabel, QLabel *name) - { - MusEGui::Knob* knob = NULL; - MusEGui::DoubleLabel* knobLabel = NULL; - switch(type) - { - case Knob::panType: - knob = new Knob(this); - knob->setRange(-1.0, +1.0); - knob->setToolTip(tr("panorama")); - knobLabel = new MusEGui::DoubleLabel(0, -1.0, +1.0, this); - knobLabel->setPrecision(2); - break; - case Knob::auxType: - knob = new Knob(this); - knob->setRange(MusEGlobal::config.minSlider-0.1, 10.0); - knob->setToolTip(tr("aux send level")); - knob->setFaceColor(Qt::blue); - knobLabel = new MusEGui::DoubleLabel(0.0, MusEGlobal::config.minSlider, 10.1, this); - knobLabel->setPrecision(0); - break; - case Knob::gainType: - knob = new Knob(this); - knob->setRange(1.0, 20.0); - knob->setFaceColor(Qt::yellow); - knob->setToolTip(tr("calibration gain")); - knobLabel = new MusEGui::DoubleLabel(1.0, 1.0, 30.0, this); - knobLabel->setPrecision(1); - break; - default: - fprintf(stderr, "FIXME: AudioStrip::addKnob(): Unknown type. Aborting!\n"); - abort(); - } - - knob->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - knob->setBackgroundRole(QPalette::Mid); - - if (dlabel) - *dlabel = knobLabel; - knobLabel->setSlider(knob); - knobLabel->setBackgroundRole(QPalette::Mid); - knobLabel->setFrame(true); - knobLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - - name->setParent(this); - name->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - name->setAlignment(Qt::AlignCenter); - - grid->addWidget(name, _curGridRow, 0); - grid->addWidget(knobLabel, _curGridRow+1, 0); - grid->addWidget(knob, _curGridRow, 1, 2, 1); - _curGridRow += 2; - - connect(knob, SIGNAL(valueChanged(double,int)), knobLabel, SLOT(setValue(double))); - - if (type == Knob::panType) { - connect(knobLabel, SIGNAL(valueChanged(double, int)), SLOT(panLabelChanged(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 &))); - } - else if (type == Knob::auxType){ - knobLabel->setReadOnly(true); - knob->setId(id); - connect(knobLabel, SIGNAL(valueChanged(double, int)), knob, SLOT(setValue(double))); - connect(knob, SIGNAL(sliderMoved(double, int)), SLOT(auxChanged(double, int))); - } - else if (type == Knob::gainType){ - knobLabel->setReadOnly(true); - knob->setId(id); - connect(knobLabel, SIGNAL(valueChanged(double, int)), knob, SLOT(setValue(double))); - connect(knob, SIGNAL(sliderMoved(double, int)), SLOT(gainChanged(double))); - } - return knob; } //--------------------------------------------------------- @@ -761,349 +1331,622 @@ { } - //--------------------------------------------------------- // AudioStrip // create mixer strip //--------------------------------------------------------- -AudioStrip::AudioStrip(QWidget* parent, MusECore::AudioTrack* at) - : Strip(parent, at) +AudioStrip::AudioStrip(QWidget* parent, MusECore::AudioTrack* at, bool hasHandle, bool isEmbedded) + : Strip(parent, at, hasHandle, isEmbedded) { + _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; + + MusECore::Track::TrackType type = at->type(); + volume = -1.0; - panVal = 0; + _volPressed = false; - record = 0; + slider = 0; + sl = 0; off = 0; - - // Set the whole strip's font, except for the label. p4.0.45 - setFont(MusEGlobal::config.fonts[1]); + _recMonitor = 0; + // Start the layout in mode A (normal, racks on left). + _isExpanded = false; - MusECore::AudioTrack* t = (MusECore::AudioTrack*)track; + // Set the whole strip's font, except for the label. + // May be good to keep this. In the midi strip without it the upper rack is too tall at first. So avoid trouble. + setFont(MusEGlobal::config.fonts[1]); + setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); + channel = at->channels(); - ///setMinimumWidth(STRIP_WIDTH); - + + _inRoutesPos = GridPosStruct(_curGridRow, 0, 1, 1); + _outRoutesPos = GridPosStruct(_curGridRow, 1, 1, 1); + _routesPos = GridPosStruct(_curGridRow, 0, 1, 2); + + _effectRackPos = GridPosStruct(_curGridRow + 1, 0, 1, 3); + + + _stereoToolPos = GridPosStruct(_curGridRow + 2, 0, 1, 1); + _preToolPos = GridPosStruct(_curGridRow + 2, 1, 1, 1); + + _preScrollAreaPos_A = GridPosStruct(_curGridRow + 3, 0, 1, 3); + + + _preScrollAreaPos_B = GridPosStruct(_curGridRow + 4, 2, 1, 1); + _sliderPos = GridPosStruct(_curGridRow + 4, 0, 4, 2); + + + _infoSpacerTop = GridPosStruct(_curGridRow + 5, 2, 1, 1); + + _propertyRackPos = GridPosStruct(_curGridRow + 6, 2, 1, 1); + + _infoSpacerBottom = GridPosStruct(_curGridRow + 7, 2, 1, 1); + + _sliderLabelPos = GridPosStruct(_curGridRow + 8, 0, 1, 2); + _postScrollAreaPos_B = GridPosStruct(_curGridRow + 8, 2, 1, 1); + + _postScrollAreaPos_A = GridPosStruct(_curGridRow + 9, 0, 1, 3); + + _offPos = GridPosStruct(_curGridRow + 10, 0, 1, 1); + _recPos = GridPosStruct(_curGridRow + 10, 1, 1, 1); + _offMonRecPos = GridPosStruct(_curGridRow + 10, 0, 1, 2); + + + _mutePos = GridPosStruct(_curGridRow + 11, 0, 1, 1); + _soloPos = GridPosStruct(_curGridRow + 11, 1, 1, 1); + + _automationPos = GridPosStruct(_curGridRow + 12, 0, 1, 2); + + _rightSpacerPos = GridPosStruct(_curGridRow + 13, 2, 1, 1); + + + _infoRack = new AudioComponentRack(at, aStripInfoRack, false); + //_infoRack->setVisible(false); // Not visible unless expanded. + // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. + //_infoRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + _infoRack->setFrameStyle(QFrame::Box | QFrame::Sunken); + _infoRack->setLineWidth(rackFrameWidth); + _infoRack->setMidLineWidth(0); + _infoRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); + _infoRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); + _infoRack->setFocusPolicy(Qt::NoFocus); + _infoRack->addStretch(); + addGridWidget(_infoRack, _propertyRackPos); + + grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding), + _infoSpacerTop._row, _infoSpacerTop._col, _infoSpacerTop._rowSpan, _infoSpacerTop._colSpan); + + _upperRack = new AudioComponentRack(at, aStripUpperRack, true); // True = manage auxs. + // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. + //_upperRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + _upperRack->setFrameStyle(QFrame::Box | QFrame::Sunken); + _upperRack->setLineWidth(rackFrameWidth); + _upperRack->setMidLineWidth(0); + // We do set a minimum height on this widget. Tested: Must be on fixed. Thankfully, it'll expand if more controls are added. + _upperRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + _upperRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); + _upperRack->setFocusPolicy(Qt::NoFocus); + int ch = 0; for (; ch < channel; ++ch) - meter[ch] = new MusEGui::Meter(this); + { + meter[ch] = new Meter(this, Meter::DBMeter, Qt::Vertical, MusEGlobal::config.minMeter, volSliderMax); + meter[ch]->setRefreshRate(MusEGlobal::config.guiRefresh); + meter[ch]->setFixedWidth(FIXED_METER_WIDTH); + meter[ch]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + _clipperLabel[ch] = new ClipperLabel(this); + _clipperLabel[ch]->setContentsMargins(0, 0, 0, 0); + _clipperLabel[ch]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + setClipperTooltip(ch); + connect(_clipperLabel[ch], SIGNAL(clicked()), SLOT(resetClipper())); + + } for (; ch < MAX_CHANNELS; ++ch) + { meter[ch] = 0; + _clipperLabel[ch] = 0; + } //--------------------------------------------------- // plugin rack //--------------------------------------------------- - rack = new EffectRack(this, t); - rack->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - grid->addWidget(rack, _curGridRow++, 0, 1, 2); + rack = new EffectRack(this, at); + rack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); + addGridWidget(rack, _effectRackPos); + addGridWidget(_upperRack, _preScrollAreaPos_A); + //--------------------------------------------------- // mono/stereo pre/post //--------------------------------------------------- - stereo = new QToolButton(); - ///stereo->setFont(MusEGlobal::config.fonts[1]); + stereo = new IconButton(stereoOnSVGIcon, stereoOffSVGIcon, 0, 0, false, true); + stereo->setContentsMargins(0, 0, 0, 0); stereo->setFocusPolicy(Qt::NoFocus); stereo->setCheckable(true); stereo->setToolTip(tr("1/2 channel")); stereo->setChecked(channel == 2); - stereo->setIcon(channel == 2 ? QIcon(*stereoIcon) : QIcon(*monoIcon)); - stereo->setIconSize(monoIcon->size()); - stereo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - connect(stereo, SIGNAL(clicked(bool)), SLOT(stereoToggled(bool))); + stereo->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + connect(stereo, SIGNAL(toggled(bool)), SLOT(stereoToggled(bool))); // disable mono/stereo for Synthesizer-Plugins - if (t->type() == MusECore::Track::AUDIO_SOFTSYNTH) + if (type == MusECore::Track::AUDIO_SOFTSYNTH) stereo->setEnabled(false); - pre = new QToolButton(); - ///pre->setFont(MusEGlobal::config.fonts[1]); + pre = new IconButton(preFaderOnSVGIcon, preFaderOffSVGIcon, 0, 0, false, true); + pre->setContentsMargins(0, 0, 0, 0); pre->setFocusPolicy(Qt::NoFocus); pre->setCheckable(true); - pre->setText(tr("Pre")); - pre->setToolTip(tr("pre fader - post fader")); - pre->setChecked(t->prefader()); - pre->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - connect(pre, SIGNAL(clicked(bool)), SLOT(preToggled(bool))); + pre->setToolTip(tr("Pre Fader Listening (PFL)")); + pre->setChecked(at->prefader()); + pre->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + connect(pre, SIGNAL(toggled(bool)), SLOT(preToggled(bool))); - grid->addWidget(stereo, _curGridRow, 0); - grid->addWidget(pre, _curGridRow++, 1); - - //--------------------------------------------------- - // Gain - //--------------------------------------------------- - - gain = addKnob(MusEGui::Knob::gainType, 0, &gainLabel, new QLabel("Gain", this)); - gain->setValue(t->gain()); - - //--------------------------------------------------- - // aux send - //--------------------------------------------------- - - int auxsSize = MusEGlobal::song->auxs()->size(); - if (t->hasAuxSend()) { - for (int idx = 0; idx < auxsSize; ++idx) { - MusEGui::DoubleLabel* al; // the thought was to aquire the correct Aux name for each Aux - // now they are only called Aux1, Aux2, which isn't too usable. - QString title = ((MusECore::AudioAux*)(MusEGlobal::song->auxs()->at(idx)))->auxName(); - if (title.length() > 8) { // shorten name - title = title.mid(0,8) + "."; - } - QLabel *name = new QLabel(title,this); - MusEGui::Knob* ak = addKnob(MusEGui::Knob::auxType, idx, &al, name); - - auxKnob.push_back(ak); - auxLabel.push_back(al); - double val = MusECore::fast_log10(t->auxSend(idx))*20.0; - ak->setValue(val); - al->setValue(val); - - // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! - // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. - int rc = track->auxRefCount(); - ak->setEnabled( rc == 0 ); - al->setEnabled( rc == 0 ); - } - } - else { - ///if (auxsSize) - //layout->addSpacing((STRIP_WIDTH/2 + 2) * auxsSize); - ///grid->addSpacing((STRIP_WIDTH/2 + 2) * auxsSize); // ??? - } + addGridWidget(stereo, _stereoToolPos); + addGridWidget(pre, _preToolPos); //--------------------------------------------------- // slider, label, meter //--------------------------------------------------- sliderGrid = new QGridLayout(); - sliderGrid->setRowStretch(0, 100); sliderGrid->setContentsMargins(0, 0, 0, 0); sliderGrid->setSpacing(0); - - slider = new MusEGui::Slider(this, "vol", Qt::Vertical, MusEGui::Slider::None); + /*-------------- clipper label -------------------*/ + _clipperLayout = new QHBoxLayout(); + _clipperLayout->setSpacing(0); + for(int ch = 0; ch < channel; ++ch) + _clipperLayout->addWidget(_clipperLabel[ch]); + sliderGrid->addLayout(_clipperLayout, 0, 0, 1, -1, Qt::AlignCenter); + sliderGrid->addItem(new QSpacerItem(0, 1), 1, 0, 1, -1); + + slider = new Slider(this, "vol", Qt::Vertical, MusEGui::Slider::InsideVertical, 14, + MusEGlobal::config.audioVolumeSliderColor, + ScaleDraw::TextHighlightSplitAndShadow); + slider->setId(MusECore::AC_VOLUME); + slider->setFocusPolicy(Qt::NoFocus); + slider->setContentsMargins(0, 0, 0, 0); slider->setCursorHoming(true); - slider->setRange(MusEGlobal::config.minSlider-0.1, 10.0); - slider->setFixedWidth(20); - ///slider->setFont(MusEGlobal::config.fonts[1]); - slider->setValue(MusECore::fast_log10(t->volume())*20.0); + //slider->setThumbLength(1); + DEBUG_AUDIO_STRIP(stderr, "AudioStrip::AudioStrip new slider: step:%.20f\n", volSliderStep); + slider->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep); + //slider->setScaleMaxMinor(5); + slider->setScale(MusEGlobal::config.minSlider, volSliderMax, 6.0, false); + slider->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. + slider->setScaleBackBone(false); + //slider->setFillThumb(false); + + slider->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + + double track_vol = at->volume(); + if(track_vol == 0.0) + track_vol = MusEGlobal::config.minSlider; + else + { + track_vol = muse_val2dbr(track_vol); + if(track_vol < MusEGlobal::config.minSlider) + track_vol = MusEGlobal::config.minSlider; + } + // Slider::fitValue() not required so far. The log function is accurate but rounded to the nearest .000001 + slider->setValue(track_vol); - sliderGrid->addWidget(slider, 0, 0, Qt::AlignHCenter); + sliderGrid->addWidget(slider, 2, 0, Qt::AlignHCenter); for (int i = 0; i < channel; ++i) { //meter[i]->setRange(MusEGlobal::config.minSlider, 10.0); - meter[i]->setRange(MusEGlobal::config.minMeter, 10.0); - meter[i]->setFixedWidth(15); - connect(meter[i], SIGNAL(mousePress()), this, SLOT(resetPeaks())); - sliderGrid->addWidget(meter[i], 0, i+1, Qt::AlignHCenter); - sliderGrid->setColumnStretch(i, 50); + meter[i]->setRange(MusEGlobal::config.minMeter, volSliderMax); + meter[i]->setRefreshRate(MusEGlobal::config.guiRefresh); + meter[i]->setFixedWidth(Strip::FIXED_METER_WIDTH); + meter[i]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + meter[i]->setPrimaryColor(MusEGlobal::config.audioMeterPrimaryColor); + connect(meter[i], SIGNAL(mousePress()), this, SLOT(resetClipper())); + sliderGrid->addWidget(meter[i], 2, i+1, Qt::AlignHCenter); + meter[i]->show(); } - sliderGrid->addItem(new QSpacerItem(2,0),0,3); - grid->addLayout(sliderGrid, _curGridRow++, 0, 1, 2); + + addGridLayout(sliderGrid, _sliderPos); + + sl = new DoubleLabel(0.0, MusEGlobal::config.minSlider, volSliderMax, this); + sl->setContentsMargins(0, 0, 0, 0); + sl->setTextMargins(0, 0, 0, 0); + sl->setFocusPolicy(Qt::WheelFocus); + sl->setMouseTracking(true); + sl->setFrame(true); + sl->setAlignment(Qt::AlignCenter); + //sl->setAutoFillBackground(true); - sl = new MusEGui::DoubleLabel(0.0, MusEGlobal::config.minSlider, 10.0, this); sl->setSlider(slider); - ///sl->setFont(MusEGlobal::config.fonts[1]); - sl->setBackgroundRole(QPalette::Mid); + //sl->setBackgroundRole(QPalette::Mid); + sl->setToolTip(tr("Volume/gain")); sl->setSuffix(tr("dB")); - sl->setFrame(true); - sl->setPrecision(0); - sl->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum)); - sl->setValue(MusECore::fast_log10(t->volume()) * 20.0); + sl->setSpecialText(QString('-') + QChar(0x221e) + QChar(' ') + tr("dB")); + sl->setOff(MusEGlobal::config.minSlider); + sl->setPrecision(volSliderPrec); + sl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + sl->setValue(track_vol); + sl->setEnableStyleHack(MusEGlobal::config.lineEditStyleHack); + + // If smart focus is on redirect strip focus to slider label. + //if(MusEGlobal::config.smartFocus) + setFocusProxy(sl); connect(sl, SIGNAL(valueChanged(double,int)), SLOT(volLabelChanged(double))); connect(slider, SIGNAL(valueChanged(double,int)), sl, SLOT(setValue(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 &))); - grid->addWidget(sl, _curGridRow++, 0, 1, 2, Qt::AlignCenter); + connect(slider, SIGNAL(valueChanged(double,int,int)), SLOT(volumeChanged(double,int,int))); + connect(slider, SIGNAL(sliderMoved(double,int,bool)), SLOT(volumeMoved(double,int,bool))); + connect(slider, SIGNAL(sliderPressed(double, int)), SLOT(volumePressed(double,int))); + connect(slider, SIGNAL(sliderReleased(double, int)), SLOT(volumeReleased(double,int))); + connect(slider, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(volumeRightClicked(QPoint))); + + grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding), + _infoSpacerBottom._row, _infoSpacerBottom._col, _infoSpacerBottom._rowSpan, _infoSpacerBottom._colSpan); + + addGridWidget(sl, _sliderLabelPos, Qt::AlignCenter); //--------------------------------------------------- // pan, balance //--------------------------------------------------- - pan = addKnob(MusEGui::Knob::panType, 0, &panl, new QLabel("Pan", this)); - pan->setValue(t->pan()); - + _lowerRack = new AudioComponentRack(at, aStripLowerRack, false); + // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. + //_lowerRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + _lowerRack->setFrameStyle(QFrame::Box | QFrame::Sunken); + _lowerRack->setLineWidth(rackFrameWidth); + _lowerRack->setMidLineWidth(0); + // We do set a minimum height on this widget. Tested: Must be on fixed. Thankfully, it'll expand if more controls are added. + _lowerRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + _lowerRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); + _lowerRack->setFocusPolicy(Qt::NoFocus); + + addGridWidget(_lowerRack, _postScrollAreaPos_A); + + _upperRack->setEnabled(!at->off()); + _infoRack->setEnabled(!at->off()); + _lowerRack->setEnabled(!at->off()); + //--------------------------------------------------- // mute, solo, record //--------------------------------------------------- if (track->canRecord()) { - record = new MusEGui::TransparentToolButton(this); - record->setFocusPolicy(Qt::NoFocus); + record = new IconButton(recArmOnSVGIcon, recArmOffSVGIcon, 0, 0, false, true); + record->setFocusPolicy(Qt::NoFocus); record->setCheckable(true); - record->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - record->setBackgroundRole(QPalette::Mid); - record->setToolTip(tr("record")); - record->setChecked(t->recordFlag()); - record->setIcon(t->recordFlag() ? QIcon(*record_on_Icon) : QIcon(*record_off_Icon)); - ///record->setIconSize(record_on_Icon->size()); - connect(record, SIGNAL(clicked(bool)), SLOT(recordToggled(bool))); + record->setContentsMargins(0, 0, 0, 0); + record->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + if(type == MusECore::Track::AUDIO_OUTPUT) + record->setToolTip(tr("Record downmix")); + else + record->setToolTip(tr("Record arm")); + record->setChecked(at->recordFlag()); + connect(record, SIGNAL(toggled(bool)), SLOT(recordToggled(bool))); } - MusECore::Track::TrackType type = t->type(); - - mute = new QToolButton(); + mute = new IconButton(muteOnSVGIcon, muteOffSVGIcon, muteAndProxyOnSVGIcon, muteProxyOnSVGIcon, false, true); mute->setFocusPolicy(Qt::NoFocus); mute->setCheckable(true); - mute->setToolTip(tr("mute")); - mute->setChecked(t->mute()); - mute->setIcon(t->mute() ? QIcon(*muteIconOff) : QIcon(*muteIconOn)); - ///mute->setIconSize(muteIconOn->size()); - mute->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - connect(mute, SIGNAL(clicked(bool)), SLOT(muteToggled(bool))); + mute->setContentsMargins(0, 0, 0, 0); + mute->setToolTip(tr("Mute or proxy mute")); + mute->setChecked(at->mute()); + updateMuteIcon(); + mute->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + connect(mute, SIGNAL(toggled(bool)), SLOT(muteToggled(bool))); - solo = new QToolButton(); + solo = new IconButton(soloOnSVGIcon, soloOffSVGIcon, soloAndProxyOnSVGIcon, soloProxyOnSVGIcon, false, true); solo->setFocusPolicy(Qt::NoFocus); + solo->setContentsMargins(0, 0, 0, 0); + solo->setToolTip(tr("Solo or proxy solo")); solo->setCheckable(true); - solo->setChecked(t->solo()); - if(t->internalSolo()) - solo->setIcon(t->solo() ? QIcon(*soloblksqIconOn) : QIcon(*soloblksqIconOff)); - else - solo->setIcon(t->solo() ? QIcon(*soloIconOn) : QIcon(*soloIconOff)); - ///solo->setIconSize(soloIconOn->size()); - solo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - connect(solo, SIGNAL(clicked(bool)), SLOT(soloToggled(bool))); - if (type == MusECore::Track::AUDIO_OUTPUT) { - record->setToolTip(tr("record downmix")); - //solo->setToolTip(tr("solo mode (monitor)")); - solo->setToolTip(tr("solo mode")); - } - else { - //solo->setToolTip(tr("pre fader listening")); - solo->setToolTip(tr("solo mode")); - } + solo->setIconSetB(at->internalSolo()); + solo->setChecked(at->solo()); + solo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + connect(solo, SIGNAL(toggled(bool)), SLOT(soloToggled(bool))); - off = new MusEGui::TransparentToolButton(this); + off = new IconButton(trackOffSVGIcon, trackOnSVGIcon, 0, 0, false, true); + off->setContentsMargins(0, 0, 0, 0); off->setFocusPolicy(Qt::NoFocus); - off->setBackgroundRole(QPalette::Mid); - off->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); + off->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); off->setCheckable(true); - off->setToolTip(tr("off")); - off->setChecked(t->off()); - off->setIcon(t->off() ? QIcon(*exit1Icon) : QIcon(*exitIcon)); - ///off->setIconSize(exit1Icon->size()); - connect(off, SIGNAL(clicked(bool)), SLOT(offToggled(bool))); - - grid->addWidget(off, _curGridRow, 0); - if (record) - grid->addWidget(record, _curGridRow, 1); - ++_curGridRow; - grid->addWidget(mute, _curGridRow, 0); - grid->addWidget(solo, _curGridRow++, 1); + off->setToolTip(tr("Track off")); + off->setChecked(at->off()); + connect(off, SIGNAL(toggled(bool)), SLOT(offToggled(bool))); //--------------------------------------------------- // routing //--------------------------------------------------- if (type != MusECore::Track::AUDIO_AUX) { - iR = new QToolButton(); - iR->setFocusPolicy(Qt::NoFocus); - ///iR->setFont(MusEGlobal::config.fonts[1]); - iR->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum)); - ///iR->setText(tr("iR")); - iR->setIcon(QIcon(*routesInIcon)); - iR->setIconSize(routesInIcon->size()); + iR = new IconButton(routingInputSVGIcon, routingInputSVGIcon, + routingInputUnconnectedSVGIcon, routingInputUnconnectedSVGIcon, false, true); + iR->setContentsMargins(0, 0, 0, 0); + iR->setFocusPolicy(Qt::NoFocus); + iR->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); iR->setCheckable(false); - iR->setToolTip(tr("input routing")); - grid->addWidget(iR, _curGridRow, 0); + iR->setToolTip(MusEGlobal::inputRoutingToolTipBase); connect(iR, SIGNAL(pressed()), SLOT(iRoutePressed())); } - - oR = new QToolButton(); + + oR = new IconButton(routingOutputSVGIcon, routingOutputSVGIcon, + routingOutputUnconnectedSVGIcon, routingOutputUnconnectedSVGIcon, false, true); + oR->setContentsMargins(0, 0, 0, 0); oR->setFocusPolicy(Qt::NoFocus); - ///oR->setFont(MusEGlobal::config.fonts[1]); - oR->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum)); - ///oR->setText(tr("oR")); - oR->setIcon(QIcon(*routesOutIcon)); - oR->setIconSize(routesOutIcon->size()); + oR->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); oR->setCheckable(false); - oR->setToolTip(tr("output routing")); - grid->addWidget(oR, _curGridRow++, 1); + oR->setToolTip(MusEGlobal::outputRoutingToolTipBase); connect(oR, SIGNAL(pressed()), SLOT(oRoutePressed())); + updateRouteButtons(); + + if (track && track->canRecordMonitor()) + { + _recMonitor = new IconButton(monitorOnSVGIcon, monitorOffSVGIcon, 0, 0, false, true); + _recMonitor->setFocusPolicy(Qt::NoFocus); + _recMonitor->setContentsMargins(0, 0, 0, 0); + _recMonitor->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + _recMonitor->setCheckable(true); + _recMonitor->setToolTip(tr("Input monitor")); + _recMonitor->setWhatsThis(tr("Pass input through to output")); + _recMonitor->setChecked(at->recMonitor()); + connect(_recMonitor, SIGNAL(toggled(bool)), SLOT(recMonitorToggled(bool))); + } + + if(off && record && _recMonitor) + { + QHBoxLayout* offRecMonLayout = new QHBoxLayout(); + offRecMonLayout = new QHBoxLayout(); + offRecMonLayout->setContentsMargins(0, 0, 0, 0); + offRecMonLayout->setSpacing(0); + offRecMonLayout->addWidget(off); + offRecMonLayout->addWidget(_recMonitor); + offRecMonLayout->addWidget(record); + addGridLayout(offRecMonLayout, _offMonRecPos); + } + else + { + if(off) + addGridWidget(off, _offPos); + if(_recMonitor) + addGridWidget(_recMonitor, _recPos); + else if(record) + addGridWidget(record, _recPos); + } + addGridWidget(mute, _mutePos); + addGridWidget(solo, _soloPos); + + if(iR) + addGridWidget(iR, _inRoutesPos); + if(oR) + addGridWidget(oR, _outRoutesPos); + //--------------------------------------------------- // automation type //--------------------------------------------------- - autoType = new MusEGui::ComboBox(); + autoType = new CompactComboBox(); + autoType->setContentsMargins(0, 0, 0, 0); autoType->setFocusPolicy(Qt::NoFocus); - ///autoType->setFont(MusEGlobal::config.fonts[1]); - autoType->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); + autoType->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); //autoType->setAutoFillBackground(true); autoType->addAction(tr("Off"), AUTO_OFF); autoType->addAction(tr("Read"), AUTO_READ); autoType->addAction(tr("Touch"), AUTO_TOUCH); autoType->addAction(tr("Write"), AUTO_WRITE); - autoType->setCurrentItem(t->automationType()); + autoType->setCurrentItem(at->automationType()); QPalette palette; - //QLinearGradient gradient(autoType->geometry().topLeft(), autoType->geometry().bottomLeft()); - if(t->automationType() == AUTO_TOUCH || t->automationType() == AUTO_WRITE) + if(at->automationType() == AUTO_TOUCH || at->automationType() == AUTO_WRITE) { palette.setColor(QPalette::Button, QColor(215, 76, 39)); // red - /* QLinearGradient gradient(autoType->geometry().topLeft(), autoType->geometry().bottomLeft()); - QColor c(Qt::red); - //QColor c(215, 76, 39); // red - gradient.setColorAt(0, c.darker()); - gradient.setColorAt(0.5, c); - gradient.setColorAt(1, c.darker()); - palette.setBrush(QPalette::Button, gradient); - //palette.setBrush(autoType->backgroundRole(), gradient); - //palette.setBrush(QPalette::Window, gradient); */ autoType->setPalette(palette); } - else if(t->automationType() == AUTO_READ) + else if(at->automationType() == AUTO_READ) { palette.setColor(QPalette::Button, QColor(100, 172, 49)); // green - /*QLinearGradient gradient(autoType->geometry().topLeft(), autoType->geometry().bottomLeft()); - QColor c(Qt::green); - //QColor c(100, 172, 49); // green - gradient.setColorAt(0, c.darker()); - gradient.setColorAt(0.5, c); - gradient.setColorAt(1, c.darker()); - palette.setBrush(QPalette::Button, gradient); - //palette.setBrush(autoType->backgroundRole(), gradient); - //palette.setBrush(QPalette::Window, gradient); */ autoType->setPalette(palette); } else { palette.setColor(QPalette::Button, qApp->palette().color(QPalette::Active, QPalette::Background)); - //QColor c(qApp->palette().color(QPalette::Active, QPalette::Background)); - //gradient.setColorAt(0, c); - //gradient.setColorAt(1, c.darker()); - //palette.setBrush(QPalette::Button, gradient); autoType->setPalette(palette); } autoType->setToolTip(tr("automation type")); connect(autoType, SIGNAL(activated(int)), SLOT(setAutomationType(int))); - grid->addWidget(autoType, _curGridRow++, 0, 1, 2); + addGridWidget(autoType, _automationPos); + + grid->setColumnStretch(2, 10); if (off) { off->blockSignals(true); updateOffState(); // init state off->blockSignals(false); } + + // Now build the strip components. + buildStrip(); + // Now set up all tabbing on the strip. + // Don't bother if the strip is part of the mixer (not embedded), + // the non-embedding parent (mixer) should set up all the tabs and make this call. + if(isEmbedded) + setupComponentTabbing(); + connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); updateRouteButtons(); + connect(_upperRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); + connect(_upperRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); + connect(_upperRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); + connect(_upperRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); + + connect(_infoRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); + connect(_infoRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); + connect(_infoRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); + connect(_infoRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); + + connect(_lowerRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); + connect(_lowerRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); + connect(_lowerRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); + connect(_lowerRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); +} + +//--------------------------------------------------- +// buildStrip +// Destroy and rebuild strip components. +//--------------------------------------------------- + +void AudioStrip::buildStrip() +{ + // Destroys all components and clears the component list. + _infoRack->clearDelete(); + _upperRack->clearDelete(); + _lowerRack->clearDelete(); + + MusECore::AudioTrack* at = static_cast(track); + + //--------------------------------------------------- + // Upper rack + //--------------------------------------------------- + + // Gain... + if(_preferKnobs) + { + CompactKnobComponentDescriptor gain_desc + ( + ComponentRack::propertyComponent, + "MixerStripAudioGain", + AudioComponentRack::aStripGainProperty + ); + _upperRack->newComponent(&gain_desc); + } + else + { + CompactSliderComponentDescriptor gain_desc + ( + ComponentRack::propertyComponent, + "MixerStripAudioGain", + AudioComponentRack::aStripGainProperty + ); + _upperRack->newComponent(&gain_desc); + } + + // Aux sends... + int auxsSize = MusEGlobal::song->auxs()->size(); + if(at->hasAuxSend()) + { + for (int idx = 0; idx < auxsSize; ++idx) + { + if(_preferKnobs) + { + CompactKnobComponentDescriptor aux_desc + ( + AudioComponentRack::aStripAuxComponent, + "MixerStripAudioAux", + idx + ); + _upperRack->newComponent(&aux_desc); + } + else + { + CompactSliderComponentDescriptor aux_desc + ( + AudioComponentRack::aStripAuxComponent, + "MixerStripAudioAux", + idx + ); + _upperRack->newComponent(&aux_desc); } + } + } + else + { + ///if (auxsSize) + //layout->addSpacing((STRIP_WIDTH/2 + 2) * auxsSize); + ///grid->addSpacing((STRIP_WIDTH/2 + 2) * auxsSize); // ??? + } + // Keep this if dynamic layout (flip to right side) is desired. + _upperRack->addStretch(); + + updateRackSizes(true, false); + + //--------------------------------------------------- + // Lower rack + //--------------------------------------------------- + + // Pan... + if(_preferKnobs) + { + CompactKnobComponentDescriptor pan_desc + ( + ComponentRack::controllerComponent, + "MixerStripAudioPan", + MusECore::AC_PAN + ); + _lowerRack->newComponent(&pan_desc); + } + else + { + CompactSliderComponentDescriptor pan_desc + ( + ComponentRack::controllerComponent, + "MixerStripAudioPan", + MusECore::AC_PAN + ); + _lowerRack->newComponent(&pan_desc); + } + + // Keep this if dynamic layout (flip to right side) is desired. + _lowerRack->addStretch(); + + updateRackSizes(false, true); +} + +QWidget* AudioStrip::setupComponentTabbing(QWidget* previousWidget) +{ + QWidget* prev = previousWidget; + prev = _upperRack->setupComponentTabbing(prev); + prev = _infoRack->setupComponentTabbing(prev); + if(sl) + { + if(prev) + QWidget::setTabOrder(prev, sl); + prev = sl; + } + prev = _lowerRack->setupComponentTabbing(prev); + return prev; +} + +void AudioStrip::setClipperTooltip(int ch) +{ + QString clip_tt; + switch(ch) + { + case 0: + clip_tt = tr("L meter peak/clip"); + break; + case 1: + clip_tt = tr("R meter peak/clip"); + break; + default: + clip_tt = locale().toString(ch); + break; + } + _clipperLabel[ch]->setToolTip(clip_tt); +} + //--------------------------------------------------------- // iRoutePressed //--------------------------------------------------------- void AudioStrip::iRoutePressed() { - //MusEGui::RoutePopupMenu* pup = MusEGlobal::muse->getRoutingPopupMenu(); - RoutePopupMenu* pup = new RoutePopupMenu(); + RoutePopupMenu* pup = new RoutePopupMenu(0, false, _broadcastChanges); pup->exec(QCursor::pos(), track, false); delete pup; iR->setDown(false); @@ -1115,11 +1958,91 @@ void AudioStrip::oRoutePressed() { - //MusEGui::RoutePopupMenu* pup = MusEGlobal::muse->getRoutingPopupMenu(); - RoutePopupMenu* pup = new RoutePopupMenu(); + RoutePopupMenu* pup = new RoutePopupMenu(0, true, _broadcastChanges); pup->exec(QCursor::pos(), track, true); delete pup; oR->setDown(false); } +void AudioStrip::incVolume(int v) +{ + if(!track || track->isMidiTrack()) + return; + + const int id = MusECore::AC_VOLUME; + MusECore::AudioTrack* at = static_cast(track); + + // Get the slider's current value. + const double prev_val = slider->value(); + // Increment the slider. Do not allow signalling. + slider->blockSignals(true); + slider->incValue(v); + slider->blockSignals(false); + // Now grab the control's new value. + const double new_val = slider->value(); + + sl->blockSignals(true); + sl->setValue(new_val); + sl->blockSignals(false); + + double d_new_val = new_val; + if (d_new_val <= MusEGlobal::config.minSlider) + d_new_val = 0.0; + else + d_new_val = muse_db2val(d_new_val); + volume = d_new_val; + + // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. + // ScrDirect mode is one-time only on press with modifier. +// if(scrollMode != SliderBase::ScrDirect) + at->recordAutomation(id, d_new_val); + at->setParam(id, d_new_val); // Schedules a timed control change. + at->enableController(id, false); + + componentIncremented(ComponentRack::controllerComponent, + prev_val, new_val, + false, id, Slider::ScrNone); +} + +void AudioStrip::incPan(int v) +{ + if(!track || track->isMidiTrack()) + return; + + const int id = MusECore::AC_PAN; + MusECore::AudioTrack* at = static_cast(track); + + ComponentRack* rack = 0; + ComponentWidget* cw = 0; + // Be sure to search all racks. Even if pan is in multiple racks, only one hit is + // needed since after the value is set, the other pan controls will be updated too. + if((cw = _upperRack->findComponent(ComponentRack::controllerComponent, -1, id))) + rack = _upperRack; + else if((cw = _infoRack->findComponent(ComponentRack::controllerComponent, -1, id))) + rack = _infoRack; + else if((cw = _lowerRack->findComponent(ComponentRack::controllerComponent, -1, id))) + rack = _lowerRack; + + if(!cw || !rack) + return; + + // Get the component's current value. + const double prev_val = rack->componentValue(*cw); + // Now increment the component. Do not allow signalling. + rack->incComponentValue(*cw, v, true); + // Now grab its value. + const double d_new_val = rack->componentValue(*cw); + + // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. + // ScrDirect mode is one-time only on press with modifier. +// if(scrollMode != SliderBase::ScrDirect) + at->recordAutomation(id, d_new_val); + at->setParam(id, d_new_val); // Schedules a timed control change. + at->enableController(id, false); + + componentIncremented(ComponentRack::controllerComponent, + prev_val, d_new_val, + false, id, Slider::ScrNone); +} + } // namespace MusEGui diff -Nru muse-2.1.2/muse/mixer/astrip.h muse-3.0.2+ds1/muse/mixer/astrip.h --- muse-2.1.2/muse/mixer/astrip.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/astrip.h 2017-12-04 21:01:18.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: astrip.h,v 1.8.2.6 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,22 +25,15 @@ #ifndef __ASTRIP_H__ #define __ASTRIP_H__ -#include - #include "type_defs.h" #include "strip.h" -#include "knob.h" -//#include "route.h" +#include "clipper_label.h" -//class QDialog; -class QToolButton; -//class QAction; -//class QPopupMenu; -//class PopupMenu; class QButton; - -namespace MusEGui { -} +class QHBoxLayout; +class QVBoxLayout; +class QColor; +class QWidget; namespace MusECore { class AudioTrack; @@ -51,67 +44,156 @@ class EffectRack; class Knob; class Slider; -class TransparentToolButton; - +class CompactSlider; +class IconButton; + +//--------------------------------------------------------- +// AudioComponentRack +//--------------------------------------------------------- + +class AudioComponentRack : public ComponentRack +{ + Q_OBJECT + + public: + // Type of component. + enum AStripComponentType { aStripAuxComponent = userComponent }; + // Possible widget types. + //enum AStripComponentWidgetType { type = userComponentWidget }; + // Possible component properties. + enum AStripComponentProperties { aStripGainProperty = userComponentProperty }; + + protected: + MusECore::AudioTrack* _track; + bool _manageAuxs; + + // Creates a new component widget from the given desc. Called by newComponent(). + // Connects known widget types' signals to slots. + //virtual void newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); + // Scan and automatically remove missing / add new controllers. + void scanControllerComponents(); + // Scan and automatically remove missing / add new aux. + void scanAuxComponents(); + // Set component colours upon config changed. + void setComponentColors(); + + protected slots: + virtual void controllerChanged(double val, bool isOff, int id, int scrollMode); + virtual void controllerMoved(double, int, bool); + virtual void controllerPressed(double, int); + virtual void controllerReleased(double, int); + virtual void controllerRightClicked(QPoint, int); + virtual void propertyChanged(double val, bool isOff, int id, int scrollMode); + virtual void propertyMoved(double, int, bool); + virtual void propertyPressed(double, int); + virtual void propertyReleased(double, int); + virtual void propertyRightClicked(QPoint, int); + virtual void auxChanged(double val, bool isOff, int id, int scrollMode); + virtual void auxMoved(double, int, bool); + virtual void auxPressed(double, int); + virtual void auxReleased(double, int); + virtual void auxRightClicked(QPoint p, int); + + public slots: + virtual void configChanged(); + virtual void songChanged(MusECore::SongChangedFlags_t); + + public: + AudioComponentRack(MusECore::AudioTrack* track, int id = -1, bool _manageAuxs = false, QWidget* parent = 0, Qt::WindowFlags f = 0); + + MusECore::AudioTrack* track() { return _track; } + // Adds a component to the layout and the list. Creates a new component using + // the given desc values if the desc widget is not given. + virtual void newComponent( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); + // Updates all the components, for example updating the values of sliders. + void updateComponents(); + // Enable or disable all the aux components in this rack. + void setAuxEnabled(bool enable); +}; + + //--------------------------------------------------------- // AudioStrip //--------------------------------------------------------- class AudioStrip : public Strip { Q_OBJECT - + + public: + // ID numbers for each rack in this strip. + enum AStripRacks { aStripUpperRack = 0, aStripInfoRack = 1, aStripLowerRack = 2 }; + + private: + GridPosStruct _preScrollAreaPos_A; + GridPosStruct _preScrollAreaPos_B; + GridPosStruct _effectRackPos; + GridPosStruct _stereoToolPos; + GridPosStruct _preToolPos; + GridPosStruct _gainToolPos; + GridPosStruct _infoSpacerTop; + GridPosStruct _infoSpacerBottom; + GridPosStruct _propertyRackPos; + GridPosStruct _sliderPos; + GridPosStruct _sliderLabelPos; + GridPosStruct _postScrollAreaPos_A; + GridPosStruct _postScrollAreaPos_B; + GridPosStruct _offPos; + GridPosStruct _recPos; + GridPosStruct _mutePos; + GridPosStruct _soloPos; + GridPosStruct _routesPos; + GridPosStruct _inRoutesPos; + GridPosStruct _outRoutesPos; + GridPosStruct _automationPos; + GridPosStruct _rightSpacerPos; + GridPosStruct _offMonRecPos; + int channel; MusEGui::Slider* slider; MusEGui::DoubleLabel* sl; EffectRack* rack; - MusEGui::Knob* pan; - MusEGui::DoubleLabel* panl; - - MusEGui::Knob* gain; - MusEGui::DoubleLabel* gainLabel; - - std::vector auxKnob; - std::vector auxLabel; - - QToolButton* stereo; - QToolButton* pre; - MusEGui::TransparentToolButton* off; - MusEGui::TransparentToolButton* showArr; + AudioComponentRack* _upperRack; + AudioComponentRack* _lowerRack; + AudioComponentRack* _infoRack; + + // Whether the layout is in mode A (normal, racks on left) or B (racks on right). + bool _isExpanded; + // Current local state of knobs versus sliders preference global setting. + bool _preferKnobs; + + IconButton* _recMonitor; + IconButton* stereo; + IconButton* pre; + IconButton* off; double volume; - double panVal; + bool _volPressed; - //QToolButton* iR; - //QToolButton* oR; - - MusEGui::Knob* addKnob(Knob::KnobType, int, MusEGui::DoubleLabel**, QLabel *name); + ClipperLabel* _clipperLabel[MAX_CHANNELS]; + QHBoxLayout* _clipperLayout; + + void setClipperTooltip(int ch); void updateOffState(); void updateVolume(); - void updatePan(); void updateChannels(); - void updateRouteButtons(); + void updateRackSizes(bool upper, bool lower); private slots: + void recMonitorToggled(bool); void stereoToggled(bool); void preToggled(bool); void offToggled(bool); void iRoutePressed(); void oRoutePressed(); - void auxChanged(double, int); - void gainChanged(double); - void volumeChanged(double,int,bool); - void volumePressed(); - void volumeReleased(); - void panChanged(double,int,bool); - void panPressed(); - void panReleased(); + void volumeMoved(double val, int id, bool shift_pressed); + void volumeChanged(double val, int id, int scrollMode); + void volumePressed(double val, int id); + void volumeReleased(double val, int id); void volLabelChanged(double); - void panLabelChanged(double); - void auxLabelChanged(double, unsigned int); - void volumeRightClicked(const QPoint &); - void panRightClicked(const QPoint &); + void volumeRightClicked(QPoint); + void resetClipper(); protected slots: virtual void heartBeat(); @@ -119,12 +201,38 @@ public slots: virtual void configChanged(); virtual void songChanged(MusECore::SongChangedFlags_t); + void incVolume(int v); + void incPan(int v); public: - AudioStrip(QWidget* parent, MusECore::AudioTrack*); - ~AudioStrip(); - ///virtual QSize minimumSizeHint () const; - //virtual QSize sizeHint () const; + AudioStrip(QWidget* parent, MusECore::AudioTrack*, bool hasHandle = false, bool isEmbedded = true); + virtual ~AudioStrip(); + + static const double volSliderStep; + static const double volSliderMax; + static const int volSliderPrec; + + static const double auxSliderStep; + static const double auxSliderMax; + static const int auxSliderPrec; + + static const double gainSliderStep; + static const double gainSliderMin; + static const double gainSliderMax; + static const int gainSliderPrec; + + static const int xMarginHorSlider; + static const int yMarginHorSlider; + static const int upperRackSpacerHeight; + static const int rackFrameWidth; + + // Destroy and rebuild strip components. + virtual void buildStrip(); + + // Sets up tabbing for the entire strip. + // Accepts a previousWidget which can be null and returns the last widget in the strip, + // which allows chaining other widgets. + virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/mixer/auxknob.cpp muse-3.0.2+ds1/muse/mixer/auxknob.cpp --- muse-2.1.2/muse/mixer/auxknob.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/auxknob.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -24,6 +24,7 @@ #include #include "auxknob.h" #include "gconfig.h" +#include "muse_math.h" namespace MusEGui { @@ -51,7 +52,7 @@ val -= 1.0; // display special value "off" } else - vol = pow(10.0, val/20.0); + vol = muse_db2val(val); emit auxChanged(idx, vol); } diff -Nru muse-2.1.2/muse/mixer/CMakeLists.txt muse-3.0.2+ds1/muse/mixer/CMakeLists.txt --- muse-2.1.2/muse/mixer/CMakeLists.txt 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( mixer_mocs +QT5_WRAP_CPP ( mixer_mocs amixer.h astrip.h auxknob.h @@ -40,7 +40,7 @@ file (GLOB mixer_ui_files routedialogbase.ui ) -QT4_WRAP_UI ( mixer_uis ${mixer_ui_files} ) +QT5_WRAP_UI ( mixer_uis ${mixer_ui_files} ) ## ## List of source files to compile diff -Nru muse-2.1.2/muse/mixer/mstrip.cpp muse-3.0.2+ds1/muse/mixer/mstrip.cpp --- muse-2.1.2/muse/mixer/mstrip.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/mstrip.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: mstrip.cpp,v 1.9.2.13 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011 - 2017 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,273 +22,1599 @@ // //========================================================= -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "app.h" +#include "midi.h" +#include "midictrl.h" +#include "ctrl.h" +#include "mstrip.h" +#include "midiport.h" +#include "globals.h" +#include "audio.h" +#include "song.h" +#include "slider.h" +#include "knob.h" +#include "combobox.h" +#include "meter.h" +#include "track.h" +#include "doublelabel.h" +#include "rack.h" +#include "node.h" +#include "amixer.h" +#include "icons.h" +#include "gconfig.h" +#include "pixmap_button.h" +#include "popupmenu.h" +#include "routepopup.h" + +#include "minstrument.h" +#include "midievent.h" +#include "compact_knob.h" +#include "compact_slider.h" +#include "compact_patch_edit.h" +#include "lcd_widgets.h" +#include "elided_label.h" +#include "utils.h" +#include "muse_math.h" +#include "operations.h" + +#include "synth.h" +#ifdef LV2_SUPPORT +#include "lv2host.h" +#endif + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_MIDI_STRIP(dev, format, args...) // fprintf(dev, format, ##args); + +namespace MusEGui { + +const double MidiStrip::volSliderStepLin = 1.0; + +const double MidiStrip::volSliderStepDb = 0.5; +const double MidiStrip::volSliderMaxDb = 0.0; +const int MidiStrip::volSliderPrecDb = 1; + +const int MidiStrip::xMarginHorSlider = 1; +const int MidiStrip::yMarginHorSlider = 1; +const int MidiStrip::upperRackSpacerHeight = 2; +const int MidiStrip::rackFrameWidth = 1; + +//--------------------------------------------------------- +// MidiComponentRack +//--------------------------------------------------------- + +MidiComponentRack::MidiComponentRack(MusECore::MidiTrack* track, int id, QWidget* parent, Qt::WindowFlags f) + : ComponentRack(id, parent, f), _track(track) +{ + +} + +void MidiComponentRack::newComponent( ComponentDescriptor* desc, const ComponentWidget& before ) +{ + int min = 0; + int max = 0; + int val = 0; + bool hasOffMode = false; + bool off = false; + bool showval = MusEGlobal::config.showControlValues; + + switch(desc->_componentType) + { + case controllerComponent: + { + const int midiCtrlNum = desc->_index; + const int chan = _track->outChannel(); + const int port = _track->outPort(); + if(chan < 0 || chan >= MIDI_CHANNELS || port < 0 || port >= MIDI_PORTS) + return; + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiController* mc = mp->midiController(midiCtrlNum); // Auto-create the controller if necessary. + if(!mc) + return; + min = mc->minVal(); + max = mc->maxVal(); + + if(midiCtrlNum == MusECore::CTRL_PROGRAM) + { + val = mp->hwCtrlState(chan, midiCtrlNum); + if(val == MusECore::CTRL_VAL_UNKNOWN) + { + int lastv = mp->lastValidHWCtrlState(chan, midiCtrlNum); + if(lastv == MusECore::CTRL_VAL_UNKNOWN) + { + if(mc->initVal() == MusECore::CTRL_VAL_UNKNOWN) + val = 0; + else + val = mc->initVal(); + } + off = true; + } + } + else + { + hasOffMode = true; + val = mp->hwCtrlState(chan, midiCtrlNum); + if(val == MusECore::CTRL_VAL_UNKNOWN) + { + int lastv = mp->lastValidHWCtrlState(chan, midiCtrlNum); + if(lastv == MusECore::CTRL_VAL_UNKNOWN) + { + if(mc->initVal() == MusECore::CTRL_VAL_UNKNOWN) + val = 0; + else + val = mc->initVal(); + } + else + val = lastv - mc->bias(); + off = true; + } + else + { + // Auto bias... + val -= mc->bias(); + } + } + + if(desc->_label.isEmpty()) + { + QString ctlname = mc->name(); + if(ctlname.isEmpty()) + { + switch(midiCtrlNum) + { + case MusECore::CTRL_PROGRAM: + ctlname = tr("Pro"); + break; + + case MusECore::CTRL_VARIATION_SEND: + ctlname = tr("Var"); + break; + + case MusECore::CTRL_REVERB_SEND: + ctlname = tr("Rev"); + break; + + case MusECore::CTRL_CHORUS_SEND: + ctlname = tr("Cho"); + break; + + case MusECore::CTRL_PANPOT: + ctlname = tr("Pan"); + break; + + default: + ctlname = QString("#%1").arg(midiCtrlNum); + break; + } + } + desc->_label = ctlname; + } + + if(desc->_toolTipText.isEmpty()) + { + QString ctlname = mc->name(); + if(ctlname.isEmpty()) + { + switch(midiCtrlNum) + { + case MusECore::CTRL_PROGRAM: + ctlname = tr("Program"); + break; + + case MusECore::CTRL_VARIATION_SEND: + ctlname = tr("VariationSend"); + break; + + case MusECore::CTRL_REVERB_SEND: + ctlname = tr("ReverbSend"); + break; + + case MusECore::CTRL_CHORUS_SEND: + ctlname = tr("ChorusSend"); + break; + + case MusECore::CTRL_PANPOT: + ctlname = tr("Pan/Balance"); + break; + + default: + ctlname = tr("Controller"); + break; + } + } + desc->_toolTipText = QString("%1 (# %2)\n%3").arg(ctlname).arg(midiCtrlNum).arg(tr("(Ctrl-double-click on/off)")); + } + + if(!desc->_color.isValid()) + { + switch(midiCtrlNum) + { + case MusECore::CTRL_PANPOT: + desc->_color = MusEGlobal::config.panSliderColor; + break; + + case MusECore::CTRL_PROGRAM: + desc->_color = MusEGlobal::config.midiPatchReadoutColor; + break; + + default: + desc->_color = MusEGlobal::config.midiControllerSliderDefaultColor; + break; + } + } + } + break; + + case propertyComponent: + { + switch(desc->_index) + { + case mStripInstrumentProperty: + { + const int port = _track->outPort(); + if(port >= 0 && port < MIDI_PORTS) + { + if(MusECore::MidiInstrument* minstr = MusEGlobal::midiPorts[_track->outPort()].instrument()) + { + desc->_enabled = !minstr->isSynti(); + if(desc->_label.isEmpty()) + desc->_label = minstr->iname(); + } + else + { + desc->_enabled = false; + if(desc->_label.isEmpty()) + desc->_label = tr(""); + } + } + if(desc->_toolTipText.isEmpty()) + desc->_toolTipText = tr("Instrument"); + } + break; + + case mStripTranspProperty: + { + val = _track->transposition; + min = -127; + max = 127; + if(desc->_label.isEmpty()) + desc->_label = tr("Transpose"); + if(desc->_toolTipText.isEmpty()) + desc->_toolTipText = tr("Transpose notes up or down"); + if(!desc->_color.isValid()) + desc->_color = MusEGlobal::config.midiPropertySliderDefaultColor; + } + break; + + case mStripDelayProperty: + { + val = _track->delay; + min = -1000; + max = 1000; + if(desc->_label.isEmpty()) + desc->_label = tr("Delay"); + if(desc->_toolTipText.isEmpty()) + desc->_toolTipText = tr("Offset playback of notes before or after actual note"); + if(!desc->_color.isValid()) + desc->_color = MusEGlobal::config.midiPropertySliderDefaultColor; + } + break; + + case mStripLenProperty: + { + val = _track->len; + min = 25; + max = 200; + if(desc->_label.isEmpty()) + desc->_label = tr("Length"); + if(desc->_toolTipText.isEmpty()) + desc->_toolTipText = tr("Change note length in percent of actual length"); + if(!desc->_color.isValid()) + desc->_color = MusEGlobal::config.midiPropertySliderDefaultColor; + } + break; + + case mStripVeloProperty: + { + val = _track->velocity; + min = -127; + max = 127; + if(desc->_label.isEmpty()) + desc->_label = tr("Velocity"); + if(desc->_toolTipText.isEmpty()) + desc->_toolTipText = tr("

    Add or substract velocity to notes" + " on track.

    Since" + " the midi note range is 0-127 this
    might mean that the" + " notes do not reach
    the combined velocity, note + " + " Velocity.

    "); + if(!desc->_color.isValid()) + desc->_color = MusEGlobal::config.midiPropertySliderDefaultColor; + } + break; + + case mStripComprProperty: + { + val = _track->compression; + min = 25; + max = 200; + if(desc->_label.isEmpty()) + desc->_label = tr("Compress"); + if(desc->_toolTipText.isEmpty()) + desc->_toolTipText = tr("Compress the notes velocity range, in percent of actual velocity"); + if(!desc->_color.isValid()) + desc->_color = MusEGlobal::config.midiPropertySliderDefaultColor; + } + break; + + } + } + break; + } + + switch(desc->_widgetType) + { + case ElidedLabelComponentWidget: + { + ElidedLabelComponentDescriptor* d = static_cast(desc); + + // Adds a component. Creates a new component using the given desc values if the desc widget is not given. + // Connects known widget types' signals to slots. + newComponentWidget(d, before); + } + break; + + case CompactKnobComponentWidget: + { + CompactKnobComponentDescriptor* d = static_cast(desc); + d->_min = min; + d->_max = max; + d->_precision = 0; + d->_step = 1.0; + d->_initVal = val; + d->_hasOffMode = hasOffMode; + d->_isOff = off; + d->_showValue = showval; + if(!d->_color.isValid()) + d->_color = MusEGlobal::config.sliderDefaultColor; + + // Adds a component. Creates a new component using the given desc values if the desc widget is not given. + // Connects known widget types' signals to slots. + newComponentWidget(d, before); + } + break; + + case CompactSliderComponentWidget: + { + CompactSliderComponentDescriptor* d = static_cast(desc); + d->_min = min; + d->_max = max; + d->_precision = 0; + d->_step = 1.0; + d->_initVal = val; + d->_hasOffMode = hasOffMode; + d->_isOff = off; + d->_showValue = showval; + if(!d->_color.isValid()) + d->_color = MusEGlobal::config.sliderDefaultColor; + // Set the bar color the same. + if(!d->_barColor.isValid()) + //d->_barColor = d->_color; + d->_barColor = MusEGlobal::config.sliderBarDefaultColor; + + // Adds a component. Creates a new component using the given desc values if the desc widget is not given. + // Connects known widget types' signals to slots. + newComponentWidget(d, before); + } + break; + + case mStripCompactPatchEditComponentWidget: + { + CompactPatchEditComponentDescriptor* d = static_cast(desc); + d->_initVal = val; + d->_isOff = off; + if(!d->_color.isValid()) + d->_color = MusEGlobal::config.midiPatchReadoutColor; + + // Adds a component. Creates a new component using the given desc values if the desc widget is not given. + // Connects known widget types' signals to slots. + newComponentWidget(d, before); + } + break; + } +} + +void MidiComponentRack::newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before ) +{ + switch(desc->_widgetType) + { + case mStripCompactPatchEditComponentWidget: + { + CompactPatchEditComponentDescriptor* d = static_cast(desc); + if(!d->_compactPatchEdit) + { + CompactPatchEdit* control = new CompactPatchEdit(0, + d->_objName, + CompactSlider::None); + d->_compactPatchEdit = control; + control->setId(d->_index); + control->setValue(d->_initVal); + // Don't allow anything here, it interferes with the CompactPatchEdit which sets it's own controls' tooltips. + //control->setToolTip(d->_toolTipText); + control->setEnabled(d->_enabled); + control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + control->setContentsMargins(0, 0, 0, 0); + + if(d->_color.isValid()) + control->setReadoutColor(d->_color); + + control->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + + connect(d->_compactPatchEdit, SIGNAL(valueChanged(int,int)), SLOT(controllerChanged(int,int))); + connect(d->_compactPatchEdit, SIGNAL(patchValueRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int))); + connect(d->_compactPatchEdit, SIGNAL(patchNameClicked(QPoint,int)), SLOT(patchEditNameClicked(QPoint,int))); + connect(d->_compactPatchEdit, SIGNAL(patchNameRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int))); + } + + ComponentWidget cw = ComponentWidget( + d->_compactPatchEdit, + d->_widgetType, + d->_componentType, + d->_index + ); + + addComponentWidget(cw, before); + return; + } + break; + } + + // If not handled above, let ancestor handle it. + ComponentRack::newComponentWidget(desc, before); +} + +void MidiComponentRack::scanControllerComponents() +{ + const int chan = _track->outChannel(); + const int port = _track->outPort(); + if(chan < 0 || chan >= MIDI_CHANNELS || port < 0 || port >= MIDI_PORTS) + return; + + QString namestr; + std::vector to_be_erased; + for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + ComponentWidget& cw = *ic; + if(!cw._widget) + continue; + + switch(cw._componentType) + { + case controllerComponent: + { + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiCtrlValListList* mcvll = mp->controller(); + MusECore::ciMidiCtrlValList imcvll = mcvll->find(chan, cw._index); + if(imcvll == mcvll->end()) + to_be_erased.push_back(ic); + else + { + // While we are here, let's update the name of the control, in case the instrument changed. + switch(cw._widgetType) + { + case CompactKnobComponentWidget: + case CompactSliderComponentWidget: + { + // false = do not create the controller if not found. + MusECore::MidiController* mc = mp->midiController(cw._index, false); + if(mc) + setComponentText(cw, mc->name()); + } + break; + } + } + } + break; + } + } + for(std::vector::iterator i = to_be_erased.begin(); i != to_be_erased.end(); ++i) + { + iComponentWidget icw = *i; + ComponentWidget& cw = *icw; + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::scanControllerComponents: deleting controller component index:%d\n", cw._index); + if(cw._widget) + delete cw._widget; + _components.erase(icw); + } +} + +void MidiComponentRack::updateComponents() +{ + for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + ComponentWidget& cw = *ic; + if(!cw._widget) + continue; + + switch(cw._componentType) + { + case controllerComponent: + { + // Inhibit the controller stream if control is currently pressed. + // Note _pressed operates differently than simply checking if the control is pressed! + if(cw._pressed) + continue; + + const int channel = _track->outChannel(); + const int port = _track->outPort(); + if(channel < 0 || channel >= MIDI_CHANNELS || port < 0 || port >= MIDI_PORTS) + continue; + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiCtrlValListList* mcvll = mp->controller(); + + MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, cw._index); + const bool enable = imcvl != mcvll->end() && !_track->off(); + if(cw._widget->isEnabled() != enable) + cw._widget->setEnabled(enable); + + if(enable) + { + MusECore::MidiCtrlValList* mcvl = imcvl->second; + switch(cw._index) + { + case MusECore::CTRL_PROGRAM: + { + switch(cw._widgetType) + { + case mStripCompactPatchEditComponentWidget: + { + // Special for new LCD patch edit control: Need to give both current and last values. + // Keeping a local last value with the control won't work. + CompactPatchEdit* control = static_cast(cw._widget); + const int hwVal = mcvl->hwVal(); + control->blockSignals(true); + control->setLastValidValue(mcvl->lastValidHWVal()); + control->setLastValidBytes(mcvl->lastValidByte2(), mcvl->lastValidByte1(), mcvl->lastValidByte0()); + control->setValue(hwVal); + control->blockSignals(false); + + if(hwVal == MusECore::CTRL_VAL_UNKNOWN) + { + control->setPatchNameOff(true); + const QString patchName(tr("")); + if(control->patchName() != patchName) + control->setPatchName(patchName); + } + else + { + // Try to avoid calling MidiInstrument::getPatchName too often. +// if(_heartBeatCounter == 0) + { + control->setPatchNameOff(false); + MusECore::MidiInstrument* instr = mp->instrument(); + QString patchName(instr->getPatchName(channel, hwVal, _track->isDrumTrack(), true)); // Include default. + if(patchName.isEmpty()) + patchName = QString("???"); + if(control->patchName() != patchName) + control->setPatchName(patchName); + } + } + } + break; + } + } + break; + + default: + { + switch(cw._widgetType) + { + case CompactKnobComponentWidget: + { + CompactKnob* control = static_cast(cw._widget); + + int hwVal = mcvl->hwVal(); + int min = 0; + int max = 127; + int bias = 0; + int initval = 0; + MusECore::MidiController* mc = mp->midiController(cw._index, false); + if(mc) + { + bias = mc->bias(); + min = mc->minVal(); + max = mc->maxVal(); + initval = mc->initVal(); + if(initval == MusECore::CTRL_VAL_UNKNOWN) + initval = 0; + } + + const double dmin = (double)min; + const double dmax = (double)max; + const double c_dmin = control->minValue(); + const double c_dmax = control->maxValue(); + if(c_dmin != min && c_dmax != max) + { + control->blockSignals(true); + control->setRange(dmin, dmax, 1.0); + control->blockSignals(false); + } + else if(c_dmin != min) + { + control->blockSignals(true); + control->setMinValue(min); + control->blockSignals(false); + } + else if(c_dmax != max) + { + control->blockSignals(true); + control->setMaxValue(max); + control->blockSignals(false); + } + + if(hwVal == MusECore::CTRL_VAL_UNKNOWN) + { + hwVal = mcvl->lastValidHWVal(); + if(hwVal == MusECore::CTRL_VAL_UNKNOWN) + { + hwVal = initval; + if(!control->isOff() || hwVal != control->value()) + { + control->blockSignals(true); + control->setValueState(hwVal, true); + control->blockSignals(false); + } + } + else + { + hwVal -= bias; + if(!control->isOff() || hwVal != control->value()) + { + control->blockSignals(true); + control->setValueState(hwVal, true); + control->blockSignals(false); + } + } + } + else + { + hwVal -= bias; + if(control->isOff() || hwVal != control->value()) + { + control->blockSignals(true); + control->setValueState(hwVal, false); + control->blockSignals(false); + } + } + } + break; + + case CompactSliderComponentWidget: + { + CompactSlider* control = static_cast(cw._widget); + + int hwVal = mcvl->hwVal(); + int min = 0; + int max = 127; + int bias = 0; + int initval = 0; + MusECore::MidiController* mc = mp->midiController(cw._index, false); + if(mc) + { + bias = mc->bias(); + min = mc->minVal(); + max = mc->maxVal(); + initval = mc->initVal(); + if(initval == MusECore::CTRL_VAL_UNKNOWN) + initval = 0; + } + + const double dmin = (double)min; + const double dmax = (double)max; + const double c_dmin = control->minValue(); + const double c_dmax = control->maxValue(); + if(c_dmin != min && c_dmax != max) + { + control->blockSignals(true); + control->setRange(dmin, dmax, 1.0); + control->blockSignals(false); + } + else if(c_dmin != min) + { + control->blockSignals(true); + control->setMinValue(min); + control->blockSignals(false); + } + else if(c_dmax != max) + { + control->blockSignals(true); + control->setMaxValue(max); + control->blockSignals(false); + } + + if(hwVal == MusECore::CTRL_VAL_UNKNOWN) + { + hwVal = mcvl->lastValidHWVal(); + if(hwVal == MusECore::CTRL_VAL_UNKNOWN) + { + hwVal = initval; + if(!control->isOff() || hwVal != control->value()) + { + control->blockSignals(true); + control->setValueState(hwVal, true); + control->blockSignals(false); + } + } + else + { + hwVal -= bias; + if(!control->isOff() || hwVal != control->value()) + { + control->blockSignals(true); + control->setValueState(hwVal, true); + control->blockSignals(false); + } + } + } + else + { + hwVal -= bias; + if(control->isOff() || hwVal != control->value()) + { + control->blockSignals(true); + control->setValueState(hwVal, false); + control->blockSignals(false); + } + } + } + break; + } + } + break; + } + } + } + break; + + case propertyComponent: + { + switch(cw._index) + { + case mStripInstrumentProperty: + { + const int port = _track->outPort(); + if(port >= 0 && port < MIDI_PORTS) + { + if(MusECore::MidiInstrument* minstr = MusEGlobal::midiPorts[_track->outPort()].instrument()) + { + setComponentEnabled(cw, !minstr->isSynti()); + setComponentText(cw, minstr->iname()); + } + else + { + setComponentEnabled(cw, false); + setComponentText(cw, tr("")); + } + } + } + break; + + case mStripTranspProperty: + setComponentValue(cw, _track->transposition); // Signals blocked. Redundant ignored. + break; + + case mStripDelayProperty: + setComponentValue(cw, _track->delay); // Signals blocked. Redundant ignored. + break; + + case mStripLenProperty: + setComponentValue(cw, _track->len); // Signals blocked. Redundant ignored. + break; + + case mStripVeloProperty: + setComponentValue(cw, _track->velocity); // Signals blocked. Redundant ignored. + break; + + case mStripComprProperty: + setComponentValue(cw, _track->compression); // Signals blocked. Redundant ignored. + break; + } + } + break; + } + } +} + +//--------------------------------------------------------- +// instrPopup +//--------------------------------------------------------- + +void MidiComponentRack::instrPopup(QPoint p) +{ + const int port = _track->outPort(); + if(port < 0 || port >= MIDI_PORTS) + return; + + MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); + if(!instr) + return; + + PopupMenu* pup = new PopupMenu(false); + + MusECore::MidiInstrument::populateInstrPopup(pup, instr, false); + + if(pup->actions().count() == 0) + { + delete pup; + return; + } + + QAction *act = pup->exec(p); + if(act) + { + QString s = act->text(); + for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) + { + if((*i)->iname() == s) + { + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + MusEGlobal::midiPorts[port].changeInstrument(*i); + MusEGlobal::audio->msgIdle(false); + // Make sure device initializations are sent if necessary. + MusEGlobal::audio->msgInitMidiDevices(false); // false = Don't force + MusEGlobal::song->update(SC_MIDI_INSTRUMENT); + break; + } + } + } + delete pup; +} + +//--------------------------------------------------------- +// patchPopup +//--------------------------------------------------------- + +void MidiComponentRack::patchPopup(QPoint p) +{ + const int channel = _track->outChannel(); + const int port = _track->outPort(); + if(channel < 0 || channel >= MIDI_CHANNELS || port < 0 || port >= MIDI_PORTS) + return; + + MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); + PopupMenu* pup = new PopupMenu(true); + + instr->populatePatchPopup(pup, channel, _track->isDrumTrack()); + + if(pup->actions().count() == 0) + { + delete pup; + return; + } + + connect(pup, SIGNAL(triggered(QAction*)), SLOT(patchPopupActivated(QAction*))); + + pup->exec(p); + delete pup; +} + +//--------------------------------------------------------- +// patchPopupActivated +//--------------------------------------------------------- + +void MidiComponentRack::patchPopupActivated(QAction* act) +{ + if(!act) + return; + + const int channel = _track->outChannel(); + const int port = _track->outPort(); + if(channel < 0 || channel >= MIDI_CHANNELS || port < 0 || port >= MIDI_PORTS) + return; + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiInstrument* instr = mp->instrument(); + if(!instr) + return; + + if(act->data().type() == QVariant::Int || act->data().type() == QVariant::UInt) + { + bool ok; + int rv = act->data().toInt(&ok); + if(ok && rv != -1) + { + // If the chosen patch's number is don't care (0xffffff), + // then by golly since we "don't care" let's just set it to '-/-/1'. + // 0xffffff cannot be a valid patch number... yet... + if(rv == MusECore::CTRL_PROGRAM_VAL_DONT_CARE) + rv = 0xffff00; + MusECore::MidiPlayEvent ev(MusEGlobal::audio->curFrame(), port, channel, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, rv); + mp->putEvent(ev); + } + } + else if(instr->isSynti() && act->data().canConvert()) + { + MusECore::SynthI *si = static_cast(instr); + MusECore::Synth *s = si->synth(); +#ifdef LV2_SUPPORT + //only for lv2 synths call applyPreset function. + if(s && s->synthType() == MusECore::Synth::LV2_SYNTH) + { + MusECore::LV2SynthIF *sif = static_cast(si->sif()); + //be pedantic about checks + if(sif) + { + if(mp) + { + if(mp->hwCtrlState(channel, MusECore::CTRL_PROGRAM) != MusECore::CTRL_VAL_UNKNOWN) + mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), port, channel, + MusECore::ME_CONTROLLER, + MusECore::CTRL_PROGRAM, + MusECore::CTRL_VAL_UNKNOWN)); + sif->applyPreset(act->data().value()); + } + } + } +#endif + } +} + +void MidiComponentRack::controllerChanged(int v, int id) +{ + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerChanged id:%d val:%d\n", id, val); + +// if (inHeartBeat) +// return; + int val = v; + int port = _track->outPort(); + int channel = _track->outChannel(); + if(channel < 0 || channel >= MIDI_CHANNELS || port < 0 || port >= MIDI_PORTS) + { + emit componentChanged(controllerComponent, val, false, id, 0); + return; + } + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + + MusECore::MidiCtrlValListList* mcvll = mp->controller(); + MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, id); + if(imcvl == mcvll->end()) + { + emit componentChanged(controllerComponent, val, false, id, 0); + return; + } + + MusECore::MidiController* mc = mp->midiController(id, false); + if(mc) + { + int ival = val; + //if(off || ival < mc->minVal() || ival > mc->maxVal()) + if(ival < mc->minVal() || ival > mc->maxVal()) + ival = MusECore::CTRL_VAL_UNKNOWN; + + if(ival != MusECore::CTRL_VAL_UNKNOWN) + // Auto bias... + ival += mc->bias(); + + MusECore::MidiPlayEvent ev(MusEGlobal::audio->curFrame(), port, channel, MusECore::ME_CONTROLLER, id, ival); + mp->putEvent(ev); + } + + emit componentChanged(controllerComponent, v, false, id, 0); +} + +void MidiComponentRack::controllerChanged(double val, int id) +{ + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerChanged id:%d val:%.20f\n", id, val); + controllerChanged(int(lrint(val)), id); +} + +void MidiComponentRack::controllerChanged(double val, bool off, int id, int scrollMode) +{ + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerChanged id:%d val:%.20f scrollMode:%d\n", id, val, scrollMode); + + int port = _track->outPort(); + int channel = _track->outChannel(); + if(channel < 0 || channel >= MIDI_CHANNELS || port < 0 || port >= MIDI_PORTS) + { + emit componentChanged(controllerComponent, val, off, id, scrollMode); + return; + } + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + + MusECore::MidiCtrlValListList* mcvll = mp->controller(); + MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, id); + if(imcvl == mcvll->end()) + { + emit componentChanged(controllerComponent, val, off, id, scrollMode); + return; + } + + MusECore::MidiController* mc = mp->midiController(id, false); + if(mc) + { + int ival = lrint(val); + if(off || ival < mc->minVal() || ival > mc->maxVal()) + ival = MusECore::CTRL_VAL_UNKNOWN; + + if(ival != MusECore::CTRL_VAL_UNKNOWN) + // Auto bias... + ival += mc->bias(); + + MusECore::MidiPlayEvent ev(MusEGlobal::audio->curFrame(), port, channel, MusECore::ME_CONTROLLER, id, ival); + mp->putEvent(ev); + } + + emit componentChanged(controllerComponent, val, off, id, scrollMode); +} + +void MidiComponentRack::controllerMoved(double val, int id, bool shift_pressed) +{ + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerMoved id:%d val:%.20f\n", id, val); + emit componentMoved(controllerComponent, val, id, shift_pressed); +} + +void MidiComponentRack::controllerPressed(double val, int id) +{ + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerPressed id:%d\n", id); + emit componentPressed(controllerComponent, val, id); +} + +void MidiComponentRack::controllerReleased(double val, int id) +{ + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerReleased id:%d\n", id); + emit componentReleased(controllerComponent, val, id); +} + +void MidiComponentRack::controllerRightClicked(QPoint p, int id) +{ + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerRightClicked id:%d\n", id); + MusEGlobal::song->execMidiAutomationCtlPopup(_track, 0, p, id); // Do not give a parent here, otherwise accelerators are returned in text() ! +} + + +void MidiComponentRack::propertyChanged(double val, bool off, int id, int scrollMode) +{ + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::propertyChanged id:%d val:%.20f\n", id, val); + + const int ival = lrint(val); + + // FIXME ! This direct setting is probably not safe. Use a FIFO buffer or something. + // A problem with using MidiPort::putEvent is that it does not appear to be safe + // when directly setting the hwValues. + switch(id) + { + case mStripTranspProperty: + _track->transposition = ival; + break; + + case mStripDelayProperty: + _track->delay = ival; + break; + + case mStripLenProperty: + _track->len = ival; + break; + + case mStripVeloProperty: + _track->velocity = ival; + break; + + case mStripComprProperty: + _track->compression = ival; + break; + } -#include -#include -#include -//#include -#include -#include -#include -#include -#include -//#include -#include -#include + emit componentChanged(propertyComponent, val, off, id, scrollMode); +} -#include -#include "app.h" -#include "midi.h" -#include "midictrl.h" -#include "mstrip.h" -#include "midiport.h" -#include "globals.h" -#include "audio.h" -#include "song.h" -#include "slider.h" -#include "knob.h" -#include "combobox.h" -#include "meter.h" -#include "track.h" -#include "doublelabel.h" -#include "rack.h" -#include "node.h" -#include "amixer.h" -#include "icons.h" -#include "gconfig.h" -#include "ttoolbutton.h" -//#include "utils.h" -//#include "popupmenu.h" -#include "routepopup.h" +void MidiComponentRack::propertyMoved(double val, int id, bool shift_pressed) +{ + DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::propertyMoved id:%d val:%.20f\n", id, val); + emit componentMoved(propertyComponent, val, id, shift_pressed); +} -namespace MusEGui { +void MidiComponentRack::propertyPressed(double val, int id) +{ + emit componentPressed(propertyComponent, val, id); +} + +void MidiComponentRack::propertyReleased(double val, int id) +{ + emit componentReleased(propertyComponent, val, id); +} + +void MidiComponentRack::propertyRightClicked(QPoint, int) +{ + +} + +void MidiComponentRack::labelPropertyPressed(QPoint p, int id, Qt::MouseButtons /*buttons*/, Qt::KeyboardModifiers keys) +{ + labelPropertyPressHandler(p, id, keys); +} + +void MidiComponentRack::labelPropertyReleased(QPoint /*p*/, int /*id*/, Qt::MouseButtons /*buttons*/, Qt::KeyboardModifiers /*keys*/) +{ + +} + +void MidiComponentRack::labelPropertyReturnPressed(QPoint p, int id, Qt::KeyboardModifiers keys) +{ + labelPropertyPressHandler(p, id, keys); +} + +void MidiComponentRack::labelPropertyPressHandler(QPoint /*p*/, int id, Qt::KeyboardModifiers /*keys*/) +{ + switch(id) + { + case mStripInstrumentProperty: + { + ciComponentWidget icw = _components.find(propertyComponent, -1, id); + if(icw == _components.end()) + return; + + const ComponentWidget& cw = *icw; + if(!cw._widget) + return; + + instrPopup(cw._widget->mapToGlobal(QPoint(10,5))); + } + break; + } +} + +void MidiComponentRack::patchEditNameClicked(QPoint /*p*/, int id) +{ + ciComponentWidget icw = _components.find(controllerComponent, -1, id); + if(icw == _components.end()) + return; + + const ComponentWidget& cw = *icw; + if(!cw._widget) + return; + + patchPopup(cw._widget->mapToGlobal(QPoint(10,5))); +} + + +//--------------------------------------------------------- +// songChanged +//--------------------------------------------------------- -enum { KNOB_PAN, KNOB_VAR_SEND, KNOB_REV_SEND, KNOB_CHO_SEND }; +void MidiComponentRack::songChanged(MusECore::SongChangedFlags_t flags) +{ + // Scan controllers. + if(flags & (SC_RACK | SC_MIDI_CONTROLLER_ADD | SC_MIDI_INSTRUMENT)) + { + scanControllerComponents(); + } +} //--------------------------------------------------------- -// addKnob +// configChanged +// Catch when label font, or configuration min slider and meter values change, or viewable tracks etc. //--------------------------------------------------------- -void MidiStrip::addKnob(int idx, const QString& tt, const QString& label, - const char* slot, bool enabled) +void MidiComponentRack::configChanged() +{ + // Handle font changes etc. + ComponentRack::configChanged(); + + for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + const ComponentWidget& cw = *ic; + if(!cw._widget) + continue; + + // Whether to show values along with labels for certain controls. + setComponentShowValue(cw, MusEGlobal::config.showControlValues); + + switch(cw._widgetType) + { + case mStripCompactPatchEditComponentWidget: { - int ctl = MusECore::CTRL_PANPOT, mn, mx, v; - int chan = ((MusECore::MidiTrack*)track)->outChannel(); - switch(idx) + //CompactPatchEdit* w = static_cast(cw._widget); + //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + } + break; + + case CompactKnobComponentWidget: { - //case KNOB_PAN: - // ctl = MusECore::CTRL_PANPOT; - //break; - case KNOB_VAR_SEND: - ctl = MusECore::CTRL_VARIATION_SEND; - break; - case KNOB_REV_SEND: - ctl = MusECore::CTRL_REVERB_SEND; - break; - case KNOB_CHO_SEND: - ctl = MusECore::CTRL_CHORUS_SEND; - break; + //CompactKnob* w = static_cast(cw._widget); + //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + } + break; + } + } + setComponentColors(); +} + +//--------------------------------------------------------- +// setComponentColors +//--------------------------------------------------------- + +void MidiComponentRack::setComponentColors() +{ + for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + const ComponentWidget& cw = *ic; + if(!cw._widget) + continue; + + QColor color = MusEGlobal::config.sliderDefaultColor; + switch(cw._componentType) + { + case controllerComponent: + { + switch(cw._index) + { + case MusECore::CTRL_PANPOT: + color = MusEGlobal::config.panSliderColor; + break; + + case MusECore::CTRL_PROGRAM: + color = MusEGlobal::config.midiPatchReadoutColor; + break; + + default: + color = MusEGlobal::config.midiControllerSliderDefaultColor; + break; + } } - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[((MusECore::MidiTrack*)track)->outPort()]; - MusECore::MidiController* mc = mp->midiController(ctl); - mn = mc->minVal(); - mx = mc->maxVal(); - - MusEGui::Knob* knob = new MusEGui::Knob(this); - knob->setRange(double(mn), double(mx), 1.0); - knob->setId(ctl); - - controller[idx].knob = knob; - knob->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - knob->setBackgroundRole(QPalette::Mid); - knob->setToolTip(tt); - knob->setEnabled(enabled); - - MusEGui::DoubleLabel* dl = new MusEGui::DoubleLabel(0.0, double(mn), double(mx), this); - dl->setId(idx); - dl->setSpecialText(tr("off")); - dl->setToolTip(tr("ctrl-double-click on/off")); - controller[idx].dl = dl; - ///dl->setFont(MusEGlobal::config.fonts[1]); - dl->setBackgroundRole(QPalette::Mid); - dl->setFrame(true); - dl->setPrecision(0); - dl->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - dl->setEnabled(enabled); - - double dlv; - v = mp->hwCtrlState(chan, ctl); - if(v == MusECore::CTRL_VAL_UNKNOWN) - { - //v = mc->initVal(); - //if(v == MusECore::CTRL_VAL_UNKNOWN) - // v = 0; -// v = mn - 1; - int lastv = mp->lastValidHWCtrlState(chan, ctl); - if(lastv == MusECore::CTRL_VAL_UNKNOWN) + break; + + case propertyComponent: + { + switch(cw._index) { - if(mc->initVal() == MusECore::CTRL_VAL_UNKNOWN) - v = 0; - else - v = mc->initVal(); + case mStripInstrumentProperty: + break; + + case mStripTranspProperty: + case mStripDelayProperty: + case mStripLenProperty: + case mStripVeloProperty: + case mStripComprProperty: + color = MusEGlobal::config.midiPropertySliderDefaultColor; + break; } - else - v = lastv - mc->bias(); - //dlv = mn - 1; - dlv = dl->off() - 1.0; - } - else + } + break; + } + + switch(cw._widgetType) + { + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + w->setFaceColor(color); + } + break; + + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + w->setBorderColor(color); + //w->setBarColor(color); + w->setBarColor(MusEGlobal::config.sliderBarDefaultColor); + } + break; + + case mStripCompactPatchEditComponentWidget: { - // Auto bias... - v -= mc->bias(); - dlv = double(v); - } - - knob->setValue(double(v)); - dl->setValue(dlv); - //} - //else - // knob->setRange(0.0, 127.0); - - QLabel* lb = new QLabel(label, this); - controller[idx].lb = lb; - ///lb->setFont(MusEGlobal::config.fonts[1]); - lb->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - lb->setAlignment(Qt::AlignCenter); - lb->setEnabled(enabled); - - grid->addWidget(lb, _curGridRow, 0); - grid->addWidget(dl, _curGridRow+1, 0); - grid->addWidget(knob, _curGridRow, 1, 2, 1); - _curGridRow += 2; - - connect(knob, SIGNAL(sliderMoved(double,int)), slot); - connect(knob, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(controlRightClicked(const QPoint &, int))); - connect(dl, SIGNAL(valueChanged(double, int)), slot); - connect(dl, SIGNAL(ctrlDoubleClicked(int)), SLOT(labelDoubleClicked(int))); + CompactPatchEdit* w = static_cast(cw._widget); + w->setReadoutColor(color); } + break; + } + } +} + +QWidget* MidiComponentRack::setupComponentTabbing(QWidget* previousWidget) +{ + QWidget* prev = previousWidget; + for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + const ComponentWidget& cw = *ic; + if(cw._widget) + { + switch(cw._widgetType) + { + case mStripCompactPatchEditComponentWidget: + { + CompactPatchEdit* w = static_cast(cw._widget); + prev = w->setupComponentTabbing(prev); + } + break; + + default: + if(prev) + QWidget::setTabOrder(prev, cw._widget); + prev = cw._widget; + break; + } + } + } + return prev; +} + //--------------------------------------------------------- // MidiStrip //--------------------------------------------------------- -MidiStrip::MidiStrip(QWidget* parent, MusECore::MidiTrack* t) - : Strip(parent, t) +MidiStrip::MidiStrip(QWidget* parent, MusECore::MidiTrack* t, bool hasHandle, bool isEmbedded) + : Strip(parent, t, hasHandle, isEmbedded) { inHeartBeat = true; + _heartBeatCounter = 0; + volume = MusECore::CTRL_VAL_UNKNOWN; + + _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; + _preferMidiVolumeDb = MusEGlobal::config.preferMidiVolumeDb; - // Set the whole strip's font, except for the label. p4.0.45 - setFont(MusEGlobal::config.fonts[1]); + slider = 0; + sl = 0; + off = 0; + _recMonitor = 0; + + // Start the layout in mode A (normal, racks on left). + _isExpanded = false; + + // Set the whole strip's font, except for the label. + setFont(MusEGlobal::config.fonts[1]); // For some reason must keep this, the upper rack is too tall at first. + setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); // Clear so the meters don't start off by showing stale values. t->setActivity(0); t->setLastActivity(0); - - volume = MusECore::CTRL_VAL_UNKNOWN; - pan = MusECore::CTRL_VAL_UNKNOWN; - variSend = MusECore::CTRL_VAL_UNKNOWN; - chorusSend = MusECore::CTRL_VAL_UNKNOWN; - reverbSend = MusECore::CTRL_VAL_UNKNOWN; - - addKnob(KNOB_VAR_SEND, tr("VariationSend"), tr("Var"), SLOT(setVariSend(double)), false); - addKnob(KNOB_REV_SEND, tr("ReverbSend"), tr("Rev"), SLOT(setReverbSend(double)), false); - addKnob(KNOB_CHO_SEND, tr("ChorusSend"), tr("Cho"), SLOT(setChorusSend(double)), false); - ///int auxsSize = MusEGlobal::song->auxs()->size(); - ///if (auxsSize) - //layout->addSpacing((STRIP_WIDTH/2 + 1) * auxsSize); - ///grid->addSpacing((STRIP_WIDTH/2 + 1) * auxsSize); // ?? + + _inRoutesPos = GridPosStruct(_curGridRow, 0, 1, 1); + _outRoutesPos = GridPosStruct(_curGridRow, 1, 1, 1); + _routesPos = GridPosStruct(_curGridRow, 0, 1, 2); + + _upperStackTabPos = GridPosStruct(_curGridRow + 1, 0, 1, 2); + + _preScrollAreaPos_A = GridPosStruct(_curGridRow + 2, 0, 1, 3); + + _propertyRackPos = GridPosStruct(_curGridRow + 3, 0, 1, 3); + + _sliderPos = GridPosStruct(_curGridRow + 4, 0, 1, 2); + + _sliderLabelPos = GridPosStruct(_curGridRow + 5, 0, 1, 2); + + _postScrollAreaPos_A = GridPosStruct(_curGridRow + 6, 0, 1, 3); + + _offPos = GridPosStruct(_curGridRow + 7, 0, 1, 1); + _recPos = GridPosStruct(_curGridRow + 7, 1, 1, 1); + _offMonRecPos = GridPosStruct(_curGridRow + 7, 0, 1, 2); + + _mutePos = GridPosStruct(_curGridRow + 8, 0, 1, 1); + _soloPos = GridPosStruct(_curGridRow + 8, 1, 1, 1); + + _automationPos = GridPosStruct(_curGridRow + 9, 0, 1, 2); + + _rightSpacerPos = GridPosStruct(_curGridRow + 9, 2, 1, 1); + + + _upperStackTabButtonA = new ElidedLabel(); + _upperStackTabButtonB = new ElidedLabel(); + _upperStackTabButtonA->setContentsMargins(0, 0, 0, 0); + _upperStackTabButtonB->setContentsMargins(0, 0, 0, 0); + _upperStackTabButtonA->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + _upperStackTabButtonB->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + _upperStackTabButtonA->setFocusPolicy(Qt::StrongFocus); + _upperStackTabButtonB->setFocusPolicy(Qt::StrongFocus); + _upperStackTabButtonA->setAlignment(Qt::AlignCenter); + _upperStackTabButtonB->setAlignment(Qt::AlignCenter); + _upperStackTabButtonA->setToolTip(tr("Palette A")); + _upperStackTabButtonB->setToolTip(tr("Palette B")); + _upperStackTabButtonA->setText(tr("A")); + _upperStackTabButtonB->setText(tr("B")); + _upperStackTabButtonA->setHasOffMode(true); + _upperStackTabButtonB->setHasOffMode(true); + // Start with control rack palette 'A' showing. + // TODO: Make this button class mutually exclusive capable. + _upperStackTabButtonA->setOff(false); + _upperStackTabButtonB->setOff(true); + + QHBoxLayout* upperStackTabLayout = new QHBoxLayout(); + upperStackTabLayout->setContentsMargins(0, 0, 0, 0); + upperStackTabLayout->setMargin(0); + upperStackTabLayout->setSpacing(0); + upperStackTabLayout->addSpacing(6); + upperStackTabLayout->addWidget(_upperStackTabButtonA); + upperStackTabLayout->addWidget(_upperStackTabButtonB); + upperStackTabLayout->addSpacing(6); + + addGridLayout(upperStackTabLayout, _upperStackTabPos); + + connect(_upperStackTabButtonA, + SIGNAL(pressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)), + SLOT(upperStackTabButtonAPressed())); + connect(_upperStackTabButtonB, + SIGNAL(pressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)), + SLOT(upperStackTabButtonBPressed())); + connect(_upperStackTabButtonA, + SIGNAL(returnPressed(QPoint,int,Qt::KeyboardModifiers)), + SLOT(upperStackTabButtonAPressed())); + connect(_upperStackTabButtonB, + SIGNAL(returnPressed(QPoint,int,Qt::KeyboardModifiers)), + SLOT(upperStackTabButtonBPressed())); + + _infoRack = new MidiComponentRack(t, mStripInfoRack); + //_infoRack->setVisible(false); // Not visible unless expanded. + // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. + //_infoRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + _infoRack->setFrameStyle(QFrame::Box | QFrame::Sunken); + _infoRack->setLineWidth(rackFrameWidth); + _infoRack->setMidLineWidth(0); + _infoRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + _infoRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); + _infoRack->setFocusPolicy(Qt::NoFocus); + _infoRack->setVisible(false); + + _upperRack = new MidiComponentRack(t, mStripUpperRack); + // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. + //_upperRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + _upperRack->setFrameStyle(QFrame::Box | QFrame::Sunken); + _upperRack->setLineWidth(rackFrameWidth); + _upperRack->setMidLineWidth(0); + // We do set a minimum height on this widget. Tested: Must be on fixed. Thankfully, it'll expand if more controls are added. + _upperRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + _upperRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); + _upperRack->setFocusPolicy(Qt::NoFocus); + _upperRack->setVisible(true); + + addGridWidget(_upperRack, _preScrollAreaPos_A); + addGridWidget(_infoRack, _propertyRackPos); //--------------------------------------------------- // slider, label, meter //--------------------------------------------------- MusECore::MidiPort* mp = &MusEGlobal::midiPorts[t->outPort()]; - MusECore::MidiController* mc = mp->midiController(MusECore::CTRL_VOLUME); + MusECore::MidiController* mc = mp->midiController(MusECore::CTRL_VOLUME); // Auto-create the controller if necessary. int chan = t->outChannel(); - int mn = mc->minVal(); - int mx = mc->maxVal(); - - slider = new MusEGui::Slider(this, "vol", Qt::Vertical, MusEGui::Slider::None, - QColor(100, 255, 100)); - slider->setCursorHoming(true); - slider->setRange(double(mn), double(mx), 1.0); - slider->setFixedWidth(20); - ///slider->setFont(MusEGlobal::config.fonts[1]); - slider->setId(MusECore::CTRL_VOLUME); - meter[0] = new MusEGui::Meter(this, MusEGui::Meter::LinMeter); - meter[0]->setRange(0, 127.0); - meter[0]->setFixedWidth(15); + slider = new Slider(0, "vol", Qt::Vertical, Slider::InsideVertical, 14, + MusEGlobal::config.midiVolumeSliderColor, + ScaleDraw::TextHighlightSplitAndShadow); + slider->setId(MusECore::CTRL_VOLUME); + slider->setFocusPolicy(Qt::NoFocus); + slider->setContentsMargins(0, 0, 0, 0); + slider->setCursorHoming(true); + slider->setSpecialText(tr("off")); + slider->setScaleBackBone(false); + //slider->setFillThumb(false); + slider->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + + meter[0] = new Meter(0, Meter::LinMeter, Qt::Vertical, 0.0, 127.0); + meter[0]->setRefreshRate(MusEGlobal::config.guiRefresh); + meter[0]->setContentsMargins(0, 0, 0, 0); + meter[0]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + meter[0]->setFixedWidth(FIXED_METER_WIDTH); + meter[0]->setPrimaryColor(MusEGlobal::config.midiMeterPrimaryColor); connect(meter[0], SIGNAL(mousePress()), this, SLOT(resetPeaks())); sliderGrid = new QGridLayout(); - sliderGrid->setRowStretch(0, 100); + sliderGrid->setSpacing(0); + sliderGrid->setContentsMargins(0, 0, 0, 0); sliderGrid->addWidget(slider, 0, 0, Qt::AlignHCenter); sliderGrid->addWidget(meter[0], 0, 1, Qt::AlignHCenter); - grid->addLayout(sliderGrid, _curGridRow++, 0, 1, 2); + + addGridLayout(sliderGrid, _sliderPos); - sl = new MusEGui::DoubleLabel(0.0, -98.0, 0.0, this); - ///sl->setFont(MusEGlobal::config.fonts[1]); - sl->setBackgroundRole(QPalette::Mid); - sl->setSpecialText(tr("off")); - sl->setSuffix(tr("dB")); - sl->setToolTip(tr("ctrl-double-click on/off")); + sl = new MusEGui::DoubleLabel(0.0, -98.0, 0.0); + sl->setContentsMargins(0, 0, 0, 0); + sl->setTextMargins(0, 0, 0, 0); + sl->setFocusPolicy(Qt::WheelFocus); + sl->setMouseTracking(true); sl->setFrame(true); - sl->setPrecision(0); - sl->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum)); + sl->setAlignment(Qt::AlignCenter); + //sl->setAutoFillBackground(true); + + //sl->setBackgroundRole(QPalette::Mid); + sl->setSpecialText(tr("off")); + sl->setToolTip(tr("Volume/gain\n(Ctrl-double-click on/off)")); + sl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Set the label's slider 'buddy'. sl->setSlider(slider); - - double dlv; - int v = mp->hwCtrlState(chan, MusECore::CTRL_VOLUME); - if(v == MusECore::CTRL_VAL_UNKNOWN) + sl->setEnableStyleHack(MusEGlobal::config.lineEditStyleHack); + + // Special for midi volume slider and label: Setup midi volume as decibel preference. + setupMidiVolume(); + + // If smart focus is on redirect strip focus to slider label. + //if(MusEGlobal::config.smartFocus) + setFocusProxy(sl); + + if(mc) { - int lastv = mp->lastValidHWCtrlState(chan, MusECore::CTRL_VOLUME); - if(lastv == MusECore::CTRL_VAL_UNKNOWN) + double dlv; + double v = mp->hwDCtrlState(chan, MusECore::CTRL_VOLUME); + if(MusECore::MidiController::dValIsUnknown(v)) { - if(mc->initVal() == MusECore::CTRL_VAL_UNKNOWN) - v = 0; - else - v = mc->initVal(); + double lastv = mp->lastValidHWDCtrlState(chan, MusECore::CTRL_VOLUME); + if(MusECore::MidiController::dValIsUnknown(lastv)) + { + if(mc->initValIsUnknown()) + v = 0.0; + else + v = double(mc->initVal()); + } + else + v = lastv - double(mc->bias()); + dlv = sl->off() - 1.0; } - else - v = lastv - mc->bias(); - dlv = sl->off() - 1.0; - } - else - { - if(v == 0) - dlv = sl->minValue() - 0.5 * (sl->minValue() - sl->off()); else - { - dlv = -MusECore::fast_log10(float(127*127)/float(v*v))*20.0; - if(dlv > sl->maxValue()) - dlv = sl->maxValue(); - } - // Auto bias... - v -= mc->bias(); - } - slider->setValue(double(v)); - sl->setValue(dlv); - + { + if(v <= 0.0) + dlv = sl->minValue() - 0.5 * (sl->minValue() - sl->off()); + else + { + dlv = _preferMidiVolumeDb ? (muse_val2dbr(v / double(mc->maxVal())) * 2.0) : v; + if(dlv > sl->maxValue()) + dlv = sl->maxValue(); + } + // Auto bias... + v -= double(mc->bias()); + } + double slv; + if(v <= 0.0) + { + if(_preferMidiVolumeDb) + slv = MusEGlobal::config.minSlider; + else + slv = 0.0; + } + else + slv = _preferMidiVolumeDb ? (muse_val2dbr(v / double(mc->maxVal())) * 2.0) : v; -// connect(sl, SIGNAL(valueChanged(double,int)), slider, SLOT(setValue(double))); -// connect(slider, SIGNAL(valueChanged(double,int)), sl, SLOT(setValue(double))); - connect(slider, SIGNAL(sliderMoved(double,int)), SLOT(setVolume(double))); - connect(slider, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(controlRightClicked(const QPoint &, int))); + slider->setValue(slv); + sl->setValue(dlv); + } + + connect(slider, SIGNAL(valueChanged(double,int,int)), SLOT(setVolume(double,int,int))); + connect(slider, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(controlRightClicked(QPoint,int))); + connect(slider, SIGNAL(sliderPressed(double, int)), SLOT(volumePressed(double, int))); + connect(slider, SIGNAL(sliderReleased(double, int)), SLOT(volumeReleased(double, int))); connect(sl, SIGNAL(valueChanged(double, int)), SLOT(volLabelChanged(double))); - connect(sl, SIGNAL(ctrlDoubleClicked(int)), SLOT(labelDoubleClicked(int))); + connect(sl, SIGNAL(ctrlDoubleClicked(int)), SLOT(volLabelDoubleClicked())); - grid->addWidget(sl, _curGridRow++, 0, 1, 2, Qt::AlignCenter); + addGridWidget(sl, _sliderLabelPos, Qt::AlignCenter); //--------------------------------------------------- // pan, balance //--------------------------------------------------- - addKnob(KNOB_PAN, tr("Pan/Balance"), tr("Pan"), SLOT(setPan(double)), true); - - updateControls(); + _lowerRack = new MidiComponentRack(t, mStripLowerRack); + // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. + //_lowerRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + _lowerRack->setFrameStyle(QFrame::Box | QFrame::Sunken); + _lowerRack->setLineWidth(rackFrameWidth); + _lowerRack->setMidLineWidth(0); + // We do set a minimum height on this widget. Tested: Must be on fixed. Thankfully, it'll expand if more controls are added. + _lowerRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + _lowerRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); + _lowerRack->setFocusPolicy(Qt::NoFocus); + + addGridWidget(_lowerRack, _postScrollAreaPos_A); + + _upperRack->setEnabled(!t->off()); + _infoRack->setEnabled(!t->off()); + _lowerRack->setEnabled(!t->off()); //--------------------------------------------------- // mute, solo @@ -296,95 +1622,116 @@ // record, mixdownfile //--------------------------------------------------- - record = new MusEGui::TransparentToolButton(this); + record = new IconButton(recArmOnSVGIcon, recArmOffSVGIcon, 0, 0, false, true); record->setFocusPolicy(Qt::NoFocus); - record->setBackgroundRole(QPalette::Mid); record->setCheckable(true); - record->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - record->setToolTip(tr("record")); + record->setContentsMargins(0, 0, 0, 0); + record->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + record->setToolTip(tr("Record arm")); record->setChecked(track->recordFlag()); - record->setIcon(track->recordFlag() ? QIcon(*record_on_Icon) : QIcon(*record_off_Icon)); - ///record->setIconSize(record_on_Icon->size()); - connect(record, SIGNAL(clicked(bool)), SLOT(recordToggled(bool))); + connect(record, SIGNAL(toggled(bool)), SLOT(recordToggled(bool))); - mute = new QToolButton(); + mute = new IconButton(muteOnSVGIcon, muteOffSVGIcon, muteAndProxyOnSVGIcon, muteProxyOnSVGIcon, false, true); mute->setFocusPolicy(Qt::NoFocus); mute->setCheckable(true); - mute->setToolTip(tr("mute")); + mute->setContentsMargins(0, 0, 0, 0); + mute->setToolTip(tr("Mute or proxy mute")); mute->setChecked(track->mute()); - mute->setIcon(track->mute() ? QIcon(*muteIconOff) : QIcon(*muteIconOn)); - ///mute->setIconSize(muteIconOn->size()); - mute->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - connect(mute, SIGNAL(clicked(bool)), SLOT(muteToggled(bool))); + updateMuteIcon(); + mute->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + connect(mute, SIGNAL(toggled(bool)), SLOT(muteToggled(bool))); - solo = new QToolButton(); + solo = new IconButton(soloOnSVGIcon, soloOffSVGIcon, soloAndProxyOnSVGIcon, soloProxyOnSVGIcon, false, true); solo->setFocusPolicy(Qt::NoFocus); - solo->setToolTip(tr("solo mode")); + solo->setToolTip(tr("Solo or proxy solo")); + solo->setContentsMargins(0, 0, 0, 0); solo->setCheckable(true); + solo->setIconSetB(track->internalSolo()); solo->setChecked(track->solo()); - solo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); - if(track->internalSolo()) - solo->setIcon(track->solo() ? QIcon(*soloblksqIconOn) : QIcon(*soloblksqIconOff)); - else - solo->setIcon(track->solo() ? QIcon(*soloIconOn) : QIcon(*soloIconOff)); - ///solo->setIconSize(soloIconOn->size()); - connect(solo, SIGNAL(clicked(bool)), SLOT(soloToggled(bool))); + solo->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + connect(solo, SIGNAL(toggled(bool)), SLOT(soloToggled(bool))); - off = new MusEGui::TransparentToolButton(this); + off = new IconButton(trackOffSVGIcon, trackOnSVGIcon, 0, 0, false, true); + off->setContentsMargins(0, 0, 0, 0); off->setFocusPolicy(Qt::NoFocus); - off->setBackgroundRole(QPalette::Mid); - off->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); + off->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); off->setCheckable(true); - off->setToolTip(tr("off")); + off->setToolTip(tr("Track off")); off->setChecked(track->off()); - off->setIcon(track->off() ? QIcon(*exit1Icon) : QIcon(*exitIcon)); - ///off->setIconSize(exit1Icon->size()); - connect(off, SIGNAL(clicked(bool)), SLOT(offToggled(bool))); - - grid->addWidget(off, _curGridRow, 0); - grid->addWidget(record, _curGridRow++, 1); - grid->addWidget(mute, _curGridRow, 0); - grid->addWidget(solo, _curGridRow++, 1); + connect(off, SIGNAL(toggled(bool)), SLOT(offToggled(bool))); //--------------------------------------------------- // routing //--------------------------------------------------- - iR = new QToolButton(); + iR = new IconButton(routingInputSVGIcon, routingInputSVGIcon, + routingInputUnconnectedSVGIcon, routingInputUnconnectedSVGIcon, false, true); + iR->setContentsMargins(0, 0, 0, 0); iR->setFocusPolicy(Qt::NoFocus); - ///iR->setFont(MusEGlobal::config.fonts[1]); - iR->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum)); - ///iR->setText(tr("iR")); - iR->setIcon(QIcon(*routesMidiInIcon)); - iR->setIconSize(routesMidiInIcon->size()); + iR->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); iR->setCheckable(false); - iR->setToolTip(tr("input routing")); - grid->addWidget(iR, _curGridRow, 0); + iR->setToolTip(MusEGlobal::inputRoutingToolTipBase); connect(iR, SIGNAL(pressed()), SLOT(iRoutePressed())); - oR = new QToolButton(); + oR = new IconButton(routingOutputSVGIcon, routingOutputSVGIcon, + routingOutputUnconnectedSVGIcon, routingOutputUnconnectedSVGIcon, false, true); + oR->setContentsMargins(0, 0, 0, 0); oR->setFocusPolicy(Qt::NoFocus); - ///oR->setFont(MusEGlobal::config.fonts[1]); - oR->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum)); - ///oR->setText(tr("oR")); - oR->setIcon(QIcon(*routesMidiOutIcon)); - oR->setIconSize(routesMidiOutIcon->size()); + oR->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); oR->setCheckable(false); - // TODO: Works OK, but disabled for now, until we figure out what to do about multiple out routes and display values... - // Enabled (for Midi Port to Audio Input routing). p4.0.14 Tim. - //oR->setEnabled(false); - oR->setToolTip(tr("output routing")); - grid->addWidget(oR, _curGridRow++, 1); + oR->setToolTip(MusEGlobal::outputRoutingToolTipBase); connect(oR, SIGNAL(pressed()), SLOT(oRoutePressed())); + + updateRouteButtons(); + + if(track && track->canRecordMonitor()) + { + _recMonitor = new IconButton(monitorOnSVGIcon, monitorOffSVGIcon, 0, 0, false, true); + _recMonitor->setFocusPolicy(Qt::NoFocus); + _recMonitor->setContentsMargins(0, 0, 0, 0); + _recMonitor->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + _recMonitor->setCheckable(true); + _recMonitor->setToolTip(tr("Input monitor")); + _recMonitor->setWhatsThis(tr("Pass input through to output")); + _recMonitor->setChecked(t->recMonitor()); + connect(_recMonitor, SIGNAL(toggled(bool)), SLOT(recMonitorToggled(bool))); + } + + if(off && record && _recMonitor) + { + QHBoxLayout* offRecMonLayout = new QHBoxLayout(); + offRecMonLayout->setContentsMargins(0, 0, 0, 0); + offRecMonLayout->setSpacing(0); + offRecMonLayout->addWidget(off); + offRecMonLayout->addWidget(_recMonitor); + offRecMonLayout->addWidget(record); + addGridLayout(offRecMonLayout, _offMonRecPos); + } + else + { + if(off) + addGridWidget(off, _offPos); + if(_recMonitor) + addGridWidget(_recMonitor, _recPos); + else if(record) + addGridWidget(record, _recPos); + } + addGridWidget(mute, _mutePos); + addGridWidget(solo, _soloPos); + + if(iR) + addGridWidget(iR, _inRoutesPos); + if(oR) + addGridWidget(oR, _outRoutesPos); //--------------------------------------------------- // automation mode //--------------------------------------------------- - autoType = new MusEGui::ComboBox(); + autoType = new CompactComboBox(); + autoType->setContentsMargins(0, 0, 0, 0); autoType->setFocusPolicy(Qt::NoFocus); - ///autoType->setFont(MusEGlobal::config.fonts[1]); - autoType->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum)); + autoType->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); autoType->setEnabled(false); // Removed by T356. @@ -399,10 +1746,315 @@ autoType->addAction(" ", AUTO_OFF); // Just a dummy text to fix sizing problems. REMOVE later if full automation added. autoType->setCurrentItem(AUTO_OFF); // - grid->addWidget(autoType, _curGridRow++, 0, 1, 2); - connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); - inHeartBeat = false; + addGridWidget(autoType, _automationPos); + + grid->setColumnStretch(2, 10); + + if (off) { + off->blockSignals(true); + updateOffState(); // init state + off->blockSignals(false); + } + + // Now build the strip components. + buildStrip(); + + // Now set up all tabbing on the strip. + // Don't bother if the strip is part of the mixer (not embedded), + // the non-embedding parent (mixer) should set up all the tabs and make this call. + if(isEmbedded) + setupComponentTabbing(); + + // TODO: Activate this. But owners want to marshall this signal and send it themselves. Change that. + //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); + + connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); + + connect(_upperRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); + connect(_upperRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); + connect(_upperRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); + connect(_upperRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); + + connect(_infoRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); + connect(_infoRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); + connect(_infoRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); + connect(_infoRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); + + connect(_lowerRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); + connect(_lowerRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); + connect(_lowerRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); + connect(_lowerRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); + + inHeartBeat = false; + } + +//--------------------------------------------------- +// buildStrip +// Destroy and rebuild strip components. +//--------------------------------------------------- + +void MidiStrip::buildStrip() +{ + // Destroys all components and clears the component list. + _infoRack->clearDelete(); + _upperRack->clearDelete(); + _lowerRack->clearDelete(); + + //--------------------------------------------------- + // Upper rack + //--------------------------------------------------- + + ElidedLabelComponentDescriptor instrPropertyDesc(ComponentRack::propertyComponent, + "MixerStripInstrumentProperty", + MidiComponentRack::mStripInstrumentProperty, + Qt::ElideNone); + _upperRack->newComponent(&instrPropertyDesc); + + CompactPatchEditComponentDescriptor progControllerDesc(ComponentRack::controllerComponent, "MixerStripMidiProgramController", MusECore::CTRL_PROGRAM); + _upperRack->newComponent(&progControllerDesc); + + if(_preferKnobs) + { + CompactKnobComponentDescriptor varSendControllerDesc(ComponentRack::controllerComponent, "MixerStripMidiVarSendController", MusECore::CTRL_VARIATION_SEND); + CompactKnobComponentDescriptor revSendControllerDesc(ComponentRack::controllerComponent, "MixerStripMidiRevSendController", MusECore::CTRL_REVERB_SEND); + CompactKnobComponentDescriptor choSendControllerDesc(ComponentRack::controllerComponent, "MixerStripMidiChoSendController", MusECore::CTRL_CHORUS_SEND); + _upperRack->newComponent(&varSendControllerDesc); + _upperRack->newComponent(&revSendControllerDesc); + _upperRack->newComponent(&choSendControllerDesc); + } + else + { + // To avoid too bright or annoying joined borders which are twice the normal width, + // show no bottom borders except for last one! + CompactSliderComponentDescriptor varSendControllerDesc( + ComponentRack::controllerComponent, + "MixerStripMidiVarSendController", + MusECore::CTRL_VARIATION_SEND, + CompactSlider::AllBordersExceptBottom); + CompactSliderComponentDescriptor revSendControllerDesc( + ComponentRack::controllerComponent, + "MixerStripMidiRevSendController", + MusECore::CTRL_REVERB_SEND, + CompactSlider::AllBordersExceptBottom); + CompactSliderComponentDescriptor choSendControllerDesc( + ComponentRack::controllerComponent, + "MixerStripMidiChoSendController", + MusECore::CTRL_CHORUS_SEND, + CompactSlider::AllBorders); + _upperRack->newComponent(&varSendControllerDesc); + _upperRack->newComponent(&revSendControllerDesc); + _upperRack->newComponent(&choSendControllerDesc); + } + + // Keep this if dynamic layout (flip to right side) is desired. + _upperRack->addStretch(); + + updateRackSizes(true, false); + + + //--------------------------------------------------- + // Track properties rack + //--------------------------------------------------- + + if(_preferKnobs) + { + CompactKnobComponentDescriptor transpPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiTranspProperty", MidiComponentRack::mStripTranspProperty); + CompactKnobComponentDescriptor delayPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiDelayProperty", MidiComponentRack::mStripDelayProperty); + CompactKnobComponentDescriptor lenPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiLenProperty", MidiComponentRack::mStripLenProperty); + CompactKnobComponentDescriptor veloPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiVeloProperty", MidiComponentRack::mStripVeloProperty); + CompactKnobComponentDescriptor comprPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiComprProperty", MidiComponentRack::mStripComprProperty); + + _infoRack->newComponent(&transpPropertyDesc); + _infoRack->newComponent(&delayPropertyDesc); + _infoRack->newComponent(&lenPropertyDesc); + _infoRack->newComponent(&veloPropertyDesc); + _infoRack->newComponent(&comprPropertyDesc); + } + else + { + // To avoid too bright or annoying joined borders which are twice the normal width, + // show no bottom borders except for last one! + CompactSliderComponentDescriptor transpPropertyDesc( + ComponentRack::propertyComponent, + "MixerStripMidiTranspProperty", + MidiComponentRack::mStripTranspProperty, + CompactSlider::AllBordersExceptBottom); + CompactSliderComponentDescriptor delayPropertyDesc( + ComponentRack::propertyComponent, + "MixerStripMidiDelayProperty", + MidiComponentRack::mStripDelayProperty, + CompactSlider::AllBordersExceptBottom); + CompactSliderComponentDescriptor lenPropertyDesc( + ComponentRack::propertyComponent, + "MixerStripMidiLenProperty", + MidiComponentRack::mStripLenProperty, + CompactSlider::AllBordersExceptBottom); + CompactSliderComponentDescriptor veloPropertyDesc( + ComponentRack::propertyComponent, + "MixerStripMidiVeloProperty", + MidiComponentRack::mStripVeloProperty, + CompactSlider::AllBordersExceptBottom); + CompactSliderComponentDescriptor comprPropertyDesc( + ComponentRack::propertyComponent, + "MixerStripMidiComprProperty", + MidiComponentRack::mStripComprProperty, + CompactSlider::AllBorders); + + _infoRack->newComponent(&transpPropertyDesc); + _infoRack->newComponent(&delayPropertyDesc); + _infoRack->newComponent(&lenPropertyDesc); + _infoRack->newComponent(&veloPropertyDesc); + _infoRack->newComponent(&comprPropertyDesc); + } + _infoRack->addStretch(); + + + //--------------------------------------------------- + // Lower rack + //--------------------------------------------------- + + // Pan... + if(_preferKnobs) + { + CompactKnobComponentDescriptor panControllerDesc + ( + ComponentRack::controllerComponent, + "MixerStripMidiPanController", + MusECore::CTRL_PANPOT + ); + _lowerRack->newComponent(&panControllerDesc); + } + else + { + CompactSliderComponentDescriptor panControllerDesc + ( + ComponentRack::controllerComponent, + "MixerStripMidiPanController", + MusECore::CTRL_PANPOT + ); + _lowerRack->newComponent(&panControllerDesc); + } + + // Keep this if dynamic layout (flip to right side) is desired. + _lowerRack->addStretch(); + + updateRackSizes(false, true); +} + +QWidget* MidiStrip::setupComponentTabbing(QWidget* previousWidget) +{ + QWidget* prev = previousWidget; + if(_upperStackTabButtonA) + { + if(prev) + QWidget::setTabOrder(prev, _upperStackTabButtonA); + prev = _upperStackTabButtonA; + } + if(_upperStackTabButtonB) + { + if(prev) + QWidget::setTabOrder(prev, _upperStackTabButtonB); + prev = _upperStackTabButtonB; + } + prev = _upperRack->setupComponentTabbing(prev); + prev = _infoRack->setupComponentTabbing(prev); + if(sl) + { + if(prev) + QWidget::setTabOrder(prev, sl); + prev = sl; + } + prev = _lowerRack->setupComponentTabbing(prev); + return prev; +} + +void MidiStrip::setupMidiVolume() +{ + const bool show_db = MusEGlobal::config.preferMidiVolumeDb; + + if(track && track->isMidiTrack()) + { + const int num = MusECore::CTRL_VOLUME; + MusECore::MidiTrack* mt = static_cast(track); + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; + MusECore::MidiController* mc = mp->midiController(num, false); + if(!mc) + return; + const int mn = mc->minVal(); + const int mx = mc->maxVal(); + + if(show_db) + { + slider->setRange(MusEGlobal::config.minSlider, volSliderMaxDb, volSliderStepDb); + //slider->setScaleMaxMinor(5); + slider->setScale(MusEGlobal::config.minSlider, volSliderMaxDb, 6.0, false); + //slider->setSpecialText(tr("off")); + //slider->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. + + sl->setPrecision(volSliderPrecDb); + sl->setRange(MusEGlobal::config.minSlider, volSliderMaxDb); + sl->setOff(MusEGlobal::config.minSlider); + //sl->setSpecialText(tr("off")); + //sl->setSpecialText(QString('-') + QChar(0x221e) + QChar(' ') + tr("dB")); // The infinity character. + //sl->setToolTip(tr("Volume/gain")); + sl->setSuffix(tr("dB")); + } + else + { + slider->setRange(double(mn), double(mx), volSliderStepLin); + //slider->setScaleMaxMinor(5); + slider->setScale(double(mn), double(mx), 10.0, false); + //slider->setSpecialText(tr("off")); + //slider->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. + + sl->setPrecision(0); + sl->setRange(double(mn), double(mx)); + sl->setOff(double(mn) - 1.0); // Reset to default. + //sl->setSpecialText(tr("off")); + //sl->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. + //sl->setToolTip(tr("Volume/gain\n(Ctrl-double-click on/off)")); + sl->setSuffix(QString()); + } + + // Invalidate the cached volume so that the next heartbeat updates with a new value. + volume = MusECore::CTRL_VAL_UNKNOWN; + + if(_preferMidiVolumeDb != show_db) + { + const int chan = mt->outChannel(); + const double d_lastv = mp->lastValidHWDCtrlState(chan, num); + const double d_curv = mp->hwDCtrlState(chan, num); + + if(MusECore::MidiController::dValIsUnknown(d_curv)) + { + // If no value has ever been set yet, use the current knob value + // (or the controller's initial value?) to 'turn on' the controller. + if(MusECore::MidiController::dValIsUnknown(d_lastv)) + { + double slider_v = slider->value(); + if(slider_v == 0.0) + { + if(show_db) + slider_v = MusEGlobal::config.minSlider; + } + else + { + if(show_db) + slider_v = muse_val2dbr(slider_v / double(mx)) * 2.0; + else + slider_v = double(mx) * muse_db2val(slider_v / 2.0); + } + + slider->blockSignals(true); + slider->setValue(slider_v); + slider->blockSignals(false); + } } + } + } + + _preferMidiVolumeDb = show_db; +} //--------------------------------------------------------- // updateOffState @@ -410,13 +2062,21 @@ void MidiStrip::updateOffState() { + if(!track) + return; + bool val = !track->off(); slider->setEnabled(val); sl->setEnabled(val); - controller[KNOB_PAN].knob->setEnabled(val); - controller[KNOB_PAN].dl->setEnabled(val); - label->setEnabled(val); + _upperRack->setEnabled(val); + _infoRack->setEnabled(val); + _lowerRack->setEnabled(val); + + label->setEnabled(val); + + if (_recMonitor) + _recMonitor->setEnabled(val); if (record) record->setEnabled(val); if (solo) @@ -430,15 +2090,65 @@ // iR->setEnabled(val); //if (oR) // oR->setEnabled(val); + if (off) { off->blockSignals(true); off->setChecked(track->off()); off->blockSignals(false); - off->setIcon(track->off() ? QIcon(*exit1Icon) : QIcon(*exitIcon)); - //off->setIconSize(exit1Icon->size()); } } +void MidiStrip::updateRackSizes(bool upper, bool lower) +{ +// const QFontMetrics fm = fontMetrics(); + if(upper) + { + // Make room for 3 CompactSliders and one CompactPatchEdit. + // TODO: Add the instrument select label height! + +// // const int csh = CompactSlider::getMinimumSizeHint(fm, +// // Qt::Horizontal, +// // CompactSlider::None, +// // xMarginHorSlider, yMarginHorSlider).height(); +// // const int cpeh = CompactPatchEdit::getMinimumSizeHint(fm, +// // Qt::Horizontal, +// // CompactSlider::None, +// // xMarginHorSlider, yMarginHorSlider).height(); +// // const int ilh = _instrLabel->sizeHint().height(); +// +// // DEBUG_MIDI_STRIP(stderr, "MidiStrip::updateRackSizes: CompactSlider h:%d CompactPatchEdit h:%d instrLabel h:%d upper frame w:%d \n", +// // csh, cpeh, ilh, _upperRack->frameWidth()); +// +// _upperRack->setMinimumHeight( +// 3 * CompactSlider::getMinimumSizeHint(fm, +// Qt::Horizontal, +// CompactSlider::None, +// xMarginHorSlider, yMarginHorSlider).height() + +// 1 * CompactPatchEdit::getMinimumSizeHint(fm, +// Qt::Horizontal, +// CompactSlider::None, +// xMarginHorSlider, yMarginHorSlider).height() + +// upperRackSpacerHeight + +// +// _instrLabel->sizeHint().height() + +// +// 2 * rackFrameWidth); + } + if(lower) + { + // Make room for 1 CompactSlider (Pan, so far). + + //DEBUG_MIDI_STRIP(stderr, "MidiStrip::updateRackSizes: lower frame w:%d \n", _lowerRack->frameWidth()); + +// _lowerRack->setMinimumHeight( +// 1 * CompactSlider::getMinimumSizeHint(fm, +// Qt::Horizontal, +// CompactSlider::None, +// xMarginHorSlider, yMarginHorSlider).height() + +// 2 * rackFrameWidth); + } +} + //--------------------------------------------------------- // configChanged // Catch when config label font changes, viewable tracks etc. @@ -446,13 +2156,56 @@ void MidiStrip::configChanged() { - // Set the whole strip's font, except for the label. p4.0.45 + // Detect when knobs are preferred and rebuild. + if(_preferKnobs != MusEGlobal::config.preferKnobsVsSliders) + { + _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; + // Rebuild the strip components. + buildStrip(); + // Now set up all tabbing on the strip. + // Don't bother if the strip is part of the mixer (not embedded), + // the non-embedding parent (mixer) should set up all the tabs and make this call. + if(isEmbedded()) + setupComponentTabbing(); + } + + // Set the whole strip's font, except for the label. if(font() != MusEGlobal::config.fonts[1]) + { + //DEBUG_MIDI_STRIP(stderr, "MidiStrip::configChanged changing font: current size:%d\n", font().pointSize()); setFont(MusEGlobal::config.fonts[1]); + setStyleSheet(MusECore::font2StyleSheet(MusEGlobal::config.fonts[1])); + // Update in case font changed. + updateRackSizes(true, true); + } + // Update always, in case style, stylesheet, or font changed. + //updateRackSizes(true, true); // Set the strip label's font. - setLabelFont(); - setLabelText(); + setLabelText(); + + slider->setFillColor(MusEGlobal::config.midiVolumeSliderColor); + + // Enable special hack for line edits. + if(sl->enableStyleHack() != MusEGlobal::config.lineEditStyleHack) + sl->setEnableStyleHack(MusEGlobal::config.lineEditStyleHack); + + // Special for midi volume slider and label: Setup midi volume as decibel preference. + setupMidiVolume(); + + _upperRack->configChanged(); + _infoRack->configChanged(); + _lowerRack->configChanged(); + + // Adjust meter and colour. + meter[0]->setPrimaryColor(MusEGlobal::config.midiMeterPrimaryColor); + meter[0]->setRefreshRate(MusEGlobal::config.guiRefresh); + + // If smart focus is on redirect strip focus to slider label. +// if(MusEGlobal::config.smartFocus) +// setFocusProxy(sl); +// else +// setFocusProxy(0); } //--------------------------------------------------------- @@ -463,32 +2216,27 @@ { if (mute && (val & SC_MUTE)) { // mute && off mute->blockSignals(true); - //mute->setChecked(track->isMute()); mute->setChecked(track->mute()); mute->blockSignals(false); - mute->setIcon(track->mute() ? QIcon(*muteIconOff) : QIcon(*muteIconOn)); - //mute->setIconSize(muteIconOn->size()); + updateMuteIcon(); updateOffState(); } - if (solo && (val & SC_SOLO)) + if (solo && (val & (SC_SOLO | SC_ROUTE))) { solo->blockSignals(true); solo->setChecked(track->solo()); solo->blockSignals(false); - if(track->internalSolo()) - solo->setIcon(track->solo() ? QIcon(*soloblksqIconOn) : QIcon(*soloblksqIconOff)); - else - solo->setIcon(track->solo() ? QIcon(*soloIconOn) : QIcon(*soloIconOff)); - //solo->setIconSize(soloIconOn->size()); - } + solo->setIconSetB(track->internalSolo()); + updateMuteIcon(); + } if (val & SC_RECFLAG) + { setRecordFlag(track->recordFlag()); + } if (val & SC_TRACK_MODIFIED) { setLabelText(); - setLabelFont(); - } // Catch when label font changes. @@ -496,110 +2244,128 @@ { // So far only 1 instance of sending SC_CONFIG in the entire app, in instrument editor when a new instrument is saved. } + + _upperRack->songChanged(val); + _infoRack->songChanged(val); + _lowerRack->songChanged(val); + + if (val & SC_ROUTE) { + updateRouteButtons(); + } + + if(val & SC_TRACK_REC_MONITOR) + { + // Set record monitor. + if(_recMonitor) + { + _recMonitor->blockSignals(true); + _recMonitor->setChecked(track->recMonitor()); + _recMonitor->blockSignals(false); + } + } } //--------------------------------------------------------- // controlRightClicked //--------------------------------------------------------- -void MidiStrip::controlRightClicked(const QPoint &p, int id) +void MidiStrip::controlRightClicked(QPoint p, int id) +{ + MusEGlobal::song->execMidiAutomationCtlPopup(static_cast(track), 0, p, id); +} + +void MidiStrip::upperStackTabButtonAPressed() +{ + _infoRack->hide(); + _upperRack->show(); + _upperStackTabButtonA->setOff(false); + _upperStackTabButtonB->setOff(true); +} + +void MidiStrip::upperStackTabButtonBPressed() +{ + _upperRack->hide(); + _infoRack->show(); + _upperStackTabButtonA->setOff(true); + _upperStackTabButtonB->setOff(false); +} + +//--------------------------------------------------------- +// recMonitorToggled +//--------------------------------------------------------- + +void MidiStrip::recMonitorToggled(bool v) { - MusEGlobal::song->execMidiAutomationCtlPopup((MusECore::MidiTrack*)track, 0, p, id); + if(!track) + return; + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(track, v, MusECore::PendingOperationItem::SetTrackRecMonitor)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- -// labelDoubleClicked +// volLabelDoubleClicked //--------------------------------------------------------- -void MidiStrip::labelDoubleClicked(int idx) +void MidiStrip::volLabelDoubleClicked() { - //int mn, mx, v; - //int num = MusECore::CTRL_VOLUME; - int num; - switch(idx) - { - case KNOB_PAN: - num = MusECore::CTRL_PANPOT; - break; - case KNOB_VAR_SEND: - num = MusECore::CTRL_VARIATION_SEND; - break; - case KNOB_REV_SEND: - num = MusECore::CTRL_REVERB_SEND; - break; - case KNOB_CHO_SEND: - num = MusECore::CTRL_CHORUS_SEND; - break; - //case -1: - default: - num = MusECore::CTRL_VOLUME; - break; - } - int outport = ((MusECore::MidiTrack*)track)->outPort(); - int chan = ((MusECore::MidiTrack*)track)->outChannel(); + const int num = MusECore::CTRL_VOLUME; + const int outport = static_cast(track)->outPort(); + const int chan = static_cast(track)->outChannel(); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport]; - MusECore::MidiController* mc = mp->midiController(num); - - int lastv = mp->lastValidHWCtrlState(chan, num); - int curv = mp->hwCtrlState(chan, num); + MusECore::MidiController* mc = mp->midiController(num, false); + if(!mc) + return; - if(curv == MusECore::CTRL_VAL_UNKNOWN) + const double lastv = mp->lastValidHWDCtrlState(chan, num); + const double curv = mp->hwDCtrlState(chan, num); + + if(MusECore::MidiController::dValIsUnknown(curv)) { // If no value has ever been set yet, use the current knob value // (or the controller's initial value?) to 'turn on' the controller. - if(lastv == MusECore::CTRL_VAL_UNKNOWN) + if(MusECore::MidiController::dValIsUnknown(lastv)) { - //int kiv = _ctrl->initVal()); - int kiv; - if(idx == -1) - kiv = lrint(slider->value()); - else - kiv = lrint(controller[idx].knob->value()); - if(kiv < mc->minVal()) - kiv = mc->minVal(); - if(kiv > mc->maxVal()) - kiv = mc->maxVal(); - kiv += mc->bias(); - - //MusECore::MidiPlayEvent ev(MusEGlobal::song->cpos(), outport, chan, MusECore::ME_CONTROLLER, num, kiv); - MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_CONTROLLER, num, kiv); - MusEGlobal::audio->msgPlayMidiEvent(&ev); + double slv = slider->value(); + if(_preferMidiVolumeDb) + slv = double(mc->maxVal()) * muse_db2val(slv / 2.0); + if(slv < double(mc->minVal())) + slv = mc->minVal(); + if(slv > double(mc->maxVal())) + slv = mc->maxVal(); + slv += double(mc->bias()); + + mp->putControllerValue(outport, chan, num, slv, false); } else { - //MidiPlayEvent ev(MusEGlobal::song->cpos(), outport, chan, MusECore::ME_CONTROLLER, num, lastv); - MusECore::MidiPlayEvent ev(0, outport, chan, MusECore::ME_CONTROLLER, num, lastv); - MusEGlobal::audio->msgPlayMidiEvent(&ev); + mp->putControllerValue(outport, chan, num, lastv, false); } } else { if(mp->hwCtrlState(chan, num) != MusECore::CTRL_VAL_UNKNOWN) - MusEGlobal::audio->msgSetHwCtrlState(mp, chan, num, MusECore::CTRL_VAL_UNKNOWN); + mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), outport, chan, + MusECore::ME_CONTROLLER, + num, + MusECore::CTRL_VAL_UNKNOWN)); } - MusEGlobal::song->update(SC_MIDI_CONTROLLER); } - //--------------------------------------------------------- // offToggled //--------------------------------------------------------- void MidiStrip::offToggled(bool val) { - track->setOff(val); - MusEGlobal::song->update(SC_MUTE); - } - -/* -//--------------------------------------------------------- -// routeClicked -//--------------------------------------------------------- - -void MidiStrip::routeClicked() - { + if(!track) + return; + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackOff)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); } -*/ //--------------------------------------------------------- // heartBeat @@ -609,23 +2375,55 @@ { inHeartBeat = true; - int act = track->activity(); - double dact = double(act) * (slider->value() / 127.0); + // Try to avoid calling MidiInstrument::getPatchName too often. + if(++_heartBeatCounter >= 10) + _heartBeatCounter = 0; - if((int)dact > track->lastActivity()) - track->setLastActivity((int)dact); + if(track && track->isMidiTrack()) + { + int act = track->activity(); + double m_val = slider->value(); + + if(_preferMidiVolumeDb) + { + MusECore::MidiTrack* t = static_cast(track); + const int port = t->outPort(); + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiController* mctl = mp->midiController(MusECore::CTRL_VOLUME, false); + if(mctl) + m_val = double(mctl->maxVal()) * muse_db2val(m_val / 2.0); + + m_val += double(mctl->bias()); + + if(m_val < double(mctl->minVal())) + m_val = double(mctl->minVal()); + if(m_val > double(mctl->maxVal())) + m_val = double(mctl->maxVal()); + } + + double dact = double(act) * (m_val / 127.0); + + if((int)dact > track->lastActivity()) + track->setLastActivity((int)dact); + + if(meter[0]) + meter[0]->setVal(dact, track->lastActivity(), false); + + // Gives reasonable decay with gui update set to 20/sec. + if(act) + track->setActivity((int)((double)act * 0.8)); + } - if(meter[0]) - //meter[0]->setVal(int(double(act) * (slider->value() / 127.0)), 0, false); - meter[0]->setVal(dact, track->lastActivity(), false); - - // Gives reasonable decay with gui update set to 20/sec. - if(act) - track->setActivity((int)((double)act * 0.8)); + updateControls(); + _upperRack->updateComponents(); + _infoRack->updateComponents(); + _lowerRack->updateComponents(); + + //if(_recMonitor && _recMonitor->isChecked() && MusEGlobal::blinkTimerPhase != _recMonitor->blinkPhase()) + // _recMonitor->setBlinkPhase(MusEGlobal::blinkTimerPhase); + Strip::heartBeat(); - updateControls(); - inHeartBeat = false; } @@ -634,289 +2432,173 @@ //--------------------------------------------------------- void MidiStrip::updateControls() +{ + MusECore::MidiTrack* mt = static_cast(track); + const int channel = mt->outChannel(); + const int port = mt->outPort(); + if(channel < 0 || channel >= MIDI_CHANNELS || port < 0 || port >= MIDI_PORTS) + return; + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiCtrlValListList* mcvll = mp->controller(); + + MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, MusECore::CTRL_VOLUME); + const bool enable = imcvl != mcvll->end() && !mt->off(); + if(slider->isEnabled() != enable) + slider->setEnabled(enable); + if(sl->isEnabled() != enable) + sl->setEnabled(enable); + + if(enable) + { + MusECore::MidiCtrlValList* mcvl = imcvl->second; + double d_hwVal = mcvl->hwDVal(); + int max = 127; + int bias = 0; + MusECore::MidiController* mc = mp->midiController(MusECore::CTRL_VOLUME, false); + if(mc) + { + max = mc->maxVal(); + bias = mc->bias(); + } + + if(mcvl->hwValIsUnknown()) + { + sl->setValue(sl->off() - 1.0); + volume = MusECore::CTRL_VAL_UNKNOWN; + + d_hwVal = mcvl->lastValidHWDVal(); + if(mcvl->lastHwValIsUnknown()) { - bool en; - int channel = ((MusECore::MidiTrack*)track)->outChannel(); - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[((MusECore::MidiTrack*)track)->outPort()]; - MusECore::MidiCtrlValListList* mc = mp->controller(); - MusECore::ciMidiCtrlValList icl; - - MusECore::MidiController* ctrl = mp->midiController(MusECore::CTRL_VOLUME); - int nvolume = mp->hwCtrlState(channel, MusECore::CTRL_VOLUME); - if(nvolume == MusECore::CTRL_VAL_UNKNOWN) - { - //if(nvolume != volume) - //{ - // MusEGui::DoubleLabel ignores the value if already set... - sl->setValue(sl->off() - 1.0); - //volume = nvolume; - //} - volume = MusECore::CTRL_VAL_UNKNOWN; - nvolume = mp->lastValidHWCtrlState(channel, MusECore::CTRL_VOLUME); - //if(nvolume != volume) - if(nvolume != MusECore::CTRL_VAL_UNKNOWN) - { - nvolume -= ctrl->bias(); - //slider->blockSignals(true); - if(double(nvolume) != slider->value()) - { - //printf("MidiStrip::updateControls setting volume slider\n"); - - slider->setValue(double(nvolume)); - } - } - } - else - { - int ivol = nvolume; - nvolume -= ctrl->bias(); - if(nvolume != volume) { - //printf("MidiStrip::updateControls setting volume slider\n"); - - //slider->blockSignals(true); - slider->setValue(double(nvolume)); - //sl->setValue(double(nvolume)); - if(ivol == 0) - { - //printf("MidiStrip::updateControls setting volume slider label\n"); - - sl->setValue(sl->minValue() - 0.5 * (sl->minValue() - sl->off())); - } - else - { - double v = -MusECore::fast_log10(float(127*127)/float(ivol*ivol))*20.0; - if(v > sl->maxValue()) - { - //printf("MidiStrip::updateControls setting volume slider label\n"); - - sl->setValue(sl->maxValue()); - } - else - { - //printf("MidiStrip::updateControls setting volume slider label\n"); - - sl->setValue(v); - } - } - //slider->blockSignals(false); - volume = nvolume; - } - } - - - KNOB* gcon = &controller[KNOB_PAN]; - ctrl = mp->midiController(MusECore::CTRL_PANPOT); - int npan = mp->hwCtrlState(channel, MusECore::CTRL_PANPOT); - if(npan == MusECore::CTRL_VAL_UNKNOWN) - { - // MusEGui::DoubleLabel ignores the value if already set... - //if(npan != pan) - //{ - gcon->dl->setValue(gcon->dl->off() - 1.0); - //pan = npan; - //} - pan = MusECore::CTRL_VAL_UNKNOWN; - npan = mp->lastValidHWCtrlState(channel, MusECore::CTRL_PANPOT); - if(npan != MusECore::CTRL_VAL_UNKNOWN) - { - npan -= ctrl->bias(); - if(double(npan) != gcon->knob->value()) - { - //printf("MidiStrip::updateControls setting pan knob\n"); - - gcon->knob->setValue(double(npan)); - } - } - } +// TODO +// if(!control->isOff()) +// { +// control->blockSignals(true); +// control->setOff(true); +// control->blockSignals(false); +// } + } + else + { + d_hwVal -= double(bias); + + double slider_v; + if(d_hwVal <= 0.0) + { + if(_preferMidiVolumeDb) + slider_v = MusEGlobal::config.minSlider; else + slider_v = 0.0; + } + else + { + if(_preferMidiVolumeDb) { - npan -= ctrl->bias(); - if(npan != pan) - { - //printf("MidiStrip::updateControls setting pan label and knob\n"); - - //controller[KNOB_PAN].knob->blockSignals(true); - gcon->knob->setValue(double(npan)); - gcon->dl->setValue(double(npan)); - //controller[KNOB_PAN].knob->blockSignals(false); - pan = npan; - } - } - - - icl = mc->find(channel, MusECore::CTRL_VARIATION_SEND); - en = icl != mc->end(); - - gcon = &controller[KNOB_VAR_SEND]; - if(gcon->knob->isEnabled() != en) - gcon->knob->setEnabled(en); - if(gcon->lb->isEnabled() != en) - gcon->lb->setEnabled(en); - if(gcon->dl->isEnabled() != en) - gcon->dl->setEnabled(en); - - if(en) - { - ctrl = mp->midiController(MusECore::CTRL_VARIATION_SEND); - int nvariSend = icl->second->hwVal(); - if(nvariSend == MusECore::CTRL_VAL_UNKNOWN) - { - // MusEGui::DoubleLabel ignores the value if already set... - //if(nvariSend != variSend) - //{ - gcon->dl->setValue(gcon->dl->off() - 1.0); - //variSend = nvariSend; - //} - variSend = MusECore::CTRL_VAL_UNKNOWN; - nvariSend = mp->lastValidHWCtrlState(channel, MusECore::CTRL_VARIATION_SEND); - if(nvariSend != MusECore::CTRL_VAL_UNKNOWN) - { - nvariSend -= ctrl->bias(); - if(double(nvariSend) != gcon->knob->value()) - { - gcon->knob->setValue(double(nvariSend)); - } - } + slider_v = muse_val2dbr(d_hwVal / double(max)) * 2.0; + if(slider_v < MusEGlobal::config.minSlider) + slider_v = MusEGlobal::config.minSlider; } else + slider_v = d_hwVal; + } + + if(slider_v != slider->value()) + { + slider->blockSignals(true); + slider->setValue(slider_v); + slider->blockSignals(false); + } + } + } + else + { + double d_vol = d_hwVal; + d_hwVal -= double(bias); + if(d_hwVal != volume) + { + double slider_v; + if(d_hwVal <= 0.0) + { + if(_preferMidiVolumeDb) + slider_v = MusEGlobal::config.minSlider; + else + slider_v = 0.0; + } + else + { + if(_preferMidiVolumeDb) { - nvariSend -= ctrl->bias(); - if(nvariSend != variSend) - { - //controller[KNOB_VAR_SEND].knob->blockSignals(true); - gcon->knob->setValue(double(nvariSend)); - gcon->dl->setValue(double(nvariSend)); - //controller[KNOB_VAR_SEND].knob->blockSignals(false); - variSend = nvariSend; - } - } - } - - icl = mc->find(channel, MusECore::CTRL_REVERB_SEND); - en = icl != mc->end(); - - gcon = &controller[KNOB_REV_SEND]; - if(gcon->knob->isEnabled() != en) - gcon->knob->setEnabled(en); - if(gcon->lb->isEnabled() != en) - gcon->lb->setEnabled(en); - if(gcon->dl->isEnabled() != en) - gcon->dl->setEnabled(en); - - if(en) - { - ctrl = mp->midiController(MusECore::CTRL_REVERB_SEND); - int nreverbSend = icl->second->hwVal(); - if(nreverbSend == MusECore::CTRL_VAL_UNKNOWN) - { - // MusEGui::DoubleLabel ignores the value if already set... - //if(nreverbSend != reverbSend) - //{ - gcon->dl->setValue(gcon->dl->off() - 1.0); - //reverbSend = nreverbSend; - //} - reverbSend = MusECore::CTRL_VAL_UNKNOWN; - nreverbSend = mp->lastValidHWCtrlState(channel, MusECore::CTRL_REVERB_SEND); - if(nreverbSend != MusECore::CTRL_VAL_UNKNOWN) - { - nreverbSend -= ctrl->bias(); - if(double(nreverbSend) != gcon->knob->value()) - { - gcon->knob->setValue(double(nreverbSend)); - } - } + slider_v = muse_val2dbr(d_hwVal / double(max)) * 2.0; + if(slider_v < MusEGlobal::config.minSlider) + slider_v = MusEGlobal::config.minSlider; } else - { - nreverbSend -= ctrl->bias(); - if(nreverbSend != reverbSend) - { - //controller[KNOB_REV_SEND].knob->blockSignals(true); - gcon->knob->setValue(double(nreverbSend)); - gcon->dl->setValue(double(nreverbSend)); - //controller[KNOB_REV_SEND].knob->blockSignals(false); - reverbSend = nreverbSend; - } - } + slider_v = d_hwVal; } - - icl = mc->find(channel, MusECore::CTRL_CHORUS_SEND); - en = icl != mc->end(); - - gcon = &controller[KNOB_CHO_SEND]; - if(gcon->knob->isEnabled() != en) - gcon->knob->setEnabled(en); - if(gcon->lb->isEnabled() != en) - gcon->lb->setEnabled(en); - if(gcon->dl->isEnabled() != en) - gcon->dl->setEnabled(en); - - if(en) - { - ctrl = mp->midiController(MusECore::CTRL_CHORUS_SEND); - int nchorusSend = icl->second->hwVal(); - if(nchorusSend == MusECore::CTRL_VAL_UNKNOWN) - { - // MusEGui::DoubleLabel ignores the value if already set... - //if(nchorusSend != chorusSend) - //{ - gcon->dl->setValue(gcon->dl->off() - 1.0); - //chorusSend = nchorusSend; - //} - chorusSend = MusECore::CTRL_VAL_UNKNOWN; - nchorusSend = mp->lastValidHWCtrlState(channel, MusECore::CTRL_CHORUS_SEND); - if(nchorusSend != MusECore::CTRL_VAL_UNKNOWN) - { - nchorusSend -= ctrl->bias(); - if(double(nchorusSend) != gcon->knob->value()) - { - gcon->knob->setValue(double(nchorusSend)); - } - } - } + + if(slider_v != slider->value()) + { + slider->blockSignals(true); + slider->setValue(slider_v); + slider->blockSignals(false); + } + + if(d_vol <= 0.0) + sl->setValue(sl->minValue() - 0.5 * (sl->minValue() - sl->off())); + else + { + double sl_v = _preferMidiVolumeDb ? (muse_val2dbr(d_vol / double(max)) * 2.0) : d_vol; + + if(sl_v > sl->maxValue()) + sl->setValue(sl->maxValue()); else - { - nchorusSend -= ctrl->bias(); - if(nchorusSend != chorusSend) - { - gcon->knob->setValue(double(nchorusSend)); - gcon->dl->setValue(double(nchorusSend)); - chorusSend = nchorusSend; - } - } + sl->setValue(sl_v); } + + volume = d_hwVal; } + } + } +} + //--------------------------------------------------------- // ctrlChanged //--------------------------------------------------------- -void MidiStrip::ctrlChanged(int num, int val) +void MidiStrip::ctrlChanged(double v, bool off, int num, int scrollMode) { if (inHeartBeat) return; - - MusECore::MidiTrack* t = (MusECore::MidiTrack*) track; + if(!track || !track->isMidiTrack()) + return; + + MusECore::MidiTrack* t = static_cast(track); int port = t->outPort(); - int chan = t->outChannel(); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; - MusECore::MidiController* mctl = mp->midiController(num); - if((val < mctl->minVal()) || (val > mctl->maxVal())) - { - if(mp->hwCtrlState(chan, num) != MusECore::CTRL_VAL_UNKNOWN) - MusEGlobal::audio->msgSetHwCtrlState(mp, chan, num, MusECore::CTRL_VAL_UNKNOWN); - } - else + MusECore::MidiController* mctl = mp->midiController(num, false); + if(mctl) { - val += mctl->bias(); - - int tick = MusEGlobal::song->cpos(); - - MusECore::MidiPlayEvent ev(tick, port, chan, MusECore::ME_CONTROLLER, num, val); - - MusEGlobal::audio->msgPlayMidiEvent(&ev); - } - MusEGlobal::song->update(SC_MIDI_CONTROLLER); + double m_val = v; + if(_preferMidiVolumeDb) + m_val = double(mctl->maxVal()) * muse_db2val(m_val / 2.0); + + if(off || (m_val < double(mctl->minVal())) || (m_val > double(mctl->maxVal()))) + { + if(mp->hwCtrlState(chan, num) != MusECore::CTRL_VAL_UNKNOWN) + mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), port, chan, + MusECore::ME_CONTROLLER, + num, + MusECore::CTRL_VAL_UNKNOWN)); + } + else + { + m_val += double(mctl->bias()); + mp->putControllerValue(port, chan, num, m_val, false); + } + } + + componentChanged(ComponentRack::controllerComponent, v, off, num, scrollMode); } //--------------------------------------------------------- @@ -924,69 +2606,52 @@ //--------------------------------------------------------- void MidiStrip::volLabelChanged(double val) - { - val = sqrt( float(127*127) / pow(10.0, -val/20.0) ); - - ctrlChanged(MusECore::CTRL_VOLUME, lrint(val)); - - } +{ + ctrlChanged(val, false, MusECore::CTRL_VOLUME, SliderBase::ScrNone); +} //--------------------------------------------------------- // setVolume //--------------------------------------------------------- -void MidiStrip::setVolume(double val) - { - -// printf("Vol %d\n", lrint(val)); - ctrlChanged(MusECore::CTRL_VOLUME, lrint(val)); - } - +void MidiStrip::setVolume(double val, int id, int scrollMode) +{ + DEBUG_MIDI_STRIP("Vol %d\n", lrint(val)); + ctrlChanged(val, false, id, scrollMode); +} + //--------------------------------------------------------- -// setPan +// volumePressed //--------------------------------------------------------- -void MidiStrip::setPan(double val) +void MidiStrip::volumePressed(double val, int id) { - - ctrlChanged(MusECore::CTRL_PANPOT, lrint(val)); + DEBUG_MIDI_STRIP(stderr, "MidiStrip::volumePressed\n"); + if(!track || !track->isMidiTrack()) + return; + componentPressed(ComponentRack::controllerComponent, val, id); } //--------------------------------------------------------- -// setVariSend +// volumeReleased //--------------------------------------------------------- -void MidiStrip::setVariSend(double val) +void MidiStrip::volumeReleased(double val, int id) { - ctrlChanged(MusECore::CTRL_VARIATION_SEND, lrint(val)); + DEBUG_MIDI_STRIP(stderr, "MidiStrip::volumeReleased\n"); + if(!track || !track->isMidiTrack()) + return; + componentReleased(ComponentRack::controllerComponent, val, id); } - -//--------------------------------------------------------- -// setChorusSend -//--------------------------------------------------------- -void MidiStrip::setChorusSend(double val) - { - ctrlChanged(MusECore::CTRL_CHORUS_SEND, lrint(val)); - } - -//--------------------------------------------------------- -// setReverbSend -//--------------------------------------------------------- -void MidiStrip::setReverbSend(double val) - { - ctrlChanged(MusECore::CTRL_REVERB_SEND, lrint(val)); - } - //--------------------------------------------------------- // iRoutePressed //--------------------------------------------------------- void MidiStrip::iRoutePressed() { - //MusEGui::RoutePopupMenu* pup = MusEGlobal::muse->getRoutingPopupMenu(); - RoutePopupMenu* pup = new RoutePopupMenu(); + RoutePopupMenu* pup = new RoutePopupMenu(0, false, _broadcastChanges); pup->exec(QCursor::pos(), track, false); delete pup; iR->setDown(false); @@ -998,11 +2663,119 @@ void MidiStrip::oRoutePressed() { - //MusEGui::RoutePopupMenu* pup = MusEGlobal::muse->getRoutingPopupMenu(); - RoutePopupMenu* pup = new RoutePopupMenu(); + RoutePopupMenu* pup = new RoutePopupMenu(0, true, _broadcastChanges); pup->exec(QCursor::pos(), track, true); delete pup; oR->setDown(false); } +void MidiStrip::incVolume(int v) +{ + if(!track || !track->isMidiTrack()) + return; + + const int id = MusECore::CTRL_VOLUME; + + MusECore::MidiTrack* t = static_cast(track); + const int port = t->outPort(); + const int chan = t->outChannel(); + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiController* mctl = mp->midiController(id, false); + + if(mctl) + { + // Get the slider's current value. + const double prev_val = slider->value(); + double d_prev_val = prev_val; + if(_preferMidiVolumeDb) + d_prev_val = double(mctl->maxVal()) * muse_db2val(d_prev_val / 2.0); + + // Increment the slider. Do not allow signalling. + slider->blockSignals(true); + slider->incValue(v); + slider->blockSignals(false); + // Now grab the control's new value. + const double new_val = slider->value(); + + double d_new_val = new_val; + if(_preferMidiVolumeDb) + d_new_val = double(mctl->maxVal()) * muse_db2val(d_new_val / 2.0); + + if((d_new_val < double(mctl->minVal())) || (d_new_val > double(mctl->maxVal()))) + { + if(mp->hwCtrlState(chan, id) != MusECore::CTRL_VAL_UNKNOWN) + mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), port, chan, + MusECore::ME_CONTROLLER, + id, + MusECore::CTRL_VAL_UNKNOWN)); + } + else + { + d_new_val += double(mctl->bias()); + mp->putControllerValue(port, chan, id, d_new_val, false); + } + + componentIncremented(ComponentRack::controllerComponent, + prev_val, new_val, + false, id, Slider::ScrNone); + } +} + +void MidiStrip::incPan(int v) +{ + if(!track || !track->isMidiTrack()) + return; + + const int id = MusECore::CTRL_PANPOT; + + ComponentRack* rack = 0; + ComponentWidget* cw = 0; + // Be sure to search all racks. Even if pan is in multiple racks, only one hit is + // needed since after the value is set, the other pan controls will be updated too. + if((cw = _upperRack->findComponent(ComponentRack::controllerComponent, -1, id))) + rack = _upperRack; + else if((cw = _infoRack->findComponent(ComponentRack::controllerComponent, -1, id))) + rack = _infoRack; + else if((cw = _lowerRack->findComponent(ComponentRack::controllerComponent, -1, id))) + rack = _lowerRack; + + if(!cw || !rack) + return; + + MusECore::MidiTrack* t = static_cast(track); + const int port = t->outPort(); + const int chan = t->outChannel(); + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiController* mctl = mp->midiController(id, false); + if(mctl) + { + // Get the component's current value. + double prev_val = rack->componentValue(*cw); + // Now increment the component. Do not allow signalling. + rack->incComponentValue(*cw, v, true); + // Now grab its value. + const double d_new_val = rack->componentValue(*cw); + + double d_fin_val = d_new_val; + + if((d_fin_val < double(mctl->minVal())) || (d_fin_val > double(mctl->maxVal()))) + { + if(mp->hwCtrlState(chan, MusECore::CTRL_PANPOT) != MusECore::CTRL_VAL_UNKNOWN) + mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), port, chan, + MusECore::ME_CONTROLLER, + id, + MusECore::CTRL_VAL_UNKNOWN)); + } + else + { + d_fin_val += double(mctl->bias()); + mp->putControllerValue(port, chan, id, d_fin_val, false); + } + + componentIncremented(ComponentRack::controllerComponent, + prev_val, d_new_val, + false, id, Slider::ScrNone); + } +} + } // namespace MusEGui diff -Nru muse-2.1.2/muse/mixer/mstrip.h muse-3.0.2+ds1/muse/mixer/mstrip.h --- muse-2.1.2/muse/mixer/mstrip.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/mstrip.h 2017-12-04 21:01:18.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: mstrip.h,v 1.4.2.4 2009/10/25 19:26:29 lunar_shuttle Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011 - 2017 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -27,22 +27,159 @@ #include "type_defs.h" #include "strip.h" -#include +class QWidget; class QAction; class QDialog; -class QLabel; class QString; +class QString; +class QPoint; +class QVBoxLayout; +class QSpacerItem; namespace MusECore { class MidiTrack; } namespace MusEGui { +class ElidedLabel; class DoubleLabel; -class Knob; class Slider; -class TransparentToolButton; +class CompactSlider; +class CompactPatchEdit; +class IconButton; + +//--------------------------------------------------------- +// MidiComponentRack +//--------------------------------------------------------- + +class MidiComponentRack : public ComponentRack +{ + Q_OBJECT + + public: + // Type of component. + //enum MStripComponentType { type = userComponent }; + // Possible widget types. + enum MStripComponentWidgetType { mStripCompactPatchEditComponentWidget = userComponentWidget }; + // Some controller types. + enum MStripControlType { mStripPanControl = 0, mStripVarSendControl, mStripRevSendControl, mStripChoSendControl, mStripProgramControl }; + // Possible component properties. + enum MStripComponentProperties + { + mStripInstrumentProperty = userComponentProperty, + mStripTranspProperty, + mStripDelayProperty, + mStripLenProperty, + mStripVeloProperty, + mStripComprProperty + }; + + protected: + MusECore::MidiTrack* _track; + + // Creates a new component widget from the given desc. Called by newComponent(). + // Connects known widget types' signals to slots. + virtual void newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); + // Scan and automatically remove missing / add new controllers. + void scanControllerComponents(); + // Set component colours upon config changed. + void setComponentColors(); + void labelPropertyPressHandler(QPoint p, int id, Qt::KeyboardModifiers keys); + + protected slots: + virtual void controllerChanged(int val, int id); + virtual void controllerChanged(double val, int id); + virtual void controllerChanged(double val, bool isOff, int id, int scrollMode); + virtual void controllerMoved(double, int, bool); + virtual void controllerPressed(double, int); + virtual void controllerReleased(double, int); + virtual void controllerRightClicked(QPoint, int); + virtual void propertyChanged(double val, bool isOff, int id, int scrollMode); + virtual void propertyMoved(double, int, bool); + virtual void propertyPressed(double, int); + virtual void propertyReleased(double, int); + virtual void propertyRightClicked(QPoint, int); + virtual void labelPropertyPressed(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); + virtual void labelPropertyReleased(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); + virtual void labelPropertyReturnPressed(QPoint p, int id, Qt::KeyboardModifiers keys); + void patchPopup(QPoint p); + void patchPopupActivated(QAction*); + void instrPopup(QPoint p); + + + virtual void patchEditNameClicked(QPoint p, int id); + + public slots: + virtual void configChanged(); + virtual void songChanged(MusECore::SongChangedFlags_t); + + public: + MidiComponentRack(MusECore::MidiTrack* track, int id = -1, QWidget* parent = 0, Qt::WindowFlags f = 0); + + MusECore::MidiTrack* track() { return _track; } + // Adds a component to the layout and the list. Creates a new component using + // the given desc values if the desc widget is not given. + virtual void newComponent( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); + // Updates all the components, for example updating the values of sliders. + void updateComponents(); + // Enable or disable all the aux components in this rack. + void setAuxEnabled(bool enable); + // Sets up tabbing for the existing controls in the rack. + // Accepts a previousWidget which can be null and returns the last widget in the rack, + // which allows chaining racks or other widgets. + virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); +}; + +//--------------------------------------------- +// CompactPatchEditComponentDescriptor +// Class defining a CompactPatchEdit to be added to a rack layout. +//--------------------------------------------- + +class CompactPatchEditComponentDescriptor : public ComponentDescriptor +{ + public: + // Return value pointer created by the function, corresponding to a ComponentWidgetType: + CompactPatchEdit* _compactPatchEdit; + + double _initVal; + bool _isOff; + QColor _readoutColor; + + public: + CompactPatchEditComponentDescriptor() : + ComponentDescriptor(ComponentRack::CompactSliderComponentWidget, + ComponentRack::controllerComponent), + _compactPatchEdit(0), + _initVal(0.0), + _isOff(false) + { } + + CompactPatchEditComponentDescriptor( + ComponentWidget::ComponentType componentType, + const char* objName = 0, + int index = 0, + const QString& toolTipText = QString(), + const QString& label = QString(), + const QColor& readoutColour = QColor(), + bool enabled = true, + double initVal = 0.0, + bool isOff = false + ) + : ComponentDescriptor(MidiComponentRack::mStripCompactPatchEditComponentWidget, + componentType, + objName, + index, + toolTipText, + label, + readoutColour, + enabled + ), + _compactPatchEdit(0), + _initVal(initVal), + _isOff(isOff) + { } +}; //--------------------------------------------------------- // MidiStrip @@ -51,40 +188,78 @@ class MidiStrip : public Strip { Q_OBJECT - MusEGui::Slider* slider; - MusEGui::DoubleLabel* sl; - MusEGui::TransparentToolButton* off; - - struct KNOB { - MusEGui::Knob* knob; - MusEGui::DoubleLabel* dl; - QLabel* lb; - } controller[4]; // pan variation reverb chorus - - int volume; - int variSend; - int reverbSend; - int chorusSend; - int pan; + public: + // ID numbers for each rack in this strip. + enum MStripRacks { mStripUpperRack = 0, mStripInfoRack = 1, mStripLowerRack = 2 }; + + private: + GridPosStruct _upperStackTabPos; + GridPosStruct _preScrollAreaPos_A; + GridPosStruct _preScrollAreaPos_B; + GridPosStruct _infoSpacerTop; + GridPosStruct _infoSpacerBottom; + GridPosStruct _propertyRackPos; + GridPosStruct _sliderPos; + GridPosStruct _sliderLabelPos; + GridPosStruct _postScrollAreaPos_A; + GridPosStruct _postScrollAreaPos_B; + GridPosStruct _offPos; + GridPosStruct _recPos; + GridPosStruct _mutePos; + GridPosStruct _soloPos; + GridPosStruct _routesPos; + GridPosStruct _inRoutesPos; + GridPosStruct _outRoutesPos; + GridPosStruct _automationPos; + GridPosStruct _rightSpacerPos; + GridPosStruct _offMonRecPos; + + Slider* slider; + DoubleLabel* sl; + IconButton* off; + IconButton* _recMonitor; + + ElidedLabel* _upperStackTabButtonA; + ElidedLabel* _upperStackTabButtonB; + MidiComponentRack* _upperRack; + MidiComponentRack* _lowerRack; + MidiComponentRack* _infoRack; + + // Whether the layout is in mode A (normal, racks on left) or B (racks on right). + bool _isExpanded; + // Current local state of knobs versus sliders preference global setting. + bool _preferKnobs; + // Current local state of midi volume as decibels preference. + bool _preferMidiVolumeDb; + + int _heartBeatCounter; + + double volume; bool inHeartBeat; - void addKnob(int idx, const QString&, const QString&, const char*, bool); - void ctrlChanged(int num, int val); void updateControls(); void updateOffState(); - + void updateRackSizes(bool upper, bool lower); + + protected: + void setupMidiVolume(); + private slots: + void recMonitorToggled(bool); void offToggled(bool); void iRoutePressed(); void oRoutePressed(); - void setVolume(double); - void setPan(double); - void setChorusSend(double); - void setVariSend(double); - void setReverbSend(double); - void labelDoubleClicked(int); + void setVolume(double val, int id, int scrollMode); + void volumePressed(double val, int id); + void volumeReleased(double val, int id); + void ctrlChanged(double val, bool off, int num, int scrollMode); + + void volLabelDoubleClicked(); void volLabelChanged(double); - void controlRightClicked(const QPoint&, int); + void controlRightClicked(QPoint, int); + + void upperStackTabButtonAPressed(); + void upperStackTabButtonBPressed(); protected slots: virtual void heartBeat(); @@ -92,9 +267,30 @@ public slots: virtual void songChanged(MusECore::SongChangedFlags_t); virtual void configChanged(); + void incVolume(int v); + void incPan(int v); public: - MidiStrip(QWidget* parent, MusECore::MidiTrack*); + MidiStrip(QWidget* parent, MusECore::MidiTrack*, bool hasHandle = false, bool isEmbedded = true); + + static const double volSliderStepLin; + + static const double volSliderStepDb; + static const double volSliderMaxDb; + static const int volSliderPrecDb; + + static const int xMarginHorSlider; + static const int yMarginHorSlider; + static const int upperRackSpacerHeight; + static const int rackFrameWidth; + + // Destroy and rebuild strip components. + virtual void buildStrip(); + + // Sets up tabbing for the entire strip. + // Accepts a previousWidget which can be null and returns the last widget in the strip, + // which allows chaining other widgets. + virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/mixer/rack.cpp muse-3.0.2+ds1/muse/mixer/rack.cpp --- muse-2.1.2/muse/mixer/rack.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/rack.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: rack.cpp,v 1.7.2.7 2007/01/27 14:52:43 spamatica Exp $ // // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -33,6 +34,7 @@ #include #include #include +#include "widgets/popupmenu.h" #include @@ -44,10 +46,17 @@ #include "gconfig.h" #include "globaldefs.h" #include "plugin.h" +#include "plugindialog.h" #include "filedialog.h" +#include "background_painter.h" +#ifdef LV2_SUPPORT +#include "lv2host.h" +#endif namespace MusEGui { +QString MUSE_MIME_TYPE = "text/x-muse-plugin"; + //--------------------------------------------------------- // class EffectRackDelegate //--------------------------------------------------------- @@ -62,42 +71,52 @@ const QStyleOptionViewItem & option, const QModelIndex & index ) const; EffectRackDelegate(QObject * parent, MusECore::AudioTrack* at ); + + virtual QSize sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const; + static const int itemXMargin; + static const int itemYMargin; + static const int itemTextXMargin; + static const int itemTextYMargin; }; +const int EffectRackDelegate::itemXMargin = 1; +const int EffectRackDelegate::itemYMargin = 1; +const int EffectRackDelegate::itemTextXMargin = 1; +const int EffectRackDelegate::itemTextYMargin = 1; + EffectRackDelegate::EffectRackDelegate(QObject * parent, MusECore::AudioTrack* at ) : QStyledItemDelegate(parent) { er = (EffectRack*) parent; tr = at; } +QSize EffectRackDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const +{ + return QSize(10, option.fontMetrics.height() + 2 * itemYMargin + 2 * itemTextYMargin); +} + void EffectRackDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { painter->save(); painter->setRenderHint(QPainter::Antialiasing); - QRect rr = er->visualItemRect(er->item(index.row())); - QRect cr = QRect(rr.x()+1, rr.y()+1, - rr.width()-2, rr.height() -2); - painter->fillRect(rr, option.palette.dark().color().darker(130)); - - QColor mask_edge = QColor(110, 110, 110, 55); - QColor mask_center = QColor(220, 220, 220, 55); - QLinearGradient mask; - mask.setColorAt(0, mask_edge); - mask.setColorAt(0.5, mask_center); - mask.setColorAt(1, mask_edge); - mask.setStart(QPointF(0, cr.y())); - mask.setFinalStop(QPointF(0, cr.y() + cr.height())); - - painter->setBrush(tr->efxPipe()->isOn(index.row()) ? - er->getActiveColor() : - option.palette.dark()); - painter->setPen(Qt::NoPen); - painter->drawRoundedRect(cr, 2, 2); - painter->setBrush(mask); - painter->drawRoundedRect(cr, 2, 2); + const QRect rr = option.rect; + QRect cr = QRect(rr.x()+itemXMargin, rr.y()+itemYMargin, + rr.width() - 2 * itemXMargin, rr.height() - 2 * itemYMargin); + + const QRect onrect = tr->efxPipe()->isOn(index.row()) ? rr : QRect(); + ItemBackgroundPainter* ibp = er->getBkgPainter(); + ibp->drawBackground(painter, + rr, + option.palette, + itemXMargin, + itemYMargin, + onrect); //, + // No, let the background painter handle the on colour. + //tr->efxPipe()->isOn(index.row()) ? er->getActiveColor() : option.palette.dark(); QString name = tr->efxPipe()->name(index.row()); - if (name.length() > 11) - name = name.left(9) + "..."; + //if (name.length() > 11) + // name = name.left(9) + "..."; if (option.state & QStyle::State_Selected) { @@ -110,10 +129,13 @@ painter->setPen(QPen(QColor(48,48,48))); else painter->setPen(QPen(Qt::black)); - - painter->drawText(cr.x()+2, cr.y()+1, - cr.width()-2, cr.height()-1, - Qt::AlignLeft, name); + + painter->drawText(cr.x() + itemTextXMargin, + cr.y() + itemTextYMargin, + cr.width() - 2 * itemTextXMargin, + cr.height() - 2 * itemTextYMargin, + Qt::AlignLeft | Qt::AlignVCenter, + name); painter->restore(); } @@ -130,7 +152,7 @@ public: RackSlot(QListWidget* lb, MusECore::AudioTrack* t, int i, int h); ~RackSlot(); - void setBackgroundColor(const QBrush& brush) {setBackground(brush);}; + //void setBackgroundColor(const QBrush& brush) {setBackground(brush);} }; RackSlot::~RackSlot() @@ -142,12 +164,11 @@ // RackSlot //--------------------------------------------------------- -RackSlot::RackSlot(QListWidget* b, MusECore::AudioTrack* t, int i, int h) +RackSlot::RackSlot(QListWidget* b, MusECore::AudioTrack* t, int i, int /*h*/) : QListWidgetItem(b) { node = t; idx = i; - setSizeHint(QSize(10,h)); } //--------------------------------------------------------- @@ -159,10 +180,13 @@ { setObjectName("Rack"); setAttribute(Qt::WA_DeleteOnClose); + + _bkgPainter = new ItemBackgroundPainter(); + track = t; itemheight = 19; - setFont(MusEGlobal::config.fonts[1]); - activeColor = QColor(74, 165, 49); + //setFont(MusEGlobal::config.fonts[1]); + //activeColor = QColor(74, 165, 49); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); @@ -190,9 +214,14 @@ for (int i = 0; i < PipelineDepth; ++i) { QString name = track->efxPipe()->name(i); item(i)->setText(name); - item(i)->setBackground(track->efxPipe()->isOn(i) ? activeColor : palette().dark()); item(i)->setToolTip(name == QString("empty") ? tr("effect rack") : name ); - } + //item(i)->setBackground(track->efxPipe()->isOn(i) ? activeColor : palette().dark()); + if(viewport()) + { + QRect r(visualItemRect(item(i))); + viewport()->update(r); + } + } } //--------------------------------------------------------- @@ -201,6 +230,8 @@ EffectRack::~EffectRack() { + if(_bkgPainter) + delete _bkgPainter; } //--------------------------------------------------------- @@ -220,8 +251,10 @@ QSize EffectRack::minimumSizeHint() const { - // FIXME(Orcan): Why do we have to manually add 6 pixels? - return QSize(10, itemheight * PipelineDepth + 6); + return QSize(10, + 2 * frameWidth() + + (fontMetrics().height() + 2 * EffectRackDelegate::itemYMargin + 2 * EffectRackDelegate::itemTextYMargin) + * PipelineDepth); } //--------------------------------------------------------- @@ -302,6 +335,10 @@ showGuiAction->setChecked(pipe->guiVisible(idx)); showNativeGuiAction->setChecked(pipe->nativeGuiVisible(idx)); +#ifdef LV2_SUPPORT + PopupMenu *mSubPresets = NULL; +#endif + if (pipe->empty(idx)) { menu->removeAction(changeAction); menu->removeAction(saveAction); @@ -321,7 +358,23 @@ //if(!pipe->isDssiPlugin(idx)) if(!pipe->has_dssi_ui(idx)) // p4.0.19 Tim. showNativeGuiAction->setEnabled(false); +#ifdef LV2_SUPPORT + //show presets submenu for lv2 plugins + mSubPresets = new PopupMenu(tr("Presets")); + if(pipe->isLV2Plugin(idx)) + { + menu->addMenu(mSubPresets); + MusECore::PluginI *plugI = pipe->at(idx); + static_cast(plugI->plugin())->populatePresetsMenu(plugI, mSubPresets); + } + else + { + delete mSubPresets; + mSubPresets = NULL; } +#endif + } + #ifndef OSC_SUPPORT showNativeGuiAction->setEnabled(false); @@ -336,6 +389,19 @@ delete menu; return; } +#ifdef LV2_SUPPORT + if (mSubPresets != NULL) { + QWidget *mwidget = act->parentWidget(); + if (mwidget != NULL) { + if(mSubPresets == dynamic_cast(mwidget)) { + MusECore::PluginI *plugI = pipe->at(idx); + static_cast(plugI->plugin())->applyPreset(plugI, act->data().value()); + delete menu; + return; + } + } + } +#endif int sel = act->data().toInt(); delete menu; @@ -477,7 +543,7 @@ fclose(presetFp); } -void EffectRack::startDrag(int idx) +void EffectRack::startDragItem(int idx) { if (idx < 0) { printf("illegal to drag index %d\n",idx); @@ -517,14 +583,13 @@ QString xmlconf; xml.dump(xmlconf); - printf("[%s]\n", xmlconf.toLatin1().constData()); - - - QByteArray data(xmlconf.toLatin1().constData()); - //printf("sending %d [%s]\n", data.length(), xmlconf.toLatin1().constData()); QMimeData* md = new QMimeData(); - - md->setData("text/x-muse-plugin", data); + QByteArray data(xmlconf.toLatin1().constData()); + + if (MusEGlobal::debugMsg) + printf("Sending %d [%s]\n", data.length(), xmlconf.toLatin1().constData()); + + md->setData(MUSE_MIME_TYPE, data); QDrag* drag = new QDrag(this); drag->setMimeData(md); @@ -541,13 +606,12 @@ { QStringList mTypes; mTypes << "text/uri-list"; - mTypes << "text/x-muse-plugin"; + mTypes << MUSE_MIME_TYPE; return mTypes; } void EffectRack::dropEvent(QDropEvent *event) - { - QString text; +{ QListWidgetItem *i = itemAt( event->pos() ); if (!i) return; @@ -557,7 +621,7 @@ if (pipe) { if ((*pipe)[idx] != NULL) { - QWidget *sw = event->source(); + QWidget *sw = static_cast(event->source()); if(sw) { if(strcmp(sw->metaObject()->className(), "EffectRack") == 0) @@ -585,22 +649,24 @@ } } - if(event->mimeData()->hasFormat("text/x-muse-plugin")) + if(event->mimeData()->hasFormat(MUSE_MIME_TYPE)) { - MusECore::Xml xml(event->mimeData()->data("text/x-muse-plugin").data()); + QByteArray mimeData = event->mimeData()->data(MUSE_MIME_TYPE).constData(); + MusECore::Xml xml(mimeData.constData()); + if (MusEGlobal::debugMsg) + printf("received %d [%s]\n", mimeData.size(), mimeData.constData()); + initPlugin(xml, idx); } - else - if (event->mimeData()->hasUrls()) + else if (event->mimeData()->hasUrls()) { // Multiple urls not supported here. Grab the first one. - text = event->mimeData()->urls()[0].path(); + QString text = event->mimeData()->urls()[0].path(); if (text.endsWith(".pre", Qt::CaseInsensitive) || text.endsWith(".pre.gz", Qt::CaseInsensitive) || text.endsWith(".pre.bz2", Qt::CaseInsensitive)) { - //bool popenFlag = false; bool popenFlag; FILE* fp = MusEGui::fileOpen(this, text, ".pre", "r", popenFlag, false, false); if (fp) @@ -608,7 +674,6 @@ MusECore::Xml xml(fp); initPlugin(xml, idx); - // Added by T356. if (popenFlag) pclose(fp); else @@ -617,7 +682,7 @@ } } } - } +} void EffectRack::dragEnterEvent(QDragEnterEvent *event) { @@ -662,7 +727,7 @@ QListWidgetItem *i = itemAt( event->pos() ); if (i) { int idx = row(i); - startDrag(idx); + startDragItem(idx); } } } diff -Nru muse-2.1.2/muse/mixer/rack.h muse-3.0.2+ds1/muse/mixer/rack.h --- muse-2.1.2/muse/mixer/rack.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/rack.h 2017-12-04 21:01:18.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: rack.h,v 1.5.2.3 2006/09/24 19:32:31 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -38,6 +39,7 @@ } namespace MusEGui { +class ItemBackgroundPainter; //--------------------------------------------------------- // EffectRack @@ -49,12 +51,13 @@ MusECore::AudioTrack* track; int itemheight; - QColor activeColor; + //QColor activeColor; + ItemBackgroundPainter* _bkgPainter; virtual QSize minimumSizeHint() const; virtual QSize sizeHint() const; - void startDrag(int idx); + void startDragItem(int idx); void initPlugin(MusECore::Xml xml, int idx); QPoint dragPos; void savePreset(int idx); @@ -81,7 +84,8 @@ MusECore::AudioTrack* getTrack() { return track; } QPoint getDragPos() { return dragPos; } - QColor getActiveColor() { return activeColor; } + //QColor getActiveColor() { return activeColor; } + ItemBackgroundPainter* getBkgPainter() const { return _bkgPainter; } }; diff -Nru muse-2.1.2/muse/mixer/routedialogbase.ui muse-3.0.2+ds1/muse/mixer/routedialogbase.ui --- muse-2.1.2/muse/mixer/routedialogbase.ui 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/routedialogbase.ui 2017-12-04 21:01:18.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 316 - 383 + 555 + 337 @@ -19,156 +19,447 @@ MusE: Routing - - - 4 - - - 4 - + - - - Add Route + + + Qt::Vertical - - - 4 + + + Qt::Horizontal - - 4 + + false - - + + + + 0 + - - - - 0 - 0 - - - - Source: - - - - 0 - - - 2 - - - - - + + + Qt::Vertical + - - - - 0 - 0 - - - - Destination: - - - - 0 - - - 2 + + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOn + + + false + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + 16 + 16 + + + + Qt::ElideMiddle + + + false + + + true + + + false + + + + 1 - - - - + - - - - - - 0 - 0 - + + + + + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. + + + true + + + + + + 0 - - connect source to destination - - - Connect - - - - + + + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOn + + + false + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + 16 + 16 + + + + Qt::ElideMiddle + + + false + + + false + + + false + + + + 1 + + + + + + + + Qt::Vertical + + + + + + + + + Itemized list of current connections. + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideMiddle + + + false + + + + 1 + + + - - - Current Routes + + + 2 - - - 4 - - - 4 - - - - - - Source - - - true - - - true - - - - - Destination - - - true - - - true - - - - - - - - - 0 - 0 - - - - remove selected route - - - Remove - - - - - + + + + Show only selected sources + + + Show only selected sources + + + true + + + + + + + Show only destination routes for selected source + + + Show only destination routes for selected source + + + true + + + + + + + Qt::Vertical + + + + + + + Show only selected destinations + + + Show only selected destinations + + + true + + + + + + + Show only source routes for selected destination + + + Show only source routes for selected destination + + + true + + + + + + + Qt::Vertical + + + + + + + Show all Midi Ports + + + Show all Midi Ports + + + true + + + + + + + Qt::Vertical + + + + + + + Auto adjust column size + + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + + + + + + true + + + + + + + Preferred route name or alias + + + Preferred route name or alias + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Connect source to destination + + + Connect source to destination + + + Connect + + + + + + + + 0 + 0 + + + + Remove selected route + + + Remove selected route + + + Remove + + + + + + + MusEGui::ConnectionsView + QFrame +
    routedialog.h
    + 1 +
    + + MusEGui::RouteTreeWidget + QTreeWidget +
    routedialog.h
    + 1 +
    +
    diff -Nru muse-2.1.2/muse/mixer/routedialog.cpp muse-3.0.2+ds1/muse/mixer/routedialog.cpp --- muse-2.1.2/muse/mixer/routedialog.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/routedialog.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: routedialog.cpp,v 1.5.2.2 2007/01/04 00:35:17 terminator356 Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,184 +23,7465 @@ //========================================================= #include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "routedialog.h" +#include "globaldefs.h" +#include "gconfig.h" #include "track.h" #include "song.h" #include "audio.h" #include "driver/jackaudio.h" +#include "globaldefs.h" +#include "app.h" +#include "operations.h" +#include "icons.h" + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); + +// Undefine if and when multiple output routes are added to midi tracks. +#define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + +// An arbitrarily large value for size hint calculations. +#define _VERY_LARGE_INTEGER_ 1000000 namespace MusEGui { +const QString RouteDialog::tracksCat(QObject::tr("Tracks:")); +const QString RouteDialog::midiPortsCat(QObject::tr("Midi ports:")); +const QString RouteDialog::midiDevicesCat(QObject::tr("Midi devices:")); +const QString RouteDialog::jackCat(QObject::tr("Jack:")); +const QString RouteDialog::jackMidiCat(QObject::tr("Jack midi:")); + +const int RouteDialog::channelDotDiameter = 12; +const int RouteDialog::channelDotSpacing = 1; +const int RouteDialog::channelDotsPerGroup = 4; +const int RouteDialog::channelDotGroupSpacing = 3; +const int RouteDialog::channelDotsMargin = 1; +const int RouteDialog::channelBarHeight = RouteDialog::channelDotDiameter + 2 * RouteDialog::channelDotsMargin; +const int RouteDialog::channelLineWidth = 1; +const int RouteDialog::channelLinesSpacing = 1; +const int RouteDialog::channelLinesMargin = 1; + +std::list tmpJackInPorts; +std::list tmpJackOutPorts; +std::list tmpJackMidiInPorts; +std::list tmpJackMidiOutPorts; + +//--------------------------------------------------------- +// RouteChannelsList +//--------------------------------------------------------- + +int RouteChannelsList::connectedChannels() const +{ + int n = 0; + const int sz = size(); + for(int i = 0; i < sz; ++i) + if(at(i)._connected) + ++n; + return n; +} + +// Static. +int RouteChannelsList::channelsPerWidth(int width) +{ +// if(width <= 0) +// return size(); + if(width < 0) + width = _VERY_LARGE_INTEGER_; + + int groups_per_col = (width - 2 * RouteDialog::channelDotsMargin) / + (RouteDialog::channelDotGroupSpacing + + RouteDialog::channelDotsPerGroup * (RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing)); + if(groups_per_col < 1) + groups_per_col = 1; + return RouteDialog::channelDotsPerGroup * groups_per_col; +} + +// Static. +int RouteChannelsList::groupsPerChannels(int channels) +{ + + int groups = channels / RouteDialog::channelDotsPerGroup; + //if(groups < 1) + // groups = 1; + if(channels % RouteDialog::channelDotsPerGroup) + ++groups; + return groups; +} + +int RouteChannelsList::barsPerColChannels(int cc) const +{ + if(cc == 0) + return 0; + const int chans = size(); + int bars = chans / cc; + if(chans % cc) + ++bars; + //if(chan_rows < 1) + // chan_rows = 1; + return bars; +} + +// Static. +int RouteChannelsList::minimumWidthHint() +{ + return RouteDialog::channelDotsPerGroup * (RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing) + + RouteDialog::channelDotGroupSpacing + + 2 * RouteDialog::channelDotsMargin; +} + +int RouteChannelsList::widthHint(int width) const +{ + const int chans = size(); + int chans_per_col = channelsPerWidth(width); + // Limit to actual number of channels available. + if(chans_per_col > chans) + chans_per_col = chans; + const int groups_per_col = groupsPerChannels(chans_per_col); + return chans_per_col * (RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing) + + groups_per_col * RouteDialog::channelDotGroupSpacing + + 2 * RouteDialog::channelDotsMargin; +} + +int RouteChannelsList::heightHint(int width) const +{ + const int chans = size(); + int chans_per_col = channelsPerWidth(width); + // Limit to actual number of channels available. + if(chans_per_col > chans) + chans_per_col = chans; + const int bars = barsPerColChannels(chans_per_col); + return bars * RouteDialog::channelBarHeight + + connectedChannels() * (RouteDialog::channelLinesSpacing + RouteDialog::channelLineWidth) + + 4 * RouteDialog::channelLinesMargin; +} + +//--------------------------------------------------------- +// RouteTreeWidgetItem +//--------------------------------------------------------- + +void RouteTreeWidgetItem::init() +{ + _curChannel = 0; + setChannels(); + //computeChannelYValues(); + + // A data role to pass the item type from item to delegate. + //setData(RouteDialog::ROUTE_NAME_COL, TypeRole, QVariant::fromValue(type())); +} + +bool RouteTreeWidgetItem::setChannels() +{ + bool changed = false; + + switch(type()) + { + case NormalItem: + case CategoryItem: + case RouteItem: + break; + + case ChannelsItem: + switch(_route.type) + { + case MusECore::Route::TRACK_ROUTE: + if(_route.track) + { + MusECore::RouteCapabilitiesStruct rcaps = _route.track->routeCapabilities(); + int chans = 0; + switch(_route.track->type()) + { + case MusECore::Track::AUDIO_INPUT: + chans = _isInput ? rcaps._trackChannels._outChannels : rcaps._jackChannels._inChannels; + break; + case MusECore::Track::AUDIO_OUTPUT: + chans = _isInput ? rcaps._jackChannels._outChannels : rcaps._trackChannels._inChannels; + break; + case MusECore::Track::MIDI: + case MusECore::Track::DRUM: + case MusECore::Track::NEW_DRUM: +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + chans = _isInput ? MIDI_CHANNELS : rcaps._midiPortChannels._inChannels; +#else + chans = _isInput ? rcaps._midiPortChannels._outChannels : rcaps._midiPortChannels._inChannels; +#endif + break; + + case MusECore::Track::WAVE: + case MusECore::Track::AUDIO_AUX: + case MusECore::Track::AUDIO_SOFTSYNTH: + case MusECore::Track::AUDIO_GROUP: + chans = _isInput ? rcaps._trackChannels._outChannels : rcaps._trackChannels._inChannels; + break; + } + + if(chans != _channels.size()) + { + _channels.resize(chans); + changed = true; + } + } + break; + + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + break; + } + + + if(changed) + { + _curChannel = 0; + //computeChannelYValues(); + } + + return changed; +} + +void RouteTreeWidgetItem::getSelectedRoutes(MusECore::RouteList& routes) +{ + switch(type()) + { + case NormalItem: + case CategoryItem: + break; + case RouteItem: + if(isSelected()) + routes.push_back(_route); + break; + case ChannelsItem: + switch(_route.type) + { + case MusECore::Route::TRACK_ROUTE: + if(_route.track) + { + MusECore::Route r(_route); + const int sz = _channels.size(); + if(_route.track->isMidiTrack()) + { + for(int i = 0; i < sz && i < MIDI_CHANNELS; ++i) + { + //if(_channels.testBit(i)) + if(_channels.selected(i)) + { + //r.channel = (1 << i); + r.channel = i; + routes.push_back(r); + } + } + } + else + { + for(int i = 0; i < sz; ++i) + { + //if(_channels.testBit(i)) + if(_channels.selected(i)) + { + r.channel = i; + routes.push_back(r); + } + } + } + } + break; + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + if(isSelected()) + routes.push_back(_route); + break; + } + + break; + } +} + +int RouteTreeWidgetItem::channelAt(const QPoint& pt, const QRect& rect) const +{ +// if(!treeWidget()->viewport()) +// return false; + + RouteTreeWidget* rtw = qobject_cast(treeWidget()); + if(!rtw) + return false; + + const int col = rtw->columnAt(pt.x()); + const int col_width = rtw->columnWidth(col); + //const int view_width = rtw->viewport()->width(); + const int chans = _channels.size(); + const int view_offset = rtw->header()->offset(); +// const int x_offset = (_isInput ? view_width - _channels.widthHint(view_width) - view_offset : -view_offset); + const int x_offset = (_isInput ? + //view_width - _channels.widthHint(rtw->wordWrap() ? view_width : -1) - view_offset : -view_offset); + col_width - _channels.widthHint(rtw->channelWrap() ? col_width : -1) - view_offset : -view_offset); + + QPoint p(pt.x() - x_offset, pt.y() - rect.y()); + + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::channelAt() pt x:%d y:%d rect x:%d y:%d w:%d h:%d view_offset:%d x_offset:%d col w:%d header w:%d view w:%d p x:%d y:%d\n", + pt.x(), pt.y(), rect.x(), rect.y(), rect.width(), rect.height(), view_offset, x_offset, + rtw->columnWidth(col), rtw->header()->sectionSize(col), view_width, p.x(), p.y()); // REMOVE Tim. + + for(int i = 0; i < chans; ++i) + { + const RouteChannelsStruct& ch_struct = _channels.at(i); + const QRect& ch_rect = ch_struct._buttonRect; + if(ch_rect.contains(p)) + return i; + } + return -1; +} +/* + const int channels = _channels.size(); + //const QRect rect = visualItemRect(item); + QPoint p = pt - rect.topLeft(); + + int w = RouteDialog::channelDotsMargin * 2 + RouteDialog::channelDotDiameter * channels; + if(channels > 1) + w += RouteDialog::channelDotSpacing * (channels - 1); + if(channels > 4) + w += RouteDialog::channelDotGroupSpacing * (channels - 1) / 4; + + const int xoff =_isInput ? rect.width() - w : RouteDialog::channelDotsMargin; + const int yoff = RouteDialog::channelDotsMargin + (_isInput ? channels : 0); + p.setY(p.y() - yoff); + p.setX(p.x() - xoff); + if(p.y() < 0 || p.y() >= RouteDialog::channelDotDiameter) + return -1; + for(int i = 0; i < channels; ++i) + { + if(p.x() < 0) + return -1; + if(p.x() < RouteDialog::channelDotDiameter) + return i; + p.setX(p.x() - RouteDialog::channelDotDiameter - RouteDialog::channelDotSpacing); + if(i && ((i % 4) == 0)) + p.setX(p.x() - RouteDialog::channelDotGroupSpacing); + } + return -1; +}*/ + +// int RouteTreeWidgetItem::connectedChannels() const +// { +// int n = 0; +// //const int sz = _channelYValues.size(); +// const int sz = _channels.size(); +// for(int i = 0; i < sz; ++i) +// //if(_channelYValues.at(i) != -1) +// if(_channels.at(i)._connected) +// ++n; +// return n; +// } + +// int RouteTreeWidgetItem::channelsPerWidth(int w) const +// { +// if(type() == ChannelsItem) +// { +// if(w == -1) +// // w = treeWidget()->columnWidth(RouteDialog::ROUTE_NAME_COL); +// w = treeWidget()->viewport()->width(); +// int groups_per_col = (w - 2 * RouteDialog::channelDotsMargin) / +// (RouteDialog::channelDotGroupSpacing + RouteDialog::channelDotsPerGroup * (RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing)); +// if(groups_per_col < 1) +// groups_per_col = 1; +// +// return RouteDialog::channelDotsPerGroup * groups_per_col; +// } +// return 0; +// } + +// int RouteTreeWidgetItem::groupsPerChannels(int c) const +// { +// +// int groups = c / RouteDialog::channelDotsPerGroup; +// //if(groups < 1) +// // groups = 1; +// if(c % RouteDialog::channelDotsPerGroup) +// ++groups; +// return groups; +// } + +// int RouteTreeWidgetItem::barsPerColChannels(int cc) const +// { +// if(cc == 0) +// return 0; +// const int chans = _channels.size(); +// int bars = chans / cc; +// if(chans % cc) +// ++bars; +// //if(chan_rows < 1) +// // chan_rows = 1; +// return bars; +// } + + +void RouteTreeWidgetItem::computeChannelYValues(int col_width) +{ + //_channelYValues.resize(); + if(type() != ChannelsItem) + return; + //_channelYValues.fill(-1); + _channels.fillConnected(false); + switch(_route.type) + { + case MusECore::Route::TRACK_ROUTE: + if(_route.track) + { + //_channelYValues.fill(-1); + //_channels.fillConnected(false); + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + if(_isInput && _route.track->isMidiTrack()) + _channels.setConnected(static_cast(_route.track)->outChannel(), true); + else +#endif + { + const MusECore::RouteList* rl = _isInput ? _route.track->outRoutes() : _route.track->inRoutes(); + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + switch(ir->type) + { + case MusECore::Route::TRACK_ROUTE: + //if(ir->track && ir->channel != -1) + if(ir->channel != -1) + { + //if(ir->channel >= _channelYValues.size()) + //if(ir->channel >= _channels.size()) + //{ + //DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::computeChannelYValues() Error: iRoute channel:%d out of channels range:%d\n", ir->channel, _channelYValues.size()); + // DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::computeChannelYValues() Error: iRoute channel:%d out of channels range:%d\n", ir->channel, _channels.size()); + // break; + //} + // Mark the channel as used with a zero, for now. + //_channelYValues.replace(ir->channel, 0); + _channels.setConnected(ir->channel, true); + } + break; + + case MusECore::Route::MIDI_PORT_ROUTE: + if(ir->isValid() && ir->channel != -1) + { + // for(int i = 0; i < MIDI_CHANNELS; ++i) + // { + // if(ir->channel & (1 << i)) + // { + // // Mark the channel as used with a zero, for now. + // //_channelYValues.replace(i, 0); + // _channels.setConnected(i, true); + // } + // } + _channels.setConnected(ir->channel, true); + } + break; + + case MusECore::Route::JACK_ROUTE: + if(ir->channel != -1) + _channels.setConnected(ir->channel, true); + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; + } + } + } + } + break; + + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + + const int chans = _channels.size(); +// int w = RouteDialog::channelDotsMargin * 2 + RouteDialog::channelDotDiameter * chans; +// //int w = RouteDialog::channelDotsMargin * 2 + (RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing) * chans; +// if(chans > 1) +// w += RouteDialog::channelDotSpacing * (chans - 1); +// if(chans > RouteDialog::channelDotsPerGroup) +// w += RouteDialog::channelDotGroupSpacing * (chans - 1) / RouteDialog::channelDotsPerGroup; + + //const int col_width = treeWidget()->columnWidth(RouteDialog::ROUTE_NAME_COL); +// if(col_width == -1) +// col_width = treeWidget()->columnWidth(RouteDialog::ROUTE_NAME_COL); +// col_width = treeWidget()->viewport()->width(); + int chans_per_w = _channels.channelsPerWidth(col_width); + // Limit to actual number of channels available. + if(chans_per_w > chans) + chans_per_w = chans; + + //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint src list width:%d src viewport width:%d\n", router->newSrcList->width(), router->newSrcList->viewport()->width()); // REMOVE Tim. + //int x = _isInput ? router->newSrcList->viewport()->width() - w : RouteDialog::midiDotsMargin; + //int x = _isInput ? painter->device()->width() - w : RouteDialog::channelDotsMargin; + //const int x_orig = _isInput ? treeWidget()->width() - w : RouteDialog::channelDotsMargin; + const int x_orig = RouteDialog::channelDotsMargin; + int x = x_orig; + //int chan_y = RouteDialog::channelDotsMargin + (_isInput ? chans : 0); + int chan_y = 2 * RouteDialog::channelDotsMargin; + + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::computeChannelYValues() col_width:%d chans_per_w:%d\n", col_width, chans_per_w); // REMOVE Tim. + + int line_y = 2 * RouteDialog::channelLinesMargin + + (_isInput ? 0 : (RouteDialog::channelBarHeight + RouteDialog::channelDotsMargin + RouteDialog::channelLinesMargin)); + + //QList chan_ys; + + //// If it's a source, take care of the first batch of lines first, which are above the first channel bar. + //if(_isInput) + //{ +// for(int i = 0; i < chans; ++i) +// { +// const bool new_group = i && (i % chans_per_w == 0); +// // Is it marked as used? +// if(_channels.at(i)._connected) +// { +// // Set the value to an appropriate y value useful for drawing channel lines. +// _channels[i]._lineY = line_y; +// if(new_group) +// line_y += RouteDialog::channelBarHeight; +// else +// line_y += RouteDialog::channelLinesSpacing; +// } +// } + //} + + + int cur_chan = 0; + for(int i = 0; i < chans; ) + //for(int i = 0; i < chans; ++i) + { + //const bool new_group = i && (i % RouteDialog::channelDotsPerGroup == 0); + //const bool new_section = i && (i % chans_per_w == 0); + const bool is_connected = _channels.at(i)._connected; + + //if(new_section) + //{ + // chan_y = line_y + RouteDialog::channelDotsMargin; + //} + + // Is it marked as used? + //if(_channelYValues.at(i) != -1) + //if(_channels.at(i)._connected) + if(is_connected) + { + // Replace the zero value with an appropriate y value useful for drawing channel lines. + //_channelYValues.replace(i, y); + _channels[i]._lineY = line_y; + //if(new_section) + // line_y += RouteDialog::channelBarHeight; + //else + // line_y += RouteDialog::channelLinesSpacing; + } + +// if(_isInput) +// { +// // If we finished a section set button rects, or we reached the end +// // set the remaining button rects, based on current line y (and x). +// if(new_section || i + 1 == chans) +// { +// for( ; cur_chan < i; ++cur_chan) +// { +// _channels[cur_chan]._buttonRect = QRect(x, chan_y, RouteDialog::channelDotDiameter, RouteDialog::channelDotDiameter); +// } +// } +// +// } +// else +// { +// _channels[i]._buttonRect = QRect(x, chan_y, RouteDialog::channelDotDiameter, RouteDialog::channelDotDiameter); +// +// } + + if(!_isInput) + _channels[i]._buttonRect = QRect(x, chan_y, RouteDialog::channelDotDiameter, RouteDialog::channelDotDiameter); + + ++i; + const bool new_group = (i % RouteDialog::channelDotsPerGroup == 0); + const bool new_section = (i % chans_per_w == 0); + + if(is_connected) + line_y += RouteDialog::channelLineWidth + RouteDialog::channelLinesSpacing; + + if(_isInput) + { + // If we finished a section set button rects, or we reached the end + // set the remaining button rects, based on current line y (and x). + if(new_section || i == chans) + { + x = x_orig; + for( ; cur_chan < i; ) + { + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::computeChannelYValues() i:%d cur_chan:%d x:%d\n", i, cur_chan, x); // REMOVE Tim. + _channels[cur_chan]._buttonRect = QRect(x, line_y + RouteDialog::channelLinesMargin, RouteDialog::channelDotDiameter, RouteDialog::channelDotDiameter); + ++cur_chan; + x += RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing; + if(cur_chan % RouteDialog::channelDotsPerGroup == 0) + x += RouteDialog::channelDotGroupSpacing; + } + //line_y += RouteDialog::channelLinesMargin; + //++cur_chan; + } + } +// else +// { +// if(new_section) +// x = x_orig; // Reset +// else +// { +// x += RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing; +// if(new_group) +// x += RouteDialog::channelDotGroupSpacing; +// } +// } + + if(new_section) + { + x = x_orig; // Reset +// chan_y = line_y + RouteDialog::channelLinesMargin + RouteDialog::channelDotsMargin; +// chan_y = line_y + RouteDialog::channelLinesMargin; + chan_y = line_y; +// line_y += (RouteDialog::channelBarHeight + RouteDialog::channelLinesMargin); + line_y += RouteDialog::channelBarHeight; + } + else + { + x += RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing; + if(new_group) + x += RouteDialog::channelDotGroupSpacing; + } + } +} + +bool RouteTreeWidgetItem::mousePressHandler(QMouseEvent* e, const QRect& rect) +{ + const QPoint pt = e->pos(); + const Qt::KeyboardModifiers km = e->modifiers(); + bool ctl = false; + switch(_itemMode) + { + case ExclusiveMode: + ctl = false; + break; + case NormalMode: + ctl = km & Qt::ControlModifier; + break; + } + //bool shift = km & Qt::ShiftModifier; + +// RouteTreeWidgetItem* item = static_cast(itemAt(pt)); +// bool is_cur = item && currentItem() && (item == currentItem()); + + //if(is_cur) + // QTreeWidget::mousePressEvent(e); + + switch(type()) + { + case NormalItem: + case CategoryItem: + case RouteItem: + break; + case ChannelsItem: + switch(_route.type) + { + case MusECore::Route::TRACK_ROUTE: + if(_route.track && _route.channel != -1) // && item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).canConvert()) + { +// int chans; +// if(_route.track->isMidiTrack()) +// chans = MIDI_CHANNELS; +// else +// { +// MusECore::AudioTrack* atrack = static_cast(_route.track); +// if(atrack->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// chans = _isInput ? atrack->totalOutChannels() : atrack->totalInChannels(); +// else +// chans = atrack->channels(); +// } + + int ch = channelAt(pt, rect); + + //QBitArray ba = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); + //QBitArray ba_m = ba; + //QBitArray ba_m = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); + //const int ba_sz = ba_m.size(); + const int ba_sz = _channels.size(); + bool changed = false; + //if(!ctl) + { + //ba_m.fill(false); + for(int i = 0; i < ba_sz; ++i) + { + //const bool b = ba_m.testBit(i); + + if(i == ch) + { + if(ctl) + { + //_channels.toggleBit(i); + _channels[i].toggleSelected(); + changed = true; + } + else + { + //if(!_channels.testBit(i)) + if(!_channels.at(i)._selected) + changed = true; + //_channels.setBit(i); + _channels[i]._selected = true; + } + } + else if(!ctl) + { + //if(_channels.testBit(i)) + if(_channels. at(i)._selected) + changed = true; + //_channels.clearBit(i); + _channels[i]._selected = false; + } + + // //if(ba_m.testBit(i)) + // { + // ba_m.clearBit(i); + // changed = true; + // } + } + } + // //clearChannels(); + // // clearSelection(); + // //int ch = channelAt(item, pt, chans); + // if(ch != -1 && ch < ba_sz) + // { + // ba_m.toggleBit(ch); + // changed = true; + // } + + //if(is_cur) + // QTreeWidget::mousePressEvent(e); + + //if(ba_m != ba) +// if(changed) +// { +// item->setData(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole, qVariantFromValue(ba_m)); +// //setCurrentItem(item); +// update(visualItemRect(item)); +// //emit itemSelectionChanged(); +// } + +// //if(!is_cur) +// QTreeWidget::mousePressEvent(e); + +// if(changed && is_cur) +// //setCurrentItem(item); +// emit itemSelectionChanged(); + + //e->accept(); +// return; + return changed; + } + break; + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + + break; + } + + return false; + +// QTreeWidget::mousePressEvent(e); +} + +bool RouteTreeWidgetItem::mouseMoveHandler(QMouseEvent* e, const QRect& rect) +{ + const Qt::MouseButtons mb = e->buttons(); + if(mb != Qt::LeftButton) + return false; + + const QPoint pt = e->pos(); + const Qt::KeyboardModifiers km = e->modifiers(); + + bool ctl = false; + switch(_itemMode) + { + case ExclusiveMode: + ctl = false; + break; + case NormalMode: + //ctl = true; + //ctl = km & Qt::ControlModifier; + ctl = km & Qt::ShiftModifier; + break; + } + //bool shift = km & Qt::ShiftModifier; + +// RouteTreeWidgetItem* item = static_cast(itemAt(pt)); +// bool is_cur = item && currentItem() && (item == currentItem()); + + //if(is_cur) + // QTreeWidget::mousePressEvent(e); + + switch(type()) + { + case NormalItem: + case CategoryItem: + case RouteItem: + break; + case ChannelsItem: + switch(_route.type) + { + case MusECore::Route::TRACK_ROUTE: + if(_route.track && _route.channel != -1) // && item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).canConvert()) + { + int ch = channelAt(pt, rect); + + const int ba_sz = _channels.size(); + bool changed = false; + for(int i = 0; i < ba_sz; ++i) + { + if(i == ch) + { +// if(ctl) +// { +// _channels[i].toggleSelected(); +// changed = true; +// } +// else + { + if(!_channels.at(i)._selected) + changed = true; + _channels[i]._selected = true; + } + } + else if(!ctl) + { + if(_channels. at(i)._selected) + changed = true; + _channels[i]._selected = false; + } + } + return changed; + } + break; + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + + break; + } + + return false; + +// QTreeWidget::mousePressEvent(e); +} + +// bool RouteTreeWidgetItem::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +// { +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint\n"); // REMOVE Tim. +// // RouteDialog* router = qobject_cast< RouteDialog* >(parent()); +// //if(parent() && qobject_cast< RouteDialog* >(parent())) +// // if(router) +// // { +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint parent is RouteDialog\n"); // REMOVE Tim. +// //QWidget* qpd = qobject_cast(painter->device()); +// //if(qpd) +// if(painter->device()) +// { +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint device is QWidget\n"); // REMOVE Tim. +// //RouteDialog* router = static_cast(parent()); +// +// // if(index.column() == RouteDialog::ROUTE_NAME_COL && index.data(RouteDialog::RouteRole).canConvert()) +// if(type() == ChannelsItem && index.column() == RouteDialog::ROUTE_NAME_COL) +// { +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint data is Route\n"); // REMOVE Tim. +// // MusECore::Route r = qvariant_cast(index.data(RouteDialog::RouteRole)); +// QRect rect(option.rect); +// // switch(r.type) +// switch(_route.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint route is track\n"); // REMOVE Tim. +// if(_route.track && _route.channel != -1) +// { +// const int chans = _channels.size(); +// // int chans; +// // if(_route.track->isMidiTrack()) +// // { +// // //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint track is midi\n"); // REMOVE Tim. +// // chans = MIDI_CHANNELS; +// // } +// // else +// // { +// // //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint track is audio\n"); // REMOVE Tim. +// // MusECore::AudioTrack* atrack = static_cast(_route.track); +// // if(atrack->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// // { +// // if(_isInput) +// // chans = atrack->totalOutChannels(); +// // else +// // chans = atrack->totalInChannels(); +// // } +// // else +// // chans = atrack->channels(); +// // } +// +// int w = RouteDialog::channelDotsMargin * 2 + RouteDialog::channelDotDiameter * chans; +// if(chans > 1) +// w += RouteDialog::channelDotSpacing * (chans - 1); +// if(chans > 4) +// w += RouteDialog::channelDotGroupSpacing * (chans - 1) / 4; +// +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint src list width:%d src viewport width:%d\n", router->newSrcList->width(), router->newSrcList->viewport()->width()); // REMOVE Tim. +// //int x = _isInput ? router->newSrcList->viewport()->width() - w : RouteDialog::midiDotsMargin; +// int x = _isInput ? painter->device()->width() - w : RouteDialog::channelDotsMargin; +// const int y = RouteDialog::channelDotsMargin + (_isInput ? chans : 0); +// +// // QBitArray ba; +// // int basize = 0; +// // if(index.data(RouteDialog::ChannelsRole).canConvert()) +// // { +// // ba = index.data(RouteDialog::ChannelsRole).value(); +// // basize = ba.size(); +// // } +// +// //const int y_sz = _channelYValues.size(); +// //const int y_sz = _channels.size(); +// const int connected_chans = connectedChannels(); +// int cur_chan_line = 0; +// for(int i = 0; i < chans; ) +// { +// painter->setPen(Qt::black); +// //painter->drawRoundedRect(option.rect.x() + x, option.rect.y() + y, +// // if(!ba.isNull() && i < basize && ba.testBit(i)) +// //if(!_channels.isNull() && _channels.testBit(i)) +// if(_channels.at(i)._selected) +// painter->fillRect(x, option.rect.y() + y, +// RouteDialog::channelDotDiameter, RouteDialog::channelDotDiameter, +// option.palette.highlight()); +// //else +// painter->drawRoundedRect(x, option.rect.y() + y, +// RouteDialog::channelDotDiameter, RouteDialog::channelDotDiameter, +// 30, 30); +// if((i % 2) == 0) +// painter->setPen(Qt::darkGray); +// else +// painter->setPen(Qt::black); +// const int xline = x + RouteDialog::channelDotDiameter / 2; +// //if(i < y_sz) +// if(_channels.at(i)._connected) +// { +// //const int chan_y = _channelYValues.at(i); +// const int chan_y = _channels.at(i)._lineY; +// // -1 means not connected. +// //if(chan_y != -1) +// //{ +// if(_isInput) +// { +// //const int yline = option.rect.y() + y; +// //painter->drawLine(xline, yline, xline, yline - chans + i); +// //painter->drawLine(xline, yline - chans + i, painter->device()->width(), yline - chans + i); +// const int yline = option.rect.y() + chan_y; +// painter->drawLine(xline, yline, xline, yline - connected_chans + cur_chan_line); +// painter->drawLine(xline, yline - connected_chans + cur_chan_line, painter->device()->width(), yline - connected_chans + cur_chan_line); +// } +// else +// { +// //const int yline = option.rect.y() + RouteDialog::midiDotsMargin + RouteDialog::midiDotDiameter; +// //painter->drawLine(xline, yline, xline, yline + i); +// //painter->drawLine(0, yline + i, xline, yline + i); +// const int yline = option.rect.y() + RouteDialog::channelDotsMargin + RouteDialog::channelDotDiameter; +// painter->drawLine(xline, yline, xline, yline + chan_y); +// painter->drawLine(0, yline + chan_y, xline, yline + chan_y); +// } +// ++cur_chan_line; +// //} +// } +// +// ++i; +// x += RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing; +// if(i && ((i % 4) == 0)) +// x += RouteDialog::channelDotGroupSpacing; +// } +// return true; +// } +// break; +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// case MusECore::Route::JACK_ROUTE: +// break; +// } +// } +// } +// // } +// // QStyledItemDelegate::paint(painter, option, index); +// return false; +// } + +bool RouteTreeWidgetItem::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + { + if(index.column() == RouteDialog::ROUTE_NAME_COL) + { + RouteTreeWidget* rtw = qobject_cast(treeWidget()); + if(!rtw) + return false; + + switch(type()) + { + case ChannelsItem: + { + if(!treeWidget()->viewport()) + return false; + + const int col_width = rtw->columnWidth(index.column()); + const int view_width = rtw->viewport()->width(); + const int chans = _channels.size(); + const int view_offset = rtw->header()->offset(); + //const int x_offset = (_isInput ? view_width - getSizeHint(index.column(), col_width).width() + view_offset : -view_offset); + //const int x_offset = (_isInput ? view_width - getSizeHint(index.column(), col_width).width() - view_offset : -view_offset); + // const int x_offset = (_isInput ? view_width - getSizeHint(index.column(), view_width).width() - view_offset : -view_offset); + const int x_offset = (_isInput ? + //view_width - _channels.widthHint(rtw->wordWrap() ? view_width : -1) - view_offset : -view_offset); + col_width - _channels.widthHint(rtw->channelWrap() ? col_width : -1) - view_offset : -view_offset); + + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::paint() rect x:%d y:%d w:%d h:%d view_offset:%d x_offset:%d dev w:%d col w:%d header w:%d view w:%d\n", + option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height(), view_offset, x_offset, painter->device()->width(), + rtw->columnWidth(index.column()), rtw->header()->sectionSize(index.column()), view_width); // REMOVE Tim. + + + // From QStyledItemDelegate::paint help: Necessary? + // "After painting, you should ensure that the painter is returned to its the state it was supplied in when this function + // was called. For example, it may be useful to call QPainter::save() before painting and QPainter::restore() afterwards." + painter->save(); + + // Need to be able to paint beyond the right edge of the column width, + // all the way to the view's right edge. + //painter->setClipRect(option.rect); + QRect clip_rect(option.rect); + clip_rect.setWidth(view_width - option.rect.x()); + painter->setClipRect(clip_rect); + + if(index.parent().isValid() && (index.parent().row() & 0x01)) + painter->fillRect(option.rect, option.palette.alternateBase()); + int cur_chan = 0; + //QColor color; +// QBrush brush; + QPen pen; + + // Set a small five-pixel font size for the numbers inside the dots. + QFont fnt = font(index.column()); + //fnt.setStyleStrategy(QFont::NoAntialias); + //fnt.setStyleStrategy(QFont::PreferBitmap); + // -2 for the border, -2 for the margin, and +1 for setPixelSize which seems to like it. + //fnt.setPixelSize(RouteDialog::channelDotDiameter - 2 - 2 + 1); + fnt.setPixelSize(RouteDialog::channelDotDiameter / 2 + 1); + painter->setFont(fnt); + for(int i = 0; i < chans; ++i) + { + const RouteChannelsStruct& ch_struct = _channels.at(i); + const QRect& ch_rect = ch_struct._buttonRect; + + QPainterPath path; + path.addRoundedRect(x_offset + ch_rect.x(), option.rect.y() + ch_rect.y(), + ch_rect.width(), ch_rect.height(), + 30, 30); + if(ch_struct._selected) + painter->fillPath(path, option.palette.highlight()); + //painter->setPen(ch_struct._selected ? option.palette.highlightedText().color() : option.palette.text().color()); + //painter->setPen(ch_struct._routeSelected ? Qt::yellow : option.palette.text().color()); + painter->setPen(option.palette.text().color()); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->drawPath(path); + + //const int ch_num = (i + 1) % 10; + if(chans > RouteDialog::channelDotsPerGroup) + { + //if((i % RouteDialog::channelDotsPerGroup) == 0 || ((i + 1) % 10 == 0)) + if((i % RouteDialog::channelDotsPerGroup) == 0) + { + painter->setPen(ch_struct._selected ? option.palette.highlightedText().color() : option.palette.text().color()); + painter->drawText(x_offset + ch_rect.x(), option.rect.y() + ch_rect.y(), + ch_rect.width(), ch_rect.height(), + Qt::AlignCenter, + //QString::number((ch_num + 1) / 10)); + QString::number(i + 1)); + } + } + + if(ch_struct._connected) + { +// if((cur_chan % 2) == 0) +// painter->setPen(option.palette.text().color().lighter()); +// else +// painter->setPen(option.palette.text().color()); + //painter->setPen(ch_struct._selected ? option.palette.highlight().color() : option.palette.text().color()); + +// if(ch_struct._routeSelected) +// brush = Qt::yellow; +// //brush = Qt::red; +// //brush = QColor(0, 255, 255); +// //brush = option.palette.highlight(); +// else if(ch_struct._selected) +// brush = option.palette.highlight(); +// else +// brush = option.palette.text(); + + // painter->setPen(ch_struct._selected ? option.palette.highlight().color() : option.palette.text().color()); +// painter->setPen(color); + + // Need sharp lines here. + painter->setRenderHint(QPainter::Antialiasing, false); + + const int line_x = x_offset + ch_rect.x() + RouteDialog::channelDotDiameter / 2; + const int line_y = option.rect.y() + ch_struct._lineY; + if(_isInput) + { + const int ch_y = option.rect.y() + ch_rect.y() -1; + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::paint() input: line_x:%d ch_y:%d line_y:%d view_w:%d\n", line_x, ch_y, line_y, view_width); // REMOVE Tim. + pen.setBrush((ch_struct._selected && !ch_struct._routeSelected) ? option.palette.highlight() : option.palette.text()); + pen.setStyle(Qt::SolidLine); + painter->setPen(pen); + painter->drawLine(line_x, ch_y, line_x, line_y); + painter->drawLine(line_x, line_y, view_width, line_y); + if(ch_struct._routeSelected) + { + pen.setBrush(Qt::yellow); + pen.setStyle(Qt::DotLine); + painter->setPen(pen); + painter->drawLine(line_x, ch_y, line_x, line_y); + painter->drawLine(line_x, line_y, view_width, line_y); + } + } + else + { + const int ch_y = option.rect.y() + ch_rect.y() + ch_rect.height(); + pen.setBrush((ch_struct._selected && !ch_struct._routeSelected) ? option.palette.highlight() : option.palette.text()); + pen.setStyle(Qt::SolidLine); + painter->setPen(pen); + painter->drawLine(line_x, ch_y, line_x, line_y); + painter->drawLine(x_offset, line_y, line_x, line_y); + if(ch_struct._routeSelected) + { + pen.setBrush(Qt::yellow); + pen.setStyle(Qt::DotLine); + painter->setPen(pen); + painter->drawLine(line_x, ch_y, line_x, line_y); + painter->drawLine(x_offset, line_y, line_x, line_y); + } + } + ++cur_chan; + } + } + painter->restore(); + return true; + } + break; + + case CategoryItem: + case RouteItem: + { + if(const QStyle* st = rtw->style()) + { + st = st->proxy(); + painter->save(); + painter->setClipRect(option.rect); + + const QRect cb_rect = st->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option); + const QRect ico_rect = st->subElementRect(QStyle::SE_ItemViewItemDecoration, &option); + const QRect text_rect = st->subElementRect(QStyle::SE_ItemViewItemText, &option); + + // Draw the row background (alternating colours etc.) + QPalette::ColorGroup cg = (/* widget ? widget->isEnabled() : */ (option.state & QStyle::State_Enabled)) ? + QPalette::Normal : QPalette::Disabled; + if(cg == QPalette::Normal && !(option.state & QStyle::State_Active)) + cg = QPalette::Inactive; + if((option.state & QStyle::State_Selected) && st->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, &option /*, widget*/)) + painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Highlight)); + //else if(option.features & QStyleOptionViewItem::Alternate) + // Hm, something else draws the alternating colours, no control over it here. + // Disabled it in the UI so it does not interfere here. + //else if(treeWidget()->alternatingRowColors() && (index.row() & 0x01)) + else if((index.row() & 0x01)) + painter->fillRect(option.rect, option.palette.brush(cg, QPalette::AlternateBase)); + + // Draw the item background. + st->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter); + + // Draw the check mark + if(option.features & QStyleOptionViewItem::HasCheckIndicator) + { + QStyleOptionViewItem opt(option); + opt.rect = cb_rect; + opt.state = opt.state & ~QStyle::State_HasFocus; + switch(option.checkState) + { + case Qt::Unchecked: + opt.state |= QStyle::State_Off; + break; + case Qt::PartiallyChecked: + opt.state |= QStyle::State_NoChange; + break; + case Qt::Checked: + opt.state |= QStyle::State_On; + break; + } + st->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &opt, painter); + } + + // Draw the icon. + QIcon::Mode mode = QIcon::Normal; + if(!(option.state & QStyle::State_Enabled)) + mode = QIcon::Disabled; + else if(option.state & QStyle::State_Selected) + mode = QIcon::Selected; + QIcon::State state = option.state & QStyle::State_Open ? QIcon::On : QIcon::Off; + option.icon.paint(painter, ico_rect, option.decorationAlignment, mode, state); + + // Draw the text. + st->drawItemText(painter, + text_rect, + //textAlignment(index.column()) | Qt::TextWordWrap | Qt::TextWrapAnywhere, + option.displayAlignment | (rtw->wordWrap() ? (Qt::TextWordWrap | Qt::TextWrapAnywhere) : 0), + //treeWidget()->palette(), + option.palette, + //!isDisabled(), + option.state & QStyle::State_Enabled, + //text(index.column()), + rtw->wordWrap() ? + option.text : option.fontMetrics.elidedText(option.text, rtw->textElideMode(), text_rect.width()), + //isSelected() ? QPalette::HighlightedText : QPalette::Text + (option.state & QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::Text + ); + + // Draw the focus. + if(option.state & QStyle::State_HasFocus) + { + QStyleOptionFocusRect o; + o.QStyleOption::operator=(option); + o.rect = st->subElementRect(QStyle::SE_ItemViewItemFocusRect, &option); + o.state |= QStyle::State_KeyboardFocusChange; + o.state |= QStyle::State_Item; + QPalette::ColorGroup cg = + (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; + o.backgroundColor = option.palette.color(cg, + (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); + st->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter); + } + + painter->restore(); + return true; + } + } + break; + + case NormalItem: + break; + } + } + } + return false; +} + +// QSize RouteTreeWidgetItem::getSizeHint(int col, int col_width) const +// { +// DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::getSizeHint width:%d view width:%d col:%d column width:%d\n", +// treeWidget()->viewport()->width(), treeWidget()->width(), col, treeWidget()->columnWidth(RouteDialog::ROUTE_NAME_COL)); // REMOVE Tim. +// +// if(col_width == -1) +// col_width = treeWidget()->viewport()->width(); +// +// if(col == RouteDialog::ROUTE_NAME_COL) +// { +// switch(type()) +// { +// case ChannelsItem: +// return _channels.sizeHint(col_width); +// break; +// +// case NormalItem: +// break; +// +// case CategoryItem: +// case RouteItem: +// { +// QStyle* st = treeWidget()->style(); +// if(st) +// { +// // Qt sources show itemTextRect() just calls QFontMetrics::boundingRect and supports enabled. +// // And Qt::TextWrapAnywhere is not listed as supported in boundingRect() help, yet it is in drawItemText(). +// // A look through the Qt sources shows it IS supported. Try it... +// QRect r = st->itemTextRect(treeWidget()->fontMetrics(), QRect(0, 0, col_width, 32767), +// textAlignment(RouteDialog::ROUTE_NAME_COL) | Qt::TextWordWrap | Qt::TextWrapAnywhere, +// !isDisabled(), text(RouteDialog::ROUTE_NAME_COL)); +// return r.size(); +// } +// } +// break; +// } +// +// // switch(_route.type) +// // { +// // case MusECore::Route::TRACK_ROUTE: +// // if(_route.track && _route.channel != -1) +// // { +// // //int chans; +// // const int chans = _channels.size(); +// // // if(_route.track->isMidiTrack()) +// // // chans = MIDI_CHANNELS; +// // // else +// // // { +// // // MusECore::AudioTrack* atrack = static_cast(_route.track); +// // // if(atrack->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// // // { +// // // if(_isInput) +// // // chans = atrack->totalOutChannels(); +// // // else +// // // chans = atrack->totalInChannels(); +// // // } +// // // else +// // // chans = atrack->channels(); +// // // } +// // +// // // int w = RouteDialog::midiDotsMargin * 2 + RouteDialog::midiDotDiameter * chans; +// // // if(chans > 1) +// // // w += RouteDialog::midiDotSpacing * (chans - 1); +// // // if(chans > RouteDialog::midiDotsPerGroup) +// // // w += RouteDialog::midiDotGroupSpacing * (chans - 1) / RouteDialog::midiDotsPerGroup; +// // int w = col_width; +// // const int h = RouteDialog::midiDotDiameter + RouteDialog::midiDotsMargin * 2 + chans; +// // return QSize(w, h); +// // } +// // break; +// // case MusECore::Route::MIDI_DEVICE_ROUTE: +// // case MusECore::Route::MIDI_PORT_ROUTE: +// // case MusECore::Route::JACK_ROUTE: +// // break; +// // } +// } +// +// //return QStyledItemDelegate::sizeHint(option, index); +// return QSize(); +// //return sizeHint(col); +// } + +//QSize RouteTreeWidgetItem::getSizeHint(const QStyleOptionViewItem& option, const QModelIndex &index) const +QSize RouteTreeWidgetItem::getSizeHint(int column, int width) const +{ +// if (index.data().canConvert()) { +// StarRating starRating = qvariant_cast(index.data()); +// return starRating.sizeHint(); +// } else + +// if(index.column() == ControlMapperDialog::C_COLOR) +// return QSize(__COLOR_CHOOSER_ELEMENT_WIDTH__ * __COLOR_CHOOSER_NUM_COLUMNS__, +// __COLOR_CHOOSER_ELEMENT_HEIGHT__ * (__COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__)); +// + //return QStyledItemDelegate::sizeHint(option, index); + +// const QSize sz = getSizeHint(index.column()); +// DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::sizeHint opt rect x:%d y:%d w:%d h:%d hint w:%d h:%d column width:%d\n", +// option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height(), +// sz.width(), sz.height(), +// treeWidget()->columnWidth(RouteDialog::ROUTE_NAME_COL)); // REMOVE Tim. +// return sz; + + +// return getSizeHint(index.column()); + + +// const int col = index.column(); +// int width = option.rect.width(); + + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::getSizeHint width:%d col:%d column width:%d\n", + treeWidget()->width(), column, treeWidget()->columnWidth(column)); // REMOVE Tim. + +// if(width <= 0) +// width = treeWidget()->viewport()->width(); +// if(width <= 0) +// width = treeWidget()->columnWidth(column); + + if(column == RouteDialog::ROUTE_NAME_COL) + { + RouteTreeWidget* rtw = qobject_cast(treeWidget()); + if(!rtw) + return QSize(); + + switch(type()) + { + case NormalItem: + break; + + case ChannelsItem: + //fprintf(stderr, "RouteTreeWidgetItem::getSizeHint ChannelsItem w:%d\n", width); // REMOVE Tim. + return _channels.sizeHint(rtw->channelWrap() ? width : -1); + break; + + case CategoryItem: + case RouteItem: + { + if(!rtw->wordWrap()) + return QSize(); + + if(const QStyle* st = rtw->style()) + { + st = st->proxy(); + QStyleOptionViewItem vopt; + vopt.features = QStyleOptionViewItem::None; + + vopt.text = text(column); + vopt.rect = QRect(0, 0, rtw->wordWrap() ? width : _VERY_LARGE_INTEGER_, -1); + vopt.displayAlignment = Qt::Alignment(textAlignment(column)); + + if(icon(column).isNull()) + vopt.decorationSize = QSize(); + else + { + vopt.features |= QStyleOptionViewItem::HasDecoration; + vopt.decorationSize = rtw->iconSize(); + vopt.icon = icon(column); + } + + if(rtw->wordWrap()) + vopt.features |= QStyleOptionViewItem::WrapText; + vopt.features |= QStyleOptionViewItem::HasDisplay; + + vopt.font = font(column); + vopt.fontMetrics = rtw->fontMetrics(); + + vopt.state = QStyle::State_Active; + if(!isDisabled()) + vopt.state |= QStyle::State_Enabled; + if(flags() & Qt::ItemIsUserCheckable) + { + vopt.features |= QStyleOptionViewItem::HasCheckIndicator; + vopt.checkState = checkState(column); + if(checkState(column) == Qt::Unchecked) + vopt.state |= QStyle::State_Off; + else if(checkState(column) == Qt::Checked) + vopt.state |= QStyle::State_On; + } + + if(isSelected()) + vopt.state |= QStyle::State_Selected; + + QSize ct_sz = st->sizeFromContents(QStyle::CT_ItemViewItem, &vopt, QSize(rtw->wordWrap() ? width : _VERY_LARGE_INTEGER_, -1)); + const QRect text_rect = st->subElementRect(QStyle::SE_ItemViewItemText, &vopt); + QRect r = st->itemTextRect(//treeWidget()->fontMetrics(), + vopt.fontMetrics, + text_rect, + //textAlignment(column) | Qt::TextWordWrap | Qt::TextWrapAnywhere, + vopt.displayAlignment | Qt::TextWordWrap | Qt::TextWrapAnywhere, + //!isDisabled(), + vopt.state & QStyle::State_Enabled, + //text(column)); + vopt.text); + if(r.height() > ct_sz.height()) + ct_sz.setHeight(r.height()); + + return ct_sz; + } + } + break; + } + } + + return QSize(); +} + +// //if(index.column() == RouteDialog::ROUTE_NAME_COL && index.data(RouteDialog::RouteRole).canConvert()) +// if(type() == ChannelsItem && index.column() == RouteDialog::ROUTE_NAME_COL) +// { +// switch(_route.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// if(_route.track && _route.channel != -1) +// { +// //int chans; +// const int chans = _channels.size(); +// // if(_route.track->isMidiTrack()) +// // chans = MIDI_CHANNELS; +// // else +// // { +// // MusECore::AudioTrack* atrack = static_cast(_route.track); +// // if(atrack->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// // { +// // if(_isInput) +// // chans = atrack->totalOutChannels(); +// // else +// // chans = atrack->totalInChannels(); +// // } +// // else +// // chans = atrack->channels(); +// // } +// int w = RouteDialog::midiDotsMargin * 2 + RouteDialog::midiDotDiameter * chans; +// if(chans > 1) +// w += RouteDialog::midiDotSpacing * (chans - 1); +// if(chans > 4) +// w += RouteDialog::midiDotGroupSpacing * (chans - 1) / 4; +// const int h = RouteDialog::midiDotDiameter + RouteDialog::midiDotsMargin * 2 + chans; +// return QSize(w, h); +// } +// break; +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// case MusECore::Route::JACK_ROUTE: +// break; +// } +// } +// //return QStyledItemDelegate::sizeHint(option, index); +// //return QSize(); +// return sizeHint(index.column()); +// } + +// void RouteTreeWidgetItem::columnSizeChanged(int logicalIndex, int oldSize, int newSize) +// { +// DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::columnSizeChanged idx:%d old sz:%d new sz:%d\n", logicalIndex, oldSize, newSize); +// if(type() == ChannelsItem && logicalIndex == RouteDialog::ROUTE_NAME_COL) +// { +// //setSizeHint(logicalIndex, getSizeHint(logicalIndex)); +// } +// } + +//bool RouteTreeWidgetItem::testForRelayout(const QStyleOptionViewItem& /*option*/, const QModelIndex& index, int old_width, int new_width) const +bool RouteTreeWidgetItem::testForRelayout(int column, int old_width, int new_width) +{ + switch(type()) + { + case NormalItem: + break; + + case CategoryItem: + case RouteItem: + { + //if(index.column() == RouteDialog::ROUTE_NAME_COL) + if(column == RouteDialog::ROUTE_NAME_COL) + { +// if(const QStyle* st = treeWidget()->style()) +// { +// st = st->proxy(); +// // Works fine with TextWrapAnywhere. The -1 represents 'infinite' vertical space - +// // itemTextRect doesn't seem to care in this case with wrap anywhere. +// QRect old_r = st->itemTextRect(treeWidget()->fontMetrics(), +// QRect(0, 0, old_width, -1), +// textAlignment(RouteDialog::ROUTE_NAME_COL) | Qt::TextWordWrap | Qt::TextWrapAnywhere, +// !isDisabled(), text(RouteDialog::ROUTE_NAME_COL)); +// QRect new_r = st->itemTextRect(treeWidget()->fontMetrics(), +// QRect(0, 0, new_width, -1), +// textAlignment(RouteDialog::ROUTE_NAME_COL) | Qt::TextWordWrap | Qt::TextWrapAnywhere, +// !isDisabled(), text(RouteDialog::ROUTE_NAME_COL)); +// return new_r.height() != old_r.height(); +// } +// return new_sz.height() != old_sz.height(); + + //if(MusEGlobal::config.routerExpandVertically) + if(!treeWidget()->wordWrap()) + return false; + + return getSizeHint(column, new_width).height() != getSizeHint(column, old_width).height(); + } + } + break; + + case ChannelsItem: + { + //if(index.column() == RouteDialog::ROUTE_NAME_COL) + if(column == RouteDialog::ROUTE_NAME_COL) + { +// // If the width hints are different we must (at least) update the channels' button rectangles. +// if(_channels.widthHint(new_width) != _channels.widthHint(old_width)) +// computeChannelYValues(new_width); +// // If the height hints are different we must trigger a relayout. +// return _channels.heightHint(new_width) != _channels.heightHint(old_width); + + RouteTreeWidget* rtw = qobject_cast(treeWidget()); + if(!rtw) + return false; + + if(!rtw->channelWrap()) + return false; + + const QSize old_sz = getSizeHint(column, old_width); + const QSize new_sz = getSizeHint(column, new_width); + // If the width hints are different we must (at least) update the channels' button rectangles. + if(new_sz.width() != old_sz.width()) + computeChannelYValues(new_width); + // If the height hints are different we must trigger a relayout. + return new_sz.height() != old_sz.height(); + } + } + break; + } + return false; +} + +// // if(type() == ChannelsItem && col == RouteDialog::ROUTE_NAME_COL) +// if(type() == ChannelsItem && index.column() == RouteDialog::ROUTE_NAME_COL) +// { +// // const QSize old_sz = getSizeHint(col, old_width); +// // const QSize new_sz = getSizeHint(col, new_width); +// +// const QSize old_sz = getSizeHint(option, index); +// const QSize new_sz = getSizeHint(option, index); +// +// +// //return old_sz.isValid() && new_sz.isValid() && old_sz.height() != new_sz.height(); +// //return old_sz.isValid() && new_sz.isValid() && old_sz != new_sz; +// return old_sz != new_sz; +// } +// return false; +// } + +bool RouteTreeWidgetItem::routeNodeExists() +{ + switch(type()) + { + case CategoryItem: + case NormalItem: + return true; + break; + + case RouteItem: + case ChannelsItem: + return _route.exists(); + break; + } + return false; +} + + +//----------------------------------- +// ConnectionsView +//----------------------------------- + +ConnectionsView::ConnectionsView(QWidget* parent, RouteDialog* d) + : QFrame(parent), _routeDialog(d) +{ + lastY = 0; + setMinimumWidth(20); + //setMaximumWidth(120); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); +} + +ConnectionsView::~ConnectionsView() +{ +} + +int ConnectionsView::itemY(RouteTreeWidgetItem* item, bool /*is_input*/, int channel) const +{ +// if(item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// const MusECore::Route r = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::RouteRole).value(); +// } + + + QRect rect; + QTreeWidget* tree = item->treeWidget(); + //QTreeWidgetItem* parent = item->parent(); + + QTreeWidgetItem* top_closed = 0; + QTreeWidgetItem* parent = item; + while(parent) + { + parent = parent->parent(); + if(!parent) + break; + if(!parent->isExpanded()) + top_closed = parent; + } + + const int line_width = _routeDialog->newSrcList->lineWidth(); + + //if(parent && !parent->isExpanded()) + if(top_closed) + { + rect = tree->visualItemRect(top_closed); + return line_width + rect.top() + rect.height() / 2; + } +// else +// { + rect = tree->visualItemRect(item); + if(channel != -1) + return line_width + rect.top() + item->channelYValue(channel); + return line_width + rect.top() + rect.height() / 2; +// } + //DEBUG_PRST_ROUTES(stderr, "ConnectionsView::itemY: left:%d top:%d right:%d bottom:%d\n", rect.left(), rect.top(), rect.right(), rect.bottom()); +// if(channel != -1) +// //return rect.top() + RouteDialog::channelDotsMargin + (is_input ? 0 : RouteDialog::channelDotDiameter) + channel; +// return rect.top() + item->channelYValue(channel); +// +// return rect.top() + rect.height() / 2; +} + + +void ConnectionsView::drawConnectionLine(QPainter* pPainter, + int x1, int y1, int x2, int y2, int h1, int h2 ) +{ + //DEBUG_PRST_ROUTES(stderr, "ConnectionsView::drawConnectionLine: x1:%d y1:%d x2:%d y2:%d h1:%d h2:%d\n", x1, y1, x2, y2, h1, h2); + + // Account for list view headers. + y1 += h1; + y2 += h2; + + // Invisible output ports don't get a connecting dot. + if(y1 > h1) + pPainter->drawLine(x1, y1, x1 + 4, y1); + + // How do we'll draw it? // TODO + if(1) + { + // Setup control points + QPolygon spline(4); + const int cp = int(float(x2 - x1 - 8) * 0.4f); + spline.putPoints(0, 4, + x1 + 4, y1, x1 + 4 + cp, y1, + x2 - 4 - cp, y2, x2 - 4, y2); + // The connection line, it self. + QPainterPath path; + path.moveTo(spline.at(0)); + path.cubicTo(spline.at(1), spline.at(2), spline.at(3)); + pPainter->strokePath(path, pPainter->pen()); + } + else + pPainter->drawLine(x1 + 4, y1, x2 - 4, y2); + + // Invisible input ports don't get a connecting dot. + if(y2 > h2) + pPainter->drawLine(x2 - 4, y2, x2, y2); +} + +void ConnectionsView::drawItem(QPainter* painter, QTreeWidgetItem* routesItem, const QColor& col) +{ + const int yc = QWidget::pos().y(); + const int yo = _routeDialog->newSrcList->pos().y(); + const int yi = _routeDialog->newDstList->pos().y(); + const int x1 = 0; + const int x2 = QWidget::width(); + const int h1 = (_routeDialog->newSrcList->header())->sizeHint().height(); + const int h2 = (_routeDialog->newDstList->header())->sizeHint().height(); + int y1; + int y2; + QPen pen; + const int pen_wid_norm = 0; + const int pen_wid_wide = 3; + + if(routesItem->data(RouteDialog::ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && + routesItem->data(RouteDialog::ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) + { + const MusECore::Route src = routesItem->data(RouteDialog::ROUTE_SRC_COL, RouteDialog::RouteRole).value(); + const MusECore::Route dst = routesItem->data(RouteDialog::ROUTE_DST_COL, RouteDialog::RouteRole).value(); + RouteTreeWidgetItem* srcItem = _routeDialog->newSrcList->findItem(src); + if(srcItem) + { + RouteTreeWidgetItem* dstItem = _routeDialog->newDstList->findItem(dst); + if(dstItem) + { + int src_chan = src.channel; + int dst_chan = dst.channel; + bool src_wid = false; + bool dst_wid = false; + switch(src.type) + { + case MusECore::Route::TRACK_ROUTE: + // Don't draw if channel is unavailable. + if(src_chan >= srcItem->channelCount()) + return; + if(src_chan == -1 && src.channels == -1) + src_wid = true; + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + if(src_chan == -1 && src.channels == -1) + src_wid = true; + // Support port/device items (no channel bar) to track channel item routes: + // Set source channel to -1 so it draws to the vertical middle of the item. + src_chan = -1; + break; + + case MusECore::Route::JACK_ROUTE: + break; + } + switch(dst.type) + { + case MusECore::Route::TRACK_ROUTE: + // Don't draw if channel is unavailable. + if(dst_chan >= dstItem->channelCount()) + return; + if(dst_chan == -1 && dst.channels == -1) + dst_wid = true; + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + if(dst_chan == -1 && dst.channels == -1) + dst_wid = true; + // Support track channel items to port/device items (no channel bar) routes: + // Set dest channel to -1 so it draws to the vertical middle of the item. + dst_chan = -1; + break; + + case MusECore::Route::JACK_ROUTE: + break; + } + + if(src_wid && dst_wid) + pen.setWidth(pen_wid_wide); + else + pen.setWidth(pen_wid_norm); + + pen.setColor(col); + painter->setPen(pen); + y1 = itemY(srcItem, true, src_chan) + (yo - yc); + y2 = itemY(dstItem, false, dst_chan) + (yi - yc); + drawConnectionLine(painter, x1, y1, x2, y2, h1, h2); + } +// else +// { +// fprintf(stderr, "ConnectionsView::drawItem: dstItem not found:\n"); +// src.dump(); +// dst.dump(); +// } + } +// else +// { +// fprintf(stderr, "ConnectionsView::drawItem: srcItem not found:\n"); +// src.dump(); +// dst.dump(); +// } + } +} + +// Draw visible port connection relation arrows. +void ConnectionsView::paintEvent(QPaintEvent*) +{ + //DEBUG_PRST_ROUTES(stderr, "ConnectionsView::paintEvent: _routeDialog:%p\n", _routeDialog); + if(!_routeDialog) + return; + + QPainter painter(this); +// int i, rgb[3] = { 0x33, 0x66, 0x99 }; + int i, rgb[3] = { 0x33, 0x58, 0x7f }; + //int i, rgb[3] = { 0x00, 0x2c, 0x7f }; + + // Inline adaptive to darker background themes... + if(QWidget::palette().window().color().value() < 0x7f) + for (i = 0; i < 3; ++i) + //rgb[i] += 0x33; + //rgb[i] += 0x66; + rgb[i] += 0x80; + + i = 0; +// const int x1 = 0; +// const int x2 = QWidget::width(); +// const int h1 = (_routeDialog->newSrcList->header())->sizeHint().height(); +// const int h2 = (_routeDialog->newDstList->header())->sizeHint().height(); +// QPen pen; +// const int pen_wid_norm = 0; +// const int pen_wid_wide = 3; +// +// QTreeWidgetItem* src_sel = 0; +// QTreeWidgetItem* dst_sel = 0; +// int src_sel_ch = -1; +// int dst_sel_ch = -1; +// bool sel_wid = false; + +// QTreeWidgetItem* cur_item = _routeDialog->routeList->currentItem(); + const int iItemCount = _routeDialog->routeList->topLevelItemCount(); + // Draw unselected items behind selected items. + for(int iItem = 0; iItem < iItemCount; ++iItem, ++i) + { + QTreeWidgetItem* item = _routeDialog->routeList->topLevelItem(iItem); + //++i; + //if(!item) + if(!item || item->isHidden() || item->isSelected()) + continue; + drawItem(&painter, item, QColor(rgb[i % 3], rgb[(i / 3) % 3], rgb[(i / 9) % 3], 128)); + } + // Draw selected items on top of unselected items. + for(int iItem = 0; iItem < iItemCount; ++iItem) + { + QTreeWidgetItem* item = _routeDialog->routeList->topLevelItem(iItem); + //if(!item) + if(!item || item->isHidden() || !item->isSelected()) + continue; + drawItem(&painter, item, Qt::yellow); + //drawItem(&painter, item, Qt::red); + //drawItem(&painter, item, QColor(0, 255, 255)); + //drawItem(&painter, item, palette().highlight().color()); + } + + +// const QColor col(rgb[i % 3], rgb[(i / 3) % 3], rgb[(i / 9) % 3]); +// if(item->data(RouteDialog::ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && item->data(RouteDialog::ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) +// { +// const MusECore::Route src = item->data(RouteDialog::ROUTE_SRC_COL, RouteDialog::RouteRole).value(); +// const MusECore::Route dst = item->data(RouteDialog::ROUTE_DST_COL, RouteDialog::RouteRole).value(); +// QTreeWidgetItem* srcItem = _routeDialog->newSrcList->findItem(src); +// if(srcItem) +// { +// QTreeWidgetItem* dstItem = _routeDialog->newDstList->findItem(dst); +// if(dstItem) +// { +// int src_chan = src.channel; +// int dst_chan = dst.channel; +// bool src_wid = false; +// bool dst_wid = false; +// switch(src.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// if(src_chan != -1 && src.track && src.track->isMidiTrack()) +// { +// for(int i = 0; i < MIDI_CHANNELS; ++i) +// if(src_chan & (1 << i)) +// { +// src_chan = i; +// break; +// } +// } // Fall through +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// if(src_chan == -1 && src.channels == -1) +// src_wid = true; +// break; +// case MusECore::Route::JACK_ROUTE: +// break; +// } +// switch(dst.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// if(dst_chan != -1 && dst.track && dst.track->isMidiTrack()) +// { +// for(int i = 0; i < MIDI_CHANNELS; ++i) +// if(dst_chan & (1 << i)) +// { +// dst_chan = i; +// break; +// } +// } // Fall through +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// if(dst_chan == -1 && dst.channels == -1) +// dst_wid = true; +// break; +// case MusECore::Route::JACK_ROUTE: +// break; +// } +// +// if(item == cur_item) +// { +// // Remember the selected items and draw that line last over top all else. +// src_sel = srcItem; +// dst_sel = dstItem; +// src_sel_ch = src_chan; +// dst_sel_ch = dst_chan; +// sel_wid = src_wid && dst_wid; +// continue; +// } +// +// if(src_wid && dst_wid) +// pen.setWidth(pen_wid_wide); +// else +// pen.setWidth(pen_wid_norm); +// +// pen.setColor(col); +// painter.setPen(pen); +// y1 = itemY(srcItem, true, src_chan) + (yo - yc); +// y2 = itemY(dstItem, false, dst_chan) + (yi - yc); +// drawConnectionLine(&painter, x1, y1, x2, y2, h1, h2); +// } +// else +// { +// DEBUG_PRST_ROUTES(stderr, "ConnectionsView::paintEvent: dstItem not found:\n"); +// src.dump(); +// dst.dump(); +// } +// } +// else +// { +// DEBUG_PRST_ROUTES(stderr, "ConnectionsView::paintEvent: srcItem not found:\n"); +// src.dump(); +// dst.dump(); +// } +// } +// } + +// // Draw the selected items over top all else. +// if(src_sel && dst_sel) +// { +// if(sel_wid) +// pen.setWidth(pen_wid_wide); +// else +// pen.setWidth(pen_wid_norm); +// pen.setColor(Qt::yellow); +// painter.setPen(pen); +// y1 = itemY(src_sel, true, src_sel_ch) + (yo - yc); +// y2 = itemY(dst_sel, false, dst_sel_ch) + (yi - yc); +// drawConnectionLine(&painter, x1, y1, x2, y2, h1, h2); +// } +} + +void ConnectionsView::mousePressEvent(QMouseEvent* e) +{ + e->setAccepted(true); + lastY = e->y(); +} + +void ConnectionsView::mouseMoveEvent(QMouseEvent* e) +{ + e->setAccepted(true); + const Qt::MouseButtons mb = e->buttons(); + const int y = e->y(); + const int ly = lastY; + lastY = y; + if(mb & Qt::LeftButton) + emit scrollBy(0, ly - y); +} + +void ConnectionsView::wheelEvent(QWheelEvent* e) +{ + int delta = e->delta(); + DEBUG_PRST_ROUTES(stderr, "ConnectionsView::wheelEvent: delta:%d\n", delta); // REMOVE Tim. + e->setAccepted(true); + emit scrollBy(0, delta < 0 ? 1 : -1); +} + +// Context menu request event handler. +void ConnectionsView::contextMenuEvent(QContextMenuEvent* /*pContextMenuEvent*/) +{ +// qjackctlConnect *pConnect = m_pConnectView->binding(); +// if (pConnect == 0) +// return; +// +// QMenu menu(this); +// QAction *pAction; +// +// pAction = menu.addAction(QIcon(":/images/connect1.png"), +// tr("&Connect"), pConnect, SLOT(connectSelected()), +// tr("Alt+C", "Connect")); +// pAction->setEnabled(pConnect->canConnectSelected()); +// pAction = menu.addAction(QIcon(":/images/disconnect1.png"), +// tr("&Disconnect"), pConnect, SLOT(disconnectSelected()), +// tr("Alt+D", "Disconnect")); +// pAction->setEnabled(pConnect->canDisconnectSelected()); +// pAction = menu.addAction(QIcon(":/images/disconnectall1.png"), +// tr("Disconnect &All"), pConnect, SLOT(disconnectAll()), +// tr("Alt+A", "Disconect All")); +// pAction->setEnabled(pConnect->canDisconnectAll()); +// +// menu.addSeparator(); +// pAction = menu.addAction(QIcon(":/images/refresh1.png"), +// tr("&Refresh"), pConnect, SLOT(refresh()), +// tr("Alt+R", "Refresh")); +// +// menu.exec(pContextMenuEvent->globalPos()); +} + + +// // Widget event slots... +// void ConnectionsView::contentsChanged (void) +// { +// QWidget::update(); +// } + + +//----------------------------------- +// RouteTreeWidget +//----------------------------------- + +RouteTreeWidget::RouteTreeWidget(QWidget* parent, bool is_input) : QTreeWidget(parent), _isInput(is_input), _channelWrap(false) +{ + if(header()) + connect(header(), SIGNAL(sectionResized(int,int,int)), SLOT(headerSectionResized(int,int,int))); +} + +RouteTreeWidget::~RouteTreeWidget() +{ +} + +void RouteTreeWidget::computeChannelYValues() +{ + const int ch_w = channelWrap() ? columnWidth(RouteDialog::ROUTE_NAME_COL) : -1; + QTreeWidgetItemIterator itw(this); + while(*itw) + { + RouteTreeWidgetItem* item = static_cast(*itw); +// item->computeChannelYValues(wordWrap() ? viewport()->width() : -1); + //item->computeChannelYValues(wordWrap() ? columnWidth(RouteDialog::ROUTE_NAME_COL) : -1); + //item->computeChannelYValues(MusEGlobal::config.routerExpandVertically ? columnWidth(RouteDialog::ROUTE_NAME_COL) : _VERY_LARGE_INTEGER_); + //item->computeChannelYValues(wordWrap() ? columnWidth(RouteDialog::ROUTE_NAME_COL) : _VERY_LARGE_INTEGER_); + item->computeChannelYValues(ch_w); + ++itw; + } +} + +void RouteTreeWidget::headerSectionResized(int logicalIndex, int oldSize, int newSize) +{ + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::headerSectionResized idx:%d old sz:%d new sz:%d\n", logicalIndex, oldSize, newSize); +// fprintf(stderr, "RouteTreeWidget::headerSectionResized idx:%d old sz:%d new sz:%d\n", logicalIndex, oldSize, newSize); // REMOVE Tim. +// scheduleDelayedItemsLayout(); + + //if(!wordWrap()) + // return; + + // Self adjust certain item heights... + // NOTE: Delegate sizeHints are NOT called automatically. scheduleDelayedItemsLayout() seems to solve it. + // But that is costly here! And results in some flickering especially at scrollbar on/off conditions as it fights with itself. + // So check if we really need to do it... + QTreeWidgetItemIterator ii(this); + //bool do_layout = false; + int relayouts = 0; + while(*ii) + { + RouteTreeWidgetItem* item = static_cast(*ii); + if(item->testForRelayout(logicalIndex, oldSize, newSize)) + { + const QModelIndex mdl_idx = indexFromItem(item); + if(mdl_idx.isValid()) + { + QAbstractItemDelegate* id = itemDelegate(); + if(RoutingItemDelegate* rid = qobject_cast(id)) + { + rid->emitSizeHintChanged(mdl_idx); + ++relayouts; + } + } + } + +// // switch(item->type()) +// // { +// // case RouteTreeWidgetItem::NormalItem: +// // break; +// // +// // case RouteTreeWidgetItem::CategoryItem: +// // case RouteTreeWidgetItem::RouteItem: +// // case RouteTreeWidgetItem::ChannelsItem: +// // { +// const QModelIndex midx = indexFromItem(item); +// if(midx.isValid()) +// { +// QAbstractItemDelegate* id = itemDelegate(); +// if(RoutingItemDelegate* rid = qobject_cast(id)) +// { +// QStyleOptionViewItem vopt; +// rid->initStyleOption(&vopt, midx); +// // if(item->testForRelayout(vopt, midx, oldSize, newSize)) +// //fprintf(stderr, "RouteTreeWidget::headerSectionResized calling rid->testForRelayout\n"); // REMOVE Tim. +// if(rid->testForRelayout(vopt, midx, oldSize, newSize)) +// { +// do_layout = true; +// // item->computeChannelYValues(newSize); +// //scheduleDelayedItemsLayout(); +// //return; +// } +// } +// } +// // } +// // break; +// // } + + ++ii; + } + +// if(do_layout) +// { +// //DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::headerSectionResized idx:%d old sz:%d new sz:%d calling scheduleDelayedItemsLayout()\n", logicalIndex, oldSize, newSize); +// // Neither updateGeometry() or updateGeometries() works here. +// scheduleDelayedItemsLayout(); +// } +// if(do_layout) + if(relayouts) + { + //DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::headerSectionResized idx:%d old sz:%d new sz:%d calling scheduleDelayedItemsLayout()\n", logicalIndex, oldSize, newSize); + //fprintf(stderr, "RouteTreeWidget::headerSectionResized idx:%d old sz:%d new sz:%d no of sizeHintChanged emitted:%d\n", logicalIndex, oldSize, newSize, relayouts); // REMOVE Tim. + // Neither updateGeometry() or updateGeometries() works here. +// scheduleDelayedItemsLayout(); + + // Redraw after computeChannelYValues has been called. +// update(); + + //connectionsWidget->update(); // Redraw the connections. FIXME: TODO: Need to access the dialog + } + +} + +RouteTreeWidgetItem* RouteTreeWidget::itemFromIndex(const QModelIndex& index) const +{ + return static_cast(QTreeWidget::itemFromIndex(index)); +} + +RouteTreeWidgetItem* RouteTreeWidget::findItem(const MusECore::Route& r, int type) +{ + QTreeWidgetItemIterator ii(this); + while(*ii) + { + QTreeWidgetItem* item = *ii; + switch(item->type()) + { + case RouteTreeWidgetItem::NormalItem: + case RouteTreeWidgetItem::CategoryItem: + break; + + case RouteTreeWidgetItem::RouteItem: + case RouteTreeWidgetItem::ChannelsItem: + { + RouteTreeWidgetItem* rtwi = static_cast(item); + if((type == -1 || type == item->type()) && rtwi->route().compare(r)) + return rtwi; + } + break; + } + ++ii; + } + return NULL; + +// const int cnt = topLevelItemCount(); +// for(int i = 0; i < cnt; ++i) +// { +// RouteTreeWidgetItem* item = static_cast(topLevelItem(i)); +// if(!item) +// continue; +// if((type == -1 || type == RouteTreeWidgetItem::CategoryItem) && item->route().compare(r)) +// return item; +// +// const int c_cnt = item->childCount(); +// for(int j = 0; j < c_cnt; ++j) +// { +// RouteTreeWidgetItem* c_item = static_cast(item->child(j)); +// if(!c_item) +// continue; +// if((type == -1 || type == RouteTreeWidgetItem::RouteItem) && c_item->route().compare(r)) +// return c_item; +// +// const int cc_cnt = c_item->childCount(); +// for(int k = 0; k < cc_cnt; ++k) +// { +// RouteTreeWidgetItem* cc_item = static_cast(c_item->child(k)); +// if(!cc_item) +// continue; +// if((type == -1 || type == RouteTreeWidgetItem::ChannelsItem) && cc_item->route().compare(r)) +// return cc_item; +// } +// } +// } +// return 0; +} + +RouteTreeWidgetItem* RouteTreeWidget::findCategoryItem(const QString& name) +{ + const int cnt = topLevelItemCount(); + for(int i = 0; i < cnt; ++i) + { + RouteTreeWidgetItem* item = static_cast(topLevelItem(i)); + if(item && item->type() == RouteTreeWidgetItem::CategoryItem && item->text(RouteDialog::ROUTE_NAME_COL) == name) + return item; + } + return 0; +} + +void RouteTreeWidget::getSelectedRoutes(MusECore::RouteList& routes) +{ + RouteTreeItemList sel = selectedItems(); + const int selSz = sel.size(); + if(selSz == 0) + return; + for(int idx = 0; idx < selSz; ++idx) + { + RouteTreeWidgetItem* item = static_cast(sel.at(idx)); + if(!item) + continue; + item->getSelectedRoutes(routes); + } +} + +// if(!item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// continue; +// MusECore::Route r = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::RouteRole).value(); +// if(item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).canConvert()) +// { +// QBitArray ba = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); +// switch(r.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// if(r.track) +// { +// const int sz = ba.size(); +// if(r.track->isMidiTrack()) +// { +// for(int i = 0; i < sz; ++i) +// { +// if(i >= MIDI_CHANNELS) +// break; +// if(ba.testBit(i)) +// { +// r.channel = (1 << i); +// routes.push_back(r); +// } +// } +// } +// else +// { +// for(int i = 0; i < sz; ++i) +// { +// if(ba.testBit(i)) +// { +// r.channel = i; +// routes.push_back(r); +// } +// } +// } +// } +// break; +// case MusECore::Route::JACK_ROUTE: +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// break; +// } +// } +// else +// routes.push_back(r); +// } +// } + +int RouteTreeWidget::channelAt(RouteTreeWidgetItem* item, const QPoint& pt) +{ + const QRect rect = visualItemRect(item); + + return item->channelAt(pt, rect); + +// QPoint p = pt - rect.topLeft(); +// +// int w = RouteDialog::midiDotsMargin * 2 + RouteDialog::midiDotDiameter * channels; +// if(channels > 1) +// w += RouteDialog::midiDotSpacing * (channels - 1); +// if(channels > 4) +// w += RouteDialog::midiDotGroupSpacing * (channels - 1) / 4; +// +// const int xoff =_isInput ? rect.width() - w : RouteDialog::midiDotsMargin; +// const int yoff = RouteDialog::midiDotsMargin + (_isInput ? channels : 0); +// p.setY(p.y() - yoff); +// p.setX(p.x() - xoff); +// if(p.y() < 0 || p.y() >= RouteDialog::midiDotDiameter) +// return -1; +// for(int i = 0; i < channels; ++i) +// { +// if(p.x() < 0) +// return -1; +// if(p.x() < RouteDialog::midiDotDiameter) +// return i; +// p.setX(p.x() - RouteDialog::midiDotDiameter - RouteDialog::midiDotSpacing); +// if(i && ((i % 4) == 0)) +// p.setX(p.x() - RouteDialog::midiDotGroupSpacing); +// } +// return -1; +} + +void RouteTreeWidget::resizeEvent(QResizeEvent* event) +{ + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::resizeEvent old w:%d h:%d new w:%d h:%d\n", event->oldSize().width(), event->oldSize().height(), + event->size().width(), event->size().height()); + + event->ignore(); + QTreeWidget::resizeEvent(event); + //if(wordWrap()) + //if(MusEGlobal::config.routerExpandVertically) +// headerSectionResized(RouteDialog::ROUTE_NAME_COL, event->oldSize().width(), event->size().width()); // ZZZ +} + +void RouteTreeWidget::mousePressEvent(QMouseEvent* e) +{ + const QPoint pt = e->pos(); + //Qt::KeyboardModifiers km = e->modifiers(); + //bool ctl = km & Qt::ControlModifier; + //bool shift = km & Qt::ShiftModifier; + RouteTreeWidgetItem* item = static_cast(itemAt(pt)); + bool is_cur = item && currentItem() && (item == currentItem()); + + //if(is_cur) + // QTreeWidget::mousePressEvent(e); + + if(item) + { + bool changed = item->mousePressHandler(e, visualItemRect(item)); + if(changed) + { + //setCurrentItem(item); + //update(visualItemRect(item)); + QRect r(visualItemRect(item)); + // Need to update from the item's right edge to the viewport right edge, + // for the connector lines. + r.setRight(this->viewport()->geometry().right()); + setDirtyRegion(r); + //emit itemSelectionChanged(); + } + + //if(!is_cur) + QTreeWidget::mousePressEvent(e); + + if(changed && is_cur) + //setCurrentItem(item); + emit itemSelectionChanged(); + + //e->accept(); + return; + + } + QTreeWidget::mousePressEvent(e); +} + +/* + if(item && item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) + { + const MusECore::Route r = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::RouteRole).value(); + switch(r.type) + { + case MusECore::Route::TRACK_ROUTE: + if(r.track && r.channel != -1 && item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).canConvert()) + { + int chans; + if(r.track->isMidiTrack()) + chans = MIDI_CHANNELS; + else + { + MusECore::AudioTrack* atrack = static_cast(r.track); + if(atrack->type() == MusECore::Track::AUDIO_SOFTSYNTH) + chans = _isInput ? atrack->totalOutChannels() : atrack->totalInChannels(); + else + chans = atrack->channels(); + } + + int ch = channelAt(item, pt, chans); + + //QBitArray ba = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); + //QBitArray ba_m = ba; + QBitArray ba_m = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); + const int ba_sz = ba_m.size(); + bool changed = false; + //if(!ctl) + { + //ba_m.fill(false); + for(int i = 0; i < ba_sz; ++i) + { + //const bool b = ba_m.testBit(i); + + if(i == ch) + { + if(ctl) + { + ba_m.toggleBit(i); + changed = true; + } + else + { + if(!ba_m.testBit(i)) + changed = true; + ba_m.setBit(i); + } + } + else if(!ctl) + { + if(ba_m.testBit(i)) + changed = true; + ba_m.clearBit(i); + } + +// //if(ba_m.testBit(i)) +// { +// ba_m.clearBit(i); +// changed = true; +// } + } + } +// //clearChannels(); +// // clearSelection(); +// //int ch = channelAt(item, pt, chans); +// if(ch != -1 && ch < ba_sz) +// { +// ba_m.toggleBit(ch); +// changed = true; +// } + + //if(is_cur) + // QTreeWidget::mousePressEvent(e); + + //if(ba_m != ba) + if(changed) + { + item->setData(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole, qVariantFromValue(ba_m)); + //setCurrentItem(item); + update(visualItemRect(item)); + //emit itemSelectionChanged(); + } + + //if(!is_cur) + QTreeWidget::mousePressEvent(e); + + if(changed && is_cur) + //setCurrentItem(item); + emit itemSelectionChanged(); + + //e->accept(); + return; + } + break; + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + } + QTreeWidget::mousePressEvent(e); +}*/ + +void RouteTreeWidget::mouseMoveEvent(QMouseEvent* e) +{ + const QPoint pt = e->pos(); + //Qt::KeyboardModifiers km = e->modifiers(); + //bool ctl = km & Qt::ControlModifier; + //bool shift = km & Qt::ShiftModifier; + RouteTreeWidgetItem* item = static_cast(itemAt(pt)); + bool is_cur = item && currentItem() && (item == currentItem()); + + //if(is_cur) + // QTreeWidget::mouseMoveEvent(e); + + if(item) + { + bool changed = item->mouseMoveHandler(e, visualItemRect(item)); + if(changed) + { + //setCurrentItem(item); + //update(visualItemRect(item)); + setDirtyRegion(visualItemRect(item)); + //emit itemSelectionChanged(); + } + + //if(!is_cur) + QTreeWidget::mouseMoveEvent(e); + + if(changed && is_cur) + //setCurrentItem(item); + emit itemSelectionChanged(); + + //e->accept(); + return; + + } + QTreeWidget::mouseMoveEvent(e); +} + +QItemSelectionModel::SelectionFlags RouteTreeWidget::selectionCommand(const QModelIndex& index, const QEvent* e) const +{ + QItemSelectionModel::SelectionFlags flags = QTreeWidget::selectionCommand(index, e); + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::selectionCommand flags:%d row:%d col:%d ev type:%d\n", int(flags), index.row(), index.column(), e ? e->type() : -1); + + RouteTreeWidgetItem* item = itemFromIndex(index); + + if(item && item->type() == RouteTreeWidgetItem::ChannelsItem) + { + if(flags & QItemSelectionModel::Toggle) + { + flags &= ~QItemSelectionModel::Toggle; + flags |= QItemSelectionModel::Select; + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::selectionCommand new flags:%d\n", int(flags)); + } + } + + return flags; +} + +// //if(index.data(RouteDialog::RouteRole).canConvert()) +// if(item) +// { +// DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::selectionCommand can convert data to Route\n"); +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint data is Route\n"); // REMOVE Tim. +// //const MusECore::Route r = qvariant_cast(index.data(RouteDialog::RouteRole)); +// //const MusECore::Route r = index.data(RouteDialog::RouteRole).value(); +// const MusECore::Route& r = item->route(); +// switch(r.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// //if(e->type() == QEvent:: r.channel != -1) +// if(r.channel != -1) +// { +// if(flags & QItemSelectionModel::Toggle) +// { +// flags &= ~QItemSelectionModel::Toggle; +// flags |= QItemSelectionModel::Select; +// DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::selectionCommand new flags:%d\n", int(flags)); +// } +// } +// break; +// +// case MusECore::Route::JACK_ROUTE: +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// break; +// } +// } +// return flags; +// } + +void RouteTreeWidget::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) +{ + QModelIndexList mil = deselected.indexes(); + const int dsz = mil.size(); + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::selectionChanged: selected size:%d deselected size:%d\n", selected.size(), dsz); + for(int i = 0; i < dsz; ++i) + { + const QModelIndex& index = mil.at(i); + RouteTreeWidgetItem* item = itemFromIndex(index); + + if(item && item->type() == RouteTreeWidgetItem::ChannelsItem) + item->fillSelectedChannels(false); + } + QTreeWidget::selectionChanged(selected, deselected); +} +// //if(item && item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// if(item) +// { +// //const MusECore::Route r = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::RouteRole).value(); +// const MusECore::Route& r = item->route(); +// switch(r.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// //if(e->type() == QEvent:: r.channel != -1) +// if(r.channel != -1) +// { +// // if(item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).canConvert()) +// // { +// // DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::selectionChanged: track route: deselected idx:%d clearing channels bitarray\n", i); +// // QBitArray ba = item->data(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); +// // ba.fill(false); +// // item->setData(RouteDialog::ROUTE_NAME_COL, RouteDialog::ChannelsRole, qVariantFromValue(ba)); +// item->fillChannels(false); +// // } +// } +// break; +// +// case MusECore::Route::JACK_ROUTE: +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// break; +// } +// } +// +// } +// QTreeWidget::selectionChanged(selected, deselected); +// } + +void RouteTreeWidget::scrollBy(int dx, int dy) +{ + DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::scrollBy: dx:%d dy:%d\n", dx, dy); // REMOVE Tim. + int hv = horizontalScrollBar()->value(); + int vv = verticalScrollBar()->value(); + if(dx) + { + hv += dx; + horizontalScrollBar()->setValue(hv); + } + if(dy) + { + vv += dy; + verticalScrollBar()->setValue(vv); + } +} + +void RouteTreeWidget::getItemsToDelete(QVector& items_to_remove, bool showAllMidiPorts) +{ + QTreeWidgetItemIterator ii(this); + while(*ii) + { + QTreeWidgetItem* item = *ii; + if(item) + { + QTreeWidgetItem* twi = item; + while((twi = twi->parent())) + { + if(items_to_remove.contains(twi)) + break; + } + // No parent found to be deleted. Determine if this should be deleted. + if(!twi) + { + if(!items_to_remove.contains(item)) + { + RouteTreeWidgetItem* rtwi = static_cast(item); + + switch(rtwi->type()) + { + case RouteTreeWidgetItem::NormalItem: + case RouteTreeWidgetItem::CategoryItem: + case RouteTreeWidgetItem::ChannelsItem: + break; + + case RouteTreeWidgetItem::RouteItem: + { + const MusECore::Route& rt = rtwi->route(); + switch(rt.type) + { + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + break; + + case MusECore::Route::MIDI_PORT_ROUTE: + { + bool remove_port = false; + if(!rt.isValid()) + remove_port = true; + else + if(!showAllMidiPorts) + { + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[rt.midiPort]; + if(!mp->device() && (_isInput ? mp->outRoutes()->empty() : mp->inRoutes()->empty())) + { + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + if(!_isInput) + { + MusECore::MidiTrackList* tl = MusEGlobal::song->midis(); + MusECore::ciMidiTrack imt = tl->begin(); + for( ; imt != tl->end(); ++imt) + if((*imt)->outPort() == rt.midiPort) + break; + if(imt == tl->end()) + remove_port = true; + } + else +#endif // _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + remove_port = true; + + } + } + + if(remove_port) + items_to_remove.append(item); + ++ii; + continue; + } + break; + } + } + break; + } + + if(!rtwi->routeNodeExists()) + items_to_remove.append(item); + } + } + } + ++ii; + } + +/* + + const int cnt = topLevelItemCount(); + for(int i = 0; i < cnt; ++i) + { + RouteTreeWidgetItem* item = static_cast(topLevelItem(i)); + if(item) + { + const int c_cnt = item->childCount(); + for(int j = 0; j < c_cnt; ++j) + { + RouteTreeWidgetItem* c_item = static_cast(item->child(j)); + if(c_item) + { + const int cc_cnt = c_item->childCount(); + for(int k = 0; k < cc_cnt; ++k) + { + RouteTreeWidgetItem* cc_item = static_cast(c_item->child(k)); + if(cc_item) + { + if(!cc_item->routeNodeExists()) + items_to_remove.append(cc_item); + } + } + if(!c_item->routeNodeExists()) + items_to_remove.append(c_item); + } + } + if(!item->routeNodeExists()) + items_to_remove.append(item); + } + }*/ +} + +void RouteTreeWidget::selectRoutes(const QList& routes, bool doNormalSelections) +{ + QTreeWidgetItemIterator ii(this); + while(*ii) + { + RouteTreeWidgetItem* rtwi = static_cast(*ii); + switch(rtwi->type()) + { + case RouteTreeWidgetItem::NormalItem: + case RouteTreeWidgetItem::CategoryItem: + case RouteTreeWidgetItem::RouteItem: + break; + + case RouteTreeWidgetItem::ChannelsItem: + { + bool do_upd = rtwi->fillChannelsRouteSelected(false); + if(doNormalSelections && rtwi->fillSelectedChannels(false)) + do_upd = true; + const MusECore::Route& rtwi_route = rtwi->route(); + const int sz = routes.size(); + for(int i = 0; i < sz; ++i) + { + const QTreeWidgetItem* routes_item = routes.at(i); + const MusECore::Route r = + routes_item->data(isInput() ? RouteDialog::ROUTE_SRC_COL : RouteDialog::ROUTE_DST_COL, RouteDialog::RouteRole).value(); + if(rtwi_route.compare(r)) + { + const int chan = r.channel; + if(chan >= 0) + { + //if(!rtwi->channelRouteSelected(chan)) + //{ + rtwi->routeSelectChannel(chan, true); + do_upd = true; + //} + //if(doNormalSelections && !rtwi->channelSelected(chan)) + if(doNormalSelections) + { + rtwi->selectChannel(chan, true); + do_upd = true; + } + } + +// const int chans = rtwi->channelCount(); +// for(int c = 0; c < chans; ++c) +// { +// // Set both the selected and route selected flags. +// if(rtwi->channelSelected(c) != (c == chan)) +// { +// rtwi->selectChannel(c, c == chan); +// do_upd = true; +// } +// if(rtwi->channelRouteSelected(c) != (c == chan)) +// { +// rtwi->routeSelectChannel(c, c == chan); +// do_upd = true; +// } +// } + } + } + if(do_upd) + { + QRect r(visualItemRect(rtwi)); + // Need to update from the item's right edge to the viewport right edge, + // for the connector lines. + r.setRight(this->viewport()->geometry().right()); + setDirtyRegion(r); + } + } + break; + } + ++ii; + } +} + +//----------------------------------- +// RoutingItemDelegate +//----------------------------------- + +RoutingItemDelegate::RoutingItemDelegate(bool is_input, RouteTreeWidget* tree, QWidget *parent) + : QStyledItemDelegate(parent), _tree(tree), _isInput(is_input) +{ + _firstPress = true; +} + +// //----------------------------------- +// // getItemRectangle +// // editor is optional and provides info +// //----------------------------------- +// +// QRect RoutingItemDelegate::getItemRectangle(const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement subElement, QWidget* editor) const +// { +// // Taken from QStyledItemDelegate source. +// QStyleOptionViewItemV4 opt = option; +// initStyleOption(&opt, index); +// const QWidget* widget = NULL; +// const QStyleOptionViewItemV3* v3 = qstyleoption_cast(&option); +// if(v3) +// widget = v3->widget; +// // Let the editor take up all available space if the editor is not a QLineEdit or it is in a QTableView. +// #if !defined(QT_NO_TABLEVIEW) && !defined(QT_NO_LINEEDIT) +// if(editor && qobject_cast(editor) && !qobject_cast(widget)) +// opt.showDecorationSelected = editor->style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, 0, editor); +// else +// #endif +// opt.showDecorationSelected = true; +// const QStyle *style = widget ? widget->style() : QApplication::style(); +// // if(editor->layoutDirection() == Qt::RightToLeft) +// // { +// // const int delta = qSmartMinSize(editor).width() - r.width(); // qSmartMinSize ??? +// // if (delta > 0) +// // { +// // //we need to widen the geometry +// // r.adjust(-delta, 0, 0, 0); +// // } +// // } +// +// return style->subElementRect(subElement, &opt, widget); +// } +// +// //----------------------------------- +// // subElementHitTest +// // editor is optional and provides info +// //----------------------------------- +// +// bool RoutingItemDelegate::subElementHitTest(const QPoint& point, const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement* subElement, QWidget* editor) const +// { +// QRect checkBoxRect = getItemRectangle(option, index, QStyle::SE_ItemViewItemCheckIndicator, editor); +// if(checkBoxRect.isValid() && checkBoxRect.contains(point)) +// { +// if(subElement) +// (*subElement) = QStyle::SE_ItemViewItemCheckIndicator; +// return true; +// } +// +// QRect decorationRect = getItemRectangle(option, index, QStyle::SE_ItemViewItemDecoration, editor); +// if(decorationRect.isValid() && decorationRect.contains(point)) +// { +// if(subElement) +// (*subElement) = QStyle::SE_ItemViewItemDecoration; +// return true; +// } +// +// QRect textRect = getItemRectangle(option, index, QStyle::SE_ItemViewItemText, editor); +// if(textRect.isValid() && textRect.contains(point)) +// { +// if(subElement) +// (*subElement) = QStyle::SE_ItemViewItemText; +// return true; +// } +// +// return false; +// } + +// //----------------------------------- +// // updateEditorGeometry +// //----------------------------------- +// +// void RoutingItemDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const +// { +// // REMOVE Tim. +// DEBUG_PRST_ROUTES(stderr, "ColorChooserEditor::updateEditorGeometry editor x:%d y:%d w:%d h:%d rect x:%d y:%d w:%d h:%d\n", +// editor->x(), editor->y(), editor->width(), editor->height(), +// option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height()); +// +// // For the color editor, move it down to the start of the next item so it doesn't cover the current item row. +// // Width and height are not used - the color editor fixates it's own width and height. +// if(index.column() == ControlMapperDialog::C_NAME) +// { +// QRect r = getItemRectangle(option, index, QStyle::SE_ItemViewItemText, editor); // Get the text rectangle. +// if(r.isValid()) +// { +// editor->move(r.x(), option.rect.y() + option.rect.height()); +// return; +// } +// } +// +// QStyledItemDelegate::updateEditorGeometry(editor, option, index); +// } + +void RoutingItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint row:%d col:%d, rect x:%d y:%d w:%d h:%d showDecorationSelected:%d\n", +// index.row(), index.column(), +// option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height(), +// option.showDecorationSelected); // REMOVE Tim. + + RouteTreeWidgetItem* item = _tree->itemFromIndex(index); + if(item) + { + // Required. option is not automatically filled from index. + QStyleOptionViewItem vopt(option); + initStyleOption(&vopt, index); + + if(item->paint(painter, vopt, index)) + return; + } + QStyledItemDelegate::paint(painter, option, index); +} + + //QStyleOptionViewItemV4 opt = option; + //initStyleOption(&opt, index); + //opt.showDecorationSelected = false; + + // TODO: Don't forget these if necessary. + //painter->save(); + //painter->restore(); + +// if (index.data().canConvert()) { +// StarRating starRating = qvariant_cast(index.data()); +// +// if (option.state & QStyle::State_Selected) +// painter->fillRect(option.rect, option.palette.highlight()); +// +// starRating.paint(painter, option.rect, option.palette, +// StarRating::ReadOnly); +// } else + +// if(index.column() == ControlMapperDialog::C_NAME) +// { +// // TODO: Disable all this Style stuff if using a style sheet. +// +// //QRect disclosure_r = getItemRectangle(option, index, QStyle::SE_TreeViewDisclosureItem); // Get the text rectangle. +// //if(disclosure_r.isValid()) +// //{ +// //} +// +// QRect checkbox_r = getItemRectangle(option, index, QStyle::SE_ItemViewItemCheckIndicator); // Get the text rectangle. +// if(checkbox_r.isValid()) +// { +// if(option.state & QStyle::State_Selected) +// painter->fillRect(checkbox_r & option.rect, option.palette.highlight()); +// QStyleOptionViewItemV4 opt = option; +// initStyleOption(&opt, index); // Required ? +// opt.rect = checkbox_r & option.rect; +// QApplication::style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt, painter); +// //QApplication::style()->drawControl(); +// } +// +// //QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); +// +// //QApplication::style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter); +// +// QRect deco_r = getItemRectangle(option, index, QStyle::SE_ItemViewItemDecoration); // Get the text rectangle. +// if(deco_r.isValid()) +// painter->fillRect(deco_r & option.rect, index.data(Qt::DecorationRole).value()); +// +// QRect text_r = getItemRectangle(option, index, QStyle::SE_ItemViewItemText); // Get the text rectangle. +// if(text_r.isValid()) +// { +// if(option.state & QStyle::State_Selected) +// painter->fillRect(text_r & option.rect, option.palette.highlight()); +// QApplication::style()->drawItemText(painter, text_r & option.rect, option.displayAlignment, option.palette, true, index.data(Qt::DisplayRole).toString()); +// } +// +// return; +// } + + //QStyledItemDelegate::paint(painter, option, index); + + //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint\n"); // REMOVE Tim. + +// // RouteDialog* router = qobject_cast< RouteDialog* >(parent()); +// //if(parent() && qobject_cast< RouteDialog* >(parent())) +// // if(router) +// // { +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint parent is RouteDialog\n"); // REMOVE Tim. +// //QWidget* qpd = qobject_cast(painter->device()); +// //if(qpd) +// if(painter->device()) +// { +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint device is QWidget\n"); // REMOVE Tim. +// //RouteDialog* router = static_cast(parent()); +// +// if(index.column() == RouteDialog::ROUTE_NAME_COL && index.data(RouteDialog::RouteRole).canConvert()) +// { +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint data is Route\n"); // REMOVE Tim. +// MusECore::Route r = qvariant_cast(index.data(RouteDialog::RouteRole)); +// QRect rect(option.rect); +// switch(r.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint route is track\n"); // REMOVE Tim. +// if(r.track && r.channel != -1) +// { +// int chans; +// if(r.track->isMidiTrack()) +// { +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint track is midi\n"); // REMOVE Tim. +// chans = MIDI_CHANNELS; +// } +// else +// { +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint track is audio\n"); // REMOVE Tim. +// MusECore::AudioTrack* atrack = static_cast(r.track); +// if(atrack->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// { +// if(_isInput) +// chans = atrack->totalOutChannels(); +// else +// chans = atrack->totalInChannels(); +// } +// else +// chans = atrack->channels(); +// } +// +// int w = RouteDialog::midiDotsMargin * 2 + RouteDialog::midiDotDiameter * chans; +// if(chans > 1) +// w += RouteDialog::midiDotSpacing * (chans - 1); +// if(chans > 4) +// w += RouteDialog::midiDotGroupSpacing * (chans - 1) / 4; +// +// //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint src list width:%d src viewport width:%d\n", router->newSrcList->width(), router->newSrcList->viewport()->width()); // REMOVE Tim. +// //int x = _isInput ? router->newSrcList->viewport()->width() - w : RouteDialog::midiDotsMargin; +// //int x = _isInput ? painter->device()->width() - w : RouteDialog::midiDotsMargin; +// int x = _isInput ? _tree->width() - w : RouteDialog::midiDotsMargin; +// const int y = RouteDialog::midiDotsMargin + (_isInput ? chans : 0); +// QBitArray ba; +// int basize = 0; +// if(index.data(RouteDialog::ChannelsRole).canConvert()) +// { +// ba = index.data(RouteDialog::ChannelsRole).value(); +// basize = ba.size(); +// } +// +// for(int i = 0; i < chans; ) +// { +// painter->setPen(Qt::black); +// //painter->drawRoundedRect(option.rect.x() + x, option.rect.y() + y, +// if(!ba.isNull() && i < basize && ba.testBit(i)) +// painter->fillRect(x, option.rect.y() + y, +// RouteDialog::midiDotDiameter, RouteDialog::midiDotDiameter, +// option.palette.highlight()); +// //else +// painter->drawRoundedRect(x, option.rect.y() + y, +// RouteDialog::midiDotDiameter, RouteDialog::midiDotDiameter, +// 30, 30); +// if((i % 2) == 0) +// painter->setPen(Qt::darkGray); +// else +// painter->setPen(Qt::black); +// int xline = x + RouteDialog::midiDotDiameter / 2; +// if(_isInput) +// { +// int yline = option.rect.y() + y; +// painter->drawLine(xline, yline, xline, yline - chans + i); +// //painter->drawLine(xline, yline - chans + i, painter->device()->width(), yline - chans + i); +// painter->drawLine(xline, yline - chans + i, _tree->width(), yline - chans + i); +// +// } +// else +// { +// int yline = option.rect.y() + RouteDialog::midiDotsMargin + RouteDialog::midiDotDiameter; +// painter->drawLine(xline, yline, xline, yline + i); +// painter->drawLine(0, yline + i, xline, yline + i); +// +// } +// +// ++i; +// x += RouteDialog::midiDotDiameter + RouteDialog::midiDotSpacing; +// if(i && ((i % 4) == 0)) +// x += RouteDialog::midiDotGroupSpacing; +// } +// return; +// } +// break; +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// case MusECore::Route::JACK_ROUTE: +// break; +// } +// } +// } +// // } +// QStyledItemDelegate::paint(painter, option, index); +// } + +// QWidget* RoutingItemDelegate::createEditor(QWidget *parent, +// const QStyleOptionViewItem &option, +// const QModelIndex &index) const +// { +// // if (index.data().canConvert()) { +// // StarEditor *editor = new StarEditor(parent); +// // connect(editor, SIGNAL(editingFinished()), +// // this, SLOT(commitAndCloseEditor())); +// // return editor; +// // } else +// +// int opt_state = option.state; +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::createEditor option state:%d\n", opt_state); // REMOVE Tim. +// +// // HACK: For some reason when using CurrentChanged trigger, createEditor is called upon opening the dialog, yet nothing is selected. +// // It suddenly started doing that after working just fine. Can't find what may have changed. +// //if(!(option.state & QStyle::State_Selected)) // Nope. option.state is always the same, never seems to change. +// // return NULL; +// //if(_firstPress) +// // return NULL; +// +// switch(index.column()) +// { +// // case ControlMapperDialog::C_SHOW: +// // //return QStyledItemDelegate::createEditor(parent, option, index); +// // // This is a checkbox column. No editable info. +// // //DEBUG_PRST_ROUTES(stderr, "ERROR: RoutingItemDelegate::createEditor called for SHOW column\n"); +// // return 0; +// +// //case ControlMapperDialog::C_NAME: +// //DEBUG_PRST_ROUTES(stderr, "ERROR: RoutingItemDelegate::createEditor called for NAME column\n"); +// // This seems to be a way we can prevent editing of a cell here in this tree widget. +// // Table widget has individual item cell edting enable but here in tree widget it's per row. +// //return 0; +// +// //case ControlMapperDialog::C_COLOR: +// case ControlMapperDialog::C_NAME: +// { +// ColorEditor* color_list = new ColorEditor(parent); +// //connect(color_list, SIGNAL(activated(int)), this, SLOT(colorEditorChanged())); +// connect(color_list, SIGNAL(activated(const QColor&)), this, SLOT(editorChanged())); +// return color_list; +// } +// +// case ControlMapperDialog::C_ASSIGN_PORT: +// { +// QComboBox* combo = new QComboBox(parent); +// +// // combo->addItem(tr(""), -1); +// // combo->addItem(tr("Control7"), MusECore::MidiController::Controller7); +// // combo->addItem(tr("Control14"), MusECore::MidiController::Controller14); +// // combo->addItem(tr("RPN"), MusECore::MidiController::RPN); +// // combo->addItem(tr("NPRN"), MusECore::MidiController::NRPN); +// // combo->addItem(tr("RPN14"), MusECore::MidiController::RPN14); +// // combo->addItem(tr("NRPN14"), MusECore::MidiController::NRPN14); +// // combo->addItem(tr("Pitch"), MusECore::MidiController::Pitch); +// // combo->addItem(tr("Program"), MusECore::MidiController::Program); +// // //combo->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); // Not supported yet. Need a way to select pitch. +// // combo->addItem(tr("Aftertouch"), MusECore::MidiController::Aftertouch); +// // //combo->setCurrentIndex(0); +// +// // combo->addItem(tr(""), -1); +// // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller7), MusECore::MidiController::Controller7); +// // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller14), MusECore::MidiController::Controller14); +// // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN), MusECore::MidiController::RPN); +// // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN), MusECore::MidiController::NRPN); +// // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN14), MusECore::MidiController::RPN14); +// // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN14), MusECore::MidiController::NRPN14); +// // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Pitch), MusECore::MidiController::Pitch); +// // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Program), MusECore::MidiController::Program); +// // //combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::PolyAftertouch), MusECore::MidiController::PolyAftertouch); // Not supported yet. Need a way to select pitch. +// // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Aftertouch), MusECore::MidiController::Aftertouch); +// +// combo->addItem("---", -1); +// int port = index.data(RouteDialog::RouteRole).toInt(); +// QString port_name; +// 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 && (md->rwFlags() & 2)) && (i != port)) // Only readable ports, or current one. +// continue; +// //name.sprintf("%d:%s", i+1, MusEGlobal::midiPorts[i].portname().toLatin1().constData()); +// QString name = QString("%1:%2").arg(i+1).arg(MusEGlobal::midiPorts[i].portname()); +// combo->addItem(name, i); +// } +// connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(editorChanged())); +// return combo; +// } +// +// case ControlMapperDialog::C_ASSIGN_CHAN: +// { +// // QSpinBox* spin_box = new QSpinBox(parent); +// // spin_box->setMinimum(0); +// // spin_box->setMaximum(127); +// // return spin_box; +// +// QWidget* widget = QStyledItemDelegate::createEditor(parent, option, index); +// QSpinBox* spin_box = qobject_cast(widget); +// if(spin_box) +// { +// spin_box->setMinimum(0); +// spin_box->setMaximum(MIDI_CHANNELS - 1); +// } +// return widget; +// } +// +// case ControlMapperDialog::C_MCTL_NUM: +// { +// QComboBox* combo = new QComboBox(parent); +// +// // combo->addItem(tr(""), -1); +// // combo->addItem(tr("Control7"), MusECore::MidiController::Controller7); +// // combo->addItem(tr("Control14"), MusECore::MidiController::Controller14); +// // combo->addItem(tr("RPN"), MusECore::MidiController::RPN); +// // combo->addItem(tr("NPRN"), MusECore::MidiController::NRPN); +// // combo->addItem(tr("RPN14"), MusECore::MidiController::RPN14); +// // combo->addItem(tr("NRPN14"), MusECore::MidiController::NRPN14); +// // combo->addItem(tr("Pitch"), MusECore::MidiController::Pitch); +// // combo->addItem(tr("Program"), MusECore::MidiController::Program); +// // //combo->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); // Not supported yet. Need a way to select pitch. +// // combo->addItem(tr("Aftertouch"), MusECore::MidiController::Aftertouch); +// // //combo->setCurrentIndex(0); +// +// //combo->addItem(tr(""), -1); +// combo->addItem("---", -1); +// combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller7), MusECore::MidiController::Controller7); +// combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller14), MusECore::MidiController::Controller14); +// combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN), MusECore::MidiController::RPN); +// combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN), MusECore::MidiController::NRPN); +// combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN14), MusECore::MidiController::RPN14); +// combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN14), MusECore::MidiController::NRPN14); +// combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Pitch), MusECore::MidiController::Pitch); +// combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Program), MusECore::MidiController::Program); +// // TODO Per-pitch controls not supported yet. Need a way to select pitch. +// //combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::PolyAftertouch), MusECore::MidiController::PolyAftertouch); +// combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Aftertouch), MusECore::MidiController::Aftertouch); +// connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(editorChanged())); +// return combo; +// } +// +// // case ControlMapperDialog::C_MCTL_H: +// // { +// // // QSpinBox* spin_box = new QSpinBox(parent); +// // // spin_box->setMinimum(0); +// // // spin_box->setMaximum(127); +// // // return spin_box; +// // +// // QWidget* widget = QStyledItemDelegate::createEditor(parent, option, index); +// // QSpinBox* spin_box = qobject_cast(widget); +// // if(spin_box) +// // { +// // spin_box->setMinimum(0); +// // spin_box->setMaximum(127); +// // } +// // return widget; +// // } +// +// /// case ControlMapperDialog::C_MCTL_H: +// // case ControlMapperDialog::C_MCTL_L: +// // { +// // // QSpinBox* spin_box = new QSpinBox(parent); +// // // spin_box->setMinimum(0); +// // // spin_box->setMaximum(127); +// // // return spin_box; +// // +// // QWidget* widget = QStyledItemDelegate::createEditor(parent, option, index); +// // QSpinBox* spin_box = qobject_cast(widget); +// // if(spin_box) +// // { +// // spin_box->setMinimum(0); +// // spin_box->setMaximum(127); +// // } +// // return widget; +// // } +// } +// +// return QStyledItemDelegate::createEditor(parent, option, index); +// } + +// void RoutingItemDelegate::editorChanged() +// { +// // StarEditor *editor = qobject_cast(sender()); +// // emit commitData(editor); +// // emit closeEditor(editor); +// +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::editorChanged\n"); // REMOVE Tim. +// +// // Wow, I thought using sender was frowned upon ("breaks modularity"). But hey, it's necessary sometimes. TODO Improve this? +// //ColorEditor* editor = qobject_cast(sender()); +// QWidget* editor = qobject_cast(sender()); +// if(editor) +// { +// emit commitData(editor); +// emit closeEditor(editor); +// } +// } + +// // void RoutingItemDelegate::commitAndCloseEditor() +// // { +// // // StarEditor *editor = qobject_cast(sender()); +// // // emit commitData(editor); +// // // emit closeEditor(editor); +// // } + +// void RoutingItemDelegate::setEditorData(QWidget *editor, +// const QModelIndex &index) const +// { +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::setEditorData\n"); // REMOVE Tim. +// // if (index.data().canConvert()) { +// // StarRating starRating = qvariant_cast(index.data()); +// // StarEditor *starEditor = qobject_cast(editor); +// // starEditor->setStarRating(starRating); +// // } else +// +// //if(index.column() == ControlMapperDialog::C_COLOR) +// +// +// switch(index.column()) +// { +// case ControlMapperDialog::C_NAME: +// { +// ColorEditor* color_editor = qobject_cast(editor); +// if(color_editor) +// color_editor->setColor(index.data(Qt::DecorationRole).value()); +// return; +// } +// +// case ControlMapperDialog::C_ASSIGN_PORT: +// case ControlMapperDialog::C_MCTL_NUM: +// { +// QComboBox* combo = qobject_cast(editor); +// if(combo) +// { +// int data = index.data(RouteDialog::RouteRole).toInt(); +// int idx = combo->findData(data); +// if(idx != -1) +// { +// combo->blockSignals(true); // Prevent currentIndexChanged or activated from being called +// combo->setCurrentIndex(idx); +// combo->blockSignals(false); +// } +// } +// return; +// } +// +// default: +// QStyledItemDelegate::setEditorData(editor, index); +// } +// +// // if(index.column() == ControlMapperDialog::C_NAME) +// // { +// // ColorEditor* color_editor = qobject_cast(editor); +// // if(color_editor) +// // color_editor->setColor(index.data(Qt::DecorationRole).value()); +// // } +// // else +// // if(index.column() == ControlMapperDialog::C_ASSIGN_PORT) +// // { +// // QComboBox* combo = qobject_cast(editor); +// // if(combo) +// // { +// // int data = index.data(RouteDialog::RouteRole).toInt(); +// // int idx = combo->findData(data); +// // if(idx != -1) +// // combo->setCurrentIndex(idx); +// // } +// // } +// // else +// // if(index.column() == ControlMapperDialog::C_MCTL_TYPE) +// // { +// // QComboBox* combo = qobject_cast(editor); +// // if(combo) +// // { +// // int data = index.data(RouteDialog::RouteRole).toInt(); +// // int idx = combo->findData(data); +// // if(idx != -1) +// // combo->setCurrentIndex(idx); +// // } +// // } +// // else +// // QStyledItemDelegate::setEditorData(editor, index); +// } + +void RoutingItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::setModelData\n"); // REMOVE Tim. +// if (index.data().canConvert()) { +// StarEditor *starEditor = qobject_cast(editor); +// model->setData(index, QVariant::fromValue(starEditor->starRating())); +// } else + + //if(index.column() == ControlMapperDialog::C_COLOR) + + switch(index.column()) + { +// case ControlMapperDialog::C_NAME: +// { +// ColorEditor* color_editor = qobject_cast(editor); +// if(color_editor) +// model->setData(index, color_editor->color(), Qt::DecorationRole); +// return; +// } +// +// case ControlMapperDialog::C_ASSIGN_PORT: +// case ControlMapperDialog::C_MCTL_NUM: +// { +// QComboBox* combo = qobject_cast(editor); +// if(combo) +// { +// int idx = combo->currentIndex(); +// if(idx != -1) +// { +// model->setData(index, combo->itemData(idx), RouteDialog::RouteRole); // Do this one before the text so that the tree view's itemChanged handler gets it first! +// model->blockSignals(true); +// model->setData(index, combo->itemText(idx), Qt::DisplayRole); // This will cause another handler call. Prevent it by blocking. +// model->blockSignals(false); +// } +// } +// return; +// } + + default: + QStyledItemDelegate::setModelData(editor, model, index); + } + +// if(index.column() == ControlMapperDialog::C_NAME) +// { +// ColorEditor* color_editor = qobject_cast(editor); +// if(color_editor) +// model->setData(index, color_editor->color(), Qt::DecorationRole); +// } +// else +// if(index.column() == ControlMapperDialog::C_ASSIGN_PORT) +// { +// QComboBox* combo = qobject_cast(editor); +// if(combo) +// { +// int idx = combo->currentIndex(); +// if(idx != -1) +// { +// model->setData(index, combo->itemText(idx), Qt::DisplayRole); +// model->setData(index, combo->itemData(idx), RouteDialog::RouteRole); +// } +// } +// } +// else +// if(index.column() == ControlMapperDialog::C_MCTL_TYPE) +// { +// QComboBox* combo = qobject_cast(editor); +// if(combo) +// { +// int idx = combo->currentIndex(); +// if(idx != -1) +// { +// model->setData(index, combo->itemText(idx), Qt::DisplayRole); +// model->setData(index, combo->itemData(idx), RouteDialog::RouteRole); +// } +// } +// } +// else +// QStyledItemDelegate::setModelData(editor, model, index); +} + +QSize RoutingItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ +// if (index.data().canConvert()) { +// StarRating starRating = qvariant_cast(index.data()); +// return starRating.sizeHint(); +// } else + +// if(index.column() == ControlMapperDialog::C_COLOR) +// return QSize(__COLOR_CHOOSER_ELEMENT_WIDTH__ * __COLOR_CHOOSER_NUM_COLUMNS__, +// __COLOR_CHOOSER_ELEMENT_HEIGHT__ * (__COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__)); +// + //return QStyledItemDelegate::sizeHint(option, index); + + DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::sizeHint\n"); // REMOVE Tim. + + if(RouteTreeWidgetItem* item = _tree->itemFromIndex(index)) + { +// // Required. option is not automatically filled from index. +// QStyleOptionViewItem vopt(option); +// initStyleOption(&vopt, index); + +// const QSize sz = item->getSizeHint(vopt, index); +// const QSize sz = item->getSizeHint(index.column(), _tree->wordWrap() ? _tree->viewport()->width() : -1); + //const QSize sz = item->getSizeHint(index.column(), _tree->wordWrap() ? _tree->columnWidth(RouteDialog::ROUTE_NAME_COL) : _VERY_LARGE_INTEGER_); +// const QSize sz = item->getSizeHint(index.column(), _tree->wordWrap() ? _tree->columnWidth(RouteDialog::ROUTE_NAME_COL) : -1); + const QSize sz = item->getSizeHint(index.column(), _tree->columnWidth(RouteDialog::ROUTE_NAME_COL)); + if(sz.isValid()) + { + //fprintf(stderr, "RoutingItemDelegate::sizeHint w:%d h:%d\n", sz.width(), sz.height()); // REMOVE Tim. + return sz; + } + } + return QStyledItemDelegate::sizeHint(option, index); +} + +// RouteDialog* router = qobject_cast< RouteDialog* >(parent()); +// if(router) +// { +// if(index.column() == RouteDialog::ROUTE_NAME_COL && index.data(RouteDialog::RouteRole).canConvert()) +// { +// MusECore::Route r = qvariant_cast(index.data(RouteDialog::RouteRole)); +// switch(r.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// if(r.track && r.channel != -1) +// { +// int chans; +// if(r.track->isMidiTrack()) +// chans = MIDI_CHANNELS; +// else +// { +// MusECore::AudioTrack* atrack = static_cast(r.track); +// if(atrack->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// { +// if(_isInput) +// chans = atrack->totalOutChannels(); +// else +// chans = atrack->totalInChannels(); +// } +// else +// chans = atrack->channels(); +// } +// int w = RouteDialog::midiDotsMargin * 2 + RouteDialog::midiDotDiameter * chans; +// if(chans > 1) +// w += RouteDialog::midiDotSpacing * (chans - 1); +// if(chans > 4) +// w += RouteDialog::midiDotGroupSpacing * (chans - 1) / 4; +// const int h = RouteDialog::midiDotDiameter + RouteDialog::midiDotsMargin * 2 + chans; +// return QSize(w, h); +// } +// break; +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// case MusECore::Route::JACK_ROUTE: +// break; +// } +// } +// } +// return QStyledItemDelegate::sizeHint(option, index); +// } + +// bool RoutingItemDelegate::testForRelayout(const QStyleOptionViewItem &option, const QModelIndex& index, int old_width, int new_width) +// { +// if(index.column() == RouteDialog::ROUTE_NAME_COL) +// { +// //index.data(RouteTreeWidgetItem::TypeRole); +// +// QStyleOptionViewItem vopt(option); +// +// vopt.rect = QRect(vopt.rect.x(), vopt.rect.y(), old_width, -1); +// const QSize old_sz = sizeHint(vopt, index); +// vopt.rect = QRect(vopt.rect.x(), vopt.rect.y(), new_width, -1); +// const QSize new_sz = sizeHint(vopt, index); +// +// //return old_sz.isValid() && new_sz.isValid() && old_sz.height() != new_sz.height(); +// //return old_sz.isValid() && new_sz.isValid() && old_sz != new_sz; +// +// //if(old_sz != new_sz) +// if(old_sz.height() != new_sz.height()) +// { +// // emit sizeHintChanged(index); +// return true; +// } +// } +// return false; +// } + + + +bool RoutingItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) +{ +// if(event->type() == QEvent::MouseMove) +// { +// QMouseEvent* me = static_cast(event); +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::editorEvent: Move X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. +// // If any buttons down, ignore. +// // if(me->buttons() != Qt::NoButton) +// // { +// // event->accept(); +// // return true; +// // } +// } +// else +// if(event->type() == QEvent::MouseButtonPress) +// { +// QMouseEvent* me = static_cast(event); +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::editorEvent: Press X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. +// +// // _firstPress = false; // HACK +// // +// // QStyle::SubElement sub_element; +// // if(subElementHitTest(me->pos(), option, index, &sub_element)) +// // _currentSubElement = sub_element; +// // //event->accept(); +// // //return true; +// } +// else +// if(event->type() == QEvent::MouseButtonRelease) +// { +// QMouseEvent* me = static_cast(event); +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::editorEvent: Release X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. +// +// // // If the element under the mouse is not the one when pressed, eat up these events because +// // // they trigger the editor or action of the element under the mouse at the release position. +// // QStyle::SubElement sub_element = _currentSubElement; +// // if(!subElementHitTest(me->pos(), option, index, &sub_element) || sub_element != _currentSubElement) +// // //QRect r = getItemRectangle(option, index, QStyle::SE_ItemViewItemDecoration); +// // //if(!subElementHitTest(me->pos(), option, index, &sub_element) || +// // // (sub_element != QStyle::SE_ItemViewItemCheckIndicator && sub_element != QStyle::SE_ItemViewItemDecoration)) +// // //if(r.isValid()) +// // { +// // event->accept(); +// // return true; +// // } +// } +// else +// if(event->type() == QEvent::Close) +// { +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::editorEvent: Close\n"); // REMOVE Tim. +// } +// else +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::editorEvent: event type:%d\n", event->type()); // REMOVE Tim. + + +// switch(index.column()) +// { +// case ControlMapperDialog::C_SHOW: +// // This is checkbox column. No editable info. +// //event->accept(); +// //return true; +// //return false; +// return QStyledItemDelegate::editorEvent(event, model, option, index); +// +// case ControlMapperDialog::C_NAME: +// // This is non-editable name. +// event->accept(); +// return true; +// +// case ControlMapperDialog::C_COLOR: +// { +// if(event->type() == QEvent::MouseButtonRelease) +// { +// QMouseEvent* me = static_cast(event); +// DEBUG_PRST_ROUTES(stderr, " X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. +// +// } +// +// event->accept(); +// return true; +// } +// +// case ControlMapperDialog::C_ASSIGN: +// // This is editable assigned input controller. +// return false; +// +// case ControlMapperDialog::C_MCTL_TYPE: +// // This is editable midi control type. +// return false; +// +// case ControlMapperDialog::C_MCTL_H: +// // This is editable midi control num high. +// return false; +// +// case ControlMapperDialog::C_MCTL_L: +// // This is editable midi control num low. +// return false; +// } +// +// return false; + + return QStyledItemDelegate::editorEvent(event, model, option, index); +} + + +bool RoutingItemDelegate::eventFilter(QObject* editor, QEvent* event) +{ +// if(event->type() == QEvent::MouseButtonPress) +// { +// QMouseEvent* me = static_cast(event); +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::eventFilter: Press X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. +// //event->accept(); +// //return true; +// } +// else +// if(event->type() == QEvent::MouseButtonRelease) +// { +// QMouseEvent* me = static_cast(event); +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::eventFilter: Release X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. +// //event->accept(); +// //return true; +// } +// else +// if(event->type() == QEvent::Close) +// { +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::eventFilter: Close\n"); // REMOVE Tim. +// } +// else +// DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::eventFilter: event type:%d\n", event->type()); // REMOVE Tim. + + return QStyledItemDelegate::eventFilter(editor, event); +} + + + + + //--------------------------------------------------------- // RouteDialog //--------------------------------------------------------- RouteDialog::RouteDialog(QWidget* parent) : QDialog(parent) +{ + setupUi(this); + +// newSrcList->setWordWrap(false); +// newDstList->setWordWrap(false); +// routeList->setWordWrap(false); +// newSrcList->setTextElideMode(Qt::ElideNone); +// newDstList->setTextElideMode(Qt::ElideNone); +// routeList->setTextElideMode(Qt::ElideNone); + + filterSrcButton->setIcon(*routerFilterSourceIcon); + filterDstButton->setIcon(*routerFilterDestinationIcon); + srcRoutesButton->setIcon(*routerFilterSourceRoutesIcon); + dstRoutesButton->setIcon(*routerFilterDestinationRoutesIcon); + allMidiPortsButton->setIcon(*settings_midiport_softsynthsIcon); + verticalLayoutButton->setIcon(*routerViewSplitterIcon); + + routeAliasList->addItem(tr("Normal"), QVariant::fromValue(MusEGlobal::RoutePreferCanonicalName)); + routeAliasList->addItem(tr("Alias 1"), QVariant::fromValue(MusEGlobal::RoutePreferFirstAlias)); + routeAliasList->addItem(tr("Alias 2"), QVariant::fromValue(MusEGlobal::RoutePreferSecondAlias)); + +// verticalLayoutButton->setChecked(newSrcList->wordWrap() || newDstList->wordWrap()); + + //newSrcList->viewport()->setLayoutDirection(Qt::LeftToRight); + + //_srcFilterItem = NULL; + //_dstFilterItem = NULL; + + srcItemDelegate = new RoutingItemDelegate(true, newSrcList, this); + dstItemDelegate = new RoutingItemDelegate(false, newDstList, this); + + newSrcList->setItemDelegate(srcItemDelegate); + newDstList->setItemDelegate(dstItemDelegate); + + //newSrcList->setItemsExpandable(false); // REMOVE Tim. For test only. + //newDstList->setItemsExpandable(false); // REMOVE Tim. For test only. + + connectionsWidget->setRouteDialog(this); + + QStringList columnnames; + columnnames << tr("Source"); + newSrcList->setColumnCount(columnnames.size()); + newSrcList->setHeaderLabels(columnnames); + for (int i = 0; i < columnnames.size(); ++i) { + //setWhatsThis(newSrcList->horizontalHeaderItem(i), i); + //setToolTip(newSrcList->horizontalHeaderItem(i), i); + } + + columnnames.clear(); + columnnames << tr("Destination"); + newDstList->setColumnCount(columnnames.size()); + newDstList->setHeaderLabels(columnnames); + for (int i = 0; i < columnnames.size(); ++i) { + //setWhatsThis(newDstList->horizontalHeaderItem(i), i); + //setToolTip(newDstList->horizontalHeaderItem(i), i); + } + + newSrcList->setTreePosition(1); + newDstList->setTreePosition(1); + + // Need this. Don't remove. +// newSrcList->header()->setSectionResizeMode(QHeaderView::Stretch); +// newDstList->header()->setSectionResizeMode(QHeaderView::Stretch); +// newSrcList->header()->setSectionResizeMode(QHeaderView::Interactive); +// newDstList->header()->setSectionResizeMode(QHeaderView::Interactive); + + //newSrcList->header()->setStretchLastSection(false); + //newDstList->header()->setStretchLastSection(false); + + newSrcList->setTextElideMode(Qt::ElideMiddle); + newDstList->setTextElideMode(Qt::ElideMiddle); + + + columnnames.clear(); + columnnames << tr("Source") + << tr("Destination"); + routeList->setColumnCount(columnnames.size()); + routeList->setHeaderLabels(columnnames); + for (int i = 0; i < columnnames.size(); ++i) { + //setWhatsThis(routeList->horizontalHeaderItem(i), i); + //setToolTip(routeList->horizontalHeaderItem(i), i); + } + + // Make it so that the column(s) cannot be shrunk below the size of one group of channels in a ChannelsItem. + newSrcList->header()->setMinimumSectionSize(RouteChannelsList::minimumWidthHint()); + newDstList->header()->setMinimumSectionSize(RouteChannelsList::minimumWidthHint()); + + verticalLayoutButton->setChecked(MusEGlobal::config.routerExpandVertically); + if(MusEGlobal::config.routerExpandVertically) + { +// newSrcList->resizeColumnToContents(ROUTE_NAME_COL); +// newDstList->resizeColumnToContents(ROUTE_NAME_COL); + newSrcList->setWordWrap(true); + newDstList->setWordWrap(true); + newSrcList->setChannelWrap(true); + newDstList->setChannelWrap(true); + newSrcList->header()->setSectionResizeMode(QHeaderView::Stretch); + newDstList->header()->setSectionResizeMode(QHeaderView::Stretch); + newSrcList->setColumnWidth(ROUTE_NAME_COL, RouteChannelsList::minimumWidthHint()); + newDstList->setColumnWidth(ROUTE_NAME_COL, RouteChannelsList::minimumWidthHint()); + } + else + { + newSrcList->setWordWrap(false); + newDstList->setWordWrap(false); + newSrcList->setChannelWrap(true); + newDstList->setChannelWrap(true); + newSrcList->header()->setSectionResizeMode(QHeaderView::Interactive); + newDstList->header()->setSectionResizeMode(QHeaderView::Interactive); + } + + songChanged(SC_EVERYTHING); + + connect(newSrcList->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), srcTreeScrollBar, SLOT(setRange(int,int))); + connect(newDstList->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), dstTreeScrollBar, SLOT(setRange(int,int))); + connect(newSrcList->verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(srcTreeScrollValueChanged(int))); + connect(newDstList->verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(dstTreeScrollValueChanged(int))); + connect(srcTreeScrollBar, SIGNAL(valueChanged(int)), SLOT(srcScrollBarValueChanged(int))); + connect(dstTreeScrollBar, SIGNAL(valueChanged(int)), SLOT(dstScrollBarValueChanged(int))); + + connect(routeList, SIGNAL(itemSelectionChanged()), SLOT(routeSelectionChanged())); + connect(newSrcList, SIGNAL(itemSelectionChanged()), SLOT(srcSelectionChanged())); + connect(newDstList, SIGNAL(itemSelectionChanged()), SLOT(dstSelectionChanged())); + //connect(newSrcList->verticalScrollBar(), SIGNAL(sliderMoved(int)), connectionsWidget, SLOT(update())); + //connect(newDstList->verticalScrollBar(), SIGNAL(sliderMoved(int)), connectionsWidget, SLOT(update())); + connect(newSrcList->verticalScrollBar(), SIGNAL(valueChanged(int)), connectionsWidget, SLOT(update())); + connect(newDstList->verticalScrollBar(), SIGNAL(valueChanged(int)), connectionsWidget, SLOT(update())); + connect(newSrcList, SIGNAL(itemCollapsed(QTreeWidgetItem*)), connectionsWidget, SLOT(update())); + connect(newSrcList, SIGNAL(itemExpanded(QTreeWidgetItem*)), connectionsWidget, SLOT(update())); + connect(newDstList, SIGNAL(itemCollapsed(QTreeWidgetItem*)), connectionsWidget, SLOT(update())); + connect(newDstList, SIGNAL(itemExpanded(QTreeWidgetItem*)), connectionsWidget, SLOT(update())); + connect(connectionsWidget, SIGNAL(scrollBy(int, int)), newSrcList, SLOT(scrollBy(int, int))); + connect(connectionsWidget, SIGNAL(scrollBy(int, int)), newDstList, SLOT(scrollBy(int, int))); + connect(removeButton, SIGNAL(clicked()), SLOT(disconnectClicked())); + connect(connectButton, SIGNAL(clicked()), SLOT(connectClicked())); + connect(allMidiPortsButton, SIGNAL(clicked(bool)), SLOT(allMidiPortsClicked(bool))); + connect(verticalLayoutButton, SIGNAL(clicked(bool)), SLOT(verticalLayoutClicked(bool))); + connect(filterSrcButton, SIGNAL(clicked(bool)), SLOT(filterSrcClicked(bool))); + connect(filterDstButton, SIGNAL(clicked(bool)), SLOT(filterDstClicked(bool))); + connect(srcRoutesButton, SIGNAL(clicked(bool)), SLOT(filterSrcRoutesClicked(bool))); + connect(dstRoutesButton, SIGNAL(clicked(bool)), SLOT(filterDstRoutesClicked(bool))); + connect(routeAliasList, SIGNAL(activated(int)), SLOT(preferredRouteAliasChanged(int))); + connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); +} + +void RouteDialog::srcTreeScrollValueChanged(int value) +{ + // Prevent recursion + srcTreeScrollBar->blockSignals(true); + srcTreeScrollBar->setValue(value); + srcTreeScrollBar->blockSignals(false); +} + +void RouteDialog::dstTreeScrollValueChanged(int value) +{ + // Prevent recursion + dstTreeScrollBar->blockSignals(true); + dstTreeScrollBar->setValue(value); + dstTreeScrollBar->blockSignals(false); +} + +void RouteDialog::srcScrollBarValueChanged(int value) +{ + // Prevent recursion + newSrcList->blockSignals(true); + newSrcList->verticalScrollBar()->setValue(value); + newSrcList->blockSignals(false); +} + +void RouteDialog::dstScrollBarValueChanged(int value) +{ + // Prevent recursion + newDstList->blockSignals(true); + newDstList->verticalScrollBar()->setValue(value); + newDstList->blockSignals(false); +} + +// void RouteDialog::routeSplitterMoved(int pos, int index) +// { +// DEBUG_PRST_ROUTES(stderr, "RouteDialog::routeSplitterMoved pos:%d index:%d\n", pos, index); // REMOVE Tim. +// +// } + + +void RouteDialog::preferredRouteAliasChanged(int /*idx*/) +{ + if(routeAliasList->currentData().canConvert()) + { + bool ok = false; + const int n = routeAliasList->currentData().toInt(&ok); + if(ok) + { + switch(n) { - setupUi(this); - connect(routeList, SIGNAL(itemSelectionChanged()), SLOT(routeSelectionChanged())); - connect(newSrcList, SIGNAL(itemSelectionChanged()), SLOT(srcSelectionChanged())); - connect(newDstList, SIGNAL(itemSelectionChanged()), SLOT(dstSelectionChanged())); - connect(removeButton, SIGNAL(clicked()), SLOT(removeRoute())); - connect(connectButton, SIGNAL(clicked()), SLOT(addRoute())); - connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); - routingChanged(); + case MusEGlobal::RoutePreferCanonicalName: + case MusEGlobal::RoutePreferFirstAlias: + case MusEGlobal::RoutePreferSecondAlias: + MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RouteNameAliasPreference(n); + MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); + break; + default: + break; } + } + } +} -//--------------------------------------------------------- -// routingChanged -//--------------------------------------------------------- +void RouteDialog::verticalLayoutClicked(bool v) +{ + if(v) + { +// fprintf(stderr, "RouteDialog::verticalLayoutClicked v:%d calling src resizeColumnToContents\n", v); // REMOVE Tim. +// newSrcList->resizeColumnToContents(ROUTE_NAME_COL); +// fprintf(stderr, "RouteDialog::verticalLayoutClicked v:%d calling dst resizeColumnToContents\n", v); // REMOVE Tim. +// newDstList->resizeColumnToContents(ROUTE_NAME_COL); + MusEGlobal::config.routerExpandVertically = v; + newSrcList->setWordWrap(v); + newDstList->setWordWrap(v); + newSrcList->setChannelWrap(v); + newDstList->setChannelWrap(v); + newSrcList->header()->setSectionResizeMode(QHeaderView::Stretch); + newDstList->header()->setSectionResizeMode(QHeaderView::Stretch); + newSrcList->setColumnWidth(ROUTE_NAME_COL, RouteChannelsList::minimumWidthHint()); + newDstList->setColumnWidth(ROUTE_NAME_COL, RouteChannelsList::minimumWidthHint()); + } + else + { + MusEGlobal::config.routerExpandVertically = v; + newSrcList->setWordWrap(v); + newDstList->setWordWrap(v); + newSrcList->setChannelWrap(true); + newDstList->setChannelWrap(true); + newSrcList->header()->setSectionResizeMode(QHeaderView::Interactive); + newDstList->header()->setSectionResizeMode(QHeaderView::Interactive); + } + //fprintf(stderr, "RouteDialog::verticalLayoutClicked v:%d calling dst computeChannelYValues\n", v); // REMOVE Tim. + newDstList->computeChannelYValues(); + newSrcList->computeChannelYValues(); + //fprintf(stderr, "RouteDialog::verticalLayoutClicked v:%d calling src computeChannelYValues\n", v); // REMOVE Tim. + connectionsWidget->update(); // Redraw the connections. +} + +void RouteDialog::allMidiPortsClicked(bool v) +{ + // TODO: This is a bit brutal and sweeping... Refine this down to needed parts only. +// routingChanged(); + + + // Refill the lists of available external ports. +// tmpJackOutPorts = MusEGlobal::audioDevice->outputPorts(); +// tmpJackInPorts = MusEGlobal::audioDevice->inputPorts(); +// tmpJackMidiOutPorts = MusEGlobal::audioDevice->outputPorts(true); +// tmpJackMidiInPorts = MusEGlobal::audioDevice->inputPorts(true); + if(v) + addItems(); // Add any new items. + else + removeItems(); // Remove unused items. + +// newSrcList->resizeColumnToContents(ROUTE_NAME_COL); +// newDstList->resizeColumnToContents(ROUTE_NAME_COL); + routeList->resizeColumnToContents(ROUTE_SRC_COL); + routeList->resizeColumnToContents(ROUTE_DST_COL); + + // Now that column resizing is done, update all channel y values in source and destination lists. + // Must be done here because it relies on the column width. + newDstList->computeChannelYValues(); + newSrcList->computeChannelYValues(); + + routeSelectionChanged(); // Init remove button. + srcSelectionChanged(); // Init select button. + connectionsWidget->update(); // Redraw the connections. +} + +void RouteDialog::filterSrcClicked(bool v) +{ +// if(v) +// { +// RouteTreeWidgetItem* item = static_cast(newSrcList->currentItem()); +// filter(item, false); +// } +// else +// filter(NULL, false); + + + //if(v) + // _srcFilterItems = newSrcList->selectedItems(); + //else + // _srcFilterItems.clear(); + + if(dstRoutesButton->isChecked()) + { + dstRoutesButton->blockSignals(true); + dstRoutesButton->setChecked(false); + dstRoutesButton->blockSignals(false); + } + filter(v ? newSrcList->selectedItems() : RouteTreeItemList(), RouteTreeItemList(), true, false); +// if(v) +// { +// //if(dstRoutesButton->isEnabled()) +// // dstRoutesButton->setEnabled(false); +// filter(newSrcList->selectedItems(), RouteTreeItemList(), true, false); +// } +// else +// { +// //if(!dstRoutesButton->isEnabled()) +// // dstRoutesButton->setEnabled(true); +// filter(RouteTreeItemList(), RouteTreeItemList(), true, false); +// } +} + +void RouteDialog::filterDstClicked(bool v) +{ +// if(v) +// { +// RouteTreeWidgetItem* item = static_cast(newDstList->currentItem()); +// filter(item, true); +// } +// else +// filter(NULL, true); + +// if(v) +// _dstFilterItems = newDstList->selectedItems(); +// else +// _dstFilterItems.clear(); +// +// filter(); + + if(srcRoutesButton->isChecked()) + { + srcRoutesButton->blockSignals(true); + srcRoutesButton->setChecked(false); + srcRoutesButton->blockSignals(false); + } + filter(RouteTreeItemList(), v ? newDstList->selectedItems() : RouteTreeItemList(), false, true); +// if(v) +// { +// //if(srcRoutesButton->isEnabled()) +// // srcRoutesButton->setEnabled(false); +// filter(RouteTreeItemList(), newDstList->selectedItems(), false, true); +// } +// else +// { +// //if(!srcRoutesButton->isEnabled()) +// // srcRoutesButton->setEnabled(true); +// filter(RouteTreeItemList(), RouteTreeItemList(), false, true); +// } +} -void RouteDialog::routingChanged() +void RouteDialog::filterSrcRoutesClicked(bool /*v*/) +{ +// if(v) +// { +// RouteTreeWidgetItem* item = static_cast(newSrcList->currentItem()); +// filter(item, false); +// } +// else +// filter(NULL, false); + + +// if(v) +// _srcFilterItems = newSrcList->selectedItems(); +// else +// _srcFilterItems.clear(); +// +// filter(); + + if(dstRoutesButton->isChecked()) + { + dstRoutesButton->blockSignals(true); + dstRoutesButton->setChecked(false); + dstRoutesButton->blockSignals(false); + } + if(filterDstButton->isChecked()) + { + filterDstButton->blockSignals(true); + filterDstButton->setChecked(false); + filterDstButton->blockSignals(false); + } + // Unfilter the entire destination list, while (un)filtering with the 'show only possible routes' part. + filter(RouteTreeItemList(), RouteTreeItemList(), false, true); +// if(v) +// { +// //if(filterSrcButton->isEnabled()) +// // filterSrcButton->setEnabled(false); +// filter(RouteTreeItemList(), newDstList->selectedItems(), false, true); +// } +// else +// { +// //if(!filterSrcButton->isEnabled()) +// // filterSrcButton->setEnabled(true); +// filter(RouteTreeItemList(), RouteTreeItemList(), false, true); +// } +} + +void RouteDialog::filterDstRoutesClicked(bool /*v*/) +{ +// if(v) +// { +// RouteTreeWidgetItem* item = static_cast(newDstList->currentItem()); +// filter(item, true); +// } +// else +// filter(NULL, true); + +// if(v) +// _dstFilterItems = newDstList->selectedItems(); +// else +// _dstFilterItems.clear(); +// +// filter(); +// filter(RouteTreeItemList(), +// filterDstButton->isChecked() ? newDstList->selectedItems() : RouteTreeItemList(), +// false, true); + + if(srcRoutesButton->isChecked()) + { + srcRoutesButton->blockSignals(true); + srcRoutesButton->setChecked(false); + srcRoutesButton->blockSignals(false); + } + if(filterSrcButton->isChecked()) + { + filterSrcButton->blockSignals(true); + filterSrcButton->setChecked(false); + filterSrcButton->blockSignals(false); + } + // Unfilter the entire source list, while (un)filtering with the 'show only possible routes' part. + filter(RouteTreeItemList(), RouteTreeItemList(), true, false); +} + +void RouteDialog::filter(const RouteTreeItemList& srcFilterItems, + const RouteTreeItemList& dstFilterItems, + bool filterSrc, + bool filterDst) +{ + bool src_changed = false; + bool dst_changed = false; + const RouteTreeItemList src_sel_items = newSrcList->selectedItems(); + const RouteTreeItemList dst_sel_items = newDstList->selectedItems(); + bool hide; + + QTreeWidgetItemIterator iSrcTree(newSrcList); + while(*iSrcTree) + { + QTreeWidgetItem* item = *iSrcTree; + hide = item->isHidden(); + + if(filterSrc) + { + hide = false; + if(!srcFilterItems.isEmpty()) { - //--------------------------------------------------- - // populate lists - //--------------------------------------------------- + RouteTreeItemList::const_iterator ciFilterItems = srcFilterItems.cbegin(); + for( ; ciFilterItems != srcFilterItems.cend(); ++ciFilterItems) + { + QTreeWidgetItem* flt_item = *ciFilterItems; + QTreeWidgetItem* twi = flt_item; + while(twi != item && (twi = twi->parent())) ; + + if(twi == item) + break; + QTreeWidgetItem* twi_p = item; + while(twi_p != flt_item && (twi_p = twi_p->parent())) ; - routeList->clear(); - newSrcList->clear(); - newDstList->clear(); + if(twi_p == flt_item) + break; + } + hide = ciFilterItems == srcFilterItems.cend(); + } + } + //else + + if(!filterSrc || srcFilterItems.isEmpty()) + { + hide = false; + //// Is the item slated to hide? Check finally the 'show only possible routes' settings... + //if(hide && dstRoutesButton->isChecked()) + // Check finally the 'show only possible routes' settings... + if(dstRoutesButton->isChecked()) + { + switch(item->type()) + { + case RouteTreeWidgetItem::NormalItem: + case RouteTreeWidgetItem::CategoryItem: + hide = true; + break; + + case RouteTreeWidgetItem::RouteItem: + case RouteTreeWidgetItem::ChannelsItem: + { + RouteTreeWidgetItem* rtwi = static_cast(item); + RouteTreeItemList::const_iterator iSelItems = dst_sel_items.cbegin(); + for( ; iSelItems != dst_sel_items.cend(); ++iSelItems) + { + RouteTreeWidgetItem* sel_dst_item = static_cast(*iSelItems); + //if(sel_dst_item->type() == item->type() && MusECore::routesCompatible(rtwi->route(), sel_dst_item->route(), false)) + if(MusECore::routesCompatible(rtwi->route(), sel_dst_item->route(), true)) + { + //hide = false; + // Hm, Qt doesn't seem to do this for us: + QTreeWidgetItem* twi = item; + while((twi = twi->parent())) + { + //if(twi->isHidden() != hide) + if(twi->isHidden()) + { + //twi->setHidden(hide); + twi->setHidden(false); + src_changed = true; + } + } + break; + } + } + hide = iSelItems == dst_sel_items.cend(); + } + break; + } + } + } + + if(item->isHidden() != hide) + { + item->setHidden(hide); + src_changed = true; + } + + ++iSrcTree; + } - MusECore::TrackList* tl = MusEGlobal::song->tracks(); - for (MusECore::ciTrack i = tl->begin(); i != tl->end(); ++i) { - if ((*i)->isMidiTrack()) - continue; - // p3.3.38 - //WaveTrack* track = (WaveTrack*)(*i); - MusECore::AudioTrack* track = (MusECore::AudioTrack*)(*i); - if (track->type() == MusECore::Track::AUDIO_INPUT) { - for (int channel = 0; channel < track->channels(); ++channel) - newDstList->addItem(MusECore::Route(track, channel).name()); - const MusECore::RouteList* rl = track->inRoutes(); - for (MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) { - //MusECore::Route dst(track->name(), true, r->channel); - MusECore::Route dst(track->name(), true, r->channel, MusECore::Route::TRACK_ROUTE); - new QTreeWidgetItem(routeList, QStringList() << r->name() << dst.name()); - } - } - else if (track->type() != MusECore::Track::AUDIO_AUX) - newDstList->addItem(MusECore::Route(track, -1).name()); - if (track->type() == MusECore::Track::AUDIO_OUTPUT) { - for (int channel = 0; channel < track->channels(); ++channel) { - MusECore::Route r(track, channel); - newSrcList->addItem(r.name()); - } - } - else - newSrcList->addItem(MusECore::Route(track, -1).name()); - - const MusECore::RouteList* rl = track->outRoutes(); - for (MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) { - QString src(track->name()); - if (track->type() == MusECore::Track::AUDIO_OUTPUT) { - MusECore::Route s(src, false, r->channel); - src = s.name(); - } - new QTreeWidgetItem(routeList, QStringList() << src << r->name()); - } - } - if (!MusEGlobal::checkAudioDevice()) return; - std::list sl = MusEGlobal::audioDevice->outputPorts(); - for (std::list::iterator i = sl.begin(); i != sl.end(); ++i) - newSrcList->addItem(*i); - sl = MusEGlobal::audioDevice->inputPorts(); - for (std::list::iterator i = sl.begin(); i != sl.end(); ++i) - newDstList->addItem(*i); - routeSelectionChanged(); // init remove button - srcSelectionChanged(); // init select button + + QTreeWidgetItemIterator iDstTree(newDstList); + while(*iDstTree) + { + QTreeWidgetItem* item = *iDstTree; + hide = item->isHidden(); + + if(filterDst) + { + hide = false; + if(!dstFilterItems.isEmpty()) + { + RouteTreeItemList::const_iterator ciFilterItems = dstFilterItems.cbegin(); + for( ; ciFilterItems != dstFilterItems.cend(); ++ciFilterItems) + { + QTreeWidgetItem* flt_item = *ciFilterItems; + QTreeWidgetItem* twi = flt_item; + while(twi != item && (twi = twi->parent())) ; + + if(twi == item) + break; + QTreeWidgetItem* twi_p = item; + while(twi_p != flt_item && (twi_p = twi_p->parent())) ; + + if(twi_p == flt_item) + break; + } + hide = ciFilterItems == dstFilterItems.cend(); + } + } + //else + + if(!filterDst || dstFilterItems.isEmpty()) + { + hide = false; + //// Is the item slated to hide? Check finally the 'show only possible routes' settings... + //if(hide && srcRoutesButton->isChecked()) + // Check finally the 'show only possible routes' settings... + if(srcRoutesButton->isChecked()) + { + switch(item->type()) + { + case RouteTreeWidgetItem::NormalItem: + case RouteTreeWidgetItem::CategoryItem: + hide = true; + break; + + case RouteTreeWidgetItem::RouteItem: + case RouteTreeWidgetItem::ChannelsItem: + { + RouteTreeWidgetItem* rtwi = static_cast(item); + RouteTreeItemList::const_iterator iSelItems = src_sel_items.cbegin(); + for( ; iSelItems != src_sel_items.cend(); ++iSelItems) + { + RouteTreeWidgetItem* sel_src_item = static_cast(*iSelItems); + + MusECore::Route& src = sel_src_item->route(); + MusECore::Route& dst = rtwi->route(); + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ +// Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist +// but not in any track or port routelist) until multiple output routes are allowed +// instead of just single port and channel properties. The route is exclusive. + bool is_compatible = false; + switch(src.type) + { + case MusECore::Route::TRACK_ROUTE: + switch(dst.type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + if(src.track->isMidiTrack()) + is_compatible = true; + break; + + case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; + } + break; + + case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; + } + + if(is_compatible || +#else + if( +#endif + MusECore::routesCompatible(sel_src_item->route(), rtwi->route(), true)) + { + //hide = false; + // Hm, Qt doesn't seem to do this for us: + QTreeWidgetItem* twi = item; + while((twi = twi->parent())) + { + //if(twi->isHidden() != hide) + if(twi->isHidden()) + { + //twi->setHidden(hide); + twi->setHidden(false); + dst_changed = true; + } + } + break; + } + } + hide = iSelItems == src_sel_items.cend(); + } + break; + } } + } + + if(item->isHidden() != hide) + { + item->setHidden(hide); + dst_changed = true; + } + + ++iDstTree; + } + + + + // Update the connecting lines. + if(src_changed) + { +// QTreeWidgetItemIterator iSrcList(newSrcList); +// while(*iSrcList) +// { +// RouteTreeWidgetItem* item = static_cast(*iSrcList); +// item->computeChannelYValues(); +// ++iSrcList; +// } + newSrcList->computeChannelYValues(); + } + if(dst_changed) + { +// QTreeWidgetItemIterator iDstList(newDstList); +// while(*iDstList) +// { +// RouteTreeWidgetItem* item = static_cast(*iDstList); +// item->computeChannelYValues(); +// ++iDstList; +// } + newDstList->computeChannelYValues(); + } + + if(src_changed || dst_changed) + connectionsWidget->update(); // Redraw the connections. + + QTreeWidgetItemIterator iRouteTree(routeList); +// //RouteTreeItemList::iterator iFilterItems; +// //filterItems::iterator iFilterItems; +// + while(*iRouteTree) + { + QTreeWidgetItem* item = *iRouteTree; + //if(item->data(isDestination ? RouteDialog::ROUTE_DST_COL : RouteDialog::ROUTE_SRC_COL, + // RouteDialog::RouteRole).canConvert()) + if(item && item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) + { + //const MusECore::Route r = item->data(isDestination ? RouteDialog::ROUTE_DST_COL : RouteDialog::ROUTE_SRC_COL, + // RouteDialog::RouteRole).value(); + const MusECore::Route src = item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value(); + const MusECore::Route dst = item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value(); + +// bool hide = false; +// +// //if(_srcFilterItems.isEmpty()) +// // item->setHidden(false); +// //else +// //{ +// RouteTreeItemList::const_iterator ciFilterItems = _srcFilterItems.begin(); +// for( ; ciFilterItems != _srcFilterItems.end(); ++ciFilterItems) +// if(src.compare(static_cast(*ciFilterItems)->route())) // FIXME Ugly +// break; +// if(ciFilterItems == _srcFilterItems.end()) +// hide = true; +// item->setHidden(ciFilterItems == filterItems.end()); +// //} + + RouteTreeWidgetItem* src_item = newSrcList->findItem(src); + RouteTreeWidgetItem* dst_item = newDstList->findItem(dst); + item->setHidden((src_item && src_item->isHidden()) || (dst_item && dst_item->isHidden())); + } + ++iRouteTree; + } + + //routingChanged(); +} //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void RouteDialog::songChanged(MusECore::SongChangedFlags_t v) - { - if (v & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_ROUTE)) { - routingChanged(); - } - } +{ + if(v & SC_PORT_ALIAS_PREFERENCE) + { + const int idx = routeAliasList->findData(QVariant::fromValue(MusEGlobal::config.preferredRouteNameOrAlias)); + if(idx != -1 && idx != routeAliasList->currentIndex()) + { + routeAliasList->blockSignals(true); + routeAliasList->setCurrentIndex(idx); + routeAliasList->blockSignals(false); + } + } + + if(v & (SC_ROUTE | SC_CONFIG)) + { + // Refill the lists of available external ports. + tmpJackOutPorts = MusEGlobal::audioDevice->outputPorts(); + tmpJackInPorts = MusEGlobal::audioDevice->inputPorts(); + tmpJackMidiOutPorts = MusEGlobal::audioDevice->outputPorts(true); + tmpJackMidiInPorts = MusEGlobal::audioDevice->inputPorts(true); + } + + if(v & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | + SC_ROUTE | SC_CONFIG | SC_CHANNELS | SC_PORT_ALIAS_PREFERENCE)) + { + removeItems(); // Remove unused items. + addItems(); // Add any new items. + routeList->resizeColumnToContents(ROUTE_SRC_COL); + routeList->resizeColumnToContents(ROUTE_DST_COL); + + // Now that column resizing is done, update all channel y values in source and destination lists. + newDstList->computeChannelYValues(); + newSrcList->computeChannelYValues(); +// newDstList->scheduleDelayedLayout(); +// newSrcList->scheduleDelayedLayout(); + + routeSelectionChanged(); // Init remove button. + srcSelectionChanged(); // Init select button. + connectionsWidget->update(); // Redraw the connections. + } +} //--------------------------------------------------------- // routeSelectionChanged //--------------------------------------------------------- void RouteDialog::routeSelectionChanged() +{ + QTreeWidgetItem* item = routeList->currentItem(); + if(item == 0) + { + connectButton->setEnabled(false); + removeButton->setEnabled(false); + return; + } + if(!item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() || !item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) + { + connectButton->setEnabled(false); + removeButton->setEnabled(false); + return; + } + const MusECore::Route src = item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value(); + const MusECore::Route dst = item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value(); + RouteTreeWidgetItem* srcItem = newSrcList->findItem(src); + RouteTreeWidgetItem* dstItem = newDstList->findItem(dst); + newSrcList->blockSignals(true); + newSrcList->setCurrentItem(srcItem); + newSrcList->blockSignals(false); + newDstList->blockSignals(true); + newDstList->setCurrentItem(dstItem); + newDstList->blockSignals(false); + selectRoutes(true); + if(srcItem) + newSrcList->scrollToItem(srcItem, QAbstractItemView::PositionAtCenter); + //newSrcList->scrollToItem(srcItem, QAbstractItemView::EnsureVisible); + if(dstItem) + newDstList->scrollToItem(dstItem, QAbstractItemView::PositionAtCenter); + //newDstList->scrollToItem(dstItem, QAbstractItemView::EnsureVisible); + connectionsWidget->update(); +// connectButton->setEnabled(MusECore::routeCanConnect(src, dst)); + connectButton->setEnabled(false); +// removeButton->setEnabled(MusECore::routeCanDisconnect(src, dst)); +// removeButton->setEnabled(true); + + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ +// Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist +// but not in any track or port routelist) until multiple output routes are allowed +// instead of just single port and channel properties. The route is exclusive. + switch(src.type) { - QTreeWidgetItem* item = routeList->currentItem(); - removeButton->setEnabled(item != 0); + case MusECore::Route::TRACK_ROUTE: + switch(dst.type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + if(src.track->isMidiTrack()) + { + MusECore::MidiTrack* mt = static_cast(src.track); + // We cannot 'remove' a simulated midi track output port and channel route. + // (Midi port cannot be -1 meaning 'no port'.) + // Only remove it if it's a different port or channel. + removeButton->setEnabled(mt->outPort() != dst.midiPort || mt->outChannel() != src.channel); + return; + } + break; + + case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; + } + break; + + case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; } +#endif + + removeButton->setEnabled(true); + +} //--------------------------------------------------------- -// removeRoute +// disconnectClicked //--------------------------------------------------------- -void RouteDialog::removeRoute() +void RouteDialog::disconnectClicked() +{ +// QTreeWidgetItem* item = routeList->currentItem(); +// if(item && item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) +// { +// const MusECore::Route src = item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value(); +// const MusECore::Route dst = item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value(); +// MusEGlobal::audio->msgRemoveRoute(src, dst); +// MusEGlobal::audio->msgUpdateSoloStates(); +// MusEGlobal::song->update(SC_SOLO); +// } +// routingChanged(); +// +// +// const int cnt = routeList->topLevelItemCount(); +// for(int i = 0; i < cnt; ++i) +// { +// QTreeWidgetItem* item = routeList->topLevelItem(i); +// if(!item || !item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() || !item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) +// continue; +// if(item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value() == src && item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value() == dst) +// return item; +// } + + + MusECore::PendingOperationList operations; + QTreeWidgetItemIterator ii(routeList); + while(*ii) + { + QTreeWidgetItem* item = *ii; + if(item && item->isSelected() && + item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && + item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) + { + const MusECore::Route src = item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value(); + const MusECore::Route dst = item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value(); + //if(MusECore::routeCanDisconnect(src, dst)) +// operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ +// Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist +// but not in any track or port routelist) until multiple output routes are allowed +// instead of just single port and channel properties. The route is exclusive. + switch(src.type) { - QTreeWidgetItem* item = routeList->currentItem(); - if (item == 0) - return; - MusEGlobal::audio->msgRemoveRoute(MusECore::Route(item->text(0), false, -1), MusECore::Route(item->text(1), true, -1)); - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_SOLO); - delete item; + case MusECore::Route::TRACK_ROUTE: + switch(dst.type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + if(src.track->isMidiTrack()) + { +// MusECore::MidiTrack* mt = static_cast(src.track); + // We cannot 'remove' a simulated midi track output port and channel route. + // (Midi port cannot be -1 meaning 'no port'.) + // Only remove it if it's a different port or channel. +// if(mt->outPort() != dst.midiPort || mt->outChannel() != src.channel) +// operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); + ++ii; + continue; + } + break; + + case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; + } + break; + + case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; } +#endif + + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); + + } + ++ii; + } + + if(!operations.empty()) + { + MusEGlobal::audio->msgExecutePendingOperations(operations, true); +// MusEGlobal::song->update(SC_ROUTE); + //MusEGlobal::song->update(SC_SOLO); + //routingChanged(); + } + + +// QTreeWidgetItem* srcItem = newSrcList->currentItem(); +// QTreeWidgetItem* dstItem = newDstList->currentItem(); +// if(srcItem == 0 || dstItem == 0) +// return; +// if(srcItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert() && dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// MusECore::Route src = srcItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); +// MusECore::Route dst = dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); +// MusEGlobal::audio->msgRemoveRoute(src, dst); +// MusEGlobal::audio->msgUpdateSoloStates(); +// MusEGlobal::song->update(SC_SOLO); +// } +// routingChanged(); +} //--------------------------------------------------------- -// addRoute +// connectClicked //--------------------------------------------------------- -void RouteDialog::addRoute() +void RouteDialog::connectClicked() +{ +// RouteTreeWidgetItem* srcItem = static_cast(newSrcList->currentItem()); +// RouteTreeWidgetItem* dstItem = static_cast(newDstList->currentItem()); +// if(srcItem == 0 || dstItem == 0) +// return; +// +// const MusECore::Route src = srcItem->route(); +// const MusECore::Route dst = dstItem->route(); +// MusEGlobal::audio->msgAddRoute(src, dst); +// MusEGlobal::audio->msgUpdateSoloStates(); +// MusEGlobal::song->update(SC_SOLO); +// routingChanged(); + + MusECore::PendingOperationList operations; + MusECore::RouteList srcList; + MusECore::RouteList dstList; + newSrcList->getSelectedRoutes(srcList); + newDstList->getSelectedRoutes(dstList); + const int srcSelSz = srcList.size(); + const int dstSelSz = dstList.size(); + bool upd_trk_props = false; + MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; + + for(int srcIdx = 0; srcIdx < srcSelSz; ++srcIdx) + { + const MusECore::Route& src = srcList.at(srcIdx); + for(int dstIdx = 0; dstIdx < dstSelSz; ++dstIdx) + { + const MusECore::Route& dst = dstList.at(dstIdx); + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ +// Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist +// but not in any track or port routelist) until multiple output routes are allowed +// instead of just single port and channel properties. The route is exclusive. + switch(src.type) { - QListWidgetItem* srcItem = newSrcList->currentItem(); - QListWidgetItem* dstItem = newDstList->currentItem(); - if (srcItem == 0 || dstItem == 0) - return; - MusEGlobal::audio->msgAddRoute(MusECore::Route(srcItem->text(), false, -1), MusECore::Route(dstItem->text(), true, -1)); - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_SOLO); - new QTreeWidgetItem(routeList, QStringList() << srcItem->text() << dstItem->text()); + case MusECore::Route::TRACK_ROUTE: + switch(dst.type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + if(src.track->isMidiTrack()) + { + MusECore::MidiTrack* mt = static_cast(src.track); + // We cannot 'remove' a simulated midi track output port and channel route. + // (Midi port cannot be -1 meaning 'no port'.) + // Only remove it if it's a different port or channel. + if(src.channel >= 0 && src.channel < MIDI_CHANNELS && (mt->outPort() != dst.midiPort || mt->outChannel() != src.channel)) + { + MusEGlobal::audio->msgIdle(true); + changed |= mt->setOutPortAndChannelAndUpdate(dst.midiPort, src.channel, false); + MusEGlobal::audio->msgIdle(false); + //MusEGlobal::audio->msgUpdateSoloStates(); + //MusEGlobal::song->update(SC_MIDI_TRACK_PROP); + upd_trk_props = true; + } + continue; + } + break; + + case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; + } + break; + + case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; } +#endif + + if(MusECore::routeCanConnect(src, dst)) + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); + } + } + + if(!operations.empty()) + { + operations.add(MusECore::PendingOperationItem((MusECore::TrackList*)NULL, MusECore::PendingOperationItem::UpdateSoloStates)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true, + upd_trk_props ? (SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)) : 0); +// MusEGlobal::song->update(SC_ROUTE | (upd_trk_props ? SC_MIDI_TRACK_PROP : 0)); + //MusEGlobal::song->update(SC_SOLO); + //routingChanged(); + } + else if(upd_trk_props) + MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); + +} +// if(srcItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert() && dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// const MusECore::Route src = srcItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); +// const MusECore::Route dst = dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); +// MusEGlobal::audio->msgAddRoute(src, dst); +// MusEGlobal::audio->msgUpdateSoloStates(); +// MusEGlobal::song->update(SC_SOLO); +// } +// routingChanged(); +// } //--------------------------------------------------------- // srcSelectionChanged //--------------------------------------------------------- void RouteDialog::srcSelectionChanged() +{ + DEBUG_PRST_ROUTES(stderr, "RouteDialog::srcSelectionChanged\n"); // REMOVE Tim. + + MusECore::RouteList srcList; + MusECore::RouteList dstList; + newSrcList->getSelectedRoutes(srcList); + newDstList->getSelectedRoutes(dstList); + const int srcSelSz = srcList.size(); + const int dstSelSz = dstList.size(); + //if(srcSelSz == 0 || dstSelSz == 0 || (srcSelSz > 1 && dstSelSz > 1)) + //{ + // connectButton->setEnabled(false); + // removeButton->setEnabled(false); + // return; + //} + + routeList->blockSignals(true); + routeList->clearSelection(); + bool canConnect = false; + QTreeWidgetItem* routesItem = 0; + int routesSelCnt = 0; + int routesRemoveCnt = 0; + for(int srcIdx = 0; srcIdx < srcSelSz; ++srcIdx) + { + MusECore::Route& src = srcList.at(srcIdx); +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + int mt_to_mp_cnt = 0; +#endif + for(int dstIdx = 0; dstIdx < dstSelSz; ++dstIdx) + { +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + bool useMTOutProps = false; +#endif + MusECore::Route& dst = dstList.at(dstIdx); + // Special for some item type combos: Before searching, cross-update the routes if necessary. + // To minimize route copying, here on each iteration we alter each list route, but should be OK. + switch(src.type) + { + case MusECore::Route::TRACK_ROUTE: + switch(dst.type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + if(src.track->isMidiTrack()) + { + dst.channel = src.channel; + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ +// Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist +// but not in any track or port routelist) until multiple output routes are allowed +// instead of just single port and channel properties. The route is exclusive. + useMTOutProps = true; + MusECore::MidiTrack* mt = static_cast(src.track); + if(src.channel >= 0 && src.channel < MIDI_CHANNELS && (mt->outPort() != dst.midiPort || mt->outChannel() != src.channel)) + ++mt_to_mp_cnt; +#endif + + } + break; + + case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; + } + break; + + case MusECore::Route::MIDI_PORT_ROUTE: + switch(dst.type) + { + case MusECore::Route::TRACK_ROUTE: src.channel = dst.channel; break; + case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; + } + break; + + case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; + } + + QTreeWidgetItem* ri = findRoutesItem(src, dst); + if(ri) { - QListWidgetItem* srcItem = newSrcList->currentItem(); - QListWidgetItem* dstItem = newDstList->currentItem(); - connectButton->setEnabled((srcItem != 0) - && (dstItem != 0) - && MusECore::checkRoute(srcItem->text(), dstItem->text())); + ri->setSelected(true); + routesItem = ri; + ++routesSelCnt; +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + if(!useMTOutProps) +#endif + ++routesRemoveCnt; } + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + if(useMTOutProps) + continue; +#endif + + if(MusECore::routeCanConnect(src, dst)) + canConnect = true; +// if(MusECore::routeCanDisconnect(src, dst)) +// canDisconnect = true; + } + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + // If there was one and only one selected midi port for a midi track, allow (simulated) connection. + if(mt_to_mp_cnt == 1) + canConnect = true; +#endif + + } + + if(routesSelCnt == 0) + routeList->setCurrentItem(0); + //routeList->setCurrentItem(routesItem); + routeList->blockSignals(false); + if(routesSelCnt == 1) + routeList->scrollToItem(routesItem, QAbstractItemView::PositionAtCenter); + + selectRoutes(false); + + connectionsWidget->update(); + //connectButton->setEnabled(can_connect && (srcSelSz == 1 || dstSelSz == 1)); + connectButton->setEnabled(canConnect); + //removeButton->setEnabled(can_disconnect); +// removeButton->setEnabled(routesSelCnt > 0); + removeButton->setEnabled(routesRemoveCnt != 0); + +/* + RouteTreeItemList srcSel = newSrcList->selectedItems(); + RouteTreeItemList dstSel = newDstList->selectedItems(); + const int srcSelSz = srcSel.size(); + const int dstSelSz = dstSel.size(); + if(srcSelSz == 0 || dstSelSz == 0) + { + connectButton->setEnabled(false); + removeButton->setEnabled(false); + return; + } + + bool can_connect = false; + bool can_disconnect = false; + for(int srcIdx = 0; srcIdx < srcSelSz; ++srcIdx) + { + QTreeWidgetItem* srcItem = srcSel.at(srcIdx); + if(!srcItem) + continue; + if(!srcItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) + continue; + MusECore::Route src = srcItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); + if(srcItem->data(ROUTE_NAME_COL, RouteDialog::ChannelsRole).canConvert()) + { + QBitArray ba = srcItem->data(ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); + switch(src.type) + { + case MusECore::Route::TRACK_ROUTE: + if(src.track && src.track->isMidiTrack()) + { + int chans = 0; + const int sz = ba.size(); + for(int i = 0; i < sz; ++i) + { + if(i >= MIDI_CHANNELS) + break; + if(ba.testBit(i)) + src.channel |= (1 << i); + } + } + break; + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + } + + for(int dstIdx = 0; dstIdx < dstSelSz; ++dstIdx) + { + QTreeWidgetItem* dstItem = dstSel.at(dstIdx); + if(!dstItem) + continue; + if(!dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) + continue; + MusECore::Route dst = dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); + if(dstItem->data(ROUTE_NAME_COL, RouteDialog::ChannelsRole).canConvert()) + { + QBitArray ba = dstItem->data(ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); + } + + + + } + + + + } + + + QTreeWidgetItem* srcItem = newSrcList->currentItem(); + QTreeWidgetItem* dstItem = newDstList->currentItem(); + if(srcItem == 0 || dstItem == 0) + { + connectButton->setEnabled(false); + removeButton->setEnabled(false); + return; + } + if(!srcItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert() || !dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) + { + connectButton->setEnabled(false); + removeButton->setEnabled(false); + return; + } + //const MusECore::Route src = srcItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); + //const MusECore::Route dst = dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); + MusECore::Route src = srcItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); + MusECore::Route dst = dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); + if(srcItem->data(ROUTE_NAME_COL, RouteDialog::ChannelsRole).canConvert()) + { + QBitArray ba = srcItem->data(ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); + } + +// || !dstItem->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// connectButton->setEnabled(false); +// removeButton->setEnabled(false); +// return; +// } + QTreeWidgetItem* routesItem = findRoutesItem(src, dst); + routeList->blockSignals(true); + routeList->setCurrentItem(routesItem); + routeList->blockSignals(false); + if(routesItem) + routeList->scrollToItem(routesItem, QAbstractItemView::PositionAtCenter); + connectionsWidget->update(); + connectButton->setEnabled(MusECore::routeCanConnect(src, dst)); + removeButton->setEnabled(MusECore::routeCanDisconnect(src, dst));*/ +} //--------------------------------------------------------- // dstSelectionChanged //--------------------------------------------------------- void RouteDialog::dstSelectionChanged() - { - QListWidgetItem* dstItem = newDstList->currentItem(); - QListWidgetItem* srcItem = newSrcList->currentItem(); - connectButton->setEnabled((srcItem != 0) - && (dstItem != 0) - && MusECore::checkRoute(srcItem->text(), dstItem->text())); - } +{ + srcSelectionChanged(); +} //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void RouteDialog::closeEvent(QCloseEvent* e) +{ + emit closed(); + e->accept(); +} + +// RouteTreeWidgetItem* RouteDialog::findSrcItem(const MusECore::Route& src) +// { +// int cnt = newSrcList->topLevelItemCount(); +// for(int i = 0; i < cnt; ++i) +// { +// RouteTreeWidgetItem* item = static_cast(newSrcList->topLevelItem(i)); +// if(item) +// { +// if(item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// //if(item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value() == src) +// if(item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value().compare(src)) +// return item; +// } +// +// int c_cnt = item->childCount(); +// for(int j = 0; j < c_cnt; ++j) +// { +// RouteTreeWidgetItem* c_item = static_cast(item->child(j)); +// if(c_item) +// { +// if(c_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// //if(c_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value() == src) +// if(c_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value().compare(src)) +// return c_item; +// } +// +// int cc_cnt = c_item->childCount(); +// for(int k = 0; k < cc_cnt; ++k) +// { +// RouteTreeWidgetItem* cc_item = static_cast(c_item->child(k)); +// if(cc_item) +// { +// if(cc_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// //if(cc_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value() == src) +// if(cc_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value().compare(src)) +// return cc_item; +// } +// } +// } +// } +// } +// } +// } +// return 0; +// } + +// RouteTreeWidgetItem* RouteDialog::findDstItem(const MusECore::Route& dst) +// { +// int cnt = newDstList->topLevelItemCount(); +// for(int i = 0; i < cnt; ++i) +// { +// RouteTreeWidgetItem* item = static_cast(newDstList->topLevelItem(i)); +// if(item) +// { +// if(item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// //if(item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value() == dst) +// if(item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value().compare(dst)) +// return item; +// } +// +// int c_cnt = item->childCount(); +// for(int j = 0; j < c_cnt; ++j) +// { +// RouteTreeWidgetItem* c_item = static_cast(item->child(j)); +// if(c_item) +// { +// if(c_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// //if(c_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value() == dst) +// if(c_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value().compare(dst)) +// return c_item; +// } +// +// int cc_cnt = c_item->childCount(); +// for(int k = 0; k < cc_cnt; ++k) +// { +// RouteTreeWidgetItem* cc_item = static_cast(c_item->child(k)); +// if(cc_item) +// { +// if(cc_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// //if(cc_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value() == dst) +// if(cc_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value().compare(dst)) +// return cc_item; +// } +// } +// } +// } +// } +// } +// } +// return 0; +// } + +QTreeWidgetItem* RouteDialog::findRoutesItem(const MusECore::Route& src, const MusECore::Route& dst) +{ + int cnt = routeList->topLevelItemCount(); + for(int i = 0; i < cnt; ++i) + { + QTreeWidgetItem* item = routeList->topLevelItem(i); + if(!item || !item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() || !item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) + continue; + if(item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value() == src && item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value() == dst) + return item; + } + return 0; +} + +// RouteTreeWidgetItem* RouteDialog::findCategoryItem(QTreeWidget* tree, const QString& name) +// { +// int cnt = tree->topLevelItemCount(); +// for(int i = 0; i < cnt; ++i) +// { +// RouteTreeWidgetItem* item = static_cast(tree->topLevelItem(i)); +// if(item && item->text(ROUTE_NAME_COL) == name) +// return item; +// } +// return 0; +// } + +// void RouteDialog::getSelectedRoutes(QTreeWidget* tree, MusECore::RouteList& routes) +// { +// //DEBUG_PRST_ROUTES(stderr, "RouteDialog::getSelectedRoutes\n"); // REMOVE Tim. +// +// RouteTreeItemList sel = tree->selectedItems(); +// const int selSz = sel.size(); +// if(selSz == 0) +// return; +// +// for(int idx = 0; idx < selSz; ++idx) +// { +// RouteTreeWidgetItem* item = static_cast(sel.at(idx)); +// if(!item) +// continue; +// if(!item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// continue; +// MusECore::Route r = item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value(); +// if(item->data(ROUTE_NAME_COL, RouteDialog::ChannelsRole).canConvert()) +// { +// QBitArray ba = item->data(ROUTE_NAME_COL, RouteDialog::ChannelsRole).value(); +// switch(r.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// if(r.track) +// { +// const int sz = ba.size(); +// if(r.track->isMidiTrack()) +// { +// for(int i = 0; i < sz; ++i) +// { +// if(i >= MIDI_CHANNELS) +// break; +// if(ba.testBit(i)) +// { +// r.channel = (1 << i); +// routes.push_back(r); +// } +// } +// } +// else +// { +// for(int i = 0; i < sz; ++i) +// { +// if(ba.testBit(i)) +// { +// r.channel = i; +// routes.push_back(r); +// } +// } +// } +// } +// break; +// case MusECore::Route::JACK_ROUTE: +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// case MusECore::Route::MIDI_PORT_ROUTE: +// break; +// } +// } +// else +// routes.push_back(r); +// } +// } + + + + +void RouteDialog::removeItems() +{ + QVector itemsToDelete; + + newSrcList->getItemsToDelete(itemsToDelete); + newDstList->getItemsToDelete(itemsToDelete); + getRoutesToDelete(routeList, itemsToDelete); + + + newSrcList->blockSignals(true); + newDstList->blockSignals(true); + routeList->blockSignals(true); + + if(!itemsToDelete.empty()) + { + int cnt = itemsToDelete.size(); + for(int i = 0; i < cnt; ++i) + delete itemsToDelete.at(i); + } + + selectRoutes(false); + + routeList->blockSignals(false); + newDstList->blockSignals(false); + newSrcList->blockSignals(false); + + //connectionsWidget->update(); +} + +// void RouteDialog::getItemsToDelete(QTreeWidget* tree, QVector& items_to_remove) +// { +// int cnt = tree->topLevelItemCount(); +// for(int i = 0; i < cnt; ++i) +// { +// RouteTreeWidgetItem* item = static_cast(tree->topLevelItem(i)); +// if(item) +// { +// int c_cnt = item->childCount(); +// for(int j = 0; j < c_cnt; ++j) +// { +// RouteTreeWidgetItem* c_item = static_cast(item->child(j)); +// if(c_item) +// { +// int cc_cnt = c_item->childCount(); +// for(int k = 0; k < cc_cnt; ++k) +// { +// RouteTreeWidgetItem* cc_item = static_cast(c_item->child(k)); +// if(cc_item) +// { +// if(cc_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// if(!routeNodeExists(cc_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value())) +// items_to_remove.append(cc_item); +// } +// } +// } +// if(c_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// if(!routeNodeExists(c_item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value())) +// items_to_remove.append(c_item); +// } +// } +// } +// if(item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).canConvert()) +// { +// if(!routeNodeExists(item->data(ROUTE_NAME_COL, RouteDialog::RouteRole).value())) +// items_to_remove.append(item); +// } +// } +// } +// } + +void RouteDialog::getRoutesToDelete(QTreeWidget* tree, QVector& items_to_remove) +{ + const int iItemCount = tree->topLevelItemCount(); + for (int iItem = 0; iItem < iItemCount; ++iItem) + { + QTreeWidgetItem *item = tree->topLevelItem(iItem); + if(item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) + { + const MusECore::Route src = item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value(); + const MusECore::Route dst = item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value(); + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ +// Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist +// but not in any track or port routelist) until multiple output routes are allowed +// instead of just single port and channel properties. The route is exclusive. + switch(src.type) + { + case MusECore::Route::TRACK_ROUTE: + switch(dst.type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + if(src.track->isMidiTrack()) + { + MusECore::MidiTrack* mt = static_cast(src.track); + // We cannot 'remove' a simulated midi track output port and channel route. + // (Midi port cannot be -1 meaning 'no port'.) + // Only remove it if it's a different port or channel. + if(mt->outPort() != dst.midiPort || mt->outChannel() != src.channel) + items_to_remove.append(item); + continue; + } + break; + + case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; + } + break; + + case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: + break; + } +#endif + + if(!MusECore::routeCanDisconnect(src, dst)) + items_to_remove.append(item); + } + } +} + +void RouteDialog::selectRoutes(bool doNormalSelections) +{ + const QList route_list = routeList->selectedItems(); + newSrcList->selectRoutes(route_list, doNormalSelections); + newDstList->selectRoutes(route_list, doNormalSelections); +} + +// bool RouteDialog::routeNodeExists(const MusECore::Route& r) +// { +// switch(r.type) +// { +// case MusECore::Route::TRACK_ROUTE: +// { +// MusECore::TrackList* tl = MusEGlobal::song->tracks(); +// for(MusECore::ciTrack i = tl->begin(); i != tl->end(); ++i) +// { +// if((*i)->isMidiTrack()) +// continue; +// MusECore::AudioTrack* track = (MusECore::AudioTrack*)(*i); +// if(track->type() == MusECore::Track::AUDIO_INPUT) +// { +// if(r == MusECore::Route(track, -1)) +// return true; +// for(int channel = 0; channel < track->channels(); ++channel) +// if(r == MusECore::Route(track, channel)) +// return true; +// +// // const MusECore::RouteList* rl = track->inRoutes(); +// // for (MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) { +// // //MusECore::Route dst(track->name(), true, r->channel); +// // QString src(r->name()); +// // if(r->channel != -1) +// // src += QString(":") + QString::number(r->channel); +// // MusECore::Route dst(track->name(), true, r->channel, MusECore::Route::TRACK_ROUTE); +// // item = new QTreeWidgetItem(routeList, QStringList() << src << dst.name()); +// // item->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(*r)); +// // item->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); +// // } +// } +// else if(track->type() != MusECore::Track::AUDIO_AUX) +// { +// if(r == MusECore::Route(track, -1)) +// return true; +// } +// +// if(track->type() == MusECore::Track::AUDIO_OUTPUT) +// { +// if(r == MusECore::Route(track, -1)) +// return true; +// for (int channel = 0; channel < track->channels(); ++channel) +// if(r == MusECore::Route(track, channel)) +// return true; +// } +// else if(r == MusECore::Route(track, -1)) +// return true; +// +// // const MusECore::RouteList* rl = track->outRoutes(); +// // for (MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) +// // { +// // QString srcName(track->name()); +// // if (track->type() == MusECore::Track::AUDIO_OUTPUT) { +// // MusECore::Route s(srcName, false, r->channel); +// // srcName = s.name(); +// // } +// // if(r->channel != -1) +// // srcName += QString(":") + QString::number(r->channel); +// // MusECore::Route src(track->name(), false, r->channel, MusECore::Route::TRACK_ROUTE); +// // item = new QTreeWidgetItem(routeList, QStringList() << srcName << r->name()); +// // item->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); +// // item->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(*r)); +// // } +// } +// } +// break; +// +// case MusECore::Route::JACK_ROUTE: +// { +// if(MusEGlobal::checkAudioDevice()) +// { +// for(std::list::iterator i = tmpJackOutPorts.begin(); i != tmpJackOutPorts.end(); ++i) +// if(r == MusECore::Route(*i, false, -1, MusECore::Route::JACK_ROUTE)) +// return true; +// for (std::list::iterator i = tmpJackInPorts.begin(); i != tmpJackInPorts.end(); ++i) +// if(r == MusECore::Route(*i, true, -1, MusECore::Route::JACK_ROUTE)) +// return true; +// for(std::list::iterator i = tmpJackMidiOutPorts.begin(); i != tmpJackMidiOutPorts.end(); ++i) +// if(r == MusECore::Route(*i, false, -1, MusECore::Route::JACK_ROUTE)) +// return true; +// for (std::list::iterator i = tmpJackMidiInPorts.begin(); i != tmpJackMidiInPorts.end(); ++i) +// if(r == MusECore::Route(*i, true, -1, MusECore::Route::JACK_ROUTE)) +// return true; +// } +// } +// break; +// +// case MusECore::Route::MIDI_DEVICE_ROUTE: +// for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) +// { +// MusECore::MidiDevice* md = *i; +// // Synth are tracks and devices. Don't list them as devices here, list them as tracks, above. +// if(md->deviceType() == MusECore::MidiDevice::SYNTH_MIDI) +// continue; +// +// if(r == MusECore::Route(md, -1)) +// return true; +// for(int channel = 0; channel < MIDI_CHANNELS; ++channel) +// if(r == MusECore::Route(md, channel)) +// return true; +// } +// +// case MusECore::Route::MIDI_PORT_ROUTE: +// break; +// +// } +// return false; +// } + +void RouteDialog::addItems() +{ + RouteTreeWidgetItem* srcCatItem; + RouteTreeWidgetItem* dstCatItem; + RouteTreeWidgetItem* item; + RouteTreeWidgetItem* subitem; + QTreeWidgetItem* routesItem; + // Tried wrap flags: Doesn't work (at least not automatically). + //const int align_flags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap | Qt::TextWrapAnywhere; + const int align_flags = Qt::AlignLeft | Qt::AlignVCenter; + + // + // Tracks: + // + + dstCatItem = newDstList->findCategoryItem(tracksCat); + srcCatItem = newSrcList->findCategoryItem(tracksCat); + MusECore::TrackList* tl = MusEGlobal::song->tracks(); + for(MusECore::ciTrack i = tl->begin(); i != tl->end(); ++i) + { + MusECore::Track* track = *i; + const MusECore::RouteCapabilitiesStruct rcaps = track->routeCapabilities(); + int src_chans = 0; + int dst_chans = 0; + bool src_routable = false; + bool dst_routable = false; + + switch(track->type()) + { + case MusECore::Track::AUDIO_INPUT: + src_chans = rcaps._trackChannels._outChannels; + dst_chans = rcaps._jackChannels._inChannels; + src_routable = rcaps._trackChannels._outRoutable; + dst_routable = rcaps._jackChannels._inRoutable || rcaps._trackChannels._inRoutable; // Support Audio Out to Audio In omni route. + break; + case MusECore::Track::AUDIO_OUTPUT: + src_chans = rcaps._jackChannels._outChannels; + dst_chans = rcaps._trackChannels._inChannels; + src_routable = rcaps._jackChannels._outRoutable || rcaps._trackChannels._outRoutable; // Support Audio Out to Audio In omni route. + dst_routable = rcaps._trackChannels._inRoutable; + break; + case MusECore::Track::MIDI: + case MusECore::Track::DRUM: + case MusECore::Track::NEW_DRUM: +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + src_chans = MIDI_CHANNELS; +#else + src_chans = rcaps._midiPortChannels._outChannels; +#endif + + dst_chans = rcaps._midiPortChannels._inChannels; + src_routable = rcaps._midiPortChannels._outRoutable || rcaps._trackChannels._outRoutable; // Support Midi Track to Audio In omni route. + dst_routable = rcaps._midiPortChannels._inRoutable; + break; + case MusECore::Track::WAVE: + case MusECore::Track::AUDIO_AUX: + case MusECore::Track::AUDIO_SOFTSYNTH: + case MusECore::Track::AUDIO_GROUP: + src_chans = rcaps._trackChannels._outChannels; + dst_chans = rcaps._trackChannels._inChannels; + src_routable = rcaps._trackChannels._outRoutable; + dst_routable = rcaps._trackChannels._inRoutable; + break; + } + + +// if((*i)->isMidiTrack()) +// { +// MusECore::MidiTrack* track = static_cast(*i); +// +// } +// else +// { +// const MusECore::AudioTrack* track = static_cast(*i); + + + // + // DESTINATION section: + // + +// if(track->type() != MusECore::Track::AUDIO_AUX) +// if(track->totalRoutableInputs(MusECore::Route::TRACK_ROUTE) != 0) +// if(rcaps._trackChannels._inRoutable || rcaps._trackChannels._inChannels != 0) + if(dst_routable || dst_chans != 0) { - emit closed(); - e->accept(); + const MusECore::Route r(track, -1); + item = newDstList->findItem(r, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, track->name()); + } + else + { + if(!dstCatItem) + { + newDstList->blockSignals(true); + //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << tracksCat << QString() ); + dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << tracksCat, RouteTreeWidgetItem::CategoryItem, false); + dstCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = dstCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + dstCatItem->setFont(ROUTE_NAME_COL, fnt); + dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + dstCatItem->setExpanded(true); + dstCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + newDstList->blockSignals(false); + } + newDstList->blockSignals(true); + //item = new QTreeWidgetItem(dstCatItem, QStringList() << track->name() << trackLabel ); + item = new RouteTreeWidgetItem(dstCatItem, QStringList() << track->name(), RouteTreeWidgetItem::RouteItem, false, r); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newDstList->blockSignals(false); + //dstCatItem->setExpanded(true); // REMOVE Tim. For test only. + } + if(QPixmap* r_pm = r.icon(false, false)) + item->setIcon(ROUTE_NAME_COL, QIcon(*r_pm)); + +// if(track->isMidiTrack()) +// if(rcaps._trackChannels._inChannels != 0) + if(dst_chans != 0) + { +// const MusECore::Route sub_r(track, 0); + const MusECore::Route sub_r(track, 0, track->isMidiTrack() ? -1 : 1); + subitem = newDstList->findItem(sub_r, RouteTreeWidgetItem::ChannelsItem); + if(subitem) + { + // Update the channel y values. + //subitem->computeChannelYValues(); + // Update the number of channels. + subitem->setChannels(); + } + else +// if(!subitem) + { + newDstList->blockSignals(true); + item->setExpanded(true); + //subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel) << QString() ); + //subitem = new QTreeWidgetItem(item, QStringList() << QString() << QString() ); + subitem = new RouteTreeWidgetItem(item, QStringList() << QString(), RouteTreeWidgetItem::ChannelsItem, false, sub_r); + //subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(sub_r)); + //subitem->setData(ROUTE_NAME_COL, RouteDialog::ChannelsRole, QVariant::fromValue(QBitArray(MIDI_CHANNELS))); + subitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + subitem->setTextAlignment(ROUTE_NAME_COL, align_flags); + newDstList->blockSignals(false); + } + // Update the channel y values. + //subitem->computeChannelYValues(); + } +// } +// else +// { +// // MusECore::AudioTrack* atrack = static_cast(track); +// // const int chans = atrack->type() == MusECore::Track::AUDIO_SOFTSYNTH ? atrack->totalInChannels() : atrack->channels(); +// //const int chans = atrack->totalRoutableInputs(); +// //for(int channel = 0; channel < chans; ++channel) +// // if(chans != 0) +// // { +// //const MusECore::Route sub_r(track, channel, 1); +// const MusECore::Route sub_r(track, 0, 1); +// subitem = newDstList->findItem(sub_r, RouteTreeWidgetItem::ChannelsItem); +// // if(subitem) +// // { +// // // Update the channel y values. +// // subitem->computeChannelYValues(); +// // } +// // else +// if(!subitem) +// { +// newDstList->blockSignals(true); +// item->setExpanded(true); +// //subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel) << QString() ); +// //subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel) ); +// subitem = new RouteTreeWidgetItem(item, QStringList() << QString(), RouteTreeWidgetItem::ChannelsItem, false, sub_r); +// //subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(sub_r)); +// //subitem->setData(ROUTE_NAME_COL, RouteDialog::ChannelsRole, QVariant::fromValue(QBitArray(chans))); +// subitem->setTextAlignment(ROUTE_NAME_COL, align_flags); +// newDstList->blockSignals(false); +// } +// // Update the channel y values. +// //subitem->computeChannelYValues(); +// // } +// } } + const MusECore::RouteList* irl = track->inRoutes(); + for(MusECore::ciRoute r = irl->begin(); r != irl->end(); ++r) + { + // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 + // After: [src TrackA, Channel 4, Remote Channel 2] dst: TrackB channel 2 Remote Channel 4 + // + // Ex. + // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 + // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 + MusECore::Route src; + MusECore::Route dst; + QString srcName; + QString dstName = track->name(); + switch(r->type) + { + case MusECore::Route::JACK_ROUTE: + //src = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, r->remoteChannel, r->channels, -1, r->persistentJackPortName); + //src = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, r->remoteChannel, -1, -1, r->persistentJackPortName); + src = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); + //src = *r; + //dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, r->channels, -1, 0); + dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, 1, -1, 0); + srcName = r->name(MusEGlobal::config.preferredRouteNameOrAlias); + break; + case MusECore::Route::MIDI_DEVICE_ROUTE: + continue; + break; + // Midi ports taken care of below... + case MusECore::Route::MIDI_PORT_ROUTE: + continue; + break; + + /*{ + //continue; // TODO +// //src = MusECore::Route(MusECore::Route::MIDI_PORT_ROUTE, r->midiPort, 0, r->channel, -1, -1, 0); +// MusECore::MidiDevice* md = MusEGlobal::midiPorts[r->midiPort].device(); + if(r->channel == -1) + { +// if(md) +// src = MusECore::Route(md); +// else + src = MusECore::Route(r->midiPort); + dst = MusECore::Route(track); + srcName = r->name(); + break; + } + +// for(int i = 0; i < MIDI_CHANNELS; ++i) + { +// int chbits = 1 << i; +// if(r->channel & chbits) + { + //src = MusECore::Route(r->midiPort, r->channel); + //src = MusECore::Route(r->midiPort, 1 << i); +// if(md) +// //src = MusECore::Route(md, chbits); +// src = MusECore::Route(md); +// //src = MusECore::Route(md, r->channel); +// else + //src = MusECore::Route(r->midiPort, chbits); + //src = MusECore::Route(r->midiPort); + src = MusECore::Route(r->midiPort, r->channel); + // //dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, 1, -1, 0); + //dst = MusECore::Route(track, r->channel, 1); + dst = MusECore::Route(track, r->channel); +// dst = MusECore::Route(track, chbits); + //dst = MusECore::Route(track, i); + srcName = r->name(); + //if(src.channel != -1) + // srcName += QString(" [") + QString::number(i + 1) + QString("]"); +// dstName = track->name() + QString(" [") + QString::number(i + 1) + QString("]"); + dstName = track->name() + QString(" [") + QString::number(r->channel + 1) + QString("]"); + routesItem = findRoutesItem(src, dst); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, srcName); + routesItem->setText(ROUTE_DST_COL, dstName); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + routeList->blockSignals(false); + } + } + } + continue; + } + break; */ + + case MusECore::Route::TRACK_ROUTE: + src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, r->track, r->remoteChannel, r->channels, -1, 0); + dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, r->channels, -1, 0); + srcName = r->name(); + break; + } + + if(src.channel != -1) + srcName += QString(" [") + QString::number(src.channel) + QString("]"); + if(dst.channel != -1) + dstName += QString(" [") + QString::number(dst.channel) + QString("]"); + + + +// QString srcName(r->name()); +// if(r->channel != -1) +// srcName += QString(":") + QString::number(r->channel); +// +// +// MusECore::Route src(*r); +// if(src.type == MusECore::Route::JACK_ROUTE) +// src.channel = -1; +// //const MusECore::Route dst(track->name(), true, r->channel, MusECore::Route::TRACK_ROUTE); +// const MusECore::Route dst(MusECore::Route::TRACK_ROUTE, -1, track, r->remoteChannel, r->channels, r->channel, 0); +// +// +// src.remoteChannel = src.channel; +// dst.remoteChannel = dst.channel; +// const int src_chan = src.channel; +// src.channel = dst.channel; +// dst.channel = src_chan; + + + routesItem = findRoutesItem(src, dst); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, srcName); + routesItem->setText(ROUTE_DST_COL, dstName); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); + routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + if(QPixmap* src_pm = src.icon(true, false)) + routesItem->setIcon(ROUTE_SRC_COL, QIcon(*src_pm)); + if(QPixmap* dst_pm = dst.icon(false, false)) + routesItem->setIcon(ROUTE_DST_COL, QIcon(*dst_pm)); + routeList->blockSignals(false); + } + } +// else if(track->type() != MusECore::Track::AUDIO_AUX) +// { +// const MusECore::Route r(track, -1); +// item = findDstItem(r); +// if(item) +// { +// // Update the text. +// item->setText(ROUTE_NAME_COL, track->name()); +// } +// else +// { +// if(!dstCatItem) +// { +// newDstList->blockSignals(true); +// dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << tracksCat << QString() ); +// //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newDstList->blockSignals(false); +// } +// newDstList->blockSignals(true); +// item = new QTreeWidgetItem(dstCatItem, QStringList() << track->name() << trackLabel ); +// item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newDstList->blockSignals(false); +// } +// //if((*i)->isMidiTrack()) +// //if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// { +// //for(int channel = 0; channel < track->channels(); ++channel) +// const int chans = track->type() == MusECore::Track::AUDIO_SOFTSYNTH ? track->totalInChannels() : track->channels(); +// for(int channel = 0; channel < chans; ++channel) +// { +// const MusECore::Route subr(track, channel, 1); +// subitem = findDstItem(subr); +// if(!subitem) +// { +// newDstList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(subr)); +// newDstList->blockSignals(false); +// } +// } +// } +// } + + + // + // SOURCE section: + // + + //if(track->type() == MusECore::Track::AUDIO_OUTPUT) +// if(track->totalRoutableOutputs(MusECore::Route::TRACK_ROUTE) != 0 +// if(rcaps._trackChannels._outRoutable || rcaps._trackChannels._outChannels != 0 + if(src_routable || src_chans != 0 +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + || track->isMidiTrack() +#endif + ) + { + const MusECore::Route r(track, -1); + item = newSrcList->findItem(r, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, track->name()); + } + else + { + if(!srcCatItem) + { + newSrcList->blockSignals(true); + //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << tracksCat << QString() ); + srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << tracksCat, RouteTreeWidgetItem::CategoryItem, true); + srcCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = srcCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + srcCatItem->setFont(ROUTE_NAME_COL, fnt); + srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + srcCatItem->setExpanded(true); + srcCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + newSrcList->blockSignals(false); + } + newSrcList->blockSignals(true); + //item = new QTreeWidgetItem(srcCatItem, QStringList() << track->name() << trackLabel ); + item = new RouteTreeWidgetItem(srcCatItem, QStringList() << track->name(), RouteTreeWidgetItem::RouteItem, true, r); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newSrcList->blockSignals(false); + } + if(QPixmap* r_pm = r.icon(true, false)) + item->setIcon(ROUTE_NAME_COL, QIcon(*r_pm)); + +// if(track->isMidiTrack()) +// if(rcaps._trackChannels._outChannels != 0 + if(src_chans != 0 +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + || track->isMidiTrack() +#endif + ) + { + //for(int channel = 0; channel < MIDI_CHANNELS; ++channel) + //{ +// const MusECore::Route sub_r(track, 0); + const MusECore::Route sub_r(track, 0, track->isMidiTrack() ? -1 : 1); + subitem = newSrcList->findItem(sub_r, RouteTreeWidgetItem::ChannelsItem); + if(subitem) + { + // Update the channel y values. + //subitem->computeChannelYValues(); + // Update the number of channels. + subitem->setChannels(); + } + else +// if(!subitem) + { + newSrcList->blockSignals(true); + item->setExpanded(true); + //subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel) << QString() ); + //subitem = new QTreeWidgetItem(item, QStringList() << QString() << QString() ); + + subitem = new RouteTreeWidgetItem(item, QStringList() << QString(), RouteTreeWidgetItem::ChannelsItem, true, sub_r +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + , track->isMidiTrack() ? RouteTreeWidgetItem::ExclusiveMode : RouteTreeWidgetItem::NormalMode +#endif + ); + + //subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(sub_r)); + //subitem->setData(ROUTE_NAME_COL, RouteDialog::ChannelsRole, QVariant::fromValue(QBitArray(MIDI_CHANNELS))); + subitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + subitem->setTextAlignment(ROUTE_NAME_COL, align_flags); + newSrcList->blockSignals(false); + } + // Update the channel y values. + //subitem->computeChannelYValues(); + } +// } +// else +// { +// MusECore::AudioTrack* atrack = static_cast(track); +// // const int chans = atrack->type() == MusECore::Track::AUDIO_SOFTSYNTH ? atrack->totalOutChannels() : atrack->channels(); +// const int chans = atrack->totalRoutableOutputs(); +// //for(int channel = 0; channel < chans; ++channel) +// if(chans != 0) +// { +// //const MusECore::Route src_r(track, channel, 1); +// const MusECore::Route src_r(track, 0, 1); +// subitem = newSrcList->findItem(src_r, RouteTreeWidgetItem::ChannelsItem); +// // if(subitem) +// // { +// // // Update the channel y values. +// // subitem->computeChannelYValues(); +// // } +// // else +// if(!subitem) +// { +// newSrcList->blockSignals(true); +// item->setExpanded(true); +// //subitem = new QTreeWidgetItem(item, QStringList() << QString("ch ") + QString::number(channel + 1) << QString() ); +// //subitem = new QTreeWidgetItem(item, QStringList() << QString("ch ") + QString::number(channel + 1) ); +// subitem = new RouteTreeWidgetItem(item, QStringList() << QString(), RouteTreeWidgetItem::ChannelsItem, true, src_r); +// //subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(src_r)); +// //subitem->setData(ROUTE_NAME_COL, RouteDialog::ChannelsRole, QVariant::fromValue(QBitArray(chans))); +// subitem->setTextAlignment(ROUTE_NAME_COL, align_flags); +// newSrcList->blockSignals(false); +// } +// // Update the channel y values. +// //subitem->computeChannelYValues(); +// } +// } + +// } +// else +// { +// const MusECore::Route r(track, -1); +// item = findSrcItem(r); +// if(item) +// { +// // Update the text. +// item->setText(ROUTE_NAME_COL, track->name()); +// } +// else +// { +// if(!srcCatItem) +// { +// newSrcList->blockSignals(true); +// srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << tracksCat << QString() ); +// //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newSrcList->blockSignals(false); +// } +// newSrcList->blockSignals(true); +// item = new QTreeWidgetItem(srcCatItem, QStringList() << track->name() << trackLabel ); +// item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newSrcList->blockSignals(false); +// } +// +// //if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// { +// //for(int channel = 0; channel < track->channels(); ++channel) +// const int chans = track->type() == MusECore::Track::AUDIO_SOFTSYNTH ? track->totalOutChannels() : track->channels(); +// for(int channel = 0; channel < chans; ++channel) +// { +// const MusECore::Route subr(track, channel, 1); +// subitem = findSrcItem(subr); +// if(!subitem) +// { +// newSrcList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString("ch ") + QString::number(channel + 1) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(subr)); +// newSrcList->blockSignals(false); +// } +// } +// } + } + + const MusECore::RouteList* orl = track->outRoutes(); + for(MusECore::ciRoute r = orl->begin(); r != orl->end(); ++r) + { + // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 + // After: src: TrackA, Channel 4, Remote Channel 2 [dst: TrackB channel 2 Remote Channel 4] + // + // Ex. + // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 + // After: (src: TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 + MusECore::Route src; + MusECore::Route dst; + QString srcName = track->name(); + QString dstName; + switch(r->type) + { + case MusECore::Route::JACK_ROUTE: + //src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, r->channels, -1, 0); + src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, 1, -1, 0); + //dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, r->remoteChannel, r->channels, -1, r->persistentJackPortName); + //dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, r->remoteChannel, -1, -1, r->persistentJackPortName); + dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); + //dst = *r; + dstName = r->name(MusEGlobal::config.preferredRouteNameOrAlias); + break; + case MusECore::Route::MIDI_DEVICE_ROUTE: + continue; // TODO + break; + // Midi ports taken care of below... + case MusECore::Route::MIDI_PORT_ROUTE: + continue; + break; + + /*{ + //continue; // TODO + //src = MusECore::Route(r->midiPort, r->channel); +// MusECore::MidiDevice* md = MusEGlobal::midiPorts[r->midiPort].device(); + if(r->channel == -1) + { + src = MusECore::Route(track); +// if(md) +// dst = MusECore::Route(md); +// else + dst = MusECore::Route(r->midiPort); + dstName = r->name(); + break; + } + +// for(int i = 0; i < MIDI_CHANNELS; ++i) + { +// int chbits = 1 << i; +// if(r->channel & chbits) + { + src = MusECore::Route(track, r->channel); +// src = MusECore::Route(track, chbits); + //src = MusECore::Route(track, i); + // //dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, 1, -1, 0); + //dst = MusECore::Route(track, r->channel, 1); + //dst = MusECore::Route(track, r->channel); + //dst = MusECore::Route(r->midiPort, r->channel); + //dst = MusECore::Route(r->midiPort, 1 << i); +// if(md) +// //dst = MusECore::Route(md, chbits); +// dst = MusECore::Route(md); +// //dst = MusECore::Route(md, r->channel); +// else + //dst = MusECore::Route(r->midiPort, chbits); + //dst = MusECore::Route(r->midiPort); + dst = MusECore::Route(r->midiPort, r->channel); +// srcName = track->name() + QString(" [") + QString::number(i + 1) + QString("]"); + srcName = track->name() + QString(" [") + QString::number(r->channel + 1) + QString("]"); + dstName = r->name(); + //if(dst.channel != -1) + // dstName += QString(" [") + QString::number(i + 1) + QString("]"); + routesItem = findRoutesItem(src, dst); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, srcName); + routesItem->setText(ROUTE_DST_COL, dstName); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + routeList->blockSignals(false); + } + } + } + continue; + } + break;*/ + + case MusECore::Route::TRACK_ROUTE: + src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, r->channels, -1, 0); + dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, r->track, r->remoteChannel, r->channels, -1, 0); + dstName = r->name(); + break; + } + + if(src.channel != -1) + srcName += QString(" [") + QString::number(src.channel) + QString("]"); + if(dst.channel != -1) + dstName += QString(" [") + QString::number(dst.channel) + QString("]"); + + + + + //QString srcName(track->name()); + //if(track->type() == MusECore::Track::AUDIO_OUTPUT) + //{ + // const MusECore::Route s(srcName, false, r->channel); + // srcName = s.name(); + //} + //if(src->channel != -1) + // srcName += QString(":") + QString::number(r->channel); + //const MusECore::Route src(track->name(), false, r->channel, MusECore::Route::TRACK_ROUTE); + //const MusECore::Route src(track->name(), false, r->channel, MusECore::Route::TRACK_ROUTE); + //const MusECore::Route src(MusECore::Route::TRACK_ROUTE, -1, track, r->remoteChannel, r->channels, r->channel, 0); + + //MusECore::Route dst(*r); + //if(dst.type == MusECore::Route::JACK_ROUTE) + // dst.channel = -1; + routesItem = findRoutesItem(src, dst); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, srcName); + routesItem->setText(ROUTE_DST_COL, dstName); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); + routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + if(QPixmap* src_pm = src.icon(true, false)) + routesItem->setIcon(ROUTE_SRC_COL, QIcon(*src_pm)); + if(QPixmap* dst_pm = dst.icon(false, false)) + routesItem->setIcon(ROUTE_DST_COL, QIcon(*dst_pm)); + routeList->blockSignals(false); + } + } + } + //} + + + // + // MIDI ports: + // + + const QString none_str = tr(""); + dstCatItem = newDstList->findCategoryItem(midiPortsCat); + srcCatItem = newSrcList->findCategoryItem(midiPortsCat); + for(int i = 0; i < MIDI_PORTS; ++i) + { + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; + if(!mp) + continue; + MusECore::MidiDevice* md = mp->device(); + // Synth are tracks and devices. Don't list them as devices here, list them as tracks, above. + //if(md && md->deviceType() == MusECore::MidiDevice::SYNTH_MIDI) + // continue; + + QString mdname; + mdname = QString::number(i + 1) + QString(":"); + mdname += md ? md->name() : none_str; + + // + // DESTINATION section: + // + + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + bool non_route_found = false; + // Simulate routes for each midi track's output port and channel properties. + MusECore::MidiTrackList* tl = MusEGlobal::song->midis(); + for(MusECore::ciMidiTrack imt = tl->begin(); imt != tl->end(); ++imt) + { + MusECore::MidiTrack* mt = *imt; + const int port = mt->outPort(); + const int chan = mt->outChannel(); + if(port != i) + continue; + non_route_found = true; + const MusECore::Route src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, mt, chan, -1, -1, NULL); + const MusECore::Route dst(i, chan); + const QString srcName = mt->name() + QString(" [") + QString::number(chan) + QString("]"); + const QString dstName = mdname; + routesItem = findRoutesItem(src, dst); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, srcName); + routesItem->setText(ROUTE_DST_COL, dstName); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); + routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + if(QPixmap* src_pm = src.icon(true, true)) + routesItem->setIcon(ROUTE_SRC_COL, QIcon(*src_pm)); + if(QPixmap* dst_pm = dst.icon(false, true)) + routesItem->setIcon(ROUTE_DST_COL, QIcon(*dst_pm)); + routeList->blockSignals(false); + } + } +#endif // _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + + //if(md->rwFlags() & 0x02) // Readable +// if(md->rwFlags() & 0x01) // Writeable + //if(md->rwFlags() & 0x03) // Both readable and writeable need to be shown + + // Show either all midi ports, or only ports that have a device or have input routes. + if(allMidiPortsButton->isChecked() || md || !mp->inRoutes()->empty() + #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + || non_route_found + #endif + ) + { + const MusECore::Route dst(i, -1); + item = newDstList->findItem(dst, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, mdname); + } + else + { + if(!dstCatItem) + { + newDstList->blockSignals(true); + //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << midiDevicesCat << QString() ); + dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << midiPortsCat, RouteTreeWidgetItem::CategoryItem, false); + dstCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = dstCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + dstCatItem->setFont(ROUTE_NAME_COL, fnt); + dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + dstCatItem->setExpanded(true); + dstCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + dstCatItem->setIcon(ROUTE_NAME_COL, QIcon(*settings_midiport_softsynthsIcon)); + newDstList->blockSignals(false); + } + newDstList->blockSignals(true); + + //item = new QTreeWidgetItem(dstCatItem, QStringList() << mdname << midiDeviceLabel ); + item = new RouteTreeWidgetItem(dstCatItem, QStringList() << mdname, RouteTreeWidgetItem::RouteItem, false, dst); + //item->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newDstList->blockSignals(false); + } +// for(int channel = 0; channel < MIDI_CHANNELS; ++channel) +// { +// const MusECore::Route sub_r(md, channel); +// subitem = findDstItem(sub_r); +// if(!subitem) +// { +// newDstList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel + 1) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(sub_r)); +// +// QFont fnt(subitem->font(ROUTE_NAME_COL)); +// fnt.setPointSize(4); +// //DEBUG_PRST_ROUTES(stderr, "point size:%d family:%s\n", fnt.pointSize(), fnt.family().toLatin1().constData()); +// //subitem->font(ROUTE_NAME_COL).setPointSize(2); +// //subitem->font(ROUTE_TYPE_COL).setPointSize(2); +// subitem->setFont(ROUTE_NAME_COL, fnt); +// subitem->setFont(ROUTE_TYPE_COL, fnt); +// newDstList->blockSignals(false); +// } +// } + + +// #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ +// // Simulate routes for each midi track's output port and channel properties. +// MusECore::MidiTrackList* tl = MusEGlobal::song->midis(); +// for(MusECore::ciMidiTrack imt = tl->begin(); imt != tl->end(); ++imt) +// { +// MusECore::MidiTrack* mt = *imt; +// const int port = mt->outPort(); +// const int chan = mt->outChannel(); +// if(port != i) +// continue; +// const MusECore::Route src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, mt, chan, -1, -1, NULL); +// const MusECore::Route dst(i, chan); +// const QString srcName = mt->name() + QString(" [") + QString::number(chan) + QString("]"); +// const QString dstName = mdname; +// routesItem = findRoutesItem(src, dst); +// if(routesItem) +// { +// // Update the text. +// routesItem->setText(ROUTE_SRC_COL, srcName); +// routesItem->setText(ROUTE_DST_COL, dstName); +// } +// else +// { +// routeList->blockSignals(true); +// routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); +// routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); +// routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); +// routeList->blockSignals(false); +// } +// } +// #endif // _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + + const MusECore::RouteList* rl = mp->inRoutes(); + for(MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) + { + switch(r->type) + { + case MusECore::Route::TRACK_ROUTE: + +#ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + { + if(!r->track || !r->track->isMidiTrack()) + continue; + +// const MusECore::Route src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, r->track, r->channel, r->channels, r->remoteChannel, NULL); + const MusECore::Route& src = *r; + QString srcName = r->name(); + QString dstName = mdname; + //const MusECore::Route dst(i, -1); + const MusECore::Route dst(i, src.channel); + + if(src.channel != -1) + srcName += QString(" [") + QString::number(src.channel) + QString("]"); +// if(dst.channel != -1) +// dstName += QString(" [") + QString::number(dst.channel) + QString("]"); + + routesItem = findRoutesItem(src, dst); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, srcName); + routesItem->setText(ROUTE_DST_COL, dstName); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); + routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + if(QPixmap* src_pm = src.icon(true, true)) + routesItem->setIcon(ROUTE_SRC_COL, QIcon(*src_pm)); + if(QPixmap* dst_pm = dst.icon(false, true)) + routesItem->setIcon(ROUTE_DST_COL, QIcon(*dst_pm)); + routeList->blockSignals(false); + } +// if(!r->jackPort) +// routesItem->setBackground(ROUTE_SRC_COL, routesItem->background(ROUTE_SRC_COL).color().darker()); + } +#endif // _USE_MIDI_TRACK_OUT_ROUTES_ + + break; + + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; + } + } + } +// else if(track->type() != MusECore::Track::AUDIO_AUX) +// { +// const MusECore::Route r(track, -1); +// item = findDstItem(r); +// if(!item) +// { +// if(!dstCatItem) +// { +// newDstList->blockSignals(true); +// dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << QString("Tracks") << QString() ); +// //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newDstList->blockSignals(false); +// } +// newDstList->blockSignals(true); +// item = new QTreeWidgetItem(dstCatItem, QStringList() << track->name() << QString("Track") ); +// item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newDstList->blockSignals(false); +// } +// //if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// { +// //for(int channel = 0; channel < track->channels(); ++channel) +// const int chans = track->type() == MusECore::Track::AUDIO_SOFTSYNTH ? track->totalInChannels() : track->channels(); +// for(int channel = 0; channel < chans; ++channel) +// { +// const MusECore::Route subr(track, channel, 1); +// subitem = findDstItem(subr); +// if(!subitem) +// { +// newDstList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(subr)); +// newDstList->blockSignals(false); +// } +// } +// } +// } + + // + // SOURCE section: + // + + //if(md->rwFlags() & 0x01) // Writeable +// if(md->rwFlags() & 0x02) // Readable + //if(md->rwFlags() & 0x03) // Both readable and writeable need to be shown + + // Show only ports that have a device, or have output routes. + if(allMidiPortsButton->isChecked() || md || !mp->outRoutes()->empty()) + { + const MusECore::Route src(i, -1); + item = newSrcList->findItem(src, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, mdname); + } + else + { + if(!srcCatItem) + { + newSrcList->blockSignals(true); + //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << midiDevicesCat << QString() ); + srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << midiPortsCat, RouteTreeWidgetItem::CategoryItem, true); + srcCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = srcCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + srcCatItem->setFont(ROUTE_NAME_COL, fnt); + srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + srcCatItem->setExpanded(true); + srcCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + srcCatItem->setIcon(ROUTE_NAME_COL, QIcon(*settings_midiport_softsynthsIcon)); + newSrcList->blockSignals(false); + } + newSrcList->blockSignals(true); + + //item = new QTreeWidgetItem(srcCatItem, QStringList() << mdname << midiDeviceLabel ); + item = new RouteTreeWidgetItem(srcCatItem, QStringList() << mdname, RouteTreeWidgetItem::RouteItem, true, src); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newSrcList->blockSignals(false); + } +// for(int channel = 0; channel < MIDI_CHANNELS; ++channel) +// { +// const MusECore::Route src_r(md, channel); +// subitem = findSrcItem(src_r); +// if(!subitem) +// { +// newSrcList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel + 1) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(src_r)); +// newSrcList->blockSignals(false); +// } +// } + +// else +// { +// const MusECore::Route r(track, -1); +// item = findSrcItem(r); +// if(!item) +// { +// if(!srcCatItem) +// { +// newSrcList->blockSignals(true); +// srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << QString("Tracks") << QString() ); +// //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newSrcList->blockSignals(false); +// } +// newSrcList->blockSignals(true); +// item = new QTreeWidgetItem(srcCatItem, QStringList() << track->name() << QString("Track") ); +// item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newSrcList->blockSignals(false); +// } +// +// //if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// { +// //for(int channel = 0; channel < track->channels(); ++channel) +// const int chans = track->type() == MusECore::Track::AUDIO_SOFTSYNTH ? track->totalOutChannels() : track->channels(); +// for(int channel = 0; channel < chans; ++channel) +// { +// const MusECore::Route subr(track, channel, 1); +// subitem = findSrcItem(subr); +// if(!subitem) +// { +// newSrcList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString("ch ") + QString::number(channel + 1) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(subr)); +// newSrcList->blockSignals(false); +// } +// } +// } +// } + + const MusECore::RouteList* rl = mp->outRoutes(); + for(MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) + { + // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 + // After: src: TrackA, Channel 4, Remote Channel 2 [dst: TrackB channel 2 Remote Channel 4] + // + // Ex. + // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 + // After: (src: TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 + //MusECore::Route src(md, -1); + //MusECore::Route dst; + //QString srcName = mdname; + //QString dstName; + switch(r->type) + { + case MusECore::Route::TRACK_ROUTE: + { + if(!r->track || !r->track->isMidiTrack()) + continue; + + //const MusECore::Route dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); + const MusECore::Route& dst = *r; + QString dstName = r->name(); + QString srcName = mdname; + //const MusECore::Route src(i, -1); + const MusECore::Route src(i, dst.channel); + + //if(src.channel != -1) + // srcName += QString(" [") + QString::number(src.channel) + QString("]"); + if(dst.channel != -1) + dstName += QString(" [") + QString::number(dst.channel) + QString("]"); + + routesItem = findRoutesItem(src, dst); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, srcName); + routesItem->setText(ROUTE_DST_COL, dstName); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); + routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + if(QPixmap* src_pm = src.icon(true, true)) + routesItem->setIcon(ROUTE_SRC_COL, QIcon(*src_pm)); + if(QPixmap* dst_pm = dst.icon(false, true)) + routesItem->setIcon(ROUTE_DST_COL, QIcon(*dst_pm)); + routeList->blockSignals(false); + } +// if(!r->jackPort) +// routesItem->setBackground(ROUTE_DST_COL, routesItem->background(ROUTE_DST_COL).color().darker()); + } + break; + + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + continue; + } + + // QString srcName = mdname; + // MusECore::Route src(md, -1); + // + // if(src.channel != -1) + // srcName += QString(" [") + QString::number(src.channel) + QString("]"); + // if(dst.channel != -1) + // dstName += QString(" [") + QString::number(dst.channel) + QString("]"); + + + + + //QString srcName(track->name()); + //if(track->type() == MusECore::Track::AUDIO_OUTPUT) + //{ + // const MusECore::Route s(srcName, false, r->channel); + // srcName = s.name(); + //} + //if(src->channel != -1) + // srcName += QString(":") + QString::number(r->channel); + //const MusECore::Route src(track->name(), false, r->channel, MusECore::Route::TRACK_ROUTE); + //const MusECore::Route src(track->name(), false, r->channel, MusECore::Route::TRACK_ROUTE); + //const MusECore::Route src(MusECore::Route::TRACK_ROUTE, -1, track, r->remoteChannel, r->channels, r->channel, 0); + + //MusECore::Route dst(*r); + //if(dst.type == MusECore::Route::JACK_ROUTE) + // dst.channel = -1; + // routesItem = findRoutesItem(src, dst); + // if(routesItem) + // { + // // Update the text. + // routesItem->setText(ROUTE_SRC_COL, srcName); + // routesItem->setText(ROUTE_DST_COL, dstName); + // } + // else + // { + // routeList->blockSignals(true); + // routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + // routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + // routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + // routeList->blockSignals(false); + // } + } + } + } + + + // + // MIDI devices: + // + + dstCatItem = newDstList->findCategoryItem(midiDevicesCat); + srcCatItem = newSrcList->findCategoryItem(midiDevicesCat); + for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + MusECore::MidiDevice* md = *i; + // Synth are tracks and devices. Don't list them as devices here, list them as tracks, above. + if(md->deviceType() == MusECore::MidiDevice::SYNTH_MIDI) + continue; + +// QString mdname; +// if(md->midiPort() != -1) +// mdname = QString::number(md->midiPort() + 1) + QString(":"); +// mdname += md->name(); + QString mdname = md->name(); + // + // DESTINATION section: + // + + //if(md->rwFlags() & 0x02) // Readable + //if(md->rwFlags() & 0x01) // Writeable + if(md->rwFlags() & 0x03) // Both readable and writeable need to be shown + { + const MusECore::Route dst(md, -1); + item = newDstList->findItem(dst, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, mdname); + } + else + { + if(!dstCatItem) + { + newDstList->blockSignals(true); + //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << midiDevicesCat << QString() ); + dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << midiDevicesCat, RouteTreeWidgetItem::CategoryItem, false); + dstCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = dstCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + dstCatItem->setFont(ROUTE_NAME_COL, fnt); + dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + dstCatItem->setExpanded(true); + dstCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + newDstList->blockSignals(false); + } + newDstList->blockSignals(true); + + //item = new QTreeWidgetItem(dstCatItem, QStringList() << mdname << midiDeviceLabel ); + item = new RouteTreeWidgetItem(dstCatItem, QStringList() << mdname, RouteTreeWidgetItem::RouteItem, false, dst); + //item->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newDstList->blockSignals(false); + } +// for(int channel = 0; channel < MIDI_CHANNELS; ++channel) +// { +// const MusECore::Route sub_r(md, channel); +// subitem = findDstItem(sub_r); +// if(!subitem) +// { +// newDstList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel + 1) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(sub_r)); +// +// QFont fnt(subitem->font(ROUTE_NAME_COL)); +// fnt.setPointSize(4); +// //DEBUG_PRST_ROUTES(stderr, "point size:%d family:%s\n", fnt.pointSize(), fnt.family().toLatin1().constData()); +// //subitem->font(ROUTE_NAME_COL).setPointSize(2); +// //subitem->font(ROUTE_TYPE_COL).setPointSize(2); +// subitem->setFont(ROUTE_NAME_COL, fnt); +// subitem->setFont(ROUTE_TYPE_COL, fnt); +// newDstList->blockSignals(false); +// } +// } + + const MusECore::RouteList* rl = md->inRoutes(); + for(MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) + { + // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 + // After: [src TrackA, Channel 4, Remote Channel 2] dst: TrackB channel 2 Remote Channel 4 + // + // Ex. + // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 + // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 + //MusECore::Route src; + //MusECore::Route dst(md, -1); + //QString srcName; + //QString dstName = mdname; + switch(r->type) + { + case MusECore::Route::JACK_ROUTE: + { + //src = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, r->remoteChannel, r->channels, -1, r->persistentJackPortName); + //src = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, r->remoteChannel, -1, -1, r->persistentJackPortName); + const MusECore::Route src = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); + //src = *r; + //dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, r->channels, -1, 0); + //dst = MusECore::Route(MusECore::Route::MIDI_DEVICE_ROUTE, -1, md, r->channel, 1, -1, 0); + //dst = MusECore::Route(md, r->channel); + //dst = MusECore::Route(md, -1); + QString srcName = r->name(MusEGlobal::config.preferredRouteNameOrAlias); + QString dstName = mdname; + const MusECore::Route dst(md, -1); + + if(src.channel != -1) + srcName += QString(" [") + QString::number(src.channel) + QString("]"); + if(dst.channel != -1) + dstName += QString(" [") + QString::number(dst.channel) + QString("]"); + + routesItem = findRoutesItem(src, dst); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, srcName); + routesItem->setText(ROUTE_DST_COL, dstName); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); + routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + if(QPixmap* src_pm = src.icon(true, true)) + routesItem->setIcon(ROUTE_SRC_COL, QIcon(*src_pm)); + if(QPixmap* dst_pm = dst.icon(false, true)) + routesItem->setIcon(ROUTE_DST_COL, QIcon(*dst_pm)); + routeList->blockSignals(false); + } + + QBrush br; + if(r->jackPort) + { + if(routeList->alternatingRowColors()) + { + const int idx = routeList->indexOfTopLevelItem(routesItem); + br = (idx != -1 && (idx & 0x01)) ? routeList->palette().alternateBase() : routeList->palette().base(); + } + else + br = routeList->palette().base(); + + routesItem->setBackground(ROUTE_SRC_COL, br); + routesItem->setForeground(ROUTE_SRC_COL, routeList->palette().windowText()); + } + else + { + //QPalette pal(QColor(Qt::red)); +// if(routeList->alternatingRowColors()) +// { +// const int idx = routeList->indexOfTopLevelItem(routesItem); +// //br = (idx != -1 && (idx & 0x01)) ? pal.alternateBase() : pal.base(); +// br = (idx != -1 && (idx & 0x01)) ? QBrush(QColor(Qt::red).darker()) : QBrush(Qt::red); +// } +// else + br = QBrush(Qt::red); + + routesItem->setBackground(ROUTE_SRC_COL, br); + //routesItem->setForeground(ROUTE_SRC_COL, pal.windowText()); + } + } + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + case MusECore::Route::TRACK_ROUTE: + continue; + } + +// QString dstName = mdname; +// MusECore::Route dst(md, -1); +// +// if(src.channel != -1) +// srcName += QString(" [") + QString::number(src.channel) + QString("]"); +// if(dst.channel != -1) +// dstName += QString(" [") + QString::number(dst.channel) + QString("]"); + + + +// QString srcName(r->name()); +// if(r->channel != -1) +// srcName += QString(":") + QString::number(r->channel); +// +// +// MusECore::Route src(*r); +// if(src.type == MusECore::Route::JACK_ROUTE) +// src.channel = -1; +// //const MusECore::Route dst(track->name(), true, r->channel, MusECore::Route::TRACK_ROUTE); +// const MusECore::Route dst(MusECore::Route::TRACK_ROUTE, -1, track, r->remoteChannel, r->channels, r->channel, 0); +// +// +// src.remoteChannel = src.channel; +// dst.remoteChannel = dst.channel; +// const int src_chan = src.channel; +// src.channel = dst.channel; +// dst.channel = src_chan; + + +// routesItem = findRoutesItem(src, dst); +// if(routesItem) +// { +// // Update the text. +// routesItem->setText(ROUTE_SRC_COL, srcName); +// routesItem->setText(ROUTE_DST_COL, dstName); +// } +// else +// { +// routeList->blockSignals(true); +// routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); +// routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); +// routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); +// routeList->blockSignals(false); +// } + } + } +// else if(track->type() != MusECore::Track::AUDIO_AUX) +// { +// const MusECore::Route r(track, -1); +// item = findDstItem(r); +// if(!item) +// { +// if(!dstCatItem) +// { +// newDstList->blockSignals(true); +// dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << QString("Tracks") << QString() ); +// //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newDstList->blockSignals(false); +// } +// newDstList->blockSignals(true); +// item = new QTreeWidgetItem(dstCatItem, QStringList() << track->name() << QString("Track") ); +// item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newDstList->blockSignals(false); +// } +// //if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// { +// //for(int channel = 0; channel < track->channels(); ++channel) +// const int chans = track->type() == MusECore::Track::AUDIO_SOFTSYNTH ? track->totalInChannels() : track->channels(); +// for(int channel = 0; channel < chans; ++channel) +// { +// const MusECore::Route subr(track, channel, 1); +// subitem = findDstItem(subr); +// if(!subitem) +// { +// newDstList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(subr)); +// newDstList->blockSignals(false); +// } +// } +// } +// } + + // + // SOURCE section: + // + + //if(md->rwFlags() & 0x01) // Writeable + //if(md->rwFlags() & 0x02) // Readable + if(md->rwFlags() & 0x03) // Both readable and writeable need to be shown + { + const MusECore::Route src(md, -1); + item = newSrcList->findItem(src, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, mdname); + } + else + { + if(!srcCatItem) + { + newSrcList->blockSignals(true); + //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << midiDevicesCat << QString() ); + srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << midiDevicesCat, RouteTreeWidgetItem::CategoryItem, true); + srcCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = srcCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + srcCatItem->setFont(ROUTE_NAME_COL, fnt); + srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + srcCatItem->setExpanded(true); + srcCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + newSrcList->blockSignals(false); + } + newSrcList->blockSignals(true); + //item = new QTreeWidgetItem(srcCatItem, QStringList() << mdname << midiDeviceLabel ); + item = new RouteTreeWidgetItem(srcCatItem, QStringList() << mdname, RouteTreeWidgetItem::RouteItem, true, src); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newSrcList->blockSignals(false); + } +// for(int channel = 0; channel < MIDI_CHANNELS; ++channel) +// { +// const MusECore::Route src_r(md, channel); +// subitem = findSrcItem(src_r); +// if(!subitem) +// { +// newSrcList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString::number(channel + 1) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(src_r)); +// newSrcList->blockSignals(false); +// } +// } + +// else +// { +// const MusECore::Route r(track, -1); +// item = findSrcItem(r); +// if(!item) +// { +// if(!srcCatItem) +// { +// newSrcList->blockSignals(true); +// srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << QString("Tracks") << QString() ); +// //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newSrcList->blockSignals(false); +// } +// newSrcList->blockSignals(true); +// item = new QTreeWidgetItem(srcCatItem, QStringList() << track->name() << QString("Track") ); +// item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); +// newSrcList->blockSignals(false); +// } +// +// //if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH) +// { +// //for(int channel = 0; channel < track->channels(); ++channel) +// const int chans = track->type() == MusECore::Track::AUDIO_SOFTSYNTH ? track->totalOutChannels() : track->channels(); +// for(int channel = 0; channel < chans; ++channel) +// { +// const MusECore::Route subr(track, channel, 1); +// subitem = findSrcItem(subr); +// if(!subitem) +// { +// newSrcList->blockSignals(true); +// subitem = new QTreeWidgetItem(item, QStringList() << QString("ch ") + QString::number(channel + 1) << QString() ); +// subitem->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(subr)); +// newSrcList->blockSignals(false); +// } +// } +// } +// } + + const MusECore::RouteList* rl = md->outRoutes(); + for(MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) + { + // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 + // After: src: TrackA, Channel 4, Remote Channel 2 [dst: TrackB channel 2 Remote Channel 4] + // + // Ex. + // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 + // After: (src: TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 + //MusECore::Route src(md, -1); + //MusECore::Route dst; + //QString srcName = mdname; + //QString dstName; + switch(r->type) + { + case MusECore::Route::JACK_ROUTE: + { + //src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, r->channels, -1, 0); + //src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, 1, -1, 0); + //src = MusECore::Route(md, r->channel); + //src = MusECore::Route(md, -1); + //dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, r->remoteChannel, r->channels, -1, r->persistentJackPortName); + //dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, r->remoteChannel, -1, -1, r->persistentJackPortName); + const MusECore::Route dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); + //dst = *r; + QString dstName = r->name(MusEGlobal::config.preferredRouteNameOrAlias); + QString srcName = mdname; + const MusECore::Route src(md, -1); + + if(src.channel != -1) + srcName += QString(" [") + QString::number(src.channel) + QString("]"); + if(dst.channel != -1) + dstName += QString(" [") + QString::number(dst.channel) + QString("]"); + + routesItem = findRoutesItem(src, dst); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, srcName); + routesItem->setText(ROUTE_DST_COL, dstName); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); + routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + if(QPixmap* src_pm = src.icon(true, true)) + routesItem->setIcon(ROUTE_SRC_COL, QIcon(*src_pm)); + if(QPixmap* dst_pm = dst.icon(false, true)) + routesItem->setIcon(ROUTE_DST_COL, QIcon(*dst_pm)); + routeList->blockSignals(false); + } + + QBrush br; + if(r->jackPort) + { + if(routeList->alternatingRowColors()) + { + const int idx = routeList->indexOfTopLevelItem(routesItem); + br = (idx != -1 && (idx & 0x01)) ? routeList->palette().alternateBase() : routeList->palette().base(); + } + else + br = routeList->palette().base(); + + routesItem->setBackground(ROUTE_DST_COL, br); + routesItem->setForeground(ROUTE_DST_COL, routeList->palette().windowText()); + } + else + { + //QPalette pal(QColor(Qt::red)); +// if(routeList->alternatingRowColors()) +// { +// const int idx = routeList->indexOfTopLevelItem(routesItem); +// //br = (idx != -1 && (idx & 0x01)) ? pal.alternateBase() : pal.base(); +// br = (idx != -1 && (idx & 0x01)) ? QBrush(QColor(Qt::red).darker()) : QBrush(Qt::red); +// } +// else + br = QBrush(Qt::red); + + routesItem->setBackground(ROUTE_DST_COL, br); + //routesItem->setForeground(ROUTE_DST_COL, pal.windowText()); + } + } + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + case MusECore::Route::TRACK_ROUTE: + continue; + } + + // QString srcName = mdname; + // MusECore::Route src(md, -1); + // + // if(src.channel != -1) + // srcName += QString(" [") + QString::number(src.channel) + QString("]"); + // if(dst.channel != -1) + // dstName += QString(" [") + QString::number(dst.channel) + QString("]"); + + + + + //QString srcName(track->name()); + //if(track->type() == MusECore::Track::AUDIO_OUTPUT) + //{ + // const MusECore::Route s(srcName, false, r->channel); + // srcName = s.name(); + //} + //if(src->channel != -1) + // srcName += QString(":") + QString::number(r->channel); + //const MusECore::Route src(track->name(), false, r->channel, MusECore::Route::TRACK_ROUTE); + //const MusECore::Route src(track->name(), false, r->channel, MusECore::Route::TRACK_ROUTE); + //const MusECore::Route src(MusECore::Route::TRACK_ROUTE, -1, track, r->remoteChannel, r->channels, r->channel, 0); + + //MusECore::Route dst(*r); + //if(dst.type == MusECore::Route::JACK_ROUTE) + // dst.channel = -1; + // routesItem = findRoutesItem(src, dst); + // if(routesItem) + // { + // // Update the text. + // routesItem->setText(ROUTE_SRC_COL, srcName); + // routesItem->setText(ROUTE_DST_COL, dstName); + // } + // else + // { + // routeList->blockSignals(true); + // routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); + // routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); + // routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); + // routeList->blockSignals(false); + // } + } + } + } + + // + // JACK ports: + // + + if(MusEGlobal::checkAudioDevice()) + { + //------------ + // Jack audio: + //------------ + + srcCatItem = newSrcList->findCategoryItem(jackCat); + MusECore::RouteList in_rl; + for(std::list::iterator i = tmpJackOutPorts.begin(); i != tmpJackOutPorts.end(); ++i) + { + const MusECore::Route in_r(*i, false, -1, MusECore::Route::JACK_ROUTE); + item = newSrcList->findItem(in_r, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, in_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + } + else + { + if(!srcCatItem) + { + newSrcList->blockSignals(true); + //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << jackCat << QString() ); + srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << jackCat, RouteTreeWidgetItem::CategoryItem, true); + srcCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = srcCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + srcCatItem->setFont(ROUTE_NAME_COL, fnt); + srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + srcCatItem->setExpanded(true); + srcCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + srcCatItem->setIcon(ROUTE_NAME_COL, QIcon(*routesInIcon)); + newSrcList->blockSignals(false); + } + newSrcList->blockSignals(true); + //item = new QTreeWidgetItem(srcCatItem, QStringList() << in_r.name() << jackLabel ); + item = new RouteTreeWidgetItem(srcCatItem, + QStringList() << in_r.name(MusEGlobal::config.preferredRouteNameOrAlias), + RouteTreeWidgetItem::RouteItem, + true, + in_r); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(in_r)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newSrcList->blockSignals(false); + } + in_rl.push_back(in_r); + } + + dstCatItem = newDstList->findCategoryItem(jackCat); + for(std::list::iterator i = tmpJackInPorts.begin(); i != tmpJackInPorts.end(); ++i) + { + const MusECore::Route out_r(*i, true, -1, MusECore::Route::JACK_ROUTE); + item = newDstList->findItem(out_r, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, out_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + } + else + { + if(!dstCatItem) + { + newDstList->blockSignals(true); + //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << jackCat << QString() ); + dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << jackCat, RouteTreeWidgetItem::CategoryItem, false); + dstCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = dstCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + dstCatItem->setFont(ROUTE_NAME_COL, fnt); + dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + dstCatItem->setExpanded(true); + dstCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + dstCatItem->setIcon(ROUTE_NAME_COL, QIcon(*routesOutIcon)); + newDstList->blockSignals(false); + } + newDstList->blockSignals(true); + //item = new QTreeWidgetItem(dstCatItem, QStringList() << out_r.name() << jackLabel ); + item = new RouteTreeWidgetItem(dstCatItem, + QStringList() << out_r.name(MusEGlobal::config.preferredRouteNameOrAlias), + RouteTreeWidgetItem::RouteItem, + false, + out_r); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(out_r)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newDstList->blockSignals(false); + } + const QIcon src_ico(*routesInIcon); + const QIcon dst_ico(*routesOutIcon); + for(MusECore::ciRoute i = in_rl.begin(); i != in_rl.end(); ++i) + { + const MusECore::Route& in_r = *i; + if(MusECore::routeCanDisconnect(in_r, out_r)) + { + routesItem = findRoutesItem(in_r, out_r); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, in_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + routesItem->setText(ROUTE_DST_COL, out_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, + QStringList() << in_r.name(MusEGlobal::config.preferredRouteNameOrAlias) << out_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); + routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(in_r)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(out_r)); + routesItem->setIcon(ROUTE_SRC_COL, src_ico); + routesItem->setIcon(ROUTE_DST_COL, dst_ico); + routeList->blockSignals(false); + } + } + } + } + + //------------ + // Jack midi: + //------------ + + srcCatItem = newSrcList->findCategoryItem(jackMidiCat); + in_rl.clear(); + for(std::list::iterator i = tmpJackMidiOutPorts.begin(); i != tmpJackMidiOutPorts.end(); ++i) + { + const MusECore::Route in_r(*i, false, -1, MusECore::Route::JACK_ROUTE); + item = newSrcList->findItem(in_r, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, in_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + } + else + { + if(!srcCatItem) + { + newSrcList->blockSignals(true); + //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << jackMidiCat << QString() ); + srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << jackMidiCat, RouteTreeWidgetItem::CategoryItem, true); + srcCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = srcCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + srcCatItem->setFont(ROUTE_NAME_COL, fnt); + srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + srcCatItem->setExpanded(true); + srcCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + srcCatItem->setIcon(ROUTE_NAME_COL, QIcon(*routesMidiInIcon)); + newSrcList->blockSignals(false); + } + newSrcList->blockSignals(true); + //item = new QTreeWidgetItem(srcCatItem, QStringList() << in_r.name() << jackMidiLabel ); + item = new RouteTreeWidgetItem(srcCatItem, + QStringList() << in_r.name(MusEGlobal::config.preferredRouteNameOrAlias), + RouteTreeWidgetItem::RouteItem, + true, + in_r); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(in_r)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newSrcList->blockSignals(false); + } + in_rl.push_back(in_r); + } + + dstCatItem = newDstList->findCategoryItem(jackMidiCat); + for(std::list::iterator i = tmpJackMidiInPorts.begin(); i != tmpJackMidiInPorts.end(); ++i) + { + const MusECore::Route out_r(*i, true, -1, MusECore::Route::JACK_ROUTE); + item = newDstList->findItem(out_r, RouteTreeWidgetItem::RouteItem); + if(item) + { + // Update the text. + item->setText(ROUTE_NAME_COL, out_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + } + else + { + if(!dstCatItem) + { + newDstList->blockSignals(true); + //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << jackMidiCat << QString() ); + dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << jackMidiCat, RouteTreeWidgetItem::CategoryItem, false); + dstCatItem->setFlags(Qt::ItemIsEnabled); + QFont fnt = dstCatItem->font(ROUTE_NAME_COL); + fnt.setBold(true); + fnt.setItalic(true); + //fnt.setPointSize(fnt.pointSize() + 2); + dstCatItem->setFont(ROUTE_NAME_COL, fnt); + dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); + dstCatItem->setExpanded(true); + dstCatItem->setBackground(ROUTE_NAME_COL, palette().mid()); + dstCatItem->setIcon(ROUTE_NAME_COL, QIcon(*routesMidiOutIcon)); + newDstList->blockSignals(false); + } + newDstList->blockSignals(true); + //item = new QTreeWidgetItem(dstCatItem, QStringList() << out_r.name() << jackMidiLabel ); + item = new RouteTreeWidgetItem(dstCatItem, + QStringList() << out_r.name(MusEGlobal::config.preferredRouteNameOrAlias), + RouteTreeWidgetItem::RouteItem, + false, + out_r); + //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(out_r)); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + item->setTextAlignment(ROUTE_NAME_COL, align_flags); + newDstList->blockSignals(false); + } + const QIcon src_ico(*routesMidiInIcon); + const QIcon dst_ico(*routesMidiOutIcon); + for(MusECore::ciRoute i = in_rl.begin(); i != in_rl.end(); ++i) + { + const MusECore::Route& in_r = *i; + if(MusECore::routeCanDisconnect(in_r, out_r)) + { + routesItem = findRoutesItem(in_r, out_r); + if(routesItem) + { + // Update the text. + routesItem->setText(ROUTE_SRC_COL, in_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + routesItem->setText(ROUTE_DST_COL, out_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + } + else + { + routeList->blockSignals(true); + routesItem = new QTreeWidgetItem(routeList, + QStringList() << in_r.name(MusEGlobal::config.preferredRouteNameOrAlias) << out_r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); + routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); + routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(in_r)); + routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(out_r)); + routesItem->setIcon(ROUTE_SRC_COL, src_ico); + routesItem->setIcon(ROUTE_DST_COL, dst_ico); + routeList->blockSignals(false); + } + } + } + } + } +} + +void MusE::startRouteDialog() +{ + if(routeDialog == 0) + // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). + routeDialog = new MusEGui::RouteDialog; + routeDialog->show(); + routeDialog->raise(); +} + + } // namespace MusEGui diff -Nru muse-2.1.2/muse/mixer/routedialog.h muse-3.0.2+ds1/muse/mixer/routedialog.h --- muse-2.1.2/muse/mixer/routedialog.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/routedialog.h 2017-12-04 21:01:18.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: routedialog.h,v 1.2 2004/01/31 17:31:49 wschweer Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,15 +25,389 @@ #ifndef __ROUTEDIALOG_H__ #define __ROUTEDIALOG_H__ -#include "ui_routedialogbase.h" +#include +#include +#include +#include +#include +#include +#include + +//#include "ui_routedialogbase.h" #include "type_defs.h" +#include "route.h" class QCloseEvent; -class QDialog; +class QMouseEvent; +class QWheelEvent; +class QString; +class QMouseEvent; +class QItemSelectionModel; +class QPainter; +class QColor; +class QResizeEvent; + +namespace MusEGui { + + +typedef QList RouteTreeItemList; + +//--------------------------------------------------------- +// RouteTreeWidgetItem +//--------------------------------------------------------- + +struct RouteChannelsStruct +{ + // Whether the channel is selected (highlight colour). + bool _selected; + // Whether the channel is selected as part of one or more complete routes (yellow colour). + bool _routeSelected; + // Whether the channel is connected to any other route (whether to draw a header connector line). + bool _connected; + // Rectangle of the channel. + QRect _buttonRect; + // Contains pre-computed handy y value for drawing connection lines. + int _lineY; + + RouteChannelsStruct() : _selected(false), _routeSelected(false), _connected(false), _lineY(-1) { } + void toggleSelected() { _selected = !_selected; } + void toggleRouteSelected() { _routeSelected = !_routeSelected; } + void toggleConnected() { _connected = !_connected; } +}; + +class RouteChannelsList : public QVector +{ + public: + // Returns true if any channel was changed. + bool fillSelected(bool v) { + bool changed = false; const int sz = size(); + for(int i = 0; i < sz; ++i) { + bool& sel = operator[](i)._selected; + if(sel != v) changed = true; + sel = v; + } + return changed; + } + bool selected(int c) const { if(c >= size()) return false; return at(c)._selected; } + void select(int c, bool v) { if(c >= size()) return; operator[](c)._selected = v; } + void toggleSelected(int c) { if(c >= size()) return; operator[](c).toggleSelected(); } + + // Returns true if any channel was changed. + bool fillRouteSelected(bool v) { + bool changed = false; const int sz = size(); + for(int i = 0; i < sz; ++i) { + bool& sel = operator[](i)._routeSelected; + if(sel != v) changed = true; + sel = v; + } + return changed; + } + bool routeSelected(int c) const { if(c >= size()) return false; return at(c)._routeSelected; } + void routeSelect(int c, bool v) { if(c >= size()) return; operator[](c)._routeSelected = v; } + void toggleRouteSelected(int c) { if(c >= size()) return; operator[](c).toggleRouteSelected(); } + + // Returns true if any channel was changed. + bool fillConnected(bool v) { + bool changed = false; const int sz = size(); + for(int i = 0; i < sz; ++i) { + bool& sel = operator[](i)._connected; + if(sel != v) changed = true; + sel = v; + } + return changed; + } + bool connected(int c) const { if(c >= size()) return false; return at(c)._connected; } + void setConnected(int c, bool v) { if(c >= size()) return; operator[](c)._connected = v; } + void toggleConnected(int c) { if(c >= size()) return; operator[](c).toggleConnected(); } + + // Pre-computed handy y value for drawing connection lines. + int lineY(int c) const { if(c >= size()) return -1; return at(c)._lineY; } + // Set pre-computed handy y value for drawing connection lines. + void setLineY(int c, int v) { if(c >= size()) return; operator[](c)._lineY = v; } + + // How many channels are connected. + int connectedChannels() const; + // Returns the smallest width that can fit a channel bar. + static int minimumWidthHint(); + // Returns the minimum width of the array that will fit into the given width constraint. + int widthHint(int width) const; + // Returns the minimum height of the array that will fit into the given width constraint. + int heightHint(int width) const; + // Returns a suitable size based on the given width constraint. + QSize sizeHint(int width) const { return QSize(widthHint(width), heightHint(width)); } + // How many channels fit into the width. + static int channelsPerWidth(int width); + // How many groups accommodate the given number of channels, all on a single bar. + static int groupsPerChannels(int channels); + // How many bars accommodate the item's total channels, for the given number of maximum number of channels on a single bar. + int barsPerColChannels(int cc) const; +}; + +class RouteTreeWidgetItem : public QTreeWidgetItem +{ + public: + enum ItemType { NormalItem = Type, CategoryItem = UserType, RouteItem = UserType + 1, ChannelsItem = UserType + 2}; + enum ItemMode { NormalMode, ExclusiveMode }; + // A data role to pass the item type from item to delegate. + //enum ItemDataRole { TypeRole = Qt::UserRole}; + + private: + bool _isInput; + MusECore::Route _route; + RouteChannelsList _channels; + ItemMode _itemMode; + int _curChannel; + + void init(); + + public: + // Overrides for QTreeWidgetItem constructor... + RouteTreeWidgetItem(int type = NormalItem, bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) + : QTreeWidgetItem(type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } + + RouteTreeWidgetItem(const QStringList& strings, int type = NormalItem, + bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) + : QTreeWidgetItem(strings, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } + + RouteTreeWidgetItem(QTreeWidget* parent, int type = NormalItem, + bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) + : QTreeWidgetItem(parent, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } + + RouteTreeWidgetItem(QTreeWidget* parent, const QStringList& strings, int type = NormalItem, + bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) + : QTreeWidgetItem(parent, strings, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } + + RouteTreeWidgetItem(QTreeWidget* parent, QTreeWidgetItem* preceding, int type = NormalItem, + bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) + : QTreeWidgetItem(parent, preceding, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } + + RouteTreeWidgetItem(QTreeWidgetItem* parent, int type = NormalItem, + bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) + : QTreeWidgetItem(parent, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } + + RouteTreeWidgetItem(QTreeWidgetItem* parent, const QStringList& strings, int type = NormalItem, + bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) + : QTreeWidgetItem(parent, strings, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } + + RouteTreeWidgetItem(QTreeWidgetItem* parent, QTreeWidgetItem* preceding, int type = NormalItem, + bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) + : QTreeWidgetItem(parent, preceding, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } + + MusECore::Route& route() { return _route; } + // Whether this item should exist or not, based on _route and the item type. + bool routeNodeExists(); + // Fills a list of routes with selected items' routes. + void getSelectedRoutes(MusECore::RouteList& routes); + + // Returns item exclusive mode setting. + ItemMode itemMode() const { return _itemMode; } + // Sets item exclusive mode setting. + void setItemMode(ItemMode mode) { _itemMode = mode; } + + // Automatically sets the number of channels. Returns true if channel count was changed. + bool setChannels(); + // Returns the number of channels. + int channelCount() const { return _channels.size(); } + // Sets the number of channels. + void setChannelCount(int c) { _channels.resize(c); } + // Returns true if the channel is selected. + bool channelSelected(int c) const { return _channels.selected(c); } + // Selects the channel. + void selectChannel(int c, bool v) { _channels.select(c, v); } + // Toggles the channel selection. + void toggleChannel(int c) { _channels.toggleSelected(c); } + // Sets all channels' selected state. Returns true if any channel was changed. + bool fillSelectedChannels(bool v) { return _channels.fillSelected(v); } + // Returns true if the channel is route-selected. + bool channelRouteSelected(int c) const { return _channels.routeSelected(c); } + // Route-selects the channel. + void routeSelectChannel(int c, bool v) { _channels.routeSelect(c, v); } + // Toggles the channel route-selection. + void toggleChannelRouteSelect(int c) { _channels.toggleRouteSelected(c); } + // Sets all channels' route-selected state. Returns true if any channel was changed. + bool fillChannelsRouteSelected(bool v) { return _channels.fillRouteSelected(v); } + // Returns the channel, based at rect y, whose rectangle contains pt. + int channelAt(const QPoint& pt, const QRect& rect) const; +// // How many non-omni channels are connected. For speed, it looks in the channel y values list, which must be current. +// int connectedChannels() const; +// // How many channels fit into the column. If w is -1, it uses the width of the first tree column. +// int channelsPerWidth(int w = -1) const; +// // How many groups accommodate the given number of channels, all on a single bar. +// int groupsPerChannels(int c) const; +// // How many bars accommodate the item's total channels, for the given number of maximum number of channels on a single bar. +// int barsPerColChannels(int cc) const; + + // For drawing channel lines: + int channelYValue(int c) const { return _channels.lineY(c); } + // Computes, and caches, channel y values. May be slow. Don't call on each draw, only when routes change. Use channelYValue() to draw. + void computeChannelYValues(int col_width = -1); + + // Returns current channel. (Unlike being selected.) + int curChannel() const { return _curChannel; } + // Sets the current channel. (Unlike being selected.) + void setCurChannel(int c) { _curChannel = c; } + + // Handles mouse press events. + bool mousePressHandler(QMouseEvent* e, const QRect& rect); + // Handles mouse move events. + bool mouseMoveHandler(QMouseEvent* e, const QRect& rect); + // Handles painting. Returns true if the painting was handled. + bool paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; +// // Returns suggested size of item. +// QSize getSizeHint(int col, int col_width = -1) const; + // Returns suggested size of item. +// QSize getSizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + // Returns suggested size of item, that will fit into the given width while expanding vertically. + // If width is -1, it uses the view width. + QSize getSizeHint(int column, int width = -1) const; + // Returns true if the item should be re-laid out (ie. with a tree's scheduleDelayedItemsLayout() or a delegate's sizeHintChanged()). + // For channel items, it first adjusts the channel bar width to fit the new width. + //bool testForRelayout(const QModelIndex& index, int old_width, int new_width) const; + bool testForRelayout(int column, int old_width, int new_width); +}; + +//--------------------------------------------------------- +// ConnectionsView +//--------------------------------------------------------- + +class RouteDialog; +class ConnectionsView : public QFrame +{ + Q_OBJECT + + private: + RouteDialog* _routeDialog; + int lastY; + int itemY(RouteTreeWidgetItem* item, bool is_input, int channel = -1) const; + void drawItem(QPainter* pPainter, QTreeWidgetItem* routesItem, const QColor& col); + void drawConnectionLine(QPainter* pPainter, + int x1, int y1, int x2, int y2, int h1, int h2); + + protected: + virtual void paintEvent(QPaintEvent*); + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void wheelEvent(QWheelEvent*); + virtual void contextMenuEvent(QContextMenuEvent*); + + signals: + void scrollBy(int, int); + + + public: + ConnectionsView(QWidget* parent = 0, RouteDialog* d = 0); + virtual ~ConnectionsView(); + void setRouteDialog(RouteDialog* d) { _routeDialog = d; } +}; + +//--------------------------------------------------------- +// RouteTreeWidget +//--------------------------------------------------------- + +class RouteTreeWidget : public QTreeWidget +{ + + Q_OBJECT + + Q_PROPERTY(bool isInput READ isInput WRITE setIsInput) +private: + bool _isInput; + bool _channelWrap; + +private slots: + void headerSectionResized(int logicalIndex, int oldSize, int newSize); + +protected: + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex& index, const QEvent* event = 0) const; + virtual void resizeEvent(QResizeEvent*); + +protected slots: + virtual void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + +public slots: + void scrollBy(int dx, int dy); + +public: + RouteTreeWidget(QWidget* parent = 0, bool is_input = false); + virtual ~RouteTreeWidget(); + + void computeChannelYValues(); + + bool isInput() { return _isInput; } + void setIsInput(bool v) { _isInput = v; } + + bool channelWrap() { return _channelWrap; } + void setChannelWrap(bool v) { _channelWrap = v; } + + RouteTreeWidgetItem* itemFromIndex(const QModelIndex& index) const; + RouteTreeWidgetItem* findItem(const MusECore::Route&, int itemType = -1); + RouteTreeWidgetItem* findCategoryItem(const QString&); + int channelAt(RouteTreeWidgetItem* item, const QPoint& pt); + //void clearChannels(); + + void getSelectedRoutes(MusECore::RouteList& routes); + void getItemsToDelete(QVector& items_to_remove, bool showAllMidiPorts = false); + //void scheduleDelayedLayout() { scheduleDelayedItemsLayout(); } // Just to make it public. + void selectRoutes(const QList& routes, bool doNormalSelections); +}; + + +} // namespace MusEGui + +#include "ui_routedialogbase.h" namespace MusEGui { +//----------------------------------- +// RoutingItemDelegate +//----------------------------------- + +class RoutingItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + + private: + RouteTreeWidget* _tree; + bool _isInput; + QStyle::SubElement _currentSubElement; // Set in mouse press, checked in release to prevent unwanted editor opening. + // Need this. For some reason when using CurrentChanged trigger, createEditor is called upon opening the dialog, yet nothing is selected. + bool _firstPress; + + QRect getItemRectangle(const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement subElement, QWidget* editor = NULL) const; + bool subElementHitTest(const QPoint& point, const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement* subElement, QWidget* editor = NULL) const; + + public: + RoutingItemDelegate(bool is_input, RouteTreeWidget* tree, QWidget *parent = 0); + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + //QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + // const QModelIndex &index) const; + //void setEditorData(QWidget *editor, const QModelIndex &index) const; + virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; + // Exposed as public from protected, so that it may be called from the tree widget. + virtual void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const + { QStyledItemDelegate::initStyleOption(option, index); } + +// // Returns true if tree should be re laid out (ie. with scheduleDelayedItemsLayout()). +// bool testForRelayout(const QStyleOptionViewItem &option, const QModelIndex& index, int old_width, int new_width); + + // Emits the required sizeHintChanged(index) signal, to notify the tree to relayout the item. + virtual void emitSizeHintChanged(const QModelIndex &index) { emit sizeHintChanged(index); } + + protected: + bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index); + bool eventFilter(QObject* editor, QEvent* event); + //void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + + //private slots: + // void editorChanged(); + ////void commitAndCloseEditor(); +}; + //--------------------------------------------------------- // RouteDialog //--------------------------------------------------------- @@ -40,22 +415,87 @@ class RouteDialog : public QDialog, public Ui::RouteDialogBase { Q_OBJECT + MusECore::RouteList tmpSrcList; + MusECore::RouteList tmpDstList; + MusECore::RouteList tmpRoutesSrcList; + MusECore::RouteList tmpRoutesDstList; + + RoutingItemDelegate* srcItemDelegate; + RoutingItemDelegate* dstItemDelegate; + + //RouteTreeWidgetItem* _srcFilterItem; + //RouteTreeWidgetItem* _dstFilterItem; + RouteTreeItemList _srcFilterItems; + RouteTreeItemList _dstFilterItems; + //RouteTreeItemList _srcFilterRouteItems; + //RouteTreeItemList _dstFilterRouteItems; + virtual void closeEvent(QCloseEvent*); - void routingChanged(); + void removeItems(); + void addItems(); + void getRoutesToDelete(QTreeWidget* routesTree, QVector& items_to_remove); + void selectRoutes(bool doNormalSelections); private slots: void routeSelectionChanged(); - void removeRoute(); - void addRoute(); + void disconnectClicked(); + void connectClicked(); void srcSelectionChanged(); void dstSelectionChanged(); void songChanged(MusECore::SongChangedFlags_t); + void srcTreeScrollValueChanged(int value); + void dstTreeScrollValueChanged(int value); + void srcScrollBarValueChanged(int value); + void dstScrollBarValueChanged(int value); + + void filterSrcClicked(bool v); + void filterDstClicked(bool v); + + void filterSrcRoutesClicked(bool v); + void filterDstRoutesClicked(bool v); + + void allMidiPortsClicked(bool v); + void preferredRouteAliasChanged(int); + void verticalLayoutClicked(bool); + signals: void closed(); public: RouteDialog(QWidget* parent=0); + QTreeWidgetItem* findRoutesItem(const MusECore::Route&, const MusECore::Route&); + + // Hide all items in the source or destination tree except the filter items, + // and hide any items in the route tree whose source or destination route data + // matches matches the filter items' routes. + // If filter items is empty show all items, in both the source or destination tree + // and the route tree. + // Hiding items does not disturb the open state of an entire tree. + void filter(const RouteTreeItemList& srcFilterItems, + const RouteTreeItemList& dstFilterItems, + bool filterSrc, + bool filterDst); + + enum { ROUTE_NAME_COL = 0 }; //, ROUTE_TYPE_COL }; + enum { ROUTE_SRC_COL = 0, ROUTE_DST_COL }; + enum RoutingRoles { RouteRole = Qt::UserRole}; //, ChannelsRole = Qt::UserRole + 1 }; + + static const QString tracksCat; + static const QString midiPortsCat; + static const QString midiDevicesCat; + static const QString jackCat; + static const QString jackMidiCat; + + static const int channelDotDiameter; + static const int channelDotSpacing; + static const int channelDotsPerGroup; + static const int channelDotGroupSpacing; + static const int channelDotsMargin; + static const int channelBarHeight; + static const int channelLineWidth; + static const int channelLinesSpacing; + static const int channelLinesMargin; }; diff -Nru muse-2.1.2/muse/mixer/strip.cpp muse-3.0.2+ds1/muse/mixer/strip.cpp --- muse-2.1.2/muse/mixer/strip.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/strip.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: strip.cpp,v 1.6.2.5 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011 - 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,15 +22,20 @@ // //========================================================= -#include -#include +#include + #include #include #include -#include #include #include #include +#include +#include +#include +#include +#include +#include #include "globals.h" #include "gconfig.h" @@ -41,312 +46,2843 @@ #include "strip.h" #include "meter.h" #include "utils.h" +#include "muse_math.h" +#include "ctrl.h" +#include "midi.h" +#include "midictrl.h" #include "icons.h" +#include "undo.h" +#include "operations.h" +#include "amixer.h" +#include "compact_knob.h" +#include "compact_slider.h" +#include "elided_label.h" +#include "menutitleitem.h" +#include "pixmap_button.h" + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_STRIP(dev, format, args...) // fprintf(dev, format, ##args); + +using MusECore::UndoOp; namespace MusEGui { + //--------------------------------------------------------- -// setRecordFlag +// ComponentRack //--------------------------------------------------------- -void Strip::setRecordFlag(bool flag) - { - if (record) { - record->blockSignals(true); - record->setChecked(flag); - record->blockSignals(false); - record->setIcon(flag ? QIcon(*record_on_Icon) : QIcon(*record_off_Icon)); - //record->setIconSize(record_on_Icon->size()); - } - } +ComponentRack::ComponentRack(int id, QWidget* parent, Qt::WindowFlags f) + : QFrame(parent, f), _id(id) +{ + _layout = new ComponentRackLayout(this); // Install a layout manager. + _layout->setSpacing(0); + _layout->setContentsMargins(0, 0, 0, 0); +} -//--------------------------------------------------------- -// resetPeaks -//--------------------------------------------------------- +ComponentWidget* ComponentRack::findComponent( + ComponentWidget::ComponentType componentType, + int componentWidgetType, + int index, + QWidget* widget) +{ + iComponentWidget icw = _components.find(componentType, componentWidgetType, index, widget); + if(icw != _components.end()) + return &(*icw); + return 0; +} -void Strip::resetPeaks() +void ComponentRack::clearDelete() +{ + for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + ComponentWidget& cw = *ic; + if(cw._widget) + delete cw._widget; + } + _components.clear(); +} + +void ComponentRack::addComponentWidget( const ComponentWidget& cw, const ComponentWidget& before ) +{ + if(cw._widget) + { + int idx = -1; + if(before.isValid()) + { + iComponentWidget ibcw = _components.find(before); + if(ibcw == _components.end()) { - track->resetPeaks(); + DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' item not found. Pushing back.\n"); + _components.push_back(cw); } - -//--------------------------------------------------------- -// recordToggled -//--------------------------------------------------------- - -void Strip::recordToggled(bool val) + else { - if (track->type() == MusECore::Track::AUDIO_OUTPUT) { - if (val && track->recordFlag() == false) { - MusEGlobal::muse->bounceToFile((MusECore::AudioOutput*)track); - } - MusEGlobal::audio->msgSetRecord((MusECore::AudioOutput*)track, val); - if (!((MusECore::AudioOutput*)track)->recFile()) - { - record->setChecked(false); - record->setIcon(QIcon(*record_off_Icon)); - //record->setIconSize(record_on_Icon->size()); - } - return; - } - MusEGlobal::song->setRecordFlag(track, val); + idx = _layout->indexOf(before._widget); + if(idx == -1) + { + DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' widget not found. Pushing back.\n"); + _components.push_back(cw); + } + else + { + DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' widget found. Inserting. Layout idx:%d.\n", idx); + _components.insert(ibcw, cw); + } } -//--------------------------------------------------------- -// heartBeat -//--------------------------------------------------------- + } + else + { + DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' item not valid. Pushing back.\n"); + _components.push_back(cw); + } + + if(idx == -1) + _layout->addWidget(cw._widget); + else + _layout->insertWidget(idx, cw._widget); + } +} -void Strip::heartBeat() +void ComponentRack::newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before ) +{ + QPalette pal(palette()); + ComponentWidget cw; + switch(desc->_widgetType) + { + case CompactKnobComponentWidget: + { + CompactKnobComponentDescriptor* d = static_cast(desc); + if(!d->_compactKnob) { + CompactKnob* control = new CompactKnob(0, + d->_objName, + CompactKnob::Right, + d->_label); + d->_compactKnob = control; + control->setId(d->_index); + control->setRange(d->_min, d->_max, d->_step); + control->setValueDecimals(d->_precision); + control->setSpecialValueText(d->_specialValueText); + control->setHasOffMode(d->_hasOffMode); + control->setValueState(d->_initVal, d->_isOff); + control->setValPrefix(d->_prefix); + control->setValSuffix(d->_suffix); + control->setShowValue(d->_showValue); + // Do not set. Compact knob needs to manage it's own tooltips. + //control->setToolTip(d->_toolTipText); + control->setEnabled(d->_enabled); + control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + control->setContentsMargins(0, 0, 0, 0); + if(d->_color.isValid()) + control->setFaceColor(d->_color); + //if(d->_rimColor.isValid()) + // control->setRimColor(d->_rimColor); + if(d->_faceColor.isValid()) + control->setFaceColor(d->_faceColor); + if(d->_shinyColor.isValid()) + control->setShinyColor(d->_shinyColor); + + //control->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + + switch(d->_componentType) + { + case controllerComponent: + connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(controllerChanged(double,bool,int,int))); + connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(controllerMoved(double,int,bool))); + connect(control, SIGNAL(sliderPressed(double, int)), SLOT(controllerPressed(double, int))); + connect(control, SIGNAL(sliderReleased(double, int)), SLOT(controllerReleased(double, int))); + connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int))); + break; + + case propertyComponent: + connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(propertyChanged(double,bool,int,int))); + connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(propertyMoved(double,int,bool))); + connect(control, SIGNAL(sliderPressed(double, int)), SLOT(propertyPressed(double, int))); + connect(control, SIGNAL(sliderReleased(double, int)), SLOT(propertyReleased(double, int))); + connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(propertyRightClicked(QPoint,int))); + break; + } } -//--------------------------------------------------------- -// setLabelFont -//--------------------------------------------------------- - -void Strip::setLabelFont() -{ - // Use the new font #6 I created just for these labels (so far). - // Set the label's font. - label->setFont(MusEGlobal::config.fonts[6]); - // Dealing with a horizontally constrained label. Ignore vertical. Use a minimum readable point size. - MusECore::autoAdjustFontSize(label, label->text(), false, true, MusEGlobal::config.fonts[6].pointSize(), 5); -} + cw = ComponentWidget( + d->_compactKnob, + d->_widgetType, + d->_componentType, + d->_index + ); -//--------------------------------------------------------- -// setLabelText -//--------------------------------------------------------- + } + break; -void Strip::setLabelText() -{ - QColor c; - switch(track->type()) { - case MusECore::Track::AUDIO_OUTPUT: - //c = Qt::green; - c = MusEGlobal::config.outputTrackLabelBg; - break; - case MusECore::Track::AUDIO_GROUP: - //c = Qt::yellow; - c = MusEGlobal::config.groupTrackLabelBg; - break; - case MusECore::Track::AUDIO_AUX: - //c = QColor(120, 255, 255); // Light blue - c = MusEGlobal::config.auxTrackLabelBg; - break; - case MusECore::Track::WAVE: - //c = Qt::magenta; - c = MusEGlobal::config.waveTrackLabelBg; - break; - case MusECore::Track::AUDIO_INPUT: - //c = Qt::red; - c = MusEGlobal::config.inputTrackLabelBg; - break; - case MusECore::Track::AUDIO_SOFTSYNTH: - //c = QColor(255, 130, 0); // Med orange - c = MusEGlobal::config.synthTrackLabelBg; - break; - case MusECore::Track::MIDI: - //c = QColor(0, 160, 255); // Med blue - c = MusEGlobal::config.midiTrackLabelBg; - break; - case MusECore::Track::DRUM: - //c = QColor(0, 160, 255); // Med blue - c = MusEGlobal::config.drumTrackLabelBg; - break; - case MusECore::Track::NEW_DRUM: - //c = QColor(0, 160, 255); // Med blue - c = MusEGlobal::config.newDrumTrackLabelBg; - break; - default: - return; - } + case CompactSliderComponentWidget: + { + CompactSliderComponentDescriptor* d = static_cast(desc); + if(!d->_compactSlider) + { + CompactSlider* control = new CompactSlider(0, d->_objName, Qt::Horizontal, CompactSlider::None, d->_label); + d->_compactSlider = control; + control->setId(d->_index); + control->setRange(d->_min, d->_max, d->_step); + control->setValueDecimals(d->_precision); + control->setSpecialValueText(d->_specialValueText); + control->setHasOffMode(d->_hasOffMode); + control->setValueState(d->_initVal, d->_isOff); + control->setValPrefix(d->_prefix); + control->setValSuffix(d->_suffix); + control->setShowValue(d->_showValue); + control->setActiveBorders(d->_activeBorders); + control->setToolTip(d->_toolTipText); + control->setEnabled(d->_enabled); + control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + control->setContentsMargins(0, 0, 0, 0); + if(d->_color.isValid()) + control->setBorderColor(d->_color); + if(d->_barColor.isValid()) + control->setBarColor(d->_barColor); + if(d->_slotColor.isValid()) + control->setSlotColor(d->_slotColor); + if(d->_thumbColor.isValid()) + control->setThumbColor(d->_thumbColor); + + control->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + + switch(d->_componentType) + { + case controllerComponent: + connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(controllerChanged(double,bool,int,int))); + connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(controllerMoved(double,int,bool))); + connect(control, SIGNAL(sliderPressed(double, int)), SLOT(controllerPressed(double, int))); + connect(control, SIGNAL(sliderReleased(double, int)), SLOT(controllerReleased(double, int))); + connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int))); + break; + + case propertyComponent: + connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(propertyChanged(double,bool,int,int))); + connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(propertyMoved(double,int,bool))); + connect(control, SIGNAL(sliderPressed(double, int)), SLOT(propertyPressed(double, int))); + connect(control, SIGNAL(sliderReleased(double, int)), SLOT(propertyReleased(double, int))); + connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(propertyRightClicked(QPoint,int))); + break; + } + } - if (track->type() == MusECore::Track::AUDIO_AUX) { - label->setText(((MusECore::AudioAux*)track)->auxName()); - } else { - label->setText(track->name()); + cw = ComponentWidget( + d->_compactSlider, + d->_widgetType, + d->_componentType, + d->_index + ); + + } + break; + + case ElidedLabelComponentWidget: + { + ElidedLabelComponentDescriptor* d = static_cast(desc); + if(!d->_elidedLabel) + { + ElidedLabel* control = new ElidedLabel(0, d->_elideMode); + d->_elidedLabel = control; + control->setObjectName(d->_objName); + + control->setId(d->_index); + control->setToolTip(d->_toolTipText); + control->setEnabled(d->_enabled); + control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + control->setContentsMargins(0, 0, 0, 0); + + if(d->_color.isValid()) + { + pal.setColor(QPalette::Active, QPalette::Button, d->_color); // Border + pal.setColor(QPalette::Inactive, QPalette::Button, d->_color); // Border + control->setPalette(pal); + } + + switch(d->_componentType) + { + case propertyComponent: + connect(control, SIGNAL(pressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)), + SLOT(labelPropertyPressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers))); + connect(control, SIGNAL(released(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)), + SLOT(labelPropertyReleased(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers))); + connect(control, SIGNAL(returnPressed(QPoint,int,Qt::KeyboardModifiers)), + SLOT(labelPropertyReturnPressed(QPoint,int,Qt::KeyboardModifiers))); + break; + } } - QPalette palette; - //palette.setColor(label->backgroundRole(), c); - QLinearGradient gradient(label->geometry().topLeft(), label->geometry().bottomLeft()); - //gradient.setColorAt(0, c.darker()); - //gradient.setColorAt(0, c); - //gradient.setColorAt(1, c.darker()); - gradient.setColorAt(0, c); - gradient.setColorAt(0.5, c.lighter()); - gradient.setColorAt(1, c); - //palette.setBrush(QPalette::Button, gradient); - //palette.setBrush(QPalette::Window, gradient); - palette.setBrush(label->backgroundRole(), gradient); - label->setPalette(palette); - //label->setStyleSheet(QString("background-color: ") + c.name()); + cw = ComponentWidget( + d->_elidedLabel, + d->_widgetType, + d->_componentType, + d->_index + ); + + } + break; + + case ExternalComponentWidget: + { + WidgetComponentDescriptor* d = static_cast(desc); + + QWidget* widget = d->_widget; + if(widget) + { + widget->setToolTip(d->_toolTipText); + widget->setEnabled(d->_enabled); + widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + + if(d->_color.isValid()) + { + pal.setColor(QPalette::Active, QPalette::Button, d->_color); // Border + pal.setColor(QPalette::Inactive, QPalette::Button, d->_color); // Border + widget->setPalette(pal); + } + + cw = ComponentWidget( + d->_widget, + d->_widgetType, + d->_componentType, + d->_index + ); + } + } + break; + } + + if(cw._widget) + addComponentWidget(cw, before); } - -//--------------------------------------------------------- -// muteToggled -//--------------------------------------------------------- - -void Strip::muteToggled(bool val) + +void ComponentRack::setComponentMinValue(const ComponentWidget& cw, double min, bool updateOnly) +{ + if(!cw._widget) + return; + + switch(cw._widgetType) + { + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + if(min != w->minValue()) { - track->setMute(val); - MusEGlobal::song->update(SC_MUTE); + if(updateOnly) + w->blockSignals(true); + w->setMinValue(min); + if(updateOnly) + w->blockSignals(false); } + } + break; -//--------------------------------------------------------- -// soloToggled -//--------------------------------------------------------- - -void Strip::soloToggled(bool val) + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + if(min != w->minValue()) { - MusEGlobal::audio->msgSetSolo(track, val); - MusEGlobal::song->update(SC_SOLO); + if(updateOnly) + w->blockSignals(true); + w->setMinValue(min); + if(updateOnly) + w->blockSignals(false); } + } + break; + } +} -//--------------------------------------------------------- -// Strip -// create mixer strip -//--------------------------------------------------------- +void ComponentRack::setComponentMaxValue(const ComponentWidget& cw, double max, bool updateOnly) +{ + if(!cw._widget) + return; + + switch(cw._widgetType) + { + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + if(max != w->maxValue()) + { + if(updateOnly) + w->blockSignals(true); + w->setMaxValue(max); + if(updateOnly) + w->blockSignals(false); + } + } + break; -Strip::Strip(QWidget* parent, MusECore::Track* t) - : QFrame(parent) + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + if(max != w->maxValue()) { - _curGridRow = 0; - setAttribute(Qt::WA_DeleteOnClose); - iR = 0; - oR = 0; - - ///setBackgroundRole(QPalette::Mid); - setFrameStyle(Panel | Raised); - setLineWidth(2); - - // NOTE: Workaround for freakin' improper disabled button text colour (at least with Oxygen colours). - // Just set the parent palette. - //QPalette pal(palette()); - //pal.setColor(QPalette::Disabled, QPalette::ButtonText, - // pal.color(QPalette::Disabled, QPalette::WindowText)); - //setPalette(pal); - - track = t; - meter[0] = 0; - meter[1] = 0; - //setFixedWidth(STRIP_WIDTH); - //setMinimumWidth(STRIP_WIDTH); // TESTING Tim. - //setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding)); // TESTING Tim. - setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding)); // TESTING Tim. - - grid = new QGridLayout(); - grid->setContentsMargins(0, 0, 0, 0); - grid->setSpacing(0); - setLayout(grid); + if(updateOnly) + w->blockSignals(true); + w->setMaxValue(max); + if(updateOnly) + w->blockSignals(false); + } + } + break; + } +} - //--------------------------------------------- - // label - //--------------------------------------------- +double ComponentRack::componentMinValue(const ComponentWidget& cw) const +{ + if(cw._widget) + { + switch(cw._widgetType) + { + case CompactSliderComponentWidget: + return static_cast(cw._widget)->minValue(); + break; + + case CompactKnobComponentWidget: + return static_cast(cw._widget)->minValue(); + break; + } + } - //label = new QLabel(this); - // NOTE: This was required, otherwise the strip labels have no colour in the mixer only - track info OK ! - // Not sure why... - label = new QLabel(this); - label->setObjectName(track->cname()); - - // Moved by Tim. p3.3.9 - //setLabelText(); - //label->setFont(MusEGlobal::config.fonts[1]); - - //printf("Strip::Strip w:%d frw:%d layoutmarg:%d lx:%d ly:%d lw:%d lh:%d\n", STRIP_WIDTH, frameWidth(), layout->margin(), label->x(), label->y(), label->width(), label->height()); - - // Tested: The label's width is 100. It does not become STRIP_WIDTH - 2*layout->margin - // until the mixer is shown in MusE::showMixer. - // Therefore 'fake' set the size of the label now. - // Added by Tim. p3.3.9 - //label->setGeometry(label->x(), label->y(), STRIP_WIDTH - 2*frameWidth() - 2*layout->margin(), label->height()); - ///label->setGeometry(label->x(), label->y(), STRIP_WIDTH - 2*grid->margin(), label->height()); - - label->setTextFormat(Qt::PlainText); - - // Unfortunately for the mixer labels, QLabel doesn't support the BreakAnywhere flag. - // Changed by Tim. p3.3.9 - //label->setAlignment(AlignCenter); - //label->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); - // MusE-2 Tested: TextWrapAnywhere actually works, but in fact it takes precedence - // over word wrap, so I found it is not really desirable. Maybe with a user setting... - //label->setAlignment(Qt::AlignCenter | Qt::TextWordWrap | Qt::TextWrapAnywhere); - // changed by Orcan: We can't use Qt::TextWordWrap in alignment in Qt4. - label->setAlignment(Qt::AlignCenter); - label->setWordWrap(true); - label->setAutoFillBackground(true); - label->setLineWidth(2); - label->setFrameStyle(Sunken | StyledPanel); - - //label->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum)); - label->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum)); - - // Added by Tim. p3.3.9 - setLabelText(); - setLabelFont(); - - //layout->addWidget(label); - grid->addWidget(label, _curGridRow++, 0, 1, 2); - } + return 0.0; +} -//--------------------------------------------------------- -// Strip -//--------------------------------------------------------- +double ComponentRack::componentMaxValue(const ComponentWidget& cw) const +{ + if(cw._widget) + { + switch(cw._widgetType) + { + case CompactSliderComponentWidget: + return static_cast(cw._widget)->maxValue(); + break; + + case CompactKnobComponentWidget: + return static_cast(cw._widget)->maxValue(); + break; + } + } -Strip::~Strip() + return 0.0; +} + +void ComponentRack::setComponentRange(const ComponentWidget& cw, double min, double max, bool updateOnly, + double step, int pageSize, + DoubleRange::ConversionMode mode) +{ + if(!cw._widget) + return; + + switch(cw._widgetType) + { + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + if(min != w->minValue() || max != w->maxValue()) { + if(updateOnly) + w->blockSignals(true); + if(min != w->minValue() && max != w->maxValue()) + w->setRange(min, max, step, pageSize, mode); + else if(min != w->minValue()) + w->setMinValue(max); + else + w->setMaxValue(max); + if(updateOnly) + w->blockSignals(false); } + } + break; -//--------------------------------------------------------- -// setAutomationType -//--------------------------------------------------------- + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + if(min != w->minValue() || max != w->maxValue()) + { + if(updateOnly) + w->blockSignals(true); + if(min != w->minValue() && max != w->maxValue()) + w->setRange(min, max, step, pageSize, mode); + else if(min != w->minValue()) + w->setMinValue(max); + else + w->setMaxValue(max); + if(updateOnly) + w->blockSignals(false); + } + } + break; + } +} -void Strip::setAutomationType(int t) +double ComponentRack::componentValue(const ComponentWidget& cw) const { - // If going to OFF mode, need to update current 'manual' values from the automation values at this time... - if(t == AUTO_OFF && track->automationType() != AUTO_OFF) // && track->automationType() != AUTO_WRITE) + if(cw._widget) { - // May have a lot to do in updateCurValues, so try using idle. - MusEGlobal::audio->msgIdle(true); - track->setAutomationType(AutomationType(t)); - if(!track->isMidiTrack()) - (static_cast(track))->controller()->updateCurValues(MusEGlobal::audio->curFramePos()); - MusEGlobal::audio->msgIdle(false); + switch(cw._widgetType) + { + case CompactSliderComponentWidget: + return static_cast(cw._widget)->value(); + break; + + case CompactKnobComponentWidget: + return static_cast(cw._widget)->value(); + break; + } } - else - // Try it within one message. - MusEGlobal::audio->msgSetTrackAutomationType(track, t); - MusEGlobal::song->update(SC_AUTOMATION); + return 0.0; } - -void Strip::resizeEvent(QResizeEvent* ev) + +void ComponentRack::setComponentValue(const ComponentWidget& cw, double val, bool updateOnly) { - //printf("Strip::resizeEvent\n"); - QFrame::resizeEvent(ev); - setLabelText(); - setLabelFont(); -} + if(!cw._widget) + return; + + switch(cw._widgetType) + { + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + //if(val != cw->_currentValue) // TODO ? + if(val != w->value()) + { + if(updateOnly) + w->blockSignals(true); + w->setValue(val); + if(updateOnly) + w->blockSignals(false); + //cw->_currentValue = val; // TODO ? + } + } + break; -void Strip::mousePressEvent(QMouseEvent* ev) + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + //if(val != cw->_currentValue) // TODO ? + if(val != w->value()) + { + if(updateOnly) + w->blockSignals(true); + w->setValue(val); + if(updateOnly) + w->blockSignals(false); + //cw->_currentValue = val; // TODO ? + } + } + break; + } +} + +void ComponentRack::fitComponentValue(const ComponentWidget& cw, double val, bool updateOnly) { - if (ev->button() == Qt::RightButton) { - QMenu* menu = new QMenu; - menu->addAction(tr("Remove track?")); - QPoint pt = QCursor::pos(); - QAction* act = menu->exec(pt, 0); - if (!act) + if(!cw._widget) + return; + + switch(cw._widgetType) + { + case CompactSliderComponentWidget: { - delete menu; - QFrame::mousePressEvent(ev); - return; + CompactSlider* w = static_cast(cw._widget); + //if(val != cw->_currentValue) // TODO ? + if(val != w->value()) + { + if(updateOnly) + w->blockSignals(true); + w->fitValue(val); + if(updateOnly) + w->blockSignals(false); + //cw->_currentValue = val; // TODO ? + } } - MusEGlobal::song->removeTrack0(track); - MusEGlobal::audio->msgUpdateSoloStates(); - ev->accept(); + break; + + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + //if(val != cw->_currentValue) // TODO ? + if(val != w->value()) + { + if(updateOnly) + w->blockSignals(true); + w->fitValue(val); + if(updateOnly) + w->blockSignals(false); + //cw->_currentValue = val; // TODO ? + } + } + break; + } +} + +void ComponentRack::incComponentValue(const ComponentWidget& cw, int steps, bool updateOnly) +{ + if(!cw._widget) return; + + switch(cw._widgetType) + { + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + if(updateOnly) + w->blockSignals(true); + w->incValue(steps); + if(updateOnly) + w->blockSignals(false); + } + break; + + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + if(updateOnly) + w->blockSignals(true); + w->incValue(steps); + if(updateOnly) + w->blockSignals(false); + } + break; } - QFrame::mousePressEvent(ev); } +void ComponentRack::setComponentText(const ComponentWidget& cw, const QString& text, bool updateOnly) +{ + if(!cw._widget) + return; + + switch(cw._widgetType) + { + case ElidedLabelComponentWidget: + { + ElidedLabel* w = static_cast(cw._widget); + if(text != w->text()) + { + if(updateOnly) + w->blockSignals(true); + w->setText(text); + if(updateOnly) + w->blockSignals(false); + } + } + break; + + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + if(text != w->labelText()) + { + if(updateOnly) + w->blockSignals(true); + w->setLabelText(text); + if(updateOnly) + w->blockSignals(false); + } + } + break; + + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + if(text != w->labelText()) + { + if(updateOnly) + w->blockSignals(true); + w->setLabelText(text); + if(updateOnly) + w->blockSignals(false); + } + } + break; + } +} + +void ComponentRack::setComponentEnabled(const ComponentWidget& cw, bool enable, bool /*updateOnly*/) +{ + if(!cw._widget) + return; + + // Nothing special for now. Just operate on the widget itself. + cw._widget->setEnabled(enable); +} + +void ComponentRack::setComponentShowValue(const ComponentWidget& cw, bool show, bool updateOnly) +{ + if(!cw._widget) + return; + + switch(cw._widgetType) + { + case CompactKnobComponentWidget: + { + CompactKnob* w = static_cast(cw._widget); + if(show != w->showValue()) + { + if(updateOnly) + w->blockSignals(true); + w->setShowValue(show); + if(updateOnly) + w->blockSignals(false); + } + } + break; + + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + if(show != w->showValue()) + { + if(updateOnly) + w->blockSignals(true); + w->setShowValue(show); + if(updateOnly) + w->blockSignals(false); + } + } + break; + } +} + +QWidget* ComponentRack::setupComponentTabbing(QWidget* previousWidget) +{ + QWidget* prev = previousWidget; + for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + const ComponentWidget& cw = *ic; + if(cw._widget) + { + if(prev) + QWidget::setTabOrder(prev, cw._widget); + prev = cw._widget; + } + } + return prev; +} + +//--------------------------------------------------------- +// configChanged +// Catch when label font, or configuration min slider and meter values change, or viewable tracks etc. +//--------------------------------------------------------- + +void ComponentRack::configChanged() +{ + for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) + { + const ComponentWidget& cw = *ic; + if(!cw._widget) + continue; + + switch(cw._widgetType) + { + case CompactKnobComponentWidget: + { + //CompactKnob* w = static_cast(cw._widget); + //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + } + break; + + case CompactSliderComponentWidget: + { + CompactSlider* w = static_cast(cw._widget); + w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); + } + break; + +// case ElidedLabelComponentWidget: +// { +// ElidedLabel* w = static_cast(cw._widget); +// //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); +// } +// break; + + default: + break; + } + } +} + + +//--------------------------------------------------------- +// TrackNameLabel +//--------------------------------------------------------- + +TrackNameLabel::TrackNameLabel(QWidget* parent, const char* name, Qt::WindowFlags f) + : QLabel(parent, f) +{ + setObjectName(name); +} + +TrackNameLabel::TrackNameLabel(const QString& text, QWidget* parent, const char* name, Qt::WindowFlags f) + : QLabel(text, parent, f) +{ + setObjectName(name); +} + +void TrackNameLabel::mouseDoubleClickEvent(QMouseEvent* ev) +{ + ev->accept(); + emit doubleClicked(); +} + +//--------------------------------------------------------- +// Strip +//--------------------------------------------------------- + +const int Strip::FIXED_METER_WIDTH = 7; + +//--------------------------------------------------------- +// setRecordFlag +//--------------------------------------------------------- + +void Strip::setRecordFlag(bool flag) + { + if (record) { + record->blockSignals(true); + record->setChecked(flag); + record->blockSignals(false); + } + } + +//--------------------------------------------------------- +// resetPeaks +//--------------------------------------------------------- + +void Strip::resetPeaks() + { + track->resetPeaks(); + } + +//--------------------------------------------------------- +// recordToggled +//--------------------------------------------------------- + +void Strip::recordToggled(bool val) +{ + if (track->type() == MusECore::Track::AUDIO_OUTPUT) + { + if (val && !track->recordFlag()) + { + MusEGlobal::muse->bounceToFile((MusECore::AudioOutput*)track); + + if (!((MusECore::AudioOutput*)track)->recFile()) + { + if(record) + { + record->blockSignals(true); + record->setChecked(false); + record->blockSignals(false); + } + } + return; + } + } + + MusEGlobal::song->setRecordFlag(track, val); +} + +//--------------------------------------------------------- +// returnPressed +//--------------------------------------------------------- + +void Strip::changeTrackName() +{ + if(!track) + return; + + const QString oldname = track->name(); + + QInputDialog dlg(this); + dlg.setWindowTitle(tr("Name")); + dlg.setLabelText(tr("Enter track name:")); + dlg.setTextValue(oldname); + // FIXME: Can't seem to set a larger font. Seems to pick one used by strip. + //dlg.setStyleSheet(""); + //dlg.setFont(MusEGlobal::config.fonts[0]); + + const int res = dlg.exec(); + if(res == QDialog::Rejected) + return; + + const QString newname = dlg.textValue(); + + if(newname == oldname) + return; + + MusECore::TrackList* tl = MusEGlobal::song->tracks(); + for (MusECore::iTrack i = tl->begin(); i != tl->end(); ++i) + { + if ((*i)->name() == newname) + { + QMessageBox::critical(this, + tr("MusE: bad trackname"), + tr("please choose a unique track name"), + QMessageBox::Ok, + Qt::NoButton, + Qt::NoButton); + return; + } + } + + MusEGlobal::song->applyOperation( + MusECore::UndoOp(MusECore::UndoOp::ModifyTrackName, track, oldname, newname)); +} + +//--------------------------------------------------------- +// heartBeat +//--------------------------------------------------------- + +void Strip::heartBeat() + { + } + +void Strip::paintEvent(QPaintEvent * ev) +{ + QFrame::paintEvent(ev); + QPainter p(this); + if (_highlight) { + QPen pen(Qt::yellow); + pen.setWidth(1); + p.setPen(pen); + p.drawRect(0,0,width()-1,height()-1); + } + ev->accept(); +} + +//--------------------------------------------------------- +// updateStyleSheet +//--------------------------------------------------------- + +void Strip::updateStyleSheet() +{ + if(!track) + return; + + QFont fnt(MusEGlobal::config.fonts[6]); + const bool need_word_wrap = + !MusECore::autoAdjustFontSize(label, label->text(), fnt, false, true, + fnt.pointSize(), 6); + + // Set the label's font. + // Note that this is effectively useless if a stylesheet font is set. + // When reading font(), it returns what the stylesheet says. + // But we set it here anyway, in case stylesheets are not used. + label->setFont(fnt); + + if(need_word_wrap) + label->setWordWrap(true); +// label->setWordWrapMode(QTextOption::WrapAnywhere); + else +// label->setWordWrapMode(QTextOption::NoWrap); + label->setWordWrap(false); + + QColor c(track->labelColor()); + QColor c2(c.lighter()); + c.setAlpha(190); + c2.setAlpha(190); + + QString stxt = QString("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1," + "stop:0.263158 rgba(%1, %2, %3, %4), stop:0.7547368 rgba(%5, %6, %7, %8));") + .arg(c2.red()).arg(c2.green()).arg(c2.blue()).arg(c2.alpha()).arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha()); + stxt += QString("color: rgb(0, 0, 0);"); + stxt += MusECore::font2StyleSheet(fnt); + + label->setStyleSheet(stxt); +} + +//--------------------------------------------------------- +// setLabelText +//--------------------------------------------------------- + +void Strip::setLabelText() +{ + if(!track) + return; + + if (track->type() == MusECore::Track::AUDIO_AUX) { + label->setText(((MusECore::AudioAux*)track)->auxName()); + } else { + label->setText(track->name()); + } + + updateStyleSheet(); +} + +//--------------------------------------------------------- +// muteToggled +//--------------------------------------------------------- + +void Strip::muteToggled(bool val) + { + if(track) + { + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackMute)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } + updateMuteIcon(); + } + +//--------------------------------------------------------- +// soloToggled +//--------------------------------------------------------- + +void Strip::soloToggled(bool val) + { + solo->setIconSetB(track && track->internalSolo()); + if(!track) + return; + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackSolo)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } + +//--------------------------------------------------------- +// Strip +// create mixer strip +//--------------------------------------------------------- + +Strip::Strip(QWidget* parent, MusECore::Track* t, bool hasHandle, bool isEmbedded) + : QFrame(parent) + { + setMouseTracking(true); + setAttribute(Qt::WA_DeleteOnClose); + setFrameStyle(Panel | Raised); + setLineWidth(1); + + // Set so that strip can redirect focus proxy to volume label in descendants. + // Nope. Seemed like a good idea but no. Do not allow the strip to gain focus. + // Just in case it does, which is possible, keep descendants' focus proxy code + // so that the slider label will be a sensible place for focus to land. + setFocusPolicy(Qt::NoFocus); + + _focusYieldWidget = 0; + _isEmbedded = isEmbedded; + _broadcastChanges = false; + _selected = false; + _highlight = false; + + _curGridRow = 0; + _userWidth = 0; + _visible = true; + dragOn=false; + + sliderGrid = 0; + record = 0; + solo = 0; + mute = 0; + iR = 0; + oR = 0; + autoType = 0; + + track = t; + meter[0] = 0; + meter[1] = 0; + setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); + + grid = new QGridLayout(); + grid->setContentsMargins(0, 0, 0, 0); + grid->setSpacing(0); + + _handle = 0; + if(hasHandle) + { + _handle = new ExpanderHandle(); + connect(_handle, SIGNAL(moved(int)), SLOT(changeUserWidth(int))); + QHBoxLayout* hlayout = new QHBoxLayout(this); + hlayout->setContentsMargins(0, 0, 0, 0); + hlayout->setSpacing(0); + hlayout->addLayout(grid); + hlayout->addWidget(_handle); + } + else + { + setLayout(grid); + } + + //--------------------------------------------- + // label + //--------------------------------------------- + +// label = new TextEdit(this); +// label->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +// label->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +// label->viewport()->setContentsMargins(0, 0, 0, 0); +// label->setMaximumBlockCount(1); +// label->setBackgroundVisible(false); +// label->setTextFormat(Qt::PlainText); +// label->setLineWrapMode(QPlainTextEdit::WidgetWidth); + label = new TrackNameLabel(this); + label->setObjectName(track->cname()); + label->setContentsMargins(0, 0, 0, 0); + label->setAlignment(Qt::AlignCenter); + label->setAutoFillBackground(true); + label->setLineWidth(2); + label->setFrameStyle(Sunken | StyledPanel); + label->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum)); + //label->setWordWrap(true); // This is set dynamically. + + setLabelText(); + + grid->addWidget(label, _curGridRow++, 0, 1, 3); + + connect(label, SIGNAL(doubleClicked()), SLOT(changeTrackName())); + } + +//--------------------------------------------------------- +// Strip +//--------------------------------------------------------- + +Strip::~Strip() + { + } + +void Strip::setFocusYieldWidget(QWidget* w) +{ + if(_focusYieldWidget == w) + return; + if(_focusYieldWidget) + disconnect(_focusYieldWidget, SIGNAL(destroyed(QObject*)), this, SLOT(focusYieldWidgetDestroyed(QObject*))); + _focusYieldWidget = w; + if(_focusYieldWidget) + connect(_focusYieldWidget, SIGNAL(destroyed(QObject*)), this, SLOT(focusYieldWidgetDestroyed(QObject*))); +} + +void Strip::focusYieldWidgetDestroyed(QObject* obj) +{ + if(obj != _focusYieldWidget) + return; + //disconnect(_focusYieldWidget, SIGNAL(destroyed(QObject*)), this, SLOT(focusYieldWidgetDestroyed(QObject*))); + _focusYieldWidget = 0; +} + +void Strip::addGridWidget(QWidget* w, const GridPosStruct& pos, Qt::Alignment alignment) +{ + grid->addWidget(w, pos._row, pos._col, pos._rowSpan, pos._colSpan, alignment); +} + +void Strip::addGridLayout(QLayout* l, const GridPosStruct& pos, Qt::Alignment alignment) +{ + grid->addLayout(l, pos._row, pos._col, pos._rowSpan, pos._colSpan, alignment); +} + +//--------------------------------------------------------- +// setAutomationType +//--------------------------------------------------------- + +void Strip::setAutomationType(int t) +{ +// REMOVE Tim. mixer. Removed. TESTING... +// // If going to OFF mode, need to update current 'manual' values from the automation values at this time... +// if(t == AUTO_OFF && track->automationType() != AUTO_OFF) // && track->automationType() != AUTO_WRITE) +// { +// // May have a lot to do in updateCurValues, so try using idle. +// MusEGlobal::audio->msgIdle(true); +// track->setAutomationType(AutomationType(t)); +// if(!track->isMidiTrack()) +// (static_cast(track))->controller()->updateCurValues(MusEGlobal::audio->curFramePos()); +// MusEGlobal::audio->msgIdle(false); +// } +// else + // Try it within one message. + MusEGlobal::audio->msgSetTrackAutomationType(track, t); + + MusEGlobal::song->update(SC_AUTOMATION); +} + +void Strip::resizeEvent(QResizeEvent* ev) +{ + DEBUG_STRIP(stderr, "Strip::resizeEvent\n"); + QFrame::resizeEvent(ev); + setLabelText(); +} + +void Strip::updateRouteButtons() +{ + if (iR) + { + iR->setIconSetB(track->noInRoute()); + if (track->noInRoute()) + iR->setToolTip(MusEGlobal::noInputRoutingToolTipWarn); + else + iR->setToolTip(MusEGlobal::inputRoutingToolTipBase); + } + + if (oR) + { + oR->setIconSetB(track->noOutRoute()); + if (track->noOutRoute()) + oR->setToolTip(MusEGlobal::noOutputRoutingToolTipWarn); + else + oR->setToolTip(MusEGlobal::outputRoutingToolTipBase); + } +} + +void Strip::mousePressEvent(QMouseEvent* ev) +{ + ev->accept(); + + // Only one button at a time. + if(ev->buttons() ^ ev->button()) + return; + + QPoint mousePos = QCursor::pos(); + mouseWidgetOffset = pos() - mousePos; + + if (ev->button() == Qt::RightButton) { + QMenu* menu = new QMenu; + + menu->addAction(new MenuTitleItem(tr("Configuration:"), menu)); + + QAction* act = menu->addAction(tr("Prefer knobs, not sliders")); + act->setData(int(2)); + act->setCheckable(true); + act->setChecked(MusEGlobal::config.preferKnobsVsSliders); + + act = menu->addAction(tr("Show values in controls")); + act->setData(int(3)); + act->setCheckable(true); + act->setChecked(MusEGlobal::config.showControlValues); + + act = menu->addAction(tr("Prefer midi volume as decibels")); + act->setData(int(4)); + act->setCheckable(true); + act->setChecked(MusEGlobal::config.preferMidiVolumeDb); + + menu->addSeparator(); + + act = menu->addAction(tr("Monitor on record-arm automatically")); + act->setData(int(5)); + act->setCheckable(true); + act->setChecked(MusEGlobal::config.monitorOnRecord); + + menu->addAction(new MenuTitleItem(tr("Actions:"), menu)); + + act = menu->addAction(tr("Change track name")); + act->setData(int(1001)); + + if(!_isEmbedded) + { + +// act = menu->addAction(tr("Remove track")); +// act->setData(int(0)); +// menu->addSeparator(); + act = menu->addAction(tr("Hide strip")); + act->setData(int(1)); + } + + QPoint pt = QCursor::pos(); + act = menu->exec(pt, 0); + if (!act) + { + delete menu; + return; + } + + DEBUG_STRIP("Menu finished, data returned %d\n", act->data().toInt()); + + const int sel = act->data().toInt(); + const bool checked = act->isChecked(); + delete menu; + + switch(sel) + { + case 0: + // DEBUG_STRIP(stderr, "Strip:: delete track\n"); + // MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(track), track)); + break; + + case 1: + DEBUG_STRIP(stderr, "Strip:: setStripVisible false \n"); + setStripVisible(false); + setVisible(false); + MusEGlobal::song->update(); + break; + + case 2: + if(MusEGlobal::config.preferKnobsVsSliders != checked) + { + MusEGlobal::config.preferKnobsVsSliders = checked; + MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version. + } + break; + + case 3: + if(MusEGlobal::config.showControlValues != checked) + { + MusEGlobal::config.showControlValues = checked; + MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version. + } + break; + + case 4: + if(MusEGlobal::config.preferMidiVolumeDb != checked) + { + MusEGlobal::config.preferMidiVolumeDb = checked; + MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version. + } + break; + + case 5: + if(MusEGlobal::config.monitorOnRecord != checked) + { + MusEGlobal::config.monitorOnRecord = checked; + MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version. + } + break; + + case 1001: + changeTrackName(); + break; + } + + ev->accept(); + return; + } + else if (ev->button() == Qt::LeftButton) + { + if(!_isEmbedded) + { + if (ev->modifiers() & Qt::ControlModifier) + { + setSelected(!isSelected()); + track->setSelected(isSelected()); + MusEGlobal::song->update(SC_TRACK_SELECTION); + } + else + { + emit clearStripSelection(); + MusEGlobal::song->selectAllTracks(false); + setSelected(true); + track->setSelected(true); + MusEGlobal::song->update(SC_TRACK_SELECTION); + } + } + } +} + +QSize Strip::sizeHint() const +{ + const QSize sz = QFrame::sizeHint(); + return QSize(sz.width() + _userWidth, sz.height()); +// return QSize(_userWidth, sz.height()); +} + +void Strip::setUserWidth(int w) +{ + _userWidth = w; + if(_userWidth < 0) + _userWidth = 0; + +// grid->invalidate(); +// grid->activate(); +// grid->update(); +// adjustSize(); + updateGeometry(); +} + +void Strip::changeUserWidth(int delta) +{ + _userWidth += delta; + if(_userWidth < 0) + _userWidth = 0; + updateGeometry(); +} + +//--------------------------------------------------------- +// ExpanderHandle +//--------------------------------------------------------- + +ExpanderHandle::ExpanderHandle(QWidget* parent, int handleWidth, Qt::WindowFlags f) + : QFrame(parent, f), _handleWidth(handleWidth) +{ + setObjectName("ExpanderHandle"); + setCursor(Qt::SplitHCursor); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); + setFixedWidth(_handleWidth); + setContentsMargins(0, 0, 0, 0); + _resizeMode = ResizeModeNone; +} + +void ExpanderHandle::paintEvent(QPaintEvent * ev) +{ + QPainter p(this); + + if(const QStyle* st = style()) + { + st = st->proxy(); + + QStyleOption o; + o.initFrom(this); + o.rect = rect(); + o.state = QStyle::State_Active | QStyle::State_Enabled; + st->drawControl(QStyle::CE_Splitter, &o, &p); + } + + ev->accept(); +} + +void ExpanderHandle::mousePressEvent(QMouseEvent* e) +{ + // Only one button at a time. +// if(e->buttons() ^ e->button()) +// { +// //_resizeMode = ResizeModeNone; +// //unsetCursor(); +// e->accept(); +// return; +// } + + //if(_resizeMode == ResizeModeHovering) + //{ + // Switch to dragging mode. + //_resizeMode = ResizeModeDragging; + //ev->ignore(); + //return; + //} + + switch(_resizeMode) + { + case ResizeModeNone: + case ResizeModeHovering: + _dragLastGlobPos = e->globalPos(); + _resizeMode = ResizeModeDragging; + e->accept(); + return; + break; + + case ResizeModeDragging: + e->accept(); + return; + break; + } + + e->ignore(); + QFrame::mousePressEvent(e); +} + +void ExpanderHandle::mouseMoveEvent(QMouseEvent* e) +{ +// const QPoint p = e->pos(); + switch(_resizeMode) + { + case ResizeModeNone: + { +// if(p.x() >= (width() - frameWidth()) && p.x() < width() && p.y() >= 0 && p.y() < height()) +// { +// _resizeMode = ResizeModeHovering; +// setCursor(Qt::SizeHorCursor); +// } +// e->accept(); +// return; + } + break; + + case ResizeModeHovering: + { +// if(p.x() < (width() - frameWidth()) || p.x() >= width() || p.y() < 0 || p.y() >= height()) +// { +// _resizeMode = ResizeModeNone; +// unsetCursor(); +// } +// e->accept(); +// return; + } + break; + + case ResizeModeDragging: + { + const QPoint gp = e->globalPos(); + const QPoint delta = gp -_dragLastGlobPos; + _dragLastGlobPos = gp; + emit moved(delta.x()); + e->accept(); + return; + } + break; + } + + e->ignore(); + QFrame::mouseMoveEvent(e); +} + +void ExpanderHandle::mouseReleaseEvent(QMouseEvent* e) +{ +// switch(_resizeMode) +// { +// case ResizeModeNone: +// case ResizeModeHovering: +// break; +// +// case ResizeModeDragging: +// { +// const QPoint p = ev->pos(); +// if(p.x() >= (width() - frameWidth()) && p.x() < width() && p.y() >= 0 && p.y() < height()) +// { +// _resizeMode = ResizeModeHovering; +// setCursor(Qt::SizeHorCursor); +// } +// else +// { +// _resizeMode = ResizeModeNone; +// unsetCursor(); +// } +// ev->ignore(); +// return; +// } +// break; +// } + _resizeMode = ResizeModeNone; + e->ignore(); + QFrame::mouseReleaseEvent(e); +} + +// void ExpanderHandle::leaveEvent(QEvent* e) +// { +// e->ignore(); +// QFrame::leaveEvent(e); +// switch(_resizeMode) +// { +// case ResizeModeDragging: +// return; +// break; +// +// case ResizeModeHovering: +// _resizeMode = ResizeModeNone; +// Fall through... +// case ResizeModeNone: +// unsetCursor(); +// return; +// break; +// } +// } + +QSize ExpanderHandle::sizeHint() const +{ + QSize sz = QFrame::sizeHint(); + sz.setWidth(_handleWidth); + return sz; +} + + +void Strip::mouseReleaseEvent(QMouseEvent* ev) +{ + ev->accept(); + if (!_isEmbedded && dragOn) { + emit moveStrip(this); + } + dragOn=false; +} + +void Strip::mouseMoveEvent(QMouseEvent* e) +{ + e->accept(); + if(e->buttons() == Qt::LeftButton) + { + if(!_isEmbedded) + { + if(dragOn) + { + QPoint mousePos = QCursor::pos(); + move(mousePos + mouseWidgetOffset); + } + else + { + raise(); + dragOn = true; + } + } + } + //QFrame::mouseMoveEvent(ev); +} + +void Strip::keyPressEvent(QKeyEvent* ev) +{ + const bool shift = ev->modifiers() & Qt::ShiftModifier; + const bool alt = ev->modifiers() & Qt::AltModifier; + const bool ctl = ev->modifiers() & Qt::ControlModifier; + const bool meta = ev->modifiers() & Qt::MetaModifier; + const int val = shift ? 5 : 1; + + switch (ev->key()) + { + case Qt::Key_Escape: + //if(hasFocus() && _focusYieldWidget) + if(_focusYieldWidget) + { + ev->accept(); + // Yield the focus to the given widget. + _focusYieldWidget->setFocus(); + // Activate the window. + if(!_focusYieldWidget->isActiveWindow()) + _focusYieldWidget->activateWindow(); + return; + } + break; + + case Qt::Key_Up: + if(alt && !ctl && !meta) + { + incVolume(val); + ev->accept(); + return; + } + break; + + case Qt::Key_Down: + if(alt && !ctl && !meta) + { + incVolume(-val); + ev->accept(); + return; + } + break; + + case Qt::Key_Left: + if(alt && !ctl && !meta) + { + incPan(-val); + ev->accept(); + return; + } + break; + + case Qt::Key_Right: + if(alt && !ctl && !meta) + { + incPan(val); + ev->accept(); + return; + } + break; + + default: + break; + } + + // Let mixer window or other higher up handle it. + ev->ignore(); + QFrame::keyPressEvent(ev); +} + +void Strip::setSelected(bool v) +{ + if(_selected == v) + return; + + if(_isEmbedded) + { + _selected = false; + return; + } + if (v) { + label->setFrameStyle(Raised | StyledPanel); + setHighLight(true); + // First time selected? Set the focus. + setFocus(); + } + else { + label->setFrameStyle(Sunken | StyledPanel); + setHighLight(false); + } + _selected=v; +} + +void Strip::setHighLight(bool highlight) +{ + _highlight = highlight; + update(); +} + +QString Strip::getLabelText() +{ + return label->text(); +} + +void Strip::updateMuteIcon() +{ + if(!track) + return; + + bool found = false; + MusECore::TrackList* tl = MusEGlobal::song->tracks(); + for(MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) + { + MusECore::Track* t = *it; + // Ignore this track. + if(t != track && (t->internalSolo() || t->solo())) + { + found = true; + break; + } + } + mute->setIconSetB(found && !track->internalSolo() && !track->solo()); +} + + + +//-------------------------------- +// Control ganging support. +//-------------------------------- + +struct MidiIncListStruct +{ + int _port; + int _chan; + MidiIncListStruct(int port, int chan) : _port(port), _chan(chan) { } + bool operator==(const MidiIncListStruct& other) { return other._port == _port && other._chan == _chan; } +}; + +void Strip::componentChanged(int type, double val, bool off, int id, int scrollMode) +{ + // The track has already taken care of its own controllers. + // Don't bother other tracks if the track is not selected. ie part of a selected group. + // Don't bother if broadcasting changes is disabled. + if(!track || !track->selected() || !_broadcastChanges) + return; + + // TODO: Only controller components handled for now. + if(type != ComponentRack::controllerComponent) + return; + + QList doneMidiTracks; + QList doneAudioTracks; + + if(track->isMidiTrack()) + { + // TODO: Only volume and pan handled for now. + int a_ctlnum; + switch(id) + { + case MusECore::CTRL_VOLUME: + a_ctlnum = MusECore::AC_VOLUME; + break; + case MusECore::CTRL_PANPOT: + a_ctlnum = MusECore::AC_PAN; + break; + default: + return; + break; + } + + MusECore::MidiTrack* m_track = static_cast(track); + const int m_port = m_track->outPort(); + const int m_chan = m_track->outChannel(); + MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port]; + MusECore::MidiController* m_mctl = m_mp->midiController(id, false); + if(!m_mctl) + return; + + int i_m_min = m_mctl->minVal(); + const int i_m_max = m_mctl->maxVal(); + const int i_m_bias = m_mctl->bias(); + + //---------------------------------------------------------- + // For midi volume, the formula given by MMA is: + // volume dB = 40 * log(midivol / 127) and the inverse is: + // midi volume = 127 * 10 ^ (volume dB / 40) + // Unusual, it is a factor of 40 not 20. Since muse_db2val() + // does 10 ^ (volume dB / 20), just divide volume dB by 2. + //---------------------------------------------------------- + double m_val = val; + double ma_val = val; + if(id == MusECore::CTRL_VOLUME) + { + if(MusEGlobal::config.preferMidiVolumeDb) + { + if(ma_val <= MusEGlobal::config.minSlider) + m_val = ma_val = 0.0; + else + { + m_val = double(i_m_max) * muse_db2val(m_val); + ma_val = double(i_m_max) * muse_db2val(ma_val / 2.0); + } + } + else + { + // It's a linear scale. We need to convert to Db and + // scale the linear value by 2 since the midi scale is + // twice the usual Db per step. Then convert back to linear. + m_val = muse_val2dbr(m_val / double(i_m_max)) * 2.0; + m_val *= 2.0; + m_val = double(i_m_max) * muse_db2val(m_val / 2.0); + } + } + + //-------------------------------------------------------------- + // NOTE: Midi int to audio float conversion: + // If the control has a bias at all, it is supposed + // to define the 'middle', like pan. + // But if the control's range is odd (127), that + // makes an uneven, uncentered float conversion. + // So the policy here is to force an even range for + // symmetrical +/- float conversion. (0.0 in the middle.) + // Treat value '0' (-64 pan) and '1' (-63 pan) the same. + //-------------------------------------------------------------- + if(i_m_bias != 0 && ((i_m_max - i_m_min) & 0x1)) + ++i_m_min; + const int i_m_range = i_m_max - i_m_min; + if(i_m_range == 0) // Avoid divide by zero. + return; + + if(m_val < i_m_min) + m_val = i_m_min; + if(m_val > i_m_max) + m_val = i_m_max; + const double m_fact = (m_val - (double)i_m_min) / (double)i_m_range; + + // Make sure to include this track as already done. + doneMidiTracks.append(MidiIncListStruct(m_port, m_chan)); + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Do selected tracks. Ignore this track, it has already taken care of its own controllers. + if(t == track || !t->selected()) + continue; + + if(t->isMidiTrack()) + { + MusECore::MidiTrack* mt = static_cast(t); + const int port = mt->outPort(); + const int chan = mt->outChannel(); + // Make sure midi tracks on same port and channel are only done once. + const MidiIncListStruct mils(port, chan); + if(doneMidiTracks.contains(mils)) + continue; + doneMidiTracks.append(mils); + + double d_val = ma_val; + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiController* mctl = mp->midiController(id, false); + if(!mctl) + continue; + if(off || (d_val < double(mctl->minVal())) || (d_val > double(mctl->maxVal()))) + { + if(mp->hwCtrlState(chan, id) != MusECore::CTRL_VAL_UNKNOWN) + mp->putHwCtrlEvent(MusECore::MidiPlayEvent(0, port, chan, + MusECore::ME_CONTROLLER, + id, + MusECore::CTRL_VAL_UNKNOWN)); + } + else + { + d_val += double(mctl->bias()); + mp->putControllerValue(port, chan, id, d_val, false); + } + } + else + { + // Make sure we're not doing the same track more than once. + if(doneAudioTracks.contains(t)) + continue; + doneAudioTracks.append(t); + + MusECore::AudioTrack* at = static_cast(t); + MusECore::iCtrlList icl = at->controller()->find(a_ctlnum); + if(icl == at->controller()->end()) + continue; + MusECore::CtrlList* cl = icl->second; + + // The audio volume can go above 0dB (amplification) while + // the top midi value of 127 represents 0dB. Cut it off at 0dB. + const double a_min = cl->minVal(); + const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); + const double a_range = a_max - a_min; + const double a_val = (m_fact * a_range) + a_min; + + // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. + // ScrDirect mode is one-time only on press with modifier. + if(scrollMode != SliderBase::ScrDirect) + at->recordAutomation(a_ctlnum, a_val); + at->setParam(a_ctlnum, a_val); // Schedules a timed control change. + at->enableController(a_ctlnum, false); + } + } + } + else + { + // TODO: Only volume and pan handled for now. + int m_ctlnum; + switch(id) + { + case MusECore::AC_VOLUME: + m_ctlnum = MusECore::CTRL_VOLUME; + break; + case MusECore::AC_PAN: + m_ctlnum = MusECore::CTRL_PANPOT; + break; + default: + return; + break; + } + + MusECore::AudioTrack* a_track = static_cast(track); + MusECore::iCtrlList icl = a_track->controller()->find(id); + if(icl == a_track->controller()->end()) + return; + MusECore::CtrlList* cl = icl->second; + + //---------------------------------------------------------- + // For midi volume, the formula given by MMA is: + // volume dB = 40 * log(midivol / 127) and the inverse is: + // midi volume = 127 * 10 ^ (volume dB / 40) + // Unusual, it is a factor of 40 not 20. Since muse_db2val() + // does 10 ^ (volume dB / 20), just divide volume dB by 2. + //---------------------------------------------------------- + double a_val = val; + double ma_val = val; + if(id == MusECore::AC_VOLUME) + { + if(ma_val <= MusEGlobal::config.minSlider) + a_val = ma_val = 0.0; + else + { + a_val = muse_db2val(a_val); + ma_val = muse_db2val(ma_val / 2.0); + } + } + + // The audio volume can go above 0dB (amplification) while + // the top midi value of 127 represents 0dB. Cut it off at 0dB. + const double a_min = cl->minVal(); + const double a_max = (id == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); + const double a_range = a_max - a_min; + if(a_range < 0.0001) // Avoid divide by zero. + return; + const double a_fact = (ma_val - a_min) / a_range; + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Do selected tracks. Ignore this track, it has already taken care of its own controllers. + if(t == track || !t->selected()) + continue; + if(t->isMidiTrack()) + { + MusECore::MidiTrack* mt = static_cast(t); + const int port = mt->outPort(); + const int chan = mt->outChannel(); + // Make sure midi tracks on same port and channel are only done once. + const MidiIncListStruct mils(port, chan); + if(doneMidiTracks.contains(mils)) + continue; + doneMidiTracks.append(mils); + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiController* mctl = mp->midiController(m_ctlnum, false); + if(mctl) + { + int min = mctl->minVal(); + const int max = mctl->maxVal(); + const int bias = mctl->bias(); + + //-------------------------------------------------------------- + // NOTE: Midi int to audio float conversion: + // If the control has a bias at all, it is supposed + // to define the 'middle', like pan. + // But if the control's range is odd (127), that + // makes an uneven, uncentered float conversion. + // So the policy here is to force an even range for + // symmetrical +/- float conversion. (0.0 in the middle.) + // Treat value '0' (-64 pan) and '1' (-63 pan) the same. + //-------------------------------------------------------------- + if(bias != 0 && ((max - min) & 0x1)) + ++min; + + const double d_min = (double)min; + const double d_range = double(max - min); + double d_val = a_fact * d_range + d_min; + + if(d_val < double(mctl->minVal())) + d_val = mctl->minVal(); + if(d_val > double(mctl->maxVal())) + d_val = mctl->maxVal(); + d_val += double(mctl->bias()); + mp->putControllerValue(port, chan, m_ctlnum, d_val, false); + } + } + else + { + // Make sure we're not doing the same track more than once. + if(doneAudioTracks.contains(t)) + continue; + doneAudioTracks.append(t); + + MusECore::AudioTrack* at = static_cast(t); + // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. + // ScrDirect mode is one-time only on press with modifier. + if(scrollMode != SliderBase::ScrDirect) + at->recordAutomation(id, a_val); + at->setParam(id, a_val); // Schedules a timed control change. + at->enableController(id, false); + } + } + } +} + +void Strip::componentMoved(int /*type*/, double /*val*/, int /*id*/, bool /*shift_pressed*/) +{ + //emit componentMoved(this, type, val, id, shift_pressed); +} + +void Strip::componentPressed(int type, double val, int id) +{ + // The track has already taken care of its own controllers. + // Don't bother other tracks if the track is not selected. ie part of a selected group. + // Don't bother if broadcasting changes is disabled. + if(!track || !track->selected() || !_broadcastChanges) + return; + + // TODO: Only controller components handled for now. + if(type != ComponentRack::controllerComponent) + return; + + QList doneMidiTracks; + QList doneAudioTracks; + + if(track->isMidiTrack()) + { + // TODO: Only volume and pan handled for now. + int a_ctlnum; + switch(id) + { + case MusECore::CTRL_VOLUME: + a_ctlnum = MusECore::AC_VOLUME; + break; + case MusECore::CTRL_PANPOT: + a_ctlnum = MusECore::AC_PAN; + break; + default: + return; + break; + } + + MusECore::MidiTrack* m_track = static_cast(track); + const int m_port = m_track->outPort(); + const int m_chan = m_track->outChannel(); + MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port]; + MusECore::MidiController* m_mctl = m_mp->midiController(id, false); + if(!m_mctl) + return; + + int i_m_min = m_mctl->minVal(); + const int i_m_max = m_mctl->maxVal(); + const int i_m_bias = m_mctl->bias(); + + //---------------------------------------------------------- + // For midi volume, the formula given by MMA is: + // volume dB = 40 * log(midivol / 127) and the inverse is: + // midi volume = 127 * 10 ^ (volume dB / 40) + // Unusual, it is a factor of 40 not 20. Since muse_db2val() + // does 10 ^ (volume dB / 20), just divide volume dB by 2. + //---------------------------------------------------------- + double m_val = val; + double ma_val = val; + if(id == MusECore::CTRL_VOLUME) + { + if(MusEGlobal::config.preferMidiVolumeDb) + { + if(ma_val <= MusEGlobal::config.minSlider) + m_val = ma_val = 0.0; + else + { + m_val = double(i_m_max) * muse_db2val(m_val); + ma_val = double(i_m_max) * muse_db2val(ma_val / 2.0); + } + } + else + { + // It's a linear scale. We need to convert to Db and + // scale the linear value by 2 since the midi scale is + // twice the usual Db per step. Then convert back to linear. + m_val = muse_val2dbr(m_val / double(i_m_max)) * 2.0; + m_val *= 2.0; + m_val = double(i_m_max) * muse_db2val(m_val / 2.0); + } + } + + //-------------------------------------------------------------- + // NOTE: Midi int to audio float conversion: + // If the control has a bias at all, it is supposed + // to define the 'middle', like pan. + // But if the control's range is odd (127), that + // makes an uneven, uncentered float conversion. + // So the policy here is to force an even range for + // symmetrical +/- float conversion. (0.0 in the middle.) + // Treat value '0' (-64 pan) and '1' (-63 pan) the same. + //-------------------------------------------------------------- + if(i_m_bias != 0 && ((i_m_max - i_m_min) & 0x1)) + ++i_m_min; + const int i_m_range = i_m_max - i_m_min; + if(i_m_range == 0) // Avoid divide by zero. + return; + + if(m_val < i_m_min) + m_val = i_m_min; + if(m_val > i_m_max) + m_val = i_m_max; + const double m_fact = (m_val - (double)i_m_min) / (double)i_m_range; + + // Make sure to include this track as already done. + doneMidiTracks.append(MidiIncListStruct(m_port, m_chan)); + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Do selected tracks. Ignore this track, it has already taken care of its own controllers. + if(t == track || !t->selected()) + continue; + + if(t->isMidiTrack()) + { + } + else + { + // Make sure we're not doing the same track more than once. + if(doneAudioTracks.contains(t)) + continue; + doneAudioTracks.append(t); + + MusECore::AudioTrack* at = static_cast(t); + MusECore::iCtrlList icl = at->controller()->find(a_ctlnum); + if(icl == at->controller()->end()) + continue; + MusECore::CtrlList* cl = icl->second; + + // The audio volume can go above 0dB (amplification) while + // the top midi value of 127 represents 0dB. Cut it off at 0dB. + const double a_min = cl->minVal(); + const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); + const double a_range = a_max - a_min; + const double a_val = (m_fact * a_range) + a_min; + + at->startAutoRecord(a_ctlnum, a_val); + at->setPluginCtrlVal(a_ctlnum, a_val); + //at->setParam(a_ctlnum, val); // Schedules a timed control change. // TODO Try this instead + at->enableController(a_ctlnum, false); + } + } + } + else + { + // TODO: Only volume and pan handled for now. + switch(id) + { + case MusECore::AC_VOLUME: + break; + case MusECore::AC_PAN: + break; + + default: + return; + break; + } + + double a_val = val; + if(id == MusECore::AC_VOLUME) + { + if(a_val <= MusEGlobal::config.minSlider) + a_val = 0.0; + else + a_val = muse_db2val(a_val); + } + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Do selected tracks. Ignore this track, it has already taken care of its own controllers. + if(t == track || !t->selected()) + continue; + if(t->isMidiTrack()) + { + } + else + { + // Make sure we're not doing the same track more than once. + if(doneAudioTracks.contains(t)) + continue; + doneAudioTracks.append(t); + + MusECore::AudioTrack* at = static_cast(t); + at->startAutoRecord(id, a_val); + at->setPluginCtrlVal(id, a_val); + //at->setParam(id, val); // Schedules a timed control change. // TODO Try this instead + at->enableController(id, false); + } + } + } +} + +void Strip::componentReleased(int type, double val, int id) +{ + // The track has already taken care of its own controllers. + // Don't bother other tracks if the track is not selected. ie part of a selected group. + // Don't bother if broadcasting changes is disabled. + if(!track || !track->selected() || !_broadcastChanges) + return; + + // TODO: Only controller components handled for now. + if(type != ComponentRack::controllerComponent) + return; + + QList doneMidiTracks; + QList doneAudioTracks; + + if(track->isMidiTrack()) + { + // TODO: Only volume and pan handled for now. + int a_ctlnum; + switch(id) + { + case MusECore::CTRL_VOLUME: + a_ctlnum = MusECore::AC_VOLUME; + break; + case MusECore::CTRL_PANPOT: + a_ctlnum = MusECore::AC_PAN; + break; + default: + return; + break; + } + + MusECore::MidiTrack* m_track = static_cast(track); + const int m_port = m_track->outPort(); + const int m_chan = m_track->outChannel(); + MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port]; + MusECore::MidiController* m_mctl = m_mp->midiController(id, false); + if(!m_mctl) + return; + + int i_m_min = m_mctl->minVal(); + const int i_m_max = m_mctl->maxVal(); + const int i_m_bias = m_mctl->bias(); + + //---------------------------------------------------------- + // For midi volume, the formula given by MMA is: + // volume dB = 40 * log(midivol / 127) and the inverse is: + // midi volume = 127 * 10 ^ (volume dB / 40) + // Unusual, it is a factor of 40 not 20. Since muse_db2val() + // does 10 ^ (volume dB / 20), just divide volume dB by 2. + //---------------------------------------------------------- + double m_val = val; + double ma_val = val; + if(id == MusECore::CTRL_VOLUME) + { + if(MusEGlobal::config.preferMidiVolumeDb) + { + if(ma_val <= MusEGlobal::config.minSlider) + m_val = ma_val = 0.0; + else + { + m_val = double(i_m_max) * muse_db2val(m_val); + ma_val = double(i_m_max) * muse_db2val(ma_val / 2.0); + } + } + else + { + // It's a linear scale. We need to convert to Db and + // scale the linear value by 2 since the midi scale is + // twice the usual Db per step. Then convert back to linear. + m_val = muse_val2dbr(m_val / double(i_m_max)) * 2.0; + m_val *= 2.0; + m_val = double(i_m_max) * muse_db2val(m_val / 2.0); + } + } + + //-------------------------------------------------------------- + // NOTE: Midi int to audio float conversion: + // If the control has a bias at all, it is supposed + // to define the 'middle', like pan. + // But if the control's range is odd (127), that + // makes an uneven, uncentered float conversion. + // So the policy here is to force an even range for + // symmetrical +/- float conversion. (0.0 in the middle.) + // Treat value '0' (-64 pan) and '1' (-63 pan) the same. + //-------------------------------------------------------------- + if(i_m_bias != 0 && ((i_m_max - i_m_min) & 0x1)) + ++i_m_min; + const int i_m_range = i_m_max - i_m_min; + if(i_m_range == 0) // Avoid divide by zero. + return; + + if(m_val < i_m_min) + m_val = i_m_min; + if(m_val > i_m_max) + m_val = i_m_max; + const double m_fact = (m_val - (double)i_m_min) / (double)i_m_range; + + // Make sure to include this track as already done. + doneMidiTracks.append(MidiIncListStruct(m_port, m_chan)); + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Do selected tracks. Ignore this track, it has already taken care of its own controllers. + if(t == track || !t->selected()) + continue; + + if(t->isMidiTrack()) + { + } + else + { + // Make sure we're not doing the same track more than once. + if(doneAudioTracks.contains(t)) + continue; + doneAudioTracks.append(t); + + MusECore::AudioTrack* at = static_cast(t); + MusECore::iCtrlList icl = at->controller()->find(a_ctlnum); + if(icl == at->controller()->end()) + continue; + MusECore::CtrlList* cl = icl->second; + + // The audio volume can go above 0dB (amplification) while + // the top midi value of 127 represents 0dB. Cut it off at 0dB. + const double a_min = cl->minVal(); + const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); + const double a_range = a_max - a_min; + const double a_val = (m_fact * a_range) + a_min; + + AutomationType atype = at->automationType(); + at->stopAutoRecord(a_ctlnum, a_val); + if(atype == AUTO_OFF || atype == AUTO_TOUCH) + at->enableController(a_ctlnum, true); + } + } + } + else + { + // TODO: Only volume and pan handled for now. + switch(id) + { + case MusECore::AC_VOLUME: + break; + case MusECore::AC_PAN: + break; + + default: + return; + break; + } + + //---------------------------------------------------------- + // For midi volume, the formula given by MMA is: + // volume dB = 40 * log(midivol / 127) and the inverse is: + // midi volume = 127 * 10 ^ (volume dB / 40) + // Unusual, it is a factor of 40 not 20. Since muse_db2val() + // does 10 ^ (volume dB / 20), just divide volume dB by 2. + //---------------------------------------------------------- + double a_val = val; + if(id == MusECore::AC_VOLUME) + { + if(a_val <= MusEGlobal::config.minSlider) + a_val = 0.0; + else + a_val = muse_db2val(a_val); + } + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Do selected tracks. Ignore this track, it has already taken care of its own controllers. + if(t == track || !t->selected()) + continue; + if(t->isMidiTrack()) + { + } + else + { + // Make sure we're not doing the same track more than once. + if(doneAudioTracks.contains(t)) + continue; + doneAudioTracks.append(t); + + MusECore::AudioTrack* at = static_cast(t); + AutomationType atype = at->automationType(); + at->stopAutoRecord(id, a_val); + if(atype == AUTO_OFF || atype == AUTO_TOUCH) + at->enableController(id, true); + } + } + } +} + +void Strip::componentIncremented(int type, double oldCompVal, double newCompVal, + bool off, int id, int /*scrollMode*/) +{ + // The track has already taken care of its own controllers. + // Don't bother other tracks if the track is not selected. ie part of a selected group. + // Don't bother if broadcasting changes is disabled. + if(!track || !track->selected() || !_broadcastChanges) + return; + + // TODO: Only controller components handled for now. + if(type != ComponentRack::controllerComponent) + return; + + bool wait_required = false; + QList doneMidiTracks; + QList doneAudioTracks; + + const double d_comp_val_delta = newCompVal - oldCompVal; + + if(track->isMidiTrack()) + { + // TODO: Only volume and pan handled for now. + int a_ctlnum; + switch(id) + { + case MusECore::CTRL_VOLUME: + a_ctlnum = MusECore::AC_VOLUME; + break; + case MusECore::CTRL_PANPOT: + a_ctlnum = MusECore::AC_PAN; + break; + default: + return; + break; + } + + MusECore::MidiTrack* m_track = static_cast(track); + const int m_port = m_track->outPort(); + const int m_chan = m_track->outChannel(); + MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port]; + MusECore::MidiController* m_mctl = m_mp->midiController(id, false); + if(!m_mctl) + return; + + const int i_m_min = m_mctl->minVal(); + const int i_m_max = m_mctl->maxVal(); + const int i_m_bias = m_mctl->bias(); + int i_ma_min = i_m_min; + const int i_ma_max = i_m_max; + + //---------------------------------------------------------- + // For midi volume, the formula given by MMA is: + // volume dB = 40 * log(midivol / 127) and the inverse is: + // midi volume = 127 * 10 ^ (volume dB / 40) + // Unusual, it is a factor of 40 not 20. Since muse_db2val() + // does 10 ^ (volume dB / 20), just divide volume dB by 2. + //---------------------------------------------------------- + + double m_old_val = oldCompVal; + double ma_old_val = oldCompVal; + double m_new_val = newCompVal; + double ma_new_val = newCompVal; + + if(id == MusECore::CTRL_VOLUME) + { + if(MusEGlobal::config.preferMidiVolumeDb) + { + if(ma_old_val <= MusEGlobal::config.minSlider) + m_old_val = ma_old_val = 0.0; + else + { + m_old_val = double(i_m_max) * muse_db2val(m_old_val / 2.0); + ma_old_val = double(i_ma_max) * muse_db2val(ma_old_val); + } + + if(ma_new_val <= MusEGlobal::config.minSlider) + m_new_val = ma_new_val = 0.0; + else + { + m_new_val = double(i_m_max) * muse_db2val(m_new_val / 2.0); + ma_new_val = double(i_ma_max) * muse_db2val(ma_new_val); + } + } + else + { + // It's a linear scale. We need to convert to Db and + // scale the linear value by 2 since the midi scale is + // twice the usual Db per step. Then convert back to linear. + ma_old_val = muse_val2dbr(ma_old_val / double(i_ma_max)) * 2.0; + ma_old_val *= 2.0; + ma_old_val = double(i_ma_max) * muse_db2val(ma_old_val / 2.0); + + ma_new_val = muse_val2dbr(ma_new_val / double(i_ma_max)) * 2.0; + ma_new_val *= 2.0; + ma_new_val = double(i_ma_max) * muse_db2val(ma_new_val / 2.0); + } + } + + //-------------------------------------------------------------- + // NOTE: Midi int to audio float conversion: + // If the control has a bias at all, it is supposed + // to define the 'middle', like pan. + // But if the control's range is odd (127), that + // makes an uneven, uncentered float conversion. + // So the policy here is to force an even range for + // symmetrical +/- float conversion. (0.0 in the middle.) + // Treat value '0' (-64 pan) and '1' (-63 pan) the same. + //-------------------------------------------------------------- + if(i_m_bias != 0 && ((i_ma_max - i_ma_min) & 0x1)) + ++i_ma_min; + const int i_m_range = i_m_max - i_m_min; + const int i_ma_range = i_ma_max - i_ma_min; + + if(m_old_val < i_m_min) + m_old_val = i_m_min; + if(m_old_val > i_m_max) + m_old_val = i_m_max; + + if(m_new_val < i_m_min) + m_new_val = i_m_min; + if(m_new_val > i_m_max) + m_new_val = i_m_max; + + if(ma_old_val < i_ma_min) + ma_old_val = i_ma_min; + if(ma_old_val > i_ma_max) + ma_old_val = i_ma_max; + + if(ma_new_val < i_ma_min) + ma_new_val = i_ma_min; + if(ma_new_val > i_ma_max) + ma_new_val = i_ma_max; + + if(i_m_range == 0 || i_ma_range == 0) // Avoid divide by zero. + return; + + // Get the current or last valid actual controller value. + const double m_val_delta = m_new_val - m_old_val; + const double ma_val_delta = ma_new_val - ma_old_val; + + const double ma_new_delta_fact = ma_val_delta / (double)i_ma_range; + + // Make sure to include this track as already done. + doneMidiTracks.append(MidiIncListStruct(m_port, m_chan)); + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Do selected tracks. Ignore this track, it has already taken care of its own controllers. + if(t == track || !t->selected()) + continue; + + if(t->isMidiTrack()) + { + MusECore::MidiTrack* mt = static_cast(t); + const int port = mt->outPort(); + const int chan = mt->outChannel(); + // Make sure midi tracks on same port and channel are only done once. + const MidiIncListStruct mils(port, chan); + if(doneMidiTracks.contains(mils)) + continue; + doneMidiTracks.append(mils); + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiController* mctl = mp->midiController(id, false); + if(!mctl) + continue; + int min = mctl->minVal(); + const int max = mctl->maxVal(); + + if(off) + { + if(mp->hwCtrlState(chan, id) != MusECore::CTRL_VAL_UNKNOWN) + mp->putHwCtrlEvent(MusECore::MidiPlayEvent(0, port, chan, + MusECore::ME_CONTROLLER, + id, + MusECore::CTRL_VAL_UNKNOWN)); + } + else + { + double d_fin_val = m_new_val; + // Get the current or last valid value. + double d_cur_val = mp->hwDCtrlState(chan, id); + if(MusECore::MidiController::dValIsUnknown(d_cur_val)) + d_cur_val = mp->lastValidHWDCtrlState(chan, id); + if(MusECore::MidiController::dValIsUnknown(d_cur_val)) + d_fin_val = m_new_val; + else + { + d_cur_val = d_cur_val - double(mctl->bias()); + if(id == MusECore::CTRL_VOLUME && MusEGlobal::config.preferMidiVolumeDb) + { + d_fin_val = muse_val2dbr(d_cur_val / double(max)) * 2.0; + d_fin_val += d_comp_val_delta; + d_fin_val = double(max) * muse_db2val(d_fin_val / 2.0); + } + else + d_fin_val = d_cur_val + m_val_delta; + } + + if(d_fin_val < double(min)) + d_fin_val = min; + if(d_fin_val > double(max)) + d_fin_val = max; + + d_fin_val += double(mctl->bias()); + + // False = linear not dB because we are doing the conversion here. + mp->putControllerValue(port, chan, id, d_fin_val, false); + + // Trip the wait flag. + wait_required = true; + } + } + else + { + // Make sure we're not doing the same track more than once. + if(doneAudioTracks.contains(t)) + continue; + doneAudioTracks.append(t); + + MusECore::AudioTrack* at = static_cast(t); + MusECore::iCtrlList icl = at->controller()->find(a_ctlnum); + if(icl == at->controller()->end()) + continue; + MusECore::CtrlList* cl = icl->second; + + double d_cur_val = cl->curVal(); + // The audio volume can go above 0dB (amplification) while + // the top midi value of 127 represents 0dB. Cut it off at 0dB. + const double a_min = cl->minVal(); + const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); + const double a_range = a_max - a_min; + const double a_val_delta = ma_new_delta_fact * a_range; + + if(id == MusECore::CTRL_VOLUME && MusEGlobal::config.preferMidiVolumeDb) + { + if(d_cur_val <= 0.0) + d_cur_val = MusEGlobal::config.minSlider; + else + d_cur_val = muse_val2dbr(d_cur_val); + + d_cur_val += d_comp_val_delta; + if(d_cur_val < MusEGlobal::config.minSlider) + d_cur_val = MusEGlobal::config.minSlider; + if(d_cur_val > 10.0) + d_cur_val = 10.0; + + d_cur_val = muse_db2val(d_cur_val); + } + else + d_cur_val += a_val_delta; + + if(d_cur_val < a_min) + d_cur_val = a_min; + if(d_cur_val > a_max) + d_cur_val = a_max; + + at->recordAutomation(a_ctlnum, d_cur_val); + at->setParam(a_ctlnum, d_cur_val); // Schedules a timed control change. + at->enableController(a_ctlnum, false); + + // Trip the wait flag. + wait_required = true; + } + } + } + else + { + // TODO: Only volume and pan handled for now. + int m_ctlnum; + switch(id) + { + case MusECore::AC_VOLUME: + m_ctlnum = MusECore::CTRL_VOLUME; + break; + case MusECore::AC_PAN: + m_ctlnum = MusECore::CTRL_PANPOT; + break; + default: + return; + break; + } + + MusECore::AudioTrack* a_track = static_cast(track); + MusECore::iCtrlList icl = a_track->controller()->find(id); + if(icl == a_track->controller()->end()) + return; + MusECore::CtrlList* cl = icl->second; + + //---------------------------------------------------------- + // For midi volume, the formula given by MMA is: + // volume dB = 40 * log(midivol / 127) and the inverse is: + // midi volume = 127 * 10 ^ (volume dB / 40) + // Unusual, it is a factor of 40 not 20. Since muse_db2val() + // does 10 ^ (volume dB / 20), just divide volume dB by 2. + //---------------------------------------------------------- + double a_val = newCompVal; + double ma_val = newCompVal; + if(id == MusECore::AC_VOLUME) + { + if(ma_val <= MusEGlobal::config.minSlider) + a_val = ma_val = 0.0; + else + { + a_val = muse_db2val(a_val); + ma_val = muse_db2val(ma_val / 2.0); + } + } + + // The audio volume can go above 0dB (amplification) while + // the top midi value of 127 represents 0dB. Cut it off at 0dB. + const double a_min = cl->minVal(); + const double a_max = cl->maxVal(); + const double a_range = a_max - a_min; + if(a_range < 0.0001) // Avoid divide by zero. + return; + const double a_fact_delta = muse_round2micro((newCompVal - oldCompVal) / a_range); + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Do selected tracks. Ignore this track, it has already taken care of its own controllers. + if(t == track || !t->selected()) + continue; + if(t->isMidiTrack()) + { + MusECore::MidiTrack* mt = static_cast(t); + const int port = mt->outPort(); + const int chan = mt->outChannel(); + // Make sure midi tracks on same port and channel are only done once. + const MidiIncListStruct mils(port, chan); + if(doneMidiTracks.contains(mils)) + continue; + doneMidiTracks.append(mils); + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::MidiController* mctl = mp->midiController(m_ctlnum, false); + if(mctl) + { + int min = mctl->minVal(); + const int max = mctl->maxVal(); + const int bias = mctl->bias(); + + //-------------------------------------------------------------- + // NOTE: Midi int to audio float conversion: + // If the control has a bias at all, it is supposed + // to define the 'middle', like pan. + // But if the control's range is odd (127), that + // makes an uneven, uncentered float conversion. + // So the policy here is to force an even range for + // symmetrical +/- float conversion. (0.0 in the middle.) + // Treat value '0' (-64 pan) and '1' (-63 pan) the same. + //-------------------------------------------------------------- + if(bias != 0 && ((max - min) & 0x1)) + ++min; + + const double d_min = (double)min; + const double d_max = (double)max; + const double d_range = double(max - min); + + const double m_val_delta = muse_round2micro(a_fact_delta * d_range); + + double d_fin_val = 0.0; + // Get the current or last valid value. + double d_cur_val = mp->hwDCtrlState(chan, m_ctlnum); + if(MusECore::MidiController::dValIsUnknown(d_cur_val)) + d_cur_val = mp->lastValidHWDCtrlState(chan, m_ctlnum); + if(MusECore::MidiController::dValIsUnknown(d_cur_val)) + { + if(!mctl->initValIsUnknown()) + d_cur_val = double(mctl->initVal()) + double(bias); + } + if(MusECore::MidiController::dValIsUnknown(d_cur_val)) + d_fin_val = 0.0; + else + { + d_cur_val = d_cur_val - double(mctl->bias()); + if(m_ctlnum == MusECore::CTRL_VOLUME) + { + d_fin_val = muse_val2dbr(d_cur_val / d_max) * 2.0; + d_fin_val += d_comp_val_delta; + d_fin_val = d_max * muse_db2val(d_fin_val / 2.0); + } + else + d_fin_val = d_cur_val + m_val_delta; + } + + if(d_fin_val < d_min) + d_fin_val = d_min; + if(d_fin_val > d_max) + d_fin_val = d_max; + + d_fin_val += double(mctl->bias()); + + // False = linear not dB because we are doing the conversion here. + mp->putControllerValue(port, chan, m_ctlnum, d_fin_val, false); + + // Trip the wait flag. + wait_required = true; + } + } + else + { + // Make sure we're not doing the same track more than once. + if(doneAudioTracks.contains(t)) + continue; + doneAudioTracks.append(t); + + MusECore::AudioTrack* at = static_cast(t); + MusECore::iCtrlList icl = at->controller()->find(id); + if(icl == at->controller()->end()) + continue; + MusECore::CtrlList* cl = icl->second; + + double d_cur_val = cl->curVal(); + // The audio volume can go above 0dB (amplification) while + // the top midi value of 127 represents 0dB. Cut it off at 0dB. + const double d_min = cl->minVal(); + const double d_max = cl->maxVal(); + const double d_range = d_max - d_min; + const double d_val_delta = a_fact_delta * d_range; + + if(id == MusECore::AC_VOLUME) + { + if(d_cur_val <= 0.0) + d_cur_val = MusEGlobal::config.minSlider; + else + d_cur_val = muse_val2dbr(d_cur_val); + + d_cur_val += d_comp_val_delta; + if(d_cur_val < MusEGlobal::config.minSlider) + d_cur_val = MusEGlobal::config.minSlider; + if(d_cur_val > 10.0) + d_cur_val = 10.0; + + d_cur_val = muse_db2val(d_cur_val); + } + else + d_cur_val += d_val_delta; + + if(d_cur_val < d_min) + d_cur_val = d_min; + if(d_cur_val > d_max) + d_cur_val = d_max; + + // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. + // ScrDirect mode is one-time only on press with modifier. + //if(scrollMode != SliderBase::ScrDirect) + at->recordAutomation(id, d_cur_val); + at->setParam(id, d_cur_val); // Schedules a timed control change. + at->enableController(id, false); + + // Trip the wait flag. + wait_required = true; + } + } + } + + // This is a DELTA operation. Unfortunately we may need to WAIT for the hw controls to update + // in the audio thread before we can apply ANOTHER delta to the soon-to-be 'current' value. + if(wait_required) + MusEGlobal::audio->msgAudioWait(); +} } // namespace MusEGui diff -Nru muse-2.1.2/muse/mixer/strip.h muse-3.0.2+ds1/muse/mixer/strip.h --- muse-2.1.2/muse/mixer/strip.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mixer/strip.h 2017-12-04 21:01:18.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: strip.h,v 1.3.2.2 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011 - 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,30 +25,643 @@ #ifndef __STRIP_H__ #define __STRIP_H__ +#include + #include -#include #include -#include +#include #include #include "type_defs.h" #include "globaldefs.h" -//#include "route.h" +#include "drange.h" -class QLabel; -//class QVBoxLayout; -class QToolButton; +class QMouseEvent; +class QResizeEvent; class QGridLayout; +class QLayout; +class QSize; namespace MusECore { class Track; } namespace MusEGui { -class ComboBox; +class CompactComboBox; class Meter; +class CompactKnob; +class CompactSlider; +class ElidedLabel; +class CompactToolButton; +class IconButton; + +//--------------------------------------------- +// ComponentWidget +// Defines an item in a rack layout. +//--------------------------------------------- + +class ComponentWidget +{ + public: + // Type of components. + typedef int ComponentType; + // Type of component widget. + typedef int ComponentWidgetType; + + // The component widget. + QWidget* _widget; + + public: + // Type of widget. + ComponentWidgetType _widgetType; + // Type of component: Controller, gain property, aux property etc. + ComponentType _componentType; + // User index value (controller index, aux property index etc.) provided to slots. + int _index; + // Cached current value, for widgets that use it. + //double _currentValue; + // Cached flag, for widgets that use it. + bool _pressed; + + public: + ComponentWidget() : _widget(0), + _widgetType(0), + _componentType(0), + _index(0), + _pressed(false) + { } + + ComponentWidget(QWidget* widget, + ComponentWidgetType widgetType, + ComponentType componentType, + int index + ) : + _widget(widget), + _widgetType(widgetType), + _componentType(componentType), + _index(index), + _pressed(false) + { } + + bool isValid() const { return _widget != 0; } +}; + +//--------------------------------------------- +// ComponentWidgetList +// List of components. +//--------------------------------------------- + +class ComponentWidgetList : public std::list +{ + public: + iterator find(ComponentWidget::ComponentType componentType, + int componentWidgetType = -1, + int index = -1, QWidget* widget = 0) + { + for(iterator i = begin(); i != end(); ++i) + { + ComponentWidget& cw = *i; + if(cw._componentType == componentType) + { + if((componentWidgetType == -1 || cw._widgetType == componentWidgetType) && + (index == -1 || cw._index == index) && + (!widget || cw._widget == widget)) + return i; + } + } + return end(); + } + + const_iterator find(ComponentWidget::ComponentType componentType, + int componentWidgetType = -1, + int index = -1, const QWidget* widget = 0) const + { + for(const_iterator i = begin(); i != end(); ++i) + { + const ComponentWidget& cw = *i; + if(cw._componentType == componentType) + { + if((componentWidgetType == -1 || cw._widgetType == componentWidgetType) && + (index == -1 || cw._index == index) && + (!widget || cw._widget == widget)) + return i; + } + } + return end(); + } + + iterator find(const ComponentWidget& cw) + { + return find(cw._componentType, cw._widgetType, cw._index, cw._widget); + } + + // FIXME: I don't think this is calling the const version. + const_iterator find(const ComponentWidget& cw) const + { + return find(cw._componentType, cw._widgetType, cw._index, cw._widget); + } +}; +typedef ComponentWidgetList::iterator iComponentWidget; +typedef ComponentWidgetList::const_iterator ciComponentWidget; + + +//--------------------------------------------- +// ComponentDescriptor +// Base class defining an item to be added to a rack layout. +//--------------------------------------------- + +class ComponentDescriptor +{ + public: + // Type of widget to create. + ComponentWidget::ComponentWidgetType _widgetType; + + // Type of component: Controller, gain property, aux property etc. + ComponentWidget::ComponentType _componentType; + // Object name to assign to the created widget. + const char* _objName; + + // User index value (controller index, aux property index etc.) provided to given slots. + int _index; + + QString _toolTipText; + QString _label; + QColor _color; + + bool _enabled; + + public: + ComponentDescriptor() : + _widgetType(0), + _componentType(0), + _objName(0), + _index(0), + _enabled(true) + { } + + ComponentDescriptor(ComponentWidget::ComponentWidgetType widgetType, + ComponentWidget::ComponentType type, + const char* objName = 0, + int index = 0, + const QString& toolTipText = QString(), + const QString& label = QString(), + const QColor& color = QColor(), + bool enabled = true + ) : + _widgetType(widgetType), + _componentType(type), + _objName(objName), _index(index), + _toolTipText(toolTipText), + _label(label), + _color(color), + _enabled(enabled) + { } +}; + +class ComponentRackLayout : public QVBoxLayout +{ + Q_OBJECT + + public: + ComponentRackLayout() : QVBoxLayout() { } + ComponentRackLayout(QWidget* parent) : QVBoxLayout(parent) { } +}; + +//--------------------------------------------- +// ComponentRack +// Base class rack containing components. +//--------------------------------------------- + +class ComponentRack : public QFrame +{ + Q_OBJECT + + public: + // Type of component. + enum ComponentTypes { controllerComponent = 0, propertyComponent = 1, userComponent = 1000 }; + // Possible component properties. + enum ComponentProperties { userComponentProperty = 1000 }; + // Possible widget types. + enum ComponentWidgetTypes { ExternalComponentWidget = 0, + CompactKnobComponentWidget = 1, + CompactSliderComponentWidget = 2, + ElidedLabelComponentWidget = 3, + userComponentWidget = 1000 }; + + protected: + int _id; + ComponentWidgetList _components; + ComponentRackLayout* _layout; + + // Creates a new component widget from the given desc. Called by newComponent(). + // Connects known widget types' signals to slots. + virtual void newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); + + // Adds a component widget created by newComponentWidget. + void addComponentWidget( const ComponentWidget& cw, const ComponentWidget& before = ComponentWidget() ); + + protected slots: + virtual void controllerChanged(double /*val*/, bool /*isOff*/, int /*id*/, int /*scrollMode*/) { } + virtual void controllerMoved(double /*val*/, int /*id*/, bool /*shift_pressed*/) { } + virtual void controllerPressed(double /*val*/, int /*id*/) { } + virtual void controllerReleased(double /*val*/, int /*id*/) { } + virtual void controllerRightClicked(QPoint /*p*/, int /*id*/) { } + virtual void propertyChanged(double /*val*/, bool /*isOff*/, int /*id*/, int /*scrollMode*/) { } + virtual void propertyMoved(double /*val*/, int /*id*/, bool /*shift_pressed*/) { } + virtual void propertyPressed(double /*val*/, int /*id*/) { } + virtual void propertyReleased(double /*val*/, int /*id*/) { } + virtual void propertyRightClicked(QPoint /*p*/, int /*id*/) { } + virtual void labelPropertyPressed(QPoint /*p*/, int /*id*/, Qt::MouseButtons /*buttons*/, Qt::KeyboardModifiers /*keys*/) { } + virtual void labelPropertyReleased(QPoint /*p*/, int /*id*/, Qt::MouseButtons /*buttons*/, Qt::KeyboardModifiers /*keys*/) { } + virtual void labelPropertyReturnPressed(QPoint /*p*/, int /*id*/, Qt::KeyboardModifiers /*keys*/) { } + + public slots: + virtual void songChanged(MusECore::SongChangedFlags_t) { } + virtual void configChanged(); -static const int STRIP_WIDTH = 65; + signals: + // Argument int 'type' is ComponentRack::ComponentTypes or + // other user type (Audio/MidiComponentRack::ComponentTypes for ex). + void componentChanged(int type, double val, bool off, int id, int scrollMode); + void componentMoved(int type, double val, int id, bool shift_pressed); + void componentPressed(int type, double val, int id); + void componentReleased(int type, double val, int id); + + public: + ComponentRack(int id = -1, QWidget* parent = 0, Qt::WindowFlags f = 0); + + int id() const { return _id; } + ComponentWidgetList* components() { return &_components; } + ComponentWidget* findComponent( + ComponentWidget::ComponentType componentType, + int componentWidgetType = -1, + int index = -1, + QWidget* widget = 0); + + // Destroys all components and clears the component list. + void clearDelete(); + + // Adds a component to the layout and the list. Creates a new component using + // the given desc values if the desc widget is not given. + virtual void newComponent( ComponentDescriptor*, const ComponentWidget& /*before*/ = ComponentWidget() ) = 0; + // Add a stretch to the layout. + virtual void addStretch() { _layout->addStretch(); } + // Add spacing to the layout. + virtual void addSpacing(int spc) { _layout->addSpacing(spc); } + virtual double componentMinValue(const ComponentWidget&) const; + virtual double componentMaxValue(const ComponentWidget&) const; + virtual void setComponentRange(const ComponentWidget&, double min, double max, bool updateOnly = true, + double step = 0.0, int pageSize = 1, + DoubleRange::ConversionMode mode = DoubleRange::ConvertDefault); + virtual void setComponentMinValue(const ComponentWidget&, double min, bool updateOnly = true); + virtual void setComponentMaxValue(const ComponentWidget&, double max, bool updateOnly = true); + virtual double componentValue(const ComponentWidget&) const; + virtual void setComponentValue(const ComponentWidget&, double val, bool updateOnly = true); + virtual void fitComponentValue(const ComponentWidget&, double val, bool updateOnly = true); + virtual void incComponentValue(const ComponentWidget&, int steps, bool updateOnly = true); + virtual void setComponentText(const ComponentWidget&, const QString& text, bool updateOnly = true); + virtual void setComponentEnabled(const ComponentWidget&, bool enable, bool updateOnly = true); + virtual void setComponentShowValue(const ComponentWidget&, bool show, bool updateOnly = true); + + // Sets up tabbing for the existing controls in the rack. + // Accepts a previousWidget which can be null and returns the last widget in the rack, + // which allows chaining racks or other widgets. + virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); +}; + +//--------------------------------------------- +// WidgetComponentDescriptor +// Class defining a general external QWidget item +// to be added to a rack layout. +//--------------------------------------------- + +class WidgetComponentDescriptor : public ComponentDescriptor +{ + public: + // External widget pointer set by caller, corresponding to WidgetComponent type. + QWidget* _widget; + + public: + WidgetComponentDescriptor() : + ComponentDescriptor(ComponentRack::ExternalComponentWidget, + ComponentRack::propertyComponent), + _widget(0) + { } + + WidgetComponentDescriptor( + QWidget* widget, + ComponentWidget::ComponentType componentType, + const char* objName = 0, + int index = 0, + const QString& toolTipText = QString(), + const QString& label = QString(), + const QColor& color = QColor(), + bool enabled = true + ) : + ComponentDescriptor(ComponentRack::ExternalComponentWidget, + componentType, + objName, + index, + toolTipText, + label, + color, + enabled + ), + _widget(widget) + { } +}; + + +//--------------------------------------------- +// CompactKnobComponentDescriptor +// Class defining a CompactKnob to be added to a rack layout. +//--------------------------------------------- + +class CompactKnobComponentDescriptor : public ComponentDescriptor +{ + public: + // Return value pointer created by the function, corresponding to a ComponentWidgetType: + CompactKnob* _compactKnob; + + double _min; + double _max; + int _precision; + double _step; + double _initVal; + bool _hasOffMode; + bool _isOff; + bool _showValue; + + QColor _rimColor; + QColor _faceColor; + QColor _shinyColor; + QColor _markerColor; + + QString _prefix; + QString _suffix; + QString _specialValueText; + + public: + CompactKnobComponentDescriptor() : + ComponentDescriptor(ComponentRack::CompactKnobComponentWidget, + ComponentRack::propertyComponent), + _compactKnob(0), + _min(0.0), _max(0.0), _precision(0), _step(1.0), _initVal(0.0), _hasOffMode(false), _isOff(false), _showValue(true) { } + + CompactKnobComponentDescriptor( + ComponentWidget::ComponentType componentType, + const char* objName = 0, + int index = 0, + const QString& toolTipText = QString(), + const QString& label = QString(), + const QColor& borderColour = QColor(), + const QColor& rimColour = QColor(), + const QColor& faceColour = QColor(), + const QColor& shinyColour = QColor(), + const QColor& markerColour = QColor(), + const QString& prefix = QString(), + const QString& suffix = QString(), + const QString& specialValueText = QString(), + bool enabled = true, + double min = 0.0, + double max = 100.0, + int precision = 0, + double step = 1.0, + double initVal = 0.0, + bool hasOffMode = false, + bool isOff = false, + bool showValue = true + ) + : ComponentDescriptor(ComponentRack::CompactKnobComponentWidget, + componentType, + objName, + index, + toolTipText, + label, + borderColour, + enabled + ), + _compactKnob(0), + _min(min), + _max(max), + _precision(precision), + _step(step), + _initVal(initVal), + _hasOffMode(hasOffMode), + _isOff(isOff), + _showValue(showValue), + _rimColor(rimColour), + _faceColor(faceColour), + _shinyColor(shinyColour), + _markerColor(markerColour), + _prefix(prefix), + _suffix(suffix), + _specialValueText(specialValueText) { } +}; + +//--------------------------------------------- +// CompactSliderComponentDescriptor +// Class defining a CompactSlider to be added to a rack layout. +//--------------------------------------------- + +class CompactSliderComponentDescriptor : public ComponentDescriptor +{ + public: + // Return value pointer created by the function, corresponding to a ComponentWidgetType: + CompactSlider* _compactSlider; + + int _activeBorders; + double _min; + double _max; + int _precision; + double _step; + double _initVal; + bool _hasOffMode; + bool _isOff; + bool _showValue; + + QColor _barColor; + QColor _slotColor; + QColor _thumbColor; + QString _prefix; + QString _suffix; + QString _specialValueText; + + public: + CompactSliderComponentDescriptor() : + ComponentDescriptor(ComponentRack::CompactSliderComponentWidget, + ComponentRack::propertyComponent), + _compactSlider(0), + _activeBorders(0xf), // All borders. + _min(0.0), _max(0.0), _precision(0), _step(1.0), _initVal(0.0), _hasOffMode(false), _isOff(false), _showValue(true) { } + + CompactSliderComponentDescriptor( + ComponentWidget::ComponentType componentType, + const char* objName = 0, + int index = 0, + int activeBorders = 0xf, // All four borders. + const QString& toolTipText = QString(), + const QString& label = QString(), + const QColor& borderColour = QColor(), + const QColor& barColour = QColor(), + const QColor& slotColour = QColor(), + const QColor& thumbColour = QColor(), + const QString& prefix = QString(), + const QString& suffix = QString(), + const QString& specialValueText = QString(), + bool enabled = true, + double min = 0.0, + double max = 100.0, + int precision = 0, + double step = 1.0, + double initVal = 0.0, + bool hasOffMode = false, + bool isOff = false, + bool showValue = true + ) + : ComponentDescriptor(ComponentRack::CompactSliderComponentWidget, + componentType, + objName, + index, + toolTipText, + label, + borderColour, + enabled + ), + _compactSlider(0), + _activeBorders(activeBorders), + _min(min), + _max(max), + _precision(precision), + _step(step), + _initVal(initVal), + _hasOffMode(hasOffMode), + _isOff(isOff), + _showValue(showValue), + _barColor(barColour), + _slotColor(slotColour), + _thumbColor(thumbColour), + _prefix(prefix), + _suffix(suffix), + _specialValueText(specialValueText) { } +}; + +//--------------------------------------------- +// ElidedLabelComponentDescriptor +// Class defining a ElidedLabel to be added to a rack layout. +//--------------------------------------------- + +class ElidedLabelComponentDescriptor : public ComponentDescriptor +{ + public: + // Return value pointer created by the function, corresponding to a ComponentWidgetType: + ElidedLabel* _elidedLabel; + + Qt::TextElideMode _elideMode; + int _minFontPoint; + bool _ignoreHeight; + bool _ignoreWidth; + + public: + ElidedLabelComponentDescriptor() : + ComponentDescriptor(ComponentRack::ElidedLabelComponentWidget, + ComponentRack::propertyComponent), + _elidedLabel(0), + _elideMode(Qt::ElideNone), _minFontPoint(5), _ignoreHeight(true), _ignoreWidth(false) + { } + + ElidedLabelComponentDescriptor( + ComponentWidget::ComponentType componentType, + const char* objName = 0, + int index = 0, + Qt::TextElideMode elideMode = Qt::ElideNone, + const QString& toolTipText = QString(), + const QString& text = QString(), + const QColor& borderColour = QColor(), + int minFontPoint = 5, + bool ignoreHeight = true, + bool ignoreWidth = false, + bool enabled = true + ) + : ComponentDescriptor(ComponentRack::ElidedLabelComponentWidget, + componentType, + objName, + index, + toolTipText, + text, + borderColour, + enabled + ), + _elidedLabel(0), + _elideMode(elideMode), + _minFontPoint(minFontPoint), + _ignoreHeight(ignoreHeight), + _ignoreWidth(ignoreWidth) + { } +}; + +//--------------------------------------------------------- +// TrackNameLabel +//--------------------------------------------------------- + +class TrackNameLabel : public QLabel +{ + Q_OBJECT + + protected: + virtual void mouseDoubleClickEvent(QMouseEvent*); + + signals: + void doubleClicked(); + + public: + TrackNameLabel(QWidget* parent = 0, const char* name = 0, Qt::WindowFlags f = 0); + TrackNameLabel(const QString & text, QWidget* parent = 0, const char* name = 0, Qt::WindowFlags f = 0); +}; + + +struct GridPosStruct +{ + int _row; + int _col; + int _rowSpan; + int _colSpan; + + GridPosStruct() : _row(0), _col(0), _rowSpan(0), _colSpan(0) { } + GridPosStruct(int row, int col, int rowSpan, int colSpan) + : _row(row), _col(col), _rowSpan(rowSpan), _colSpan(colSpan) { } +}; + +//--------------------------------------------------------- +// ExpanderHandle +//--------------------------------------------------------- + +class ExpanderHandle : public QFrame +{ + Q_OBJECT + + protected: + enum ResizeMode { ResizeModeNone, ResizeModeHovering, ResizeModeDragging }; + virtual void paintEvent(QPaintEvent*); + + private: + int _handleWidth; + ResizeMode _resizeMode; + QPoint _dragLastGlobPos; + + protected: + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void mouseReleaseEvent(QMouseEvent*); + //virtual void leaveEvent(QEvent*); + virtual QSize sizeHint() const; + + signals: + void moved(int xDelta); + + public: + ExpanderHandle(QWidget * parent = 0, int handleWidth = 4, Qt::WindowFlags f = 0); +}; //--------------------------------------------------------- // Strip @@ -56,46 +669,126 @@ class Strip : public QFrame { Q_OBJECT - + + private: + // Embedded strips cannot be selected, moved, or hidden. For example arranger and pianoroll. + bool _isEmbedded; + QPoint mouseWidgetOffset; + bool dragOn; + bool _visible; + bool _selected; + bool _highlight; + protected: + // Whether to propagate changes to other selected tracks. + // This includes operating a control or using the universal up/down volume/ pan keys etc. + bool _broadcastChanges; + MusECore::Track* track; - QLabel* label; - //QVBoxLayout* layout; + + TrackNameLabel* label; QGridLayout* grid; int _curGridRow; - MusEGui::Meter* meter[MAX_CHANNELS]; - - QToolButton* record; - QToolButton* solo; - QToolButton* mute; - QToolButton* iR; // Input routing button - QToolButton* oR; // Output routing button + Meter* meter[MAX_CHANNELS]; + // Extra width applied to the sizeHint, from user expanding the strip. + int _userWidth; + ExpanderHandle* _handle; + + // The widget that will receive focus when we want to clear focus. + QWidget* _focusYieldWidget; + + IconButton* record; + IconButton* solo; + IconButton* mute; + IconButton* iR; // Input routing button + IconButton* oR; // Output routing button QGridLayout* sliderGrid; - MusEGui::ComboBox* autoType; + CompactComboBox* autoType; void setLabelText(); virtual void resizeEvent(QResizeEvent*); virtual void mousePressEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void keyPressEvent(QKeyEvent *); + virtual void paintEvent(QPaintEvent *); - private slots: - void recordToggled(bool); + virtual void updateRouteButtons(); + + protected slots: + virtual void componentChanged(int type, double val, bool off, int id, int scrollMode); + virtual void componentMoved(int type, double val, int id, bool shift_pressed); + virtual void componentPressed(int type, double val, int id); + virtual void componentReleased(int type, double val, int id); + virtual void componentIncremented(int type, double oldCompVal, double newCompVal, + bool off, int id, int scrollMode); + + virtual void recordToggled(bool); void soloToggled(bool); void muteToggled(bool); - protected slots: + virtual void focusYieldWidgetDestroyed(QObject*); virtual void heartBeat(); void setAutomationType(int t); + virtual void changeTrackName(); public slots: void resetPeaks(); virtual void songChanged(MusECore::SongChangedFlags_t) = 0; virtual void configChanged() = 0; + virtual void changeUserWidth(int delta); + + virtual void incVolume(int v) = 0; + virtual void incPan(int v) = 0; + signals: + void clearStripSelection(); + void moveStrip(Strip *s); + public: - Strip(QWidget* parent, MusECore::Track* t); - ~Strip(); + Strip(QWidget* parent, MusECore::Track* t, bool hasHandle = false, bool isEmbedded = true); + virtual ~Strip(); + + // Destroy and rebuild strip components. + virtual void buildStrip() { } + + // The widget that will receive focus when we want to clear focus. + QWidget* focusYieldWidget() const { return _focusYieldWidget; } + // Sets the widget that will receive focus when we want to clear focus. + virtual void setFocusYieldWidget(QWidget*); + + // Sets up tabbing for the entire strip. + // Accepts a previousWidget which can be null and returns the last widget in the strip, + // which allows chaining other widgets. + virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0) = 0; + + bool getStripVisible() { return _visible; } + void setStripVisible(bool v) { _visible = v; } + + static const int FIXED_METER_WIDTH; + void setRecordFlag(bool flag); MusECore::Track* getTrack() const { return track; } - void setLabelFont(); + void setHighLight(bool highlight); + QString getLabelText(); + void updateStyleSheet(); + + void addGridWidget(QWidget* w, const GridPosStruct& pos, Qt::Alignment alignment = 0); + void addGridLayout(QLayout* l, const GridPosStruct& pos, Qt::Alignment alignment = 0); + + int userWidth() const { return _userWidth; } + void setUserWidth(int w); + + virtual QSize sizeHint() const; + bool isSelected() { return _selected; } + void setSelected(bool s); + + bool isEmbedded() const { return _isEmbedded; } + void setEmbedded(bool embed) { _isEmbedded = embed; } + + bool broadcastChanges() const { return _broadcastChanges; } + void setBroadcastChanges(bool v) { _broadcastChanges = v; } + + void updateMuteIcon(); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/mpevent.cpp muse-3.0.2+ds1/muse/mpevent.cpp --- muse-2.1.2/muse/mpevent.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/mpevent.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -22,17 +22,22 @@ //========================================================= #include +#include #include "mpevent.h" -#include "helper.h" -#include "event.h" -#include "midictrl.h" -#include "midiport.h" -#include "muse/midi.h" +#include "midictrl_consts.h" +#include "muse/midi_consts.h" namespace MusECore { + +template TypedMemoryPool audioMPEventRTalloc::pool; +template TypedMemoryPool seqMPEventRTalloc::pool; + +//template TypedMemoryPool MPEventRTalloc::audio_pool; +//template TypedMemoryPool MPEventRTalloc::seq_pool; + //--------------------------------------------------------- // MEvent //--------------------------------------------------------- @@ -47,56 +52,6 @@ setChannel(0); } -MEvent::MEvent(unsigned tick, int port, int channel, const Event& e) - { - setChannel(channel); - setTime(tick); - setPort(port); - setLoopNum(0); - switch(e.type()) { - case Note: - setType(ME_NOTEON); - setA(e.dataA()); - setB(e.dataB()); - break; - case Controller: - setType(ME_CONTROLLER); - setA(e.dataA()); // controller number - setB(e.dataB()); // controller value - break; - case Sysex: - setType(ME_SYSEX); - setData(e.eventData()); - break; - default: - fprintf(stderr, "MEvent::MEvent(): event type %d not implemented\n", - type()); - break; - } - } - -//--------------------------------------------------------- -// dump -//--------------------------------------------------------- - -void MEvent::dump() const - { - fprintf(stderr, "time:%d port:%d chan:%d ", _time, _port, _channel+1); - if (_type == ME_NOTEON) { - QString s = pitch2string(_a); - fprintf(stderr, "NoteOn %s(0x%x) %d\n", s.toLatin1().constData(), _a, _b); - } - else if (_type == ME_NOTEOFF) { - QString s = pitch2string(_a); - fprintf(stderr, "NoteOff %s(0x%x) %d\n", s.toLatin1().constData(), _a, _b); - } - else if (_type == ME_SYSEX) { - fprintf(stderr, "SysEx len %d 0x%0x ...\n", len(), data()[0]); - } - else - fprintf(stderr, "type:0x%02x a=%d b=%d\n", _type, _a, _b); - } - //--------------------------------------------------------- // sortingWeight //--------------------------------------------------------- @@ -104,7 +59,7 @@ int MEvent::sortingWeight() const { // Sorting weight initially worked out by Tim E. Real - // Sorted here by most popular for quickest reponse. + // Sorted here by most popular for quickest response. switch(_type) { @@ -200,6 +155,9 @@ bool MEvent::operator<(const MEvent& e) const { + // Be careful about being any more specific than these checks. + // Be sure to examine the add() method, which might be upset by it. + if (time() != e.time()) return time() < e.time(); if (port() != e.port()) @@ -216,54 +174,222 @@ } //--------------------------------------------------------- -// put -// return true on fifo overflow +// translateCtrlNum //--------------------------------------------------------- -bool MidiFifo::put(const MidiPlayEvent& event) - { - if (size < MIDI_FIFO_SIZE) { - fifo[wIndex] = event; - wIndex = (wIndex + 1) % MIDI_FIFO_SIZE; - // q_atomic_increment(&size); - ++size; - return false; - } - return true; - } - -//--------------------------------------------------------- -// get -//--------------------------------------------------------- +int MEvent::translateCtrlNum() const +{ + const int da = dataA(); + int ctrl = -1; -MidiPlayEvent MidiFifo::get() + switch(type()) + { + case ME_CONTROLLER: + switch(da) { - MidiPlayEvent event(fifo[rIndex]); - rIndex = (rIndex + 1) % MIDI_FIFO_SIZE; - --size; - return event; + case CTRL_HBANK: + ctrl = CTRL_PROGRAM; + break; + + case CTRL_LBANK: + ctrl = CTRL_PROGRAM; + break; + + case CTRL_PROGRAM: + ctrl = CTRL_PROGRAM; + break; + + default: + ctrl = da; + break; } + break; + + case ME_POLYAFTER: + { + const int pitch = da & 0x7f; + ctrl = (CTRL_POLYAFTER & ~0xff) | pitch; + } + break; + + case ME_AFTERTOUCH: + ctrl = CTRL_AFTERTOUCH; + break; + + case ME_PITCHBEND: + ctrl = CTRL_PITCH; + break; + + case ME_PROGRAM: + ctrl = CTRL_PROGRAM; + break; + + default: + break; + } + + return ctrl; +} //--------------------------------------------------------- -// peek +// add +// Optimize to eliminate duplicate events at the SAME time. +// It will not handle duplicate events at DIFFERENT times. +// Replaces event if it already exists. //--------------------------------------------------------- -const MidiPlayEvent& MidiFifo::peek(int n) - { - int idx = (rIndex + n) % MIDI_FIFO_SIZE; - return fifo[idx]; - } +void MPEventList::add(const MidiPlayEvent& ev) +{ + MPEventListRangePair_t range = equal_range(ev); + + for(iMPEvent impe = range.first; impe != range.second; ++impe) + { + // Note that (multi)set iterators are constant and can't be modified. + // The only option is to erase the old item(s), then insert a new item. + const MidiPlayEvent& l_ev = *impe; + + // The type, time, port, and channel should already be equal, according to the operator< method. + switch(ev.type()) + { + case ME_NOTEON: + case ME_NOTEOFF: + case ME_CONTROLLER: + case ME_POLYAFTER: + // Are the notes or controller numbers the same? + if(l_ev.dataA() == ev.dataA()) + { + // If the velocities or values are the same, just ignore. + if(l_ev.dataB() == ev.dataB()) + return; + // Erase the item, and insert the replacement. + erase(impe); + insert(ev); + return; + } + break; + + case ME_PROGRAM: + case ME_AFTERTOUCH: + case ME_PITCHBEND: + case ME_SONGPOS: + case ME_MTC_QUARTER: + case ME_SONGSEL: + // If the values are the same, just ignore. + if(l_ev.dataA() == ev.dataA()) + return; + // Erase the item, and insert the replacement. + erase(impe); + insert(ev); + return; + break; + + case ME_SYSEX: + { + const int len = ev.len(); + // If length is zero there's no point in adding this sysex. Just return. + if(len == 0) + return; + } + break; + + case ME_CLOCK: + case ME_START: + case ME_CONTINUE: + case ME_STOP: + case ME_SYSEX_END: + case ME_TUNE_REQ: + case ME_TICK: + case ME_SENSE: + // Event already exists. Ignore the event to be added. + return; + break; + + case ME_META: // TODO: This could be reset, or might be a meta, depending on MPEventList usage. + break; + } + } + insert(ev); +} //--------------------------------------------------------- -// remove +// add +// Optimize to eliminate duplicate events at the SAME time. +// It will not handle duplicate events at DIFFERENT times. +// Replaces event if it already exists. //--------------------------------------------------------- -void MidiFifo::remove() - { - rIndex = (rIndex + 1) % MIDI_FIFO_SIZE; - --size; - } +void SeqMPEventList::add(const MidiPlayEvent& ev) +{ + SeqMPEventListRangePair_t range = equal_range(ev); + for(iSeqMPEvent impe = range.first; impe != range.second; ++impe) + { + // Note that (multi)set iterators are constant and can't be modified. + // The only option is to erase the old item(s), then insert a new item. + const MidiPlayEvent& l_ev = *impe; + + // The type, time, port, and channel should already be equal, according to the operator< method. + switch(ev.type()) + { + case ME_NOTEON: + case ME_NOTEOFF: + case ME_CONTROLLER: + case ME_POLYAFTER: + // Are the notes or controller numbers the same? + if(l_ev.dataA() == ev.dataA()) + { + // If the velocities or values are the same, just ignore. + if(l_ev.dataB() == ev.dataB()) + return; + // Erase the item, and insert the replacement. + erase(impe); + insert(ev); + return; + } + break; + + case ME_PROGRAM: + case ME_AFTERTOUCH: + case ME_PITCHBEND: + case ME_SONGPOS: + case ME_MTC_QUARTER: + case ME_SONGSEL: + // If the values are the same, just ignore. + if(l_ev.dataA() == ev.dataA()) + return; + // Erase the item, and insert the replacement. + erase(impe); + insert(ev); + return; + break; + + case ME_SYSEX: + { + const int len = ev.len(); + // If length is zero there's no point in adding this sysex. Just return. + if(len == 0) + return; + } + break; + + case ME_CLOCK: + case ME_START: + case ME_CONTINUE: + case ME_STOP: + case ME_SYSEX_END: + case ME_TUNE_REQ: + case ME_TICK: + case ME_SENSE: + // Event already exists. Ignore the event to be added. + return; + break; + + case ME_META: // TODO: This could be reset, or might be a meta, depending on MPEventList usage. + break; + } + } + insert(ev); +} //--------------------------------------------------------- // put @@ -312,5 +438,5 @@ rIndex = (rIndex + 1) % MIDI_REC_FIFO_SIZE; --size; } - + } // namespace MusECore diff -Nru muse-2.1.2/muse/mpevent.h muse-3.0.2+ds1/muse/mpevent.h --- muse-2.1.2/muse/mpevent.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/mpevent.h 2018-01-29 20:07:03.000000000 +0000 @@ -26,9 +26,9 @@ #define __MPEVENT_H__ #include -#include #include "evdata.h" #include "memory.h" +#include // Play events ring buffer size #define MIDI_FIFO_SIZE 4096 @@ -38,7 +38,6 @@ namespace MusECore { -class Event; class EvData; //--------------------------------------------------------- @@ -58,14 +57,13 @@ int _loopNum; // The loop count when the note was recorded. public: - MEvent() { _loopNum = 0; } + MEvent() : _time(0), _port(0), _channel(0), _type(0), _a(0), _b(0), _loopNum(0) { } MEvent(unsigned tm, int p, int c, int t, int a, int b) - : _time(tm), _port(p), _channel(c & 0xf), _type(t), _a(a), _b(b) { _loopNum = 0; } + : _time(tm), _port(p), _channel(c & 0xf), _type(t), _a(a), _b(b), _loopNum(0) { } MEvent(unsigned t, int p, int type, const unsigned char* data, int len); - MEvent(unsigned t, int p, int tpe, EvData d) : _time(t), edata(d), _port(p), _type(tpe) { _loopNum = 0; } - MEvent(unsigned t, int port, int channel, const Event& e); + MEvent(unsigned t, int p, int tpe, EvData d) : _time(t), edata(d), _port(p), _type(tpe), _loopNum(0) { } - ~MEvent() {} + virtual ~MEvent() {} MEvent& operator=(const MEvent& ed) { _time = ed._time; @@ -102,10 +100,23 @@ int len() const { return edata.dataLen; } void setData(const EvData& e) { edata = e; } void setData(const unsigned char* p, int len) { edata.setData(p, len); } - void dump() const; + bool isNote() const { return _type == 0x90; } bool isNoteOff() const { return (_type == 0x80)||(_type == 0x90 && _b == 0); } bool operator<(const MEvent&) const; + bool isValid() const { return _type != 0; } + + // Returns a valid source controller number (above zero), + // translated from the event to proper internal control type. + // For example + // ME_CONTROLLER + Data(A = CTRL_HBANK) = CTRL_PROGRAM + // ME_CONTROLLER + Data(A = CTRL_LBANK) = CTRL_PROGRAM + // ME_PROGRAM = CTRL_PROGRAM + // ME_PITCHBEND = CTRL_PITCH + // ME_CONTROLLER + Data(A = ctrl) = ctrl + // Otherwise returns -1 if the event is not translatable to a controller, + // or an error occurred. + int translateCtrlNum() const; }; //--------------------------------------------------------- @@ -125,7 +136,7 @@ : MEvent(t, p, tpe, data, len) {} MidiRecordEvent(unsigned t, int p, int type, EvData data) : MEvent(t, p, type, data) {} - ~MidiRecordEvent() {} + virtual ~MidiRecordEvent() {} unsigned int tick() {return _tick;} void setTick(unsigned int tick) {_tick = tick;} @@ -146,86 +157,146 @@ : MEvent(t, p, type, data, len) {} MidiPlayEvent(unsigned t, int p, int type, EvData data) : MEvent(t, p, type, data) {} - MidiPlayEvent(unsigned t, int port, int channel, const Event& e) - : MEvent(t, port, channel, e) {} - ~MidiPlayEvent() {} + virtual ~MidiPlayEvent() {} }; //--------------------------------------------------------- -// MPEventList -// memory allocation in audio thread domain +// MidiRecFifo //--------------------------------------------------------- -typedef std::multiset, audioRTalloc > MPEL; - -struct MPEventList : public MPEL { - void add(const MidiPlayEvent& ev) { MPEL::insert(ev); } -}; +class MidiRecFifo { + MidiRecordEvent fifo[MIDI_REC_FIFO_SIZE]; + volatile int size; + int wIndex; + int rIndex; -typedef MPEventList::iterator iMPEvent; -typedef MPEventList::const_iterator ciMPEvent; + public: + MidiRecFifo() { clear(); } + 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; } + int getSize() const { return size; } + }; -/* DELETETHIS 20 ?? //--------------------------------------------------------- -// MREventList -// memory allocation in midi thread domain +// audioMPEventRTalloc //--------------------------------------------------------- -// Changed by Tim. p3.3.8 +template class audioMPEventRTalloc +{ + private: + static TypedMemoryPool pool; + + public: + typedef T value_type; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + typedef T* pointer; + typedef const T* const_pointer; + + typedef T& reference; + typedef const T& const_reference; + + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + audioMPEventRTalloc() { } + template audioMPEventRTalloc(const audioMPEventRTalloc&) {} + ~audioMPEventRTalloc() {} + + pointer allocate(size_type n, void * = 0) { return static_cast(pool.alloc(n)); } + void deallocate(pointer p, size_type n) { pool.free(p, n); } + + audioMPEventRTalloc& operator=(const audioMPEventRTalloc&) { return *this; } + void construct(pointer p, const T& val) { new ((T*) p) T(val); } + void destroy(pointer p) { p->~T(); } + size_type max_size() const { return size_t(-1); } -// audioRTalloc? Surely this must have been a mistake? -//typedef std::list > MREL; -typedef std::list > MREL; + template struct rebind { typedef audioMPEventRTalloc other; }; + template audioMPEventRTalloc& operator=(const audioMPEventRTalloc&) { return *this; } +}; -struct MREventList : public MREL { - void add(const MidiRecordEvent& ev) { MREL::push_back(ev); } - }; +//--------------------------------------------------------- +// seqMPEventRTalloc +//--------------------------------------------------------- -typedef MREventList::iterator iMREvent; -typedef MREventList::const_iterator ciMREvent; -*/ +template class seqMPEventRTalloc +{ + private: + static TypedMemoryPool pool; + + public: + typedef T value_type; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + typedef T* pointer; + typedef const T* const_pointer; + + typedef T& reference; + typedef const T& const_reference; + + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + seqMPEventRTalloc() { } + template seqMPEventRTalloc(const seqMPEventRTalloc&) {} + ~seqMPEventRTalloc() {} + + pointer allocate(size_type n, void * = 0) { return static_cast(pool.alloc(n)); } + void deallocate(pointer p, size_type n) { pool.free(p, n); } + + seqMPEventRTalloc& operator=(const seqMPEventRTalloc&) { return *this; } + void construct(pointer p, const T& val) { new ((T*) p) T(val); } + void destroy(pointer p) { p->~T(); } + size_type max_size() const { return size_t(-1); } + + template struct rebind { typedef seqMPEventRTalloc other; }; + template seqMPEventRTalloc& operator=(const seqMPEventRTalloc&) { return *this; } +}; //--------------------------------------------------------- -// MidiFifo +// MPEventList +// memory allocation in audio thread domain //--------------------------------------------------------- -class MidiFifo { - MidiPlayEvent fifo[MIDI_FIFO_SIZE]; - volatile int size; - int wIndex; - int rIndex; +typedef std::multiset, audioMPEventRTalloc > MPEL; - public: - MidiFifo() { clear(); } - bool put(const MidiPlayEvent& event); // returns true on fifo overflow - MidiPlayEvent get(); - const MidiPlayEvent& peek(int = 0); - void remove(); - bool isEmpty() const { return size == 0; } - void clear() { size = 0, wIndex = 0, rIndex = 0; } - int getSize() const { return size; } - }; +class MPEventList : public MPEL { + public: + // Optimize to eliminate duplicate events at the SAME time. + // It will not handle duplicate events at DIFFERENT times. + // Replaces event if it already exists. + void add(const MidiPlayEvent& ev); +}; + +typedef MPEventList::iterator iMPEvent; +typedef MPEventList::const_iterator ciMPEvent; +typedef std::pair MPEventListRangePair_t; //--------------------------------------------------------- -// MidiRecFifo +// SeqMPEventList +// memory allocation in sequencer thread domain //--------------------------------------------------------- -class MidiRecFifo { - MidiRecordEvent fifo[MIDI_REC_FIFO_SIZE]; - volatile int size; - int wIndex; - int rIndex; +typedef std::multiset, seqMPEventRTalloc > SMPEL; + +class SeqMPEventList : public SMPEL { + public: + // Optimize to eliminate duplicate events at the SAME time. + // It will not handle duplicate events at DIFFERENT times. + // Replaces event if it already exists. + void add(const MidiPlayEvent& ev); +}; + +typedef SeqMPEventList::iterator iSeqMPEvent; +typedef SeqMPEventList::const_iterator ciSeqMPEvent; +typedef std::pair SeqMPEventListRangePair_t; - public: - MidiRecFifo() { clear(); } - 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; } - int getSize() const { return size; } - }; } // namespace MusECore diff -Nru muse-2.1.2/muse/mplugins/CMakeLists.txt muse-3.0.2+ds1/muse/mplugins/CMakeLists.txt --- muse-2.1.2/muse/mplugins/CMakeLists.txt 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/mplugins/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -25,14 +25,14 @@ ## Expand Qt macros in source files ## if ( ENABLE_EXPERIMENTAL ) - QT4_WRAP_UI ( mplugins_experimental_uis rhythmbase.ui) + QT5_WRAP_UI ( mplugins_experimental_uis rhythmbase.ui) set ( experimental_hdrs rhythm.h ) set ( experimental_srcs rhythm.cpp ${mplugins_experimental_uis} ) endif ( ENABLE_EXPERIMENTAL ) -QT4_WRAP_CPP ( mplugins_mocs +QT5_WRAP_CPP ( mplugins_mocs midifilterimpl.h midiitransform.h mittranspose.h @@ -47,7 +47,7 @@ midifilter.ui mrconfigbase.ui ) -QT4_WRAP_UI ( mplugins_uis ${mplugins_ui_files} ) +QT5_WRAP_UI ( mplugins_uis ${mplugins_ui_files} ) ## ## List of source files to compile diff -Nru muse-2.1.2/muse/mplugins/midiitransform.cpp muse-3.0.2+ds1/muse/mplugins/midiitransform.cpp --- muse-2.1.2/muse/mplugins/midiitransform.cpp 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/mplugins/midiitransform.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -51,20 +51,21 @@ #define MIDITRANSFORM_PITCHBEND 4 #define MIDITRANSFORM_NRPN 5 #define MIDITRANSFORM_RPN 6 +#define MIDITRANSFORM_PROGRAM 7 namespace MusECore { static int selTypeTable[] = { MIDITRANSFORM_NOTE, MIDITRANSFORM_POLY, MIDITRANSFORM_CTRL, MIDITRANSFORM_ATOUCH, - MIDITRANSFORM_PITCHBEND, MIDITRANSFORM_NRPN, MIDITRANSFORM_RPN + MIDITRANSFORM_PITCHBEND, MIDITRANSFORM_NRPN, MIDITRANSFORM_RPN, MIDITRANSFORM_PROGRAM }; static int procTypeTable[] = { MIDITRANSFORM_POLY, MIDITRANSFORM_CTRL, MIDITRANSFORM_ATOUCH, - MIDITRANSFORM_PITCHBEND, MIDITRANSFORM_NRPN, MIDITRANSFORM_RPN + MIDITRANSFORM_PITCHBEND, MIDITRANSFORM_NRPN, MIDITRANSFORM_RPN, MIDITRANSFORM_PROGRAM }; -static int procVal2Map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 10, 11 }; +static int procVal2Map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12 }; struct TDict { TransformFunction id; @@ -86,6 +87,8 @@ QString name; QString comment; + MusECore::TransformToggleState toggleState; + ValOp selEventOp; int selType; @@ -145,7 +148,7 @@ procChannelb = 0; } void write(int level, Xml& xml) const; - int apply(MidiRecordEvent& ev) const; + int apply(MidiRecordEvent& ev); bool typesMatch(MidiRecordEvent& e, int selType) const; }; @@ -234,7 +237,7 @@ // 2 - event changed //--------------------------------------------------------- -int MidiInputTransformation::apply(MidiRecordEvent& event) const +int MidiInputTransformation::apply(MidiRecordEvent& event) { int t = event.type(); @@ -312,6 +315,9 @@ event.setType(ME_CONTROLLER); } break; + case MIDITRANSFORM_PROGRAM: + event.setType(ME_PROGRAM); + break; default: break; } @@ -366,6 +372,8 @@ val = procVal1a; } break; + default: + break; } if (val < 0) val = 0; @@ -414,6 +422,28 @@ val = procVal2a; } break; + case Toggle: + { + if(event.type() == ME_CONTROLLER) + { + int num = event.dataA(); + bool state = toggleState.ctrlState(num); + if(state) + { + state = false; + val = procVal2a; + } + else + { + state = true; + val = procVal2b; + } + toggleState.setCtrlState(num, state); + } + else + printf("toggle implemented only for controllers\n"); + } + break; case ScaleMap: case Keep: case Flip: @@ -469,6 +499,7 @@ case ScaleMap: case Keep: case Flip: + case Toggle: break; } if (val < 0) @@ -521,6 +552,7 @@ case ScaleMap: case Keep: case Flip: + case Toggle: break; } if (val < 0) @@ -578,6 +610,9 @@ } } break; + case MIDITRANSFORM_PROGRAM: + matched = (t == ME_PROGRAM); + break; default: fprintf(stderr, "Error matching type in MidiTransformerDialog: unknown eventtype!\n"); break; @@ -846,7 +881,7 @@ // procChannelOp procChannelVala procChannelValb //--------------------------------------------------------- -MidiInputTransformDialog::MidiInputTransformDialog(QDialog* parent, Qt::WFlags fl) +MidiInputTransformDialog::MidiInputTransformDialog(QDialog* parent, Qt::WindowFlags fl) : QDialog(parent, fl) { setupUi(this); @@ -1116,6 +1151,7 @@ case MusECore::Random: case MusECore::ScaleMap: case MusECore::Dynamic: + case MusECore::Toggle: procVal1a->setDecimals(0); procVal1a->setEnabled(true); procVal1b->setEnabled(true); @@ -1133,7 +1169,15 @@ { MusECore::TransformOperator op = MusECore::TransformOperator(MusECore::procVal2Map[val]); cmt->procVal2 = op; + procVal2OpUpdate(op); + } +//--------------------------------------------------------- +// procVal2OpUpdate +//--------------------------------------------------------- + +void MidiInputTransformDialog::procVal2OpUpdate(MusECore::TransformOperator op) + { switch (op) { case MusECore::Keep: case MusECore::Invert: @@ -1156,6 +1200,7 @@ break; case MusECore::Random: case MusECore::Dynamic: + case MusECore::Toggle: procVal2a->setDecimals(0); procVal2a->setEnabled(true); procVal2b->setEnabled(true); @@ -1192,7 +1237,7 @@ if (isFuncOp) { procEventOpSel(cmt->procEvent); procVal1OpSel(cmt->procVal1); - procVal2OpSel(cmt->procVal2); + procVal2OpUpdate(cmt->procVal2); procPortOpSel(cmt->procPort); procChannelOpSel(cmt->procChannel); } @@ -1207,7 +1252,7 @@ { QString name; for (int i = 0;; ++i) { - name.sprintf("New-%d", i); + name = QString("New-") + QString::number(i); MusECore::iMidiInputTransformation imt; for (imt = MusECore::mtlist.begin(); imt != MusECore::mtlist.end(); ++imt) { if (name == (*imt)->name) @@ -1507,6 +1552,7 @@ case MusECore::Random: case MusECore::ScaleMap: case MusECore::Dynamic: + case MusECore::Toggle: procPortVala->setDecimals(0); procPortVala->setEnabled(true); procPortValb->setEnabled(true); @@ -1563,6 +1609,7 @@ case MusECore::Random: case MusECore::ScaleMap: case MusECore::Dynamic: + case MusECore::Toggle: procChannelVala->setDecimals(0); procChannelVala->setEnabled(true); procChannelValb->setEnabled(true); diff -Nru muse-2.1.2/muse/mplugins/midiitransform.h muse-3.0.2+ds1/muse/mplugins/midiitransform.h --- muse-2.1.2/muse/mplugins/midiitransform.h 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/mplugins/midiitransform.h 2017-12-04 21:01:18.000000000 +0000 @@ -84,6 +84,7 @@ void procEventTypeSel(int); void procVal1OpSel(int); void procVal2OpSel(int); + void procVal2OpUpdate(MusECore::TransformOperator op); void funcOpSel(int); void presetChanged(QListWidgetItem*); void nameChanged(const QString&); @@ -118,7 +119,7 @@ void songChanged(MusECore::SongChangedFlags_t); public: - MidiInputTransformDialog(QDialog* parent = 0, Qt::WFlags fl = 0); + MidiInputTransformDialog(QDialog* parent = 0, Qt::WindowFlags fl = 0); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/mplugins/mittranspose.cpp muse-3.0.2+ds1/muse/mplugins/mittranspose.cpp --- muse-2.1.2/muse/mplugins/mittranspose.cpp 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/mplugins/mittranspose.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -41,7 +41,7 @@ // MITPluginTranspose //--------------------------------------------------------- -MITPluginTranspose::MITPluginTranspose(QWidget* parent, Qt::WFlags fl) +MITPluginTranspose::MITPluginTranspose(QWidget* parent, Qt::WindowFlags fl) : QWidget(parent, fl) { setupUi(this); @@ -98,8 +98,7 @@ void MITPluginTranspose::transposeChanged() { - QString s; - s.sprintf("%c%d", transpose >= 0 ? '-' : ' ', transpose); + QString s = QString(transpose >= 0 ? '-' : ' ') + QString::number(transpose); transposeLabel->setText(s); transposeChangedFlag = false; } diff -Nru muse-2.1.2/muse/mplugins/mittranspose.h muse-3.0.2+ds1/muse/mplugins/mittranspose.h --- muse-2.1.2/muse/mplugins/mittranspose.h 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/mplugins/mittranspose.h 2017-12-04 21:01:18.000000000 +0000 @@ -79,7 +79,7 @@ void noteReceived(); public: - MITPluginTranspose(QWidget* parent = 0, Qt::WFlags fl = 0); + MITPluginTranspose(QWidget* parent = 0, Qt::WindowFlags fl = 0); virtual void process(MusECore::MEvent&); virtual void readStatus(MusECore::Xml&); virtual void writeStatus(int, MusECore::Xml&) const; diff -Nru muse-2.1.2/muse/mplugins/mrconfig.cpp muse-3.0.2+ds1/muse/mplugins/mrconfig.cpp --- muse-2.1.2/muse/mplugins/mrconfig.cpp 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/mplugins/mrconfig.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -34,7 +34,7 @@ // Midi Remote Control Config //--------------------------------------------------------- -MRConfig::MRConfig(QWidget* parent, Qt::WFlags fl) +MRConfig::MRConfig(QWidget* parent, Qt::WindowFlags fl) : QWidget(parent, fl) { setupUi(this); diff -Nru muse-2.1.2/muse/mplugins/mrconfig.h muse-3.0.2+ds1/muse/mplugins/mrconfig.h --- muse-2.1.2/muse/mplugins/mrconfig.h 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/mplugins/mrconfig.h 2017-12-04 21:01:18.000000000 +0000 @@ -52,7 +52,7 @@ void setRcSteprecNote(int); public: - MRConfig(QWidget* parent=0, Qt::WFlags fl = 0); + MRConfig(QWidget* parent=0, Qt::WindowFlags fl = 0); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/mplugins/rhythm.cpp muse-3.0.2+ds1/muse/mplugins/rhythm.cpp --- muse-2.1.2/muse/mplugins/rhythm.cpp 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/mplugins/rhythm.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -35,7 +35,7 @@ // RhythmGen //--------------------------------------------------------- -RhythmGen::RhythmGen(QWidget* parent, Qt::WFlags fo) +RhythmGen::RhythmGen(QWidget* parent, Qt::WindowFlags fo) : QMainWindow(parent, fo) { setupUi(this); @@ -235,7 +235,7 @@ * The dialog will by default be modeless, unless you set 'modal' to * TRUE to construct a modal dialog. */ -RhythmGenerator::RhythmGenerator( QWidget* parent, const char* name, bool modal, Qt::WFlags fl ) +RhythmGenerator::RhythmGenerator( QWidget* parent, const char* name, bool modal, Qt::WindowFlags fl ) : QDialog( parent, name, modal, fl ) { QPixmap image0( ( const char** ) image0_data ); diff -Nru muse-2.1.2/muse/mplugins/rhythm.h muse-3.0.2+ds1/muse/mplugins/rhythm.h --- muse-2.1.2/muse/mplugins/rhythm.h 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/mplugins/rhythm.h 2017-12-04 21:01:18.000000000 +0000 @@ -207,7 +207,7 @@ public: // virtual void OnMenuCommand(int id); // virtual void OnSize(int w, int h); - RhythmGen(QWidget* parent = 0, Qt::WFlags fo = Qt::Window); + RhythmGen(QWidget* parent = 0, Qt::WindowFlags fo = Qt::Window); virtual ~RhythmGen(); // void OnPaint(); // void GenRhythm(); diff -Nru muse-2.1.2/muse/muse_atomic.h muse-3.0.2+ds1/muse/muse_atomic.h --- muse-2.1.2/muse/muse_atomic.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/muse_atomic.h 2017-12-04 21:01:18.000000000 +0000 @@ -0,0 +1,77 @@ +//========================================================= +// MusE +// Linux Music Editor +// (C) Copyright 2001 Werner Schweer (ws@seh.de) +// +// 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 __MUSE_ATOMIC_H__ +#define __MUSE_ATOMIC_H__ + +namespace MusECore { + +// FIXME: Compiler knows this define but where is it? Is compiler recognizing it? __i386__ and __i386 work. +// danvd: change i386 to more common __i386__ though boths are known to gcc and clang +// danvd: replace pthreads calls with builtin atomic operations +// danvd: turn on asm atomic operations for both i386 and x86_64 + +typedef struct { int counter; } muse_atomic_t; + +static inline int muse_atomic_read(muse_atomic_t *v) { +#ifndef __i386__ + return __sync_fetch_and_add(&v->counter, 0); +#else + return v->counter; +#endif +} + +static inline void muse_atomic_set(muse_atomic_t *v, int i) { +#ifndef __i386__ + __sync_val_compare_and_swap(&v->counter, v->counter, i); +#else + v->counter = i; +#endif +} +static inline void muse_atomic_inc(muse_atomic_t *v) { +#if !defined(__i386__) && !defined(__x86_64__) + __sync_fetch_and_add(&v->counter, 1); +#else + __asm__ __volatile__( + "lock ; " "incl %0" + :"=m" (v->counter) + :"m" (v->counter)); +#endif +} +static inline void muse_atomic_dec(muse_atomic_t *v) { +#if !defined(__i386__) && !defined(__x86_64__) + __sync_fetch_and_sub(&v->counter, 1); +#else + __asm__ __volatile__( + "lock ; " "decl %0" + :"=m" (v->counter) + :"m" (v->counter)); +#endif +} + +static inline void muse_atomic_init(muse_atomic_t*) {} + +static inline void muse_atomic_destroy(muse_atomic_t*) {} + +} // namespace MusECore + +#endif + diff -Nru muse-2.1.2/muse/muse_math.h muse-3.0.2+ds1/muse/muse_math.h --- muse-2.1.2/muse/muse_math.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/muse_math.h 2018-01-11 17:41:21.000000000 +0000 @@ -0,0 +1,39 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// muse_math.h +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __MUSE_MATH_H__ +#define __MUSE_MATH_H__ + +#include + +// Convert value to log10, rounding to nearest .000001 due to inaccuracy with log. +#define muse_log10r(x) (round(log10(x) * 1000000.0) * 0.000001) +// Convert value to dB, rounding to nearest .000001 due to inaccuracy with log. +#define muse_val2dbr(x) (round(log10(x) * 20000000.0) * 0.000001) +// Convert dB to val. +#define muse_db2val(x) exp10(x * 0.05) +// Round to the nearest .000001 +#define muse_round2micro(x) (round(x * 1000000.0) * 0.000001) + +#endif + diff -Nru muse-2.1.2/muse/muse.qrc muse-3.0.2+ds1/muse/muse.qrc --- muse-2.1.2/muse/muse.qrc 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/muse.qrc 2017-12-04 21:01:18.000000000 +0000 @@ -2,7 +2,7 @@ style.qss - + diff -Nru muse-2.1.2/muse/node.cpp muse-3.0.2+ds1/muse/node.cpp --- muse-2.1.2/muse/node.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/node.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: node.cpp,v 1.36.2.25 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -41,46 +41,23 @@ #include "wave.h" #include "utils.h" //debug #include "ticksynth.h" // metronome +#include "wavepreview.h" #include "al/dsp.h" +// REMOVE Tim. Persistent routes. Added. Make this permanent later if it works OK and makes good sense. +#define _USE_SIMPLIFIED_SOLO_CHAIN_ + // Turn on debugging messages -//#define NODE_DEBUG +//#define NODE_DEBUG // Turn on constant flow of process debugging messages -//#define NODE_DEBUG_PROCESS +//#define NODE_DEBUG_PROCESS -//#define FIFO_DEBUG -//#define METRONOME_DEBUG +//#define FIFO_DEBUG +//#define METRONOME_DEBUG namespace MusECore { //--------------------------------------------------------- -// isMute -//--------------------------------------------------------- - -bool MidiTrack::isMute() const - { - if (_solo || (_internalSolo && !_mute)) - return false; - - if (_soloRefCnt) - return true; - - return _mute; - } - -bool AudioTrack::isMute() const - { - if (_solo || (_internalSolo && !_mute)) - return false; - - if (_soloRefCnt) - return true; - - return _mute; - } - - -//--------------------------------------------------------- // setSolo //--------------------------------------------------------- @@ -100,7 +77,7 @@ _solo = val; updateSoloStates(false); } - + if (isMute()) resetMeter(); } @@ -118,7 +95,7 @@ //--------------------------------------------------------- // clearSoloRefCounts // This is a static member function. Required for outside access. -// Clears the internal static reference counts. +// Clears the internal static reference counts. //--------------------------------------------------------- void Track::clearSoloRefCounts() @@ -149,15 +126,15 @@ { _internalSolo++; _soloRefCnt++; - } + } else - if(!_tmpSoloChainNoDec) - { + if(!_tmpSoloChainNoDec) + { if(_internalSolo) _internalSolo--; if(_soloRefCnt) _soloRefCnt--; - } + } } //--------------------------------------------------------- @@ -168,16 +145,16 @@ { if(_nodeTraversed) // Anti circular mechanism. { - fprintf(stderr, "MidiTrack::updateInternalSoloStates %s :\n MusE Warning: Please check your routes: Circular path found!\n", name().toLatin1().constData()); - return; - } - //if(this == _tmpSoloChainTrack) + fprintf(stderr, "MidiTrack::updateInternalSoloStates %s :\n MusE Warning: Please check your routes: Circular path found!\n", name().toLatin1().constData()); + return; + } + //if(this == _tmpSoloChainTrack) // return; - - _nodeTraversed = true; - + + _nodeTraversed = true; + Track::updateInternalSoloStates(); - + _nodeTraversed = false; // Reset. } @@ -190,16 +167,16 @@ { if(_nodeTraversed) // Anti circular mechanism. { - fprintf(stderr, "AudioTrack::updateInternalSoloStates %s :\n MusE Warning: Please check your routes: Circular path found!\n", name().toLatin1().constData()); - return; - } - //if(this == _tmpSoloChainTrack) + fprintf(stderr, "AudioTrack::updateInternalSoloStates %s :\n MusE Warning: Please check your routes: Circular path found!\n", name().toLatin1().constData()); + return; + } + //if(this == _tmpSoloChainTrack) // return; - - _nodeTraversed = true; + + _nodeTraversed = true; Track::updateInternalSoloStates(); - + if(_tmpSoloChainDoIns) { if(type() == AUDIO_SOFTSYNTH) @@ -212,15 +189,16 @@ mt->updateInternalSoloStates(); } } - + const RouteList* rl = inRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); - else - // Support Midi Port -> Audio Input solo chains. p4.0.37 Tim. - if(ir->type == Route::MIDI_PORT_ROUTE) +#ifndef _USE_SIMPLIFIED_SOLO_CHAIN_ + else + // Support Midi Port -> Audio Input solo chains. + if(ir->type == Route::MIDI_PORT_ROUTE) { const MidiTrackList* ml = MusEGlobal::song->midis(); for(ciMidiTrack im = ml->begin(); im != ml->end(); ++im) @@ -230,18 +208,19 @@ mt->updateInternalSoloStates(); } } +#endif } } else - { + { const RouteList* rl = outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); } - } - + } + _nodeTraversed = false; // Reset. } @@ -254,33 +233,42 @@ { if(noDec && !_solo) return; - + _nodeTraversed = true; // Anti circular mechanism. - + _tmpSoloChainTrack = this; _tmpSoloChainDoIns = false; _tmpSoloChainNoDec = noDec; updateSoloState(); - + if(outPort() >= 0) { MidiPort* mp = &MusEGlobal::midiPorts[outPort()]; MidiDevice *md = mp->device(); if(md && md->isSynti()) ((SynthI*)md)->updateInternalSoloStates(); - - // Support Midi Port -> Audio Input solo chains. p4.0.14 Tim. - const int chbits = 1 << outChannel(); - const RouteList* rl = mp->outRoutes(); - for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == Route::TRACK_ROUTE && ir->track && ir->track->type() == Track::AUDIO_INPUT && (ir->channel & chbits) ) - { - ir->track->updateInternalSoloStates(); - } - } } - + +#ifdef _USE_SIMPLIFIED_SOLO_CHAIN_ + // Support Midi Track to Audio Input track soloing chain routes. + // Support omni routes only, because if channels are supported, the graphical router becomes more complicated. + const RouteList* rl = outRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == Route::TRACK_ROUTE && ir->track && ir->track->type() == Track::AUDIO_INPUT && ir->channel == -1) + ir->track->updateInternalSoloStates(); + } +#else + // Support Midi Port -> Audio Input solo chains. + const int chbits = 1 << outChannel(); + const RouteList* rl = mp->outRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == Route::TRACK_ROUTE && ir->track && ir->track->type() == Track::AUDIO_INPUT && (ir->channel & chbits) ) + ir->track->updateInternalSoloStates(); + } +#endif + _nodeTraversed = false; // Reset. } @@ -293,13 +281,13 @@ { if(noDec && !_solo) return; - + _nodeTraversed = true; // Anti circular mechanism. _tmpSoloChainTrack = this; _tmpSoloChainNoDec = noDec; updateSoloState(); - + _tmpSoloChainDoIns = true; if(type() == AUDIO_SOFTSYNTH) { @@ -311,27 +299,28 @@ mt->updateInternalSoloStates(); } } - + + const RouteList* rl = inRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { - const RouteList* rl = inRoutes(); - for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + if(ir->type == Route::TRACK_ROUTE) + ir->track->updateInternalSoloStates(); +#ifndef _USE_SIMPLIFIED_SOLO_CHAIN_ + else + // Support Midi Port -> Audio Input solo chains. + if(ir->type == Route::MIDI_PORT_ROUTE) { - if(ir->type == Route::TRACK_ROUTE) - ir->track->updateInternalSoloStates(); - else - // Support Midi Port -> Audio Input solo chains. p4.0.14 Tim. - if(ir->type == Route::MIDI_PORT_ROUTE) + const MidiTrackList* ml = MusEGlobal::song->midis(); + for(ciMidiTrack im = ml->begin(); im != ml->end(); ++im) { - const MidiTrackList* ml = MusEGlobal::song->midis(); - for(ciMidiTrack im = ml->begin(); im != ml->end(); ++im) - { - MidiTrack* mt = *im; - if(mt->outPort() == ir->midiPort && ((1 << mt->outChannel()) & ir->channel) ) - mt->updateInternalSoloStates(); - } + MidiTrack* mt = *im; + if(mt->outPort() == ir->midiPort && ((1 << mt->outChannel()) & ir->channel) ) + mt->updateInternalSoloStates(); } } - } +#endif + } + _tmpSoloChainDoIns = false; { const RouteList* rl = outRoutes(); @@ -340,420 +329,930 @@ if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); } - } - + } + _nodeTraversed = false; // Reset. } - //--------------------------------------------------------- -// setMute +// processTrackCtrls +// If trackChans is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- -void Track::setMute(bool val) +void AudioTrack::processTrackCtrls(unsigned pos, int trackChans, unsigned nframes, float** buffer) +{ + const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); + const unsigned long min_per = (MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; + unsigned long sample = 0; + + const AutomationType at = automationType(); + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + CtrlListList* cll = controller(); + CtrlList* vol_ctrl = 0; + CtrlList* pan_ctrl = 0; + { + ciCtrlList icl = cll->find(AC_VOLUME); + if(icl == cll->end()) + return; + vol_ctrl = icl->second; + icl = cll->find(AC_PAN); + if(icl == cll->end()) + return; + pan_ctrl = icl->second; + } + + int cur_slice = 0; + while(sample < nframes) + { + unsigned long nsamp = nframes - sample; + const unsigned long slice_frame = pos + 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(trackChans != 0 && !_prefader) // Don't bother if we don't want to run, or prefader is on. + { + ciCtrlList icl = cll->begin(); + for(unsigned long k = 0; k < _controlPorts; ++k) { - _mute = val; + CtrlList* cl = (icl != cll->end() ? icl->second : NULL); + CtrlInterpolate& ci = _controls[k].interp; + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)) ) ) + { + if(cl && (unsigned long)cl->id() == k) + { + cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); + if(icl != cll->end()) + ++icl; + } + else + { + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = _controls[k].dval; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; + } + } + else + { + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. + { + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + if(icl != cll->end()) + ++icl; + } + + if(MusEGlobal::audio->isPlaying()) + { + unsigned long samps = nsamp; + if(ci.eFrame != -1) + samps = (unsigned long)ci.eFrame - slice_frame; + if(samps < nsamp) + nsamp = samps; + } + +#ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "AudioTrack::processTrackCtrls k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, slice_frame, ci.eFrame, nsamp); +#endif } + } -//--------------------------------------------------------- -// setOff -//--------------------------------------------------------- +#ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "AudioTrack::processTrackCtrls 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 evframe; + while(!_controlFifo.isEmpty()) + { + const ControlEvent& v = _controlFifo.peek(); + // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. + // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. + evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; -void Track::setOff(bool val) + // Protection. Observed this condition. Why? Supposed to be linear timestamps. + if(found && evframe < frame) { - _off = val; + fprintf(stderr, "AudioTrack::processTrackCtrls *** Error: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d\n", + evframe, v.frame, v.idx, v.value, v.unique); + + // No choice but to ignore it. + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + continue; + } + + if(evframe >= nframes // Next events are for a later period. + || (!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. + break; +// _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + + if(v.idx >= _controlPorts) // Sanity check + { + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + break; + } + + found = true; + frame = evframe; + + if(trackChans != 0 && !_prefader) // Only if we want to run, and prefader is off. + { + CtrlInterpolate* ci = &_controls[v.idx].interp; + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; } + // Need to update the automation value, otherwise it overwrites later with the last automation value. + setPluginCtrlVal(v.idx, v.value); + + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + } + + if(found && trackChans != 0 && !_prefader) // 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: 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) + { + if(trackChans != 0 && !_prefader) + { + const CtrlInterpolate& vol_interp = _controls[AC_VOLUME].interp; + const CtrlInterpolate& pan_interp = _controls[AC_PAN].interp; + unsigned long k; + //const float up_fact = 1.002711275; // 3.01.. dB / 256 + //const float down_fact = 0.997296056; + const double up_fact = 1.003471749; // 3.01.. dB / 200 + const double down_fact = 0.996540262; + + float *sp1, *sp2, *dp1, *dp2; + sp1 = sp2 = dp1 = dp2 = NULL; + double _volume, v, _pan, v1, v2; + + if(trackChans == 1) + { + sp1 = sp2 = buffer[0] + sample; + dp1 = outBuffersExtraMix[0] + sample; + dp2 = outBuffersExtraMix[1] + sample; + } + else + { + sp1 = buffer[0] + sample; + sp2 = buffer[1] + sample; + dp1 = outBuffers[0] + sample; + dp2 = outBuffers[1] + sample; + } + + if(trackChans != 2) + { + const int start_ch = trackChans == 1 ? 0 : 2; + + k = 0; + if(vol_interp.doInterp && MusEGlobal::audio->isPlaying()) + { + for( ; k < nsamp; ++k) + { + _volume = vol_ctrl->interpolate(slice_frame + k, vol_interp); + v = _volume * _gain; + if(v > _curVolume) + { + if(_curVolume == 0.0) + _curVolume = 0.001; // Kick-start it from zero at -30dB. + _curVolume *= up_fact; + if(_curVolume >= v) + _curVolume = v; + } + else + if(v < _curVolume) + { + _curVolume *= down_fact; + if(_curVolume <= v || _curVolume <= 0.001) // Or if less than -30dB. + _curVolume = v; + } + const unsigned long smp = sample + k; + for(int ch = start_ch; ch < trackChans; ++ch) + *(outBuffers[ch] + smp) = *(buffer[ch] + smp) * _curVolume; + } + _controls[AC_VOLUME].dval = _volume; // Update the port. + } + else + { + if(vol_interp.doInterp) // And not playing... + _volume = vol_ctrl->interpolate(pos, vol_interp); + else + _volume = vol_interp.sVal; + _controls[AC_VOLUME].dval = _volume; // Update the port. + v = _volume * _gain; + if(v > _curVolume) + { + //fprintf(stderr, "A %f %f\n", v, _curVolume); + if(_curVolume == 0.0) + _curVolume = 0.001; // Kick-start it from zero at -30dB. + for( ; k < nsamp; ++k) + { + _curVolume *= up_fact; + if(_curVolume >= v) + { + _curVolume = v; + break; + } + const unsigned long smp = sample + k; + for(int ch = start_ch; ch < trackChans; ++ch) + *(outBuffers[ch] + smp) = *(buffer[ch] + smp) * _curVolume; + } + } + else + if(v < _curVolume) + { + //fprintf(stderr, "B %f %f\n", v, _curVolume); + for( ; k < nsamp; ++k) + { + _curVolume *= down_fact; + if(_curVolume <= v || _curVolume <= 0.001) // Or if less than -30dB. + { + _curVolume = v; + break; + } + const unsigned long smp = sample + k; + for(int ch = start_ch; ch < trackChans; ++ch) + *(outBuffers[ch] + smp) = *(buffer[ch] + smp) * _curVolume; + } + } + + const unsigned long next_smp = sample + nsamp; + for(unsigned long smp = sample + k; smp < next_smp; ++smp) + { + for(int ch = start_ch; ch < trackChans; ++ch) + *(outBuffers[ch] + smp) = *(buffer[ch] + smp) * _curVolume; + } + } + } + + k = 0; + if((vol_interp.doInterp || pan_interp.doInterp) && MusEGlobal::audio->isPlaying()) + { + for( ; k < nsamp; ++k) + { + _volume = vol_ctrl->interpolate(slice_frame + k, vol_interp); + v = _volume * _gain; + _pan = pan_ctrl->interpolate(slice_frame + k, pan_interp); + v1 = v * (1.0 - _pan); + v2 = v * (1.0 + _pan); + if(v1 > _curVol1) + { + //fprintf(stderr, "C %f %f \n", v1, _curVol1); + if(_curVol1 == 0.0) + _curVol1 = 0.001; // Kick-start it from zero at -30dB. + _curVol1 *= up_fact; + if(_curVol1 >= v1) + _curVol1 = v1; + } + else + if(v1 < _curVol1) + { + //fprintf(stderr, "D %f %f \n", v1, _curVol1); + _curVol1 *= down_fact; + if(_curVol1 <= v1 || _curVol1 <= 0.001) // Or if less than -30dB. + _curVol1 = v1; + } + *dp1++ = *sp1++ * _curVol1; + + if(v2 > _curVol2) + { + //fprintf(stderr, "E %f %f \n", v2, _curVol2); + if(_curVol2 == 0.0) + _curVol2 = 0.001; // Kick-start it from zero at -30dB. + _curVol2 *= up_fact; + if(_curVol2 >= v2) + _curVol2 = v2; + } + else + if(v2 < _curVol2) + { + //fprintf(stderr, "F %f %f \n", v2, _curVol2); + _curVol2 *= down_fact; + if(_curVol2 <= v2 || _curVol2 <= 0.001) // Or if less than -30dB. + _curVol2 = v2; + } + *dp2++ = *sp2++ * _curVol2; + } + _controls[AC_VOLUME].dval = _volume; // Update the ports. + _controls[AC_PAN].dval = _pan; + } + else + { + if(vol_interp.doInterp) // And not playing... + _volume = vol_ctrl->interpolate(pos, vol_interp); + else + _volume = vol_interp.sVal; + if(pan_interp.doInterp) // And not playing... + _pan = pan_ctrl->interpolate(pos, pan_interp); + else + _pan = pan_interp.sVal; + _controls[AC_VOLUME].dval = _volume; // Update the ports. + _controls[AC_PAN].dval = _pan; + v = _volume * _gain; + v1 = v * (1.0 - _pan); + v2 = v * (1.0 + _pan); + if(v1 > _curVol1) + { + //fprintf(stderr, "C %f %f \n", v1, _curVol1); + if(_curVol1 == 0.0) + _curVol1 = 0.001; // Kick-start it from zero at -30dB. + for( ; k < nsamp; ++k) + { + _curVol1 *= up_fact; + if(_curVol1 >= v1) + { + _curVol1 = v1; + break; + } + *dp1++ = *sp1++ * _curVol1; + } + } + else + if(v1 < _curVol1) + { + //fprintf(stderr, "D %f %f \n", v1, _curVol1); + for( ; k < nsamp; ++k) + { + _curVol1 *= down_fact; + if(_curVol1 <= v1 || _curVol1 <= 0.001) // Or if less than -30dB. + { + _curVol1 = v1; + break; + } + *dp1++ = *sp1++ * _curVol1; + } + } + for( ; k < nsamp; ++k) + *dp1++ = *sp1++ * _curVol1; + + k = 0; + if(v2 > _curVol2) + { + //fprintf(stderr, "E %f %f \n", v2, _curVol2); + if(_curVol2 == 0.0) + _curVol2 = 0.001; // Kick-start it from zero at -30dB. + for( ; k < nsamp; ++k) + { + _curVol2 *= up_fact; + if(_curVol2 >= v2) + { + _curVol2 = v2; + break; + } + *dp2++ = *sp2++ * _curVol2; + } + } + else + if(v2 < _curVol2) + { + //fprintf(stderr, "F %f %f \n", v2, _curVol2); + for( ; k < nsamp; ++k) + { + _curVol2 *= down_fact; + if(_curVol2 <= v2 || _curVol2 <= 0.001) // Or if less than -30dB. + { + _curVol2 = v2; + break; + } + *dp2++ = *sp2++ * _curVol2; + } + } + for( ; k < nsamp; ++k) + *dp2++ = *sp2++ * _curVol2; + } + } + +#ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "AudioTrack::processTrackCtrls end of sample:%lu nsamp:%lu\n", sample, nsamp); +#endif + + sample += nsamp; + } + +#ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "AudioTrack::processTrackCtrls end of cur_slice:%d\n", cur_slice); +#endif + + ++cur_slice; // Slice is done. Moving on to any next slice now... + } +} //--------------------------------------------------------- // copyData //--------------------------------------------------------- -// this is also addData(). addData() just calls copyData(..., true); -void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer, bool add /*=false*/) +void AudioTrack::copyData(unsigned pos, + int dstStartChan, int requestedDstChannels, int availDstChannels, + int srcStartChan, int srcChannels, + unsigned nframes, float** dstBuffer, + bool add, const bool* addArray) { - //Changed by T356. 12/12/09. - // Overhaul and streamline to eliminate multiple processing during one process loop. + //Changed by T356. 12/12/09. + // Overhaul and streamline to eliminate multiple processing during one process loop. // Was causing ticking sound with synths + multiple out routes because synths were being processed multiple times. // Make better use of AudioTrack::outBuffers as a post-effect pre-volume cache system for multiple calls here during processing. // Previously only WaveTrack used them. (Changed WaveTrack as well). - + #ifdef NODE_DEBUG_PROCESS - printf("MusE: AudioTrack::copyData name:%s processed:%d\n", name().toLatin1().constData(), processed()); + fprintf(stderr, "MusE: AudioTrack::copyData name:%s processed:%d _haveData:%d\n", name().toLatin1().constData(), processed(), _haveData); #endif - + if(srcStartChan == -1) srcStartChan = 0; - - int trackChans = channels(); - int srcChans = (srcChannels == -1) ? trackChans : srcChannels; - int srcTotalOutChans = totalOutChannels(); - if(channels() == 1) - srcTotalOutChans = 1; - + if(dstStartChan == -1) + dstStartChan = 0; + + // Only the destination knows how many destination channels there are, + // while only the source (this track) knows how many source channels there are. + // So take care of the source channels here, and let the caller handle the destination channels. + const int trackChans = channels(); + const int srcTotalOutChans = totalProcessBuffers(); + const int requestedSrcChans = (srcChannels == -1) ? srcTotalOutChans : srcChannels; + int availableSrcChans = requestedSrcChans; + // Force a source range to fit actual available total out channels. + if((srcStartChan + availableSrcChans) > srcTotalOutChans) + availableSrcChans = srcTotalOutChans - srcStartChan; + // Special consideration for metronome: It is not part of the track list, // and it has no in or out routes, yet multiple output tracks may call addData on it! // We can't tell how many output tracks call it, so we can only assume there might be more than one. // Not strictly necessary here because only addData is ever called, but just to be consistent... - + int i; - + + // Protection for pre-allocated _dataBuffers. + if(nframes > MusEGlobal::segmentSize) + { + fprintf(stderr, "MusE: Error: AudioTrack::copyData: nframes:%u > segmentSize:%u\n", nframes, MusEGlobal::segmentSize); + nframes = MusEGlobal::segmentSize; + } + float* buffer[srcTotalOutChans]; - float data[nframes * srcTotalOutChans]; - - // precalculate stereo volume - double vol[2]; - 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) * _gain; - vol[1] = _volume * (1.0 + _pan) * _gain; - float meter[trackChans]; + double meter[trackChans]; + + #ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "MusE: AudioTrack::copyData " + "trackChans:%d srcTotalOutChans:%d srcStartChan:%d srcChannels:%d " + "dstStartChan:%d" + "requestedSrcChans:%d availableSrcChans:%d " + "requestedDstChannels:%d availDstChannels:%d\n", + trackChans, srcTotalOutChans, srcStartChan, srcChannels, + dstStartChan, + requestedSrcChans, availableSrcChans, + requestedDstChannels, availDstChannels); + #endif // Have we been here already during this process cycle? if(processed()) { - // If there is only one (or no) output routes, it's an error - we've been called more than once per process cycle! - // No, this is no longer an error, it's deliberate. Processing no longer done in 'chains', now done randomly. p4.0.37 - #ifdef NODE_DEBUG_PROCESS - printf("MusE: AudioTrack::copyData name:%s already processed _haveData:%d\n", name().toLatin1().constData(), _haveData); - #endif - // Is there already some data gathered from a previous call during this process cycle? if(_haveData) { - // Point the input buffers at our local cached 'pre-volume' buffers. They need processing, so continue on after. - for(i = 0; i < srcTotalOutChans; ++i) - buffer[i] = outBuffers[i]; - } - else - { - // No data was available from a previous call during this process cycle. - - if (!add) + if(requestedSrcChans == 1 && requestedDstChannels >= 2) { - //Zero the supplied buffers and just return. - for(i = 0; i < dstChannels; ++i) + const int cnt = availDstChannels > 2 ? 2 : availDstChannels; + int c = 0; + if(availableSrcChans >= 1) + { + for( ; c < cnt; ++c) { - if(MusEGlobal::config.useDenormalBias) + float* sp; + if(!_prefader && srcStartChan == 0 && trackChans == 1) + sp = outBuffersExtraMix[c]; // Use the pre-panned mono-to-stereo extra buffers. + else + sp = outBuffers[srcStartChan]; // In all other cases use the main buffers. + float* dp = dstBuffer[c + dstStartChan]; + if(addArray ? addArray[c + dstStartChan] : add) { - for(unsigned int q = 0; q < nframes; ++q) - dstBuffer[i][q] = MusEGlobal::denormalBias; - } + for(unsigned k = 0; k < nframes; ++k) + *dp++ += *sp++; + } else - memset(dstBuffer[i], 0, sizeof(float) * nframes); + AL::dsp->cpy(dp, sp, nframes); + } + } + // Zero the rest of the supplied buffers. + for(i = dstStartChan + c; i < (dstStartChan + availDstChannels); ++i) + { + if(addArray ? addArray[i] : add) + continue; + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned int q = 0; q < nframes; ++q) + dstBuffer[i][q] = MusEGlobal::denormalBias; } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); + } + } + else if(requestedSrcChans >= 2 && requestedDstChannels == 1) + { + const int cnt = availableSrcChans > 2 ? 2 : availableSrcChans; + if(availDstChannels >= 1) + { + for(int sch = 0; sch < cnt; ++sch) + { + float* sp = outBuffers[srcStartChan + sch]; + float* dp = dstBuffer[dstStartChan]; + if((addArray ? addArray[dstStartChan] : add) || sch != 0) + { + for(unsigned k = 0; k < nframes; ++k) + *dp++ += *sp++; + } + else + AL::dsp->cpy(dp, sp, nframes); + } + } + else if(addArray ? !addArray[dstStartChan] : !add) + { + // Zero the supplied buffer. + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned int q = 0; q < nframes; ++q) + dstBuffer[dstStartChan][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[dstStartChan], 0, sizeof(float) * nframes); + } + } + else + { + const int cnt = availableSrcChans < availDstChannels ? availableSrcChans : availDstChannels; + for(int c = 0; c < cnt; ++c) + { + float* sp = outBuffers[c + srcStartChan]; + float* dp = dstBuffer[c + dstStartChan]; + if(addArray ? addArray[c + dstStartChan] : add) + { + for(unsigned k = 0; k < nframes; ++k) + *dp++ += *sp++; + } + else + AL::dsp->cpy(dp, sp, nframes); + } + // Zero the rest of the supplied buffers. + for(i = dstStartChan + cnt; i < (dstStartChan + availDstChannels); ++i) + { + if(addArray ? addArray[i] : add) + continue; + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned int q = 0; q < nframes; ++q) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); + } + } + } + else + { + // No data was available from a previous call during this process cycle. + + //Zero the supplied buffers and just return. + for(i = dstStartChan; i < (dstStartChan + availDstChannels); ++i) + { + if(addArray ? addArray[i] : add) + continue; + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned int q = 0; q < nframes; ++q) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); } - // else if (add) do nothing. - return; } + return; } - else + else { - // First time here during this process cycle. - + // First time here during this process cycle. + _haveData = false; // Reset. _processed = true; // Set this now. - if(off()) - { + // Start by clearing the meters. There may be multiple contributions to them below. + for(i = 0; i < trackChans; ++i) + _meter[i] = 0.0; + + if(off()) + { #ifdef NODE_DEBUG_PROCESS - printf("MusE: AudioTrack::copyData name:%s dstChannels:%d Off, zeroing buffers\n", name().toLatin1().constData(), dstChannels); + fprintf(stderr, "MusE: AudioTrack::copyData name:%s dstChannels:%d Off, zeroing buffers\n", name().toLatin1().constData(), availDstChannels); #endif - - if (!add) + + // Track is off. Zero the supplied buffers. + for(i = dstStartChan; i < (dstStartChan + availDstChannels); ++i) { - // Track is off. Zero the supplied buffers. - unsigned int q; - for(i = 0; i < dstChannels; ++i) - { - if(MusEGlobal::config.useDenormalBias) - { - for(q = 0; q < nframes; ++q) - dstBuffer[i][q] = MusEGlobal::denormalBias; - } - else - memset(dstBuffer[i], 0, sizeof(float) * nframes); - } + if(addArray ? addArray[i] : add) + continue; + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned int q = 0; q < nframes; ++q) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); } - // else if (add) do nothing - - _efxPipe->apply(0, nframes, 0); // Just process controls only, not audio (do not 'run'). - for(i = 0; i < trackChans; ++i) - _meter[i] = 0.0; - - return; + _efxPipe->apply(pos, 0, nframes, 0); // Just process controls only, not audio (do not 'run'). + processTrackCtrls(pos, 0, nframes, 0); + + //for(i = 0; i < trackChans; ++i) + // _meter[i] = 0.0; + + return; + } + + // Point the input buffers at a temporary buffer. + for(i = 0; i < srcTotalOutChans; ++i) + buffer[i] = _dataBuffers[i]; + + // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. + // For ex. if this is an audio input, Jack will set the pointers for us in AudioInput::getData! + // Don't do any processing at all if off. Whereas, mute needs to be ready for action at all times, + // so still call getData before it. Off is NOT meant to be toggled rapidly, but mute is ! + // Since the meters are cleared above, getData can contribute (add) to them directly and return HaveMeterDataOnly + // if it does not want to pass the audio for listening. + if(!getData(pos, srcTotalOutChans, nframes, buffer)) + { + #ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "MusE: AudioTrack::copyData name:%s srcTotalOutChans:%d zeroing buffers\n", name().toLatin1().constData(), srcTotalOutChans); + #endif + + // No data was available. Track is not off. Zero the working buffers and continue on. + unsigned int q; + for(i = 0; i < srcTotalOutChans; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(q = 0; q < nframes; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * nframes); + } + } + + //--------------------------------------------------- + // apply plugin chain + //--------------------------------------------------- + + // Allow it to process even if muted so that when mute is turned off, left-over buffers (reverb tails etc) can die away. + _efxPipe->apply(pos, trackChans, nframes, buffer); + + //--------------------------------------------------- + // apply volume, pan + //--------------------------------------------------- + + processTrackCtrls(pos, trackChans, nframes, buffer); + + const int valid_out_bufs = _prefader ? 0 : trackChans; + + //--------------------------------------------------- + // metering + //--------------------------------------------------- + + // FIXME TODO Need multichannel changes here? + for(int c = 0; c < trackChans; ++c) + { + meter[c] = 0.0; + float* sp = (c >= valid_out_bufs) ? buffer[c] : outBuffers[c]; // Optimize: Don't all valid outBuffers just for meters + for(unsigned k = 0; k < nframes; ++k) + { + const double f = fabs(*sp++); // If the track is mono pan has no effect on meters. + if(f > meter[c]) + meter[c] = f; + } + if(meter[c] > _meter[c]) + _meter[c] = meter[c]; + if(_meter[c] > _peak[c]) + _peak[c] = _meter[c]; + + if(_meter [c] > 1.0) + _isClipped[c] = true; } - - // Point the input buffers at a temporary stack buffer. - for(i = 0; i < srcTotalOutChans; ++i) - buffer[i] = data + i * nframes; - - // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. - // For ex. if this is an audio input, Jack will set the pointers for us in AudioInput::getData! - // Don't do any processing at all if off. Whereas, mute needs to be ready for action at all times, - // so still call getData before it. Off is NOT meant to be toggled rapidly, but mute is ! - if(!getData(pos, srcTotalOutChans, nframes, buffer) || (!add && isMute() && !_prefader)) + +// REMOVE Tim. monitor. Changed. +// if(isMute()) + // Are both playback and input are muted? + if(isMute() && !isRecMonitored()) { - #ifdef NODE_DEBUG_PROCESS - printf("MusE: AudioTrack::copyData name:%s srcTotalOutChans:%d zeroing buffers\n", name().toLatin1().constData(), srcTotalOutChans); - #endif - - // No data was available. Track is not off. Zero the working buffers and continue on. - unsigned int q; - for(i = 0; i < srcTotalOutChans; ++i) - { - if(MusEGlobal::config.useDenormalBias) + // Nothing to do. Zero the supplied buffers. + for(i = dstStartChan; i < (dstStartChan + availDstChannels); ++i) + { + if(addArray ? addArray[i] : add) + continue; + if(MusEGlobal::config.useDenormalBias) { - for(q = 0; q < nframes; ++q) - buffer[i][q] = MusEGlobal::denormalBias; - } + for(unsigned int q = 0; q < nframes; q++) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } else - memset(buffer[i], 0, sizeof(float) * nframes); - } + memset(dstBuffer[i], 0, sizeof(float) * nframes); + } + return; // We're outta here. } - //--------------------------------------------------- - // apply plugin chain - //--------------------------------------------------- + // Copy whole blocks that we can get away with here outside of the track control processing loop. + for(i = valid_out_bufs; i < srcTotalOutChans; ++i) + AL::dsp->cpy(outBuffers[i], buffer[i], nframes); - _efxPipe->apply(trackChans, nframes, buffer); + // We now have some data! Set to true. + _haveData = true; //--------------------------------------------------- // aux sends //--------------------------------------------------- - if(hasAuxSend() && !isMute()) + // FIXME TODO Need multichannel changes here? Yes + if(hasAuxSend()) { AuxList* al = MusEGlobal::song->auxs(); unsigned naux = al->size(); - for(unsigned k = 0; k < naux; ++k) + for(unsigned k = 0; k < naux; ++k) { - float m = _auxSend[k]; + double m = _auxSend[k]; if(m <= 0.0001) // optimize continue; AudioAux* a = (AudioAux*)((*al)[k]); float** dst = a->sendBuffer(); int auxChannels = a->channels(); - if((srcChans ==1 && auxChannels==1) || srcChans == 2) + if((availableSrcChans ==1 && auxChannels==1) || availableSrcChans == 2) { - for(int ch = 0; ch < srcChans; ++ch) + for(int ch = 0; ch < availableSrcChans; ++ch) { float* db = dst[ch % a->channels()]; // no matter whether there's one or two dst buffers - float* sb = buffer[ch]; - for(unsigned f = 0; f < nframes; ++f) - *db++ += (*sb++ * m * vol[ch]); // add to mix + float* sb = outBuffers[ch]; + for(unsigned f = 0; f < nframes; ++f) + *db++ += (*sb++ * m); // add to mix } } - else if(srcChans==1 && auxChannels==2) // copy mono to both channels - { - for(int ch = 0; ch < auxChannels; ++ch) + else if(availableSrcChans==1 && auxChannels==2) // copy mono to both channels + { + for(int ch = 0; ch < auxChannels; ++ch) { float* db = dst[ch % a->channels()]; - float* sb = buffer[0]; - for(unsigned f = 0; f < nframes; ++f) - *db++ += (*sb++ * m * vol[ch]); // add to mix + float* sb = outBuffers[0]; + for(unsigned f = 0; f < nframes; ++f) + *db++ += (*sb++ * m); // add to mix } } } } //--------------------------------------------------- - // prefader metering + // copy to destination buffers //--------------------------------------------------- - if(_prefader) + // FIXME TODO Need multichannel changes here? + // Sanity check. Is source starting channel out of range? Just zero and return. + if(srcStartChan >= srcTotalOutChans) { - for(i = 0; i < trackChans; ++i) + for(i = dstStartChan; i < (dstStartChan + availDstChannels); ++i) { - float* p = buffer[i]; - meter[i] = 0.0; - for(unsigned k = 0; k < nframes; ++k) + if(addArray ? addArray[i] : add) + continue; + if(MusEGlobal::config.useDenormalBias) { - double f = fabs(*p++); - if(f > meter[i]) - meter[i] = f; + for(unsigned int q = 0; q < nframes; q++) + dstBuffer[i][q] = MusEGlobal::denormalBias; } - _meter[i] = meter[i]; - if(_meter[i] > _peak[i]) - _peak[i] = _meter[i]; + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); } + return; } - - if(isMute()) + + if(requestedSrcChans == 1 && requestedDstChannels >= 2) { - if (!add) + const int cnt = availDstChannels > 2 ? 2 : availDstChannels; + int c = 0; + if(availableSrcChans >= 1) { - unsigned int q; - for(i = 0; i < dstChannels; ++i) + for( ; c < cnt; ++c) + { + float* sp; + if(!_prefader && srcStartChan == 0 && trackChans == 1) + sp = outBuffersExtraMix[c]; // Use the pre-panned mono-to-stereo extra buffers. + else + sp = outBuffers[srcStartChan]; // In all other cases use the main buffers. + float* dp = dstBuffer[c + dstStartChan]; + if(addArray ? addArray[c + dstStartChan] : add) { - if(MusEGlobal::config.useDenormalBias) - { - for(q = 0; q < nframes; q++) - dstBuffer[i][q] = MusEGlobal::denormalBias; - } - else - memset(dstBuffer[i], 0, sizeof(float) * nframes); - } + for(unsigned k = 0; k < nframes; ++k) + *dp++ += *sp++; + } + else + AL::dsp->cpy(dp, sp, nframes); + } } - - if(!_prefader) - for(i = 0; i < trackChans; ++i) // Must process ALL channels, even if unconnected. Only max 2 channels. - _meter[i] = 0.0; - - return; - } - - // If we're using local cached 'pre-volume' buffers, copy the input buffers (as they are right now: post-effect pre-volume) back to them. - for(i = 0; i < srcTotalOutChans; ++i) - AL::dsp->cpy(outBuffers[i], buffer[i], nframes); - - // We have some data! Set to true. - _haveData = true; - } - - // Sanity check. Is source starting channel out of range? Just zero and return. - if(srcStartChan >= srcTotalOutChans) - { - unsigned int q; - for(i = 0; i < dstChannels; ++i) - { - if(MusEGlobal::config.useDenormalBias) - { - for(q = 0; q < nframes; q++) - dstBuffer[i][q] = MusEGlobal::denormalBias; - } - else - memset(dstBuffer[i], 0, sizeof(float) * nframes); - } - return; - } - - // Force a source range to fit actual available total out channels. - if((srcStartChan + srcChans) > srcTotalOutChans) - srcChans = srcTotalOutChans - srcStartChan; - - //--------------------------------------------------- - // apply volume - // postfader metering - //--------------------------------------------------- - - #ifdef NODE_DEBUG_PROCESS - printf("MusE: AudioTrack::copyData trackChans:%d srcTotalOutChans:%d srcStartChan:%d srcChans:%d dstChannels:%d\n", trackChans, srcTotalOutChans, srcStartChan, srcChans, dstChannels); - #endif - - if(!_prefader) - { - for(int c = 0; c < trackChans; ++c) - { - meter[c] = 0.0; - double v = (trackChans == 1 ? _volume : vol[c]); - float* sp = buffer[c]; - for(unsigned k = 0; k < nframes; ++k) + // Zero the rest of the supplied buffers. + for(i = dstStartChan + c; i < (dstStartChan + availDstChannels); ++i) { - float val = *sp++ * v; // If the track is mono pan has no effect on meters. - double f = fabs(val); - if(f > meter[c]) - meter[c] = f; + if(addArray ? addArray[i] : add) + continue; + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned int q = 0; q < nframes; ++q) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); } - _meter[c] = meter[c]; - if(_meter[c] > _peak[c]) - _peak[c] = _meter[c]; - } - } - - if(srcChans == dstChannels) - { - for(int c = 0; c < dstChannels; ++c) + } + else if(requestedSrcChans >= 2 && requestedDstChannels == 1) { - double v; - if(srcStartChan > 2 || _prefader) // Don't apply pan or volume to extra channels above 2. Or if prefader on. - v = 1.0; - else - if(srcChans >= 2) // If 2 channels apply pan normally. - v = vol[c]; - else - if(trackChans < 2) // If 1 channel and track is 1 channel, don't apply pan. - v = _volume; - else - v = vol[srcStartChan]; // Otherwise 1 channel but track is 2 channels. Apply the channel volume. - - float* sp = buffer[c + srcStartChan]; - float* dp = dstBuffer[c]; - - if (!add) + const int cnt = availableSrcChans > 2 ? 2 : availableSrcChans; + if(availDstChannels >= 1) { - for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp++ * v); + for(int sch = 0; sch < cnt; ++sch) + { + float* sp = outBuffers[srcStartChan + sch]; + float* dp = dstBuffer[dstStartChan]; + if((addArray ? addArray[dstStartChan] : add) || sch != 0) + { + for(unsigned k = 0; k < nframes; ++k) + *dp++ += *sp++; + } + else + AL::dsp->cpy(dp, sp, nframes); + } } - else // if (add) + else if(addArray ? !addArray[dstStartChan] : !add) { - for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp++ * v); + // Zero the supplied buffer. + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned int q = 0; q < nframes; ++q) + dstBuffer[dstStartChan][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[dstStartChan], 0, sizeof(float) * nframes); } } - } - else if(srcChans == 1 && dstChannels == 2) - { - for(int c = 0; c < dstChannels; ++c) + else //if(srcChans == dstChans) { - double v; - if(srcStartChan > 2 || _prefader) // Don't apply pan or volume to extra channels above 2. Or if prefader on. - v = 1.0; - else - if(trackChans <= 1) // If track is mono apply pan. - v = vol[c]; - else - v = vol[srcStartChan]; // Otherwise track is stereo, apply the same channel volume to both. - - float* sp = buffer[srcStartChan]; - float* dp = dstBuffer[c]; - - if (!add) + const int cnt = availableSrcChans < availDstChannels ? availableSrcChans : availDstChannels; + for(int c = 0; c < cnt; ++c) { + float* sp = outBuffers[c + srcStartChan]; + float* dp = dstBuffer[c + dstStartChan]; + if(addArray ? addArray[c + dstStartChan] : add) + { for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp++ * v); + *dp++ += *sp++; + } + else + AL::dsp->cpy(dp, sp, nframes); } - else // if (add) + // Zero the rest of the supplied buffers. + for(i = dstStartChan + cnt; i < (dstStartChan + availDstChannels); ++i) { - for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp++ * v); + if(addArray ? addArray[i] : add) + continue; + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned int q = 0; q < nframes; ++q) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); } } } - else if(srcChans == 2 && dstChannels == 1) - { - double v1 = ((srcStartChan > 2 || _prefader) ? 1.0 : vol[srcStartChan]); // Don't apply pan or volume to extra channels above 2. Or if prefader on. - double v2 = ((srcStartChan > 2 || _prefader) ? 1.0 : vol[srcStartChan + 1]); // - float* dp = dstBuffer[0]; - float* sp1 = buffer[srcStartChan]; - float* sp2 = buffer[srcStartChan + 1]; - - if (!add) - { - for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp1++ * v1 + *sp2++ * v2); - } - else // if (add) - { - for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp1++ * v1 + *sp2++ * v2); - } - } } //--------------------------------------------------------- -// addData -//--------------------------------------------------------- - -void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer) -{ - copyData(pos,dstChannels,srcStartChan,srcChannels,nframes,dstBuffer, true); -} -//--------------------------------------------------------- // readVolume //--------------------------------------------------------- @@ -785,67 +1284,6 @@ } } -// DELETETHIS 56 -// Removed by T356 -// "recfile" tag not saved anymore -/* - -THIS CODE IS OBSOLETE! _recFile has been changed from SndFile* to SndFileR. -this code has NOT been adapted! - -//--------------------------------------------------------- -// readRecfile -//--------------------------------------------------------- - -void AudioTrack::readRecfile(Xml& xml) - { - QString path; - int channels = 2; - int format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - - for (;;) { - Xml::Token token = xml.parse(); - if (token == Xml::Error || token == Xml::End) - break; - const QString& tag = xml.s1(); - switch (token) { - case Xml::TagStart: - if (tag == "path") - path = xml.parse1(); - else if (tag == "channels") - channels = xml.parseInt(); - else if (tag == "format") - format = xml.parseInt(); - else if (tag == "samplebits") - ; - else - xml.unknown("recfile"); - break; - case Xml::TagEnd: - if (tag == "recfile") { - if (QFile::exists(path)) { - setRecFile(getWave(path, true)); - } - else { - setRecFile(new SndFile(path)); - recFile()->setFormat(format, channels, sampleRate); - if (recFile()->openWrite()) { - fprintf(stderr, "create wave file(%s) failed: %s\n", - path.toLatin1().constData(), recFile()->strerror().toLatin1().constData()); - delete _recFile; - _recFile = 0; - } - } - return; - } - default: - break; - } - } - } -*/ - - //--------------------------------------------------------- // setChannels //--------------------------------------------------------- @@ -854,7 +1292,7 @@ { if(n > MAX_CHANNELS) _channels = MAX_CHANNELS; - else + else _channels = n; for (int i = 0; i < _channels; ++i) { _meter[i] = 0.0; @@ -884,7 +1322,7 @@ void AudioTrack::putFifo(int channels, unsigned long n, float** bp) { if (fifo.put(channels, n, bp, MusEGlobal::audio->pos().frame())) { - printf(" overrun ???\n"); + fprintf(stderr, " overrun ???\n"); } } @@ -898,44 +1336,68 @@ // use supplied buffers RouteList* rl = inRoutes(); - - #ifdef NODE_DEBUG_PROCESS - printf("AudioTrack::getData name:%s inRoutes:%lu\n", name().toLatin1().constData(), rl->size()); - #endif - - ciRoute ir = rl->begin(); - if (ir == rl->end()) - return false; - - if(ir->track->isMidiTrack()) - return false; - + #ifdef NODE_DEBUG_PROCESS - printf(" calling copyData on %s...\n", ir->track->name().toLatin1().constData()); + fprintf(stderr, "AudioTrack::getData name:%s channels:%d inRoutes:%d\n", name().toLatin1().constData(), channels, int(rl->size())); #endif - - ((AudioTrack*)ir->track)->copyData(pos, channels, - ir->channel, - ir->channels, - nframes, buffer); - - - ++ir; - for (; ir != rl->end(); ++ir) { - #ifdef NODE_DEBUG_PROCESS - printf(" calling addData on %s...\n", ir->track->name().toLatin1().constData()); - #endif - + + bool have_data = false; + bool used_in_chan_array[channels]; + for(int i = 0; i < channels; ++i) + used_in_chan_array[i] = false; + + for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->track->isMidiTrack()) continue; - - ((AudioTrack*)ir->track)->addData(pos, channels, - //(ir->track->type() == Track::AUDIO_SOFTSYNTH && ir->channel != -1) ? ir->channel : 0, - ir->channel, - ir->channels, - nframes, buffer); + + // Only this track knows how many destination channels there are, + // while only the route track knows how many source channels there are. + // So take care of the destination channels here, and let the route track handle the source channels. + const int dst_ch = ir->channel <= -1 ? 0 : ir->channel; + if(dst_ch >= channels) + continue; + const int dst_chs = ir->channels <= -1 ? channels : ir->channels; + const int src_ch = ir->remoteChannel <= -1 ? 0 : ir->remoteChannel; + const int src_chs = ir->channels; + + int fin_dst_chs = dst_chs; + if(dst_ch + fin_dst_chs > channels) + fin_dst_chs = channels - dst_ch; + + #ifdef NODE_DEBUG_PROCESS + fprintf(stderr, " calling copy/addData on %s dst_ch:%d dst_chs:%d fin_dst_chs:%d src_ch:%d src_chs:%d ...\n", + ir->track->name().toLatin1().constData(), + dst_ch, dst_chs, fin_dst_chs, + src_ch, src_chs); + #endif + + static_cast(ir->track)->copyData(pos, + dst_ch, dst_chs, fin_dst_chs, + src_ch, src_chs, + nframes, buffer, + false, used_in_chan_array); + const int next_chan = dst_ch + fin_dst_chs; + for(int i = dst_ch; i < next_chan; ++i) + used_in_chan_array[i] = true; + have_data = true; } - return true; + + // Fill unsused channels with silence. + for(int i = 0; i < channels; ++i) + { + if(used_in_chan_array[i]) + continue; + // Channel is unused. Zero the supplied buffer. + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned int q = 0; q < nframes; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * nframes); + } + + return have_data; } //--------------------------------------------------------- @@ -946,40 +1408,40 @@ bool AudioInput::getData(unsigned, int channels, unsigned nframes, float** buffer) { if (!MusEGlobal::checkAudioDevice()) return false; - for (int ch = 0; ch < channels; ++ch) + for (int ch = 0; ch < channels; ++ch) { void* jackPort = jackPorts[ch]; - + // Do not get buffers of unconnected client ports. Causes repeating leftover data, can be loud, or DC ! - if (jackPort && MusEGlobal::audioDevice->connections(jackPort)) + if (jackPort && MusEGlobal::audioDevice->connections(jackPort)) { //buffer[ch] = MusEGlobal::audioDevice->getBuffer(jackPort, nframes); - // If the client port buffer is also used by another channel (connected to the same jack port), - // don't directly set pointer, copy the data instead. + // If the client port buffer is also used by another channel (connected to the same jack port), + // don't directly set pointer, copy the data instead. // Otherwise the next channel will interfere - it will overwrite the buffer ! // Verified symptoms: Can't use a splitter. Mono noise source on a stereo track sounds in mono. Etc... // TODO: Problem: What if other Audio Input tracks share the same jack ports as this Audio Input track? // Users will expect that Audio Inputs just work even if the input routes originate from the same jack port. - // Solution: Rather than having to iterate all other channels, and all other Audio Input tracks and check - // their channel port buffers (if that's even possible) in order to determine if the buffer is shared, + // Solution: Rather than having to iterate all other channels, and all other Audio Input tracks and check + // their channel port buffers (if that's even possible) in order to determine if the buffer is shared, // let's just copy always, for now shall we ? float* jackbuf = MusEGlobal::audioDevice->getBuffer(jackPort, nframes); AL::dsp->cpy(buffer[ch], jackbuf, nframes); - - if (MusEGlobal::config.useDenormalBias) + + if (MusEGlobal::config.useDenormalBias) { for (unsigned int i=0; i < nframes; i++) buffer[ch][i] += MusEGlobal::denormalBias; } - } - else + } + else { - if (MusEGlobal::config.useDenormalBias) + if (MusEGlobal::config.useDenormalBias) { for (unsigned int i=0; i < nframes; i++) buffer[ch][i] = MusEGlobal::denormalBias; - } - else + } + else { memset(buffer[ch], 0, nframes * sizeof(float)); } @@ -1024,8 +1486,9 @@ void Track::resetPeaks() { - for (int i = 0; i < _channels; ++i) + for (int i = 0; i < _channels; ++i) { _peak[i] = 0.0; + } _lastActivity = 0; } @@ -1048,6 +1511,8 @@ void AudioTrack::setRecordFlag2(bool f) { + if(!canRecord()) + return; if (f == _recordFlag) return; _recordFlag = f; @@ -1055,6 +1520,26 @@ resetMeter(); } +bool AudioTrack::setRecordFlag2AndCheckMonitor(bool f) +{ + if (f != _recordFlag && canRecord()) + { + _recordFlag = f; + if (!_recordFlag) + resetMeter(); + } + + if(MusEGlobal::config.monitorOnRecord && canRecordMonitor()) + { + if(f != _recMonitor) + { + _recMonitor = f; + return true; + } + } + return false; +} + //--------------------------------------------------------- // setMute //--------------------------------------------------------- @@ -1107,69 +1592,68 @@ { unsigned pos = 0; float* buffer[_channels]; - while(fifo.getCount()) { - if (fifo.get(_channels, MusEGlobal::segmentSize, buffer, &pos)) { - printf("AudioTrack::record(): empty fifo\n"); + fprintf(stderr, "AudioTrack::record(): empty fifo\n"); return; } - if (_recFile) { - // Line removed by Tim. Oct 28, 2009 - //_recFile->seek(pos, 0); - // - // Fix for recorded waves being shifted ahead by an amount - // equal to start record position. - // - // From libsndfile ChangeLog: - // 2008-05-11 Erik de Castro Lopo - // * src/sndfile.c - // Allow seeking past end of file during write. - // - // I don't know why this line would even be called, because the FIFOs' - // 'pos' members operate in absolute frames, which at this point - // would be shifted ahead by the start of the wave part. - // So if you begin recording a new wave part at bar 4, for example, then - // this line is seeking the record file to frame 288000 even before any audio is written! - // Therefore, just let the write do its thing and progress naturally, - // it should work OK since everything was OK before the libsndfile change... - // - // Tested: With the line, audio record looping sort of works, albiet with the start offset added to - // the wave file. And it overwrites existing audio. (Check transport window 'overwrite' function. Tie in somehow...) - // With the line, looping does NOT work with libsndfile from around early 2007 (my distro's version until now). - // Therefore it seems sometime between libsndfile ~2007 and today, libsndfile must have allowed - // "seek (behind) on write", as well as the "seek past end" change of 2008... - // - // Ok, so removing that line breaks *possible* record audio 'looping' functionality, revealed with - // later libsndfile. - // Try this... And while we're at it, honour the punchin/punchout, and loop functions ! - // - // If punchin is on, or we have looped at least once, use left marker as offset. - // Note that audio::startRecordPos is reset to (roughly) the left marker pos upon loop ! - // (Not any more! I changed Audio::Process) - // Since it is possible to start loop recording before the left marker (with punchin off), we must - // use startRecordPos or loopFrame or left marker, depending on punchin and whether we have looped yet. - unsigned fr; - if(MusEGlobal::song->punchin() && (MusEGlobal::audio->loopCount() == 0)) - fr = MusEGlobal::song->lPos().frame(); - else - if((MusEGlobal::audio->loopCount() > 0) && (MusEGlobal::audio->getStartRecordPos().frame() > MusEGlobal::audio->loopFrame())) - fr = MusEGlobal::audio->loopFrame(); - else - fr = MusEGlobal::audio->getStartRecordPos().frame(); - // Now seek and write. If we are looping and punchout is on, don't let punchout point interfere with looping point. - if( (pos >= fr) && (!MusEGlobal::song->punchout() || (!MusEGlobal::song->loop() && pos < MusEGlobal::song->rPos().frame())) ) - { - pos -= fr; - - _recFile->seek(pos, 0); - _recFile->write(_channels, buffer, MusEGlobal::segmentSize); - } - - } - else { - printf("AudioNode::record(): no recFile\n"); - } + if (_recFile) { + // Line removed by Tim. Oct 28, 2009 + //_recFile->seek(pos, 0); + // + // Fix for recorded waves being shifted ahead by an amount + // equal to start record position. + // + // From libsndfile ChangeLog: + // 2008-05-11 Erik de Castro Lopo + // * src/sndfile.c + // Allow seeking past end of file during write. + // + // I don't know why this line would even be called, because the FIFOs' + // 'pos' members operate in absolute frames, which at this point + // would be shifted ahead by the start of the wave part. + // So if you begin recording a new wave part at bar 4, for example, then + // this line is seeking the record file to frame 288000 even before any audio is written! + // Therefore, just let the write do its thing and progress naturally, + // it should work OK since everything was OK before the libsndfile change... + // + // Tested: With the line, audio record looping sort of works, albeit with the start offset added to + // the wave file. And it overwrites existing audio. (Check transport window 'overwrite' function. Tie in somehow...) + // With the line, looping does NOT work with libsndfile from around early 2007 (my distro's version until now). + // Therefore it seems sometime between libsndfile ~2007 and today, libsndfile must have allowed + // "seek (behind) on write", as well as the "seek past end" change of 2008... + // + // Ok, so removing that line breaks *possible* record audio 'looping' functionality, revealed with + // later libsndfile. + // Try this... And while we're at it, honour the punchin/punchout, and loop functions ! + // + // If punchin is on, or we have looped at least once, use left marker as offset. + // Note that audio::startRecordPos is reset to (roughly) the left marker pos upon loop ! + // (Not any more! I changed Audio::Process) + // Since it is possible to start loop recording before the left marker (with punchin off), we must + // use startRecordPos or loopFrame or left marker, depending on punchin and whether we have looped yet. + unsigned fr; + if(MusEGlobal::song->punchin() && (MusEGlobal::audio->loopCount() == 0)) + fr = MusEGlobal::audio->getStartRecordPos().frame() > MusEGlobal::song->lPos().frame() ? + MusEGlobal::audio->getStartRecordPos().frame() : MusEGlobal::song->lPos().frame(); + else + if((MusEGlobal::audio->loopCount() > 0) && (MusEGlobal::audio->getStartRecordPos().frame() > MusEGlobal::audio->loopFrame())) + fr = MusEGlobal::audio->loopFrame(); + else + fr = MusEGlobal::audio->getStartRecordPos().frame(); + // Now seek and write. If we are looping and punchout is on, don't let punchout point interfere with looping point. + if( (pos >= fr) && (!MusEGlobal::song->punchout() || (!MusEGlobal::song->loop() && pos < MusEGlobal::song->rPos().frame())) ) + { + pos -= fr; + // FIXME If we are to support writing compressed file types, we probably shouldn't be seeking here. REMOVE Tim. Wave. + _recFile->seek(pos, 0); + _recFile->write(_channels, buffer, MusEGlobal::segmentSize); + } + + } + else { + fprintf(stderr, "AudioNode::record(): no recFile\n"); + } } } @@ -1190,7 +1674,7 @@ } } else - printf("PANIC: processInit: no buffer from audio driver\n"); + fprintf(stderr, "PANIC: processInit: no buffer from audio driver\n"); } } @@ -1203,13 +1687,13 @@ void AudioOutput::process(unsigned pos, unsigned offset, unsigned n) { #ifdef NODE_DEBUG_PROCESS - printf("MusE: AudioOutput::process name:%s processed:%d\n", name().toLatin1().constData(), processed()); + fprintf(stderr, "MusE: AudioOutput::process name:%s processed:%d\n", name().toLatin1().constData(), processed()); #endif - + for (int i = 0; i < _channels; ++i) { buffer1[i] = buffer[i] + offset; } - copyData(pos, _channels, -1, -1, n, buffer1); + copyData(pos, -1, _channels, _channels, -1, -1, n, buffer1); } //--------------------------------------------------------- @@ -1251,13 +1735,16 @@ } } if (sendMetronome() && MusEGlobal::audioClickFlag && MusEGlobal::song->click()) { - + #ifdef METRONOME_DEBUG - printf("MusE: AudioOutput::processWrite Calling metronome->addData frame:%u channels:%d frames:%lu\n", MusEGlobal::audio->pos().frame(), _channels, _nframes); + fprintf(stderr, "MusE: AudioOutput::processWrite Calling metronome->addData frame:%u channels:%d frames:%lu\n", MusEGlobal::audio->pos().frame(), _channels, _nframes); #endif - metronome->addData(MusEGlobal::audio->pos().frame(), _channels, -1, -1, _nframes, buffer); + metronome->copyData(MusEGlobal::audio->pos().frame(), -1, _channels, _channels, -1, -1, _nframes, buffer, true); } + + MusEGlobal::wavePreview->addData(_channels, _nframes, buffer); } + //--------------------------------------------------------- // setName //--------------------------------------------------------- @@ -1297,14 +1784,25 @@ { if(buffer[i]->buffer) free(buffer[i]->buffer); - + delete buffer[i]; } - + delete[] buffer; muse_atomic_destroy(&count); } +void Fifo::clear() +{ + #ifdef FIFO_DEBUG + fprintf(stderr, "FIFO::clear count:%d\n", muse_atomic_read(&count)); + #endif + + ridx = 0; + widx = 0; + muse_atomic_set(&count, 0); +} + //--------------------------------------------------------- // put // return true if fifo full @@ -1313,11 +1811,11 @@ bool Fifo::put(int segs, unsigned long samples, float** src, unsigned pos) { #ifdef FIFO_DEBUG - printf("FIFO::put segs:%d samples:%lu pos:%u\n", segs, samples, pos); + fprintf(stderr, "FIFO::put segs:%d samples:%lu pos:%u count:%d\n", segs, samples, pos, muse_atomic_read(&count)); #endif - + if (muse_atomic_read(&count) == nbuffer) { - printf("FIFO %p overrun... %d\n", this, count.counter); + fprintf(stderr, "FIFO %p overrun... %d\n", this, muse_atomic_read(&count)); return true; } FifoBuffer* b = buffer[widx]; @@ -1327,22 +1825,22 @@ { free(b->buffer); b->buffer = 0; - } + } int rv = posix_memalign((void**)&(b->buffer), 16, sizeof(float) * n); if(rv != 0 || !b->buffer) { - printf("Fifo::put could not allocate buffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); + fprintf(stderr, "Fifo::put could not allocate buffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); return true; } - + b->maxSize = n; } if(!b->buffer) { - printf("Fifo::put no buffer! segs:%d samples:%lu pos:%u\n", segs, samples, pos); + fprintf(stderr, "Fifo::put no buffer! segs:%d samples:%lu pos:%u\n", segs, samples, pos); return true; } - + b->size = samples; b->segs = segs; b->pos = pos; @@ -1360,23 +1858,23 @@ bool Fifo::get(int segs, unsigned long samples, float** dst, unsigned* pos) { #ifdef FIFO_DEBUG - printf("FIFO::get segs:%d samples:%lu\n", segs, samples); + fprintf(stderr, "FIFO::get segs:%d samples:%lu count:%d\n", segs, samples, muse_atomic_read(&count)); #endif - + if (muse_atomic_read(&count) == 0) { - printf("FIFO %p underrun... %d\n", this,count.counter); //by willyfoobar: added count to output //see Fifo::put() + fprintf(stderr, "FIFO %p underrun\n", this); return true; } FifoBuffer* b = buffer[ridx]; if(!b->buffer) { - printf("Fifo::get no buffer! segs:%d samples:%lu b->pos:%u\n", segs, samples, b->pos); + fprintf(stderr, "Fifo::get no buffer! segs:%d samples:%lu b->pos:%u\n", segs, samples, b->pos); return true; } - + if (pos) *pos = b->pos; - + for (int i = 0; i < segs; ++i) dst[i] = b->buffer + samples * (i % b->segs); remove(); @@ -1387,12 +1885,24 @@ { return muse_atomic_read(&count); } + +bool Fifo::isEmpty() + { + return muse_atomic_read(&count) == 0; + } + +//--------------------------------------------------------- + //--------------------------------------------------------- // remove //--------------------------------------------------------- void Fifo::remove() { + #ifdef FIFO_DEBUG + fprintf(stderr, "Fifo::remove count:%d\n", muse_atomic_read(&count)); + #endif + ridx = (ridx + 1) % nbuffer; muse_atomic_dec(&count); } @@ -1404,9 +1914,9 @@ bool Fifo::getWriteBuffer(int segs, unsigned long samples, float** buf, unsigned pos) { #ifdef FIFO_DEBUG - printf("Fifo::getWriteBuffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); + fprintf(stderr, "Fifo::getWriteBuffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); #endif - + if (muse_atomic_read(&count) == nbuffer) return true; FifoBuffer* b = buffer[widx]; @@ -1417,25 +1927,25 @@ free(b->buffer); b->buffer = 0; } - + int rv = posix_memalign((void**)&(b->buffer), 16, sizeof(float) * n); if(rv != 0 || !b->buffer) { - printf("Fifo::getWriteBuffer could not allocate buffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); + fprintf(stderr, "Fifo::getWriteBuffer could not allocate buffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); return true; } - + b->maxSize = n; } if(!b->buffer) { - printf("Fifo::getWriteBuffer no buffer! segs:%d samples:%lu pos:%u\n", segs, samples, pos); + fprintf(stderr, "Fifo::getWriteBuffer no buffer! segs:%d samples:%lu pos:%u\n", segs, samples, pos); return true; } - + for (int i = 0; i < segs; ++i) buf[i] = b->buffer + i * samples; - + b->size = samples; b->segs = segs; b->pos = pos; @@ -1448,10 +1958,31 @@ void Fifo::add() { + #ifdef FIFO_DEBUG + fprintf(stderr, "Fifo::add count:%d\n", muse_atomic_read(&count)); + #endif + widx = (widx + 1) % nbuffer; muse_atomic_inc(&count); } +//--------------------------------------------------------- +// setParam +//--------------------------------------------------------- + +void AudioTrack::setParam(unsigned long i, double val) +{ + addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame()); +} + +//--------------------------------------------------------- +// param +//--------------------------------------------------------- + +double AudioTrack::param(unsigned long i) const +{ + return _controls[i].dval; +} //--------------------------------------------------------- // setChannels @@ -1471,38 +2002,48 @@ void AudioTrack::setTotalOutChannels(int num) { int chans = _totalOutChannels; - if(num != chans) + if(num != chans) { - // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. - if(chans < MAX_CHANNELS) - chans = MAX_CHANNELS; - if(outBuffers) - { - for(int i = 0; i < chans; ++i) - { - if(outBuffers[i]) - free(outBuffers[i]); - } - delete[] outBuffers; - } - + if(_dataBuffers) + { + for(int i = 0; i < _totalOutChannels; ++i) + { + if(_dataBuffers[i]) + { + free(_dataBuffers[i]); + _dataBuffers[i] = NULL; + } + } + delete[] _dataBuffers; + _dataBuffers = NULL; + } + _totalOutChannels = num; - chans = num; - // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + int new_chans = num; + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + if(new_chans < MAX_CHANNELS) + new_chans = MAX_CHANNELS; if(chans < MAX_CHANNELS) chans = MAX_CHANNELS; - - outBuffers = new float*[chans]; - for (int i = 0; i < chans; ++i) + if(new_chans != chans) { - int rv = posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); - if(rv != 0) + if(outBuffers) { - fprintf(stderr, "ERROR: AudioTrack::setTotalOutChannels: posix_memalign returned error:%d. Aborting!\n", rv); - abort(); + for(int i = 0; i < chans; ++i) + { + if(outBuffers[i]) + { + free(outBuffers[i]); + outBuffers[i] = NULL; + } + } + delete[] outBuffers; + outBuffers = NULL; } } - } + + initBuffers(); + } chans = num; // Limit the actual track (meters, copying etc, all 'normal' operation) to two-channel stereo. if(chans > MAX_CHANNELS) @@ -1518,7 +2059,7 @@ { if(num == _totalInChannels) return; - + _totalInChannels = num; } diff -Nru muse-2.1.2/muse/node.h muse-3.0.2+ds1/muse/node.h --- muse-2.1.2/muse/node.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/node.h 2017-12-04 21:01:18.000000000 +0000 @@ -24,82 +24,10 @@ #ifndef __AUDIONODE_H__ #define __AUDIONODE_H__ -#include - -#ifndef i386 -#include -typedef struct { pthread_mutex_t lock; int counter; } muse_atomic_t; -#else -typedef struct { int counter; } muse_atomic_t; -#endif +#include "muse_atomic.h" namespace MusECore { - -static inline int muse_atomic_read(muse_atomic_t *v) { -#ifndef i386 - int ret; - pthread_mutex_lock(&v->lock); - ret = v->counter; - pthread_mutex_unlock(&v->lock); - return ret; -#else - return v->counter; -#endif -} - -static inline void muse_atomic_set(muse_atomic_t *v, int i) { -#ifndef i386 - pthread_mutex_lock(&v->lock); - v->counter = i; - pthread_mutex_unlock(&v->lock); -#else - v->counter = i; -#endif -} -static inline void muse_atomic_inc(muse_atomic_t *v) { -#ifndef i386 - pthread_mutex_lock(&v->lock); - v->counter++; - pthread_mutex_unlock(&v->lock); -#else - __asm__ __volatile__( - "lock ; " "incl %0" - :"=m" (v->counter) - :"m" (v->counter)); -#endif -} -static inline void muse_atomic_dec(muse_atomic_t *v) { -#ifndef i386 - pthread_mutex_lock(&v->lock); - v->counter--; - pthread_mutex_unlock(&v->lock); -#else - __asm__ __volatile__( - "lock ; " "decl %0" - :"=m" (v->counter) - :"m" (v->counter)); -#endif -} -#ifndef i386 -static inline void muse_atomic_init(muse_atomic_t *v) { - pthread_mutex_init(&v->lock, NULL); - } -#else -static inline void muse_atomic_init(muse_atomic_t*) {} -#endif - -#ifndef i386 -static inline void muse_atomic_destroy(muse_atomic_t *v) { - pthread_mutex_destroy(&v->lock); - } -#else -static inline void muse_atomic_destroy(muse_atomic_t*) {} -#endif - -class Xml; -class Pipeline; - - + //--------------------------------------------------------- // Fifo //--------------------------------------------------------- @@ -128,17 +56,14 @@ public: Fifo(); ~Fifo(); - void clear() { - ridx = 0; - widx = 0; - muse_atomic_set(&count, 0); - } + void clear(); bool put(int, unsigned long, float** buffer, unsigned pos); bool getWriteBuffer(int, unsigned long, float** buffer, unsigned pos); void add(); bool get(int, unsigned long, float** buffer, unsigned* pos); void remove(); int getCount(); + bool isEmpty(); }; } // namespace MusECore diff -Nru muse-2.1.2/muse/operations.cpp muse-3.0.2+ds1/muse/operations.cpp --- muse-2.1.2/muse/operations.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/operations.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -0,0 +1,2366 @@ +//========================================================= +// MusE +// Linux Music Editor +// operations.cpp +// (C) Copyright 2014, 2016 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 +// 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 "operations.h" +#include "song.h" + +// Enable for debugging: +//#define _PENDING_OPS_DEBUG_ + +namespace MusECore { + +//----------------------------------- +// PendingOperationItem +//----------------------------------- + +bool PendingOperationItem::isAllocationOp(const PendingOperationItem& op) const +{ + switch(op._type) + { + case AddMidiCtrlValList: + // A is channel B is control. + if(_type == AddMidiCtrlValList && _mcvll == op._mcvll && _intA == op._intA && _intB == op._intB) + return true; + break; + + case AddTempo: + // A is tick. + if(_type == AddTempo && _tempo_list == op._tempo_list && _intA == op._intA) + return true; + break; + + case AddSig: + // A is tick. + if(_type == AddSig && _sig_list == op._sig_list && _intA == op._intA) + return true; + break; + + // In the case of type AddMidiDevice, this searches for the name only. + case AddMidiDevice: + if(_type == AddMidiDevice && _midi_device_list == op._midi_device_list && + _midi_device->name() == op._midi_device->name()) + return true; + break; + + default: + break; + } + + return false; +} + +int PendingOperationItem::getIndex() const +{ + switch(_type) + { + case Uninitialized: + case AddAuxSendValue: + case AddMidiInstrument: + case DeleteMidiInstrument: + case ReplaceMidiInstrument: + case AddMidiDevice: + case DeleteMidiDevice: + case ModifyMidiDeviceAddress: + case ModifyMidiDeviceFlags: + case ModifyMidiDeviceName: + case AddTrack: + case DeleteTrack: + case MoveTrack: + case ModifyTrackName: + case ModifyTrackDrumMapItem: + case ReplaceTrackDrumMapPatchList: + case RemapDrumControllers: + case UpdateDrumMaps: + case SetTrackRecord: + case SetTrackMute: + case SetTrackSolo: + case SetTrackRecMonitor: + case SetTrackOff: + case ModifyPartName: + case ModifySongLength: + case AddMidiCtrlValList: + case ModifyAudioCtrlValList: + case SetGlobalTempo: + case AddRoute: + case DeleteRoute: + case AddRouteNode: + case DeleteRouteNode: + case ModifyRouteNode: + case UpdateSoloStates: + case EnableAllAudioControllers: + case ModifyAudioSamples: + // To help speed up searches of these ops, let's (arbitrarily) set index = type instead of all of them being at index 0! + return _type; + + case ModifyPartLength: + return _part->posValue(); + + case MovePart: + // _part is used here rather than _iPart since _iPart can be end(). + return _part->posValue(); + + case AddPart: + return _part->posValue(); + + case DeletePart: + return _iPart->second->posValue(); + + + case AddEvent: + return _ev.posValue(); + + case DeleteEvent: + return _ev.posValue(); + + + case AddMidiCtrlVal: + return _intA; // Tick + + case DeleteMidiCtrlVal: + return _imcv->first; // Tick + + case ModifyMidiCtrlVal: + return _imcv->first; // Tick + + + case AddAudioCtrlVal: + return _frame; // Frame + + case DeleteAudioCtrlVal: + return _iCtrl->first; // Frame + + case ModifyAudioCtrlVal: + return _iCtrl->first; // Frame + + + case AddTempo: + return _intA; // Tick + + case DeleteTempo: + return _iTEvent->first; // Tick + + case ModifyTempo: + // We want the 'real' tick, not _iTEvent->first which is the index of the next iterator! + return _iTEvent->second->tick; // Tick + + + case AddSig: + return _intA; // Tick + + case DeleteSig: + return _iSigEvent->first; // Tick + + case ModifySig: + // We want the 'real' tick, not _iSigEvent->first which is the index of the next iterator! + return _iSigEvent->second->tick; // Tick + + + case AddKey: + return _intA; // Tick + + case DeleteKey: + return _iKeyEvent->first; // Tick + + case ModifyKey: + // We want the 'real' tick, not _iKeyEvent->first which is the index of the next iterator! + return _iKeyEvent->second.tick; // Tick + + + default: + fprintf(stderr, "PendingOperationItem::getIndex unknown op type: %d\n", _type); + return 0; + break; + } +} + +SongChangedFlags_t PendingOperationItem::executeRTStage() +{ + SongChangedFlags_t flags = 0; + switch(_type) + { + case ModifyTrackDrumMapItem: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyTrackDrumMapItem drummap operation:%p\n", + _drum_map_track_operation); +#endif + + MidiTrack* mt; + MidiTrackList& mtl = _drum_map_track_operation->_tracks; + for(iMidiTrack imt = mtl.begin(); imt != mtl.end(); ++imt) + { + mt = *imt; + // FIXME Possible non realtime-friendly allocation. + mt->modifyWorkingDrumMap(_drum_map_track_operation->_workingItemList, + _drum_map_track_operation->_isReset, + _drum_map_track_operation->_includeDefault, + _drum_map_track_operation->_isInstrumentMod, + _drum_map_track_operation->_doWholeMap); + flags |= (SC_DRUMMAP); + } + + // If this is an instrument modification we must now do a + // general update of all drum track drum maps. + // Ideally we would like to update only the required ones, + // but it is too difficult to tell which maps need updating + // from inside the above loop (or inside modifyWorkingDrumMap). + if(_drum_map_track_operation->_isInstrumentMod) + { + MidiTrackList* mtlp = MusEGlobal::song->midis(); + for(iMidiTrack imt = mtlp->begin(); imt != mtlp->end(); ++imt) + { + mt = *imt; + if(mt->type() != Track::NEW_DRUM) + continue; + if(mt->updateDrummap(false)) + flags |= (SC_DRUMMAP); + } + } + flags |= (SC_DRUMMAP); + } + break; + + case ReplaceTrackDrumMapPatchList: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ReplaceTrackDrumMapPatchList drummap operation:%p\n", + _drum_map_track_patch_replace_operation); +#endif + + MidiTrack* mt = _drum_map_track_patch_replace_operation->_track; + WorkingDrumMapPatchList* orig_wdmpl = mt->workingDrumMap(); + // Simply switch pointers. Be sure to delete the original pointers later in the non-realtime stage. + // After the list pointers have been switched, swap with the replacement so that it can be deleted later. + mt->setWorkingDrumMap(_drum_map_track_patch_replace_operation->_workingItemPatchList, + _drum_map_track_patch_replace_operation->_isInstrumentMod); + _drum_map_track_patch_replace_operation->_workingItemPatchList = orig_wdmpl; + + // If this is an instrument modification we must now do a + // general update of all drum track drum maps. + // Ideally we would like to update only the required ones, + // but it is too difficult to tell which maps need updating + // from inside the above loop (or inside modifyWorkingDrumMap). + if(_drum_map_track_patch_replace_operation->_isInstrumentMod) + { + MidiTrackList* mtlp = MusEGlobal::song->midis(); + for(iMidiTrack imt = mtlp->begin(); imt != mtlp->end(); ++imt) + { + mt = *imt; + if(mt->type() != Track::NEW_DRUM) + continue; + if(mt->updateDrummap(false)) + flags |= (SC_DRUMMAP); + } + } + flags |= (SC_DRUMMAP); + } + break; + + case RemapDrumControllers: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage RemapDrumControllers remap operation:%p\n", + _midi_ctrl_val_remap_operation); +#endif + + for(iMidiCtrlValLists2bErased_t imcvle = _midi_ctrl_val_remap_operation->_midiCtrlValLists2bErased.begin(); + imcvle != _midi_ctrl_val_remap_operation->_midiCtrlValLists2bErased.end(); ++imcvle) + { + const int port = imcvle->first; + MidiCtrlValListIterators& mcvli = imcvle->second; + MidiPort* mp = &MusEGlobal::midiPorts[port]; + MidiCtrlValListList* mcvll = mp->controller(); + for(iMidiCtrlValListIterators_t imcvli = mcvli.begin(); imcvli != mcvli.end(); ++imcvli) + mcvll->del(*imcvli); + } + + for(iMidiCtrlValLists2bAdded_t imcvla = _midi_ctrl_val_remap_operation->_midiCtrlValLists2bAdded.begin(); + imcvla != _midi_ctrl_val_remap_operation->_midiCtrlValLists2bAdded.end(); ++imcvla) + { + const int port = imcvla->first; + MidiCtrlValListList* mcvll_a = imcvla->second; + MidiPort* mp = &MusEGlobal::midiPorts[port]; + MidiCtrlValListList* mcvll = mp->controller(); + for(iMidiCtrlValList imcvl = mcvll_a->begin(); imcvl != mcvll_a->end(); ++imcvl) + mcvll->add(imcvl->first >> 24, imcvl->second); + } + + // TODO: What to use here? We don't have anything SC_* related... yet. + //flags |= (SC_MIDI_CONTROLLER_ADD); + flags |= (SC_EVERYTHING); + } + break; + + case UpdateDrumMaps: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage UpdateDrumMaps: midi_port:%p:\n", _midi_port); +#endif + if(_midi_port->updateDrumMaps()) + flags |= SC_DRUMMAP; + break; + + case UpdateSoloStates: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage UpdateSoloStates: track_list:%p:\n", _track_list); +#endif + // TODO Use the track_list, or simply keep as dummy parameter to identify UpdateSoloStates? + MusEGlobal::song->updateSoloStates(); + flags |= SC_SOLO; + break; + + // TODO: Try to break this operation down so that only the actual operation is executed stage-2. + case AddRoute: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddRoute: src/dst routes:\n"); + _src_route.dump(); + _dst_route.dump(); +#endif + if(addRoute(_src_route, _dst_route)) + flags |= SC_ROUTE; + break; + + // TODO: Try to break this operation down so that only the actual operation is executed stage-2. + case DeleteRoute: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteRoute: src/dst routes:\n"); + _src_route.dump(); + _dst_route.dump(); +#endif + if(removeRoute(_src_route, _dst_route)) + flags |= SC_ROUTE; + break; + + case AddRouteNode: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddRouteNode: route_list:%p route:\n", _route_list); + _src_route.dump(); +#endif + _route_list->push_back(_src_route); + flags |= SC_ROUTE; + break; + + case DeleteRouteNode: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteRouteNode: route_list:%p route:\n", _route_list); + _iRoute->dump(); +#endif + _route_list->erase(_iRoute); + flags |= SC_ROUTE; + break; + + case ModifyRouteNode: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyRouteNode: src/dst routes:\n"); + _src_route.dump(); + _dst_route_pointer->dump(); +#endif + *_dst_route_pointer = _src_route; + flags |= SC_ROUTE; + break; + + case AddAuxSendValue: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddAuxSendValue aux_send_value_list:%p val:%f\n", _aux_send_value_list, _aux_send_value); +#endif + _aux_send_value_list->push_back(_aux_send_value); + break; + + + case AddMidiInstrument: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddMidiInstrument instrument_list:%p instrument:%p\n", _midi_instrument_list, _midi_instrument); +#endif + _midi_instrument_list->push_back(_midi_instrument); + flags |= SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD; + break; + + case DeleteMidiInstrument: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteMidiInstrument instrument_list:%p instrument:%p\n", _midi_instrument_list, *_iMidiInstrument); +#endif + _midi_instrument_list->erase(_iMidiInstrument); + flags |= SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD; + break; + + case ReplaceMidiInstrument: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ReplaceMidiInstrument instrument_list:%p instrument:%p new_ instrument:%p\n", + _midi_instrument_list, *_iMidiInstrument, _midi_instrument); +#endif + // Grab the existing pointer to be deleted. + MidiInstrument* orig = *_iMidiInstrument; + // Erase from the list. + _midi_instrument_list->erase(_iMidiInstrument); + // Add the new instrument. + _midi_instrument_list->push_back(_midi_instrument); + + // Change all ports which used the original instrument. + for(int port = 0; port < MIDI_PORTS; ++port) + { + MidiPort* mp = &MusEGlobal::midiPorts[port]; + if(mp->instrument() != orig) + continue; + // Set the new instrument and nothing more (ie. don't use MidiPort::changeInstrument()). + // Here we will flag the initializations, and normalize and update the drum maps. + mp->setInstrument(_midi_instrument); + // Flag the port to send initializations next time it bothers to check. + // TODO: Optimize: We only need this if the user changed the initialization + // lists or sysex list. Find a way to pass that info here. + mp->clearInitSent(); + } + + // Since this is an instrument modification we must now do a + // general update of all drum track drum maps using this instrument. + // Ideally we would like to update only the required ones, + // but it is too difficult to tell which maps need updating + // from inside the above loop (or inside modifyWorkingDrumMap). + MidiTrack* mt; + int mt_port; + MidiPort* mt_mp; + MidiTrackList* mtlp = MusEGlobal::song->midis(); + for(iMidiTrack imt = mtlp->begin(); imt != mtlp->end(); ++imt) + { + mt = *imt; + if(mt->type() != Track::NEW_DRUM) + continue; + mt_port = mt->outPort(); + if(mt_port < 0 || mt_port >= MIDI_PORTS) + continue; + mt_mp = &MusEGlobal::midiPorts[mt_port]; + // We are looking for tracks which are now using the new instrument. + if(mt_mp->instrument() != _midi_instrument) + continue; + // Ensure there are NO duplicate enote fields. + //mt->normalizeWorkingDrumMapPatchList(); + // Finally, update the track's drum map (and drum in map). + mt->updateDrummap(false); + } + + // Transfer the original pointer back to _midi_instrument so it can be deleted in the non-RT stage. + _midi_instrument = orig; + + flags |= SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD; + } + break; + + case AddMidiDevice: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddMidiDevice devicelist:%p device:%p\n", _midi_device_list, _midi_device); +#endif + _midi_device_list->push_back(_midi_device); + flags |= SC_CONFIG; + break; + + case DeleteMidiDevice: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteMidiDevice devicelist:%p device:%p\n", _midi_device_list, *_iMidiDevice); +#endif + _midi_device_list->erase(_iMidiDevice); + flags |= SC_CONFIG; + break; + + case ModifyMidiDeviceAddress: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyMidiDeviceAddress device:%p client:%d port:%d\n", _midiDevice, _address_client, _address_port); +#endif + _midi_device->setAddressClient(_address_client); + _midi_device->setAddressPort(_address_port); + _midi_device->setOpenFlags(_open_flags); + flags |= SC_CONFIG; + break; + + case ModifyMidiDeviceFlags: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyMidiDeviceFlags device:%p rwFlags:%d openFlags:%d\n", _midiDevice, _rw_flags, _open_flags); +#endif + _midi_device->setrwFlags(_rw_flags); + _midi_device->setOpenFlags(_open_flags); + flags |= SC_CONFIG; + break; + + case ModifyMidiDeviceName: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyMidiDeviceName device:%p name:%s\n", _midiDevice, _name->toLocal8Bit().data()); +#endif + _midi_device->setName(*_name); + flags |= SC_CONFIG; + break; + + case AddTrack: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddTrack track_list:%p track:%p\n", _track_list, _track); +#endif + if(_void_track_list) + { + switch(_track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + static_cast(_void_track_list)->push_back(static_cast(_track)); + break; + case Track::WAVE: + static_cast(_void_track_list)->push_back(static_cast(_track)); + break; + case Track::AUDIO_OUTPUT: + static_cast(_void_track_list)->push_back(static_cast(_track)); + break; + case Track::AUDIO_GROUP: + static_cast(_void_track_list)->push_back(static_cast(_track)); + break; + case Track::AUDIO_AUX: + static_cast(_void_track_list)->push_back(static_cast(_track)); + // Special for aux, make it easier to detect their changes. + flags |= SC_AUX; + break; + case Track::AUDIO_INPUT: + static_cast(_void_track_list)->push_back(static_cast(_track)); + break; + case Track::AUDIO_SOFTSYNTH: + static_cast(_void_track_list)->push_back(static_cast(_track)); + break; + default: + fprintf(stderr, "PendingOperationItem::executeRTStage AddTrack: Unknown track type %d\n", _track->type()); + return flags; + } + } + + iTrack track_it = _track_list->index2iterator(_insert_at); + _track_list->insert(track_it, _track); + flags |= SC_TRACK_INSERTED; + + // Add routes: + if(_track->isMidiTrack()) + { + // Add any port output routes to this track + const RouteList* rl = _track->inRoutes(); + for(ciRoute r = rl->begin(); r != rl->end(); ++r) + { + switch(r->type) + { + case Route::MIDI_PORT_ROUTE: { + Route src(_track, r->channel); + MusEGlobal::midiPorts[r->midiPort].outRoutes()->push_back(src); + flags |= SC_ROUTE; } + break; + case Route::TRACK_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } + } + // Add any port input routes from this track + rl = _track->outRoutes(); + for(ciRoute r = rl->begin(); r != rl->end(); ++r) + { + switch(r->type) + { + case Route::MIDI_PORT_ROUTE: { + Route src(_track, r->channel); + MusEGlobal::midiPorts[r->midiPort].inRoutes()->push_back(src); + flags |= SC_ROUTE; } + break; + case Route::TRACK_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } + } + } + else + { + // Add other tracks' output routes to this track + const RouteList* rl = _track->inRoutes(); + for(ciRoute r = rl->begin(); r != rl->end(); ++r) + { + switch(r->type) + { + case Route::TRACK_ROUTE: { + Route src(_track, r->remoteChannel, r->channels); + src.remoteChannel = r->channel; + r->track->outRoutes()->push_back(src); + flags |= SC_ROUTE; + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update this track's aux ref count. + if(r->track->auxRefCount()) + { + _track->updateAuxRoute(r->track->auxRefCount(), NULL); + } + else if(r->track->type() == Track::AUDIO_AUX) + { + _track->updateAuxRoute(1, NULL); + } + } + break; + case Route::MIDI_PORT_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } + } + // Add other tracks' input routes from this track + rl = _track->outRoutes(); + for(ciRoute r = rl->begin(); r != rl->end(); ++r) + { + switch(r->type) + { + case Route::TRACK_ROUTE: { + Route src(_track, r->remoteChannel, r->channels); + src.remoteChannel = r->channel; + r->track->inRoutes()->push_back(src); + flags |= SC_ROUTE; + // Is this track an Aux Track or else does it have Aux Tracks routed to it? + // Update the other track's aux ref count and all tracks it is connected to. + if(_track->auxRefCount()) + { + r->track->updateAuxRoute(_track->auxRefCount(), NULL); + } + else if(_track->type() == Track::AUDIO_AUX) + { + r->track->updateAuxRoute(1, NULL); + } + } + break; + case Route::MIDI_PORT_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } + } + } + chainTrackParts(_track); + + // Be sure to mark the parts as not deleted if they exist in the global copy/paste clone list. + const PartList* pl = _track->cparts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) + { + if(i->cp == ip->second) + i->is_deleted = false; + } + } + } + break; + + case DeleteTrack: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteTrack track_list:%p track:%p sec_track_list:%p\n", _track_list, _track, _void_track_list); +#endif + unchainTrackParts(_track); + if(_void_track_list) + { + switch(_track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + static_cast(_void_track_list)->erase(_track); + break; + case Track::WAVE: + static_cast(_void_track_list)->erase(_track); + break; + case Track::AUDIO_OUTPUT: + static_cast(_void_track_list)->erase(_track); + break; + case Track::AUDIO_GROUP: + static_cast(_void_track_list)->erase(_track); + break; + case Track::AUDIO_AUX: + static_cast(_void_track_list)->erase(_track); + // Special for aux, make it easier to detect their changes. + flags |= SC_AUX; + break; + case Track::AUDIO_INPUT: + static_cast(_void_track_list)->erase(_track); + break; + case Track::AUDIO_SOFTSYNTH: + static_cast(_void_track_list)->erase(_track); + break; + default: + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteTrack: Unknown track type %d\n", _track->type()); + return flags; + } + } + _track_list->erase(_track); + flags |= SC_TRACK_REMOVED; + + // Remove routes: + if(_track->type() == Track::AUDIO_OUTPUT) + { + // Clear the track's jack ports + for(int ch = 0; ch < _track->channels(); ++ch) + { + ((AudioOutput*)_track)->setJackPort(ch, 0); + flags |= SC_ROUTE; + } + + // Clear the track's output routes' jack ports + RouteList* orl = _track->outRoutes(); + for(iRoute r = orl->begin(); r != orl->end(); ++r) + { + if(r->type != Route::JACK_ROUTE) + continue; + r->jackPort = 0; + flags |= SC_ROUTE; + } + } + else if(_track->type() == Track::AUDIO_INPUT) + { + // Clear the track's jack ports + for(int ch = 0; ch < _track->channels(); ++ch) + { + ((AudioInput*)_track)->setJackPort(ch, 0); + flags |= SC_ROUTE; + } + + // Clear the track's input routes' jack ports + RouteList* irl = _track->inRoutes(); + for(iRoute r = irl->begin(); r != irl->end(); ++r) + { + if(r->type != Route::JACK_ROUTE) + continue; + r->jackPort = 0; + flags |= SC_ROUTE; + } + } + + if(_track->isMidiTrack()) + { + // Remove any port output routes to this track + const RouteList* rl = _track->inRoutes(); + for(ciRoute r = rl->begin(); r != rl->end(); ++r) + { + switch(r->type) + { + case Route::MIDI_PORT_ROUTE: { + Route src(_track, r->channel); + MusEGlobal::midiPorts[r->midiPort].outRoutes()->removeRoute(src); + flags |= SC_ROUTE; } + break; + case Route::TRACK_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } + } + // Remove any port input routes from this track + rl = _track->outRoutes(); + for(ciRoute r = rl->begin(); r != rl->end(); ++r) + { + switch(r->type) + { + case Route::MIDI_PORT_ROUTE: { + Route src(_track, r->channel); + MusEGlobal::midiPorts[r->midiPort].inRoutes()->removeRoute(src); + flags |= SC_ROUTE; } + break; + case Route::TRACK_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } + } + } + else + { + // Remove other tracks' output routes to this track + const RouteList* rl = _track->inRoutes(); + for(ciRoute r = rl->begin(); r != rl->end(); ++r) + { + switch(r->type) + { + case Route::TRACK_ROUTE: { + Route src(_track, r->remoteChannel, r->channels); + src.remoteChannel = r->channel; + r->track->outRoutes()->removeRoute(src); + flags |= SC_ROUTE; + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update this track's aux ref count. + if(r->track->auxRefCount()) + { + _track->updateAuxRoute(-r->track->auxRefCount(), NULL); + } + else if(r->track->type() == Track::AUDIO_AUX) + { + _track->updateAuxRoute(-1, NULL); + } + } + break; + case Route::MIDI_PORT_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } + } + // Remove other tracks' input routes from this track + rl = _track->outRoutes(); + for(ciRoute r = rl->begin(); r != rl->end(); ++r) + { + switch(r->type) + { + case Route::TRACK_ROUTE: { + Route src(_track, r->remoteChannel, r->channels); + src.remoteChannel = r->channel; + r->track->inRoutes()->removeRoute(src); + flags |= SC_ROUTE; + // Is this track an Aux Track or else does it have Aux Tracks routed to it? + // Update the other track's aux ref count and all tracks it is connected to. + if(_track->auxRefCount()) + { + r->track->updateAuxRoute(-_track->auxRefCount(), NULL); + } + else if(_track->type() == Track::AUDIO_AUX) + { + r->track->updateAuxRoute(-1, NULL); + } + } + break; + case Route::MIDI_PORT_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } + } + } + + // Be sure to mark the parts as deleted if they exist in the global copy/paste clone list. + const PartList* pl = _track->cparts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) + { + if(i->cp == ip->second) + i->is_deleted = true; + } + } + } + break; + + case MoveTrack: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage MoveTrack from:%d to:%d\n", _from_idx, _to_idx); +#endif + int sz = _track_list->size(); + if(_from_idx >= sz) + { + fprintf(stderr, "MusE error: PendingOperationItem::executeRTStage MoveTrack from index out of range:%d\n", _from_idx); + return flags; + } + iTrack fromIt = _track_list->begin() + _from_idx; + Track* track = *fromIt; + _track_list->erase(fromIt); + iTrack toIt = (_to_idx >= sz) ? _track_list->end() : _track_list->begin() + _to_idx; + _track_list->insert(toIt, track); + flags |= SC_TRACK_MOVED; + } + break; + + case ModifyTrackName: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyTrackName track:%p new_val:%s\n", _track, _name->toLocal8Bit().data()); +#endif + _track->setName(*_name); + flags |= (SC_TRACK_MODIFIED | SC_MIDI_TRACK_PROP); + // If it's an aux track, notify aux UI controls to reload, or change their names etc. + if(_track->type() == Track::AUDIO_AUX) + flags |= SC_AUX; + break; + + case SetTrackRecord: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage SetTrackRecord track:%p new_val:%d\n", _track, _boolA); +#endif + const bool mon = _track->setRecordFlag2AndCheckMonitor(_boolA); + flags |= SC_RECFLAG; + if(mon) + flags |= SC_TRACK_REC_MONITOR; + } + break; + + case SetTrackMute: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage SetTrackMute track:%p new_val:%d\n", _track, _boolA); +#endif + _track->setMute(_boolA); + flags |= SC_MUTE; + break; + + case SetTrackSolo: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage SetTrackSolo track:%p new_val:%d\n", _track, _boolA); +#endif + _track->setSolo(_boolA); + flags |= SC_SOLO; + break; + + case SetTrackRecMonitor: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage SetTrackRecMonitor track:%p new_val:%d\n", _track, _boolA); +#endif + _track->setRecMonitor(_boolA); + flags |= SC_TRACK_REC_MONITOR; + break; + + case SetTrackOff: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage SetTrackOff track:%p new_val:%d\n", _track, _boolA); +#endif + _track->setOff(_boolA); + flags |= SC_MUTE; + break; + + + case AddPart: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddPart part:%p\n", _part); +#endif + _part_list->add(_part); + _part->rechainClone(); + // Be sure to mark the part as not deleted if it exists in the global copy/paste clone list. + for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) + { + if(i->cp == _part) + i->is_deleted = false; + } + flags |= SC_PART_INSERTED; + break; + + case DeletePart: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeletePart part:%p\n", _iPart->second); +#endif + Part* p = _iPart->second; + _part_list->erase(_iPart); + p->unchainClone(); + // Be sure to mark the part as deleted if it exists in the global copy/paste clone list. + for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) + { + if(i->cp == p) + i->is_deleted = true; + } + flags |= SC_PART_REMOVED; + } + break; + + case ModifyPartLength: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyPartLength part:%p old_val:%d new_val:%d\n", _part, _part->lenValue(), _intA); +#endif + //_part->type() == Pos::FRAMES ? _part->setLenFrame(_intA) : _part->setLenTick(_intA); + _part->setLenValue(_intA); + flags |= SC_PART_MODIFIED; + break; + + case MovePart: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage MovePart part:%p track:%p new_pos:%d\n", _part, _track, _intA); +#endif + if(_track) + { + if(_part->track() && _iPart != _part->track()->parts()->end()) + { + _part->track()->parts()->erase(_iPart); + flags |= SC_PART_REMOVED; + } + _part->setTrack(_track); + //_part->setTick(_intA); + _part->setPosValue(_intA); + _track->parts()->add(_part); + flags |= SC_PART_INSERTED; + } + else + { + //_part->setTick(_intA); + _part->setPosValue(_intA); + } + flags |= SC_PART_MODIFIED; + break; + + case ModifyPartName: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyPartName part:%p new_val:%s\n", _part, _name->toLocal8Bit().data()); +#endif + _part->setName(*_name); + flags |= SC_PART_MODIFIED; + break; + + + case AddEvent: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddEvent pre: "); + _ev.dump(); +#endif + _part->addEvent(_ev); +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddEvent post: "); + _ev.dump(); +#endif + flags |= SC_EVENT_INSERTED; + break; + + case DeleteEvent: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteEvent pre: "); + _ev.dump(); +#endif + _part->nonconst_events().erase(_iev); +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteEvent post: "); + _ev.dump(); +#endif + flags |= SC_EVENT_REMOVED; + break; + + + case AddMidiCtrlValList: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddMidiCtrlValList: mcvll:%p mcvl:%p chan:%d\n", _mcvll, _mcvl, _intA); +#endif + _mcvll->add(_intA, _mcvl); + flags |= SC_MIDI_CONTROLLER_ADD; + break; + case AddMidiCtrlVal: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddMidiCtrlVal: mcvl:%p part:%p tick:%d val:%d\n", _mcvl, _part, _intA, _intB); +#endif + _mcvl->insert(std::pair (_intA, MidiCtrlVal(_part, _intB))); // FIXME FINDMICHJETZT XTicks!! + break; + case DeleteMidiCtrlVal: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteMidiCtrlVal: mcvl:%p tick:%d part:%p val:%d\n", + _mcvl, _imcv->first, _imcv->second.part, _imcv->second.val); +#endif + _mcvl->erase(_imcv); + break; + case ModifyMidiCtrlVal: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyMidiCtrlVal: part:%p old_val:%d new_val:%d\n", + _imcv->second.part, _imcv->second.val, _intA); +#endif + _imcv->second.val = _intA; + break; + + + case ModifyAudioCtrlValList: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyAudioCtrlValList: old ctrl_l:%p new ctrl_l:%p\n", _iCtrlList->second, _aud_ctrl_list); +#endif + CtrlList* orig = _iCtrlList->second; + _iCtrlList->second = _aud_ctrl_list; + // Transfer the original pointer back to _aud_ctrl_list so it can be deleted in the non-RT stage. + _aud_ctrl_list = orig; + flags |= SC_AUDIO_CONTROLLER_LIST; + } + break; + case AddAudioCtrlVal: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddAudioCtrlVal: ctrl_l:%p frame:%d val:%f\n", + _aud_ctrl_list, _frame, _ctl_dbl_val); +#endif + _aud_ctrl_list->insert(std::pair (_frame, CtrlVal(_frame, _ctl_dbl_val))); + flags |= SC_AUDIO_CONTROLLER; + break; + case DeleteAudioCtrlVal: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteAudioCtrlVal: ctrl_l:%p ctrl_num:%d frame:%d val:%f\n", + _aud_ctrl_list, _aud_ctrl_list->id(), _iCtrl->first, _iCtrl->second.val); +#endif + _aud_ctrl_list->erase(_iCtrl); + flags |= SC_AUDIO_CONTROLLER; + break; + case ModifyAudioCtrlVal: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyAudioCtrlVal: frame:%d old_val:%f new_val:%f\n", + _iCtrl->first, _iCtrl->second.val, _ctl_dbl_val); +#endif + // If the frame is the same, just change the value. + if(_iCtrl->second.frame == _frame) + { + _iCtrl->second.val = _ctl_dbl_val; + } + // Otherwise erase + add is required. + else + { + _aud_ctrl_list->erase(_iCtrl); + _aud_ctrl_list->insert(std::pair (_frame, CtrlVal(_frame, _ctl_dbl_val))); + } + flags |= SC_AUDIO_CONTROLLER; + break; + + + case AddTempo: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddTempo: tempolist:%p tempo:%p %d tick:%d\n", + _tempo_list, _tempo_event, _tempo_event->tempo, _tempo_event->tick); +#endif + _tempo_list->add(_intA, _tempo_event, false); // Defer normalize until end of stage 2. + flags |= SC_TEMPO; + break; + + case DeleteTempo: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteTempo: tempolist:%p event:%p: tick:%d tempo:%d\n", + _tempo_list, _iTEvent->second, _iTEvent->second->tick, _iTEvent->second->tempo); +#endif + _tempo_list->del(_iTEvent, false); // Defer normalize until end of stage 2. + flags |= SC_TEMPO; + } + break; + + case ModifyTempo: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyTempo: tempolist:%p event:%p: tick:%d old_tempo:%d new_tempo:%d\n", + _tempo_list, _iTEvent->second, _iTEvent->second->tick, _iTEvent->second->tempo, _intA); +#endif + _iTEvent->second->tempo = _intA; + flags |= SC_TEMPO; + break; + + case SetGlobalTempo: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage SetGlobalTempo: tempolist:%p new_tempo:%d\n", _tempo_list, _intA); +#endif + _tempo_list->setGlobalTempo(_intA); + flags |= SC_TEMPO; + break; + + + case AddSig: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddSig: siglist:%p sig:%p %d/%d tick:%d\n", + _sig_list, _sig_event, _sig_event->sig.z, _sig_event->sig.n, _sig_event->tick); +#endif + _sig_list->add(_intA, _sig_event, false); // Defer normalize until end of stage 2. + flags |= SC_SIG; + break; + + case DeleteSig: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteSig: siglist:%p event:%p: tick:%d sig:%d/%d\n", + _sig_list, _iSigEvent->second, _iSigEvent->second->tick, _iSigEvent->second->sig.z, _iSigEvent->second->sig.n); +#endif + _sig_list->del(_iSigEvent, false); // Defer normalize until end of stage 2. + flags |= SC_SIG; + } + break; + + case ModifySig: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifySig: siglist:%p event:%p: tick:%d old_sig:%d/%d new_sig:%d/%d\n", + _sig_list, _iSigEvent->second, _iSigEvent->second->tick, _iSigEvent->second->sig.z, _iSigEvent->second->sig.n, _intA, _intB); +#endif + _iSigEvent->second->sig.z = _intA; + _iSigEvent->second->sig.n = _intB; + flags |= SC_SIG; + break; + + + case AddKey: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage AddKey: keylist:%p key:%d tick:%d\n", _key_list, _intB, _intA); +#endif + _key_list->add(KeyEvent(key_enum(_intB), _intA)); + flags |= SC_KEY; + break; + + case DeleteKey: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage DeleteKey: keylist:%p key:%d tick:%d\n", + _key_list, _iKeyEvent->second.key, _iKeyEvent->second.tick); +#endif + _key_list->del(_iKeyEvent); + flags |= SC_KEY; + } + break; + + case ModifyKey: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyKey: keylist:%p old_key:%d new_key:%d tick:%d\n", + _key_list, _iKeyEvent->second.key, _intA, _iKeyEvent->second.tick); +#endif + _iKeyEvent->second.key = key_enum(_intA); + flags |= SC_KEY; + break; + + + case ModifySongLength: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifySongLength: len:%d\n", _intA); +#endif + MusEGlobal::song->setLen(_intA, false); // false = Do not emit update signals here ! + flags |= SC_EVERYTHING; + break; + + case EnableAllAudioControllers: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage EnableAllAudioControllers\n"); +#endif + TrackList* tl = MusEGlobal::song->tracks(); + for (iTrack it = tl->begin(); it != tl->end(); ++it) + { + Track* t = *it; + if(t->isMidiTrack()) + continue; + AudioTrack *at = static_cast(t); + // Re-enable all track and plugin controllers, and synth controllers if applicable. + at->enableAllControllers(); + flags |= SC_AUDIO_CONTROLLER; + } + } + break; + + case ModifyAudioSamples: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeRTStage ModifyAudioSamples: " + "audioSamplesPointer:%p newAudioSamples:%p audioSamplesLen:%p newAudioSamplesLen:%d\n", + _audioSamplesPointer, _newAudioSamples, _audioSamplesLen, _newAudioSamplesLen); +#endif + if(_audioSamplesPointer) + { + float* orig = *_audioSamplesPointer; + *_audioSamplesPointer = _newAudioSamples; + // Transfer the original pointer back to _audioSamplesPointer so it can be deleted in the non-RT stage. + _newAudioSamples = orig; + } + + if(_audioSamplesLen) + *_audioSamplesLen = _newAudioSamplesLen; + + // Currently no flags for this. + //flags |= SC_; + } + break; + + case Uninitialized: + break; + + default: + fprintf(stderr, "PendingOperationItem::executeRTStage unknown type %d\n", _type); + break; + } + return flags; +} + +SongChangedFlags_t PendingOperationItem::executeNonRTStage() +{ + SongChangedFlags_t flags = 0; + switch(_type) + { + case AddRoute: + if(MusEGlobal::song->connectJackRoutes(_src_route, _dst_route)) + flags |= SC_ROUTE; + break; + + case DeleteRoute: + if(MusEGlobal::song->connectJackRoutes(_src_route, _dst_route, true)) + flags |= SC_ROUTE; + break; + + case DeleteTempo: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeNonRTStage DeleteTempo: tempolist:%p event:%p:\n", + _tempo_list, _tempo_event); +#endif + if(_tempo_event) + { + delete _tempo_event; + _tempo_event = 0; + } + } + break; + + case DeleteSig: + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationItem::executeNonRTStage DeleteSig: siglist:%p event:%p:\n", + _sig_list, _sig_event); +#endif + if(_sig_event) + { + delete _sig_event; + _sig_event = 0; + } + } + break; + + case ReplaceMidiInstrument: + // At this point _midi_instrument is the original instrument that was replaced. Delete it now. + if(_midi_instrument) + delete _midi_instrument; + break; + + case ModifyAudioCtrlValList: + // At this point _aud_ctrl_list is the original list that was replaced. Delete it now. + if(_aud_ctrl_list) + delete _aud_ctrl_list; + break; + + case ModifyTrackDrumMapItem: + // Discard the operation, it has already completed. + if(_drum_map_track_operation) + delete _drum_map_track_operation; + break; + + case ReplaceTrackDrumMapPatchList: + // Discard the operation, it has already completed. + if(_drum_map_track_patch_operation) + { + // At this point _workingItemPatchList is the original list that was replaced. Delete it now. + if(_drum_map_track_patch_replace_operation->_workingItemPatchList) + delete _drum_map_track_patch_replace_operation->_workingItemPatchList; + + delete _drum_map_track_patch_replace_operation; + } + break; + + case RemapDrumControllers: + // Discard the operation, it has already completed. + if(_midi_ctrl_val_remap_operation) + { + // At this point _midiCtrlValLists2bDeleted contains the original lists that were replaced. Delete them now. + for(iMidiCtrlValLists2bDeleted_t imvld = _midi_ctrl_val_remap_operation->_midiCtrlValLists2bDeleted.begin(); + imvld != _midi_ctrl_val_remap_operation->_midiCtrlValLists2bDeleted.end(); ++imvld) + delete *imvld; + + delete _midi_ctrl_val_remap_operation; + } + break; + + case ModifyAudioSamples: + // At this point _newAudioSamples points to the original memory that was replaced. Delete it now. + if(_newAudioSamples) + delete _newAudioSamples; + break; + + default: + break; + } + return flags; +} + +SongChangedFlags_t PendingOperationList::executeRTStage() +{ +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::executeRTStage executing...\n"); +#endif + for(iPendingOperation ip = begin(); ip != end(); ++ip) + _sc_flags |= ip->executeRTStage(); + + // To avoid doing this item by item, do it here. + if(_sc_flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_ROUTE)) + { + MusEGlobal::song->updateSoloStates(); + _sc_flags |= SC_SOLO; + } + + return _sc_flags; +} + +SongChangedFlags_t PendingOperationList::executeNonRTStage() +{ +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::executeNonRTStage executing...\n"); +#endif + for(iPendingOperation ip = begin(); ip != end(); ++ip) + _sc_flags |= ip->executeNonRTStage(); + return _sc_flags; +} + +void PendingOperationList::clear() +{ + _sc_flags = 0; + _map.clear(); + std::list::clear(); +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::clear * post map size:%d list size:%d\n", _map.size(), size()); +#endif +} + +bool PendingOperationList::add(PendingOperationItem op) +{ + int t = op.getIndex(); + + switch(op._type) + { + // For these special allocation ops, searching has already been done before hand. Just add them. + case PendingOperationItem::AddMidiCtrlValList: + case PendingOperationItem::AddTempo: + case PendingOperationItem::AddSig: + { + iPendingOperation iipo = insert(end(), op); + _map.insert(std::pair(t, iipo)); + return true; + } + break; + + default: + break; + } + + iPendingOperationSortedRange r = _map.equal_range(t); + iPendingOperationSorted ipos = r.second; + while(ipos != r.first) + { + --ipos; + PendingOperationItem& poi = *ipos->second; + + switch(op._type) + { + case PendingOperationItem::ModifyTrackDrumMapItem: + if(poi._type == PendingOperationItem::ModifyTrackDrumMapItem && + poi._drum_map_track_operation == op._drum_map_track_operation) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double ModifyTrackDrumMapItem. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::ReplaceTrackDrumMapPatchList: + if(poi._type == PendingOperationItem::ReplaceTrackDrumMapPatchList && + poi._drum_map_track_patch_replace_operation == op._drum_map_track_patch_replace_operation) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double ReplaceTrackDrumMapPatchList. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::RemapDrumControllers: + if(poi._type == PendingOperationItem::RemapDrumControllers && + poi._midi_ctrl_val_remap_operation == op._midi_ctrl_val_remap_operation) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double RemapDrumControllers. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::UpdateDrumMaps: + if(poi._type == PendingOperationItem::UpdateDrumMaps && poi._midi_port == op._midi_port) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double UpdateDrumMaps. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::UpdateSoloStates: + if(poi._type == PendingOperationItem::UpdateSoloStates && poi._track_list == op._track_list) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double UpdateSoloStates. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::AddRoute: + if(poi._type == PendingOperationItem::AddRoute && poi._src_route == op._src_route && poi._dst_route == op._dst_route) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double AddRoute. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::DeleteRoute: + if(poi._type == PendingOperationItem::DeleteRoute && poi._src_route == op._src_route && poi._dst_route == op._dst_route) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteRoute. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::AddRouteNode: + if(poi._type == PendingOperationItem::AddRouteNode && poi._route_list == op._route_list && poi._src_route == op._src_route) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double AddRouteNode. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::DeleteRouteNode: + if(poi._type == PendingOperationItem::DeleteRouteNode && poi._route_list == op._route_list && poi._iRoute == op._iRoute) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteRouteNode. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::ModifyRouteNode: + if(poi._type == PendingOperationItem::ModifyRouteNode && poi._src_route == op._src_route && poi._dst_route_pointer == op._dst_route_pointer) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double ModifyRouteNode. Ignoring.\n"); + return false; + } + break; + + + case PendingOperationItem::AddAuxSendValue: + if(poi._type == PendingOperationItem::AddAuxSendValue && poi._aux_send_value_list == op._aux_send_value_list) + { + // Do nothing. So far. + } + break; + + case PendingOperationItem::AddMidiInstrument: + if(poi._type == PendingOperationItem::AddMidiInstrument && poi._midi_instrument_list == op._midi_instrument_list && + poi._midi_instrument == op._midi_instrument) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double AddMidiInstrument. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::DeleteMidiInstrument: + if(poi._type == PendingOperationItem::DeleteMidiInstrument && poi._midi_instrument_list == op._midi_instrument_list && + poi._iMidiInstrument == op._iMidiInstrument) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteMidiInstrument. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::ReplaceMidiInstrument: + if(poi._type == PendingOperationItem::ReplaceMidiInstrument && poi._midi_instrument_list == op._midi_instrument_list && + (poi._midi_instrument == op._midi_instrument || poi._iMidiInstrument == op._iMidiInstrument)) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double ReplaceMidiInstrument. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::AddMidiDevice: + if(poi._type == PendingOperationItem::AddMidiDevice && poi._midi_device_list == op._midi_device_list && poi._midi_device == op._midi_device) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double AddMidiDevice. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::DeleteMidiDevice: + if(poi._type == PendingOperationItem::DeleteMidiDevice && poi._midi_device_list == op._midi_device_list && poi._iMidiDevice == op._iMidiDevice) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteMidiDevice. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::ModifyMidiDeviceAddress: + if(poi._type == PendingOperationItem::ModifyMidiDeviceAddress && poi._midi_device == op._midi_device && + poi._address_client == op._address_client && poi._address_port == op._address_port) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double ModifyMidiDeviceAddress. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::ModifyMidiDeviceFlags: + if(poi._type == PendingOperationItem::ModifyMidiDeviceFlags && poi._midi_device == op._midi_device && + poi._rw_flags == op._rw_flags && poi._open_flags == op._open_flags) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double ModifyMidiDeviceFlags. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::ModifyMidiDeviceName: + if(poi._type == PendingOperationItem::ModifyMidiDeviceName && poi._midi_device == op._midi_device && + poi._name == op._name) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double ModifyMidiDeviceName. Ignoring.\n"); + return false; + } + break; + + + case PendingOperationItem::AddTrack: + if(poi._type == PendingOperationItem::AddTrack && poi._track_list == op._track_list && poi._track == op._track) + { + // Simply replace the insert point. + poi._insert_at = op._insert_at; + return true; + } + else if(poi._type == PendingOperationItem::DeleteTrack && poi._track_list == op._track_list && poi._track == op._track) + { + // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. + //erase(ipos->second); + //_map.erase(ipos); + //return true; + } + break; + + case PendingOperationItem::DeleteTrack: + if(poi._type == PendingOperationItem::DeleteTrack && poi._track_list == op._track_list && poi._track == op._track) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteTrack. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::AddTrack && poi._track_list == op._track_list && poi._track == op._track) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + //erase(ipos->second); + //_map.erase(ipos); + //return true; + } + break; + + case PendingOperationItem::MoveTrack: + if(poi._type == PendingOperationItem::MoveTrack && poi._track == op._track && poi._track_list == op._track_list) + { + // Simply replace the 'to' index. + poi._to_idx = op._to_idx; + return true; + } + break; + + case PendingOperationItem::ModifyTrackName: + if(poi._type == PendingOperationItem::ModifyTrackName && poi._track == op._track && + poi._name == op._name) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double ModifyTrackName. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::SetTrackRecord: + if(poi._type == PendingOperationItem::SetTrackRecord && poi._track == op._track) + { + if(poi._boolA == op._boolA) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetTrackRecord. Ignoring.\n"); + return false; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + } + break; + + case PendingOperationItem::SetTrackMute: + if(poi._type == PendingOperationItem::SetTrackMute && poi._track == op._track) + { + if(poi._boolA == op._boolA) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetTrackMute. Ignoring.\n"); + return false; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + } + break; + + case PendingOperationItem::SetTrackSolo: + if(poi._type == PendingOperationItem::SetTrackSolo && poi._track == op._track) + { + if(poi._boolA == op._boolA) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetTrackSolo. Ignoring.\n"); + return false; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + } + break; + + case PendingOperationItem::SetTrackRecMonitor: + if(poi._type == PendingOperationItem::SetTrackRecMonitor && poi._track == op._track) + { + if(poi._boolA == op._boolA) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetTrackRecMonitor. Ignoring.\n"); + return false; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + } + break; + + case PendingOperationItem::SetTrackOff: + if(poi._type == PendingOperationItem::SetTrackOff && poi._track == op._track) + { + if(poi._boolA == op._boolA) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetTrackOff. Ignoring.\n"); + return false; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + } + break; + + case PendingOperationItem::AddPart: + if(poi._type == PendingOperationItem::AddPart && poi._part_list == op._part_list && poi._part == op._part) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double AddPart. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::DeletePart && poi._part_list == op._part_list && poi._iPart->second == op._part) + { + // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + break; + + case PendingOperationItem::DeletePart: + if(poi._type == PendingOperationItem::DeletePart && poi._part_list == op._part_list && poi._iPart->second == op._iPart->second) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeletePart. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::AddPart && poi._part_list == op._part_list && poi._part == op._iPart->second) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + break; + + case PendingOperationItem::MovePart: + if(poi._type == PendingOperationItem::MovePart && poi._part == op._part) + { + // Simply replace the values. + poi._iPart = op._iPart; + poi._track = op._track; + poi._intA = op._intA; + return true; + } + break; + + case PendingOperationItem::ModifyPartName: + if(poi._type == PendingOperationItem::ModifyPartName && poi._part == op._part && + poi._name == op._name) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double ModifyPartName. Ignoring.\n"); + return false; + } + break; + + + case PendingOperationItem::AddEvent: + if(poi._type == PendingOperationItem::AddEvent && poi._part == op._part && poi._ev == op._ev) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double AddEvent. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::DeleteEvent && poi._part == op._part && poi._iev->second == op._ev) + { + // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + break; + + case PendingOperationItem::DeleteEvent: + if(poi._type == PendingOperationItem::DeleteEvent && poi._part == op._part && poi._iev->second == op._iev->second) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteEvent. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::AddEvent && poi._part == op._part && poi._ev == op._iev->second) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + break; + + case PendingOperationItem::AddMidiCtrlVal: + if(poi._type == PendingOperationItem::AddMidiCtrlVal && poi._mcvl == op._mcvl && poi._part == op._part) + { + // Simply replace the value. + poi._intB = op._intB; + return true; + } + else if(poi._type == PendingOperationItem::DeleteMidiCtrlVal && poi._mcvl == op._mcvl && poi._imcv->second.part == op._part) + { + // Transform existing delete command into a modify command. + poi._type = PendingOperationItem::ModifyMidiCtrlVal; + poi._intA = op._intB; + return true; + } + else if(poi._type == PendingOperationItem::ModifyMidiCtrlVal && poi._mcvl == op._mcvl && poi._imcv->second.part == op._part) + { + // Simply replace the value. + poi._intA = op._intB; + return true; + } + break; + + case PendingOperationItem::DeleteMidiCtrlVal: + if(poi._type == PendingOperationItem::DeleteMidiCtrlVal && poi._mcvl == op._mcvl && poi._imcv->second.part == op._imcv->second.part) + { + // Multiple delete commands not allowed! + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteMidiCtrlVal. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::AddMidiCtrlVal && poi._mcvl == op._mcvl && poi._part == op._imcv->second.part) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + else if(poi._type == PendingOperationItem::ModifyMidiCtrlVal && poi._mcvl == op._mcvl && poi._imcv->second.part == op._imcv->second.part) + { + // Modify followed by delete is equivalent to just deleting. + // Transform existing modify command into a delete command. + poi._type = PendingOperationItem::DeleteMidiCtrlVal; + return true; + } + break; + + case PendingOperationItem::ModifyMidiCtrlVal: + if(poi._type == PendingOperationItem::ModifyMidiCtrlVal && poi._mcvl == op._mcvl && poi._imcv->second.part == op._imcv->second.part) + { + // Simply replace the value. + poi._intA = op._intA; + return true; + } + else if(poi._type == PendingOperationItem::DeleteMidiCtrlVal && poi._mcvl == op._mcvl && poi._imcv->second.part == op._imcv->second.part) + { + // Transform existing delete command into a modify command. + poi._type = PendingOperationItem::ModifyMidiCtrlVal; + poi._intA = op._intA; + return true; + } + else if(poi._type == PendingOperationItem::AddMidiCtrlVal && poi._mcvl == op._mcvl && poi._part == op._imcv->second.part) + { + // Simply replace the add value with the modify value. + poi._intB = op._intA; + return true; + } + break; + + + case PendingOperationItem::ModifyAudioCtrlValList: + if(poi._type == PendingOperationItem::ModifyAudioCtrlValList && + // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). + (poi._iCtrlList->second == op._iCtrlList->second || poi._aud_ctrl_list == op._iCtrlList->second)) + { + // Simply replace the list. + poi._aud_ctrl_list = op._aud_ctrl_list; + return true; + } + break; + + case PendingOperationItem::AddAudioCtrlVal: + if(poi._type == PendingOperationItem::AddAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) + { + // Simply replace the value. + poi._ctl_dbl_val = op._ctl_dbl_val; + return true; + } + else if(poi._type == PendingOperationItem::DeleteAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) + { + // Transform existing delete command into a modify command. + poi._type = PendingOperationItem::ModifyAudioCtrlVal; + poi._ctl_dbl_val = op._ctl_dbl_val; + return true; + } + else if(poi._type == PendingOperationItem::ModifyAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) + { + // Simply replace the value. + poi._ctl_dbl_val = op._ctl_dbl_val; + return true; + } + break; + + case PendingOperationItem::DeleteAudioCtrlVal: + if(poi._type == PendingOperationItem::DeleteAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) + { + // Multiple delete commands not allowed! + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteAudioCtrlVal. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::AddAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + else if(poi._type == PendingOperationItem::ModifyAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) + { + // Modify followed by delete is equivalent to just deleting. + // Transform existing modify command into a delete command. + poi._type = PendingOperationItem::DeleteMidiCtrlVal; + return true; + } + break; + + case PendingOperationItem::ModifyAudioCtrlVal: + if(poi._type == PendingOperationItem::ModifyAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) + { + // Simply replace the value. + poi._ctl_dbl_val = op._ctl_dbl_val; + return true; + } + else if(poi._type == PendingOperationItem::DeleteAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) + { + // Transform existing delete command into a modify command. + poi._type = PendingOperationItem::ModifyAudioCtrlVal; + poi._ctl_dbl_val = op._ctl_dbl_val; + return true; + } + else if(poi._type == PendingOperationItem::AddAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) + { + // Simply replace the add value with the modify value. + poi._ctl_dbl_val = op._ctl_dbl_val; + return true; + } + break; + + + case PendingOperationItem::AddTempo: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() AddTempo\n"); +#endif + if(poi._type == PendingOperationItem::AddTempo && poi._tempo_list == op._tempo_list) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double AddTempo. Ignoring.\n"); + return false; + // Simply replace the value. + } + else if(poi._type == PendingOperationItem::DeleteTempo && poi._tempo_list == op._tempo_list) + { + // Delete followed by add. Cannot cancel them out because add already created a new object. Allow to add... + } + else if(poi._type == PendingOperationItem::ModifyTempo && poi._tempo_list == op._tempo_list) + { + // Modify followed by add. Error. + fprintf(stderr, "MusE error: PendingOperationList::add(): ModifyTempo then AddTempo. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::DeleteTempo: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() DeleteTempo\n"); +#endif + if(poi._type == PendingOperationItem::DeleteTempo && poi._tempo_list == op._tempo_list) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteTempo. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::AddTempo && poi._tempo_list == op._tempo_list) + { + // Add followed by delete. Cannot cancel them out because add already created a new object. Allow to delete... + } + else if(poi._type == PendingOperationItem::ModifyTempo && poi._tempo_list == op._tempo_list) + { + // Modify followed by delete is equivalent to just deleting. + // Transform existing modify command into a delete command. + poi._type = PendingOperationItem::DeleteTempo; + // Modify's iterator will point one AFTER the delete iterator. So decrement the iterator. + //--poi._iTEvent; + // Replace the modify iterator with the delete iterator. + poi._iTEvent = op._iTEvent; + poi._tempo_event = op._tempo_event; + return true; + } + break; + + case PendingOperationItem::ModifyTempo: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() ModifyTempo\n"); +#endif + if(poi._type == PendingOperationItem::ModifyTempo && poi._tempo_list == op._tempo_list) + { + // Simply replace the value. + poi._intA = op._intA; + return true; + } + else if(poi._type == PendingOperationItem::AddTempo && poi._tempo_list == op._tempo_list) + { + // Add followed by modify. Just replace the add value + poi._tempo_event->tempo = op._iTEvent->second->tempo; + return true; + } + else if(poi._type == PendingOperationItem::DeleteTempo && poi._tempo_list == op._tempo_list) + { + // Transform existing delete command into a modify command. + poi._type = PendingOperationItem::ModifyTempo; + // Delete's iterator will point one BEFORE the modify iterator. So increment the iterator. + //++poi._iTEvent; + // Replace the delete iterator with the modify iterator. + poi._iTEvent = op._iTEvent; + // Grab the tempo. + poi._intA = op._intA; + // Delete always does normalize, so nowhere to grab this value from. + //poi._intB = true; + return true; + } + break; + + case PendingOperationItem::SetGlobalTempo: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() SetGlobalTempo\n"); +#endif + if(poi._type == PendingOperationItem::SetGlobalTempo && poi._tempo_list == op._tempo_list) + { + // Simply replace the new value. + poi._intA = op._intA; + return true; + } + break; + + + case PendingOperationItem::AddSig: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() AddSig\n"); +#endif + if(poi._type == PendingOperationItem::AddSig && poi._sig_list == op._sig_list) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double AddSig. Ignoring.\n"); + return false; + // Simply replace the value. + } + else if(poi._type == PendingOperationItem::DeleteSig && poi._sig_list== op._sig_list) + { + // Delete followed by add. Cannot cancel them out because add already created a new object. Allow to add... + } + else if(poi._type == PendingOperationItem::ModifySig && poi._sig_list == op._sig_list) + { + // Modify followed by add. Error. + fprintf(stderr, "MusE error: PendingOperationList::add(): ModifySig then AddSig. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::DeleteSig: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() DeleteSig\n"); +#endif + if(poi._type == PendingOperationItem::DeleteSig && poi._sig_list == op._sig_list) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteSig. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::AddSig && poi._sig_list == op._sig_list) + { + // Add followed by delete. Cannot cancel them out because add already created a new object. Allow to delete... + } + else if(poi._type == PendingOperationItem::ModifySig && poi._sig_list == op._sig_list) + { + // Modify followed by delete is equivalent to just deleting. + // Transform existing modify command into a delete command. + poi._type = PendingOperationItem::DeleteSig; + // Modify's iterator will point one AFTER the delete iterator. So decrement the iterator. + //--poi._iSigEvent; + // Replace the modify iterator with the delete iterator. + poi._iSigEvent = op._iSigEvent; + poi._sig_event = op._sig_event; + return true; + } + break; + + case PendingOperationItem::ModifySig: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() ModifySig\n"); +#endif + if(poi._type == PendingOperationItem::ModifySig && poi._sig_list == op._sig_list) + { + // Simply replace the value. + poi._intA = op._intA; + poi._intB = op._intB; + return true; + } + else if(poi._type == PendingOperationItem::AddSig && poi._sig_list == op._sig_list) + { + // Add followed by modify. Just replace the add value + poi._sig_event->sig = op._iSigEvent->second->sig; + return true; + } + else if(poi._type == PendingOperationItem::DeleteSig && poi._sig_list == op._sig_list) + { + // Transform existing delete command into a modify command. + poi._type = PendingOperationItem::ModifySig; + // Delete's iterator will point one BEFORE the modify iterator. So increment the iterator. + //++poi._iSigEvent; + // Replace the delete iterator with the modify iterator. + poi._iSigEvent = op._iSigEvent; + // Grab the signature. + poi._intA = op._intA; + poi._intB = op._intB; + return true; + } + break; + + + case PendingOperationItem::AddKey: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() AddKey\n"); +#endif + if(poi._type == PendingOperationItem::AddKey && poi._key_list == op._key_list) + { + // Simply replace the value. + poi._intB = op._intB; + return true; + } + else if(poi._type == PendingOperationItem::DeleteKey && poi._key_list== op._key_list) + { + // Transform existing delete command into a modify command. + poi._type = PendingOperationItem::ModifyKey; + poi._intA = op._intB; + return true; + } + else if(poi._type == PendingOperationItem::ModifyKey && poi._key_list == op._key_list) + { + // Simply replace the value. + poi._intA = op._intB; + return true; + } + break; + + case PendingOperationItem::DeleteKey: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() DeleteKey\n"); +#endif + if(poi._type == PendingOperationItem::DeleteKey && poi._key_list == op._key_list) + { + // Multiple delete commands not allowed! + fprintf(stderr, "MusE error: PendingOperationList::add(): Double DeleteKey. Ignoring.\n"); + return false; + } + else if(poi._type == PendingOperationItem::AddKey && poi._key_list == op._key_list) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(ipos->second); + _map.erase(ipos); + return true; + } + else if(poi._type == PendingOperationItem::ModifyKey && poi._key_list == op._key_list) + { + // Modify followed by delete is equivalent to just deleting. + // Transform existing modify command into a delete command. + poi._type = PendingOperationItem::DeleteKey; + // Modify's iterator will point one AFTER the delete iterator. So decrement the iterator. + //--poi._iKeyEvent; + // Replace the modify iterator with the delete iterator. + poi._iKeyEvent = op._iKeyEvent; + return true; + } + break; + + case PendingOperationItem::ModifyKey: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() ModifyKey\n"); +#endif + if(poi._type == PendingOperationItem::ModifyKey && poi._key_list == op._key_list) + { + // Simply replace the value. + poi._intA = op._intA; + return true; + } + else if(poi._type == PendingOperationItem::AddKey && poi._key_list == op._key_list) + { + // Simply replace the add value with the modify value. + poi._intB = op._intA; + return true; + } + else if(poi._type == PendingOperationItem::DeleteKey && poi._key_list == op._key_list) + { + // Transform existing delete command into a modify command. + poi._type = PendingOperationItem::ModifyKey; + // Delete's iterator will point one BEFORE the modify iterator. So increment the iterator. + //++poi._iKeyEvent; + // Replace the delete iterator with the modify iterator. + poi._iKeyEvent = op._iKeyEvent; + // Replace the value. + poi._intA = op._intA; + return true; + } + break; + + + case PendingOperationItem::ModifySongLength: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() ModifySongLength\n"); +#endif + if(poi._type == PendingOperationItem::ModifySongLength) + { + // Simply replace the value. + poi._intA = op._intA; + return true; + } + break; + + case PendingOperationItem::EnableAllAudioControllers: +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() EnableAllAudioControllers\n"); +#endif + if(poi._type == PendingOperationItem::EnableAllAudioControllers) + { + fprintf(stderr, "MusE error: PendingOperationList::add(): Double EnableAllAudioControllers. Ignoring.\n"); + return false; + } + break; + + case PendingOperationItem::ModifyAudioSamples: +// TODO Not quite right yet. +// if(poi._type == PendingOperationItem::ModifyAudioSamples && +// // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). +// poi._audioSamplesPointer && op._audioSamplesPointer && +// (*poi._audioSamplesPointer == *op._audioSamplesPointer || poi._newAudioSamples == op._newAudioSamples)) +// { +// // Simply replace the list. +// poi._newAudioSamples = op._newAudioSamples; +// poi._newAudioSamplesLen = op._newAudioSamplesLen; +// return true; +// } + break; + + case PendingOperationItem::Uninitialized: + fprintf(stderr, "MusE error: PendingOperationList::add(): Uninitialized item. Ignoring.\n"); + return false; + break; + + default: + break; + } + } + + // Special for these types of lists: Because of the way these lists operate, + // a delete operation followed by a modify operation on the SAME iterator + // needs special attention: The delete operation will ERASE the iterator + // that the modify operation points to! Also the two operations will have + // different sorting ticks and won't be caught in the above loop. + // The only way around it is to increment the modify iterator now so that it is + // already pointing to the next item by the time the delete operation happens: + if(op._type == PendingOperationItem::ModifyTempo || + op._type == PendingOperationItem::ModifySig || + op._type == PendingOperationItem::ModifyKey) + { + int idx = 0; + if(op._type == PendingOperationItem::ModifyTempo) + idx = op._iTEvent->first; + else if(op._type == PendingOperationItem::ModifySig) + idx = op._iSigEvent->first; + else if(op._type == PendingOperationItem::ModifyKey) + idx = op._iKeyEvent->first; + + iPendingOperationSortedRange r = _map.equal_range(idx); + iPendingOperationSorted ipos = r.second; + while(ipos != r.first) + { + --ipos; + PendingOperationItem& poi = *ipos->second; + + if(op._type == PendingOperationItem::ModifyTempo) + { + if(poi._type == PendingOperationItem::DeleteTempo && poi._tempo_list == op._tempo_list) + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() DeleteTempo + ModifyTempo: Incrementing modify iterator: idx:%d cur tempo:%d tick:%d\n", + idx, op._iTEvent->second->tempo, op._iTEvent->second->tick); +#endif + op._iTEvent++; + break; + } + } + else if(op._type == PendingOperationItem::ModifySig) + { + if(poi._type == PendingOperationItem::DeleteSig && poi._sig_list == op._sig_list) + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() DeleteSig + ModifySig: Incrementing modify iterator: idx:%d cur sig:%d/%d tick:%d\n", + idx, op._iSigEvent->second->sig.z, op._iSigEvent->second->sig.n, op._iSigEvent->second->tick); +#endif + op._iSigEvent++; + break; + } + } + else if(op._type == PendingOperationItem::ModifyKey) + { + if(poi._type == PendingOperationItem::DeleteKey && poi._key_list == op._key_list) + { +#ifdef _PENDING_OPS_DEBUG_ + fprintf(stderr, "PendingOperationList::add() DeleteKey + ModifyKey: Incrementing modify iterator: idx:%d cur key:%d tick:%d\n", + idx, op._iKeyEvent->second.key, op._iKeyEvent->second.tick); +#endif + op._iKeyEvent++; + break; + } + } + } + } + + iPendingOperation iipo = insert(end(), op); + _map.insert(std::pair(t, iipo)); + return true; +} + +iPendingOperation PendingOperationList::findAllocationOp(const PendingOperationItem& op) +{ + iPendingOperationSortedRange r = _map.equal_range(op.getIndex()); + iPendingOperationSorted ipos = r.second; + while(ipos != r.first) + { + --ipos; + const PendingOperationItem& poi = *ipos->second; + if(poi.isAllocationOp(op)) // Comparison. + return ipos->second; + } + return end(); +} + + +} // namespace MusECore + diff -Nru muse-2.1.2/muse/operations.h muse-3.0.2+ds1/muse/operations.h --- muse-2.1.2/muse/operations.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/operations.h 2017-12-17 21:07:38.000000000 +0000 @@ -0,0 +1,533 @@ +//========================================================= +// MusE +// Linux Music Editor +// operations.h +// (C) Copyright 2014, 2016 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 +// 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 __OPERATIONS_H__ +#define __OPERATIONS_H__ + +#include +#include +#include + +#include "type_defs.h" +#include "event.h" +#include "midictrl.h" +#include "ctrl.h" +#include "tempo.h" +#include "al/sig.h" +#include "keyevent.h" +#include "part.h" +#include "track.h" +#include "midiedit/drummap.h" +#include "route.h" +#include "mididev.h" +#include "midiport.h" +#include "instruments/minstrument.h" + +namespace MusECore { + + +typedef std::list < iMidiCtrlValList > MidiCtrlValListIterators_t; +typedef MidiCtrlValListIterators_t::iterator iMidiCtrlValListIterators_t; +typedef MidiCtrlValListIterators_t::const_iterator ciMidiCtrlValListIterators_t; +class MidiCtrlValListIterators : public MidiCtrlValListIterators_t +{ + public: + iterator findList(const MidiCtrlValList* valList) + { + for(iterator i = begin(); i != end(); ++i) + if((*i)->second == valList) + return i; + return end(); + } + + const_iterator findList(const MidiCtrlValList* valList) const + { + for(const_iterator i = begin(); i != end(); ++i) + if((*i)->second == valList) + return i; + return end(); + } +}; + +typedef std::map < int /*port*/, MidiCtrlValListIterators, std::less > MidiCtrlValLists2bErased_t; +typedef MidiCtrlValLists2bErased_t::iterator iMidiCtrlValLists2bErased_t; +typedef MidiCtrlValLists2bErased_t::const_iterator ciMidiCtrlValLists2bErased_t; +typedef std::pair MidiCtrlValLists2bErasedInsertResult_t; +typedef std::pair MidiCtrlValLists2bErasedInsertPair_t; +typedef std::pair MidiCtrlValLists2bErasedRangePair_t; + +class MidiCtrlValLists2bErased : public MidiCtrlValLists2bErased_t +{ + public: + void add(int port, const iMidiCtrlValList& item) + { + iterator i = find(port); + if(i == end()) + { + MidiCtrlValListIterators mcvli; + mcvli.push_back(item); + insert(MidiCtrlValLists2bErasedInsertPair_t(port, mcvli)); + return; + } + MidiCtrlValListIterators& mcvli = i->second; + for(iMidiCtrlValListIterators_t imcvli = mcvli.begin(); imcvli != mcvli.end(); ++imcvli) + { + iMidiCtrlValList imcvl = *imcvli; + // Compare list pointers. + if(imcvl->second == item->second) + return; // Already exists. + } + mcvli.push_back(item); + } + + iterator findList(int port, const MidiCtrlValList* valList) + { + iterator i = find(port); + if(i == end()) + return end(); + if(i->second.findList(valList) != i->second.end()) + return i; + return end(); + } + + const_iterator findList(int port, const MidiCtrlValList* valList) const + { + const_iterator i = find(port); + if(i == end()) + return end(); + if(i->second.findList(valList) != i->second.end()) + return i; + return end(); + } +}; + + +typedef std::set < MidiCtrlValList* > MidiCtrlValLists2bDeleted_t; +typedef MidiCtrlValLists2bDeleted_t::iterator iMidiCtrlValLists2bDeleted_t; +typedef MidiCtrlValLists2bDeleted_t::const_iterator ciMidiCtrlValLists2bDeleted_t; +class MidiCtrlValLists2bDeleted : public MidiCtrlValLists2bDeleted_t +{ + +}; + + +typedef std::map < int /*port*/, MidiCtrlValListList*, std::less > MidiCtrlValLists2bAdded_t; +typedef MidiCtrlValLists2bAdded_t::iterator iMidiCtrlValLists2bAdded_t; +typedef MidiCtrlValLists2bAdded_t::const_iterator ciMidiCtrlValLists2bAdded_t; +typedef std::pair MidiCtrlValLists2bAddedInsertResult_t; +typedef std::pair MidiCtrlValLists2bAddedInsertPair_t; +typedef std::pair MidiCtrlValLists2bAddedRangePair_t; + +class MidiCtrlValLists2bAdded : public MidiCtrlValLists2bAdded_t +{ +// public: +// void add(int port, const MidiCtrlValListList* item); +// void remove(int port, const MidiCtrlValListList* item); +}; + + +struct MidiCtrlValRemapOperation +{ + // Iterators to be erased in realtime stage. + MidiCtrlValLists2bErased _midiCtrlValLists2bErased; + // New items to be added in realtime stage. + MidiCtrlValLists2bAdded _midiCtrlValLists2bAdded; + // Orphaned pointers after the iterators have been erased, deleted in post non-realtime stage. + // Automatically filled by constructor. + MidiCtrlValLists2bDeleted_t _midiCtrlValLists2bDeleted; +}; + +struct DrumMapTrackOperation +{ + // Whether this is a setting operation or a reset to defaults. + bool _isReset; + bool _isInstrumentMod; + bool _doWholeMap; + bool _includeDefault; + WorkingDrumMapList _workingItemList; + // List of tracks to apply to. + MidiTrackList _tracks; +}; + +struct DrumMapTrackPatchOperation +{ + // Whether to clear the list of overrides. + bool _clear; + // Whether this is a setting operation or a reset to defaults. + bool _isReset; + bool _isInstrumentMod; + WorkingDrumMapPatchList _workingItemPatchList; + // List of tracks to apply to. + MidiTrackList _tracks; +}; + +struct DrumMapTrackPatchReplaceOperation +{ + bool _isInstrumentMod; + WorkingDrumMapPatchList* _workingItemPatchList; + // Track to apply to. + MidiTrack* _track; +}; + +// New items created in GUI thread awaiting addition in audio thread. +struct PendingOperationItem +{ + enum PendingOperationType { Uninitialized = 0, + ModifySongLength, + AddMidiInstrument, DeleteMidiInstrument, ReplaceMidiInstrument, + AddMidiDevice, DeleteMidiDevice, + ModifyMidiDeviceAddress, ModifyMidiDeviceFlags, ModifyMidiDeviceName, + AddTrack, DeleteTrack, MoveTrack, ModifyTrackName, + SetTrackRecord, SetTrackMute, SetTrackSolo, SetTrackRecMonitor, SetTrackOff, + ModifyTrackDrumMapItem, ReplaceTrackDrumMapPatchList, UpdateDrumMaps, + AddPart, DeletePart, MovePart, ModifyPartLength, ModifyPartName, + AddEvent, DeleteEvent, + AddMidiCtrlVal, DeleteMidiCtrlVal, ModifyMidiCtrlVal, AddMidiCtrlValList, + RemapDrumControllers, + AddAudioCtrlVal, DeleteAudioCtrlVal, ModifyAudioCtrlVal, ModifyAudioCtrlValList, + AddTempo, DeleteTempo, ModifyTempo, SetGlobalTempo, + AddSig, DeleteSig, ModifySig, + AddKey, DeleteKey, ModifyKey, + AddAuxSendValue, + AddRoute, DeleteRoute, + AddRouteNode, DeleteRouteNode, ModifyRouteNode, + UpdateSoloStates, + EnableAllAudioControllers, + ModifyAudioSamples + }; + + PendingOperationType _type; + + union { + Part* _part; + MidiPort* _midi_port; + void* _void_track_list; + int* _audioSamplesLen; + }; + + union { + MidiCtrlValListList* _mcvll; + CtrlListList* _aud_ctrl_list_list; + TempoList* _tempo_list; + AL::SigList* _sig_list; + KeyList* _key_list; + PartList* _part_list; + TrackList* _track_list; + MidiDeviceList* _midi_device_list; + MidiInstrumentList* _midi_instrument_list; + AuxSendValueList* _aux_send_value_list; + RouteList* _route_list; + float** _audioSamplesPointer; + }; + + union { + MidiInstrument* _midi_instrument; + MidiDevice* _midi_device; + Track* _track; + MidiCtrlValList* _mcvl; + CtrlList* _aud_ctrl_list; + TEvent* _tempo_event; + AL::SigEvent* _sig_event; + Route* _dst_route_pointer; + float* _newAudioSamples; + }; + + iPart _iPart; + Event _ev; + iEvent _iev; + iMidiCtrlVal _imcv; + iCtrl _iCtrl; + iCtrlList _iCtrlList; + iTEvent _iTEvent; + AL::iSigEvent _iSigEvent; + iKeyEvent _iKeyEvent; + iMidiInstrument _iMidiInstrument; + iMidiDevice _iMidiDevice; + iRoute _iRoute; + Route _src_route; + Route _dst_route; + + union { + int _intA; + bool _boolA; + const QString *_name; + double _aux_send_value; + int _insert_at; + int _from_idx; + int _address_client; + int _rw_flags; + int _frame; + int _newAudioSamplesLen; + //DrumMapOperation* _drum_map_operation; + DrumMapTrackOperation* _drum_map_track_operation; + DrumMapTrackPatchOperation* _drum_map_track_patch_operation; + DrumMapTrackPatchReplaceOperation* _drum_map_track_patch_replace_operation; + MidiCtrlValRemapOperation* _midi_ctrl_val_remap_operation; + }; + + union { + int _intB; + int _to_idx; + int _address_port; + int _open_flags; + int _ctl_num; + }; + + union { + int _intC; + int _ctl_val; + double _ctl_dbl_val; + }; + + PendingOperationItem(float** samples, float* new_samples, int* samples_len, int new_samples_len, + PendingOperationType type = ModifyAudioSamples) + { _type = type; _audioSamplesPointer = samples; _newAudioSamples = new_samples; + _audioSamplesLen = samples_len, _newAudioSamplesLen = new_samples_len; } + + // The operation is constructed and allocated in non-realtime before the call, then the controllers modified in realtime stage, + // then operation is deleted in non-realtime stage. + PendingOperationItem(MidiCtrlValRemapOperation* operation, PendingOperationType type = RemapDrumControllers) + { _type = type; _midi_ctrl_val_remap_operation = operation; } + + // The operation is constructed and allocated in non-realtime before the call, then the track's map is modified in realtime stage, + // then operation is deleted in non-realtime stage. + PendingOperationItem(DrumMapTrackOperation* operation, PendingOperationType type = ModifyTrackDrumMapItem) + { _type = type; _drum_map_track_operation = operation; } + + // The operation is constructed and allocated in non-realtime before the call, then the track's map is modified in realtime stage, + // then operation is deleted in non-realtime stage. + PendingOperationItem(DrumMapTrackPatchReplaceOperation* operation, PendingOperationType type = ReplaceTrackDrumMapPatchList) + { _type = type; _drum_map_track_patch_replace_operation = operation; } + + PendingOperationItem(MidiPort* mp, PendingOperationType type = UpdateDrumMaps) + { _type = type; _midi_port = mp; } + + PendingOperationItem(TrackList* tl, PendingOperationType type = UpdateSoloStates) + { _type = type; _track_list = tl; } + + // TODO: Try to break this operation down so that only the actual operation is executed stage-2. + PendingOperationItem(const Route& src_route, const Route& dst_route, PendingOperationType type) // Type is AddRoute or DeleteRoute. + { _type = type; _src_route = src_route; _dst_route = dst_route; } + + PendingOperationItem(RouteList* route_list, const Route& route, PendingOperationType type = AddRouteNode) + { _type = type; _route_list = route_list; _src_route = route; } + + PendingOperationItem(RouteList* route_list, const iRoute& ir, PendingOperationType type = DeleteRouteNode) + { _type = type; _route_list = route_list; _iRoute = ir; } + + PendingOperationItem(const Route& src_route, Route* dst_route, PendingOperationType type = ModifyRouteNode) + { _type = type; _src_route = src_route; _dst_route_pointer = dst_route; } + + PendingOperationItem(AuxSendValueList* asvl, double val, PendingOperationType type = AddAuxSendValue) + { _type = type; _aux_send_value_list = asvl; _aux_send_value = val; } + + PendingOperationItem(MidiInstrumentList* mil, MidiInstrument* midi_instrument, PendingOperationType type = AddMidiInstrument) + { _type = type; _midi_instrument_list = mil; _midi_instrument = midi_instrument; } + + PendingOperationItem(MidiInstrumentList* mil, const iMidiInstrument& imi, PendingOperationType type = DeleteMidiInstrument) + { _type = type; _midi_instrument_list = mil; _iMidiInstrument = imi; } + + PendingOperationItem(MidiInstrumentList* mil, const iMidiInstrument& imi, MidiInstrument* new_instrument, + PendingOperationType type = ReplaceMidiInstrument) + { _type = type; _midi_instrument_list = mil; _iMidiInstrument = imi; _midi_instrument = new_instrument; } + + PendingOperationItem(MidiDeviceList* mdl, MidiDevice* midi_device, PendingOperationType type = AddMidiDevice) + { _type = type; _midi_device_list = mdl; _midi_device = midi_device; } + + PendingOperationItem(MidiDeviceList* mdl, const iMidiDevice& imd, PendingOperationType type = DeleteMidiDevice) + { _type = type; _midi_device_list = mdl; _iMidiDevice = imd; } + + // Type is ModifyMidiDeviceAddress or ModifyMidiDeviceFlags + PendingOperationItem(MidiDevice* midi_device, int address_client_or_rw_flags, int address_port_or_open_flags, PendingOperationType type) + { _type = type; _midi_device = midi_device; _intA = address_client_or_rw_flags; _intB = address_port_or_open_flags; } + + PendingOperationItem(MidiDevice* midi_device, const QString* new_name, PendingOperationType type = ModifyMidiDeviceName) + { _type = type; _midi_device = midi_device; _name = new_name; } + + + PendingOperationItem(TrackList* tl, Track* track, int insert_at, PendingOperationType type = AddTrack, void* sec_track_list = 0) + { _type = type; _track_list = tl; _track = track; _insert_at = insert_at; _void_track_list = sec_track_list; } + + PendingOperationItem(TrackList* tl, Track* track, PendingOperationType type = DeleteTrack, void* sec_track_list = 0) + { _type = type; _track_list = tl; _track = track; _void_track_list = sec_track_list; } + + PendingOperationItem(TrackList* tl, int from_idx, int to_idx, PendingOperationType type = MoveTrack) + { _type = type; _track_list = tl; _from_idx = from_idx; _to_idx = to_idx; } + + PendingOperationItem(Track* track, const QString* new_name, PendingOperationType type = ModifyTrackName) + { _type = type; _track = track; _name = new_name; } + + // type is SetTrackRecord, SetTrackMute, SetTrackSolo, SetTrackRecMonitor, SetTrackOff + PendingOperationItem(Track* track, bool v, PendingOperationType type) + { _type = type; _track = track; _boolA = v; } + + + PendingOperationItem(Part* part, const QString* new_name, PendingOperationType type = ModifyPartName) + { _type = type; _part = part; _name = new_name; } + + // new_len must already be in the part's time domain (ticks or frames). + PendingOperationItem(Part* part, int new_len, PendingOperationType type = ModifyPartLength) + { _type = type; _part = part; _intA = new_len; } + + // Erases ip from part->track()->parts(), then adds part to new_track. NOTE: ip may be part->track()->parts()->end(). + // new_pos must already be in the part's time domain (ticks or frames). + PendingOperationItem(iPart ip, Part* part, int new_pos, PendingOperationType type = MovePart, Track* new_track = 0) + { _type = type; _iPart = ip; _part = part; _track = new_track; _intA = new_pos;} + + PendingOperationItem(PartList* pl, Part* part, PendingOperationType type = AddPart) + { _type = type; _part_list = pl; _part = part; } + + PendingOperationItem(PartList* pl, const iPart& ip, PendingOperationType type = DeletePart) + { _type = type; _part_list = pl; _iPart = ip; } + + + PendingOperationItem(Part* part, const Event& ev, PendingOperationType type = AddEvent) + { _type = type; _part = part; _ev = ev; } + + // NOTE: To avoid possibly deleting the event in RT stage 2 when the event is erased from the list, + // _ev is used simply to hold a reference until non-RT stage 3 or after, when the list is cleared. + PendingOperationItem(Part* part, const iEvent& iev, PendingOperationType type = DeleteEvent) + { _type = type; _part = part; _iev = iev; _ev = iev->second; } + + + PendingOperationItem(MidiCtrlValListList* mcvll, MidiCtrlValList* mcvl, int channel, int control_num, PendingOperationType type = AddMidiCtrlValList) + { _type = type; _mcvll = mcvll; _mcvl = mcvl; _intA = channel; _intB = control_num; } + + PendingOperationItem(MidiCtrlValList* mcvl, Part* part, int tick, int val, PendingOperationType type = AddMidiCtrlVal) + { _type = type; _mcvl = mcvl; _part = part; _intA = tick; _intB = val; } + + PendingOperationItem(MidiCtrlValList* mcvl, const iMidiCtrlVal& imcv, PendingOperationType type = DeleteMidiCtrlVal) + { _type = type; _mcvl = mcvl; _imcv = imcv; } + + // NOTE: mcvl is supplied in case the operation needs to be merged, or transformed into an AddMidiCtrlVal. + PendingOperationItem(MidiCtrlValList* mcvl, const iMidiCtrlVal& imcv, int val, PendingOperationType type = ModifyMidiCtrlVal) + { _type = type; _mcvl = mcvl; _imcv = imcv; _intA = val; } + + + PendingOperationItem(const iCtrlList& ictl_l, CtrlList* ctrl_l, PendingOperationType type = ModifyAudioCtrlValList) + { _type = type; _iCtrlList = ictl_l; _aud_ctrl_list = ctrl_l; } + + PendingOperationItem(CtrlList* ctrl_l, int frame, double ctrl_val, PendingOperationType type = AddAudioCtrlVal) + { _type = type; _aud_ctrl_list = ctrl_l; _frame = frame; _ctl_dbl_val = ctrl_val; } + + PendingOperationItem(CtrlList* ctrl_l, const iCtrl& ictl, PendingOperationType type = DeleteAudioCtrlVal) + { _type = type; _aud_ctrl_list = ctrl_l; _iCtrl = ictl; } + + // NOTE: ctrl_l is supplied in case the operation needs to be merged, or transformed into an AddAudioCtrlVal. + PendingOperationItem(CtrlList* ctrl_l, const iCtrl& ictl, int new_frame, double new_ctrl_val, PendingOperationType type = ModifyAudioCtrlVal) + { _type = type; _aud_ctrl_list = ctrl_l; _iCtrl = ictl; _frame = new_frame; _ctl_dbl_val = new_ctrl_val; } + + + // NOTE: 'tick' is the desired tick. te is a new TEvent with tempo and (same) desired tick. Swapping with NEXT event is done. + PendingOperationItem(TempoList* tl, TEvent* te, int tick, PendingOperationType type = AddTempo) + { _type = type; _tempo_list = tl; _tempo_event = te; _intA = tick; } + + // NOTE: _tempo_event is required. We must erase 'ite' in stage 2, then delete the TEvent* in stage 3 (not stage 1), + // so 'ite' is unavailable to fetch the TEvent* from it (in ite->second). + PendingOperationItem(TempoList* tl, const iTEvent& ite, PendingOperationType type = DeleteTempo) + { _type = type; _tempo_list = tl; _iTEvent = ite; _tempo_event = ite->second; } + + PendingOperationItem(TempoList* tl, const iTEvent& ite, int tempo, PendingOperationType type = ModifyTempo) + { _type = type; _tempo_list = tl; _iTEvent = ite; _intA = tempo; } + + PendingOperationItem(TempoList* tl, int tempo, PendingOperationType type = SetGlobalTempo) + { _type = type; _tempo_list = tl; _intA = tempo; } + + + // NOTE: 'tick' is the desired tick. se is a new SigEvent with sig and (same) desired tick. Swapping with NEXT event is done. + PendingOperationItem(AL::SigList* sl, AL::SigEvent* se, int tick, PendingOperationType type = AddSig) + { _type = type; _sig_list = sl; _sig_event = se; _intA = tick; } + + // NOTE: _sig_event is required. We must erase 'ise' in stage 2, then delete the SigEvent* in stage 3 (not stage 1), + // so 'ise' is unavailable to fetch the SigEvent* from it (in ise->second). + PendingOperationItem(AL::SigList* sl, const AL::iSigEvent& ise, PendingOperationType type = DeleteSig) + { _type = type; _sig_list = sl; _iSigEvent = ise; _sig_event = ise->second; } + + PendingOperationItem(AL::SigList* sl, const AL::iSigEvent& ise, const AL::TimeSignature& s, PendingOperationType type = ModifySig) + { _type = type; _sig_list = sl; _iSigEvent = ise; _intA = s.z; _intB = s.n; } + + + // NOTE: 'tick' is the desired tick. ke is a new SigEvent with sig and (same) desired tick. Swapping with NEXT event is done. + PendingOperationItem(KeyList* kl, key_enum ke, int tick, PendingOperationType type = AddKey) + { _type = type; _key_list = kl; _intA = tick; _intB = ke; } + + PendingOperationItem(KeyList* kl, const iKeyEvent& ike, PendingOperationType type = DeleteKey) + { _type = type; _key_list = kl; _iKeyEvent = ike; } + + PendingOperationItem(KeyList* kl, const iKeyEvent& ike, key_enum ke, PendingOperationType type = ModifyKey) + { _type = type; _key_list = kl; _iKeyEvent = ike; _intA = ke; } + + PendingOperationItem(int len, PendingOperationType type = ModifySongLength) + { _type = type; _intA = len; } + + PendingOperationItem(PendingOperationType type) // type is EnableAllAudioControllers (so far). + { _type = type; } + + PendingOperationItem() + { _type = Uninitialized; } + + // Execute the operation. Called only from RT stage 2. + SongChangedFlags_t executeRTStage(); + // Execute the operation. Called only from post RT stage 3. + SongChangedFlags_t executeNonRTStage(); + // Get an appropriate indexing value from ops like AddEvent that use it. Other ops like AddMidiCtrlValList return their type (rather than say, zero). + int getIndex() const; + // Whether the two special allocating ops (like AddMidiCtrlValList) are the same. + // The comparison ignores the actual allocated value, so that such commands can be found before they do their allocating. + bool isAllocationOp(const PendingOperationItem&) const; +}; + +class PendingOperationList : public std::list +{ + private: + // Holds sorted version of list. Index is time value for items which have it like events or parts, + // otherwise it is the operation type for other items. It doesn't matter too much that ticks and frames + // are mixed here, sorting by time is just to speed up searches, we look for operation types. + std::multimap > _map; + // Accumulated song changed flags. + SongChangedFlags_t _sc_flags; + + public: + PendingOperationList() : _sc_flags(0) { } + // Add an operation. Returns false if already exists, otherwise true. Optimizes all added items (merge, discard, alter, embellish etc.) + bool add(PendingOperationItem); + // Execute the RT portion of the operations contained in the list. Called only from RT stage 2. + SongChangedFlags_t executeRTStage(); + // Execute the Non-RT portion of the operations contained in the list. Called only from post RT stage 3. + SongChangedFlags_t executeNonRTStage(); + // Clear both the list and the map, and flags. + void clear(); + // Returns the accumulated song changed flags. + SongChangedFlags_t flags() const { return _sc_flags; } + // Find an existing special allocation command (like AddMidiCtrlValList). + // The comparison ignores the actual allocated value, so that such commands can be found before they do their allocating. + iterator findAllocationOp(const PendingOperationItem& op); +}; + +typedef PendingOperationList::iterator iPendingOperation; +typedef std::multimap >::iterator iPendingOperationSorted; +typedef std::multimap >::reverse_iterator riPendingOperationSorted; +typedef std::pair iPendingOperationSortedRange; + +} // namespace MusECore + +#endif diff -Nru muse-2.1.2/muse/osc.cpp muse-3.0.2+ds1/muse/osc.cpp --- muse-2.1.2/muse/osc.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/osc.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -89,11 +89,11 @@ static int oscDebugHandler(const char* path, const char* types, lo_arg** argv, int argc, void*, void*) { - printf("MusE: got unhandled OSC message:\n path: <%s>\n", path); + fprintf(stderr, "MusE: got unhandled OSC message:\n path: <%s>\n", path); for (int i = 0; i < argc; i++) { - printf(" arg %d '%c' ", i, types[i]); + fprintf(stderr, " arg %d '%c' ", i, types[i]); lo_arg_pp(lo_type(types[i]), argv[i]); - printf("\n"); + fprintf(stderr, "\n"); } return 1; } @@ -113,23 +113,25 @@ #ifdef OSC_DEBUG if(argc) { - printf("oscMessageHandler: path:%s argc:%d\n", path, argc); + fprintf(stderr, "oscMessageHandler: path:%s argc:%d\n", path, argc); for(int i = 0; i < argc; ++i) { - printf(" "); + fprintf(stderr, " "); lo_arg_pp((lo_type)types[i], argv[i]); } - printf("\n"); + fprintf(stderr, "\n"); } else { - printf("%s\n", path); - printf("oscMessageHandler: no args, path:%s\n", path); + fprintf(stderr, "%s\n", path); + fprintf(stderr, "oscMessageHandler: no args, path:%s\n", path); } #endif + #if defined(DSSI_SUPPORT) || defined(OSC_DEBUG) bool isSynth = false; - + #endif + #ifdef DSSI_SUPPORT if(strncmp(p, "/dssi_synth/", 12) == 0) { @@ -293,7 +295,7 @@ serverThread = lo_server_thread_new(0, oscError); if(!serverThread) { - printf("initOSC() Failed to create OSC server!\n"); + fprintf(stderr, "initOSC() Failed to create OSC server!\n"); return; } } @@ -302,7 +304,7 @@ if(!url) { lo_server_thread_free(serverThread); - printf("initOSC() Failed to get OSC server thread url !\n"); + fprintf(stderr, "initOSC() Failed to get OSC server thread url !\n"); return; } @@ -310,7 +312,7 @@ meth = lo_server_thread_add_method(serverThread, 0, 0, oscMessageHandler, 0); if(!meth) { - printf("initOSC() Failed to add oscMessageHandler method to OSC server!\n"); + fprintf(stderr, "initOSC() Failed to add oscMessageHandler method to OSC server!\n"); // Does not return a value. lo_server_thread_free(serverThread); serverThread = 0; @@ -319,6 +321,10 @@ return; } + #ifdef OSC_DEBUG + fprintf(stderr, "initOSC() url:%s\n", url); + #endif + // Does not return a value. lo_server_thread_start(serverThread); } @@ -395,10 +401,10 @@ #ifdef _USE_QPROCESS_FOR_GUI_ if(_oscGuiQProc) { - if(_oscGuiQProc->state()) + if(_oscGuiQProc->state() != QProcess::NotRunning) { #ifdef OSC_DEBUG - printf("OscIF::~OscIF terminating _oscGuiQProc\n"); + fprintf(stderr, "OscIF::~OscIF terminating _oscGuiQProc\n"); #endif _oscGuiQProc->terminate(); @@ -493,19 +499,19 @@ */ #ifdef OSC_DEBUG - printf("OscIF::oscUpdate: _uiOscTarget:%p\n", _uiOscTarget); + fprintf(stderr, "OscIF::oscUpdate: _uiOscTarget:%p\n", _uiOscTarget); if(url) - printf(" server url:%s\n", url); + fprintf(stderr, " server url:%s\n", url); else - printf(" no server url!\n"); - printf(" update path:%s\n", purl); - printf(" _uiOscPath:%s\n", _uiOscPath); - printf(" _uiOscSampleRatePath:%s\n", _uiOscSampleRatePath); - printf(" _uiOscConfigurePath:%s\n", _uiOscConfigurePath); - printf(" _uiOscProgramPath:%s\n", _uiOscProgramPath); - printf(" _uiOscControlPath:%s\n",_uiOscControlPath); - printf(" _uiOscShowPath:%s\n", _uiOscShowPath); - printf(" museProject:%s\n", MusEGlobal::museProject.toLatin1().constData()); + fprintf(stderr, " no server url!\n"); + fprintf(stderr, " update path:%s\n", purl); + fprintf(stderr, " _uiOscPath:%s\n", _uiOscPath); + fprintf(stderr, " _uiOscSampleRatePath:%s\n", _uiOscSampleRatePath); + fprintf(stderr, " _uiOscConfigurePath:%s\n", _uiOscConfigurePath); + fprintf(stderr, " _uiOscProgramPath:%s\n", _uiOscProgramPath); + fprintf(stderr, " _uiOscControlPath:%s\n",_uiOscControlPath); + fprintf(stderr, " _uiOscShowPath:%s\n", _uiOscShowPath); + fprintf(stderr, " museProject:%s\n", MusEGlobal::museProject.toLatin1().constData()); #endif // Send sample rate. @@ -520,7 +526,7 @@ /* #ifdef DSSI_SUPPORT //lo_send(_uiOscTarget, _uiOscConfigurePath, "ss", - //DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::song->projectPath().toAscii().data()); + //DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::song->projectPath().toLatin1().data()); lo_send(_uiOscTarget, _uiOscConfigurePath, "ss", DSSI_PROJECT_DIRECTORY_KEY, museProject.toLatin1().constData()); @@ -594,12 +600,13 @@ _oscGuiVisible = false; // DELETETHIS 52 -// Just an attempt to really kill the process, an attempt to fix gui not re-showing after closing. Doesn't help. +// Just an attempt to really kill the process, an attempt to fix dssi-vst gui +// not re-showing after closing. Doesn't help. It's a design problem with dssi-vst. /* #ifdef _USE_QPROCESS_FOR_GUI_ if(_oscGuiQProc) { - if(_oscGuiQProc->state()) + if(_oscGuiQProc->state() != QProcess::NotRunning) { #ifdef OSC_DEBUG printf("OscIF::oscExiting terminating _oscGuiQProc\n"); @@ -802,7 +809,7 @@ if (maxDssiPort!=nDssiPorts) { // this should never happen, right? - printf("STRANGE: nDssiPorts has changed (old=%lu, now=%lu)!\n", maxDssiPort, nDssiPorts); + fprintf(stderr, "STRANGE: nDssiPorts has changed (old=%lu, now=%lu)!\n", maxDssiPort, nDssiPorts); delete [] old_control; old_control=new float[nDssiPorts]; for (unsigned long i=0;istate())) - return true; + if((_oscGuiQProc != 0) && (_oscGuiQProc->state() != QProcess::NotRunning)) + return false; #else if(_guiPid != -1) - return true; + return false; #endif #ifdef OSC_DEBUG @@ -858,12 +865,18 @@ #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscInitGui starting QProcess\n"); #endif + _oscGuiQProc->start(program, arguments); - - if(_oscGuiQProc->state()) + + if(_oscGuiQProc->waitForStarted(10000)) // 10 secs. { #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscInitGui started QProcess\n"); + fprintf(stderr, "guiPath:%s oscUrl:%s filePath:%s name:%s\n", + guiPath.toLatin1().constData(), + oscUrl.toLatin1().constData(), + filePath.toLatin1().constData(), + name.toLatin1().constData()); #endif } else @@ -874,6 +887,7 @@ filePath.toLatin1().constData(), name.toLatin1().constData(), strerror(errno)); + return false; } #ifdef OSC_DEBUG @@ -908,6 +922,7 @@ name.toLatin1().constData(), strerror(errno)); //exit(1); + return false; } #endif // _USE_QPROCESS_FOR_GUI_ @@ -923,14 +938,14 @@ void OscIF::oscShowGui(bool v) { #ifdef OSC_DEBUG - printf("OscIF::oscShowGui(): v:%d visible:%d\n", v, oscGuiVisible()); + fprintf(stderr, "OscIF::oscShowGui(): v:%d visible:%d\n", v, oscGuiVisible()); #endif if (v == oscGuiVisible()) return; #ifdef _USE_QPROCESS_FOR_GUI_ - if((_oscGuiQProc == 0) || (!_oscGuiQProc->state())) + if((_oscGuiQProc == 0) || (_oscGuiQProc->state() == QProcess::NotRunning)) #else if(_guiPid == -1) #endif @@ -942,23 +957,23 @@ _uiOscPath = 0; #ifdef OSC_DEBUG - printf("OscIF::oscShowGui(): No QProcess or process not running. Starting gui...\n"); + fprintf(stderr, "OscIF::oscShowGui(): No QProcess or process not running. Starting gui...\n"); #endif if(!oscInitGui()) { - printf("OscIF::oscShowGui(): failed to initialize gui on oscInitGui()\n"); + fprintf(stderr, "OscIF::oscShowGui(): failed to initialize gui on oscInitGui()\n"); return; } } - for (int i = 0; i < 20; ++i) { + for (int i = 0; i < 10; ++i) { if (_uiOscPath) break; sleep(1); } if (_uiOscPath == 0) { - printf("OscIF::oscShowGui(): no _uiOscPath. Error: Timeout - synth gui did not start within 20 seconds.\n"); + fprintf(stderr, "OscIF::oscShowGui(): no _uiOscPath. Error: Timeout - synth gui did not start within 10 seconds.\n"); return; } @@ -966,7 +981,7 @@ sprintf(uiOscGuiPath, "%s/%s", _uiOscPath, v ? "show" : "hide"); #ifdef OSC_DEBUG - printf("OscIF::oscShowGui(): Sending show/hide uiOscGuiPath:%s\n", uiOscGuiPath); + fprintf(stderr, "OscIF::oscShowGui(): Sending show/hide uiOscGuiPath:%s\n", uiOscGuiPath); #endif lo_send(_uiOscTarget, uiOscGuiPath, ""); diff -Nru muse-2.1.2/muse/part.cpp muse-3.0.2+ds1/muse/part.cpp --- muse-2.1.2/muse/part.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/part.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -22,6 +22,7 @@ // //========================================================= +#include #include #include @@ -34,321 +35,104 @@ #include "wave.h" #include "midiport.h" #include "drummap.h" +#include "midictrl.h" +#include "operations.h" namespace MusECore { int Part::snGen=0; -//--------------------------------------------------------- -// unchainClone -//--------------------------------------------------------- -void unchainClone(Part* p) +void Part::unchainClone() { - chainCheckErr(p); + chainCheckErr(this); // FIXME proper assert! - // Unchain the part. - p->prevClone()->setNextClone(p->nextClone()); - p->nextClone()->setPrevClone(p->prevClone()); + if (_backupClone) printf("THIS SHOULD NEVER HAPPEN: Part::unchainClone() called, but _backupClone was non-NULL\n"); - // Isolate the part. - p->setPrevClone(p); - p->setNextClone(p); -} - -//--------------------------------------------------------- -// chainClone -// The quick way - if part to chain to is known... -//--------------------------------------------------------- - -void chainClone(Part* p1, Part* p2) -{ - chainCheckErr(p1); + _backupClone=_prevClone; - // Make sure the part to be chained is unchained first. - p2->prevClone()->setNextClone(p2->nextClone()); - p2->nextClone()->setPrevClone(p2->prevClone()); + // Unchain the part. + _prevClone->_nextClone = _nextClone; + _nextClone->_prevClone = _prevClone; - // Link the part to be chained. - p2->setPrevClone(p1); - p2->setNextClone(p1->nextClone()); + // Isolate the part. + _prevClone = this; + _nextClone = this; - // Re-link the existing part. - p1->nextClone()->setPrevClone(p2); - p1->setNextClone(p2); + _clonemaster_sn = this->_sn; } -//--------------------------------------------------------- -// chainCloneInternal -// No error check, so it can be called by replaceClone() -//--------------------------------------------------------- - -void chainCloneInternal(Part* p) +void Part::chainClone(Part* p) { - Track* t = p->track(); - Part* p1 = 0; + // FIXME assertion + assert(p); - // Look for a part with the same event list, that we can chain to. - // It's faster if track type is known... - - if(!t || (t && t->isMidiTrack())) - { - MidiTrack* mt = 0; - MidiTrackList* mtl = MusEGlobal::song->midis(); - for(ciMidiTrack imt = mtl->begin(); imt != mtl->end(); ++imt) - { - mt = *imt; - const PartList* pl = mt->cparts(); - for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) - { - if(ip->second != p && ip->second->cevents() == p->cevents()) - { - p1 = ip->second; - break; - } - } - // If a suitable part was found on a different track, we're done. We will chain to it. - // Otherwise keep looking for parts on another track. If no others found, then we - // chain to any suitable part which was found on the same given track t. - if(p1 && mt != t) - break; - } - } - if((!p1 && !t) || (t && t->type() == Track::WAVE)) + if (! (_prevClone==this && _nextClone==this)) // the part is still part of a clone chain! { - MusECore::WaveTrack* wt = 0; - MusECore::WaveTrackList* wtl = MusEGlobal::song->waves(); - for(MusECore::ciWaveTrack iwt = wtl->begin(); iwt != wtl->end(); ++iwt) - { - wt = *iwt; - const PartList* pl = wt->cparts(); - for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) - { - if(ip->second != p && ip->second->cevents() == p->cevents()) - { - p1 = ip->second; - break; - } - } - if(p1 && wt != t) - break; - } + printf("ERROR: THIS SHOULD NEVER HAPPEN: Part::chainClone() called, but part is already chained! I'll unchain for now, but better fix that!\n"); + this->unchainClone(); } + + // Make our links to the chain + this->_prevClone = p; + this->_nextClone = p->_nextClone; - // No part found with same event list? Done. - if(!p1) - return; - - // Make sure the part to be chained is unchained first. - p->prevClone()->setNextClone(p->nextClone()); - p->nextClone()->setPrevClone(p->prevClone()); + // Make the chain's links to us + this->_nextClone->_prevClone = this; + p->_nextClone = this; - // Link the part to be chained. - p->setPrevClone(p1); - p->setNextClone(p1->nextClone()); + // we only chain clones. we must trust in the GUI thread that the eventlist is consistent. - // Re-link the existing part. - p1->nextClone()->setPrevClone(p); - p1->setNextClone(p); + this->_clonemaster_sn = p->_sn; } -//--------------------------------------------------------- -// chainClone -// The slow way - if part to chain to is not known... -//--------------------------------------------------------- - -void chainClone(Part* p) +void Part::rechainClone() { - chainCheckErr(p); - chainCloneInternal(p); + if(_backupClone) + { + this->chainClone(_backupClone); + _backupClone = NULL; + } } -//--------------------------------------------------------- -// replaceClone -//--------------------------------------------------------- +bool Part::isCloneOf(const Part* other) const +{ + return this->_clonemaster_sn == other->_clonemaster_sn; +} -// this replaces p1 by p2. p1 is isolated, and p2 takes its place instead. -void replaceClone(Part* p1, Part* p2) +int Part::nClones() const { - chainCheckErr(p1); - - // Make sure the replacement part is unchained first. - p2->prevClone()->setNextClone(p2->nextClone()); - p2->nextClone()->setPrevClone(p2->prevClone()); - - // If the two parts share the same event list, then this MUST - // be a straight forward replacement operation. Continue on. - // If not, and either part has more than one ref count, then do this... - if(p1->cevents() != p2->cevents()) - { - bool ret = false; - // If the part to be replaced is a single uncloned part, [DELETETHIS 4 this seems outdated=wrong to me] - // and the replacement part is not, then this operation - // MUST be an undo of a de-cloning of a cloned part. - //if(p1->cevents()->refCount() <= 1 && p2->cevents()->refCount() > 1) - if(p2->cevents()->refCount() > 1) - { - // Chain the replacement part. We don't know the chain it came from, - // so we use the slow method. - chainCloneInternal(p2); - //return; DELETETHIS - ret = true; - } - - // If the replacement part is a single uncloned part, DELETETHIS same as above - // and the part to be replaced is not, then this operation - // MUST be a de-cloning of a cloned part. - //if(p1->cevents()->refCount() > 1 && p2->cevents()->refCount() <= 1) - if(p1->cevents()->refCount() > 1) - { - // Unchain the part to be replaced. - p1->prevClone()->setNextClone(p1->nextClone()); - p1->nextClone()->setPrevClone(p1->prevClone()); - // Isolate the part. - p1->setPrevClone(p1); - p1->setNextClone(p1); - ret = true; - } - - // Was the operation handled? - if(ret) - return; - // Note that two parts here with different event lists, each with more than one - // reference count, would be an error. It's not done anywhere in muse. But just - // to be sure, four lines above were changed to allow that condition. - // If each of the two different event lists, has only one ref count, we - // handle it like a regular replacement, below... - } - - // If the part to be replaced is a clone not a single lone part, re-link its neighbours to the replacement part... - if(p1->prevClone() != p1) - { - p1->prevClone()->setNextClone(p2); - p2->setPrevClone(p1->prevClone()); - } - else - p2->setPrevClone(p2); - - if(p1->nextClone() != p1) - { - p1->nextClone()->setPrevClone(p2); - p2->setNextClone(p1->nextClone()); - } - else - p2->setNextClone(p2); - - // Isolate the replaced part. - p1->setNextClone(p1); - p1->setPrevClone(p1); + int n=1; + + for(const Part* it = this->_nextClone; it!=this; it=it->_nextClone) + n++; + + return n; } + +// FIXME FINDMICHJETZT TODO: weg damit! + //--------------------------------------------------------- // unchainTrackParts //--------------------------------------------------------- -void unchainTrackParts(Track* t, bool decRefCount) +void unchainTrackParts(Track* t) { PartList* pl = t->parts(); for(iPart ip = pl->begin(); ip != pl->end(); ++ip) - { - Part* p = ip->second; - chainCheckErr(p); - - // Do we want to decrease the reference count? - if(decRefCount) - p->events()->incARef(-1); - - // Unchain the part. - p->prevClone()->setNextClone(p->nextClone()); - p->nextClone()->setPrevClone(p->prevClone()); - - // Isolate the part. - p->setPrevClone(p); - p->setNextClone(p); - } + ip->second->unchainClone(); } //--------------------------------------------------------- // chainTrackParts //--------------------------------------------------------- -void chainTrackParts(Track* t, bool incRefCount) +void chainTrackParts(Track* t) { PartList* pl = t->parts(); - for(iPart ip = pl->begin(); ip != pl->end(); ++ip) - { - Part* p = ip->second; - chainCheckErr(p); - - // Do we want to increase the reference count? - if(incRefCount) - p->events()->incARef(1); - - Part* p1 = 0; - - // Look for a part with the same event list, that we can chain to. - // It's faster if track type is known... - - if(!t || (t && t->isMidiTrack())) - { - MidiTrack* mt = 0; - MidiTrackList* mtl = MusEGlobal::song->midis(); - for(ciMidiTrack imt = mtl->begin(); imt != mtl->end(); ++imt) - { - mt = *imt; - const PartList* pl = mt->cparts(); - for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) - { - if(ip->second != p && ip->second->cevents() == p->cevents()) - { - p1 = ip->second; - break; - } - } - // If a suitable part was found on a different track, we're done. We will chain to it. - // Otherwise keep looking for parts on another track. If no others found, then we - // chain to any suitable part which was found on the same given track t. - if(p1 && mt != t) - break; - } - } - if((!p1 && !t) || (t && t->type() == Track::WAVE)) - { - MusECore::WaveTrack* wt = 0; - MusECore::WaveTrackList* wtl = MusEGlobal::song->waves(); - for(MusECore::ciWaveTrack iwt = wtl->begin(); iwt != wtl->end(); ++iwt) - { - wt = *iwt; - const PartList* pl = wt->cparts(); - for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) - { - if(ip->second != p && ip->second->cevents() == p->cevents()) - { - p1 = ip->second; - break; - } - } - if(p1 && wt != t) - break; - } - } - - // No part found with same event list? Done. - if(!p1) - continue; - - // Make sure the part to be chained is unchained first. - p->prevClone()->setNextClone(p->nextClone()); - p->nextClone()->setPrevClone(p->prevClone()); - - // Link the part to be chained. - p->setPrevClone(p1); - p->setNextClone(p1->nextClone()); - - // Re-link the existing part. - p1->nextClone()->setPrevClone(p); - p1->setNextClone(p); - } + for(riPart ip = pl->rbegin(); ip != pl->rend(); ++ip) // walk through in opposite direction than we unchained them. + ip->second->rechainClone(); } //--------------------------------------------------------- @@ -364,65 +148,107 @@ printf("chainCheckErr: Prev clone:%s %p next clone:%s %p != %s %p\n", p->prevClone()->name().toLatin1().constData(), p->prevClone(), p->prevClone()->nextClone()->name().toLatin1().constData(), p->prevClone()->nextClone(), p->name().toLatin1().constData(), p); } -//--------------------------------------------------------- -// addPortCtrlEvents -//--------------------------------------------------------- - -void addPortCtrlEvents(Event& event, Part* part, bool doClones) +void addPortCtrlEvents(Event& event, Part* part) { - // Traverse and process the clone chain ring until we arrive at the same part again. - // The loop is a safety net. - // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, - // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. - Part* p = part; - while(1) + Track* t = part->track(); + if(t && t->isMidiTrack()) { - Track* t = p->track(); - if(t && t->isMidiTrack()) + MidiTrack* mt = (MidiTrack*)t; + MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; + int ch = mt->outChannel(); + unsigned len = part->lenTick(); + // Do not add events which are past the end of the part. + if(event.tick() < len) { - MidiTrack* mt = (MidiTrack*)t; - MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; - int ch = mt->outChannel(); - unsigned len = p->lenTick(); - - // Do not add events which are past the end of the part. - if(event.tick() >= len) - break; - if(event.type() == Controller) { - int tck = event.tick() + p->tick(); - int cntrl = event.dataA(); - int val = event.dataB(); + int tck = event.tick() + part->tick(); + int cntrl = event.dataA(); + int val = event.dataB(); + // Is it a drum controller event, according to the track port's instrument? + if(mt->type() == Track::DRUM) + { + MidiController* mc = mp->drumController(cntrl); + if(mc) + { + int note = cntrl & 0x7f; + cntrl &= ~0xff; + // Default to track port if -1 and track channel if -1. + if(MusEGlobal::drumMap[note].channel != -1) + ch = MusEGlobal::drumMap[note].channel; + if(MusEGlobal::drumMap[note].port != -1) + mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; + cntrl |= MusEGlobal::drumMap[note].anote; + } + } + mp->setControllerVal(ch, tck, cntrl, val, part); + } + } + } +} - // Is it a drum controller event, according to the track port's instrument? - if(mt->type() == Track::DRUM) - { - MidiController* mc = mp->drumController(cntrl); - if(mc) - { - int note = cntrl & 0x7f; - cntrl &= ~0xff; - // Default to track port if -1 and track channel if -1. - if(MusEGlobal::drumMap[note].channel != -1) - ch = MusEGlobal::drumMap[note].channel; - if(MusEGlobal::drumMap[note].port != -1) - mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; - cntrl |= MusEGlobal::drumMap[note].anote; - } - } +void addPortCtrlEvents(const Event& event, Part* part, unsigned int tick, unsigned int len, Track* track, PendingOperationList& ops) +{ + if(!track || !track->isMidiTrack()) + return; + // Do not add events which are past the end of the part. + if(event.tick() >= len) + return; + + MidiTrack* mt = (MidiTrack*)track; + MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; + int ch = mt->outChannel(); + + if(event.type() == Controller) + { + int tck = event.tick() + tick; + int cntrl = event.dataA(); + int val = event.dataB(); + // Is it a drum controller event, according to the track port's instrument? + if(mt->type() == Track::DRUM) + { + MidiController* mc = mp->drumController(cntrl); + if(mc) + { + int note = cntrl & 0x7f; + cntrl &= ~0xff; + // Default to track port if -1 and track channel if -1. + if(MusEGlobal::drumMap[note].channel != -1) + ch = MusEGlobal::drumMap[note].channel; + if(MusEGlobal::drumMap[note].port != -1) + mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; + cntrl |= MusEGlobal::drumMap[note].anote; + } + } - mp->setControllerVal(ch, tck, cntrl, val, p); + MidiCtrlValListList* mcvll = mp->controller(); + MidiCtrlValList* mcvl = NULL; + iMidiCtrlValList imcvll = mcvll->find(ch, cntrl); + if(imcvll == mcvll->end()) + { + PendingOperationItem poi(mcvll, 0, ch, cntrl, PendingOperationItem::AddMidiCtrlValList); + if(ops.findAllocationOp(poi) == ops.end()) + { + mcvl = new MidiCtrlValList(cntrl); + poi._mcvl = mcvl; + ops.add(poi); } } - - if(!doClones) - break; - // Get the next clone in the chain ring. - p = p->nextClone(); - // Same as original part? Finished. - if(p == part) - break; + else + { + mcvl = imcvll->second; + iMidiCtrlVal imcv = mcvl->findMCtlVal(tck, part); + if(imcv != mcvl->end()) + { + ops.add(PendingOperationItem(mcvl, imcv, val, PendingOperationItem::ModifyMidiCtrlVal)); + return; + } + } + //assert(mcvl != NULL); //FIXME: Can this happen? (danvd). UPDATE: Yes, it can (danvd) + if(mcvl != NULL) + { + ops.add(PendingOperationItem(mcvl, part, tck, val, PendingOperationItem::AddMidiCtrlVal)); + } } } @@ -434,8 +260,6 @@ { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. - // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, - // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. Part* p = part; while(1) { @@ -445,9 +269,8 @@ MidiTrack* mt = (MidiTrack*)t; MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; int ch = mt->outChannel(); - const EventList* el = p->cevents(); unsigned len = p->lenTick(); - for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + for(ciEvent ie = p->events().begin(); ie != p->events().end(); ++ie) { const Event& ev = ie->second; // Added by T356. Do not add events which are past the end of the part. @@ -476,7 +299,7 @@ cntrl |= MusEGlobal::drumMap[note].anote; } } - + mp->setControllerVal(ch, tck, cntrl, val, p); } } @@ -492,58 +315,98 @@ } //--------------------------------------------------------- -// removePortCtrlEvents +// addPortCtrlEvents //--------------------------------------------------------- -void removePortCtrlEvents(Event& event, Part* part, bool doClones) +void addPortCtrlEvents(Part* part, unsigned int tick, unsigned int len, Track* track, PendingOperationList& ops) { - // Traverse and process the clone chain ring until we arrive at the same part again. - // The loop is a safety net. - // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, - // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. - Part* p = part; - while(1) + if(!track || !track->isMidiTrack()) + return; + for(ciEvent ie = part->events().begin(); ie != part->events().end(); ++ie) { - Track* t = p->track(); - if(t && t->isMidiTrack()) - { - MidiTrack* mt = (MidiTrack*)t; - MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; - int ch = mt->outChannel(); - - if(event.type() == Controller) - { - int tck = event.tick() + p->tick(); - int cntrl = event.dataA(); + // Do not add events which are past the end of the part. + if(ie->second.tick() >= (unsigned int)len) + return; // Done + addPortCtrlEvents(ie->second, part, tick, len, track, ops); + } +} - // Is it a drum controller event, according to the track port's instrument? - if(mt->type() == Track::DRUM) - { - MidiController* mc = mp->drumController(cntrl); - if(mc) - { - int note = cntrl & 0x7f; - cntrl &= ~0xff; - // Default to track port if -1 and track channel if -1. - if(MusEGlobal::drumMap[note].channel != -1) - ch = MusEGlobal::drumMap[note].channel; - if(MusEGlobal::drumMap[note].port != -1) - mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; - cntrl |= MusEGlobal::drumMap[note].anote; - } - } +void removePortCtrlEvents(Event& event, Part* part) +{ + Track* t = part->track(); + if(t && t->isMidiTrack()) + { + MidiTrack* mt = (MidiTrack*)t; + MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; + int ch = mt->outChannel(); + if(event.type() == Controller) + { + int tck = event.tick() + part->tick(); + int cntrl = event.dataA(); + // Is it a drum controller event, according to the track port's instrument? + if(mt->type() == Track::DRUM) + { + MidiController* mc = mp->drumController(cntrl); + if(mc) + { + int note = cntrl & 0x7f; + cntrl &= ~0xff; + // Default to track port if -1 and track channel if -1. + if(MusEGlobal::drumMap[note].channel != -1) + ch = MusEGlobal::drumMap[note].channel; + if(MusEGlobal::drumMap[note].port != -1) + mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; + cntrl |= MusEGlobal::drumMap[note].anote; + } + } + mp->deleteController(ch, tck, cntrl, part); + } + } +} - mp->deleteController(ch, tck, cntrl, p); +void removePortCtrlEvents(const Event& event, Part* part, Track* track, PendingOperationList& ops) +{ + if(!track || !track->isMidiTrack()) + return; + + MidiTrack* mt = (MidiTrack*)track; + MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; + int ch = mt->outChannel(); + if(event.type() == Controller) + { + int tck = event.tick() + part->tick(); + int cntrl = event.dataA(); + // Is it a drum controller event, according to the track port's instrument? + if(mt->type() == Track::DRUM) + { + MidiController* mc = mp->drumController(cntrl); + if(mc) + { + int note = cntrl & 0x7f; + cntrl &= ~0xff; + // Default to track port if -1 and track channel if -1. + if(MusEGlobal::drumMap[note].channel != -1) + ch = MusEGlobal::drumMap[note].channel; + if(MusEGlobal::drumMap[note].port != -1) + mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; + cntrl |= MusEGlobal::drumMap[note].anote; } } - - if(!doClones) - break; - // Get the next clone in the chain ring. - p = p->nextClone(); - // Same as original part? Finished. - if(p == part) - break; + + MidiCtrlValListList* mcvll = mp->controller(); + iMidiCtrlValList cl = mcvll->find(ch, cntrl); + if (cl == mcvll->end()) { + fprintf(stderr, "removePortCtrlEvents: controller %d(0x%x) for channel %d not found size %zd\n", + cntrl, cntrl, ch, mcvll->size()); + return; + } + MidiCtrlValList* mcvl = cl->second; + iMidiCtrlVal imcv = mcvl->findMCtlVal(tck, part); + if (imcv == mcvl->end()) { + fprintf(stderr, "removePortCtrlEvents (%d): not found (size %zd)\n", tck, mcvl->size()); + return; + } + ops.add(PendingOperationItem(mcvl, imcv, PendingOperationItem::DeleteMidiCtrlVal)); } } @@ -555,8 +418,6 @@ { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. - // Update: Due to the varying calls, and order of, incARefcount, (msg)ChangePart, replaceClone, and remove/addPortCtrlEvents, - // we can not rely on the reference count as a safety net in these routines. We will just have to trust the clone chain. Part* p = part; while(1) { @@ -566,16 +427,9 @@ MidiTrack* mt = (MidiTrack*)t; MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; int ch = mt->outChannel(); - const EventList* el = p->cevents(); - //unsigned len = p->lenTick(); - for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + for(ciEvent ie = p->events().begin(); ie != p->events().end(); ++ie) { const Event& ev = ie->second; - // Added by T356. Do not remove events which are past the end of the part. DELETETHIS 5 - // No, actually, do remove ALL of them belonging to the part. - // Just in case there are stray values left after the part end. - //if(ev.tick() >= len) - // break; if(ev.type() == Controller) { @@ -598,7 +452,6 @@ cntrl |= MusEGlobal::drumMap[note].anote; } } - mp->deleteController(ch, tck, cntrl, p); } } @@ -614,23 +467,181 @@ } } +void removePortCtrlEvents(Part* part, Track* track, PendingOperationList& ops) +{ + if(!track || !track->isMidiTrack()) + return; + unsigned len = part->lenValue(); + for(ciEvent ie = part->events().begin(); ie != part->events().end(); ++ie) + { + // Do not attempt to remove events which are past the end of the part. + // They should never be added in the first place, and cause a benign error in the event function. + if(ie->second.posValue() >= len) + return; // Done + removePortCtrlEvents(ie->second, part, track, ops); + } +} + +void modifyPortCtrlEvents(const Event& old_event, const Event& event, Part* part, PendingOperationList& ops) +{ + Track* t = part->track(); + if(!t || !t->isMidiTrack()) + return; + if(old_event.type() != Controller || event.type() != Controller) + return; + MidiTrack* mt = static_cast(t); + + MidiPort* mp_erase = &MusEGlobal::midiPorts[mt->outPort()]; + MidiPort* mp_add = mp_erase; + int ch = mt->outChannel(); + + int tck_erase = old_event.tick() + part->tick(); + int cntrl_erase = old_event.dataA(); + iMidiCtrlVal imcv_erase; + bool found_erase = false; + // Is it a drum controller old_event, according to the track port's instrument? + if(mt->type() == Track::DRUM) + { + MidiController* mc = mp_erase->drumController(cntrl_erase); + if(mc) + { + int note = cntrl_erase & 0x7f; + cntrl_erase &= ~0xff; + // Default to track port if -1 and track channel if -1. + if(MusEGlobal::drumMap[note].channel != -1) + ch = MusEGlobal::drumMap[note].channel; + if(MusEGlobal::drumMap[note].port != -1) + mp_erase = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; + cntrl_erase |= MusEGlobal::drumMap[note].anote; + } + } + + MidiCtrlValListList* mcvll_erase = mp_erase->controller(); + MidiCtrlValList* mcvl_erase = 0; + iMidiCtrlValList cl_erase = mcvll_erase->find(ch, cntrl_erase); + if(cl_erase == mcvll_erase->end()) + { + if(MusEGlobal::debugMsg) + printf("deleteController: controller %d(0x%x) for channel %d not found size %zd\n", + cntrl_erase, cntrl_erase, ch, mcvll_erase->size()); + } + else + { + mcvl_erase = cl_erase->second; + imcv_erase = mcvl_erase->findMCtlVal(tck_erase, part); + if(imcv_erase == mcvl_erase->end()) + { + if(MusEGlobal::debugMsg) + printf("MidiCtrlValList::delMCtlVal(%d): not found (size %zd)\n", tck_erase, mcvl_erase->size()); + } + else + found_erase = true; + } + + + unsigned len = part->lenTick(); + // Do not add events which are past the end of the part. + if(event.tick() < len) + { + int tck_add = event.tick() + part->tick(); + int cntrl_add = event.dataA(); + int val_add = event.dataB(); + // Is it a drum controller event, according to the track port's instrument? + if(mt->type() == Track::DRUM) + { + MidiController* mc_add = mp_add->drumController(cntrl_add); + if(mc_add) + { + int note = cntrl_add & 0x7f; + cntrl_add &= ~0xff; + // Default to track port if -1 and track channel if -1. + if(MusEGlobal::drumMap[note].channel != -1) + ch = MusEGlobal::drumMap[note].channel; + if(MusEGlobal::drumMap[note].port != -1) + mp_add = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; + cntrl_add |= MusEGlobal::drumMap[note].anote; + } + } + else if(mt->type() == Track::NEW_DRUM) + { + MidiController* mc_add = mp_add->drumController(cntrl_add); + if(mc_add) + { + int note = cntrl_add & 0x7f; + cntrl_add &= ~0xff; + // Default to track port if -1 and track channel if -1. + if(mt->drummap()[note].channel != -1) + ch = mt->drummap()[note].channel; + if(mt->drummap()[note].port != -1) + mp_add = &MusEGlobal::midiPorts[mt->drummap()[note].port]; + cntrl_add |= mt->drummap()[note].anote; + } + } + + MidiCtrlValList* mcvl_add; + MidiCtrlValListList* mcvll_add = mp_add->controller(); + iMidiCtrlValList imcvll_add = mcvll_add->find(ch, cntrl_add); + if(imcvll_add == mcvll_add->end()) + { + if(found_erase) + ops.add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal)); + PendingOperationItem poi(mcvll_add, 0, ch, cntrl_add, PendingOperationItem::AddMidiCtrlValList); + if(ops.findAllocationOp(poi) == ops.end()) + { + poi._mcvl = new MidiCtrlValList(cntrl_add); + ops.add(poi); + } + ops.add(PendingOperationItem(poi._mcvl, part, tck_add, val_add, PendingOperationItem::AddMidiCtrlVal)); + return; + } + else + { + mcvl_add = imcvll_add->second; + iMidiCtrlVal imcv_add = mcvl_add->findMCtlVal(tck_add, part); + if(imcv_add != mcvl_add->end()) + { + if(tck_erase == tck_add && mcvl_erase == mcvl_add) + ops.add(PendingOperationItem(mcvl_add, imcv_add, val_add, PendingOperationItem::ModifyMidiCtrlVal)); + else + { + if(found_erase) + ops.add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal)); + ops.add(PendingOperationItem(mcvl_add, part, tck_add, val_add, PendingOperationItem::AddMidiCtrlVal)); + } + return; + } + else + { + if(found_erase) + ops.add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal)); + ops.add(PendingOperationItem(mcvl_add, part, tck_add, val_add, PendingOperationItem::AddMidiCtrlVal)); + } + } + } + else + { + if(found_erase) + ops.add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal)); + } +} + //--------------------------------------------------------- // addEvent //--------------------------------------------------------- iEvent Part::addEvent(Event& p) { - return _events->add(p); + return _events.add(p); } //--------------------------------------------------------- // index //--------------------------------------------------------- -int PartList::index(Part* part) +int PartList::index(const Part* part) const { int index = 0; - for (iPart i = begin(); i != end(); ++i, ++index) + for (ciPart i = begin(); i != end(); ++i, ++index) if (i->second == part) { return index; } @@ -652,65 +663,90 @@ return 0; } -//--------------------------------------------------------- -// Part -//--------------------------------------------------------- - -Part::Part(const Part& p) : PosLen(p) -{ - _sn=p._sn; - _name=p._name; - _selected=p._selected; - _mute=p._mute; - _colorIndex=p._colorIndex; - _hiddenEvents=p._hiddenEvents; - _track=p._track; - _events=p._events; - _prevClone=p._prevClone; - _nextClone=p._nextClone; - - _events->incRef(1); -} - Part::Part(Track* t) { _hiddenEvents = NoEventsHidden; _prevClone = this; _nextClone = this; - setSn(newSn()); + _backupClone = NULL; + _sn = newSn(); + _clonemaster_sn = _sn; _track = t; _selected = false; _mute = false; _colorIndex = 0; - _events = new EventList; - _events->incRef(1); - _events->incARef(1); } -Part::Part(Track* t, EventList* ev) - { - _hiddenEvents = NoEventsHidden; - _prevClone = this; - _nextClone = this; - setSn(newSn()); - _track = t; - _selected = false; - _mute = false; - _colorIndex = 0; - _events = ev; - _events->incRef(1); - _events->incARef(1); - } +WavePart* WavePart::duplicateEmpty() const +{ + WavePart* part = new WavePart((WaveTrack*)this->_track); + part->setName(name()); + part->setColorIndex(colorIndex()); + + *(PosLen*)part = *(PosLen*)this; + part->setMute(mute()); + + return part; +} -//--------------------------------------------------------- -// MidiPart -// copy constructor -//--------------------------------------------------------- +WavePart* WavePart::duplicate() const +{ + return (WavePart*)Part::duplicate(); +} -MidiPart::MidiPart(const MidiPart& p) : Part(p) +WavePart* WavePart::createNewClone() const { - _prevClone = this; - _nextClone = this; + return (WavePart*)Part::createNewClone(); +} + +MidiPart* MidiPart::duplicateEmpty() const +{ + MidiPart* part = new MidiPart((MidiTrack*)this->_track); + part->setName(name()); + part->setColorIndex(colorIndex()); + + *(PosLen*)part = *(PosLen*)this; + part->setMute(mute()); + + return part; +} + +MidiPart* MidiPart::duplicate() const +{ + return (MidiPart*)Part::duplicate(); +} + +MidiPart* MidiPart::createNewClone() const +{ + return (MidiPart*)Part::createNewClone(); +} + + +Part* Part::createNewClone() const +{ + Part* clone = duplicateEmpty(); + for (MusECore::ciEvent i = _events.begin(); i != _events.end(); ++i) + { + Event nev = i->second.clone(); // Create a non-shared clone of the event, having the same id. + clone->addEvent(nev); + } + clone->_backupClone=const_cast(this); + return clone; +} + +Part* Part::duplicate() const +{ + Part* dup = duplicateEmpty(); + + // copy the eventlist; duplicate each Event(Ptr!). + for (MusECore::ciEvent i = _events.begin(); i != _events.end(); ++i) + { + Event nev = i->second.duplicate(); // Create a duplicate of the event, excluding the _id. + + dup->addEvent(nev); + } + + return dup; } @@ -724,42 +760,21 @@ setType(FRAMES); } -WavePart::WavePart(WaveTrack* t, EventList* ev) - : Part(t, ev) - { - setType(FRAMES); - } - -//--------------------------------------------------------- -// WavePart -// copy constructor -//--------------------------------------------------------- - -WavePart::WavePart(const WavePart& p) : Part(p) -{ - _prevClone = this; - _nextClone = this; -} - //--------------------------------------------------------- // Part //--------------------------------------------------------- Part::~Part() - { +{ if (_prevClone!=this || _nextClone!=this) { if (MusEGlobal::debugMsg) { fprintf(stderr, "Part isn't unchained in ~Part()! Unchaining now...\n"); } - unchainClone(this); - } - - _events->incRef(-1); - if (_events->refCount() <= 0) - delete _events; - } + unchainClone(); + } +} //--------------------------------------------------------- @@ -814,6 +829,52 @@ printf("THIS SHOULD NEVER HAPPEN: could not find the part in PartList::remove()!\n"); } + +void PartList::addOperation(Part* part, PendingOperationList& ops) +{ + // There is protection, in the catch-all Undo::insert(), from failure here (such as double add, del + add, add + del) + // which might cause addPortCtrlEvents() without parts or without corresponding removePortCtrlEvents etc. + ops.add(PendingOperationItem(this, part, PendingOperationItem::AddPart)); + addPortCtrlEvents(part, part->posValue(), part->lenValue(), part->track(), ops); +} + +void PartList::delOperation(Part* part, PendingOperationList& ops) +{ + // There is protection, in the catch-all Undo::insert(), from failure here (such as double del, del + add, add + del) + // which might cause addPortCtrlEvents() without parts or without corresponding removePortCtrlEvents etc. + removePortCtrlEvents(part, part->track(), ops); + iPart i; + for (i = begin(); i != end(); ++i) { + if (i->second == part) { + ops.add(PendingOperationItem(this, i, PendingOperationItem::DeletePart)); + return; + } + } + printf("THIS SHOULD NEVER HAPPEN: could not find the part in PartList::delOperation()!\n"); +} + +void PartList::movePartOperation(Part* part, int new_pos, PendingOperationList& ops, Track* track) +{ + removePortCtrlEvents(part, part->track(), ops); + iPart i = end(); + if(track) + { + for (i = begin(); i != end(); ++i) { + if (i->second == part) + break; + } + if(i == end()) + printf("THIS SHOULD NEVER HAPPEN: could not find the part in PartList::movePartOperation()!\n"); + } + + ops.add(PendingOperationItem(i, part, new_pos, PendingOperationItem::MovePart, track)); + + if(!track) + track = part->track(); + + addPortCtrlEvents(part, new_pos, part->lenValue(), track, ops); +} + //--------------------------------------------------------- // addPart //--------------------------------------------------------- @@ -847,84 +908,33 @@ // cmdResizePart //--------------------------------------------------------- -void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len, bool doClones) +void Song::cmdResizePart(Track* track, Part* oPart, unsigned int len, bool doMove, int newPos, bool doClones) { switch(track->type()) { case Track::WAVE: - { - MusECore::WavePart* nPart = new MusECore::WavePart(*(MusECore::WavePart*)oPart); - EventList* el = nPart->events(); - unsigned new_partlength = MusEGlobal::tempomap.deltaTick2frame(oPart->tick(), oPart->tick() + len); - - // If new nr of frames is less than previous what can happen is: - // - 0 or more events are beginning after the new final position. Those are removed from the part - // - The last event begins before new final position and ends after it. If so, it will be resized to end at new part length - if (new_partlength < oPart->lenFrame()) { - Undo operations; - - for (iEvent i = el->begin(); i != el->end(); i++) { - Event e = i->second; - unsigned event_startframe = e.frame(); - unsigned event_endframe = event_startframe + e.lenFrame(); - if (event_endframe < new_partlength) - continue; - } - nPart->setLenFrame(new_partlength); - // Do not do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart, oPart, nPart, false, false)); - - MusEGlobal::song->applyOperationGroup(operations); - } - // If the part is expanded there can be no additional events beginning after the previous final position - // since those are removed if the part has been shrunk at some time (see above) - // The only thing we need to check is the final event: If it has data after the previous final position, - // we'll expand that event - else { - Undo operations; - if(!el->empty()) - { - iEvent i = el->end(); - i--; - Event last = i->second; - MusECore::SndFileR file = last.sndFile(); - if (file.isNull()) - return; - Event newEvent = last.clone(); - // Do not do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, last, nPart, false, false)); - } - - nPart->setLenFrame(new_partlength); - // Do not do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart, oPart, nPart, false, false)); - MusEGlobal::song->applyOperationGroup(operations); - } - } - break; case Track::MIDI: case Track::DRUM: case Track::NEW_DRUM: { Undo operations; - - unsigned orig_len=oPart->lenTick(); - MidiPart* part_it=(MidiPart*)oPart; - do - { - if (part_it->lenTick()==orig_len) - { - MidiPart* newPart = new MidiPart(*part_it); - newPart->setLenTick(len); - // Do port controller values but not clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart, part_it, newPart, true, false)); - } - - part_it=(MidiPart*)part_it->nextClone(); - } while (doClones && (part_it != (MidiPart*)oPart)); + + unsigned orig_len = oPart->lenValue(); + Part* part_it = oPart; + do + { + if(part_it->lenValue() == orig_len) + operations.push_back(UndoOp(UndoOp::ModifyPartLength, part_it, orig_len, len, Pos::TICKS)); + if(doMove) + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::MovePart, part_it, part_it->posValue(), newPos, MusECore::Pos::TICKS, track, track)); + + part_it = part_it->nextClone(); + } while (doClones && (part_it != oPart)); MusEGlobal::song->applyOperationGroup(operations); break; } + + default: break; } @@ -936,23 +946,23 @@ // create two new parts p1 and p2 //--------------------------------------------------------- -void Track::splitPart(Part* part, int tickpos, Part*& p1, Part*& p2) +void Part::splitPart(int tickpos, Part*& p1, Part*& p2) const { int l1 = 0; // len of first new part (ticks or samples) int l2 = 0; // len of second new part int samplepos = MusEGlobal::tempomap.tick2frame(tickpos); - switch (type()) { - case WAVE: - l1 = samplepos - part->frame(); - l2 = part->lenFrame() - l1; + switch (track()->type()) { + case Track::WAVE: + l1 = samplepos - frame(); + l2 = lenFrame() - l1; break; - case MIDI: - case DRUM: - case NEW_DRUM: - l1 = tickpos - part->tick(); - l2 = part->lenTick() - l1; + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + l1 = tickpos - tick(); + l2 = lenTick() - l1; break; default: return; @@ -961,20 +971,18 @@ if (l1 <= 0 || l2 <= 0) return; - p1 = newPart(part); // new left part - p2 = newPart(part); // new right part + p1 = this->duplicateEmpty(); // new left part + p2 = this->duplicateEmpty(); // new right part - // Added by Tim. p3.3.6 - - switch (type()) { - case WAVE: + switch (track()->type()) { + case Track::WAVE: p1->setLenFrame(l1); p2->setFrame(samplepos); p2->setLenFrame(l2); break; - case MIDI: - case DRUM: - case NEW_DRUM: + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: p1->setLenTick(l1); p2->setTick(tickpos); p2->setLenTick(l2); @@ -983,72 +991,42 @@ break; } - p2->setSn(p2->newSn()); - - EventList* se = part->events(); - EventList* de1 = p1->events(); - EventList* de2 = p2->events(); - - if (type() == WAVE) { - int ps = part->frame(); + if (track()->type() == Track::WAVE) { + int ps = this->frame(); int d1p1 = p1->frame(); int d2p1 = p1->endFrame(); int d1p2 = p2->frame(); int d2p2 = p2->endFrame(); - for (iEvent ie = se->begin(); ie != se->end(); ++ie) { - Event event = ie->second; + for (ciEvent ie = _events.begin(); ie != _events.end(); ++ie) { + const Event& event = ie->second; int s1 = event.frame() + ps; int s2 = event.endFrame() + ps; if ((s2 > d1p1) && (s1 < d2p1)) { Event si = event.mid(d1p1 - ps, d2p1 - ps); - de1->add(si); + p1->addEvent(si); } if ((s2 > d1p2) && (s1 < d2p2)) { Event si = event.mid(d1p2 - ps, d2p2 - ps); - de2->add(si); + p2->addEvent(si); } } } else { - for (iEvent ie = se->begin(); ie != se->end(); ++ie) { + for (ciEvent ie = _events.begin(); ie != _events.end(); ++ie) { Event event = ie->second.clone(); int t = event.tick(); if (t >= l1) { event.move(-l1); - de2->add(event); + p2->addEvent(event); } else - de1->add(event); + p1->addEvent(event); } } } //--------------------------------------------------------- -// cmdSplitPart -//--------------------------------------------------------- - -void Song::cmdSplitPart(Track* track, Part* part, int tick) - { - int l1 = tick - part->tick(); - int l2 = part->lenTick() - l1; - if (l1 <= 0 || l2 <= 0) - return; - Part* p1; - Part* p2; - track->splitPart(part, tick, p1, p2); - - //MusEGlobal::song->informAboutNewParts(part, p1); // is unneccessary because of ChangePart below - MusEGlobal::song->informAboutNewParts(part, p2); - - startUndo(); - // Indicate no undo, and do port controller values but not clone parts. - MusEGlobal::audio->msgChangePart(part, p1, false, true, false); - MusEGlobal::audio->msgAddPart(p2, false); - endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED); - } - -//--------------------------------------------------------- // changePart //--------------------------------------------------------- @@ -1061,79 +1039,12 @@ oTrack->parts()->remove(oPart); nTrack->parts()->add(nPart); - - - // Added by T356. + + // Added by T356. // adjust song len: unsigned epos = nPart->tick() + nPart->lenTick(); if (epos > len()) _len = epos; - - } - -//--------------------------------------------------------- -// cmdGluePart -//--------------------------------------------------------- - -void Song::cmdGluePart(Track* track, Part* oPart) - { - // p3.3.54 - if(track->type() != Track::WAVE && !track->isMidiTrack()) - return; - - PartList* pl = track->parts(); - Part* nextPart = 0; - - for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { - if (ip->second == oPart) { - ++ip; - if (ip == pl->end()) - return; - nextPart = ip->second; - break; - } - } - - Part* nPart = track->newPart(oPart); - nPart->setLenTick(nextPart->tick() + nextPart->lenTick() - oPart->tick()); - - // populate nPart with Events from oPart and nextPart - - EventList* sl1 = oPart->events(); - EventList* dl = nPart->events(); - - for (iEvent ie = sl1->begin(); ie != sl1->end(); ++ie) - dl->add(ie->second); - - EventList* sl2 = nextPart->events(); - - if(track->type() == Track::WAVE) - { - int frameOffset = nextPart->frame() - oPart->frame(); - for (iEvent ie = sl2->begin(); ie != sl2->end(); ++ie) - { - Event event = ie->second.clone(); - event.setFrame(event.frame() + frameOffset); - dl->add(event); - } - } - else - if(track->isMidiTrack()) - { - int tickOffset = nextPart->tick() - oPart->tick(); - for (iEvent ie = sl2->begin(); ie != sl2->end(); ++ie) - { - Event event = ie->second.clone(); - event.setTick(event.tick() + tickOffset); - dl->add(event); - } - } - - startUndo(); - MusEGlobal::audio->msgRemovePart(nextPart, false); - // Indicate no undo, and do port controller values but not clone parts. - MusEGlobal::audio->msgChangePart(oPart, nPart, false, true, false); - endUndo(SC_PART_MODIFIED | SC_PART_REMOVED); } //--------------------------------------------------------- @@ -1169,31 +1080,16 @@ } //--------------------------------------------------------- -// clone -//--------------------------------------------------------- - -MidiPart* MidiPart::clone() const - { - return new MidiPart(*this); - } - - -WavePart* WavePart::clone() const - { - return new WavePart(*this); - } - -//--------------------------------------------------------- // hasHiddenEvents // Returns combination of HiddenEventsType enum. //--------------------------------------------------------- -int MidiPart::hasHiddenEvents() +int MidiPart::hasHiddenEvents() const { unsigned len = lenTick(); // 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++) + for(ciEvent ev=_events.begin(); ev!=_events.end(); ev++) { if(ev->second.endTick() > len) { @@ -1210,12 +1106,12 @@ // Returns combination of HiddenEventsType enum. //--------------------------------------------------------- -int WavePart::hasHiddenEvents() +int WavePart::hasHiddenEvents() const { 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++) + for(ciEvent ev=_events.begin(); ev!=_events.end(); ev++) { if(ev->second.endFrame() > len) { @@ -1227,6 +1123,44 @@ return _hiddenEvents; } +bool WavePart::openAllEvents() +{ + bool opened = false; + const EventList& el = events(); + for(ciEvent ie = el.begin(); ie != el.end(); ++ie) + { + const Event& e = ie->second; + if(e.empty()) + continue; + SndFileR f = e.sndFile(); + if(!f.isNull() && !f.isOpen()) + { + opened = true; + f.openRead(); + } + } + return opened; +} + +bool WavePart::closeAllEvents() +{ + bool closed = false; + const EventList& el = events(); + for(ciEvent ie = el.begin(); ie != el.end(); ++ie) + { + const Event& e = ie->second; + if(e.empty()) + continue; + SndFileR f = e.sndFile(); + if(!f.isNull() && f.isOpen()) + { + closed = true; + f.close(); + } + } + return closed; +} + //--------------------------------------------------------- // ClonePart //--------------------------------------------------------- @@ -1235,7 +1169,8 @@ { cp = p; id = i; - uuid_generate(uuid); + is_deleted = false; + _uuid = QUuid::createUuid(); } diff -Nru muse-2.1.2/muse/part.h muse-3.0.2+ds1/muse/part.h --- muse-2.1.2/muse/part.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/part.h 2017-12-04 21:01:18.000000000 +0000 @@ -25,10 +25,9 @@ #ifndef __PART_H__ #define __PART_H__ -#include +#include -// Added by T356. -#include +#include #include "event.h" #include "audioconvert.h" @@ -42,11 +41,13 @@ class Xml; class Part; class WaveTrack; +class PendingOperationList; struct ClonePart { const Part* cp; int id; - uuid_t uuid; + QUuid _uuid; + bool is_deleted; ClonePart(const Part*, int i = -1); }; @@ -60,16 +61,13 @@ class Part : public PosLen { public: enum HiddenEventsType { NoEventsHidden = 0, LeftEventsHidden, RightEventsHidden }; - - // @@@@@@@@@@@ IMPORTANT @@@@@@@@@@@@ - // @@ when adding member variables @@ - // @@ here, don't forget to update @@ - // @@ the copy-constructor! @@ - // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + static Part* readFromXml(Xml&, Track*, bool doClone = false, bool toTrack = true); private: static int snGen; int _sn; + int _clonemaster_sn; // the serial number of some clone in the chain. every member of the chain has the same value here. QString _name; bool _selected; @@ -78,22 +76,25 @@ protected: Track* _track; - EventList* _events; + EventList _events; Part* _prevClone; Part* _nextClone; - int _hiddenEvents; // Combination of HiddenEventsType. - + Part* _backupClone; // when a part gets removed, it's still there; and for undo-ing the remove, it must know about where it was clone-chained to. + mutable int _hiddenEvents; // Combination of HiddenEventsType. + public: Part(Track*); - Part(Track*, EventList*); - Part(const Part& p); virtual ~Part(); - int sn() { return _sn; } + virtual Part* duplicate() const; + virtual Part* duplicateEmpty() const = 0; + virtual Part* createNewClone() const; // this does NOT chain clones yet. Chain is updated only when the part is really added! + virtual void splitPart(int tickpos, Part*& p1, Part*& p2) const; + void setSn(int n) { _sn = n; } + int clonemaster_sn() const { return _clonemaster_sn; } + int sn() const { return _sn; } int newSn() { return snGen++; } - virtual Part* clone() const = 0; - const QString& name() const { return _name; } void setName(const QString& s) { _name = s; } bool selected() const { return _selected; } @@ -102,23 +103,30 @@ void setMute(bool b) { _mute = b; } Track* track() const { return _track; } void setTrack(Track*t) { _track = t; } - EventList* events() const { return _events; } - const EventList* cevents() const { return _events; } + const EventList& events() const { return _events; } + EventList& nonconst_events() { return _events; } int colorIndex() const { return _colorIndex; } void setColorIndex(int idx) { _colorIndex = idx; } - Part* prevClone() { return _prevClone; } - Part* nextClone() { return _nextClone; } - void setPrevClone(Part* p) { _prevClone = p; } - void setNextClone(Part* p) { _nextClone = p; } + bool isCloneOf(const Part*) const; + bool hasClones() const { return _prevClone!=this || _nextClone!=this; } + int nClones() const; + Part* prevClone() const { return _prevClone; } // FINDMICHJETZT make it const Part*! + Part* nextClone() const { return _nextClone; } + Part* backupClone() const { return _backupClone; } + + void unchainClone(); + void chainClone(Part* p); // *this is made a sibling of p! p is not touched (except for its clone-chain), whereas this->events will get altered + void rechainClone(); // re-chains the part to the same clone chain it was unchained before // Returns combination of HiddenEventsType enum. - 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; } + virtual int hasHiddenEvents() const { return _hiddenEvents; } - iEvent addEvent(Event& p); + iEvent addEvent(Event& p); // this does not care about clones! If the part is a clone, be sure to execute this on all clones (with duplicated Events, that is!) + // Returns true if any event was opened. Does not operate on the part's clones, if any. + virtual bool openAllEvents() { return false; }; + // Returns true if any event was closed. Does not operate on the part's clones, if any. + virtual bool closeAllEvents() { return false; }; virtual void write(int, Xml&, bool isCopy = false, bool forceWavePaths = false) const; @@ -134,13 +142,15 @@ public: MidiPart(MidiTrack* t) : Part((Track*)t) {} - MidiPart(MidiTrack* t, EventList* ev) : Part((Track*)t, ev) {} - MidiPart(const MidiPart& p); virtual ~MidiPart() {} - virtual MidiPart* clone() const; + virtual MidiPart* duplicate() const; + virtual MidiPart* duplicateEmpty() const; + virtual MidiPart* createNewClone() const; + + MidiTrack* track() const { return (MidiTrack*)Part::track(); } // Returns combination of HiddenEventsType enum. - int hasHiddenEvents(); + int hasHiddenEvents() const; virtual void dump(int n = 0) const; }; @@ -154,16 +164,21 @@ // p3.3.31 AudioConvertMap _converters; - + public: WavePart(WaveTrack* t); - WavePart(WaveTrack* t, EventList* ev); - WavePart(const WavePart& p); virtual ~WavePart() {} - virtual WavePart* clone() const; + virtual WavePart* duplicate() const; + virtual WavePart* duplicateEmpty() const; + virtual WavePart* createNewClone() const; + WaveTrack* track() const { return (WaveTrack*)Part::track(); } // Returns combination of HiddenEventsType enum. - int hasHiddenEvents(); + int hasHiddenEvents() const; + // Returns true if any event was opened. Does not operate on the part's clones, if any. + bool openAllEvents(); + // Returns true if any event was closed. Does not operate on the part's clones, if any. + bool closeAllEvents(); virtual void dump(int n = 0) const; }; @@ -182,27 +197,31 @@ iPart findPart(unsigned tick); iPart add(Part*); void remove(Part* part); - int index(Part*); + int index(const Part*) const; Part* find(int idx); void clearDelete() { for (iPart i = begin(); i != end(); ++i) delete i->second; clear(); } + + void addOperation(Part* part, PendingOperationList& ops); + void delOperation(Part* part, PendingOperationList& ops); + void movePartOperation(Part* part, int new_pos, PendingOperationList& ops, Track* track = 0); }; -extern void chainClone(Part* p); -extern void chainClone(Part* p1, Part* p2); -extern void unchainClone(Part* p); -extern void replaceClone(Part* p1, Part* p2); extern void chainCheckErr(Part* p); -extern void unchainTrackParts(Track* t, bool decRefCount); -extern void chainTrackParts(Track* t, bool incRefCount); +extern void unchainTrackParts(Track* t); +extern void chainTrackParts(Track* t); extern void addPortCtrlEvents(Part* part, bool doClones); -extern void addPortCtrlEvents(Event& event, Part* part, bool doClones); +extern void addPortCtrlEvents(const Event& event, Part* part, unsigned int tick, unsigned int len, Track* track, PendingOperationList& ops); +extern void addPortCtrlEvents(Event& event, Part* part); +extern void addPortCtrlEvents(Part* part, unsigned int tick, unsigned int len, Track* track, PendingOperationList& ops); extern void removePortCtrlEvents(Part* part, bool doClones); -extern void removePortCtrlEvents(Event& event, Part* part, bool doClones); -extern Part* readXmlPart(Xml&, Track*, bool doClone = false, bool toTrack = true); +extern void removePortCtrlEvents(Part* part, Track* track, PendingOperationList& ops); +extern void removePortCtrlEvents(Event& event, Part* part); +extern void removePortCtrlEvents(const Event& event, Part* part, Track* track, PendingOperationList& ops); +extern void modifyPortCtrlEvents(const Event& old_event, const Event& event, Part* part, PendingOperationList& ops); } // namespace MusECore diff -Nru muse-2.1.2/muse/plugin.cpp muse-3.0.2+ds1/muse/plugin.cpp --- muse-2.1.2/muse/plugin.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/plugin.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: plugin.cpp,v 1.21.2.23 2009/12/15 22:07:12 spamatica Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -30,39 +30,25 @@ #include #include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include -#include #include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include #include "globals.h" #include "globaldefs.h" #include "gconfig.h" #include "filedialog.h" #include "slider.h" -#include "midictrl.h" +#include "midictrl_consts.h" #include "plugin.h" #include "controlfifo.h" #include "xml.h" @@ -71,21 +57,28 @@ #include "doublelabel.h" #include "fastlog.h" #include "checkbox.h" -#include "verticalmeter.h" -#include "popupmenu.h" -#include "menutitleitem.h" +#include "meter.h" +#include "utils.h" +#ifdef LV2_SUPPORT +#include "lv2host.h" +#endif + +#ifdef VST_NATIVE_SUPPORT +#include "vst_native.h" +#endif #include "audio.h" #include "al/dsp.h" #include "config.h" +#include "muse_math.h" // Turn on debugging messages. -//#define PLUGIN_DEBUGIN +//#define PLUGIN_DEBUGIN // Turn on constant stream of debugging messages. -//#define PLUGIN_DEBUGIN_PROCESS +//#define PLUGIN_DEBUGIN_PROCESS namespace MusEGlobal { MusECore::PluginList plugins; @@ -94,14 +87,6 @@ } -namespace MusEGui { -int PluginDialog::selectedPlugType = 0; -int PluginDialog::selectedGroup = 0; -QStringList PluginDialog::sortItems = QStringList(); -QRect PluginDialog::geometrySave = QRect(); -QByteArray PluginDialog::listSave = QByteArray(); -} - namespace MusECore { //--------------------------------------------------------- @@ -112,76 +97,76 @@ { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; - + float fmin, fmax, fdef; int imin, imax; float frng; - - bool hasdef = ladspaDefaultValue(plugin, port, &fdef); + + bool hasdef = ladspaDefaultValue(plugin, port, &fdef); MidiController::ControllerType t = midiControllerType(ctlnum); - - #ifdef PLUGIN_DEBUGIN + + #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: ctlnum:%d ladspa port:%lu has default?:%d default:%f\n", ctlnum, port, hasdef, fdef); #endif - - if(desc & LADSPA_HINT_TOGGLED) + + if(desc & LADSPA_HINT_TOGGLED) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_TOGGLED\n"); #endif - + *min = 0; *max = 1; *def = (int)lrintf(fdef); return hasdef; } - + float m = 1.0; if(desc & LADSPA_HINT_SAMPLE_RATE) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_SAMPLE_RATE\n"); #endif - + m = float(MusEGlobal::sampleRate); - } - + } + if(desc & LADSPA_HINT_BOUNDED_BELOW) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_BELOW\n"); #endif - + fmin = range.LowerBound * m; - } + } else fmin = 0.0; - + if(desc & LADSPA_HINT_BOUNDED_ABOVE) - { - #ifdef PLUGIN_DEBUGIN + { + #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_ABOVE\n"); #endif - + fmax = range.UpperBound * m; - } + } else fmax = 1.0; - + frng = fmax - fmin; - imin = lrintf(fmin); - imax = lrintf(fmax); + imin = lrintf(fmin); + imax = lrintf(fmax); int ctlmn = 0; int ctlmx = 127; - - #ifdef PLUGIN_DEBUGIN + + #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: port min:%f max:%f \n", fmin, fmax); #endif - + bool isneg = (imin < 0); int bias = 0; - switch(t) + switch(t) { case MidiController::RPN: case MidiController::NRPN: @@ -226,45 +211,45 @@ break; } float fctlrng = float(ctlmx - ctlmn); - + // Is it an integer control? if(desc & LADSPA_HINT_INTEGER) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_INTEGER\n"); #endif - + // just clip the limits instead of scaling the whole range. ie fit the range into clipped space. if(imin < ctlmn) imin = ctlmn; if(imax > ctlmx) imax = ctlmx; - + *min = imin; *max = imax; - + *def = (int)lrintf(fdef); - + return hasdef; } - + // It's a floating point control, just use wide open maximum range. *min = ctlmn; *max = ctlmx; - + float normdef = fdef / frng; fdef = normdef * fctlrng; - + // FIXME: TODO: Incorrect... Fix this somewhat more trivial stuff later.... - + *def = (int)lrintf(fdef) + bias; - - #ifdef PLUGIN_DEBUGIN + + #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: setting default:%d\n", *def); #endif - + return hasdef; -} +} //--------------------------------------------------------- // midi2LadspaValue @@ -274,75 +259,75 @@ { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; - + float fmin, fmax; int imin; float frng; - + MidiController::ControllerType t = midiControllerType(ctlnum); - - #ifdef PLUGIN_DEBUGIN + + #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: ctlnum:%d ladspa port:%lu val:%d\n", ctlnum, port, val); #endif - + float m = 1.0; if(desc & LADSPA_HINT_SAMPLE_RATE) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_SAMPLE_RATE\n"); #endif - + m = float(MusEGlobal::sampleRate); - } - + } + if(desc & LADSPA_HINT_BOUNDED_BELOW) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_BELOW\n"); #endif - + fmin = range.LowerBound * m; - } + } else fmin = 0.0; - + if(desc & LADSPA_HINT_BOUNDED_ABOVE) - { - #ifdef PLUGIN_DEBUGIN + { + #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_ABOVE\n"); #endif - + fmax = range.UpperBound * m; - } + } else fmax = 1.0; - + frng = fmax - fmin; - imin = lrintf(fmin); + imin = lrintf(fmin); - if(desc & LADSPA_HINT_TOGGLED) + if(desc & LADSPA_HINT_TOGGLED) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_TOGGLED\n"); #endif - + if(val > 0) return fmax; else return fmin; } - + int ctlmn = 0; int ctlmx = 127; - - #ifdef PLUGIN_DEBUGIN + + #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: port min:%f max:%f \n", fmin, fmax); #endif - + bool isneg = (imin < 0); int bval = val; int cval = val; - switch(t) + switch(t) { case MidiController::RPN: case MidiController::NRPN: @@ -392,7 +377,7 @@ } int ctlrng = ctlmx - ctlmn; float fctlrng = float(ctlmx - ctlmn); - + // Is it an integer control? if(desc & LADSPA_HINT_INTEGER) { @@ -401,27 +386,27 @@ ret = fmin; if(ret > fmax) ret = fmax; - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_INTEGER returning:%f\n", ret); #endif - - return ret; + + return ret; } - + // Avoid divide-by-zero error below. if(ctlrng == 0) return 0.0; - + // It's a floating point control, just use wide open maximum range. float normval = float(bval) / fctlrng; float ret = normval * frng + fmin; - - #ifdef PLUGIN_DEBUGIN + + #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: float returning:%f\n", ret); #endif - + return ret; -} +} //--------------------------------------------------------- // ladspaCtrlValueType @@ -431,7 +416,7 @@ { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; - + if(desc & LADSPA_HINT_INTEGER) return VAL_INT; else if(desc & LADSPA_HINT_LOGARITHMIC) @@ -440,8 +425,8 @@ return VAL_BOOL; else return VAL_LINEAR; -} - +} + //--------------------------------------------------------- // ladspaCtrlMode //--------------------------------------------------------- @@ -450,7 +435,7 @@ { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; - + if(desc & LADSPA_HINT_INTEGER) return CtrlList::DISCRETE; else if(desc & LADSPA_HINT_LOGARITHMIC) @@ -459,8 +444,8 @@ return CtrlList::DISCRETE; else return CtrlList::INTERPOLATE; -} - +} + // DELETETHIS 20 // Works but not needed. /* @@ -471,12 +456,12 @@ MidiController* ladspa2MidiController(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum) { int min, max, def; - + if(!ladspa2MidiControlValues(plugin, port, ctlnum, &min, &max, &def)) return 0; - + MidiController* mc = new MidiController(QString(plugin->PortNames[port]), ctlnum, min, max, def); - + return mc; } */ @@ -488,85 +473,85 @@ bool ladspaDefaultValue(const LADSPA_Descriptor* plugin, unsigned long port, float* val) { - if(port < plugin->PortCount) + if(port < plugin->PortCount) { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor; float m = (rh & LADSPA_HINT_SAMPLE_RATE) ? float(MusEGlobal::sampleRate) : 1.0f; - - if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh)) + + if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh)) { *val = range.LowerBound * m; return true; } - else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh)) + else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh)) { *val = range.UpperBound*m; return true; } - else if (LADSPA_IS_HINT_DEFAULT_LOW(rh)) + else if (LADSPA_IS_HINT_DEFAULT_LOW(rh)) { if (LADSPA_IS_HINT_LOGARITHMIC(rh)) { - *val = expf(logf(range.LowerBound * m) * .75 + + *val = expf(logf(range.LowerBound * m) * .75 + logf(range.UpperBound * m) * .25); return true; - } + } else { *val = range.LowerBound*.75*m + range.UpperBound*.25*m; return true; - } + } } - else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh)) + else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh)) { if (LADSPA_IS_HINT_LOGARITHMIC(rh)) { *val = expf(logf(range.LowerBound * m) * .5 + - logf(range.UpperBound * m) * .5); + logf(range.UpperBound * m) * .5); return true; - } + } else { *val = range.LowerBound*.5*m + range.UpperBound*.5*m; return true; - } + } } - else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh)) + else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh)) { if (LADSPA_IS_HINT_LOGARITHMIC(rh)) { *val = expf(logf(range.LowerBound * m) * .25 + logf(range.UpperBound * m) * .75); return true; - } + } else { *val = range.LowerBound*.25*m + range.UpperBound*.75*m; return true; - } + } } else if (LADSPA_IS_HINT_DEFAULT_0(rh)) { *val = 0.0; return true; - } + } else if (LADSPA_IS_HINT_DEFAULT_1(rh)) { *val = 1.0; return true; - } + } else if (LADSPA_IS_HINT_DEFAULT_100(rh)) { *val = 100.0; return true; - } + } else if (LADSPA_IS_HINT_DEFAULT_440(rh)) { *val = 440.0; return true; - } - + } + // No default found. Make one up... else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh) && LADSPA_IS_HINT_BOUNDED_ABOVE(rh)) { @@ -575,12 +560,12 @@ *val = expf(logf(range.LowerBound * m) * .5 + logf(range.UpperBound * m) * .5); return true; - } + } else { *val = range.LowerBound*.5*m + range.UpperBound*.5*m; return true; - } + } } else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh)) { @@ -599,7 +584,7 @@ return true; } } - + // No default found. Set return value to 0.0, but return false. *val = 0.0; return false; @@ -609,7 +594,7 @@ // ladspaControlRange //--------------------------------------------------------- -void ladspaControlRange(const LADSPA_Descriptor* plugin, unsigned long port, float* min, float* max) +void ladspaControlRange(const LADSPA_Descriptor* plugin, unsigned long port, float* min, float* max) { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; @@ -639,34 +624,40 @@ // Plugin //--------------------------------------------------------- -Plugin::Plugin(QFileInfo* f, const LADSPA_Descriptor* d, bool isDssi, bool isDssiSynth) +Plugin::Plugin(QFileInfo* f, const LADSPA_Descriptor* d, bool isDssi, bool isDssiSynth, bool isDssiVst, PluginFeatures reqFeatures) { _isDssi = isDssi; _isDssiSynth = isDssiSynth; - + _isDssiVst = isDssiVst; + _isLV2Plugin = false; + _isLV2Synth = false; + _isVstNativePlugin = false; + _isVstNativeSynth = false; + _requiredFeatures = reqFeatures; + #ifdef DSSI_SUPPORT dssi_descr = NULL; #endif - + fi = *f; plugin = NULL; ladspa = NULL; _handle = 0; _references = 0; _instNo = 0; - _label = QString(d->Label); - _name = QString(d->Name); - _uniqueID = d->UniqueID; - _maker = QString(d->Maker); - _copyright = QString(d->Copyright); - + _label = QString(d->Label); + _name = QString(d->Name); + _uniqueID = d->UniqueID; + _maker = QString(d->Maker); + _copyright = QString(d->Copyright); + _portCount = d->PortCount; - + _inports = 0; _outports = 0; _controlInPorts = 0; _controlOutPorts = 0; - for(unsigned long k = 0; k < _portCount; ++k) + for(unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = d->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) @@ -676,7 +667,7 @@ else if(pd & LADSPA_PORT_OUTPUT) ++_outports; - } + } else if(pd & LADSPA_PORT_CONTROL) { @@ -685,158 +676,134 @@ else if(pd & LADSPA_PORT_OUTPUT) ++_controlOutPorts; - } + } } - - _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(d->Properties); - - // By T356. Blacklist vst plugins in-place configurable for now. At one point they - // were working with in-place here, but not now, and RJ also reported they weren't working. - // Fixes problem with vst plugins not working or feeding back loudly. - // I can only think of two things that made them stop working: - // 1): I switched back from Jack-2 to Jack-1 - // 2): I changed winecfg audio to use Jack instead of ALSA. - // Will test later... - // Possibly the first one because under Mandriva2007.1 (Jack-1), no matter how hard I tried, - // the same problem existed. It may have been when using Jack-2 with Mandriva2009 that they worked. - // Apparently the plugins are lying about their in-place capability. - // Quote: - /* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin - may cease to work correctly if the host elects to use the same data - location for both input and output (see connect_port()). This - should be avoided as enabling this flag makes it impossible for - hosts to use the plugin to process audio `in-place.' */ - // Examination of all my ladspa and vst synths and effects plugins showed only one - - // EnsembleLite (EnsLite VST) has the flag set, but it is a vst synth and is not involved here! - // Yet many (all?) ladspa vst effect plugins exhibit this problem. - // Changed by Tim. p3.3.14 - // Hack: Special Flag required for example for control processing. - _isDssiVst = fi.completeBaseName() == QString("dssi-vst"); - // Hack: Blacklist vst plugins in-place, configurable for now. + + // Hack: Blacklist vst plugins in-place, configurable for now. if ((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace)) - _inPlaceCapable = false; + _requiredFeatures |= NoInPlaceProcessing; } Plugin::~Plugin() { - if(plugin) + if(plugin && !isLV2Plugin() && !isVstNativePlugin()) // delete plugin; printf("Plugin::~Plugin Error: plugin is not NULL\n"); } - + //--------------------------------------------------------- // incReferences //--------------------------------------------------------- int Plugin::incReferences(int val) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN fprintf(stderr, "Plugin::incReferences _references:%d val:%d\n", _references, val); #endif - + int newref = _references + val; - - if(newref == 0) + + if(newref == 0) { _references = 0; if(_handle) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN fprintf(stderr, "Plugin::incReferences no more instances, closing library\n"); #endif - + dlclose(_handle); } - + _handle = 0; ladspa = NULL; plugin = NULL; rpIdx.clear(); - + #ifdef DSSI_SUPPORT dssi_descr = NULL; #endif - + return 0; } - - if(_handle == 0) + + if(_handle == 0) { _handle = dlopen(fi.filePath().toLatin1().constData(), RTLD_NOW); - - if(_handle == 0) + + if(_handle == 0) { fprintf(stderr, "Plugin::incReferences dlopen(%s) failed: %s\n", fi.filePath().toLatin1().constData(), dlerror()); return 0; } - + #ifdef DSSI_SUPPORT DSSI_Descriptor_Function dssi = (DSSI_Descriptor_Function)dlsym(_handle, "dssi_descriptor"); if(dssi) { const DSSI_Descriptor* descr; - for(unsigned long i = 0;; ++i) + for(unsigned long i = 0;; ++i) { descr = dssi(i); if(descr == NULL) break; - + QString label(descr->LADSPA_Plugin->Label); - if(label == _label) - { + if(label == _label) + { _isDssi = true; ladspa = NULL; dssi_descr = descr; plugin = descr->LADSPA_Plugin; break; } - } + } } else - #endif // DSSI_SUPPORT + #endif // DSSI_SUPPORT { LADSPA_Descriptor_Function ladspadf = (LADSPA_Descriptor_Function)dlsym(_handle, "ladspa_descriptor"); if(ladspadf) { const LADSPA_Descriptor* descr; - for(unsigned long i = 0;; ++i) + for(unsigned long i = 0;; ++i) { descr = ladspadf(i); if(descr == NULL) break; - + QString label(descr->Label); if(label == _label) - { + { _isDssi = false; ladspa = ladspadf; plugin = descr; - + #ifdef DSSI_SUPPORT dssi_descr = NULL; #endif - + break; } - } + } } - } - + } + if(plugin != NULL) { - _name = QString(plugin->Name); - _uniqueID = plugin->UniqueID; - _maker = QString(plugin->Maker); - _copyright = QString(plugin->Copyright); - + _name = QString(plugin->Name); + _uniqueID = plugin->UniqueID; + _maker = QString(plugin->Maker); + _copyright = QString(plugin->Copyright); + _portCount = plugin->PortCount; - + _inports = 0; _outports = 0; _controlInPorts = 0; _controlOutPorts = 0; - for(unsigned long k = 0; k < _portCount; ++k) + for(unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = plugin->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) @@ -846,9 +813,9 @@ else if(pd & LADSPA_PORT_OUTPUT) ++_outports; - + rpIdx.push_back((unsigned long)-1); - } + } else if(pd & LADSPA_PORT_CONTROL) { @@ -856,37 +823,33 @@ { rpIdx.push_back(_controlInPorts); ++_controlInPorts; - } + } else if(pd & LADSPA_PORT_OUTPUT) { rpIdx.push_back((unsigned long)-1); ++_controlOutPorts; - } - } + } + } } - - _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(plugin->Properties); - - // Hack: Special flag required for example for control processing. - _isDssiVst = fi.completeBaseName() == QString("dssi-vst"); - // Hack: Blacklist vst plugins in-place, configurable for now. + + // Hack: Blacklist vst plugins in-place, configurable for now. if ((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace)) - _inPlaceCapable = false; + _requiredFeatures |= NoInPlaceProcessing; } - } - + } + if(plugin == NULL) { dlclose(_handle); _handle = 0; _references = 0; - fprintf(stderr, "Plugin::incReferences Error: %s no plugin!\n", fi.filePath().toLatin1().constData()); + fprintf(stderr, "Plugin::incReferences Error: %s no plugin!\n", fi.filePath().toLatin1().constData()); return 0; } - + _references = newref; - + return _references; } @@ -896,14 +859,14 @@ void Plugin::range(unsigned long i, float* min, float* max) const { - ladspaControlRange(plugin, i, min, max); + ladspaControlRange(plugin, i, min, max); } //--------------------------------------------------------- // defaultValue //--------------------------------------------------------- -float Plugin::defaultValue(unsigned long port) const +double Plugin::defaultValue(unsigned long port) const { float val; ladspaDefaultValue(plugin, port, &val); @@ -934,10 +897,10 @@ static void loadPluginLib(QFileInfo* fi) { - void* handle = dlopen(fi->filePath().toAscii().constData(), RTLD_NOW); + void* handle = dlopen(fi->filePath().toLatin1().constData(), RTLD_NOW); if (handle == 0) { fprintf(stderr, "dlopen(%s) failed: %s\n", - fi->filePath().toAscii().constData(), dlerror()); + fi->filePath().toLatin1().constData(), dlerror()); return; } @@ -946,73 +909,90 @@ if(dssi) { const DSSI_Descriptor* descr; - for (unsigned long i = 0;; ++i) + for (unsigned long i = 0;; ++i) { descr = dssi(i); if (descr == 0) break; - + // Make sure it doesn't already exist. if(MusEGlobal::plugins.find(fi->completeBaseName(), QString(descr->LADSPA_Plugin->Label)) != 0) continue; - #ifdef PLUGIN_DEBUGIN + Plugin::PluginFeatures reqfeat = Plugin::NoFeatures; + if(LADSPA_IS_INPLACE_BROKEN(descr->LADSPA_Plugin->Properties)) + reqfeat |= Plugin::NoInPlaceProcessing; + + // Hack: Special flag required for example for control processing. + bool vst = false; + if(fi->completeBaseName() == QString("dssi-vst")) + { + vst = true; + reqfeat |= Plugin::FixedBlockSize; + } + + #ifdef PLUGIN_DEBUGIN fprintf(stderr, "loadPluginLib: dssi effect name:%s inPlaceBroken:%d\n", descr->LADSPA_Plugin->Name, LADSPA_IS_INPLACE_BROKEN(descr->LADSPA_Plugin->Properties)); #endif - - bool is_synth = descr->run_synth || descr->run_synth_adding - || descr->run_multiple_synths || descr->run_multiple_synths_adding; + + bool is_synth = descr->run_synth || descr->run_synth_adding + || descr->run_multiple_synths || descr->run_multiple_synths_adding; if(MusEGlobal::debugMsg) - fprintf(stderr, "loadPluginLib: adding dssi effect plugin:%s name:%s label:%s synth:%d\n", - fi->filePath().toLatin1().constData(), + fprintf(stderr, "loadPluginLib: adding dssi effect plugin:%s name:%s label:%s synth:%d isDssiVst:%d required features:%d\n", + fi->filePath().toLatin1().constData(), descr->LADSPA_Plugin->Name, descr->LADSPA_Plugin->Label, - is_synth + is_synth, vst, reqfeat ); - - MusEGlobal::plugins.add(fi, descr->LADSPA_Plugin, true, is_synth); - } + + MusEGlobal::plugins.add(fi, descr->LADSPA_Plugin, true, is_synth, vst, reqfeat); + } } else #endif { LADSPA_Descriptor_Function ladspa = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor"); - if(!ladspa) + if(!ladspa) { const char *txt = dlerror(); - if(txt) + if(txt) { fprintf(stderr, "Unable to find ladspa_descriptor() function in plugin " "library file \"%s\": %s.\n" "Are you sure this is a LADSPA plugin file?\n", - fi->filePath().toAscii().constData(), + fi->filePath().toLatin1().constData(), txt); } dlclose(handle); return; } - + const LADSPA_Descriptor* descr; - for (unsigned long i = 0;; ++i) + for (unsigned long i = 0;; ++i) { descr = ladspa(i); if (descr == NULL) break; - + // Make sure it doesn't already exist. if(MusEGlobal::plugins.find(fi->completeBaseName(), QString(descr->Label)) != 0) continue; - - #ifdef PLUGIN_DEBUGIN + + Plugin::PluginFeatures reqfeat = Plugin::NoFeatures; + if(LADSPA_IS_INPLACE_BROKEN(descr->Properties)) + reqfeat |= Plugin::NoInPlaceProcessing; + + #ifdef PLUGIN_DEBUGIN fprintf(stderr, "loadPluginLib: ladspa effect name:%s inPlaceBroken:%d\n", descr->Name, LADSPA_IS_INPLACE_BROKEN(descr->Properties)); #endif - + if(MusEGlobal::debugMsg) - fprintf(stderr, "loadPluginLib: adding ladspa plugin:%s name:%s label:%s\n", fi->filePath().toLatin1().constData(), descr->Name, descr->Label); - MusEGlobal::plugins.add(fi, descr); + fprintf(stderr, "loadPluginLib: adding ladspa plugin:%s name:%s label:%s required features:%d\n", + fi->filePath().toLatin1().constData(), descr->Name, descr->Label, reqfeat); + MusEGlobal::plugins.add(fi, descr, false, false, false, reqfeat); } - } - + } + dlclose(handle); } @@ -1027,7 +1007,7 @@ QDir pluginDir(s, QString("*.so")); // ddskrjo if (pluginDir.exists()) { QFileInfoList list = pluginDir.entryInfoList(); - QFileInfoList::iterator it=list.begin(); + QFileInfoList::iterator it=list.begin(); while(it != list.end()) { loadPluginLib(&*it); ++it; @@ -1079,7 +1059,7 @@ std::string s; const char* p = 0; - + // Take care of DSSI plugins first... #ifdef DSSI_SUPPORT const char* dssiPath = getenv("DSSI_PATH"); @@ -1108,7 +1088,7 @@ p++; } #endif - + // Now do LADSPA plugins... const char* ladspaPath = getenv("LADSPA_PATH"); if (ladspaPath == 0) @@ -1118,10 +1098,10 @@ ladspaPath = s.c_str(); } p = ladspaPath; - + if(MusEGlobal::debugMsg) fprintf(stderr, "loadPluginDir: ladspa path:%s\n", ladspaPath); - + while (*p != '\0') { const char* pe = p; while (*pe != ':' && *pe != '\0') @@ -1134,7 +1114,7 @@ buffer[n] = '\0'; if(MusEGlobal::debugMsg) fprintf(stderr, "loadPluginDir: loading ladspa dir:%s\n", buffer); - + loadPluginDir(QString(buffer)); delete[] buffer; } @@ -1151,8 +1131,8 @@ Plugin* PluginList::find(const QString& file, const QString& name) { for (iPlugin i = begin(); i != end(); ++i) { - if ((file == i->lib()) && (name == i->label())) - return &*i; + if ((file == (*i)->lib()) && (name == (*i)->label())) + return *i; } return 0; @@ -1165,16 +1145,10 @@ Pipeline::Pipeline() : std::vector() { - for (int i = 0; i < MAX_CHANNELS; ++i) - { - int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); - if(rv != 0) - { - fprintf(stderr, "ERROR: Pipeline ctor: posix_memalign returned error:%d. Aborting!\n", rv); - abort(); - } - } - + for(int i = 0; i < MAX_CHANNELS; ++i) + buffer[i] = NULL; + initBuffers(); + for (int i = 0; i < PipelineDepth; ++i) push_back(0); } @@ -1183,23 +1157,39 @@ // Pipeline copy constructor //--------------------------------------------------------- -Pipeline::Pipeline(const Pipeline& /*p*/) +Pipeline::Pipeline(const Pipeline& p, AudioTrack* t) : std::vector() { - for (int i = 0; i < MAX_CHANNELS; ++i) + for(int i = 0; i < MAX_CHANNELS; ++i) + buffer[i] = NULL; + initBuffers(); + + for(int i = 0; i < PipelineDepth; ++i) { - int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); - if(rv != 0) + PluginI* pli = p[i]; + if(pli) { - fprintf(stderr, "ERROR: Pipeline copy ctor: posix_memalign returned error:%d. Aborting!\n", rv); - abort(); - } - } - - // TODO: Copy plug-ins ! - for (int i = 0; i < PipelineDepth; ++i) - push_back(0); - } + Plugin* pl = pli->plugin(); + if(pl) + { + PluginI* new_pl = new PluginI(); + if(new_pl->initPluginInstance(pl, t->channels())) { + fprintf(stderr, "cannot instantiate plugin <%s>\n", + pl->name().toLatin1().constData()); + delete new_pl; + } + else + { + // Assigns valid ID and track to plugin, and creates controllers for plugin. + t->setupPlugin(new_pl, i); + push_back(new_pl); + continue; + } + } + } + push_back(NULL); // No plugin. Initialize with NULL. + } + } //--------------------------------------------------------- // ~Pipeline @@ -1213,16 +1203,60 @@ ::free(buffer[i]); } +void Pipeline::initBuffers() +{ + for(int i = 0; i < MAX_CHANNELS; ++i) + { + if(!buffer[i]) + { + int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: Pipeline ctor: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + } + } + + for(int i = 0; i < MAX_CHANNELS; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); + } +} + +//--------------------------------------------------------- +// latency +//--------------------------------------------------------- + +float Pipeline::latency() +{ + float l = 0.0; + PluginI* p; + for(int i = 0; i < PipelineDepth; ++i) + { + p = (*this)[i]; + if(p) + l+= p->latency(); + } + return l; +} + //--------------------------------------------------------- // 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) +bool Pipeline::addScheduledControlEvent(int track_ctrl_id, double 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)) + 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) @@ -1233,43 +1267,38 @@ } return true; } - + //--------------------------------------------------------- -// controllersEnabled -// Returns whether automation control stream is enabled or disabled. +// controllerEnabled +// 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) +bool Pipeline::controllerEnabled(int track_ctrl_id) { // 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; + if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MAX_PLUGINS, 0)) + return false; 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; - } + return p->controllerEnabled(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK); } + return false; } //--------------------------------------------------------- // enableController -// Enable or disable gui automation control stream. +// Enable or disable gui automation control stream. // Used during automation recording to inhibit gui controls //--------------------------------------------------------- -void Pipeline::enableController(int track_ctrl_id, bool en) +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)) + 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) @@ -1282,7 +1311,7 @@ } } } - + //--------------------------------------------------------- // setChannels //--------------------------------------------------------- @@ -1394,15 +1423,16 @@ void Pipeline::move(int idx, bool up) { PluginI* p1 = (*this)[idx]; - if (up) + if (up) { (*this)[idx] = (*this)[idx-1]; - - if((*this)[idx]) + + if((*this)[idx]) { (*this)[idx]->setID(idx); - + } + (*this)[idx-1] = p1; - + if(p1) { p1->setID(idx - 1); @@ -1410,15 +1440,16 @@ MusEGlobal::audio->msgSwapControllerIDX(p1->track(), idx, idx - 1); } } - else + else { (*this)[idx] = (*this)[idx+1]; - - if((*this)[idx]) + + if((*this)[idx]) { (*this)[idx]->setID(idx); - + } + (*this)[idx+1] = p1; - + if(p1) { p1->setID(idx + 1); @@ -1437,8 +1468,27 @@ PluginI* p = (*this)[idx]; if(p) return p->isDssiPlugin(); - - return false; + + return false; +} + +bool Pipeline::isLV2Plugin(int idx) const +{ + PluginI* p = (*this)[idx]; + if(p) + return p->isLV2Plugin(); + + return false; +} + +bool Pipeline::isVstNativePlugin(int idx) const +{ + PluginI* p = (*this)[idx]; + if(p) + return p->isVstNativePlugin(); + + return false; + } //--------------------------------------------------------- @@ -1449,9 +1499,22 @@ { PluginI* p = (*this)[idx]; if(p) - return !p->dssi_ui_filename().isEmpty(); - - return false; + { +#ifdef LV2_SUPPORT + if(p->plugin() && p->plugin()->isLV2Plugin()) + return ((LV2PluginWrapper *)p->plugin())->hasNativeGui(); +#endif + +#ifdef VST_NATIVE_SUPPORT + if(p->plugin() && p->plugin()->isVstNativePlugin()) + return ((VstNativePluginWrapper *)p->plugin())->hasNativeGui(); +#endif + + + return !p->dssi_ui_filename().isEmpty(); + } + + return false; } //--------------------------------------------------------- // showGui @@ -1470,11 +1533,29 @@ void Pipeline::showNativeGui(int idx, bool flag) { - #ifdef OSC_SUPPORT PluginI* p = (*this)[idx]; +#ifdef LV2_SUPPORT + if(p && p->plugin()->isLV2Plugin()) + { + ((LV2PluginWrapper *)p->plugin())->showNativeGui(p, flag); + return; + } + +#endif + +#ifdef VST_NATIVE_SUPPORT + if(p && p->plugin()->isVstNativePlugin()) + { + ((VstNativePluginWrapper *)p->plugin())->showNativeGui(p, flag); + return; + } + +#endif + #ifdef OSC_SUPPORT + if (p) p->oscIF().oscShowGui(flag); - #endif + #endif } //--------------------------------------------------------- @@ -1488,6 +1569,21 @@ PluginI* p = (*this)[idx]; if(p) p->deleteGui(); +#ifdef LV2_SUPPORT + if(p && p->plugin()->isLV2Plugin()) + { + ((LV2PluginWrapper *)p->plugin())->showNativeGui(p, false); + } + +#endif + +#ifdef VST_NATIVE_SUPPORT + if(p && p->plugin()->isVstNativePlugin()) + { + ((VstNativePluginWrapper *)p->plugin())->showNativeGui(p, false); + } + +#endif } //--------------------------------------------------------- @@ -1520,7 +1616,20 @@ { PluginI* p = (*this)[idx]; if (p) + { +#ifdef LV2_SUPPORT + if(p->plugin()->isLV2Plugin()) + return ((LV2PluginWrapper *)p->plugin())->nativeGuiVisible(p); +#endif + +#ifdef VST_NATIVE_SUPPORT + if(p->plugin()->isVstNativePlugin()) + return ((VstNativePluginWrapper *)p->plugin())->nativeGuiVisible(p); +#endif + return p->nativeGuiVisible(); + + } return false; } @@ -1529,42 +1638,42 @@ // If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- -void Pipeline::apply(unsigned long ports, unsigned long nframes, float** buffer1) +void Pipeline::apply(unsigned pos, unsigned long ports, unsigned long nframes, float** buffer1) { bool swap = false; for (iPluginI ip = begin(); ip != end(); ++ip) { PluginI* p = *ip; - + if(p) { - if (p->on()) + if (p->on()) { - if (p->inPlaceCapable()) + if (!(p->requiredFeatures() & Plugin::NoInPlaceProcessing)) { if (swap) - p->apply(nframes, ports, buffer, buffer); + p->apply(pos, nframes, ports, buffer, buffer); else - p->apply(nframes, ports, buffer1, buffer1); + p->apply(pos, nframes, ports, buffer1, buffer1); } - else + else { if (swap) - p->apply(nframes, ports, buffer, buffer1); + p->apply(pos, nframes, ports, buffer, buffer1); else - p->apply(nframes, ports, buffer1, buffer); + p->apply(pos, nframes, ports, buffer1, buffer); swap = !swap; } } else { - p->apply(nframes, 0, 0, 0); // Do not process (run) audio, process controllers only. + p->apply(pos, nframes, 0, 0, 0); // Do not process (run) audio, process controllers only. } } } - if (ports != 0 && swap) + if (ports != 0 && swap) { - for (unsigned long i = 0; i < ports; ++i) + for (unsigned long i = 0; i < ports; ++i) AL::dsp->cpy(buffer1[i], buffer[i], nframes); } } @@ -1577,21 +1686,191 @@ { _gui = 0; } - + PluginIBase::~PluginIBase() { if(_gui) delete _gui; -} - +} + +//--------------------------------------------------------- +// showGui +//--------------------------------------------------------- + +void PluginIBase::showGui() +{ + if(_gui == 0) + makeGui(); + _gui->setWindowTitle(titlePrefix() + name()); + if(_gui->isVisible()) + _gui->hide(); + else + _gui->show(); +} + +void PluginIBase::showGui(bool flag) +{ + if(flag) + { + if(_gui == 0) + makeGui(); + _gui->show(); + } + else + { + if(_gui) + _gui->hide(); + } +} + +//--------------------------------------------------------- +// guiVisible +//--------------------------------------------------------- + +bool PluginIBase::guiVisible() const +{ + return _gui && _gui->isVisible(); +} + +void PluginIBase::setGeometry(int x, int y, int w, int h) +{ + _guiGeometry = QRect(x, y, w, h); + if(_gui) + { + +#ifdef QT_SHOW_POS_BUG_WORKAROUND + // Because of the bug, no matter what we must supply a position, + // even upon first showing... + + // Check sane size. + if(w == 0) + w = _gui->sizeHint().width(); + if(h == 0) + h = _gui->sizeHint().height(); + + // No size hint? Try minimum size. + if(w == 0) + w = _gui->minimumSize().width(); + if(h == 0) + h = _gui->minimumSize().height(); + + // Fallback. + if(w == 0) + w = 200; + if(h == 0) + h = 200; + + _gui->setGeometry(x, y, w, h); + +#else + + // If the saved geometry is valid, use it. + // Otherwise this is probably the first time showing, + // so do not set a geometry - let Qt pick one + // (using auto-placement and sizeHint). + if(!(x == 0 && y == 0 && w == 0 && h == 0)) + { + // Check sane size. + if(w == 0) + w = _gui->sizeHint().width(); + if(h == 0) + h = _gui->sizeHint().height(); + + // No size hint? Try minimum size. + if(w == 0) + w = _gui->minimumSize().width(); + if(h == 0) + h = _gui->minimumSize().height(); + + // Fallback. + if(w == 0) + w = 200; + if(h == 0) + h = 200; + + _gui->setGeometry(x, y, w, h); + } +#endif + + } +} + +// Returns the current geometry of the gui, or if the gui does not exist, +// the saved gui geometry. +void PluginIBase::getGeometry(int *x, int *y, int *w, int *h) const +{ + // If gui does not exist return the saved geometry. + if(!_gui) + { + if(x) *x = _guiGeometry.x(); + if(y) *y = _guiGeometry.y(); + if(w) *w = _guiGeometry.width(); + if(h) *h = _guiGeometry.height(); + return; + } + + // Return the actual gui geometry. + if(x) *x = _gui->geometry().x(); + if(y) *y = _gui->geometry().y(); + if(w) *w = _gui->geometry().width(); + if(h) *h = _gui->geometry().height(); +} + +// Saves the current gui geometry. +void PluginIBase::saveGeometry(int x, int y, int w, int h) +{ + _guiGeometry = QRect(x, y, w, h); +} + +// Returns the saved gui geometry. +void PluginIBase::savedGeometry(int *x, int *y, int *w, int *h) const +{ + if(x) *x = _guiGeometry.x(); + if(y) *y = _guiGeometry.y(); + if(w) *w = _guiGeometry.width(); + if(h) *h = _guiGeometry.height(); +} + + +// Sets the gui's geometry. Also updates the saved geometry. +void PluginIBase::setNativeGeometry(int x, int y, int w, int h) +{ + _nativeGuiGeometry = QRect(x, y, w, h); +} + +// Returns the current geometry of the gui, or if the gui does not exist, +// the saved gui geometry. +void PluginIBase::getNativeGeometry(int *x, int *y, int *w, int *h) const +{ + if(x) *x = 0; + if(y) *y = 0; + if(w) *w = 0; + if(h) *h = 0; +} + +// Saves the current gui geometry. +void PluginIBase::saveNativeGeometry(int x, int y, int w, int h) +{ + _nativeGuiGeometry = QRect(x, y, w, h); +} + +// Returns the saved gui geometry. +void PluginIBase::savedNativeGeometry(int *x, int *y, int *w, int *h) const +{ + if(x) *x = _nativeGuiGeometry.x(); + if(y) *y = _nativeGuiGeometry.y(); + if(w) *w = _nativeGuiGeometry.width(); + if(h) *h = _nativeGuiGeometry.height(); +} + //--------------------------------------------------------- // 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) -{ +bool PluginIBase::addScheduledControlEvent(unsigned long i, double val, unsigned frame) +{ if(i >= parameters()) { printf("PluginIBase::addScheduledControlEvent param number %lu out of range of ports:%lu\n", i, parameters()); @@ -1599,56 +1878,56 @@ } ControlEvent ce; ce.unique = false; - ce.fromGui = false; + ce.fromGui = 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 + // 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; - + 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 PluginIBase::dssi_ui_filename() const +{ QString libr(lib()); if(dirPath().isEmpty() || libr.isEmpty()) return QString(); - + QString guiPath(dirPath() + "/" + libr); QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files); - if(!guiDir.exists()) + if(!guiDir.exists()) return QString(); - + QStringList list = guiDir.entryList(); - + QString plug(pluginLabel()); QString lib_qt_ui; QString lib_any_ui; QString plug_qt_ui; QString plug_any_ui; - - for(int i = 0; i < list.count(); ++i) + + for(int i = 0; i < list.count(); ++i) { QFileInfo fi(guiPath + QString("/") + list[i]); QString gui(fi.filePath()); struct stat buf; - if(stat(gui.toLatin1().constData(), &buf)) + if(stat(gui.toLatin1().constData(), &buf)) continue; if(!((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) - continue; - + continue; + // FIXME: Qt::CaseInsensitive - a quick and dirty way to accept any suffix. Should be case sensitive... if(!libr.isEmpty()) { @@ -1656,7 +1935,7 @@ lib_qt_ui = gui; if(lib_any_ui.isEmpty() && list[i].contains(libr + QString('_') /*, Qt::CaseInsensitive*/)) lib_any_ui = gui; - } + } if(!plug.isEmpty()) { if(plug_qt_ui.isEmpty() && list[i].contains(plug + QString("_qt"), Qt::CaseInsensitive)) @@ -1664,8 +1943,8 @@ if(plug_any_ui.isEmpty() && list[i].contains(plug + QString('_') /*, Qt::CaseInsensitive*/)) plug_any_ui = gui; } - } - + } + // Prefer qt plugin ui if(!plug_qt_ui.isEmpty()) return plug_qt_ui; @@ -1694,9 +1973,13 @@ handle = 0; controls = 0; controlsOut = 0; + controlsOutDummy = 0; controlPorts = 0; controlOutPorts = 0; - //_gui = 0; + _audioInSilenceBuf = 0; + _audioOutDummyBuf = 0; + _hasLatencyOutPort = false; + _latencyOutPort = 0; _on = true; initControlValues = false; _showNativeGuiPending = false; @@ -1706,7 +1989,7 @@ { _id = -1; _track = 0; - + init(); } @@ -1717,13 +2000,21 @@ PluginI::~PluginI() { #ifdef OSC_SUPPORT - _oscif.oscSetPluginI(NULL); + _oscif.oscSetPluginI(NULL); #endif if (_plugin) { deactivate(); _plugin->incReferences(-1); } + + if(_audioInSilenceBuf) + free(_audioInSilenceBuf); + if(_audioOutDummyBuf) + free(_audioOutDummyBuf); + + if (controlsOutDummy) + delete[] controlsOutDummy; if (controlsOut) delete[] controlsOut; if (controls) @@ -1738,7 +2029,7 @@ void PluginI::setID(int i) { - _id = i; + _id = i; } //--------------------------------------------------------- @@ -1749,11 +2040,11 @@ { if(!_track) return; - - for(unsigned long i = 0; i < controlPorts; ++i) + + for(unsigned long i = 0; i < controlPorts; ++i) _track->setPluginCtrlVal(genACnum(_id, i), controls[i].val); // TODO A faster bulk message } - + //--------------------------------------------------------- // setChannel //--------------------------------------------------------- @@ -1761,7 +2052,7 @@ void PluginI::setChannels(int c) { channel = c; - + unsigned long ins = _plugin->inports(); unsigned long outs = _plugin->outports(); int ni = 1; @@ -1770,88 +2061,180 @@ else if(ins) ni = c / ins; - + if(ni < 1) ni = 1; - + if (ni == instances) return; - - // remove old instances: - deactivate(); - delete[] handle; - instances = ni; - handle = new LADSPA_Handle[instances]; - for (int i = 0; i < instances; ++i) { - handle[i] = _plugin->instantiate(); - if (handle[i] == NULL) { - printf("cannot instantiate instance %d\n", i); - return; - } - } - - unsigned long curPort = 0; - unsigned long curOutPort = 0; - unsigned long ports = _plugin->ports(); - for (unsigned long k = 0; k < ports; ++k) + + LADSPA_Handle* handles = new LADSPA_Handle[ni]; + + if(ni > instances) { - LADSPA_PortDescriptor pd = _plugin->portd(k); - if (pd & LADSPA_PORT_CONTROL) + for(int i = 0; i < ni; ++i) + { + if(i < instances) + // Transfer existing handle from old array to new array. + handles[i] = handle[i]; + else + { + // Create a new plugin instance with handle. + handles[i] = _plugin->instantiate(this); + if(handles[i] == NULL) { - if(pd & LADSPA_PORT_INPUT) - { - for (int i = 0; i < instances; ++i) - _plugin->connectPort(handle[i], k, &controls[curPort].val); - controls[curPort].idx = k; - ++curPort; - } - else - if(pd & LADSPA_PORT_OUTPUT) - { - for (int i = 0; i < instances; ++i) - _plugin->connectPort(handle[i], k, &controlsOut[curOutPort].val); - controlsOut[curOutPort].idx = k; - ++curOutPort; - } + fprintf(stderr, "PluginI::setChannels: cannot instantiate instance %d\n", i); + + // Although this is a messed up state not easy to get out of (final # of channels?), try not to assert(). + // Whoever uses these will have to check instance count or null handle, and try to gracefully fix it and allow a song save. + for(int k = i; k < ni; ++k) + handles[i] = NULL; + ni = i + 1; + //channel = ?; + break; } + } + } + } + else + { + for(int i = 0; i < instances; ++i) + { + if(i < ni) + // Transfer existing handle from old array to new array. + handles[i] = handle[i]; + else + { + // Delete existing plugin instance. + // Previously we deleted all the instances and rebuilt from scratch. + // One side effect of this: Since a GUI is constructed only on the first handle, + // previously the native GUI would close when changing channels. Now it doesn't, which is good. + _plugin->deactivate(handle[i]); + _plugin->cleanup(handle[i]); + } + } } - - activate(); -} -//--------------------------------------------------------- -// setParam -//--------------------------------------------------------- + // Delete the old array, and set the new array. + delete[] handle; + handle = handles; -void PluginI::setParam(unsigned long i, float val) -{ - addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame()); -} + // Connect ports: + unsigned long curPort = 0; + unsigned long curOutPort = 0; + unsigned long ports = _plugin->ports(); + for(unsigned long k = 0; k < ports; ++k) + { + LADSPA_PortDescriptor pd = _plugin->portd(k); + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + { + for(int i = instances; i < ni; ++i) + _plugin->connectPort(handle[i], k, &controls[curPort].val); + controls[curPort].idx = k; + ++curPort; + } + else if(pd & LADSPA_PORT_OUTPUT) + { + // Connect only the first instance's output controls. + // We don't have a mechanism to display the other instances' outputs. + _plugin->connectPort(handle[0], k, &controlsOut[curOutPort].val); + // Connect the rest to dummy ports. + for(int i = 1; i < ni; ++i) + _plugin->connectPort(handle[i], k, &controlsOutDummy[curOutPort].val); + controlsOut[curOutPort].idx = k; + ++curOutPort; + } + } + } + + // Activate new instances. + for(int i = instances; i < ni; ++i) + _plugin->activate(handle[i]); + + // Initialize control values. + if(initControlValues) + { + for(unsigned long i = 0; i < controlPorts; ++i) + controls[i].val = controls[i].tmpVal; + } + else + { + // get initial control values from plugin + for(unsigned long i = 0; i < controlPorts; ++i) + controls[i].tmpVal = controls[i].val; + } + + // Finally, set the new number of instances. + instances = ni; +} + +//--------------------------------------------------------- +// setParam +//--------------------------------------------------------- + +void PluginI::setParam(unsigned long i, double val) +{ + addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame()); +} //--------------------------------------------------------- // defaultValue //--------------------------------------------------------- -float PluginI::defaultValue(unsigned long param) const +double PluginI::defaultValue(unsigned long param) const { if(param >= controlPorts) return 0.0; - + return _plugin->defaultValue(controls[param].idx); } -LADSPA_Handle Plugin::instantiate() +void PluginI::setCustomData(const std::vector &customParams) +{ + if(_plugin == NULL) + return; + +#ifdef LV2_SUPPORT + if(_plugin->isLV2Plugin()) //now only do it for lv2 plugs + { + + LV2PluginWrapper *lv2Plug = static_cast(_plugin); + for(int i = 0; i < instances; ++i) + { + lv2Plug->setCustomData(handle [i], customParams); + } + } +#endif + +#ifdef VST_NATIVE_SUPPORT + if(_plugin->isVstNativePlugin()) //now only do it for lv2 plugs + { + + VstNativePluginWrapper *vstPlug = static_cast(_plugin); + for(int i = 0; i < instances; ++i) + { + vstPlug->setCustomData(handle [i], customParams); + } + } +#endif +} + +LADSPA_Handle Plugin::instantiate(PluginI *) { LADSPA_Handle h = plugin->instantiate(plugin, MusEGlobal::sampleRate); if(h == NULL) { - fprintf(stderr, "Plugin::instantiate() Error: plugin:%s instantiate failed!\n", plugin->Label); + fprintf(stderr, "Plugin::instantiate() Error: plugin:%s instantiate failed!\n", plugin->Label); return NULL; } - + return h; } + + //--------------------------------------------------------- // initPluginInstance // return true on error @@ -1860,19 +2243,20 @@ bool PluginI::initPluginInstance(Plugin* plug, int c) { channel = c; - if(plug == 0) + if(plug == 0) { printf("initPluginInstance: zero plugin\n"); return true; } _plugin = plug; - - _plugin->incReferences(1); + + if (_plugin->incReferences(1)==0) + return true; #ifdef OSC_SUPPORT - _oscif.oscSetPluginI(this); + _oscif.oscSetPluginI(this); #endif - + QString inst("-" + QString::number(_plugin->instNo())); _name = _plugin->name() + inst; _label = _plugin->label() + inst; @@ -1894,91 +2278,114 @@ } else instances = 1; - + handle = new LADSPA_Handle[instances]; - for(int i = 0; i < instances; ++i) + for(int i = 0; i < instances; ++i) + handle[i]=NULL; + + for(int i = 0; i < instances; ++i) { - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN fprintf(stderr, "PluginI::initPluginInstance instance:%d\n", i); #endif - - handle[i] = _plugin->instantiate(); + + handle[i] = _plugin->instantiate(this); if(handle[i] == NULL) return true; } unsigned long ports = _plugin->ports(); - + controlPorts = 0; controlOutPorts = 0; - - for(unsigned long k = 0; k < ports; ++k) + + for(unsigned long k = 0; k < ports; ++k) { LADSPA_PortDescriptor pd = _plugin->portd(k); if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) ++controlPorts; - else + else if(pd & LADSPA_PORT_OUTPUT) ++controlOutPorts; - } + } } - + controls = new Port[controlPorts]; controlsOut = new Port[controlOutPorts]; - + controlsOutDummy = new Port[controlOutPorts]; + unsigned long curPort = 0; unsigned long curOutPort = 0; - for(unsigned long k = 0; k < ports; ++k) + for(unsigned long k = 0; k < ports; ++k) { LADSPA_PortDescriptor pd = _plugin->portd(k); - if(pd & LADSPA_PORT_CONTROL) + if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) { - float val = _plugin->defaultValue(k); + controls[curPort].idx = k; + double val = _plugin->defaultValue(k); controls[curPort].val = val; controls[curPort].tmpVal = val; controls[curPort].enCtrl = true; - controls[curPort].en2Ctrl = true; + for(int i = 0; i < instances; ++i) + _plugin->connectPort(handle[i], k, &controls[curPort].val); ++curPort; } else if(pd & LADSPA_PORT_OUTPUT) { + const char* pname = _plugin->portName(k); + if(pname == QString("latency") || pname == QString("_latency")) + { + _hasLatencyOutPort = true; + _latencyOutPort = curOutPort; + } + controlsOut[curOutPort].idx = k; controlsOut[curOutPort].val = 0.0; controlsOut[curOutPort].tmpVal = 0.0; controlsOut[curOutPort].enCtrl = false; - controlsOut[curOutPort].en2Ctrl = false; + // Connect only the first instance's output controls. + // We don't have a mechanism to display the other instances' outputs. + _plugin->connectPort(handle[0], k, &controlsOut[curOutPort].val); + // Connect the rest to dummy ports. + for(int i = 1; i < instances; ++i) + _plugin->connectPort(handle[i], k, &controlsOutDummy[curOutPort].val); ++curOutPort; } } } - curPort = 0; - curOutPort = 0; - for(unsigned long k = 0; k < ports; ++k) + + int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + + if(rv != 0) { - LADSPA_PortDescriptor pd = _plugin->portd(k); - if(pd & LADSPA_PORT_CONTROL) - { - if(pd & LADSPA_PORT_INPUT) - { - for(int i = 0; i < instances; ++i) - _plugin->connectPort(handle[i], k, &controls[curPort].val); - controls[curPort].idx = k; - ++curPort; - } - else - if(pd & LADSPA_PORT_OUTPUT) + fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioInSilenceBuf posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) { - for(int i = 0; i < instances; ++i) - _plugin->connectPort(handle[i], k, &controlsOut[curOutPort].val); - controlsOut[curOutPort].idx = k; - ++curOutPort; + _audioInSilenceBuf[q] = MusEGlobal::denormalBias; } - } } + else + { + memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + } + + rv = posix_memalign((void **)&_audioOutDummyBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + + if(rv != 0) + { + fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioOutDummyBuf posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + activate(); return false; } @@ -1989,12 +2396,16 @@ void PluginI::connect(unsigned long ports, unsigned long offset, float** src, float** dst) { - unsigned long port = 0; + 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); - port = (port + 1) % ports; + if(port < ports) + _plugin->connectPort(handle[i], k, src[port] + offset); + else + // Connect to an input silence buffer. + _plugin->connectPort(handle[i], k, _audioInSilenceBuf + offset); + ++port; } } } @@ -2002,8 +2413,12 @@ 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); - port = (port + 1) % ports; // overwrite output? + if(port < ports) + _plugin->connectPort(handle[i], k, dst[port] + offset); + else + // Connect to a dummy buffer. + _plugin->connectPort(handle[i], k, _audioOutDummyBuf + offset); + ++port; } } } @@ -2043,15 +2458,27 @@ } //--------------------------------------------------------- +// latency +//--------------------------------------------------------- + +float PluginI::latency() +{ + if(!_hasLatencyOutPort) + return 0.0; + return controlsOut[_latencyOutPort].val; +} + + +//--------------------------------------------------------- // setControl // set plugin instance controller value by name //--------------------------------------------------------- -bool PluginI::setControl(const QString& s, float val) +bool PluginI::setControl(const QString& s, double val) { for (unsigned long i = 0; i < controlPorts; ++i) { if (_plugin->portName(controls[i].idx) == s) { - setParam(i, val); + setParam(i, val); return false; } } @@ -2068,20 +2495,48 @@ { 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) { +#ifdef LV2_SUPPORT + if(_plugin != NULL && _plugin->isLV2Plugin())//save lv2 plugin state custom data before controls + { + LV2PluginWrapper *lv2Plug = static_cast(_plugin); + //for multi-instance plugins write only first instance's state + if(instances > 0) + { + lv2Plug->writeConfiguration(handle [0], level, xml); + } + } +#endif + +#ifdef VST_NATIVE_SUPPORT + if(_plugin != NULL && _plugin->isVstNativePlugin())//save vst plugin state custom data before controls + { + VstNativePluginWrapper *vstPlug = static_cast(_plugin); + //for multi-instance plugins write only first instance's state + if(instances > 0) + { + vstPlug->writeConfiguration(handle [0], level, xml); + } + } +#endif + 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()); + xml.tag(level, s.arg(Xml::xmlString(_plugin->portName(idx)).toLatin1().constData()).arg(double(controls[i].tmpVal)).toLatin1().constData()); } if (_on == false) xml.intTag(level, "on", _on); - if (guiVisible()) { - xml.intTag(level, "gui", 1); - xml.geometryTag(level, "geometry", _gui); - } + if(guiVisible()) + xml.intTag(level, "gui", 1); + int x, y, w, h; + getGeometry(&x, &y, &w, &h); + QRect r(x, y, w, h); + xml.qrectTag(level, "geometry", r); + if (nativeGuiVisible()) xml.intTag(level, "nativegui", 1); + getNativeGeometry(&x, &y, &w, &h); + QRect nr(x, y, w, h); + xml.qrectTag(level, "nativeGeometry", nr); xml.tag(level--, "/plugin"); } @@ -2095,7 +2550,7 @@ QString file; QString label; QString name("mops"); - float val = 0.0; + double val = 0.0; for (;;) { Xml::Token token = xml.parse(); @@ -2112,16 +2567,16 @@ if (tag == "name") name = xml.s2(); else if (tag == "val") - val = xml.s2().toFloat(); + val = xml.s2().toDouble(); break; case Xml::TagEnd: if (tag == "control") { if(_plugin) - { - bool found = false; - for(unsigned long i = 0; i < controlPorts; ++i) + { + bool found = false; + for(unsigned long i = 0; i < controlPorts; ++i) { - if(_plugin->portName(controls[i].idx) == name) + if(_plugin->portName(controls[i].idx) == name) { controls[i].val = controls[i].tmpVal = val; found = true; @@ -2132,7 +2587,7 @@ printf("PluginI:loadControl(%s, %f) controller not found\n", name.toLatin1().constData(), val); return false; - } + } initControlValues = true; } } @@ -2153,6 +2608,11 @@ { QString file; QString label; + + //custom params in xml song file , synth tag, that will be passed to new PluginI:setCustomData(Xml &) method + //now only lv2host uses them, others simply ignore + std::vector accumulatedCustomParams; + if (!readPreset) channel = 1; @@ -2166,7 +2626,7 @@ case Xml::TagStart: if (!readPreset && _plugin == 0) { _plugin = MusEGlobal::plugins.find(file, label); - + if (_plugin) { if(initPluginInstance(_plugin, channel)) { @@ -2174,9 +2634,9 @@ 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. + //break; // Don't break - let it read any control tags. } - } + } } if (tag == "control") loadControl(xml); @@ -2191,7 +2651,7 @@ showGui(flag); } else if (tag == "nativegui") { - // We can't tell OSC to show the native plugin gui + // We can't tell OSC to show the native plugin gui // until the parent track is added to the lists. // OSC needs to find the plugin in the track lists. // Use this 'pending' flag so it gets done later. @@ -2199,11 +2659,23 @@ } else if (tag == "geometry") { QRect r(readGeometry(xml, tag)); - if (_gui) { - _gui->resize(r.size()); - _gui->move(r.topLeft()); - } + setGeometry(r.x(), r.y(), r.width(), r.height()); } + else if (tag == "nativeGeometry") { + QRect r(readGeometry(xml, tag)); + setNativeGeometry(r.x(), r.y(), r.width(), r.height()); + } + else if (tag == "customData") { //just place tag contents in accumulatedCustomParams + QString customData = xml.parse1(); + if(!customData.isEmpty()){ + accumulatedCustomParams.push_back(customData); + //now process custom data immidiatly + //because it MUST be processed before plugin controls + //writeConfiguration places custom data before plugin controls values + setCustomData(accumulatedCustomParams); + accumulatedCustomParams.clear(); + } + } else xml.unknown("PluginI"); break; @@ -2235,19 +2707,21 @@ if (!readPreset && _plugin == 0) { _plugin = MusEGlobal::plugins.find(file, label); if (_plugin == 0) - { - printf("Warning: Plugin not found (%s, %s)\n", + { + QMessageBox::warning(0,"Plugin not found!", + "Plugin: " + label + " not found, if the project is saved it will be removed from the project"); + printf("Warning: - Plugin not found (%s, %s)\n", file.toLatin1().constData(), label.toLatin1().constData()); return true; } - + if (initPluginInstance(_plugin, channel)) - { + { printf("Error initializing plugin instance (%s, %s)\n", file.toLatin1().constData(), label.toLatin1().constData()); return true; - } } + } if (_gui) _gui->updateValues(); return false; @@ -2265,41 +2739,16 @@ //--------------------------------------------------------- void PluginI::showGui() - { - if (_plugin) { - if (_gui == 0) - makeGui(); - _gui->setWindowTitle(titlePrefix() + name()); - if (_gui->isVisible()) - _gui->hide(); - else - _gui->show(); - } - } +{ + if(_plugin) + PluginIBase::showGui(); +} void PluginI::showGui(bool flag) - { - if (_plugin) { - if (flag) { - if (_gui == 0) - makeGui(); - _gui->show(); - } - else { - if (_gui) - _gui->hide(); - } - } - } - -//--------------------------------------------------------- -// guiVisible -//--------------------------------------------------------- - -bool PluginI::guiVisible() - { - return _gui && _gui->isVisible(); - } +{ + if(_plugin) + PluginIBase::showGui(flag); +} //--------------------------------------------------------- // showNativeGui @@ -2307,8 +2756,30 @@ void PluginI::showNativeGui() { + +#ifdef LV2_SUPPORT + if(plugin() && plugin()->isLV2Plugin()) + { + if(((LV2PluginWrapper *)plugin())->nativeGuiVisible(this)) + ((LV2PluginWrapper *)plugin())->showNativeGui(this, false); + else + ((LV2PluginWrapper *)plugin())->showNativeGui(this, true); + return; + } +#endif + +#ifdef VST_NATIVE_SUPPORT + if(plugin() && plugin()->isVstNativePlugin()) + { + if(((VstNativePluginWrapper *)plugin())->nativeGuiVisible(this)) + ((VstNativePluginWrapper *)plugin())->showNativeGui(this, false); + else + ((VstNativePluginWrapper *)plugin())->showNativeGui(this, true); + return; + } +#endif #ifdef OSC_SUPPORT - if (_plugin) + if (_plugin) { if (_oscif.oscGuiVisible()) _oscif.oscShowGui(false); @@ -2316,30 +2787,53 @@ _oscif.oscShowGui(true); } #endif - _showNativeGuiPending = false; + _showNativeGuiPending = false; } void PluginI::showNativeGui(bool flag) { +#ifdef LV2_SUPPORT + if(plugin() && plugin()->isLV2Plugin()) + { + ((LV2PluginWrapper *)plugin())->showNativeGui(this, flag); + return; + } +#endif + +#ifdef VST_NATIVE_SUPPORT + if(plugin() && plugin()->isVstNativePlugin()) + { + ((VstNativePluginWrapper *)plugin())->showNativeGui(this, flag); + return; + } +#endif #ifdef OSC_SUPPORT - if(_plugin) + if(_plugin) { _oscif.oscShowGui(flag); - } + } #endif - _showNativeGuiPending = false; + _showNativeGuiPending = false; } //--------------------------------------------------------- // nativeGuiVisible //--------------------------------------------------------- -bool PluginI::nativeGuiVisible() +bool PluginI::nativeGuiVisible() const { +#ifdef LV2_SUPPORT + if(plugin() && plugin()->isLV2Plugin()) + return ((LV2PluginWrapper *)plugin())->nativeGuiVisible(this); +#endif +#ifdef VST_NATIVE_SUPPORT + if(plugin() && plugin()->isVstNativePlugin()) + return ((VstNativePluginWrapper *)plugin())->nativeGuiVisible(this); +#endif #ifdef OSC_SUPPORT return _oscif.oscGuiVisible(); - #endif - + #endif + return false; } @@ -2361,7 +2855,7 @@ { delete _gui; _gui = 0; - } + } } //--------------------------------------------------------- @@ -2370,26 +2864,16 @@ void PluginI::enableAllControllers(bool v) { - for(unsigned long i = 0; i < controlPorts; ++i) + for(unsigned long i = 0; i < controlPorts; ++i) controls[i].enCtrl = v; } //--------------------------------------------------------- -// enable2AllControllers -//--------------------------------------------------------- - -void PluginI::enable2AllControllers(bool v) -{ - for(unsigned long i = 0; i < controlPorts; ++i) - controls[i].en2Ctrl = v; -} - -//--------------------------------------------------------- // titlePrefix //--------------------------------------------------------- -QString PluginI::titlePrefix() const -{ +QString PluginI::titlePrefix() const +{ if (_track) return _track->name() + QString(": "); else return ":"; @@ -2400,259 +2884,272 @@ // If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- - -void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float** bufOut) +void PluginI::apply(unsigned pos, unsigned long n, unsigned long ports, float** bufIn, float** bufOut) { - unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); - unsigned long sample = 0; - - // Must make this detectable for dssi vst effects. - const bool usefixedrate = _plugin->_isDssiVst; // Try this. (was: = true; ) - - // TODO Make this number a global setting. - // Note for dssi-vst this MUST equal audio period. It doesn't like broken-up runs (it stutters), - // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length. - unsigned long fixedsize = n; // was: 2048 - - // For now, the fixed size is clamped to the audio buffer size. - // TODO: We could later add slower processing over several cycles - - // so that users can select a small audio period but a larger control period. - if(fixedsize > n) - fixedsize = n; - - unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; - if(min_per > n) - min_per = n; - - // CtrlListList* cll = NULL; // WIP - AutomationType at = AUTO_OFF; - if(_track) - { - 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; + const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); + unsigned long sample = 0; - // - // 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) - { + const bool usefixedrate = (requiredFeatures() & Plugin::FixedBlockSize); - -#if 0 // WIP - Work in progress. Tim. + // Note for dssi-vst this MUST equal audio period. It doesn't like broken-up runs (it stutters), + // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length. + // For now, the fixed size is clamped to the audio buffer size. + // TODO: We could later add slower processing over several cycles - + // so that users can select a small audio period but a larger control period. + const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > n) ? n : MusEGlobal::config.minControlProcessPeriod; + const unsigned long min_per_mask = min_per-1; // min_per must be power of 2 + + AutomationType at = AUTO_OFF; + CtrlListList* cll = NULL; + ciCtrlList icl_first; + if(_track) + { + at = _track->automationType(); + cll = _track->controller(); + if(_id != -1 && ports != 0) // Don't bother if not 'running'. + icl_first = cll->lower_bound(genACnum(_id, 0)); + } + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + const unsigned long in_ctrls = _plugin->controlInPorts(); - 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; - } + // Special for plugins: Deal with tmpVal. TODO: Get rid of tmpVal, maybe by using the FIFO... + for(unsigned long k = 0; k < controlPorts; ++k) + controls[k].val = controls[k].tmpVal; - val2 -= val1; - val1 += (double(frame - frame1) * val2)/double(frame2 - frame1); - - if (cl->valueType() == VAL_LOG) { - val1 = exp10(val1/20.0); - } + int cur_slice = 0; + while(sample < n) + { + unsigned long nsamp = n - sample; + const unsigned long slice_frame = pos + sample; - 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; - } + // 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(ports != 0) // Don't bother if not 'running'. + { + ciCtrlList icl = icl_first; + for(unsigned long k = 0; k < controlPorts; ++k) + { + CtrlList* cl = (cll && _id != -1 && icl != cll->end()) ? icl->second : NULL; + CtrlInterpolate& ci = controls[k].interp; + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)) ) ) + { + if(cl && _id != -1 && (unsigned long)cl->id() == genACnum(_id, k)) + { + cl->getInterpolation(slice_frame, no_auto || !controls[k].enCtrl, &ci); + if(icl != cll->end()) + ++icl; + } + else + { + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = controls[k].val; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; } - -#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; - while(!_controlFifo.isEmpty()) + else { - ControlEvent v = _controlFifo.peek(); - // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. - // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. - evframe = (syncFrame > v.frame + n) ? 0 : v.frame - syncFrame + n; - // Process only items in this time period. Make sure to process all - // subsequent items which have the same frame. - - // Protection. Observed this condition. Why? Supposed to be linear timestamps. - if(found && evframe < frame) + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. { - printf("PluginI::apply *** Error: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d\n", - evframe, v.frame, v.idx, v.value, v.unique); + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + if(cl && cll && icl != cll->end()) + ++icl; + } - // No choice but to ignore it. - _controlFifo.remove(); // Done with the ring buffer's item. Remove it. - continue; - } - - // process control events up to the end of our processing cycle. - // 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 // 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(!usefixedrate && MusEGlobal::audio->isPlaying()) + { + unsigned long samps = nsamp; + if(ci.eFrame != -1) + samps = (unsigned long)ci.eFrame - slice_frame; - if(v.idx >= _plugin->_controlInPorts) // Sanity check - break; + if(!ci.doInterp && samps > min_per) + { + samps &= ~min_per_mask; + if((samps & min_per_mask) != 0) + samps += min_per; + } + else + samps = min_per; - found = true; - frame = evframe; - index = v.idx; - - controls[v.idx].tmpVal = v.value; - - // Need to update the automation value, otherwise it overwrites later with the last automation value. - if(_track && _id != -1) - _track->setPluginCtrlVal(genACnum(_id, v.idx), v.value); - } + if(samps < nsamp) + nsamp = samps; - // 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) // If a control FIFO item was found, takes priority over automation controller stream. - nsamp = frame - sample; - - if(sample + nsamp >= n) // Safety check. - nsamp = n - sample; - - // 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; - - if(ports != 0) - { - connect(ports, sample, bufIn, bufOut); - - for(int i = 0; i < instances; ++i) - _plugin->apply(handle[i], nsamp); } - - sample += nsamp; - } -} -//--------------------------------------------------------- -// oscConfigure -//--------------------------------------------------------- + if(ci.doInterp && cl) + controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); + else + controls[k].val = ci.sVal; -#ifdef OSC_SUPPORT -int Plugin::oscConfigure(LADSPA_Handle handle, const char* key, const char* value) - { - #ifdef PLUGIN_DEBUGIN +#ifdef LV2_SUPPORT + if(_plugin->isLV2Plugin()) + { + for(int i = 0; i < instances; ++i) + { + (reinterpret_cast(_plugin))->setLastStateControls(handle [i], k, true, false, true, 0.0f); + } + } +#endif + + controls[k].tmpVal = controls[k].val; // Special for plugins: Deal with tmpVal. + +#ifdef PLUGIN_DEBUGIN_PROCESS + printf("PluginI::apply k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); +#endif + } + } + +#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()) + { + const ControlEvent& v = _controlFifo.peek(); + // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. + // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. + evframe = (syncFrame > v.frame + n) ? 0 : v.frame - syncFrame + n; + + #ifdef PLUGIN_DEBUGIN_PROCESS + fprintf(stderr, "PluginI::apply found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", + found, evframe, frame, v.frame, v.idx, v.value, v.unique); + #endif + + // Protection. Observed this condition. Why? Supposed to be linear timestamps. + if(found && evframe < frame) + { + fprintf(stderr, "PluginI::apply *** Error: evframe:%lu < frame:%lu event: frame:%lu idx:%lu val:%f unique:%d\n", + evframe, frame, v.frame, v.idx, v.value, v.unique); + + // No choice but to ignore it. + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + continue; + } + + 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. + + if(v.idx >= in_ctrls) // Sanity check + { + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + break; + } + + found = true; + frame = evframe; + index = v.idx; + + if(ports == 0) // Don't bother if not 'running'. + controls[v.idx].val = controls[v.idx].tmpVal = v.value; // Might as well at least update these. + else + { + CtrlInterpolate* ci = &controls[v.idx].interp; + // Tell it to stop the current ramp at this frame, when it does stop, set this value: + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; + } + + // Need to update the automation value, otherwise it overwrites later with the last automation value. + if(_track && _id != -1) + _track->setPluginCtrlVal(genACnum(_id, v.idx), v.value); + +#ifdef LV2_SUPPORT + if(v.fromGui) + { + if(_plugin->isLV2Plugin()) + { + for(int i = 0; i < instances; ++i) + { + (reinterpret_cast(_plugin))->setLastStateControls(handle [i], v.idx, true, true, false, v.value); + } + } + } +#endif + + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + } + + 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. + nsamp = n - sample; + + // 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) + { + if(ports != 0) // Don't bother if not 'running'. + { + connect(ports, sample, bufIn, bufOut); + + for(int i = 0; i < instances; ++i) + _plugin->apply(handle[i], nsamp); + } + + sample += nsamp; + } + + ++cur_slice; // Slice is done. Moving on to any next slice now... + } +} + +//--------------------------------------------------------- +// oscConfigure +//--------------------------------------------------------- + +#ifdef OSC_SUPPORT +int Plugin::oscConfigure( +LADSPA_Handle +#if defined(DSSI_SUPPORT) +handle +#endif +, +const char* +#if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN) +key +#endif +, +const char* +#if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN) +value +#endif +) + { + #ifdef PLUGIN_DEBUGIN printf("Plugin::oscConfigure effect plugin label:%s key:%s value:%s\n", plugin->Label, key, value); #endif - + #ifdef DSSI_SUPPORT if(!dssi_descr || !dssi_descr->configure) return 0; @@ -2661,7 +3158,7 @@ strlen(DSSI_RESERVED_CONFIGURE_PREFIX))) { fprintf(stderr, "Plugin::oscConfigure OSC: UI for plugin '%s' attempted to use reserved configure key \"%s\", ignoring\n", plugin->Label, key); - + return 0; } @@ -2669,7 +3166,7 @@ if (message) { printf("Plugin::oscConfigure on configure '%s' '%s', plugin '%s' returned error '%s'\n", key, value, plugin->Label, message); - + free(message); } @@ -2681,44 +3178,49 @@ // } #endif // DSSI_SUPPORT - + return 0; } - + //--------------------------------------------------------- // oscConfigure //--------------------------------------------------------- -int PluginI::oscConfigure(const char *key, const char *value) +int PluginI::oscConfigure( +const char * +#if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN) +key +#endif +, +const char * +#if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN) +value +#endif +) { if(!_plugin) return 0; - // This is pretty much the simplest legal implementation of - // configure in a DSSI host. - - // The host has the option to remember the set of (key,value) - // pairs associated with a particular instance, so that if it - // wants to restore the "same" instance on another occasion it can - // just call configure() on it for each of those pairs and so - // restore state without any input from a GUI. Any real-world GUI - // host will probably want to do that. This host doesn't have any - // concept of restoring an instance from one run to the next, so - // we don't bother remembering these at all. + // "The host has the option to remember the set of (key,value) + // pairs associated with a particular instance, so that if it + // wants to restore the "same" instance on another occasion it can + // just call configure() on it for each of those pairs and so + // restore state without any input from a GUI. Any real-world GUI + // host will probably want to do that." - #ifdef PLUGIN_DEBUGIN + #ifdef PLUGIN_DEBUGIN printf("PluginI::oscConfigure effect plugin name:%s label:%s key:%s value:%s\n", _name.toLatin1().constData(), _label.toLatin1().constData(), key, value); #endif - + #ifdef DSSI_SUPPORT // FIXME: Don't think this is right, should probably do as example shows below. for(int i = 0; i < instances; ++i) _plugin->oscConfigure(handle[i], key, value); #endif // DSSI_SUPPORT - + return 0; } - + //--------------------------------------------------------- // oscUpdate //--------------------------------------------------------- @@ -2728,40 +3230,40 @@ #ifdef DSSI_SUPPORT // Send project directory. _oscif.oscSendConfigure(DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::museProject.toLatin1().constData()); // MusEGlobal::song->projectPath() - + /* DELETETHIS 20 // Send current string configuration parameters. StringParamMap& map = synti->stringParameters(); int i = 0; - for(ciStringParamMap r = map.begin(); r != map.end(); ++r) + for(ciStringParamMap r = map.begin(); r != map.end(); ++r) { _oscIF.oscSendConfigure(r->first.c_str(), r->second.c_str()); - // Avoid overloading the GUI if there are lots and lots of params. + // Avoid overloading the GUI if there are lots and lots of params. if((i+1) % 50 == 0) usleep(300000); - ++i; - } - + ++i; + } + // Send current bank and program. unsigned long bank, prog; synti->currentProg(&prog, &bank, 0); _oscIF.oscSendProgram(prog, bank, true); // "true" means "force" */ - + // FIXME: TESTING FLAM: I have to put a delay because flammer hasn't opened yet. // How to make sure gui is ready? usleep(300000); // Send current control values. - for(unsigned long i = 0; i < controlPorts; ++i) + for(unsigned long i = 0; i < controlPorts; ++i) { _oscif.oscSendControl(controls[i].idx, controls[i].val, true /*force*/); - // Avoid overloading the GUI if there are lots and lots of ports. + // Avoid overloading the GUI if there are lots and lots of ports. if((i+1) % 50 == 0) usleep(300000); } #endif // DSSI_SUPPORT - + return 0; } @@ -2771,110 +3273,80 @@ int PluginI::oscControl(unsigned long port, float value) { - #ifdef PLUGIN_DEBUGIN - printf("PluginI::oscControl received oscControl port:%lu val:%f\n", port, value); + #ifdef PLUGIN_DEBUGIN + printf("PluginI::oscControl received oscControl port:%lu val:%f\n", port, value); #endif - + if(port >= _plugin->rpIdx.size()) { fprintf(stderr, "PluginI::oscControl: port number:%lu is out of range of index list size:%zd\n", port, _plugin->rpIdx.size()); return 0; } - + // Convert from DSSI port number to control input port index. unsigned long cport = _plugin->rpIdx[port]; - //unsigned long cport = _plugin->port2InCtrl(port); - + if((int)cport == -1) { fprintf(stderr, "PluginI::oscControl: port number:%lu is not a control input\n", port); return 0; } - + + // Record automation: + // Take care of this immediately, because we don't want the silly delay associated with + // processing the fifo one-at-a-time in the apply(). + // NOTE: With some vsts we don't receive control events until the user RELEASES a control. + // So the events all arrive at once when the user releases a control. + // That makes this pretty useless... But what the heck... + if(_track && _id != -1) + { + unsigned long id = genACnum(_id, cport); + _track->recordAutomation(id, value); + } + // (From DSSI module). // p3.3.39 Set the DSSI control input port's value. // Observations: With a native DSSI synth like LessTrivialSynth, the native GUI's controls do not change the sound at all - // ie. they don't update the DSSI control port values themselves. - // Hence in response to the call to this oscControl, sent by the native GUI, it is required to that here. + // ie. they don't update the DSSI control port values themselves. + // Hence in response to the call to this oscControl, sent by the native GUI, it is required to do that here. /// controls[cport].val = value; // DSSI-VST synths however, unlike DSSI synths, DO change their OWN sound in response to their gui controls. - // AND this function is called ! - // Despite the descrepency we are STILL required to update the DSSI control port values here - // because dssi-vst is WAITING FOR A RESPONSE! (A CHANGE in the control port value). + // AND this function is called ! + // Despite the descrepancy we are STILL required to update the DSSI control port values here + // because dssi-vst is WAITING FOR A RESPONSE! (A CHANGE in the control port value). // It will output something like "...4 events expected..." and count that number down as 4 actual control port value CHANGES // are done here in response. Normally it says "...0 events expected..." when MusE is the one doing the DSSI control changes. - // TODO: May need FIFOs on each control(!) so that the control changes get sent one per process cycle! + // TODO: May need FIFOs on each control(!) so that the control changes get sent one per process cycle! // Observed countdown not actually going to zero upon string of changes. // Try this ... - /* DELETETHIS 20 - OscControlFifo* cfifo = _oscif.oscFifo(cport); - if(cfifo) - { - OscControlValue cv; - //cv.idx = cport; - cv.value = value; - // Time-stamp the event. Looks like no choice but to use the (possibly slow) call to gettimeofday via timestamp(), - // because these are asynchronous events arriving from OSC. 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.) p4.0.15 Tim. - cv.frame = MusEGlobal::audio->timestamp(); - if(cfifo->put(cv)) - { - fprintf(stderr, "PluginI::oscControl: fifo overflow: in control number:%lu\n", cport); - } - } - */ + + // Schedules a timed control change: ControlEvent ce; ce.unique = _plugin->_isDssiVst; // Special for messages from vst gui to host - requires processing every message. - ce.fromGui = true; // It came form the plugin's own GUI. + ce.fromGui = true; // It came from the plugin's own GUI. ce.idx = cport; ce.value = value; - // 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(); + // Don't use timestamp(), because it's circular, which is making it impossible to deal + // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. + ce.frame = MusEGlobal::audio->curFrame(); if(_controlFifo.put(ce)) - { fprintf(stderr, "PluginI::oscControl: fifo overflow: in control number:%lu\n", cport); - } - - - // Record automation: - // Take care of this immediately, because we don't want the silly delay associated with - // processing the fifo one-at-a-time in the apply(). - // NOTE: With some vsts we don't receive control events until the user RELEASES a control. - // So the events all arrive at once when the user releases a control. - // That makes this pretty useless... But what the heck... - if(_track && _id != -1) - { - unsigned long id = genACnum(_id, cport); - AutomationType at = _track->automationType(); - - // 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 ! - if ((at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying())) - enableController(cport, false); //TODO maybe re-enable the ctrl soon? - - _track->recordAutomation(id, value); - } - + + enableController(cport, false); //TODO maybe re-enable the ctrl soon? + /* DELETETHIS 12 const DSSI_Descriptor* dssi = synth->dssi; const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; - + ciMidiCtl2LadspaPort ip = synth->port2MidiCtlMap.find(cport); if(ip != synth->port2MidiCtlMap.end()) { // TODO: TODO: Update midi MusE's midi controller knobs, sliders, boxes etc with a call to the midi port's setHwCtrlState() etc. - // But first we need a ladspa2MidiValue() function! ... + // But first we need a ladspa2MidiValue() function! ... // // - //float val = ladspa2MidiValue(ld, i, ?, ?); - + //float val = ladspa2MidiValue(ld, i, ?, ?); + } */ @@ -2887,486 +3359,7 @@ namespace MusEGui { -//--------------------------------------------------------- -// PluginDialog -// select Plugin dialog -//--------------------------------------------------------- - -PluginDialog::PluginDialog(QWidget* parent) - : QDialog(parent) - { - group_info=NULL; - setWindowTitle(tr("MusE: select plugin")); - - if(!geometrySave.isNull()) - setGeometry(geometrySave); - - QVBoxLayout* layout = new QVBoxLayout(this); - - tabBar = new QTabBar(this); - tabBar->setToolTip(tr("Plugin categories.\nRight-click on tabs to manage.\nRight-click on plugins to add/remove from a category.")); - tabBar->addTab("All"); - for (QList::iterator it=MusEGlobal::plugin_group_names.begin(); it!=MusEGlobal::plugin_group_names.end(); it++) - tabBar->addTab(*it); - - - pList = new QTreeWidget(this); - - pList->setColumnCount(12); - // "Note: In order to avoid performance issues, it is recommended that sorting - // is enabled after inserting the items into the tree. Alternatively, you could - // also insert the items into a list before inserting the items into the tree. " - QStringList headerLabels; - headerLabels << tr("Type"); - headerLabels << tr("Lib"); - headerLabels << tr("Label"); - headerLabels << tr("Name"); - headerLabels << tr("AI"); - headerLabels << tr("AO"); - headerLabels << tr("CI"); - headerLabels << tr("CO"); - headerLabels << tr("IP"); - headerLabels << tr("id"); - headerLabels << tr("Maker"); - headerLabels << tr("Copyright"); - - pList->setHeaderLabels(headerLabels); - - pList->headerItem()->setToolTip(4, tr("Audio inputs")); - pList->headerItem()->setToolTip(5, tr("Audio outputs")); - pList->headerItem()->setToolTip(6, tr("Control inputs")); - pList->headerItem()->setToolTip(7, tr("Control outputs")); - pList->headerItem()->setToolTip(8, tr("In-place capable")); - pList->headerItem()->setToolTip(9, tr("ID number")); - - pList->setRootIsDecorated(false); - pList->setSelectionBehavior(QAbstractItemView::SelectRows); - pList->setSelectionMode(QAbstractItemView::SingleSelection); - pList->setAlternatingRowColors(true); - pList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - pList->setContextMenuPolicy(Qt::CustomContextMenu); - - layout->addWidget(tabBar); - layout->addWidget(pList); - - //--------------------------------------------------- - // Ok/Cancel Buttons - //--------------------------------------------------- - - QBoxLayout* w5 = new QHBoxLayout; - layout->addLayout(w5); - - QBoxLayout* ok_lo = new QVBoxLayout; - w5->addLayout(ok_lo); - - okB = new QPushButton(tr("Ok"), this); - okB->setDefault(true); - QPushButton* cancelB = new QPushButton(tr("Cancel"), this); - okB->setFixedWidth(80); - okB->setEnabled(false); - cancelB->setFixedWidth(80); - ok_lo->addWidget(okB); - ok_lo->addSpacing(8); - ok_lo->addWidget(cancelB); - - QGroupBox* plugSelGroup = new QGroupBox(this); - plugSelGroup->setTitle(tr("Show plugs:")); - plugSelGroup->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); - QGridLayout* psl = new QGridLayout; - plugSelGroup->setLayout(psl); - - QButtonGroup* plugSel = new QButtonGroup(plugSelGroup); - onlySM = new QRadioButton(this); - onlySM->setText(tr("Mono and Stereo")); - onlySM->setCheckable(true); - plugSel->addButton(onlySM); - psl->addWidget(onlySM, 1, 0); - onlyS = new QRadioButton(this); - onlyS->setText(tr("Stereo")); - onlyS->setCheckable(true); - plugSel->addButton(onlyS); - psl->addWidget(onlyS, 0, 1); - onlyM = new QRadioButton(this); - onlyM->setText(tr("Mono")); - onlyM->setCheckable(true); - plugSel->addButton(onlyM); - psl->addWidget(onlyM, 0, 0); - allPlug = new QRadioButton(this); - allPlug->setText(tr("Show All")); - allPlug->setCheckable(true); - plugSel->addButton(allPlug); - psl->addWidget(allPlug, 1, 1); - plugSel->setExclusive(true); - - switch(selectedPlugType) { - case SEL_SM: onlySM->setChecked(true); break; - case SEL_S: onlyS->setChecked(true); break; - case SEL_M: onlyM->setChecked(true); break; - case SEL_ALL: allPlug->setChecked(true); break; - } - - tabBar->setCurrentIndex(selectedGroup); - tabBar->setContextMenuPolicy(Qt::ActionsContextMenu); - newGroupAction= new QAction(tr("&create new group"),tabBar); - delGroupAction= new QAction(tr("&delete currently selected group"),tabBar); - renGroupAction= new QAction(tr("re&name currently selected group"),tabBar); - tabBar->addAction(newGroupAction); - tabBar->addAction(delGroupAction); - tabBar->addAction(renGroupAction); - - if (selectedGroup==0) - { - delGroupAction->setEnabled(false); - renGroupAction->setEnabled(false); - } - //tabBar->setMovable(true); //not yet. need to find a way to forbid moving the zeroth tab - - - plugSelGroup->setToolTip(tr("Select which types of plugins should be visible in the list.
    " - "Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.
    " - "Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack.")); - - w5->addSpacing(8); - w5->addWidget(plugSelGroup); - w5->addSpacing(8); - - QBoxLayout* srch_lo = new QVBoxLayout; - w5->addLayout(srch_lo); - - QLabel *sortLabel = new QLabel(this); - sortLabel->setText(tr("Search in 'Label' and 'Name':")); - srch_lo->addSpacing(8); - srch_lo->addWidget(sortLabel); - srch_lo->addSpacing(8); - - sortBox = new QComboBox(this); - sortBox->setEditable(true); - if (!sortItems.empty()) - sortBox->addItems(sortItems); - - sortBox->setMinimumSize(100, 10); - srch_lo->addWidget(sortBox); - // FIXME: Adding this makes the whole bottom hlayout expand. Would like some space between lineedit and bottom. - // Same thing if spacers added to group box or Ok Cancel box. - //srch_lo->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Maximum)); - - fillPlugs(); - - pList->setSortingEnabled(true); - - if(listSave.isEmpty()) - { - int sizes[] = { 80, 110, 110, 110, 30, 30, 30, 30, 30, 50, 110, 110 }; - for (int i = 0; i < 12; ++i) { - if (sizes[i] <= 50) // hack alert! - pList->header()->setResizeMode(i, QHeaderView::Fixed); - pList->header()->resizeSection(i, sizes[i]); - } - pList->sortByColumn(3, Qt::AscendingOrder); - } - else - pList->header()->restoreState(listSave); - - connect(pList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(accept())); - connect(pList, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SLOT(enableOkB())); - connect(pList, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(plistContextMenu(const QPoint&))); - connect(cancelB, SIGNAL(clicked()), SLOT(reject())); - connect(okB, SIGNAL(clicked()), SLOT(accept())); - connect(plugSel, SIGNAL(buttonClicked(QAbstractButton*)), SLOT(pluginTypeSelectionChanged(QAbstractButton*))); - connect(tabBar, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); - //connect(tabBar, SIGNAL(tabMoved(int,int)), SLOT(tabMoved(int,int))); //not yet. need to find a way to forbid moving the zeroth tab - connect(sortBox, SIGNAL(editTextChanged(const QString&)),SLOT(fillPlugs())); - connect(newGroupAction, SIGNAL(activated()), SLOT(newGroup())); - connect(delGroupAction, SIGNAL(activated()), SLOT(delGroup())); - connect(renGroupAction, SIGNAL(activated()), SLOT(renameGroup())); - sortBox->setFocus(); - } - -void PluginDialog::plistContextMenu(const QPoint& point) -{ - QTreeWidgetItem* item = pList->currentItem(); - if (item) - { - group_info = &MusEGlobal::plugin_groups.get(item->text(1), item->text(2)); - QMenu* menu = new MusEGui::PopupMenu(this, true); - QSignalMapper* mapper = new QSignalMapper(this); - menu->addAction(new MusEGui::MenuTitleItem(tr("Associated categories"), menu)); - - if (tabBar->count()==1) - { - QAction* tmp=menu->addAction(tr("You need to define some categories first.")); - tmp->setEnabled(false); - } - else - { - for (int i=1; icount(); i++) // ignore the first tab ("All") - { - QAction* act=menu->addAction(tabBar->tabText(i)); - act->setCheckable(true); - act->setChecked(group_info->contains(i)); - connect(act,SIGNAL(toggled(bool)), mapper, SLOT(map())); - mapper->setMapping(act, i); - } - connect(mapper, SIGNAL(mapped(int)), this, SLOT(groupMenuEntryToggled(int))); - } - - menu->exec(mapToGlobal(point)); - - delete mapper; - delete menu; - - if (selectedGroup!=0 && !group_info->contains(selectedGroup)) // we removed the entry from the currently visible group - fillPlugs(); - - group_info=NULL; - } -} - -void PluginDialog::groupMenuEntryToggled(int index) -{ - if (group_info) - { - if (group_info->contains(index)) - group_info->remove(index); - else - group_info->insert(index); - } - else - { - fprintf(stderr,"THIS SHOULD NEVER HAPPEN: groupMenuEntryToggled called but group_info is NULL!\n"); - } -} - - - -//--------------------------------------------------------- -// enableOkB -//--------------------------------------------------------- - -void PluginDialog::enableOkB() -{ - okB->setEnabled(true); -} - - -void PluginDialog::newGroup() -{ - MusEGlobal::plugin_groups.shift_right(selectedGroup+1, tabBar->count()); - tabBar->insertTab(selectedGroup+1, tr("new group")); - MusEGlobal::plugin_group_names.insert(selectedGroup, tr("new group")); -} - -void PluginDialog::delGroup() -{ - if (selectedGroup!=0) - { - MusEGlobal::plugin_groups.erase(selectedGroup); - MusEGlobal::plugin_groups.shift_left(selectedGroup+1, tabBar->count()); - tabBar->removeTab(selectedGroup); - MusEGlobal::plugin_group_names.removeAt(selectedGroup-1); - } -} - -void PluginDialog::renameGroup() -{ - if (selectedGroup!=0) - { - bool ok; - QString newname = QInputDialog::getText(this, tr("Enter the new group name"), - tr("Enter the new group name"), QLineEdit::Normal, - tabBar->tabText(selectedGroup), &ok); - if (ok) - { - tabBar->setTabText(selectedGroup, newname); - MusEGlobal::plugin_group_names.replace(selectedGroup-1, newname); - } - } -} - - - - -//--------------------------------------------------------- -// value -//--------------------------------------------------------- - -MusECore::Plugin* PluginDialog::value() - { - QTreeWidgetItem* item = pList->currentItem(); - if (item) - return MusEGlobal::plugins.find(item->text(1), item->text(2)); - printf("plugin not found\n"); - return 0; - } - -//--------------------------------------------------------- -// saveSettings -//--------------------------------------------------------- - -void PluginDialog::saveSettings() -{ - if (!sortBox->currentText().isEmpty()) { - bool found = false; - foreach (QString item, sortItems) - if(item == sortBox->currentText()) { - found = true; - break; - } - if(!found) - sortItems.push_front(sortBox->currentText()); - } - - QHeaderView* hdr = pList->header(); - if(hdr) - listSave = hdr->saveState(); - - geometrySave = geometry(); -} - -//--------------------------------------------------------- -// accept -//--------------------------------------------------------- - -void PluginDialog::accept() - { - saveSettings(); - QDialog::accept(); - } - -//--------------------------------------------------------- -// reject -//--------------------------------------------------------- - -void PluginDialog::reject() -{ - saveSettings(); - QDialog::reject(); -} - -//--------------------------------------------------------- -// pluginTypeSelectionChanged -//--------------------------------------------------------- - -void PluginDialog::pluginTypeSelectionChanged(QAbstractButton* ab) - { - if (ab == allPlug) - selectedPlugType = SEL_ALL; - else if (ab == onlyM) - selectedPlugType = SEL_M; - else if (ab == onlyS) - selectedPlugType = SEL_S; - else if (ab == onlySM) - selectedPlugType = SEL_SM; - fillPlugs(); - } - -void PluginDialog::tabChanged(int index) -{ - renGroupAction->setEnabled(index!=0); - delGroupAction->setEnabled(index!=0); - - selectedGroup=index; - fillPlugs(); -} - -void PluginDialog::tabMoved(int from, int to) -{ -//all the below doesn't work :/ -/* static bool recurse=false; - - if (!recurse) - { - if (from==0 && to!=0) {recurse=true; tabBar->moveTab(to, from);} - if (from!=0 && to==0) {recurse=true; tabBar->moveTab(from, to);} - } - recurse=false;*/ - - - //if ((from==0 && to!=0) || (from!=0 && to==0)) { tabBar->setMovable(false); tabBar->setMovable(true); } - printf("**** %i -> %i\n", from, to); - - //FINDMICH TODO -} - -void PluginDialog::fillPlugs() -{ - QString type_name; - pList->clear(); - okB->setEnabled(false); - for (MusECore::iPlugin i = MusEGlobal::plugins.begin(); i != MusEGlobal::plugins.end(); ++i) - if (selectedGroup==0 || MusEGlobal::plugin_groups.get(*i).contains(selectedGroup)) - { - unsigned long ai = i->inports(); - unsigned long ao = i->outports(); - unsigned long ci = i->controlInPorts(); - unsigned long co = i->controlOutPorts(); - bool found = false; - QString sb_txt = sortBox->currentText().toLower(); - if(sb_txt.isEmpty() || i->label().toLower().contains(sb_txt) || i->name().toLower().contains(sb_txt)) - found = true; - - bool addFlag = false; - switch (selectedPlugType) { - case SEL_SM: // stereo & mono - if ((ai == 1 || ai == 2) && (ao == 1 || ao ==2)) { - addFlag = true; - } - break; - case SEL_S: // stereo - if ((ai == 1 || ai == 2) && ao ==2) { - addFlag = true; - } - break; - case SEL_M: // mono - if (ai == 1 && ao == 1) { - addFlag = true; - } - break; - case SEL_ALL: // all - addFlag = true; - break; - } - if (found && addFlag) { - QTreeWidgetItem* item = new QTreeWidgetItem; - if(i->isDssiSynth()) - type_name = tr("dssi synth"); - else if(i->isDssiPlugin()) - type_name = tr("dssi effect"); - else - type_name = tr("ladspa"); - item->setText(0, type_name); - item->setText(1, i->lib()); - item->setText(2, i->label()); - item->setText(3, i->name()); - item->setText(4, QString().setNum(ai)); - item->setText(5, QString().setNum(ao)); - item->setText(6, QString().setNum(ci)); - item->setText(7, QString().setNum(co)); - item->setText(8, QString().setNum(i->inPlaceCapable())); - item->setText(9, QString().setNum(i->id())); - item->setText(10, i->maker()); - item->setText(11, i->copyright()); - pList->addTopLevelItem(item); - } - } -} - -//--------------------------------------------------------- -// getPlugin -//--------------------------------------------------------- - -MusECore::Plugin* PluginDialog::getPlugin(QWidget* parent) - { - PluginDialog* dialog = new PluginDialog(parent); - MusECore::Plugin* p = 0; - int rv = dialog->exec(); - if(rv) - p = dialog->value(); - delete dialog; - return p; - } - -// TODO: We need to use .qrc files to use icons in WhatsThis bubbles. See Qt +// TODO: We need to use .qrc files to use icons in WhatsThis bubbles. See Qt // Resource System in Qt documentation - ORCAN //const char* presetOpenText = " " // "Click this button to load a saved preset."; @@ -3387,13 +3380,13 @@ paramsOut = 0; plugin = p; setWindowTitle(plugin->titlePrefix() + plugin->name()); - + QToolBar* tools = addToolBar(tr("File Buttons")); QAction* fileOpen = new QAction(QIcon(*openIconS), tr("Load Preset"), this); connect(fileOpen, SIGNAL(triggered()), this, SLOT(load())); tools->addAction(fileOpen); - + QAction* fileSave = new QAction(QIcon(*saveIconS), tr("Save Preset"), this); connect(fileSave, SIGNAL(triggered()), this, SLOT(save())); tools->addAction(fileSave); @@ -3407,7 +3400,7 @@ connect(onOff, SIGNAL(toggled(bool)), SLOT(bypassToggled(bool))); tools->addAction(onOff); - // TODO: We need to use .qrc files to use icons in WhatsThis bubbles. See Qt + // TODO: We need to use .qrc files to use icons in WhatsThis bubbles. See Qt // Resource System in Qt documentation - ORCAN fileOpen->setWhatsThis(tr(presetOpenText)); onOff->setWhatsThis(tr(presetBypassText)); @@ -3439,7 +3432,7 @@ const char* name = ba.constData(); if (*name !='P') continue; - unsigned long parameter; + unsigned long parameter; int rv = sscanf(name, "P%lu", ¶meter); if(rv != 1) continue; @@ -3449,26 +3442,26 @@ gw = new GuiWidgets[nobj]; nobj = 0; QSignalMapper* mapper = new QSignalMapper(this); - - // 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. - + + // 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* 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; QByteArray ba = obj->objectName().toLatin1(); const char* name = ba.constData(); if (*name !='P') continue; - unsigned long parameter; + unsigned long parameter; int rv = sscanf(name, "P%lu", ¶meter); if(rv != 1) continue; @@ -3477,35 +3470,68 @@ mapperPressed->setMapping(obj, nobj); mapperReleased->setMapping(obj, nobj); mapperContextMenuReq->setMapping(obj, nobj); - - gw[nobj].widget = (QWidget*)obj; - gw[nobj].param = parameter; - gw[nobj].type = -1; + + gw[nobj].widget = (QWidget*)obj; + gw[nobj].param = parameter; + gw[nobj].type = -1; + gw[nobj].pressed = false; if (strcmp(obj->metaObject()->className(), "MusEGui::Slider") == 0) { gw[nobj].type = GuiWidgets::SLIDER; - ((Slider*)obj)->setId(nobj); - ((Slider*)obj)->setCursorHoming(true); - for(unsigned long i = 0; i < nobj; i++) + Slider* s = static_cast(obj); + s->setId(nobj); + s->setCursorHoming(true); + + LADSPA_PortRangeHint range = plugin->range(parameter); + double lower = 0.0; // default values + double upper = 1.0; + double dlower = lower; + double dupper = upper; + double val = plugin->param(parameter); + double dval = val; + getPluginConvertedValues(range, lower, upper, dlower, dupper, dval); + + // TODO + //s->setThumbLength(1); + //s->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep); + //s->setScaleMaxMinor(5); + //s->setScale(MusEGlobal::config.minSlider-0.1, 10.0, 6.0, false); + //s->setScale(dlower, dupper, 1.0, false); + //s->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. + //s->setScaleBackBone(false); + //s->setFillThumb(false); + + QFont fnt; + fnt.setFamily("Sans"); + fnt.setPixelSize(9); + //fnt.setStyleStrategy(QFont::PreferBitmap); + fnt.setStyleStrategy(QFont::NoAntialias); + fnt.setHintingPreference(QFont::PreferVerticalHinting); + s->setFont(fnt); + s->setStyleSheet(MusECore::font2StyleSheet(fnt)); + s->setSizeHint(200, 8); + + 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); + ((DoubleLabel*)gw[i].widget)->setSlider(s); } - 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))); + connect(s, SIGNAL(valueChanged(double,int,int)), mapper, SLOT(map())); + connect(s, SIGNAL(sliderPressed(double, int)), SLOT(guiSliderPressed(double, int))); + connect(s, SIGNAL(sliderReleased(double, int)), SLOT(guiSliderReleased(double, int))); + connect(s, 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; ((DoubleLabel*)obj)->setId(nobj); + ((DoubleLabel*)obj)->setAlignment(Qt::AlignCenter); for(unsigned long i = 0; i < nobj; i++) { if(gw[i].type == GuiWidgets::SLIDER && gw[i].param == parameter) { ((DoubleLabel*)obj)->setSlider((Slider*)gw[i].widget); - break; - } + break; + } } connect((DoubleLabel*)obj, SIGNAL(valueChanged(double,int)), mapper, SLOT(map())); } @@ -3515,14 +3541,14 @@ 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 &)), + connect((QCheckBox*)obj, SIGNAL(customContextMenuRequested(const QPoint &)), mapperContextMenuReq, SLOT(map())); } else if (strcmp(obj->metaObject()->className(), "QComboBox") == 0) { gw[nobj].type = GuiWidgets::QCOMBOBOX; gw[nobj].widget->setContextMenuPolicy(Qt::CustomContextMenu); connect((QComboBox*)obj, SIGNAL(activated(int)), mapper, SLOT(map())); - connect((QComboBox*)obj, SIGNAL(customContextMenuRequested(const QPoint &)), + connect((QComboBox*)obj, SIGNAL(customContextMenuRequested(const QPoint &)), mapperContextMenuReq, SLOT(map())); } else { @@ -3537,20 +3563,20 @@ view = new QScrollArea; view->setWidgetResizable(true); setCentralWidget(view); - + mw = new QWidget; QGridLayout* grid = new QGridLayout; grid->setSpacing(2); mw->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); - unsigned long n = plugin->parameters(); + 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) { + for (unsigned long i = 0; i < n; ++i) { QLabel* label = 0; LADSPA_PortRangeHint range = plugin->range(i); double lower = 0.0; // default values @@ -3559,6 +3585,7 @@ double dupper = upper; double val = plugin->param(i); double dval = val; + params[i].pressed = false; params[i].hint = range.HintDescriptor; getPluginConvertedValues(range, lower, upper, dlower, dupper, dval); @@ -3577,6 +3604,7 @@ params[i].type = GuiParam::GUI_SLIDER; params[i].label = new DoubleLabel(val, lower, upper, 0); params[i].label->setFrame(true); + params[i].label->setAlignment(Qt::AlignCenter); params[i].label->setPrecision(2); params[i].label->setId(i); @@ -3589,8 +3617,27 @@ QColor color(c1, c2, c3); Slider* s = new Slider(0, "param", Qt::Horizontal, - Slider::None, color); - + Slider::InsideHorizontal, 8, color, ScaleDraw::TextHighlightSplitAndShadow); + + // TODO + //s->setThumbLength(1); + //s->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep); + //s->setScaleMaxMinor(5); + //s->setScale(MusEGlobal::config.minSlider-0.1, 10.0, 6.0, false); + //s->setScale(dlower, dupper, 1.0, false); + //s->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. + //s->setScaleBackBone(false); + //s->setFillThumb(false); + + QFont fnt; + fnt.setFamily("Sans"); + fnt.setPixelSize(9); + //fnt.setStyleStrategy(QFont::PreferBitmap); + fnt.setStyleStrategy(QFont::NoAntialias); + fnt.setHintingPreference(QFont::PreferVerticalHinting); + s->setFont(fnt); + s->setStyleSheet(MusECore::font2StyleSheet(fnt)); + s->setCursorHoming(true); s->setId(i); s->setSizeHint(200, 8); @@ -3599,7 +3646,7 @@ s->setStep(1.0); s->setValue(dval); params[i].actuator = s; - params[i].label->setSlider((Slider*)params[i].actuator); + params[i].label->setSlider(s); } params[i].actuator->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed)); if (params[i].type == GuiParam::GUI_SLIDER) { @@ -3613,16 +3660,16 @@ grid->addWidget(params[i].actuator, i, 0, 1, 3); } if (params[i].type == GuiParam::GUI_SLIDER) { - connect(params[i].actuator, SIGNAL(sliderMoved(double,int,bool)), SLOT(sliderChanged(double,int,bool))); + connect((Slider*)params[i].actuator, SIGNAL(valueChanged(double,int,int)), SLOT(sliderChanged(double,int,int))); 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))); - connect(params[i].actuator, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(ctrlRightClicked(const QPoint &, int))); + connect((Slider*)params[i].actuator, SIGNAL(sliderPressed(double, int)), SLOT(ctrlPressed(double, int))); + connect((Slider*)params[i].actuator, SIGNAL(sliderReleased(double, int)), SLOT(ctrlReleased(double, int))); + connect((Slider*)params[i].actuator, SIGNAL(sliderRightClicked(const QPoint &, int)), SLOT(ctrlRightClicked(const QPoint &, int))); } else if (params[i].type == GuiParam::GUI_SWITCH){ - connect(params[i].actuator, SIGNAL(checkboxPressed(int)), SLOT(ctrlPressed(int))); - connect(params[i].actuator, SIGNAL(checkboxReleased(int)), SLOT(ctrlReleased(int))); - connect(params[i].actuator, SIGNAL(checkboxRightClicked(const QPoint &, int)), SLOT(ctrlRightClicked(const QPoint &, int))); + connect((CheckBox*)params[i].actuator, SIGNAL(checkboxPressed(int)), SLOT(switchPressed(int))); + connect((CheckBox*)params[i].actuator, SIGNAL(checkboxReleased(int)), SLOT(switchReleased(int))); + connect((CheckBox*)params[i].actuator, SIGNAL(checkboxRightClicked(const QPoint &, int)), SLOT(ctrlRightClicked(const QPoint &, int))); } } @@ -3631,16 +3678,16 @@ if (n2 > 0) { paramsOut = new GuiParam[n2]; - int h = fm.height() - 2; for (int i = 0; i < n2; ++i) { QLabel* label = 0; LADSPA_PortRangeHint range = plugin->rangeOut(i); double lower = 0.0; // default values - double upper = 1.0; + double upper = 32768.0; // Many latency outs have no hints so set this arbitrarily high double dlower = lower; double dupper = upper; double val = plugin->paramOut(i); double dval = val; + paramsOut[i].pressed = false; paramsOut[i].hint = range.HintDescriptor; getPluginConvertedValues(range, lower, upper, dlower, dupper, dval); @@ -3648,17 +3695,34 @@ paramsOut[i].type = GuiParam::GUI_METER; paramsOut[i].label = new DoubleLabel(val, lower, upper, 0); paramsOut[i].label->setFrame(true); + paramsOut[i].label->setAlignment(Qt::AlignCenter); paramsOut[i].label->setPrecision(2); paramsOut[i].label->setId(i); Meter::MeterType mType=Meter::LinMeter; - if(LADSPA_IS_HINT_INTEGER(range.HintDescriptor)) + //if(LADSPA_IS_HINT_INTEGER(range.HintDescriptor)) + if(LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) mType=Meter::DBMeter; - VerticalMeter* m = new VerticalMeter(this, mType); + Meter* m = new Meter(this, + mType, + Qt::Horizontal, + dlower, dupper, + Meter::InsideHorizontal); //, ScaleDraw::TextHighlightNone); + m->setRefreshRate(MusEGlobal::config.guiRefresh); + m->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum); + m->setVal(dval, dval, false); + m->setScaleBackBone(false); + m->setPrimaryColor(MusEGlobal::config.audioMeterPrimaryColor); + + QFont fnt; + fnt.setFamily("Sans"); + fnt.setPixelSize(9); + //fnt.setStyleStrategy(QFont::PreferBitmap); + fnt.setStyleStrategy(QFont::NoAntialias); + fnt.setHintingPreference(QFont::PreferVerticalHinting); + m->setFont(fnt); + m->setStyleSheet(MusECore::font2StyleSheet(fnt)); - m->setRange(dlower, dupper); - m->setVal(dval); - m->setFixedHeight(h); paramsOut[i].actuator = m; label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); paramsOut[i].label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); @@ -3690,6 +3754,83 @@ delete[] paramsOut; } +void PluginGui::hideEvent(QHideEvent *e) +{ + if(plugin) + plugin->saveGeometry(geometry().x(), geometry().y(), geometry().width(), geometry().height()); + + e->ignore(); + QMainWindow::hideEvent(e); +} + +void PluginGui::showEvent(QShowEvent *e) +{ + int x = 0, y = 0, w = 0, h = 0; + if(plugin) + plugin->savedGeometry(&x, &y, &w, &h); + +#ifdef QT_SHOW_POS_BUG_WORKAROUND + // Because of the bug, no matter what we must supply a position, + // even upon first showing... + + // Check sane size. + if(w == 0) + w = sizeHint().width(); + if(h == 0) + h = sizeHint().height(); + + // No size hint? Try minimum size. + if(w == 0) + w = minimumSize().width(); + if(h == 0) + h = minimumSize().height(); + + // Fallback. + if(w == 0) + w = 200; + if(h == 0) + h = 200; + + setGeometry(x, y, w, h); + +#else + + // If the saved geometry is valid, use it. + // Otherwise this is probably the first time showing, + // so do not set a geometry - let Qt pick one + // (using auto-placement and sizeHint). + if(!(x == 0 && y == 0 && w == 0 && h == 0)) + { + // Check sane size. + if(w == 0) + w = sizeHint().width(); + if(h == 0) + h = sizeHint().height(); + + // No size hint? Try minimum size. + if(w == 0) + w = minimumSize().width(); + if(h == 0) + h = minimumSize().height(); + + // Fallback. + if(w == 0) + w = 200; + if(h == 0) + h = 200; + + setGeometry(x, y, w, h); + } +#endif + + // Convenience: If the window was minimized, restore it. + if(isMinimized()) + setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + + e->ignore(); + QMainWindow::showEvent(e); +} + void PluginGui::getPluginConvertedValues(LADSPA_PortRangeHint range, double &lower, double &upper, double &dlower, double &dupper, double &dval) { @@ -3721,142 +3862,168 @@ void PluginGui::heartBeat() { - updateControls(); // FINDMICHJETZT TODO: this is not good. we have concurrent - // access from the audio thread (possibly writing control values) - // while reading them from some GUI thread. this will lead - // to problems if writing floats is non-atomic + updateControls(); } //--------------------------------------------------------- // ctrlPressed //--------------------------------------------------------- -void PluginGui::ctrlPressed(int param) +void PluginGui::ctrlPressed(double /*val*/, int param) +{ + params[param].pressed = true; + MusECore::AudioTrack* track = plugin->track(); + int id = plugin->id(); + if(id != -1) + { + id = MusECore::genACnum(id, param); + if(params[param].type == GuiParam::GUI_SLIDER) + { + double val = ((Slider*)params[param].actuator)->value(); + if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) + val = muse_db2val(val); + else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) + val = rint(val); + params[param].label->blockSignals(true); + params[param].label->setValue(val); + params[param].label->blockSignals(false); + if(track) + { + track->startAutoRecord(id, val); + track->setPluginCtrlVal(id, val); + } + } + else if(params[param].type == GuiParam::GUI_SWITCH) + { + float val = (float)((CheckBox*)params[param].actuator)->isChecked(); + if(track) + { + track->startAutoRecord(id, val); + track->setPluginCtrlVal(id, val); + } + } + } + plugin->enableController(param, false); +} + +//--------------------------------------------------------- +// ctrlReleased +//--------------------------------------------------------- + +void PluginGui::ctrlReleased(double /*val*/, int param) { AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); - if(track) - at = track->automationType(); - - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - plugin->enableController(param, false); - + if(track) + at = track->automationType(); + + int id = plugin->id(); + if(track && id != -1) + { + id = MusECore::genACnum(id, param); + if(params[param].type == GuiParam::GUI_SLIDER) + { + double val = ((Slider*)params[param].actuator)->value(); + if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) + val = muse_db2val(val); + else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) + val = rint(val); + track->stopAutoRecord(id, val); + } + } + + // Special for switch - don't enable controller until transport stopped. + if ((at == AUTO_OFF) || + (at == AUTO_TOUCH && (params[param].type != GuiParam::GUI_SWITCH || + !MusEGlobal::audio->isPlaying()) ) ) + plugin->enableController(param, true); + + params[param].pressed = false; +} + +//--------------------------------------------------------- +// ctrlRightClicked +//--------------------------------------------------------- + +void PluginGui::ctrlRightClicked(const QPoint &p, int param) +{ + int id = plugin->id(); + if(id != -1) + MusEGlobal::song->execAutomationCtlPopup(plugin->track(), p, MusECore::genACnum(id, param)); +} + +//--------------------------------------------------------- +// switchPressed +//--------------------------------------------------------- + +void PluginGui::switchPressed(int param) +{ + params[param].pressed = true; + MusECore::AudioTrack* track = plugin->track(); int id = plugin->id(); - - if(id == -1) - return; - - id = MusECore::genACnum(id, param); - - if(params[param].type == GuiParam::GUI_SLIDER) - { - double val = ((Slider*)params[param].actuator)->value(); - if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) - val = pow(10.0, val/20.0); - else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) - val = rint(val); - plugin->setParam(param, val); - ((DoubleLabel*)params[param].label)->setValue(val); - - if(track) - { - track->setPluginCtrlVal(id, val); - track->startAutoRecord(id, val); - } - } - else if(params[param].type == GuiParam::GUI_SWITCH) + if(id != -1) { - float val = (float)((CheckBox*)params[param].actuator)->isChecked(); - plugin->setParam(param, val); - - if(track) + id = MusECore::genACnum(id, param); + if(params[param].type == GuiParam::GUI_SWITCH) { - track->setPluginCtrlVal(id, val); - track->startAutoRecord(id, val); + float val = (float)((CheckBox*)params[param].actuator)->isChecked(); + if(track) + { + track->startAutoRecord(id, val); + track->setPluginCtrlVal(id, val); + } } } + plugin->enableController(param, false); } //--------------------------------------------------------- -// ctrlReleased +// switchReleased //--------------------------------------------------------- -void PluginGui::ctrlReleased(int param) +void PluginGui::switchReleased(int param) { AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); if(track) at = track->automationType(); - + // Special for switch - don't enable controller until transport stopped. if ((at == AUTO_OFF) || - (at == AUTO_READ) || (at == AUTO_TOUCH && (params[param].type != GuiParam::GUI_SWITCH || !MusEGlobal::audio->isPlaying()) ) ) plugin->enableController(param, true); - - int id = plugin->id(); - if(!track || id == -1) - return; - id = MusECore::genACnum(id, param); - - if(params[param].type == GuiParam::GUI_SLIDER) - { - double val = ((Slider*)params[param].actuator)->value(); - if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) - val = pow(10.0, val/20.0); - else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) - val = rint(val); - track->stopAutoRecord(id, val); - } -} - -//--------------------------------------------------------- -// ctrlRightClicked -//--------------------------------------------------------- -void PluginGui::ctrlRightClicked(const QPoint &p, int param) -{ - int id = plugin->id(); - if(id != -1) - MusEGlobal::song->execAutomationCtlPopup(plugin->track(), p, MusECore::genACnum(id, param)); + params[param].pressed = false; } //--------------------------------------------------------- // sliderChanged //--------------------------------------------------------- -void PluginGui::sliderChanged(double val, int param, bool shift_pressed) +void PluginGui::sliderChanged(double val, int param, int scrollMode) { - 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); + val = muse_db2val(val); else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) val = rint(val); - - if (plugin->param(param) != val) { - plugin->setParam(param, val); - ((DoubleLabel*)params[param].label)->setValue(val); - } - + + params[param].label->blockSignals(true); + params[param].label->setValue(val); + params[param].label->blockSignals(false); int id = plugin->id(); - if(id == -1) - return; - id = MusECore::genACnum(id, param); - - if(track) + if(track && id != -1) { - track->setPluginCtrlVal(id, val); - if (!shift_pressed) track->recordAutomation(id, val); //with shift, we get straight lines :) - } + id = MusECore::genACnum(id, param); + // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. + // ScrDirect mode is one-time only on press with modifier. + if(scrollMode != SliderBase::ScrDirect) + track->recordAutomation(id, val); + } + plugin->setParam(param, val); // Schedules a timed control change. + plugin->enableController(param, false); } //--------------------------------------------------------- @@ -3865,36 +4032,24 @@ void PluginGui::labelChanged(double val, int param) { - 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); - + double dval = val; if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) dval = MusECore::fast_log10(val) * 20.0; else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) dval = rint(val); - if (plugin->param(param) != val) { - plugin->setParam(param, val); - ((Slider*)params[param].actuator)->setValue(dval); - } - + params[param].actuator->blockSignals(true); + ((Slider*)params[param].actuator)->setValue(dval); + params[param].actuator->blockSignals(false); int id = plugin->id(); - if(id == -1) - return; - - id = MusECore::genACnum(id, param); - - if(track) + if(track && id != -1) { - track->setPluginCtrlVal(id, val); + id = MusECore::genACnum(id, param); track->startAutoRecord(id, val); - } + } + plugin->setParam(param, val); // Schedules a timed control change. + plugin->enableController(param, false); } //--------------------------------------------------------- @@ -3929,14 +4084,14 @@ if (mode == 0 && tag == "muse") mode = 1; else if (mode == 1 && tag == "plugin") { - + if(plugin->readConfiguration(xml, true)) { QMessageBox::critical(this, QString("MusE"), tr("Error reading preset. Might not be right type for this plugin")); goto ende; } - + mode = 0; } else @@ -3949,7 +4104,7 @@ { plugin->updateControllers(); goto ende; - } + } default: break; } @@ -4021,7 +4176,7 @@ void PluginGui::updateValues() { if (params) { - for (unsigned long i = 0; i < plugin->parameters(); ++i) { + for (unsigned long i = 0; i < plugin->parameters(); ++i) { GuiParam* gp = ¶ms[i]; if (gp->type == GuiParam::GUI_SLIDER) { double lv = plugin->param(i); @@ -4032,21 +4187,28 @@ { sv = rint(lv); lv = sv; - } + } + gp->label->blockSignals(true); + gp->actuator->blockSignals(true); gp->label->setValue(lv); ((Slider*)(gp->actuator))->setValue(sv); + gp->label->blockSignals(false); + gp->actuator->blockSignals(false); } else if (gp->type == GuiParam::GUI_SWITCH) { + gp->actuator->blockSignals(true); ((CheckBox*)(gp->actuator))->setChecked(int(plugin->param(i))); + gp->actuator->blockSignals(false); } } } else if (gw) { - for (unsigned long i = 0; i < nobj; ++i) { + for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; int type = gw[i].type; - unsigned long param = gw[i].param; - float val = plugin->param(param); + unsigned long param = gw[i].param; + double val = plugin->param(param); + widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: ((Slider*)widget)->setValue(val); // Note conversion to double @@ -4061,6 +4223,7 @@ ((QComboBox*)widget)->setCurrentIndex(int(val)); break; } + widget->blockSignals(false); } } } @@ -4089,7 +4252,7 @@ sv = rint(lv); lv = sv; } - ((VerticalMeter*)(gp->actuator))->setVal(sv); + ((Meter*)(gp->actuator))->setVal(sv, sv, false); gp->label->setValue(lv); } @@ -4098,126 +4261,93 @@ if (params) { - for (unsigned long i = 0; i < plugin->parameters(); ++i) { - GuiParam* gp = ¶ms[i]; - if (gp->type == GuiParam::GUI_SLIDER) { - { - 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; - else - if (LADSPA_IS_HINT_INTEGER(params[i].hint)) - { - sv = rint(lv); - lv = sv; - } - if(((Slider*)(gp->actuator))->value() != sv) + for (unsigned long i = 0; i < plugin->parameters(); ++i) { + GuiParam* gp = ¶ms[i]; + if(gp->pressed) // Inhibit the controller stream if control is currently pressed. + continue; + double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i), + MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || + plugin->track()->automationType() == AUTO_OFF || + !plugin->controllerEnabled(i)); + if (gp->type == GuiParam::GUI_SLIDER) { { - gp->label->blockSignals(true); - ((Slider*)(gp->actuator))->blockSignals(true); - ((Slider*)(gp->actuator))->setValue(sv); - gp->label->setValue(lv); - ((Slider*)(gp->actuator))->blockSignals(false); - gp->label->blockSignals(false); - } + double sv = v; + if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint)) + sv = MusECore::fast_log10(v) * 20.0; + else + if (LADSPA_IS_HINT_INTEGER(params[i].hint)) + { + sv = rint(v); + v = sv; + } + if(((Slider*)(gp->actuator))->value() != sv) + { + gp->label->blockSignals(true); + gp->actuator->blockSignals(true); + ((Slider*)(gp->actuator))->setValue(sv); + gp->label->setValue(v); + gp->actuator->blockSignals(false); + gp->label->blockSignals(false); + } + } } - } - else if (gp->type == GuiParam::GUI_SWITCH) { - { - 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) + else if (gp->type == GuiParam::GUI_SWITCH) { { - ((CheckBox*)(gp->actuator))->blockSignals(true); - ((CheckBox*)(gp->actuator))->setChecked(v); - ((CheckBox*)(gp->actuator))->blockSignals(false); - } + bool b = (int)v; + if(((CheckBox*)(gp->actuator))->isChecked() != b) + { + gp->actuator->blockSignals(true); + ((CheckBox*)(gp->actuator))->setChecked(b); + gp->actuator->blockSignals(false); + } + } } - } - } + } } else if (gw) { - for (unsigned long i = 0; i < nobj; ++i) { + for (unsigned long i = 0; i < nobj; ++i) { + if(gw[i].pressed) // Inhibit the controller stream if control is currently pressed. + continue; QWidget* widget = gw[i].widget; int type = gw[i].type; - unsigned long param = gw[i].param; + unsigned long param = gw[i].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)); + widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: { - 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); ((Slider*)widget)->setValue(v); - ((Slider*)widget)->blockSignals(false); - } } break; case GuiWidgets::DOUBLE_LABEL: { - 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); ((DoubleLabel*)widget)->setValue(v); - ((DoubleLabel*)widget)->blockSignals(false); - } } break; case GuiWidgets::QCHECKBOX: - { - 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)); + { + bool b = (bool)v; if(((QCheckBox*)widget)->isChecked() != b) - { - ((QCheckBox*)widget)->blockSignals(true); ((QCheckBox*)widget)->setChecked(b); - ((QCheckBox*)widget)->blockSignals(false); - } } break; case GuiWidgets::QCOMBOBOX: - { - 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)); + { + int n = (int)v; if(((QComboBox*)widget)->currentIndex() != n) - { - ((QComboBox*)widget)->blockSignals(true); ((QComboBox*)widget)->setCurrentIndex(n); - ((QComboBox*)widget)->blockSignals(false); - } } break; } - } + widget->blockSignals(false); + } } } @@ -4228,22 +4358,20 @@ void PluginGui::guiParamChanged(int idx) { QWidget* w = gw[idx].widget; - unsigned long param = gw[idx].param; + unsigned long param = gw[idx].param; int type = gw[idx].type; - 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); - + double val = 0.0; + bool ignoreRecAutomation = false; switch(type) { case GuiWidgets::SLIDER: val = ((Slider*)w)->value(); + // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. + // ScrDirect mode is one-time only on press with modifier. + if(((Slider*)w)->scrollMode() == Slider::ScrDirect) + ignoreRecAutomation = true; break; case GuiWidgets::DOUBLE_LABEL: val = ((DoubleLabel*)w)->value(); @@ -4256,11 +4384,12 @@ break; } - for (unsigned long i = 0; i < nobj; ++i) { + for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; if (widget == w || param != gw[i].param) continue; int type = gw[i].type; + widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: ((Slider*)widget)->setValue(val); @@ -4275,25 +4404,28 @@ ((QComboBox*)widget)->setCurrentIndex(int(val)); break; } + widget->blockSignals(false); } - + int id = plugin->id(); if(track && id != -1) { id = MusECore::genACnum(id, param); - track->setPluginCtrlVal(id, val); - switch(type) + switch(type) { case GuiWidgets::DOUBLE_LABEL: case GuiWidgets::QCHECKBOX: track->startAutoRecord(id, val); break; default: - track->recordAutomation(id, val); - break; - } - } - plugin->setParam(param, val); + if(!ignoreRecAutomation) + track->recordAutomation(id, val); + break; + } + } + + plugin->setParam(param, val); // Schedules a timed control change. + plugin->enableController(param, false); } //--------------------------------------------------------- @@ -4302,27 +4434,20 @@ void PluginGui::guiParamPressed(int idx) { - unsigned long param = gw[idx].param; - - AutomationType at = AUTO_OFF; - MusECore::AudioTrack* track = plugin->track(); - if(track) - at = track->automationType(); - - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - plugin->enableController(param, false); - - int id = plugin->id(); - if(!track || id == -1) - return; - - id = MusECore::genACnum(id, param); - + gw[idx].pressed = true; + unsigned long param = gw[idx].param; + plugin->enableController(param, false); + + //MusECore::AudioTrack* track = plugin->track(); + //int id = plugin->id(); + //if(!track || id == -1) + // return; + //id = MusECore::genACnum(id, param); // 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). RECHECK: Qt4 it does? - /* + /* switch(type) { case GuiWidgets::QCHECKBOX: double val = (double)((CheckBox*)w)->isChecked(); @@ -4333,7 +4458,7 @@ track->startAutoRecord(id, val); break; } - */ + */ } //--------------------------------------------------------- @@ -4342,33 +4467,29 @@ void PluginGui::guiParamReleased(int idx) { - unsigned long param = gw[idx].param; + unsigned long param = gw[idx].param; int type = gw[idx].type; - + AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); if(track) at = track->automationType(); - + // Special for switch - don't enable controller until transport stopped. if ((at == AUTO_OFF) || - (at == AUTO_READ) || (at == AUTO_TOUCH && (type != GuiWidgets::QCHECKBOX || !MusEGlobal::audio->isPlaying()) ) ) plugin->enableController(param, true); - - int id = plugin->id(); - - if(!track || id == -1) - return; - - id = MusECore::genACnum(id, param); - + + //int id = plugin->id(); + //if(!track || id == -1) + // return; + //id = MusECore::genACnum(id, param); // 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). // RECHECK Qt4 it does? - /* + /* switch(type) { case GuiWidgets::QCHECKBOX: double val = (double)((CheckBox*)w)->isChecked(); @@ -4380,94 +4501,85 @@ break; } */ + + gw[idx].pressed = false; } //--------------------------------------------------------- // guiSliderPressed //--------------------------------------------------------- -void PluginGui::guiSliderPressed(int idx) - { - unsigned long param = gw[idx].param; +void PluginGui::guiSliderPressed(double /*val*/, int idx) +{ + gw[idx].pressed = true; + unsigned long param = gw[idx].param; QWidget *w = gw[idx].widget; - - AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); - if(track) - at = track->automationType(); - int id = plugin->id(); - - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - plugin->enableController(param, false); - - if(!track || id == -1) - return; - - id = MusECore::genACnum(id, param); - - double val = ((Slider*)w)->value(); - plugin->setParam(param, val); - - track->setPluginCtrlVal(id, val); - 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) { - QWidget* widget = gw[i].widget; - if (widget == w || param != gw[i].param) - continue; - int type = gw[i].type; - switch(type) { - case GuiWidgets::SLIDER: - ((Slider*)widget)->setValue(val); - break; - case GuiWidgets::DOUBLE_LABEL: - ((DoubleLabel*)widget)->setValue(val); - break; - case GuiWidgets::QCHECKBOX: - ((QCheckBox*)widget)->setChecked(int(val)); - break; - case GuiWidgets::QCOMBOBOX: - ((QComboBox*)widget)->setCurrentIndex(int(val)); - break; - } - } + if(track && id != -1) + { + id = MusECore::genACnum(id, param); + double val = ((Slider*)w)->value(); + 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) { + QWidget* widget = gw[i].widget; + if (widget == w || param != gw[i].param) + continue; + int type = gw[i].type; + widget->blockSignals(true); + switch(type) { + case GuiWidgets::SLIDER: + ((Slider*)widget)->setValue(val); + break; + case GuiWidgets::DOUBLE_LABEL: + ((DoubleLabel*)widget)->setValue(val); + break; + case GuiWidgets::QCHECKBOX: + ((QCheckBox*)widget)->setChecked(int(val)); + break; + case GuiWidgets::QCOMBOBOX: + ((QComboBox*)widget)->setCurrentIndex(int(val)); + break; + } + widget->blockSignals(false); + } + track->setPluginCtrlVal(id, val); } + plugin->enableController(param, false); +} //--------------------------------------------------------- // guiSliderReleased //--------------------------------------------------------- -void PluginGui::guiSliderReleased(int idx) +void PluginGui::guiSliderReleased(double /*val*/, int idx) { int param = gw[idx].param; QWidget *w = gw[idx].widget; - + AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); if(track) at = track->automationType(); - - /* equivalent to - if ((at == AUTO_OFF) || - (at == AUTO_READ) || - (at == AUTO_TOUCH && (type != GuiWidgets::QCHECKBOX || <--- this type is SLIDER != CHECKBOX -> true - !MusEGlobal::audio->isPlaying()) ) ) <--- above==true -> this doesn't matter */ - if (at == AUTO_OFF || at == AUTO_READ || at == AUTO_TOUCH) - plugin->enableController(param, true); - + int id = plugin->id(); - - if(!track || id == -1) - return; - - id = MusECore::genACnum(id, param); - - double val = ((Slider*)w)->value(); - track->stopAutoRecord(id, val); + + if(track && id != -1) + { + id = MusECore::genACnum(id, param); + + double val = ((Slider*)w)->value(); + track->stopAutoRecord(id, val); } - + + if (at == AUTO_OFF || + at == AUTO_TOUCH) + plugin->enableController(param, true); + + gw[idx].pressed = false; + } + //--------------------------------------------------------- // guiSliderRightClicked //--------------------------------------------------------- @@ -4495,9 +4607,9 @@ QWidget* PluginLoader::createWidget(const QString & className, QWidget * parent, const QString & name) { if(className == QString("MusEGui::DoubleLabel")) - return new DoubleLabel(parent, name.toLatin1().constData()); + return new DoubleLabel(parent, name.toLatin1().constData()); if(className == QString("MusEGui::Slider")) - return new Slider(parent, name.toLatin1().constData(), Qt::Horizontal); + return new Slider(parent, name.toLatin1().constData(), Qt::Horizontal, Slider::InsideHorizontal, 8, QColor(), ScaleDraw::TextHighlightSplitAndShadow); return QUiLoader::createWidget(className, parent, name); } @@ -4510,33 +4622,33 @@ static void writePluginGroupNames(int level, MusECore::Xml& xml) { xml.tag(level++, "group_names"); - + for (QList::iterator it=plugin_group_names.begin(); it!=plugin_group_names.end(); it++) xml.strTag(level, "name", *it); - + xml.etag(--level, "group_names"); } static void writePluginGroupMap(int level, MusECore::Xml& xml) { using MusECore::PluginGroups; - + xml.tag(level++, "group_map"); - + for (PluginGroups::iterator it=plugin_groups.begin(); it!=plugin_groups.end(); it++) - if (!it.value().empty()) - { - xml.tag(level++, "entry"); - - xml.strTag(level, "lib", it.key().first); - xml.strTag(level, "label", it.key().second); - - for (QSet::iterator it2=it.value().begin(); it2!=it.value().end(); it2++) - xml.intTag(level, "group", *it2); + if (!it.value().empty()) + { + xml.tag(level++, "entry"); + + xml.strTag(level, "lib", it.key().first); + xml.strTag(level, "label", it.key().second); + + for (QSet::iterator it2=it.value().begin(); it2!=it.value().end(); it2++) + xml.intTag(level, "group", *it2); + + xml.etag(--level, "entry"); + } - xml.etag(--level, "entry"); - } - xml.etag(--level, "group_map"); } @@ -4546,145 +4658,145 @@ writePluginGroupNames(level, xml); writePluginGroupMap(level, xml); - + xml.etag(--level, "plugin_groups"); } static void readPluginGroupNames(MusECore::Xml& xml) { - plugin_group_names.clear(); - - for (;;) - { - MusECore::Xml::Token token = xml.parse(); - if (token == MusECore::Xml::Error || token == MusECore::Xml::End) - break; - - const QString& tag = xml.s1(); - switch (token) - { - case MusECore::Xml::TagStart: - if (tag=="name") - plugin_group_names.append(xml.parse1()); - else - xml.unknown("readPluginGroupNames"); - break; - - case MusECore::Xml::TagEnd: - if (tag == "group_names") - return; - - default: - break; - } - } + plugin_group_names.clear(); + + for (;;) + { + MusECore::Xml::Token token = xml.parse(); + if (token == MusECore::Xml::Error || token == MusECore::Xml::End) + break; + + const QString& tag = xml.s1(); + switch (token) + { + case MusECore::Xml::TagStart: + if (tag=="name") + plugin_group_names.append(xml.parse1()); + else + xml.unknown("readPluginGroupNames"); + break; + + case MusECore::Xml::TagEnd: + if (tag == "group_names") + return; + + default: + break; + } + } } - + static void readPluginGroupMap(MusECore::Xml& xml) { - plugin_groups.clear(); - - for (;;) - { - MusECore::Xml::Token token = xml.parse(); - if (token == MusECore::Xml::Error || token == MusECore::Xml::End) - break; - - const QString& tag = xml.s1(); - switch (token) - { - case MusECore::Xml::TagStart: - if (tag=="entry") - { - QString lib; - QString label; - QSet groups; - bool read_lib=false, read_label=false; - - for (;;) - { - MusECore::Xml::Token token = xml.parse(); - if (token == MusECore::Xml::Error || token == MusECore::Xml::End) - break; - - const QString& tag = xml.s1(); - switch (token) - { - case MusECore::Xml::TagStart: - if (tag=="lib") - { - lib=xml.parse1(); - read_lib=true; - } - else if (tag=="label") - { - label=xml.parse1(); - read_label=true; - } - else if (tag=="group") - groups.insert(xml.parseInt()); - else - xml.unknown("readPluginGroupMap"); - break; - - case MusECore::Xml::TagEnd: - if (tag == "entry") - goto done_reading_entry; - - default: - break; - } - } + plugin_groups.clear(); + + for (;;) + { + MusECore::Xml::Token token = xml.parse(); + if (token == MusECore::Xml::Error || token == MusECore::Xml::End) + break; + + const QString& tag = xml.s1(); + switch (token) + { + case MusECore::Xml::TagStart: + if (tag=="entry") + { + QString lib; + QString label; + QSet groups; + bool read_lib=false, read_label=false; + + for (;;) + { + MusECore::Xml::Token token = xml.parse(); + if (token == MusECore::Xml::Error || token == MusECore::Xml::End) + break; + + const QString& tag = xml.s1(); + switch (token) + { + case MusECore::Xml::TagStart: + if (tag=="lib") + { + lib=xml.parse1(); + read_lib=true; + } + else if (tag=="label") + { + label=xml.parse1(); + read_label=true; + } + else if (tag=="group") + groups.insert(xml.parseInt()); + else + xml.unknown("readPluginGroupMap"); + break; + + case MusECore::Xml::TagEnd: + if (tag == "entry") + goto done_reading_entry; + + default: + break; + } + } done_reading_entry: - if (read_lib && read_label) - plugin_groups.get(lib,label)=groups; - else - fprintf(stderr,"ERROR: plugin group map entry without lib or label!\n"); - } - else - xml.unknown("readPluginGroupMap"); - break; - - case MusECore::Xml::TagEnd: - if (tag == "group_map") - return; - - default: - break; - } - } + if (read_lib && read_label) + plugin_groups.get(lib,label)=groups; + else + fprintf(stderr,"ERROR: plugin group map entry without lib or label!\n"); + } + else + xml.unknown("readPluginGroupMap"); + break; + + case MusECore::Xml::TagEnd: + if (tag == "group_map") + return; + + default: + break; + } + } } void readPluginGroupConfiguration(MusECore::Xml& xml) { - for (;;) - { - MusECore::Xml::Token token = xml.parse(); - if (token == MusECore::Xml::Error || token == MusECore::Xml::End) - break; - - const QString& tag = xml.s1(); - switch (token) - { - case MusECore::Xml::TagStart: - if (tag=="group_names") - readPluginGroupNames(xml); - else if (tag=="group_map") - readPluginGroupMap(xml); - else - xml.unknown("readPluginGroupConfiguration"); - break; - - case MusECore::Xml::TagEnd: - if (tag == "plugin_groups") - return; - - default: - break; - } - } + for (;;) + { + MusECore::Xml::Token token = xml.parse(); + if (token == MusECore::Xml::Error || token == MusECore::Xml::End) + break; + + const QString& tag = xml.s1(); + switch (token) + { + case MusECore::Xml::TagStart: + if (tag=="group_names") + readPluginGroupNames(xml); + else if (tag=="group_map") + readPluginGroupMap(xml); + else + xml.unknown("readPluginGroupConfiguration"); + break; + + case MusECore::Xml::TagEnd: + if (tag == "plugin_groups") + return; + + default: + break; + } + } } - + } // namespace MusEGlobal diff -Nru muse-2.1.2/muse/plugin.h muse-3.0.2+ds1/muse/plugin.h --- muse-2.1.2/muse/plugin.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/plugin.h 2018-01-29 20:07:03.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: plugin.h,v 1.9.2.13 2009/12/06 01:25:21 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -38,7 +38,6 @@ #include #include - #include #include "globals.h" #include "globaldefs.h" @@ -46,7 +45,7 @@ #include "controlfifo.h" #include "config.h" - + #ifdef OSC_SUPPORT #include "osc.h" #endif @@ -55,6 +54,15 @@ #include #endif +// BUG I have filed a Qt bug report 64773. If we do NOT +// set a position on a new QWidget, Qt seems to take control +// of the position management such that it will NOT accept +// any new position after that! It seems to decide to +// remain in 'auto-placement' mode forever. +// If the bug is ever fixed by Qt, this define should then +// become version-sensitive. 2017/11/28 Tim. +#define QT_SHOW_POS_BUG_WORKAROUND 1; + class QAbstractButton; class QComboBox; class QRadioButton; @@ -64,6 +72,8 @@ class QTreeWidget; class QRect; class QByteArray; +class QCloseEvent; +class QShowEvent; namespace MusEGui { class PluginGui; @@ -75,17 +85,22 @@ class Xml; class MidiController; - +class PluginI; //--------------------------------------------------------- // Plugin //--------------------------------------------------------- class Plugin { - + + public: + // Can be Or'd together. + enum PluginFeature { NoFeatures=0x00, FixedBlockSize=0x01, PowerOf2BlockSize=0x02, NoInPlaceProcessing=0x04 }; + typedef int PluginFeatures; + protected: friend class PluginI; - + void* _handle; int _references; int _instNo; @@ -97,30 +112,37 @@ QString _name; QString _maker; QString _copyright; - + bool _isDssiSynth; bool _isDssi; + bool _isLV2Synth; + bool _isLV2Plugin; // Hack: Special flag required. bool _isDssiVst; - + bool _isVstNativeSynth; + bool _isVstNativePlugin; + #ifdef DSSI_SUPPORT const DSSI_Descriptor* dssi_descr; #endif - + unsigned long _portCount; unsigned long _inports; unsigned long _outports; unsigned long _controlInPorts; unsigned long _controlOutPorts; std::vector rpIdx; // Port number to control input index. Item is -1 if it's not a control input. - - bool _inPlaceCapable; - + + PluginFeatures _requiredFeatures; + public: - Plugin(QFileInfo* f, const LADSPA_Descriptor* d, bool isDssi = false, bool isDssiSynth = false); - ~Plugin(); - - QString label() const { return _label; } + Plugin() {} //empty constructor for LV2PluginWrapper + Plugin(QFileInfo* f, const LADSPA_Descriptor* d, + bool isDssi = false, bool isDssiSynth = false, bool isDssiVst = false, + PluginFeatures reqFeatures = NoFeatures); + virtual ~Plugin(); + virtual Plugin::PluginFeatures requiredFeatures() const { return _requiredFeatures; } + virtual QString label() const { return _label; } QString name() const { return _name; } unsigned long id() const { return _uniqueID; } QString maker() const { return _maker; } @@ -129,82 +151,86 @@ QString dirPath(bool complete = true) const { return complete ? fi.absolutePath() : fi.path(); } QString filePath() const { return fi.filePath(); } QString fileName() const { return fi.fileName(); } + int references() const { return _references; } - int incReferences(int); + virtual int incReferences(int); int instNo() { return _instNo++; } - bool isDssiPlugin() const { return _isDssi; } - bool isDssiSynth() const { return _isDssiSynth; } - - LADSPA_Handle instantiate(); - void activate(LADSPA_Handle handle) { + bool isDssiPlugin() const { return _isDssi; } + bool isDssiSynth() const { return _isDssiSynth; } + inline bool isLV2Plugin() const { return _isLV2Plugin; } //inline it to use in RT audio thread + bool isLV2Synth() const { return _isLV2Synth; } + inline bool isVstNativePlugin() const { return _isVstNativePlugin; } //inline it to use in RT audio thread + bool isVstNativeSynth() const { return _isVstNativeSynth; } + + virtual LADSPA_Handle instantiate(PluginI *); + virtual void activate(LADSPA_Handle handle) { if (plugin && plugin->activate) plugin->activate(handle); } - void deactivate(LADSPA_Handle handle) { + virtual void deactivate(LADSPA_Handle handle) { if (plugin && plugin->deactivate) plugin->deactivate(handle); } - void cleanup(LADSPA_Handle handle) { + virtual void cleanup(LADSPA_Handle handle) { if (plugin && plugin->cleanup) plugin->cleanup(handle); } - void connectPort(LADSPA_Handle handle, unsigned long port, float* value) { + virtual void connectPort(LADSPA_Handle handle, unsigned long port, float* value) { if(plugin) plugin->connect_port(handle, port, value); } - void apply(LADSPA_Handle handle, unsigned long n) { + virtual void apply(LADSPA_Handle handle, unsigned long n) { if(plugin) plugin->run(handle, n); } - + #ifdef OSC_SUPPORT int oscConfigure(LADSPA_Handle handle, const char* key, const char* value); #endif - + unsigned long ports() { return _portCount; } - - LADSPA_PortDescriptor portd(unsigned long k) const { + + virtual LADSPA_PortDescriptor portd(unsigned long k) const { return plugin ? plugin->PortDescriptors[k] : 0; } - - LADSPA_PortRangeHint range(unsigned long i) { + + virtual LADSPA_PortRangeHint range(unsigned long i) { // FIXME: //return plugin ? plugin->PortRangeHints[i] : 0; DELETETHIS return plugin->PortRangeHints[i]; } - float defaultValue(unsigned long port) const; - void range(unsigned long i, float*, float*) const; - CtrlValueType ctrlValueType(unsigned long i) const; - CtrlList::Mode ctrlMode(unsigned long i) const; - - const char* portName(unsigned long i) { + virtual double defaultValue(unsigned long port) const; + virtual void range(unsigned long i, float*, float*) const; + virtual CtrlValueType ctrlValueType(unsigned long i) const; + virtual CtrlList::Mode ctrlMode(unsigned long i) const; + + virtual const char* portName(unsigned long i) { return plugin ? plugin->PortNames[i] : 0; } - + unsigned long inports() const { return _inports; } unsigned long outports() const { return _outports; } unsigned long controlInPorts() const { return _controlInPorts; } unsigned long controlOutPorts() const { return _controlOutPorts; } - bool inPlaceCapable() const { return _inPlaceCapable; } - + const std::vector* getRpIdx() { return &rpIdx; } }; -typedef std::list::iterator iPlugin; +typedef std::list::iterator iPlugin; class PluginGroups : public QMap< QPair, QSet > { public: QSet& get(QString a, QString b) { return (*this)[(QPair(a,b))]; } - QSet& get(const Plugin& p) { return (*this)[(QPair(p.lib(),p.label()))]; } - + QSet& get(const Plugin *p) { return (*this)[(QPair(p->lib(),p->label()))]; } + void shift_left(int first, int last); void shift_right(int first, int last); void erase(int index); - + private: void replace_group(int old, int now); }; @@ -214,13 +240,15 @@ // PluginList //--------------------------------------------------------- -class PluginList : public std::list { +class PluginList : public std::list { public: - void add(QFileInfo* fi, const LADSPA_Descriptor* d, bool isDssi = false, bool isDssiSynth = false) + void add(QFileInfo* fi, const LADSPA_Descriptor* d, + bool isDssi = false, bool isDssiSynth = false, bool isDssiVst = false, + Plugin::PluginFeatures reqFeatures = Plugin::NoFeatures) { - push_back(Plugin(fi, d, isDssi, isDssiSynth)); + push_back(new Plugin(fi, d, isDssi, isDssiSynth, isDssiVst, reqFeatures)); } - + Plugin* find(const QString&, const QString&); PluginList() {} }; @@ -231,73 +259,117 @@ struct Port { unsigned long idx; - float val; - float tmpVal; + + // NOTE: These values represent the lowest level of control value storage. + // The choice of float or double depends on the underlying system using this struct. + // For example plugins and synthesizers usually use floats to represent control values + // and they are directly pointed to this float, while our own track controls + // (volume, pan etc.) take advantage of the double precision. + // Double precision is preferred if possible because above this lowest level all other + // controller usage is in double precision. Thus our very own track controllers are + // perfectly matched double precision throughout the system. + union { + float val; + double dval; + }; + union { + float tmpVal; // TODO Try once again and for all to remove this, was it really required? + //double dtmpVal; // Not used, should not be required. + }; bool enCtrl; // Enable controller stream. - bool en2Ctrl; // Second enable controller stream (and'ed with enCtrl). + CtrlInterpolate interp; }; //--------------------------------------------------------- -// PluginIBase +// PluginIBase //--------------------------------------------------------- -class PluginIBase +class PluginIBase { protected: ControlFifo _controlFifo; MusEGui::PluginGui* _gui; + QRect _guiGeometry; + QRect _nativeGuiGeometry; void makeGui(); public: - PluginIBase(); - virtual ~PluginIBase(); - virtual bool on() const = 0; - virtual void setOn(bool val) = 0; - virtual unsigned long pluginID() = 0; + PluginIBase(); + virtual ~PluginIBase(); + virtual Plugin::PluginFeatures requiredFeatures() const = 0; + virtual bool on() const = 0; + virtual void setOn(bool val) = 0; + virtual unsigned long pluginID() = 0; virtual int id() = 0; - virtual QString pluginLabel() const = 0; + virtual QString pluginLabel() const = 0; virtual QString name() const = 0; virtual QString lib() const = 0; virtual QString dirPath() const = 0; virtual QString fileName() const = 0; virtual QString titlePrefix() const = 0; - - virtual AudioTrack* track() = 0; - - 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 AudioTrack* track() = 0; + + virtual void enableController(unsigned long i, bool v = true) = 0; + virtual bool controllerEnabled(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 activate() = 0; virtual void deactivate() = 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 bool addScheduledControlEvent(unsigned long i, double 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; - virtual float param(unsigned long i) const = 0; - virtual float paramOut(unsigned long i) const = 0; + virtual void setParam(unsigned long i, double val) = 0; + virtual double param(unsigned long i) const = 0; + virtual double paramOut(unsigned long i) const = 0; virtual const char* paramName(unsigned long i) = 0; virtual const char* paramOutName(unsigned long i) = 0; // FIXME TODO: Either find a way to agnosticize these two ranges, or change them from ladspa ranges to a new MusE range class. virtual LADSPA_PortRangeHint range(unsigned long i) = 0; virtual LADSPA_PortRangeHint rangeOut(unsigned long i) = 0; + + virtual float latency() = 0; + virtual void setCustomData(const std::vector &) {/* Do nothing by default */} virtual CtrlValueType ctrlValueType(unsigned long i) const = 0; virtual CtrlList::Mode ctrlMode(unsigned long i) const = 0; QString dssi_ui_filename() const; - + MusEGui::PluginGui* gui() const { return _gui; } void deleteGui(); + + virtual void showGui(); + virtual void showGui(bool); + virtual bool guiVisible() const; + // Sets the gui's geometry. Also updates the saved geometry. + virtual void setGeometry(int x, int y, int w, int h); + // Returns the current geometry of the gui, or if the gui does not exist, + // the saved gui geometry. + virtual void getGeometry(int *x, int *y, int *w, int *h) const; + // Saves the current gui geometry. + virtual void saveGeometry(int x, int y, int w, int h); + // Returns the saved gui geometry. + virtual void savedGeometry(int *x, int *y, int *w, int *h) const; + + virtual void showNativeGui() { } + virtual void showNativeGui(bool) { } + virtual bool nativeGuiVisible() const { return false; } + // Sets the gui's geometry. Also updates the saved geometry. + virtual void setNativeGeometry(int x, int y, int w, int h); + // Returns the current geometry of the gui, or if the gui does not exist, + // the saved gui geometry. + virtual void getNativeGeometry(int *x, int *y, int *w, int *h) const; + // Saves the current gui geometry. + virtual void saveNativeGeometry(int x, int y, int w, int h); + // Returns the saved gui geometry. + virtual void savedNativeGeometry(int *x, int *y, int *w, int *h) const; }; //--------------------------------------------------------- @@ -305,10 +377,18 @@ // plugin instance //--------------------------------------------------------- -#define AUDIO_IN (LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT) -#define AUDIO_OUT (LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT) +#define IS_AUDIO_IN (LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT) +#define IS_AUDIO_OUT (LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT) class PluginI : public PluginIBase { +#ifdef LV2_SUPPORT + friend class LV2PluginWrapper; + friend class LV2Synth; +#endif +#ifdef VST_NATIVE_SUPPORT + friend class VstNativeSynth; + friend class VstNativePluginWrapper; +#endif Plugin* _plugin; int channel; int instances; @@ -318,10 +398,17 @@ LADSPA_Handle* handle; // per instance Port* controls; Port* controlsOut; + Port* controlsOutDummy; unsigned long controlPorts; unsigned long controlOutPorts; + bool _hasLatencyOutPort; + unsigned long _latencyOutPort; + + float *_audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. + float *_audioOutDummyBuf; // A place to connect unused outputs. + bool _on; bool initControlValues; QString _name; @@ -333,34 +420,34 @@ bool _showNativeGuiPending; void init(); - + public: PluginI(); virtual ~PluginI(); Plugin* plugin() const { return _plugin; } + + virtual Plugin::PluginFeatures requiredFeatures() const { return _plugin->requiredFeatures(); } + bool on() const { return _on; } void setOn(bool val) { _on = val; } - + void setTrack(AudioTrack* t) { _track = t; } AudioTrack* track() { return _track; } - unsigned long pluginID() { return _plugin->id(); } + unsigned long pluginID() { return _plugin->id(); } void setID(int i); int id() { return _id; } void updateControllers(); - + bool initPluginInstance(Plugin*, int channels); void setChannels(int); - void connect(unsigned long ports, unsigned long offset, float** src, float** dst); - void apply(unsigned long n, unsigned long ports, float** bufIn, float** bufOut); + void connect(unsigned long ports, unsigned long offset, float** src, float** dst); + void apply(unsigned pos, unsigned long n, unsigned long ports, float** bufIn, float** bufOut); - void enableController(unsigned long i, bool v = true) { controls[i].enCtrl = v; } - bool controllerEnabled(unsigned long i) const { return controls[i].enCtrl; } - void enable2Controller(unsigned long i, bool v = true) { controls[i].en2Ctrl = v; } - bool controllerEnabled2(unsigned long i) const { return controls[i].en2Ctrl; } + void enableController(unsigned long i, bool v = true) { controls[i].enCtrl = v; } + bool controllerEnabled(unsigned long i) const { return controls[i].enCtrl; } void enableAllControllers(bool v = true); - void enable2AllControllers(bool v = true); - + void activate(); void deactivate(); QString pluginLabel() const { return _plugin->label(); } @@ -373,43 +460,45 @@ #ifdef OSC_SUPPORT OscEffectIF& oscIF() { return _oscif; } - + int oscControl(unsigned long dssiPort, float val); int oscConfigure(const char *key, const char *val); int oscUpdate(); #endif - + void writeConfiguration(int level, Xml& xml); bool readConfiguration(Xml& xml, bool readPreset=false); bool loadControl(Xml& xml); - bool setControl(const QString& s, float val); + bool setControl(const QString& s, double val); void showGui(); void showGui(bool); - bool isDssiPlugin() const { return _plugin->isDssiPlugin(); } + bool isDssiPlugin() const { return _plugin->isDssiPlugin(); } + bool isLV2Plugin() const { return _plugin->isLV2Plugin(); } + bool isVstNativePlugin() const { return _plugin->isVstNativePlugin(); } void showNativeGui(); void showNativeGui(bool); bool isShowNativeGuiPending() { return _showNativeGuiPending; } - bool guiVisible(); - bool nativeGuiVisible(); + bool nativeGuiVisible() const; - unsigned long parameters() const { return controlPorts; } + unsigned long parameters() const { return controlPorts; } unsigned long parametersOut() const { return controlOutPorts; } - void setParam(unsigned long i, float val); - void putParam(unsigned long i, float val) { controls[i].val = controls[i].tmpVal = val; } - float param(unsigned long i) const { return controls[i].val; } - float paramOut(unsigned long i) const { return controlsOut[i].val; } - float defaultValue(unsigned long param) const; + void setParam(unsigned long i, double val); + void putParam(unsigned long i, double val) { controls[i].val = controls[i].tmpVal = val; } + double param(unsigned long i) const { return controls[i].val; } + double paramOut(unsigned long i) const { return controlsOut[i].val; } + double defaultValue(unsigned long param) const; const char* paramName(unsigned long i) { return _plugin->portName(controls[i].idx); } const char* paramOutName(unsigned long i) { return _plugin->portName(controlsOut[i].idx); } LADSPA_PortDescriptor portd(unsigned long i) const { return _plugin->portd(controls[i].idx); } void range(unsigned long i, float* min, float* max) const { _plugin->range(controls[i].idx, min, max); } - bool isAudioIn(unsigned long k) { return (_plugin->portd(k) & AUDIO_IN) == AUDIO_IN; } - bool isAudioOut(unsigned long k) { return (_plugin->portd(k) & AUDIO_OUT) == AUDIO_OUT; } + bool isAudioIn(unsigned long k) { return (_plugin->portd(k) & IS_AUDIO_IN) == IS_AUDIO_IN; } + bool isAudioOut(unsigned long k) { return (_plugin->portd(k) & IS_AUDIO_OUT) == IS_AUDIO_OUT; } LADSPA_PortRangeHint range(unsigned long i) { return _plugin->range(controls[i].idx); } LADSPA_PortRangeHint rangeOut(unsigned long i) { return _plugin->range(controlsOut[i].idx); } - bool inPlaceCapable() const { return _plugin->inPlaceCapable(); } + float latency(); CtrlValueType ctrlValueType(unsigned long i) const { return _plugin->ctrlValueType(controls[i].idx); } - CtrlList::Mode ctrlMode(unsigned long i) const { return _plugin->ctrlMode(controls[i].idx); }; + CtrlList::Mode ctrlMode(unsigned long i) const { return _plugin->ctrlMode(controls[i].idx); } + virtual void setCustomData(const std::vector &customParams); }; //--------------------------------------------------------- @@ -418,11 +507,12 @@ //--------------------------------------------------------- class Pipeline : public std::vector { + private: float* buffer[MAX_CHANNELS]; - + void initBuffers(); public: Pipeline(); - Pipeline(const Pipeline&); + Pipeline(const Pipeline&, AudioTrack*); ~Pipeline(); void insert(PluginI* p, int index); void remove(int index); @@ -432,21 +522,23 @@ QString label(int idx) const; QString name(int idx) const; void showGui(int, bool); - bool isDssiPlugin(int) const; + bool isDssiPlugin(int) const; + bool isLV2Plugin(int idx) const; + bool isVstNativePlugin(int idx) const; bool has_dssi_ui(int idx) const; void showNativeGui(int, bool); void deleteGui(int idx); void deleteAllGuis(); bool guiVisible(int); bool nativeGuiVisible(int); - void apply(unsigned long ports, unsigned long nframes, float** buffer); + void apply(unsigned pos, unsigned long ports, unsigned long nframes, float** buffer); 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); + bool addScheduledControlEvent(int track_ctrl_id, double val, unsigned frame); // returns true if event cannot be delivered + void enableController(int track_ctrl_id, bool en); + bool controllerEnabled(int track_ctrl_id); + float latency(); }; typedef Pipeline::iterator iPluginI; @@ -466,16 +558,14 @@ namespace MusEGui { class DoubleLabel; -class PluginGui; -//--------------------------------------------------------- // PluginLoader //--------------------------------------------------------- class PluginLoader : public QUiLoader { public: - virtual QWidget* createWidget(const QString & className, QWidget * parent = 0, const QString & name = QString()); + virtual QWidget* createWidget(const QString & className, QWidget * parent = 0, const QString & name = QString()); PluginLoader(QObject * parent = 0) : QUiLoader(parent) {} }; @@ -489,7 +579,8 @@ }; int type; int hint; - + bool pressed; + MusEGui::DoubleLabel* label; QWidget* actuator; // Slider or Toggle Button (SWITCH) }; @@ -504,7 +595,8 @@ }; QWidget* widget; int type; - unsigned long param; + unsigned long param; + bool pressed; }; //--------------------------------------------------------- @@ -515,10 +607,10 @@ Q_OBJECT MusECore::PluginIBase* plugin; // plugin instance - + GuiParam* params; GuiParam* paramsOut; - unsigned long nobj; // number of widgets in gw + unsigned long nobj; // number of widgets in gw GuiWidgets* gw; QAction* onOff; @@ -528,97 +620,43 @@ void updateControls(); void getPluginConvertedValues(LADSPA_PortRangeHint range, double &lower, double &upper, double &dlower, double &dupper, double &dval); + + protected: + virtual void showEvent(QShowEvent *e); + virtual void hideEvent(QHideEvent *e); + private slots: void load(); void save(); void bypassToggled(bool); - void sliderChanged(double, int, bool); + void sliderChanged(double value, int id, int scrollMode); void labelChanged(double, int); void guiParamChanged(int); - void ctrlPressed(int); - void ctrlReleased(int); + void ctrlPressed(double, int); + void ctrlReleased(double, int); + void switchPressed(int); + void switchReleased(int); void guiParamPressed(int); void guiParamReleased(int); - void guiSliderPressed(int); - void guiSliderReleased(int); + void guiSliderPressed(double, int); + void guiSliderReleased(double, int); void ctrlRightClicked(const QPoint &, int); void guiSliderRightClicked(const QPoint &, int); void guiContextMenuReq(int idx); protected slots: - void heartBeat(); + virtual void heartBeat(); public: PluginGui(MusECore::PluginIBase*); - + ~PluginGui(); void setOn(bool); void updateValues(); }; - - - -//--------------------------------------------------------- -// PluginDialog -//--------------------------------------------------------- - -enum { SEL_SM, SEL_S, SEL_M, SEL_ALL }; - -class PluginDialog : public QDialog { - Q_OBJECT - - public: - PluginDialog(QWidget* parent=0); - static MusECore::Plugin* getPlugin(QWidget* parent); - MusECore::Plugin* value(); - - public slots: - void accept(); - void reject(); - - private slots: - void enableOkB(); - void pluginTypeSelectionChanged(QAbstractButton*); - void tabChanged(int); - void tabMoved(int,int); - void fillPlugs(); - - void newGroup(); - void delGroup(); - void renameGroup(); - void plistContextMenu(const QPoint&); - void groupMenuEntryToggled(int i); - - private: - QComboBox* sortBox; - QTabBar* tabBar; - QTreeWidget* pList; - QRadioButton* allPlug; - QRadioButton* onlyM; - QRadioButton* onlyS; - QRadioButton* onlySM; - QPushButton *okB; - - QAction* newGroupAction; - QAction* delGroupAction; - QAction* renGroupAction; - - - void saveSettings(); - - static int selectedPlugType; - static int selectedGroup; // 0 means "show all" - static QStringList sortItems; - static QRect geometrySave; - static QByteArray listSave; - - QSet* group_info; //holds the group-set of the plugin which shall be affected by the plistContextMenu. -}; - -} - +} // namespace MusEGui namespace MusEGlobal { extern MusECore::PluginList plugins; diff -Nru muse-2.1.2/muse/pos.cpp muse-3.0.2+ds1/muse/pos.cpp --- muse-2.1.2/muse/pos.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/pos.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -168,7 +168,7 @@ Pos operator+(Pos a, int b) { - Pos c; + Pos c = a; c.setType(a.type()); return c += b; } @@ -242,6 +242,38 @@ } //--------------------------------------------------------- +// posValue +//--------------------------------------------------------- + +unsigned Pos::posValue() const +{ + switch(type()) + { + case FRAMES: + return _frame; + case TICKS: + return _tick; + } + return _tick; +} + +unsigned Pos::posValue(TType time_type) const +{ + switch(time_type) + { + case FRAMES: + if (_type == TICKS) + _frame = MusEGlobal::tempomap.tick2frame(_tick, _frame, &sn); + return _frame; + case TICKS: + if (_type == FRAMES) + _tick = MusEGlobal::tempomap.frame2tick(_frame, _tick, &sn); + return _tick; + } + return _tick; +} + +//--------------------------------------------------------- // setTick //--------------------------------------------------------- @@ -266,6 +298,66 @@ } //--------------------------------------------------------- +// setPosValue +//--------------------------------------------------------- + +void Pos::setPosValue(unsigned val) +{ + sn = -1; + switch(type()) { + case FRAMES: + _frame = val; + break; + case TICKS: + _tick = val; + break; + } +} + +void Pos::setPosValue(unsigned val, TType time_type) +{ + sn = -1; + switch(time_type) { + case FRAMES: + _frame = val; + if (_type == TICKS) + _tick = MusEGlobal::tempomap.frame2tick(_frame, &sn); + break; + case TICKS: + _tick = val; + if (_type == FRAMES) + _frame = MusEGlobal::tempomap.tick2frame(_tick, &sn); + break; + } +} + +//--------------------------------------------------------- +// convert (static) +//--------------------------------------------------------- + +unsigned Pos::convert(unsigned val, TType from_type, TType to_type) +{ + switch(from_type) { + case FRAMES: + switch(to_type) + { + case FRAMES: return val; + case TICKS: return MusEGlobal::tempomap.frame2tick(val); + } + break; + + case TICKS: + switch(to_type) + { + case FRAMES: return MusEGlobal::tempomap.tick2frame(val); + case TICKS: return val; + } + break; + } + return val; +} + +//--------------------------------------------------------- // write //--------------------------------------------------------- @@ -473,6 +565,69 @@ } //--------------------------------------------------------- +// setLenValue +//--------------------------------------------------------- + +void PosLen::setLenValue(unsigned val) +{ + sn = -1; + switch(type()) + { + case FRAMES: + _lenFrame = val; + break; + case TICKS: + _lenTick = val; + break; + } +} + +void PosLen::setLenValue(unsigned val, TType time_type) +{ + sn = -1; + switch(time_type) + { + case FRAMES: + _lenFrame = val; + if (type() == TICKS) + _lenTick = MusEGlobal::tempomap.deltaFrame2tick(frame(), frame() + _lenFrame, &sn); + break; + case TICKS: + _lenTick = val; + if (type() == FRAMES) + _lenFrame = MusEGlobal::tempomap.deltaTick2frame(tick(), tick() + _lenTick, &sn); + break; + } +} + +//--------------------------------------------------------- +// convertLen (static) +//--------------------------------------------------------- + +unsigned PosLen::convertLen(unsigned val, unsigned len, TType from_type, TType to_type) +{ + switch(from_type) + { + case FRAMES: + switch(to_type) + { + case FRAMES: return val; + case TICKS: return MusEGlobal::tempomap.deltaFrame2tick(val, val + len); + } + break; + + case TICKS: + switch(to_type) + { + case FRAMES: return MusEGlobal::tempomap.deltaTick2frame(val, val + len); + case TICKS: return val; + } + break; + } + return len; +} + +//--------------------------------------------------------- // lenTick //--------------------------------------------------------- @@ -495,6 +650,38 @@ } //--------------------------------------------------------- +// lenValue +//--------------------------------------------------------- + +unsigned PosLen::lenValue() const + { + switch(type()) + { + case FRAMES: + return _lenFrame; + case TICKS: + return _lenTick; + } + return _lenTick; + } + +unsigned PosLen::lenValue(TType time_type) const + { + switch(time_type) + { + case FRAMES: + if (type() == TICKS) + _lenFrame = MusEGlobal::tempomap.deltaTick2frame(tick(), tick() + _lenTick, &sn); + return _lenFrame; + case TICKS: + if (type() == FRAMES) + _lenTick = MusEGlobal::tempomap.deltaFrame2tick(frame(), frame() + _lenFrame, &sn); + return _lenTick; + } + return _lenTick; + } + +//--------------------------------------------------------- // end //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/pos.h muse-3.0.2+ds1/muse/pos.h --- muse-2.1.2/muse/pos.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/pos.h 2017-12-04 21:01:18.000000000 +0000 @@ -78,8 +78,13 @@ unsigned tick() const; unsigned frame() const; + unsigned posValue() const; + unsigned posValue(TType time_type) const; void setTick(unsigned); void setFrame(unsigned); + void setPosValue(unsigned val); + void setPosValue(unsigned val, TType time_type); + static unsigned convert(unsigned val, TType from_type, TType to_type); void write(int level, Xml&, const char*) const; void read(Xml& xml, const char*); @@ -106,12 +111,17 @@ void read(Xml& xml, const char*); void setLenTick(unsigned); void setLenFrame(unsigned); + void setLenValue(unsigned val); + void setLenValue(unsigned val, TType time_type); unsigned lenTick() const; unsigned lenFrame() const; + unsigned lenValue() const; + unsigned lenValue(TType time_type) const; Pos end() const; unsigned endTick() const { return end().tick(); } unsigned endFrame() const { return end().frame(); } void setPos(const Pos&); + static unsigned convertLen(unsigned val, unsigned len, TType from_type, TType to_type); }; } // namespace MusECore diff -Nru muse-2.1.2/muse/remote/pyapi.cpp muse-3.0.2+ds1/muse/remote/pyapi.cpp --- muse-2.1.2/muse/remote/pyapi.cpp 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/remote/pyapi.cpp 2017-12-17 21:07:38.000000000 +0000 @@ -199,9 +199,9 @@ Py_DECREF(pstrtick2); // Pack midi events into list before wrapping it all up - EventList* events = mpart->events(); + const EventList& events = mpart->events(); PyObject* pyevents = Py_BuildValue("[]"); - for (ciEvent e = events->begin(); e != events->end(); e++) { + for (ciEvent e = events.begin(); e != events.end(); e++) { PyObject* pyevent = PyDict_New(); // The event structure - a dictionary with keys 'type','tick','data' const Event& event = e->second; @@ -331,7 +331,7 @@ event.setC(data[2]); event.setTick(etick); event.setLenTick(elen); - npart->events()->add(event); + npart->addEvent(event); } else printf("Unhandled event type from python: %s\n", type.c_str()); @@ -353,10 +353,12 @@ } QString qtrackname(trackname); - MidiTrack* track = (MidiTrack*) MusEGlobal::song->findTrack(trackname); - if (track == NULL) + Track* t = MusEGlobal::song->findTrack(trackname); + if (t == NULL || t->isMidiTrack() == false) return NULL; + MidiTrack* track = (MidiTrack*)t; + MidiPart* npart = new MidiPart(track); npart->setTick(tick); npart->setLenTick(tickLen); @@ -404,12 +406,12 @@ npart->setLenTick(opart->lenTick()); npart->setSn(opart->sn()); - for (iEvent e = opart->events()->begin(); e != opart->events()->end(); e++) { - Event& event = e->second; + for (ciEvent e = opart->events().begin(); e != opart->events().end(); e++) { + Event& event = (Event &)(e->second); if (event.type() == Note || event.type() == Controller) continue; - npart->events()->add(event); + npart->addEvent(event); } addPyPartEventsToMusePart(npart, part); @@ -511,7 +513,7 @@ return NULL; Track* track = MusEGlobal::song->findTrack(QString(trackname)); - if (track == NULL) + if (track == NULL || track->isMidiTrack() == false) return NULL; MidiTrack* mt = (MidiTrack*) track; @@ -1064,7 +1066,9 @@ return false; bool muted = e->getP1() == 1; - track->setMute(muted); + // No undo. + MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetTrackMute, track, muted), false); + this->update(SC_MUTE | SC_TRACK_MODIFIED); break; } @@ -1081,10 +1085,9 @@ int num = e->getP1(); int val = e->getP2(); - int tick = MusEGlobal::song->cpos(); - MidiPlayEvent ev(tick, track->outPort(), chan, ME_CONTROLLER, num, val); + // Schedule for immediate playback. + MidiPlayEvent ev(0, track->outPort(), chan, ME_CONTROLLER, num, val); MusEGlobal::audio->msgPlayMidiEvent(&ev); - MusEGlobal::song->update(SC_MIDI_CONTROLLER); break; } case QPybridgeEvent::SONG_SETAUDIOVOL: { @@ -1131,9 +1134,7 @@ break; } case QPybridgeEvent::SONG_ADD_TRACK: { - MusECore::Undo operations; - MusEGlobal::song->addTrack(operations, (Track::TrackType)e->getP1()); // Add at end of list. - MusEGlobal::song->applyOperationGroup(operations); + MusEGlobal::song->addTrack((Track::TrackType)e->getP1()); // Add at end of list. break; } case QPybridgeEvent::SONG_CHANGE_TRACKNAME: { diff -Nru muse-2.1.2/muse/route.cpp muse-3.0.2+ds1/muse/route.cpp --- muse-2.1.2/muse/route.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/route.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: route.cpp,v 1.18.2.3 2008/05/21 00:28:52 terminator356 Exp $ // // (C) Copyright 2003-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -23,6 +23,7 @@ //========================================================= #include +#include #include "song.h" #include "route.h" @@ -32,12 +33,19 @@ #include "synth.h" #include "audiodev.h" #include "xml.h" +#include "mididev.h" #include "midiport.h" +#include "operations.h" +#include "icons.h" #include "driver/jackmidi.h" #include "driver/alsamidi.h" +#include "libs/strntcpy.h" //#define ROUTE_DEBUG +// Undefine if and when multiple output routes are added to midi tracks. +#define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + namespace MusECore { const QString ROUTE_MIDIPORT_NAME_PREFIX = "MusE MidiPort "; @@ -49,6 +57,10 @@ Route::Route(void* t, int ch) { jackPort = t; + persistentJackPortName[0] = 0; + if(MusEGlobal::checkAudioDevice()) + MusEGlobal::audioDevice->portName(jackPort, persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); + midiPort = -1; channel = ch; channels = -1; @@ -64,6 +76,7 @@ channels = chans; remoteChannel = -1; type = TRACK_ROUTE; + persistentJackPortName[0] = 0; } Route::Route(MidiDevice* d, int ch) @@ -74,6 +87,7 @@ channels = -1; remoteChannel = -1; type = MIDI_DEVICE_ROUTE; + persistentJackPortName[0] = 0; } Route::Route(int port, int ch) @@ -84,6 +98,7 @@ channels = -1; remoteChannel = -1; type = MIDI_PORT_ROUTE; + persistentJackPortName[0] = 0; } Route::Route(const QString& s, bool dst, int ch, int rtype) @@ -95,6 +110,7 @@ channels = node.channels; remoteChannel = node.remoteChannel; type = node.type; + persistentJackPortName[0] = 0; if(type == TRACK_ROUTE) { track = node.track; @@ -104,6 +120,11 @@ if(type == JACK_ROUTE) { jackPort = node.jackPort; + char* res = 0; + if(jackPort && MusEGlobal::checkAudioDevice()) + res = MusEGlobal::audioDevice->portName(jackPort, persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); + if(!res) + MusELib::strntcpy(persistentJackPortName, s.toLatin1().constData(), ROUTE_PERSISTENT_NAME_SIZE); midiPort = -1; } else @@ -120,6 +141,7 @@ } } + Route::Route() { track = 0; @@ -128,13 +150,51 @@ channels = -1; remoteChannel = -1; type = TRACK_ROUTE; + persistentJackPortName[0] = 0; } +Route::Route(RouteType type_, int midi_port_num_, void* void_pointer_, int channel_, int channels_, int remote_channel_, const char* name_) + { + type = type_; + midiPort = midi_port_num_; + voidPointer = void_pointer_; + channel = channel_; + channels = channels_; + remoteChannel = remote_channel_; + persistentJackPortName[0] = 0; + MusELib::strntcpy(persistentJackPortName, name_, ROUTE_PERSISTENT_NAME_SIZE); + } + +Route::Route(const Route& a) +{ + type = a.type; + midiPort = a.midiPort; + voidPointer = a.voidPointer; + channel = a.channel; + channels = a.channels; + remoteChannel = a.remoteChannel; + persistentJackPortName[0] = 0; + strcpy(persistentJackPortName, a.persistentJackPortName); +} + +Route& Route::operator=(const Route& a) +{ + type = a.type; + midiPort = a.midiPort; + voidPointer = a.voidPointer; + channel = a.channel; + channels = a.channels; + remoteChannel = a.remoteChannel; + persistentJackPortName[0] = 0; + strcpy(persistentJackPortName, a.persistentJackPortName); + return *this; +} + //--------------------------------------------------------- // addRoute //--------------------------------------------------------- -void addRoute(Route src, Route dst) +bool addRoute(Route src, Route dst) { #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute:\n"); @@ -146,10 +206,10 @@ fprintf(stderr, "addRoute: invalid src\n"); if(!dst.isValid()) fprintf(stderr, "addRoute: invalid dst\n"); - return; + return false; } -// printf("addRoute %d.%d:<%s> %d.%d:<%s>\n", +// fprintf(stderr, "addRoute %d.%d:<%s> %d.%d:<%s>\n", // src.type, src.channel, src.name().toLatin1().constData(), // dst.type, dst.channel, dst.name().toLatin1().constData()); if (src.type == Route::JACK_ROUTE) @@ -158,61 +218,69 @@ { if (dst.track->type() != Track::AUDIO_INPUT) { - fprintf(stderr, "addRoute: source is jack, dest:%s is track but not audio input\n", dst.track->name().toLatin1().constData()); - return; + fprintf(stderr, "addRoute: source is jack, dest:%s is track but not audio input\n", dst.track->name().toLatin1().constData()); + return false; } if (dst.channel < 0) { - fprintf(stderr, "addRoute: source is jack, dest:%s is track but invalid channel:%d\n", dst.track->name().toLatin1().constData(), dst.channel); - return; + fprintf(stderr, "addRoute: source is jack, dest:%s is track but invalid channel:%d\n", dst.track->name().toLatin1().constData(), dst.channel); + return false; } src.channel = dst.channel; - RouteList* inRoutes = dst.track->inRoutes(); - for (ciRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) + + if(dst.track->inRoutes()->contains(src)) { - if (*i == src) // route already there - { - fprintf(stderr, "addRoute: src track route already exists.\n"); - return; - } + fprintf(stderr, "addRoute: src track route already exists.\n"); + return false; } + #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src Jack dst track name: %s pushing source route\n", dst.track->name().toLatin1().constData()); #endif - inRoutes->push_back(src); + + dst.track->inRoutes()->push_back(src); + + return true; } - else - if (dst.type == Route::MIDI_DEVICE_ROUTE) + else if (dst.type == Route::MIDI_DEVICE_ROUTE) { if(dst.device->deviceType() == MidiDevice::JACK_MIDI) { src.channel = dst.channel; - RouteList* routes = dst.device->inRoutes(); - for (ciRoute i = routes->begin(); i != routes->end(); ++i) + + if(dst.device->inRoutes()->contains(src)) { - if (*i == src) // route already there - { - fprintf(stderr, "addRoute: src Jack midi route already exists.\n"); - return; - } + fprintf(stderr, "addRoute: src Jack midi route already exists.\n"); + return false; } + #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src Jack dst Jack midi name: %s pushing source route\n", dst.device->name().toLatin1().constData()); #endif - routes->push_back(src); + + dst.device->inRoutes()->push_back(src); + + return true; } else { fprintf(stderr, "addRoute: source is Jack, but destination is not jack midi - type:%d\n", dst.device->deviceType()); - return; + return false; } } + else if(dst.type == Route::JACK_ROUTE) + { + // Do nothing - it's a direct Jack connection! + return false; + } else { fprintf(stderr, "addRoute: source is Jack, but destination is not track or midi - type:%d \n", dst.type); - return; + return false; } + + return false; } else if (dst.type == Route::JACK_ROUTE) { @@ -220,490 +288,405 @@ { if (src.track->type() != Track::AUDIO_OUTPUT) { - fprintf(stderr, "addRoute: destination is jack, source is track but not audio output\n"); - return; + fprintf(stderr, "addRoute: destination is jack, source is track but not audio output\n"); + return false; } if (src.channel < 0) { - fprintf(stderr, "addRoute: destination is jack, source:%s is track but invalid channel:%d\n", src.track->name().toLatin1().constData(), src.channel); - return; + fprintf(stderr, "addRoute: destination is jack, source:%s is track but invalid channel:%d\n", src.track->name().toLatin1().constData(), src.channel); + return false; } - RouteList* outRoutes = src.track->outRoutes(); dst.channel = src.channel; - for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + if(src.track->outRoutes()->contains(dst)) { - if (*i == dst) // route already there - { - #ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: dst track route already exists.\n"); - #endif - return; - } + fprintf(stderr, "addRoute: dst track route already exists.\n"); + return false; } + #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: dst Jack src track name: %s pushing destination route\n", src.track->name().toLatin1().constData()); #endif - outRoutes->push_back(dst); + + src.track->outRoutes()->push_back(dst); + + return true; } - else - if (src.type == Route::MIDI_DEVICE_ROUTE) + else if (src.type == Route::MIDI_DEVICE_ROUTE) { if(src.device->deviceType() == MidiDevice::JACK_MIDI) { dst.channel = src.channel; - RouteList* routes = src.device->outRoutes(); - for (ciRoute i = routes->begin(); i != routes->end(); ++i) + + if(src.device->outRoutes()->contains(dst)) { - if (*i == dst) // route already there - { - fprintf(stderr, "addRoute: dst Jack midi route already exists.\n"); - return; - } + fprintf(stderr, "addRoute: dst Jack midi route already exists.\n"); + return false; } + #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: dst Jack src Jack midi name: %s pushing destination route\n", src.device->name().toLatin1().constData()); #endif + if(src.device->midiPort() != -1) // Initializations sysex etc. need to be sent to the new connection. MusEGlobal::midiPorts[src.device->midiPort()].clearInitSent(); - routes->push_back(dst); + src.device->outRoutes()->push_back(dst); + + return true; } else { fprintf(stderr, "addRoute: destination is Jack, but source is not jack midi - type:%d\n", src.device->deviceType()); - return; + return false; } + + return false; + } + else if(src.type == Route::JACK_ROUTE) + { + // Do nothing - it's a direct Jack connection! + return false; } else { fprintf(stderr, "addRoute: destination is Jack, but source is not track or midi - type:%d \n", src.type); - return; + return false; } + + return false; } else if(src.type == Route::MIDI_PORT_ROUTE) { - if(dst.type != Route::TRACK_ROUTE) + if(dst.type != Route::TRACK_ROUTE || !dst.track->isMidiTrack()) { - fprintf(stderr, "addRoute: source is midi port:%d, but destination is not track\n", src.midiPort); - return; + fprintf(stderr, "addRoute: source is midi port:%d, but destination is not midi track\n", src.midiPort); + return false; } - + if(dst.channel < -1 || dst.channel >= MIDI_CHANNELS) + { + fprintf(stderr, "addRoute: source is midi port:%d, but destination track channel:%d out of range\n", src.midiPort, dst.channel); + return false; + } + MidiPort *mp = &MusEGlobal::midiPorts[src.midiPort]; // Do not allow synth ports to connect to any track. It may be useful in some cases, // may be desired later, but for now it's just a routing hassle. p4.0.35 - if(mp->device() && mp->device()->isSynti()) - return; + //if(mp->device() && mp->device()->isSynti()) + // return false; - if(dst.channel < 1 || dst.channel >= (1 << MIDI_CHANNELS)) - { - fprintf(stderr, "addRoute: source is midi port:%d, but destination channel mask:%d out of range\n", src.midiPort, dst.channel); - return; - } - + bool ret1 = false; + bool ret2 = false; src.channel = dst.channel; - RouteList* outRoutes = mp->outRoutes(); - iRoute ir = outRoutes->begin(); - for ( ; ir != outRoutes->end(); ++ir) + RouteList* rl; + rl = mp->outRoutes(); + if(!rl->contains(dst)) { - if (ir->type == Route::TRACK_ROUTE && ir->track == dst.track) // Does a route to the track exist? - { - ir->channel |= dst.channel; // Bitwise OR the desired channel bit with the existing bit mask. - break; - } - } - #ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: src midi port:%d dst track name:%s pushing dst and src routes\n", src.midiPort, dst.track->name().toLatin1().constData()); - #endif - - if(ir == outRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. - outRoutes->push_back(dst); - - RouteList* inRoutes = dst.track->inRoutes(); - - // Make sure only one single route, with a channel mask, can ever exist. - ir = inRoutes->begin(); - for ( ; ir != inRoutes->end(); ++ir) + rl->push_back(dst); + ret1 = true; + } + rl = dst.track->inRoutes(); + if(!rl->contains(src)) { - if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == src.midiPort) // Does a route to the midi port exist? - { - ir->channel |= src.channel; // Bitwise OR the desired channel bit with the existing bit mask. - break; - } + rl->push_back(src); + ret2 = true; } - if(ir == inRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. - inRoutes->push_back(src); + return ret1 || ret2; } else if(dst.type == Route::MIDI_PORT_ROUTE) { - if(src.type != Route::TRACK_ROUTE) + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + fprintf(stderr, "addRoute: destination is midi port:%d, but source is not allowed\n", dst.midiPort); + return false; +#endif + + if(src.type != Route::TRACK_ROUTE || !src.track->isMidiTrack()) { - fprintf(stderr, "addRoute: destination is midi port:%d, but source is not track\n", dst.midiPort); - return; + fprintf(stderr, "addRoute: destination is midi port:%d, but source is not midi track\n", dst.midiPort); + return false; } - if(src.channel < 1 || src.channel >= (1 << MIDI_CHANNELS)) + if(src.channel < -1 || src.channel >= MIDI_CHANNELS) { - fprintf(stderr, "addRoute: destination is midi port:%d, but source channel mask:%d out of range\n", dst.midiPort, src.channel); - return; - } - + fprintf(stderr, "addRoute: destination is midi port:%d, but source track channel:%d out of range\n", dst.midiPort, src.channel); + return false; + } + + bool ret1 = false; + bool ret2 = false; dst.channel = src.channel; - RouteList* outRoutes = src.track->outRoutes(); - - iRoute ir = outRoutes->begin(); - for ( ; ir != outRoutes->end(); ++ir) + RouteList* rl; + rl = src.track->outRoutes(); + if(!rl->contains(dst)) { - if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == dst.midiPort) // Does a route to the midi port exist? - { - ir->channel |= dst.channel; // Bitwise OR the desired channel bit with the existing bit mask. - break; - } + rl->push_back(dst); + ret1 = true; } - - if(ir == outRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. - outRoutes->push_back(dst); - - MidiPort *mp = &MusEGlobal::midiPorts[dst.midiPort]; - - #ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: src track:%s dst midi port:%d pushing dst and src routes\n", src.track->name().toLatin1().constData(), dst.midiPort); - #endif - RouteList* inRoutes = mp->inRoutes(); - - // Make sure only one single route, with a channel mask, can ever exist. - ir = inRoutes->begin(); - for ( ; ir != inRoutes->end(); ++ir) + rl = MusEGlobal::midiPorts[dst.midiPort].inRoutes(); + if(!rl->contains(src)) { - if (ir->type == Route::TRACK_ROUTE && ir->track == src.track) // Does a route to the track exist? - { - ir->channel |= src.channel; // Bitwise OR the desired channel bit with the existing bit mask. - break; - } + rl->push_back(src); + ret2 = true; } - if(ir == inRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. - inRoutes->push_back(src); + return ret1 || ret2; } else { if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) { fprintf(stderr, "addRoute: source or destination are not track routes\n"); - return; + return false; } RouteList* outRoutes = src.track->outRoutes(); - - // - // Must enforce to ensure channel and channels are valid if defaults of -1 passed. - // - if(src.track->type() == Track::AUDIO_SOFTSYNTH) + if((src.channel == -1 && dst.channel != -1) || (dst.channel == -1 && src.channel != -1)) { - if(src.channel == -1) - src.channel = 0; - if(src.channels == -1) - src.channels = src.track->channels(); - dst.channel = src.channel; - dst.channels = src.channels; - dst.remoteChannel = src.remoteChannel; + fprintf(stderr, "addRoute: source and destination are track routes but channels incompatible: src:%d dst:%d\n", src.channel, dst.channel); + return false; } - for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + // Don't bother checking valid channel ranges, to support persistent routes... + + if(src.channels != dst.channels) { - if (*i == dst) // route already there - // TODO: DELETETHIS all these comments wtf? - //if (i->type == dst.type && i->channel == dst.channel) - { - //if(i->type == Route::TRACK_ROUTE) - { - //if(i->track == dst.track) - { - //if(i->channels == dst.channels) - { - //#ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: src track route already exists.\n"); - //#endif - return; - } - //else - //{ - - //} - } - } - } + fprintf(stderr, "addRoute: source and destination are track routes but number of channels incompatible: src:%d dst:%d\n", src.channels, dst.channels); + return false; } - outRoutes->push_back(dst); - RouteList* inRoutes; + + // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 + // After: src TrackA, Channel 4, Remote Channel 2 dst: TrackB channel 2 Remote Channel 4 + // + // Ex. (Handled above, not used here. For example only.) + // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 + // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 + src.remoteChannel = src.channel; + dst.remoteChannel = dst.channel; + const int src_chan = src.channel; + src.channel = dst.channel; + dst.channel = src_chan; #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src track ch:%d chs:%d remch:%d dst track ch:%d chs:%d remch:%d name: %s pushing dest and source routes\n", src.channel, src.channels, src.remoteChannel, dst.channel, dst.channels, dst.remoteChannel, dst.track->name().toLatin1().constData()); #endif - inRoutes = dst.track->inRoutes(); + + const bool o_found = outRoutes->contains(dst); + if(o_found) + fprintf(stderr, "addRoute: dst track route already exists in src track out routes list. Ignoring.\n"); + else + outRoutes->push_back(dst); - // make sure AUDIO_AUX is processed last - if (src.track->type() == Track::AUDIO_AUX) // REMOVE Tim. This special aux code may not be useful or needed now. - inRoutes->push_back(src); // so DELETETHIS? + RouteList* inRoutes = dst.track->inRoutes(); + const bool i_found = inRoutes->contains(src); + if(i_found) + fprintf(stderr, "addRoute: src track route already exists in dst track out routes list. Ignoring.\n"); else - inRoutes->insert(inRoutes->begin(), src); + { + // make sure AUDIO_AUX is processed last + if(src.track->type() == Track::AUDIO_AUX) // REMOVE Tim. Or not? This special aux code may not be useful or needed now. TBD + inRoutes->push_back(src); + else + inRoutes->insert(inRoutes->begin(), src); + } + + // Only if a route was established: + if(!o_found || !i_found) + { + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update the destination track's aux ref count, and all tracks it is routed to. + if(src.track->auxRefCount()) + src.track->updateAuxRoute( src.track->auxRefCount(), dst.track ); + else + if(src.track->type() == Track::AUDIO_AUX) + src.track->updateAuxRoute( 1, dst.track ); + + return true; + } - // Is the source an Aux Track or else does it have Aux Tracks routed to it? - // Update the destination track's aux ref count, and all tracks it is routed to. - if(src.track->auxRefCount()) - src.track->updateAuxRoute( src.track->auxRefCount(), dst.track ); - else - if(src.track->type() == Track::AUDIO_AUX) - src.track->updateAuxRoute( 1, dst.track ); + return false; } + + return false; } //--------------------------------------------------------- // removeRoute //--------------------------------------------------------- -void removeRoute(Route src, Route dst) +bool removeRoute(Route src, Route dst) { - if (src.type == Route::JACK_ROUTE) - { - if(!dst.isValid()) - { - printf("removeRoute: source is jack, invalid destination\n"); - return; - } - - if (dst.type == Route::TRACK_ROUTE) - { - if (dst.track->type() != Track::AUDIO_INPUT) - { - fprintf(stderr, "removeRoute: source is jack, destination is track but not audio input\n"); - return; - } - RouteList* inRoutes = dst.track->inRoutes(); - iRoute i; - for (i = inRoutes->begin(); i != inRoutes->end(); ++i) - { - if (*i == src) - { - inRoutes->erase(i); - break; - } - } - } - else - if (dst.type == Route::MIDI_DEVICE_ROUTE) - { - RouteList* routes = dst.device->inRoutes(); - iRoute i; - for (i = routes->begin(); i != routes->end(); ++i) - { - if (*i == src) - { - routes->erase(i); - break; - } - } - } - else - { - fprintf(stderr, "removeRoute: source is jack, destination unknown\n"); - return; - } + if(src.type == Route::JACK_ROUTE) + { + if(dst.isValid()) + { + switch(dst.type) + { + case Route::TRACK_ROUTE: + src.channel = dst.channel; + return dst.track->inRoutes()->removeRoute(src); + break; + case Route::MIDI_DEVICE_ROUTE: + return dst.device->inRoutes()->removeRoute(src); + break; + case Route::MIDI_PORT_ROUTE: + return MusEGlobal::midiPorts[dst.midiPort].inRoutes()->removeRoute(src); + break; + case Route::JACK_ROUTE: + // Do nothing - it's a direct Jack disconnection! + return false; + break; } - else if (dst.type == Route::JACK_ROUTE) + } + return false; + + } + else if(dst.type == Route::JACK_ROUTE) + { + if(src.isValid()) + { + switch(src.type) { - if(!src.isValid()) - { - printf("removeRoute: destination is jack, invalid source\n"); - return; - } - - if (src.type == Route::TRACK_ROUTE) - { - if (src.track->type() != Track::AUDIO_OUTPUT) - { - fprintf(stderr, "removeRoute: destination is jack, source is track but not audio output\n"); - return; - } - RouteList* outRoutes = src.track->outRoutes(); - iRoute i; - for (i = outRoutes->begin(); i != outRoutes->end(); ++i) - { - if (*i == dst) { - outRoutes->erase(i); - break; - } - } - } - else - if (src.type == Route::MIDI_DEVICE_ROUTE) - { - RouteList* routes = src.device->outRoutes(); - iRoute i; - for (i = routes->begin(); i != routes->end(); ++i) - { - if (*i == dst) { - routes->erase(i); - break; - } - } - } - else - { - fprintf(stderr, "removeRoute: destination is jack, source unknown\n"); - return; - } + case Route::TRACK_ROUTE: + dst.channel = src.channel; + return src.track->outRoutes()->removeRoute(dst); + break; + case Route::MIDI_DEVICE_ROUTE: + return src.device->outRoutes()->removeRoute(dst); + break; + case Route::MIDI_PORT_ROUTE: + return MusEGlobal::midiPorts[src.midiPort].outRoutes()->removeRoute(dst); + break; + case Route::JACK_ROUTE: + // Do nothing - it's a direct Jack disconnection! + return false; + break; } - else if(src.type == Route::MIDI_PORT_ROUTE) - { - if(dst.type != Route::TRACK_ROUTE) - { - fprintf(stderr, "removeRoute: source is midi port:%d, but destination is not track\n", src.midiPort); - return; - } - - if(src.isValid()) - { - MidiPort *mp = &MusEGlobal::midiPorts[src.midiPort]; - RouteList* outRoutes = mp->outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - { - if(i->type == Route::TRACK_ROUTE && i->track == dst.track) // Is there a route to the track? - { - //printf("i->channel:%x dst.channel:%x\n", i->channel, dst.channel); - i->channel &= ~dst.channel; // Unset the desired channel bits. - if(i->channel == 0) // Only if there are no channel bits set, erase the route. - { - //printf("erasing out route from midi port:%d\n", src.midiPort); - outRoutes->erase(i); - } - - break; // For safety, keep looking and remove any more found. - // No, must break, else crash. There should only be one route anyway... - } - } - } - else - printf("removeRoute: source is midi port:%d but invalid\n", src.midiPort); - - if(dst.isValid()) - { - RouteList* inRoutes = dst.track->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) - { - if (i->type == Route::MIDI_PORT_ROUTE && i->midiPort == src.midiPort) // Is there a route to the midi port? - { - i->channel &= ~src.channel; // Unset the desired channel bits. - if(i->channel == 0) // Only if there are no channel bits set, erase the route. - inRoutes->erase(i); - - break; // For safety, keep looking and remove any more found. - // No, must break, else crash. There should only be one route anyway... - } - } - } - else - printf("removeRoute: source is midi port:%d but destination track invalid\n", src.midiPort); - } - else if(dst.type == Route::MIDI_PORT_ROUTE) + } + return false; + + } + else if(src.type == Route::MIDI_PORT_ROUTE) + { + bool ret1 = false; + bool ret2 = false; + if(src.isValid()) + ret1 = MusEGlobal::midiPorts[src.midiPort].outRoutes()->removeRoute(dst); + if(dst.isValid()) + { + switch(dst.type) + { + case Route::TRACK_ROUTE: + ret2 = dst.track->inRoutes()->removeRoute(src); + break; + case Route::MIDI_DEVICE_ROUTE: + ret2 = dst.device->inRoutes()->removeRoute(src); + break; + case Route::MIDI_PORT_ROUTE: + ret2 = MusEGlobal::midiPorts[dst.midiPort].inRoutes()->removeRoute(src); + break; + case Route::JACK_ROUTE: + ret2 = false; + break; + } + } + return ret1 || ret2; + } + else if(dst.type == Route::MIDI_PORT_ROUTE) + { + bool ret1 = false; + bool ret2 = false; + if(src.isValid()) + { + switch(src.type) + { + case Route::TRACK_ROUTE: + ret2 = src.track->outRoutes()->removeRoute(dst); + break; + case Route::MIDI_DEVICE_ROUTE: + ret2 = src.device->outRoutes()->removeRoute(dst); + break; + case Route::MIDI_PORT_ROUTE: + ret2 = MusEGlobal::midiPorts[src.midiPort].outRoutes()->removeRoute(dst); + break; + case Route::JACK_ROUTE: + ret2 = false; + break; + } + } + + if(dst.isValid()) + ret1 = MusEGlobal::midiPorts[dst.midiPort].inRoutes()->removeRoute(src); + + return ret1 || ret2; + } + else + { + if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) + { + fprintf(stderr, "removeRoute: source and destination are not tracks\n"); + return false; + } + + // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 + // After: src TrackA, Channel 4, Remote Channel 2 dst: TrackB channel 2 Remote Channel 4 + // + // Ex. (Handled above, not used here. For example only.) + // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 + // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 + src.remoteChannel = src.channel; + dst.remoteChannel = dst.channel; + const int src_chan = src.channel; + src.channel = dst.channel; + dst.channel = src_chan; + + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update the destination track's aux ref count, and all tracks it is routed to. + if(src.isValid() && dst.isValid() && src.track->outRoutes()->contains(dst) && dst.track->inRoutes()->contains(src)) + { + if(src.track->auxRefCount()) + src.track->updateAuxRoute( -src.track->auxRefCount(), dst.track ); + else + if(src.track->type() == Track::AUDIO_AUX) + src.track->updateAuxRoute( -1, dst.track ); + } + + bool ret1 = false; + bool ret2 = false; + + if(src.isValid()) + { + RouteList* o_rl = src.track->outRoutes(); + MusECore::iRoute o_ir = o_rl->find(dst); + if(o_ir != o_rl->end()) { - if(src.type != Route::TRACK_ROUTE) - { - fprintf(stderr, "removeRoute: destination is midi port:%d, but source is not track\n", dst.midiPort); - return; - } - - if(src.isValid()) - { - RouteList* outRoutes = src.track->outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - { - if (i->type == Route::MIDI_PORT_ROUTE && i->midiPort == dst.midiPort) // Is there a route to the midi port? - { - i->channel &= ~dst.channel; // Unset the desired channel bits. - if(i->channel == 0) // Only if there are no channel bits set, erase the route. - outRoutes->erase(i); - - break; // For safety, keep looking and remove any more found. - // No, must break, else crash. There should only be one route anyway... - } - } - } - else - printf("removeRoute: destination is midi port:%d but source track is invalid\n", dst.midiPort); - - if(dst.isValid()) - { - MidiPort *mp = &MusEGlobal::midiPorts[src.midiPort]; - RouteList* inRoutes = mp->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) - { - if (i->type == Route::TRACK_ROUTE && i->track == src.track) // Is there a route to the track? - { - i->channel &= ~src.channel; // Unset the desired channel bits. - if(i->channel == 0) // Only if there are no channel bits set, erase the route. - inRoutes->erase(i); - - break; // For safety, keep looking and remove any more found. - // No, must break, else crash. There should only be one route anyway... - } - } - } - else - printf("removeRoute: destination is midi port:%d but invalid\n", dst.midiPort); + o_rl->erase(o_ir); + ret1 = true; } - else + } + else + fprintf(stderr, "removeRoute: source is track but invalid\n"); + + if(dst.isValid()) + { + RouteList* i_rl = dst.track->inRoutes(); + MusECore::iRoute i_ir = i_rl->find(src); + if(i_ir != i_rl->end()) { - if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) - { - fprintf(stderr, "removeRoute: source and destination are not tracks\n"); - return; - } - - // Is the source an Aux Track or else does it have Aux Tracks routed to it? - // Update the destination track's aux ref count, and all tracks it is routed to. - if(src.isValid() && dst.isValid()) - { - if(src.track->auxRefCount()) - src.track->updateAuxRoute( -src.track->auxRefCount(), dst.track ); - else - if(src.track->type() == Track::AUDIO_AUX) - src.track->updateAuxRoute( -1, dst.track ); - } - - if(src.isValid()) - { - RouteList* outRoutes = src.track->outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - { - if (*i == dst) { - outRoutes->erase(i); - break; - } - } - } - else - printf("removeRoute: source is track but invalid\n"); - - if(dst.isValid()) - { - RouteList* inRoutes; - - inRoutes = dst.track->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) - { - if (*i == src) { - inRoutes->erase(i); - break; - } - } - } - else - printf("removeRoute: source is track but destination invalid\n"); + i_rl->erase(i_ir); + ret2 = true; } + } + else + fprintf(stderr, "removeRoute: destination is track but invalid\n"); + + return ret1 || ret2; + } + + return false; } //--------------------------------------------------------- @@ -734,7 +717,7 @@ if(src.type == Route::MIDI_DEVICE_ROUTE) src.device->outRoutes()->clear(); else - printf("removeAllRoutes: source is not midi device\n"); + fprintf(stderr, "removeAllRoutes: source is not midi device\n"); } if(dst.isValid()) @@ -742,7 +725,7 @@ if(dst.type == Route::MIDI_DEVICE_ROUTE) dst.device->inRoutes()->clear(); else - printf("removeAllRoutes: dest is not midi device\n"); + fprintf(stderr, "removeAllRoutes: dest is not midi device\n"); } } @@ -759,35 +742,57 @@ } //--------------------------------------------------------- +// icon +//--------------------------------------------------------- + +QPixmap* Route::icon(bool isSource, bool isMidi) const +{ + switch(type) + { + case TRACK_ROUTE: + if(track) + return track->icon(); + break; + + case JACK_ROUTE: + if(isMidi) + return isSource ? MusEGui::routesMidiInIcon : MusEGui::routesMidiOutIcon; + else + return isSource ? MusEGui::routesInIcon : MusEGui::routesOutIcon; + + case MIDI_DEVICE_ROUTE: + break; + + case MIDI_PORT_ROUTE: + return MusEGui::settings_midiport_softsynthsIcon; + } + return 0; +} + +//--------------------------------------------------------- // name // create string name representation for audio node //--------------------------------------------------------- -QString Route::name() const +QString Route::name(int preferred_name_or_alias) const { if(type == MIDI_DEVICE_ROUTE) { if(device) - { - // DELETETHIS 6 - // For unified jack in/out devices, the actual port names are now different from device name. - // Like this: device: "MyJackDevice1" -> inport: "MyJackDevice1_in" outport: "MyJackDevice1_out" - /* - if(device->deviceType() == MidiDevice::JACK_MIDI) - return MusEGlobal::audioDevice->portName(device->clientPort()); - else - */ - return device->name(); - } return QWidget::tr("None"); } else if(type == JACK_ROUTE) { - if (!MusEGlobal::checkAudioDevice()) return ""; - return MusEGlobal::audioDevice->portName(jackPort); - } + if(MusEGlobal::checkAudioDevice() && jackPort) + { + char s[ROUTE_PERSISTENT_NAME_SIZE]; + return QString(MusEGlobal::audioDevice->portName(jackPort, s, ROUTE_PERSISTENT_NAME_SIZE, preferred_name_or_alias)); + } + return QString(persistentJackPortName); + + } else if(type == MIDI_PORT_ROUTE) { @@ -798,6 +803,31 @@ } //--------------------------------------------------------- +// name +// fill and return str char name representation for audio node +//--------------------------------------------------------- +char* Route::name(char* str, int str_size, int preferred_name_or_alias) const +{ + if(type == MIDI_DEVICE_ROUTE) + return MusELib::strntcpy(str, device ? device->name().toLatin1().constData() : 0, str_size); + else + if(type == JACK_ROUTE) + { + if(MusEGlobal::checkAudioDevice() && jackPort) + return MusEGlobal::audioDevice->portName(jackPort, str, str_size, preferred_name_or_alias); + return MusELib::strntcpy(str, persistentJackPortName, str_size); + } + else + if(type == MIDI_PORT_ROUTE) + { + return MusELib::strntcpy(str, (ROUTE_MIDIPORT_NAME_PREFIX + QString().setNum(midiPort)).toLatin1().constData(), str_size); + } + else + return MusELib::strntcpy(str, track ? track->name().toLatin1().constData() : 0, str_size); + +} + +//--------------------------------------------------------- // name2route //--------------------------------------------------------- @@ -808,7 +838,7 @@ // Support old route style in med files. Obsolete. if (rn[0].isNumber() && rn[1]==':') { - channel = rn[0].toAscii() - int('1'); + channel = rn[0].toLatin1() - int('1'); s = rn.mid(2); } @@ -872,9 +902,9 @@ return Route(track, channel); } } + return Route((Track*) 0, channel); } else - //if((rtype == Route::JACK_MIDI_ROUTE) || (rtype == Route::ALSA_MIDI_ROUTE)) // TODO Distinguish the device types if(rtype == Route::MIDI_DEVICE_ROUTE) { @@ -882,35 +912,20 @@ { if((*i)->name() == s) return Route(*i, channel); - - /* DELETETHIS 16? - MidiJackDevice* jmd = dynamic_cast(*i); - if(jmd) - { - if(jmd->name() == s) - //if (jmd->name() == rn) - return Route(jmd); - } - MidiAlsaDevice* amd = dynamic_cast(*i); - if(amd) - { - // TODO - if(amd->name() == s) - //if (amd->name() == rn) - return Route(amd); - } - */ } + return Route((MidiDevice*) 0, channel); } else if(rtype == Route::JACK_ROUTE) { if(MusEGlobal::checkAudioDevice()) { + // REMOVE Tim. Persistent routes. TODO FIXME: Use new cached jack port names. void* p = MusEGlobal::audioDevice->findPort(s.toLatin1().constData()); if(p) return Route(p, channel); } + return Route((void*) 0, channel); } else if(rtype == Route::MIDI_PORT_ROUTE) @@ -922,110 +937,680 @@ if(ok) return Route(port, channel); } + return Route((int) 0, channel); } } - printf(" name2route: <%s> not found\n", rn.toLatin1().constData()); + fprintf(stderr, " name2route: <%s> not found\n", rn.toLatin1().constData()); return Route((Track*) 0, channel); } //--------------------------------------------------------- -// checkRoute -// return true if route is valid +// routeCanConnect //--------------------------------------------------------- -bool checkRoute(const QString& s, const QString& d) +bool routeCanConnect(const Route& src, const Route& dst) +{ + if(!src.isValid() || !dst.isValid()) + return false; + + if(src.type == Route::JACK_ROUTE) + { + if(!MusEGlobal::checkAudioDevice() || + !src.jackPort || + MusEGlobal::audioDevice->portDirection(src.jackPort) != MusECore::AudioDevice::OutputPort) + return false; + + if(dst.type == Route::TRACK_ROUTE) + { + if(MusEGlobal::audioDevice->portType(src.jackPort) != MusECore::AudioDevice::AudioPort || + dst.track->type() != Track::AUDIO_INPUT || + dst.channel < 0) + return false; + const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); + return !dst.track->inRoutes()->contains(v_src); + } + else if(dst.type == Route::MIDI_DEVICE_ROUTE) + { + if(MusEGlobal::audioDevice->portType(src.jackPort) != MusECore::AudioDevice::MidiPort || + dst.device->deviceType() != MidiDevice::JACK_MIDI) + return false; + const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); + return !dst.device->inRoutes()->contains(v_src); + } + else if(dst.type == Route::JACK_ROUTE) + { + // Allow direct Jack connections! + return MusEGlobal::audioDevice && MusEGlobal::audioDevice->portsCanConnect(src.jackPort, dst.jackPort); + } + else + return false; + } + else if(dst.type == Route::JACK_ROUTE) { - Route src(s, false, -1); - Route dst(d, true, -1); - - if (!(src.isValid() && dst.isValid()) || (src == dst)) + if(!MusEGlobal::checkAudioDevice() || + !dst.jackPort || + MusEGlobal::audioDevice->portDirection(dst.jackPort) != MusECore::AudioDevice::InputPort) + return false; + + if(src.type == Route::TRACK_ROUTE) + { + if(MusEGlobal::audioDevice->portType(dst.jackPort) != MusECore::AudioDevice::AudioPort || + src.track->type() != Track::AUDIO_OUTPUT || src.channel < 0) + return false; + const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, -1, dst.persistentJackPortName); + return !src.track->outRoutes()->contains(v_dst); + } + else + if(src.type == Route::MIDI_DEVICE_ROUTE) + { + if(MusEGlobal::audioDevice->portType(dst.jackPort) != MusECore::AudioDevice::MidiPort || + src.device->deviceType() != MidiDevice::JACK_MIDI) + return false; + const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, -1, dst.persistentJackPortName); + return !src.device->outRoutes()->contains(v_dst); + } + else if(src.type == Route::JACK_ROUTE) + { + // Allow direct Jack connections! + return MusEGlobal::audioDevice && MusEGlobal::audioDevice->portsCanConnect(src.jackPort, dst.jackPort); + } + else + return false; + } + else if(src.type == Route::MIDI_PORT_ROUTE) + { + if(dst.type != Route::TRACK_ROUTE || !dst.track->isMidiTrack() || dst.channel < -1 || dst.channel >= MIDI_CHANNELS) + return false; + + //MidiPort *mp = &MusEGlobal::midiPorts[src.midiPort]; + + // Do not allow synth ports to connect to any track. It may be useful in some cases, + // may be desired later, but for now it's just a routing hassle. p4.0.35 + //if(mp->device() && mp->device()->isSynti()) + // return false; + + const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); + // If one route node exists and one is missing, it's OK to reconnect, addRoute will take care of it. + if(!MusEGlobal::midiPorts[src.midiPort].outRoutes()->contains(dst) || !dst.track->inRoutes()->contains(v_src)) + return true; + return false; - if (src.type == Route::JACK_ROUTE) + } + else if(dst.type == Route::MIDI_PORT_ROUTE) { - if (dst.type == Route::TRACK_ROUTE) + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + return false; +#endif + + if(src.type != Route::TRACK_ROUTE || !src.track->isMidiTrack() || src.channel < -1 || src.channel >= MIDI_CHANNELS) + return false; + const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, dst.channel, dst.persistentJackPortName); + + // If one route node exists and one is missing, it's OK to reconnect, addRoute will take care of it. + if(!src.track->outRoutes()->contains(v_dst) || !MusEGlobal::midiPorts[dst.midiPort].inRoutes()->contains(src)) + return true; + + return false; + } + else + { + if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) + return false; + if(src.track && dst.track && src.track == dst.track) + return false; + + switch(src.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: + return false; + + case Track::AUDIO_INPUT: + if(src.channel >= 0) + return false; + break; + } + break; + + + case Track::AUDIO_OUTPUT: + switch(dst.track->type()) { - if (dst.track->type() != Track::AUDIO_INPUT) { - return false; - } - src.channel = dst.channel; - RouteList* inRoutes = dst.track->inRoutes(); - for (ciRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) - { - if (*i == src) { // route already there - return false; - } - } + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: + return false; + + case Track::AUDIO_INPUT: + if(src.channel >= 0 || dst.channel >= 0) + return false; + break; } - else - if (dst.type == Route::MIDI_DEVICE_ROUTE) + break; + + case Track::AUDIO_INPUT: + switch(dst.track->type()) { - src.channel = -1; - RouteList* routes = dst.device->inRoutes(); - for (ciRoute i = routes->begin(); i != routes->end(); ++i) - { - if (*i == src) { // route already there - return false; - } - } + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + case Track::WAVE: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + case Track::AUDIO_GROUP: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + case Track::AUDIO_AUX: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::AUDIO_GROUP: + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + case Track::AUDIO_SOFTSYNTH: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::AUDIO_GROUP: + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + } + + if((src.channel == -1 && dst.channel != -1) || (dst.channel == -1 && src.channel != -1)) + return false; + + if(src.channels != dst.channels) + return false; + + // Allow for -1 = omni route. + if(src.channel >= src.track->routeCapabilities()._trackChannels._outChannels || + dst.channel >= dst.track->routeCapabilities()._trackChannels._inChannels) + return false; + + if(src.track->isCircularRoute(dst.track)) + return false; + + // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 + // After: src TrackA, Channel 4, Remote Channel 2 dst: TrackB channel 2 Remote Channel 4 + // + // Ex. (Handled above, not used here. For example only.) + // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 + // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 + const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); + const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, dst.channel, dst.persistentJackPortName); + + // Allow it to reconnect a partial route. + if(!v_src.isValid() || !v_dst.isValid() || (src.track->outRoutes()->contains(v_dst) && dst.track->inRoutes()->contains(v_src))) + return false; + + return true; + } + return false; +} + +//--------------------------------------------------------- +// routeCanDisconnect +//--------------------------------------------------------- + +bool routeCanDisconnect(const Route& src, const Route& dst) +{ + if(src.type == Route::JACK_ROUTE) + { + //if(!dst.isValid()) + if(!dst.exists()) + return false; + + if(dst.type == Route::TRACK_ROUTE) + { + if(dst.track->type() != Track::AUDIO_INPUT) + return false; + const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, -1, src.persistentJackPortName); + return dst.track->inRoutes()->contains(v_src); + } + else if(dst.type == Route::MIDI_DEVICE_ROUTE) + { + return dst.device->inRoutes()->contains(src); + } + else if(dst.type == Route::JACK_ROUTE) + { + // Allow direct Jack connections! Pass the port names here instead of ports so that + // persistent routes (where jackPort = NULL) can be removed. + return MusEGlobal::audioDevice && MusEGlobal::audioDevice->portsCanDisconnect(src.persistentJackPortName, dst.persistentJackPortName); } else return false; - } - else if (dst.type == Route::JACK_ROUTE) + } + else if(dst.type == Route::JACK_ROUTE) { - if (src.type == Route::TRACK_ROUTE) + if(!src.exists()) + return false; + + if(src.type == Route::TRACK_ROUTE) { - if (src.track->type() != Track::AUDIO_OUTPUT) { + if(src.track->type() != Track::AUDIO_OUTPUT) return false; - } - RouteList* outRoutes = src.track->outRoutes(); - dst.channel = src.channel; - for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - { - if (*i == dst) { // route already there - return false; - } - } + const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, -1, dst.persistentJackPortName); + return src.track->outRoutes()->contains(v_dst); + } + else if(src.type == Route::MIDI_DEVICE_ROUTE) + { + return src.device->outRoutes()->contains(dst); + } + else if(src.type == Route::JACK_ROUTE) + { + // Allow direct Jack disconnections! Pass the port names here instead of ports so that + // persistent routes (where jackPort = NULL) can be removed. + return MusEGlobal::audioDevice && MusEGlobal::audioDevice->portsCanDisconnect(src.persistentJackPortName, dst.persistentJackPortName); } else - if (src.type == Route::MIDI_DEVICE_ROUTE) + return false; + } + else if(src.type == Route::MIDI_PORT_ROUTE) + { + if(!src.isValid() || src.channel < -1 || src.channel >= MIDI_CHANNELS || + dst.type != Route::TRACK_ROUTE || !dst.exists() || !dst.track->isMidiTrack() || dst.channel < -1 || dst.channel >= MIDI_CHANNELS) + return false; + + // Allow it to disconnect a partial route. + if(MusEGlobal::midiPorts[src.midiPort].outRoutes()->contains(dst) || dst.track->inRoutes()->contains(src)) + return true; + + return false; + } + else if(dst.type == Route::MIDI_PORT_ROUTE) + { + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + return false; +#endif + + if(!dst.isValid() || dst.channel < -1 || dst.channel >= MIDI_CHANNELS || + src.type != Route::TRACK_ROUTE || !src.exists() || !src.track->isMidiTrack() || src.channel < -1 || src.channel >= MIDI_CHANNELS) + return false; + + // Allow it to disconnect a partial route. + if(src.track->outRoutes()->contains(dst) || MusEGlobal::midiPorts[dst.midiPort].inRoutes()->contains(src)) + return true; + + return false; + } + else + { + if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) + return false; + if(src.track && dst.track && src.track == dst.track) + return false; + + // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 + // After: src TrackA, Channel 4, Remote Channel 2 dst: TrackB channel 2 Remote Channel 4 + // + // Ex. (Handled above, not used here. For example only.) + // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 + // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 + const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); + const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, dst.channel, dst.persistentJackPortName); + + // Allow it to disconnect a partial route. + if((v_src.exists() && src.track->outRoutes()->contains(v_dst)) || (v_dst.exists() && dst.track->inRoutes()->contains(v_src))) + return true; + + return false; + } + return false; +} + +//--------------------------------------------------------- +// routeConnectionPossible +//--------------------------------------------------------- + +bool routesCompatible(const Route& src, const Route& dst, bool check_types_only) +{ + if(!src.isValid() || !dst.isValid()) + return false; + + if(src.type == Route::JACK_ROUTE) + { + if(!MusEGlobal::checkAudioDevice() || + !src.jackPort || + MusEGlobal::audioDevice->portDirection(src.jackPort) != MusECore::AudioDevice::OutputPort) + return false; + + if(dst.type == Route::TRACK_ROUTE) { - RouteList* routes = src.device->outRoutes(); - dst.channel = -1; - for (ciRoute i = routes->begin(); i != routes->end(); ++i) - { - if (*i == dst) { // route already there - return false; - } - } - } + if(MusEGlobal::audioDevice->portType(src.jackPort) != MusECore::AudioDevice::AudioPort || + dst.track->type() != Track::AUDIO_INPUT || + (!check_types_only && dst.channel < 0)) + return false; + return true; + } + else if(dst.type == Route::MIDI_DEVICE_ROUTE) + { + if(MusEGlobal::audioDevice->portType(src.jackPort) != MusECore::AudioDevice::MidiPort || + dst.device->deviceType() != MidiDevice::JACK_MIDI) + return false; + return true; + } + else if(dst.type == Route::JACK_ROUTE) + // Allow direct Jack connections! + return MusEGlobal::audioDevice->portsCompatible(src.jackPort, dst.jackPort); else return false; - } - else if (src.type == Route::MIDI_PORT_ROUTE) + } + else if(dst.type == Route::JACK_ROUTE) { - RouteList* outRoutes = MusEGlobal::midiPorts[src.midiPort].outRoutes(); - for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + if(!MusEGlobal::checkAudioDevice() || + !dst.jackPort || + MusEGlobal::audioDevice->portDirection(dst.jackPort) != MusECore::AudioDevice::InputPort) + return false; + + if(src.type == Route::TRACK_ROUTE) { - if (*i == dst) { // route already there - return false; - } - } + if(MusEGlobal::audioDevice->portType(dst.jackPort) != MusECore::AudioDevice::AudioPort || + src.track->type() != Track::AUDIO_OUTPUT || + (!check_types_only && src.channel < 0)) + return false; + return true; + } + else if(src.type == Route::MIDI_DEVICE_ROUTE) + { + if(MusEGlobal::audioDevice->portType(dst.jackPort) != MusECore::AudioDevice::MidiPort || + src.device->deviceType() != MidiDevice::JACK_MIDI) + return false; + return true; + } + // Unnecessary. This condition is handled above. + //else if(src.type == Route::JACK_ROUTE) + // // Allow direct Jack connections! + // return MusEGlobal::audioDevice->portsCompatible(src.jackPort, dst.jackPort); + else + return false; + } + else if(src.type == Route::MIDI_PORT_ROUTE) + { + if(dst.type != Route::TRACK_ROUTE || !dst.track->isMidiTrack()) + return false; + + //MidiPort *mp = &MusEGlobal::midiPorts[src.midiPort]; + + // Do not allow synth ports to connect to any track. It may be useful in some cases, + // may be desired later, but for now it's just a routing hassle. p4.0.35 + //if(mp->device() && mp->device()->isSynti()) + // return false; + + if(check_types_only) + return true; + + if(dst.channel < -1 || dst.channel >= MIDI_CHANNELS) + return false; + + // If one route node exists and one is missing, it's OK to reconnect, addRoute will take care of it. + return true; + } + else if(dst.type == Route::MIDI_PORT_ROUTE) + { + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + return false; +#endif + + if(src.type != Route::TRACK_ROUTE || !src.track->isMidiTrack()) + return false; + + if(check_types_only) + return true; + + if(src.channel < -1 || src.channel >= MIDI_CHANNELS) + return false; + + // If one route node exists and one is missing, it's OK to reconnect, addRoute will take care of it. + return true; } else { - RouteList* outRoutes = (src.type == Route::MIDI_DEVICE_ROUTE) ? src.device->outRoutes() : src.track->outRoutes(); + if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) + return false; + if(src.track && dst.track && src.track == dst.track) + return false; + + switch(src.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: + return false; + + case Track::AUDIO_INPUT: + if(!check_types_only && src.channel >= 0) + return false; + break; + } + break; + - for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + case Track::AUDIO_OUTPUT: + switch(dst.track->type()) { - if (*i == dst) { // route already there - return false; - } + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + case Track::AUDIO_SOFTSYNTH: + return false; + + case Track::AUDIO_INPUT: + if(!check_types_only && (src.channel >= 0 || dst.channel >= 0)) + return false; + break; } + break; + + case Track::AUDIO_INPUT: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + case Track::WAVE: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + case Track::AUDIO_GROUP: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + case Track::AUDIO_AUX: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::AUDIO_GROUP: + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + case Track::AUDIO_SOFTSYNTH: + switch(dst.track->type()) + { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + case Track::AUDIO_INPUT: + case Track::AUDIO_AUX: + return false; + + case Track::AUDIO_GROUP: + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_SOFTSYNTH: + break; + } + break; + + } + + // Don't bother with the circular route check, below. + if(check_types_only) + return true; + + if((src.channel == -1 && dst.channel != -1) || + (dst.channel == -1 && src.channel != -1) || + (src.channels != dst.channels)) + return false; + + // Allow for -1 = omni route. + if(src.channel >= src.track->routeCapabilities()._trackChannels._outChannels || + dst.channel >= dst.track->routeCapabilities()._trackChannels._inChannels || + src.track->isCircularRoute(dst.track)) + return false; + + return true; } - return true; - } + return false; +} + //--------------------------------------------------------- // read @@ -1036,7 +1621,7 @@ QString s; int dtype = MidiDevice::ALSA_MIDI; int port = -1; - unsigned char rtype = Route::TRACK_ROUTE; + RouteType rtype = Route::TRACK_ROUTE; for (;;) { @@ -1049,10 +1634,10 @@ return; case Xml::Attribut: #ifdef ROUTE_DEBUG - printf("Route::read(): attribute:%s\n", tag.toLatin1().constData()); + fprintf(stderr, "Route::read(): attribute:%s\n", tag.toLatin1().constData()); #endif if(tag == "type") - rtype = xml.s2().toInt(); + rtype = RouteType(xml.s2().toInt()); else if(tag == "devtype") { @@ -1069,11 +1654,11 @@ rtype = Route::MIDI_PORT_ROUTE; } else - printf("Route::read(): unknown attribute:%s\n", tag.toLatin1().constData()); + fprintf(stderr, "Route::read(): unknown attribute:%s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: #ifdef ROUTE_DEBUG - printf("Route::read(): tag end type:%d channel:%d name:%s\n", rtype, channel, s.toLatin1().constData()); + fprintf(stderr, "Route::read(): tag end type:%d channel:%d name:%s\n", rtype, channel, s.toLatin1().constData()); #endif if(rtype == MIDI_PORT_ROUTE) { @@ -1083,7 +1668,7 @@ midiPort = port; } else - printf("Route::read(): midi port <%d> out of range\n", port); + fprintf(stderr, "Route::read(): midi port <%d> out of range\n", port); } else if(!s.isEmpty()) @@ -1103,21 +1688,23 @@ } } if(i == tl->end()) - printf("Route::read(): track <%s> not found\n", s.toLatin1().constData()); + fprintf(stderr, "Route::read(): track <%s> not found\n", s.toLocal8Bit().constData()); } else if(rtype == JACK_ROUTE) { - void* jport = 0; - if (MusEGlobal::audioDevice) // fix crash if jack is zombified at this point - jport=MusEGlobal::audioDevice->findPort(s.toLatin1().constData()); - if(jport == 0) - printf("Route::read(): jack port <%s> not found\n", s.toLatin1().constData()); - else + type = rtype; + jackPort = 0; + if(MusEGlobal::audioDevice) // fix crash if jack is zombified at this point { - jackPort = jport; - type = rtype; + jackPort = MusEGlobal::audioDevice->findPort(s.toLatin1().constData()); + if(jackPort) + // Replace the name with a more appropriate one at this time. + MusEGlobal::audioDevice->portName(jackPort, persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); } + // The graph change handler will replace persistentJackPortName with a more appropriate name if necessary. + if(!jackPort) + MusELib::strntcpy(persistentJackPortName, s.toLatin1().constData(), ROUTE_PERSISTENT_NAME_SIZE); } else if(rtype == MIDI_DEVICE_ROUTE) @@ -1130,7 +1717,9 @@ { // We found a device, but if it is not in use by the song (port is -1), ignore it. // This prevents loading and propagation of bogus routes in the med file. - if(md->midiPort() == -1) + // We found a device, but if it is not a jack midi and in use by the song (port is -1), ignore it. + // This prevents loading and propagation of bogus routes in the med file. + if(md->midiPort() == -1 && md->deviceType() != MidiDevice::JACK_MIDI) break; device = md; @@ -1139,7 +1728,7 @@ } } if(imd == MusEGlobal::midiDevices.end()) - printf("Route::read(): midi device <%s> not found\n", s.toLatin1().constData()); + fprintf(stderr, "Route::read(): midi device <%s> not found\n", s.toLatin1().constData()); } } return; @@ -1159,8 +1748,10 @@ QString src; QString dst; int ch = -1; + int chmask = -1; int chs = -1; int remch = -1; + bool midi_track_out_set = false; Route sroute, droute; @@ -1190,16 +1781,26 @@ else if (tag == "dest") { droute.read(xml); - droute.channel = ch; droute.channels = chs; - droute.remoteChannel = remch; + // If channels was given, it should be a multi-channel audio route. + // Convert to new scheme by switching them around for the destination route: + if(chs > 0) + { + droute.channel = remch; + droute.remoteChannel = ch; + } + else + { + droute.channel = ch; + droute.remoteChannel = remch; + } } else xml.unknown("readRoute"); break; case Xml::Attribut: #ifdef ROUTE_DEBUG - printf("Song::readRoute(): attribute:%s\n", tag.toLatin1().constData()); + fprintf(stderr, "Song::readRoute(): attribute:%s\n", tag.toLatin1().constData()); #endif if(tag == "channel") ch = xml.s2().toInt(); @@ -1211,9 +1812,9 @@ remch = xml.s2().toInt(); else if(tag == "channelMask") // New channel mask for midi port-track routes. - ch = xml.s2().toInt(); + chmask = xml.s2().toInt(); else - printf("Song::readRoute(): unknown attribute:%s\n", tag.toLatin1().constData()); + fprintf(stderr, "Song::readRoute(): unknown attribute:%s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (xml.s1() == "Route") @@ -1229,7 +1830,7 @@ // Support new routes. if(sroute.isValid() && droute.isValid()) { - // Support pre- 1.1-RC2 midi-device-to-track routes. Obsolete. Replaced with midi port routes. + // Support pre- 1.1-RC2 midi device to track routes. Obsolete. Replaced with midi port routes. if(sroute.type == Route::MIDI_DEVICE_ROUTE && droute.type == Route::TRACK_ROUTE) { if(sroute.device->midiPort() >= 0 && sroute.device->midiPort() < MIDI_PORTS @@ -1239,40 +1840,150 @@ sroute.device = 0; sroute.type = Route::MIDI_PORT_ROUTE; - sroute.channel = 1 << ch; // Convert to new bit-wise channel mask. + sroute.channel = ch; droute.channel = sroute.channel; addRoute(sroute, droute); } else - printf(" Warning - device:%s to track route, no device midi port or chan:%d out of range. Ignoring route!\n", + fprintf(stderr, " Warning - device:%s to track route, no device midi port or chan:%d out of range. Ignoring route!\n", sroute.device->name().toLatin1().constData(), ch); } - else if(sroute.type == Route::TRACK_ROUTE && droute.type == Route::MIDI_DEVICE_ROUTE) + // Support pre- 1.1-RC2 track to midi device routes. Obsolete. Replaced with midi port routes. + else if(sroute.type == Route::TRACK_ROUTE && droute.type == Route::MIDI_DEVICE_ROUTE) { - if(droute.device->midiPort() >= 0 && droute.device->midiPort() < MIDI_PORTS - && ch >= 0 && ch < MIDI_CHANNELS) + // Device and track already validated in ::read(). + const int port = droute.device->midiPort(); + if(port >= 0 && port < MIDI_PORTS + && ch >= 0 && ch < MIDI_CHANNELS && + sroute.track->isMidiTrack()) { + MidiTrack* mt = static_cast(sroute.track); +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + if(!midi_track_out_set) + { + midi_track_out_set = true; + MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; + MusEGlobal::audio->msgIdle(true); + changed |= mt->setOutPortAndChannelAndUpdate(port, ch, false); + MusEGlobal::audio->msgIdle(false); + MusEGlobal::audio->msgUpdateSoloStates(); + MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); + } +#else droute.midiPort = droute.device->midiPort(); droute.device = 0; droute.type = Route::MIDI_PORT_ROUTE; - - droute.channel = 1 << ch; // Convert to new bit-wise channel mask. + droute.channel = ch; sroute.channel = droute.channel; - addRoute(sroute, droute); +#endif } else - printf(" Warning - track to device:%s route, no device midi port or chan:%d out of range. Ignoring route!\n", + fprintf(stderr, " Warning - track to device:%s route, no device midi port or chan:%d out of range. Ignoring route!\n", droute.device->name().toLatin1().constData(), ch); } - else + // Support old bit-wise channel mask for midi port to midi track routes and midi port to audio input soling chain routes. Obsolete! + // Check for song file version 2.0 or below: + else if(chmask > 0 && (xml.majorVersion() * 1000000 + xml.minorVersion()) <= 2000000) // Arbitrary shift, and add { - addRoute(sroute, droute); + fprintf(stderr, " Warning - Route: Converting old single-route bitwise channel mask:%d to individual routes\n", chmask); + + if(sroute.type == Route::MIDI_PORT_ROUTE && droute.type == Route::TRACK_ROUTE) + { + if(droute.track->isMidiTrack()) + { + // All channels set? Convert to new Omni route. + if(chmask == ((1 << MIDI_CHANNELS) - 1)) + { + sroute.channel = -1; + droute.channel = -1; + addRoute(sroute, droute); + } + else + { + // Check each channel bit: + for(int i = 0; i < MIDI_CHANNELS; ++i) + { + const int chbit = 1 << i; + // Is channel bit set? + if(chmask & chbit) + { + // Convert to individual routes: + sroute.channel = i; + droute.channel = i; + addRoute(sroute, droute); + } + } + } + } + // Support old midi port to audio input soloing chain routes. Obsolete! + else if(droute.track->type() == Track::AUDIO_INPUT) + { + const int port = sroute.midiPort; + // Check each channel bit: + for(int i = 0; i < MIDI_CHANNELS; ++i) + { + const int chbit = 1 << i; + // Is channel bit set? + if(chmask & chbit) + { + const MusECore::MidiTrackList* const mtl = MusEGlobal::song->midis(); + for(ciMidiTrack imt = mtl->begin(); imt != mtl->end(); ++imt) + { + MidiTrack* const mt = *imt; + if(mt->outPort() == port && mt->outChannel() == i) + { + // Convert to a midi track to audio input route: + sroute.type = Route::TRACK_ROUTE; + sroute.track = mt; + sroute.midiPort = -1; + sroute.channel = sroute.channels = sroute.remoteChannel = droute.channel = droute.channels = droute.remoteChannel = -1; + addRoute(sroute, droute); + } + } + } + } + } + } + } + // If channels was given, it must be a multi-channel audio route: + else if(chs > 0) + { + // If EITHER the channel or the remote channel are zero but the other not given, convert to an Omni route: + if((ch == -1 && remch == -1) || (ch == 0 && remch == -1) || (remch == 0 && ch == -1)) + { + sroute.channel = sroute.remoteChannel = sroute.channels = droute.channel = droute.remoteChannel = droute.channels = -1; + addRoute(sroute, droute); + } + // Otherwise convert to individual routes: + else + { + sroute.channels = droute.channels = 1; + if(sroute.channel == -1) + sroute.channel = 0; + if(sroute.remoteChannel == -1) + sroute.remoteChannel = 0; + if(droute.channel == -1) + droute.channel = 0; + if(droute.remoteChannel == -1) + droute.remoteChannel = 0; + for(int i = 0; i < chs; ++i) + { + addRoute(sroute, droute); + ++sroute.channel; + ++sroute.remoteChannel; + ++droute.channel; + ++droute.remoteChannel; + } + } } + else + + addRoute(sroute, droute); } else - printf(" Warning - route invalid. Ignoring route!\n"); + fprintf(stderr, " Warning - route invalid. Ignoring route!\n"); return; } @@ -1283,21 +1994,6 @@ } //--------------------------------------------------------- -// removeRoute -//--------------------------------------------------------- - -void RouteList::removeRoute(const Route& r) - { - for (iRoute i = begin(); i != end(); ++i) { - if (r == *i) { - erase(i); - return; - } - } - printf("internal error: cannot remove Route\n"); - } - -//--------------------------------------------------------- // dump //--------------------------------------------------------- @@ -1306,54 +2002,70 @@ if (type == TRACK_ROUTE) { if(track) - printf("Route dump: track <%s> channel %d channels %d\n", track->name().toLatin1().constData(), channel, channels); + fprintf(stderr, "Route dump: track <%s> channel %d channels %d\n", track->name().toLocal8Bit().constData(), channel, channels); } else if (type == JACK_ROUTE) { if(MusEGlobal::checkAudioDevice()) - printf("Route dump: jack audio port <%s> channel %d\n", MusEGlobal::audioDevice->portName(jackPort).toLatin1().constData(), channel); + { + if(jackPort) + { + char s[ROUTE_PERSISTENT_NAME_SIZE]; + fprintf(stderr, "Route dump: jack audio port %p <%s> persistent name <%s> channel %d\n", jackPort, MusEGlobal::audioDevice->portName(jackPort, s, ROUTE_PERSISTENT_NAME_SIZE), persistentJackPortName, channel); + } + else + fprintf(stderr, "Route dump: jack audio port %p persistent name <%s> channel %d\n", jackPort, persistentJackPortName, channel); + } } else if (type == MIDI_PORT_ROUTE) { - printf("Route dump: midi port <%d> channel mask %d\n", midiPort, channel); + fprintf(stderr, "Route dump: midi port <%d> channel mask %d\n", midiPort, channel); } else if (type == MIDI_DEVICE_ROUTE) { - printf("Route dump: "); + fprintf(stderr, "Route dump: "); if(device) { if(device->deviceType() == MidiDevice::JACK_MIDI) { if(MusEGlobal::checkAudioDevice()) { - printf("jack midi device <%s> ", device->name().toLatin1().constData()); + fprintf(stderr, "jack midi device <%s> ", device->name().toLatin1().constData()); if(device->inClientPort()) - printf("input port <%s> ", - MusEGlobal::audioDevice->portName(device->inClientPort()).toLatin1().constData()); + { + char s[ROUTE_PERSISTENT_NAME_SIZE]; + fprintf(stderr, "input port <%s> ", + //MusEGlobal::audioDevice->portName(device->inClientPort()).toLatin1().constData()); + MusEGlobal::audioDevice->portName(device->inClientPort(), s, ROUTE_PERSISTENT_NAME_SIZE)); + } if(device->outClientPort()) - printf("output port <%s> ", - MusEGlobal::audioDevice->portName(device->outClientPort()).toLatin1().constData()); + { + char s[ROUTE_PERSISTENT_NAME_SIZE]; + fprintf(stderr, "output port <%s> ", + //MusEGlobal::audioDevice->portName(device->outClientPort()).toLatin1().constData()); + MusEGlobal::audioDevice->portName(device->outClientPort(), s, ROUTE_PERSISTENT_NAME_SIZE)); + } } } else if(device->deviceType() == MidiDevice::ALSA_MIDI) - printf("alsa midi device <%s> ", device->name().toLatin1().constData()); + fprintf(stderr, "alsa midi device <%s> ", device->name().toLatin1().constData()); else if(device->deviceType() == MidiDevice::SYNTH_MIDI) - printf("synth midi device <%s> ", device->name().toLatin1().constData()); + fprintf(stderr, "synth midi device <%s> ", device->name().toLatin1().constData()); else - printf("is midi but unknown device type:%d, ", device->deviceType()); + fprintf(stderr, "is midi but unknown device type:%d, ", device->deviceType()); } else - printf("is midi but invalid device, "); + fprintf(stderr, "is midi but invalid device, "); - printf("channel:%d\n", channel); + fprintf(stderr, "channel:%d\n", channel); } else - printf("Route dump: unknown route type:%d\n", type); + fprintf(stderr, "Route dump: unknown route type:%d\n", type); } //--------------------------------------------------------- @@ -1369,13 +2081,84 @@ return track == a.track && channels == a.channels && remoteChannel == a.remoteChannel; } else - if(channel == a.channel) { if (type == JACK_ROUTE) { - //if (!MusEGlobal::checkAudioDevice()) return false; DELETETHIS? or keep? - //else return MusEGlobal::audioDevice->portName(jackPort) == MusEGlobal::audioDevice->portName(a.jackPort); - return jackPort == a.jackPort; // Simplified. + // If the ports are valid compare them, otherwise compare the persistent port names. + if(jackPort && a.jackPort) + return jackPort == a.jackPort; // Simplified. + else + return strcmp(persistentJackPortName, a.persistentJackPortName) == 0; + } + else + if (type == MIDI_PORT_ROUTE) + { + return midiPort == a.midiPort; + } + else + if (type == MIDI_DEVICE_ROUTE) + { + return device == a.device; + } + } + } + return false; +} + +//--------------------------------------------------------- +// exists +//--------------------------------------------------------- + +bool Route::exists() const +{ + switch(type) + { + case MusECore::Route::TRACK_ROUTE: + return MusEGlobal::song->tracks()->contains(track); + break; + + case MusECore::Route::JACK_ROUTE: + return MusEGlobal::checkAudioDevice() && MusEGlobal::audioDevice->findPort(persistentJackPortName); + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + return MusEGlobal::midiDevices.contains(device); + break; + + case MusECore::Route::MIDI_PORT_ROUTE: + return isValid(); + break; + } + return false; +} + +//--------------------------------------------------------- +// compare +//--------------------------------------------------------- + +bool Route::compare(const Route& a) const +{ + //if ((type == a.type) && (channel == a.channel)) + if (type == a.type) + { + if (type == TRACK_ROUTE) + { + return track == a.track && + channels == a.channels && + ((a.channel == -1) ? (channel == -1) : (channel != -1)) && + //remoteChannel == a.remoteChannel; + ((a.remoteChannel == -1) ? (remoteChannel == -1) : (remoteChannel != -1)); // TODO: Want this? Seems logical. + } + else + //if(channel == a.channel) + { + if (type == JACK_ROUTE && channel == a.channel) + { + // If the ports are valid compare them, otherwise compare the persistent port names. + if(jackPort && a.jackPort) + return jackPort == a.jackPort; // Simplified. + else + return strcmp(persistentJackPortName, a.persistentJackPortName) == 0; } else if (type == MIDI_PORT_ROUTE) @@ -1392,7 +2175,7 @@ return false; } -/* yaaaay, depth search. just wrote an exam about that. but DELETETHIS, looks unused +/* Please keep this just in case... //--------------------------------------------------------- // isCircularRoute // Recursive. diff -Nru muse-2.1.2/muse/route.h muse-3.0.2+ds1/muse/route.h --- muse-2.1.2/muse/route.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/route.h 2018-01-06 20:31:35.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: route.h,v 1.5.2.1 2008/05/21 00:28:52 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -28,60 +28,132 @@ #include #include -#include #include "globaldefs.h" +#define ROUTE_PERSISTENT_NAME_SIZE 256 // Size of char array Route::persistentName, including the terminating null character. + class QString; +class QPixmap; namespace MusECore { class Track; class MidiDevice; class Xml; +class PendingOperationList; + + +struct RouteChannelsDescriptor +{ + // Independent of _inChannels/_outChannels. Typically represents 'Omni' for objects + // which have channels, otherwise it just means the object itself is routable. + bool _inRoutable; + bool _outRoutable; + + int _inChannels; + int _outChannels; + + RouteChannelsDescriptor() : _inRoutable(false), + _outRoutable(false), + _inChannels(0), + _outChannels(0) { } + RouteChannelsDescriptor(bool inRoutable, + bool outRoutable, + int inChannels, + int outChannels) + : _inRoutable(inRoutable), + _outRoutable(outRoutable), + _inChannels(inChannels), + _outChannels(outChannels) { } +}; +typedef RouteChannelsDescriptor TrackRouteDescriptor; +typedef RouteChannelsDescriptor JackRouteDescriptor; +typedef RouteChannelsDescriptor MidiDeviceRouteDescriptor; +typedef RouteChannelsDescriptor MidiPortRouteDescriptor; + +struct RouteCapabilitiesStruct +{ + TrackRouteDescriptor _trackChannels; + JackRouteDescriptor _jackChannels; + MidiDeviceRouteDescriptor _midiDeviceChannels; + MidiPortRouteDescriptor _midiPortChannels; + + RouteCapabilitiesStruct() : _trackChannels(TrackRouteDescriptor()), + _jackChannels(JackRouteDescriptor()), + _midiDeviceChannels(MidiDeviceRouteDescriptor()), + _midiPortChannels(MidiPortRouteDescriptor()) { } + + RouteCapabilitiesStruct(const TrackRouteDescriptor& trackChannels, + const JackRouteDescriptor& jackChannels, + const MidiDeviceRouteDescriptor& midiDeviceChannels, + const MidiPortRouteDescriptor& midiPortChannels) : + _trackChannels(trackChannels), + _jackChannels(jackChannels), + _midiDeviceChannels(midiDeviceChannels), + _midiPortChannels(midiPortChannels) { } +}; //--------------------------------------------------------- // Route //--------------------------------------------------------- -struct Route { - enum { TRACK_ROUTE=0, JACK_ROUTE=1, MIDI_DEVICE_ROUTE=2, MIDI_PORT_ROUTE=3 }; +class Route { + public: + enum RouteType { TRACK_ROUTE=0, JACK_ROUTE=1, MIDI_DEVICE_ROUTE=2, MIDI_PORT_ROUTE=3 }; union { Track* track; MidiDevice* device; void* jackPort; + void* voidPointer; }; int midiPort; // Midi port number. Best not to put this in the union to avoid problems? - //snd_seq_addr_t alsaAdr; + //snd_seq_addr_t alsaAdr; // TODO - // Starting source channel (of the owner of this route). Normally zero for mono or stereo tracks, higher for multi-channel tracks. - // p3.3.50 NOTICE: channel is now a bit-wise channel mask, for MidiPort <-> MidiTrack routes. - // This saves many routes: Instead of one route per channel as before, there can now be only one single route with a channel mask, - // for each MidiPort <-> MidiTrack combination. + // Midi channel, or starting audio source channel (of the owner of this route). + // Normally zero for mono or stereo audio tracks, higher for multi-channel audio tracks. int channel; // Number of (audio) channels being routed. int channels; - - // Allow for multi-channel syntis to feed to/from regular tracks, and to feed one to another. - // If a synti is feeding to/from a regular track, remoteChannel is the 'starting' channel of this multi-channel synti. - // If a synti is feeding to/from another synti, this is not used and individual channels are routed using channel instead. + // Starting (audio) destination channel of the remote object (pointed to by the union or port etc.). int remoteChannel; - unsigned char type; // 0 - track, 1 - jackPort, 2 - midi device, 3 - midi port + RouteType type; + // Always same as the port name. When connection disappears, this holds on to the name. + char persistentJackPortName[ROUTE_PERSISTENT_NAME_SIZE]; + Route(void* t, int ch=-1); Route(Track* t, int ch = -1, int chans = -1); - Route(MidiDevice* d, int ch); - Route(int port, int ch); + Route(MidiDevice* d, int ch = -1); + Route(int port, int ch = -1); Route(const QString&, bool dst, int ch, int rtype = -1); Route(); - - QString name() const; - bool operator==(const Route& a) const; + Route(const Route&); // Copy constructor + // Useful for generic complete construction. + Route(RouteType type_, int midi_port_num_, void* void_pointer_, int channel_, int channels_, int remote_channel_, const char* name_); + + // Returns a suitable icon for the route based on type, track type etc. + // isSource determines whether to return an in-facing icon or an out-facing icon. + // isMidi determines whether to return an audio or midi themed icon. + QPixmap* icon(bool isSource = true, bool isMidi = false) const; + // Create string name representation. + // preferred_name_or_alias (mainly for Jack routes): -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. + QString name(int preferred_name_or_alias = -1) const; + // Fill and return str char name representation. + // preferred_name_or_alias (mainly for Jack routes): -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. + char* name(char* str, int str_size, int preferred_name_or_alias = -1) const; + bool operator==(const Route&) const; + // If the routes support channels, if the given route's channel is -1 meaning all channels, compare matches ONLY if this channel == -1, + // and if the given route's channel is >= 0, compare matches on ANY channel. Useful for example finding router treeview items. + bool compare(const Route&) const; + bool exists() const; + Route& operator=(const Route&); bool isValid() const { - return ((type == TRACK_ROUTE) && (track != 0)) || ((type == JACK_ROUTE) && (jackPort != 0)) || + return ((type == TRACK_ROUTE) && (track != 0)) || + (type == JACK_ROUTE) || // For persistent Jack routes: A NULL jackPort is actually valid. ((type == MIDI_DEVICE_ROUTE) && (device != 0)) || ((type == MIDI_PORT_ROUTE) && (midiPort >= 0) && (midiPort < MIDI_PORTS)); } @@ -93,24 +165,42 @@ // RouteList //--------------------------------------------------------- -struct RouteList : public std::vector { - void removeRoute(const Route& r); +class RouteList : public std::vector { + public: + iterator find(const Route& r) { return std::find(begin(), end(), r); } + const_iterator find(const Route& r) const { return std::find(begin(), end(), r); } + bool contains(const Route& r) const { return std::find(begin(), end(), r) != end(); } + bool removeRoute(const Route& r) { + iterator i = std::find(begin(), end(), r); + if(i == end()) + return false; + erase(i); + return true; + } }; typedef RouteList::iterator iRoute; typedef RouteList::const_iterator ciRoute; -extern void addRoute(Route, Route); -extern void removeRoute(Route, Route); -extern void removeAllRoutes(Route, Route); // p3.3.55 +// Returns true if something changed. +extern bool addRoute(Route src, Route dst); +// Returns true if something changed. +extern bool removeRoute(Route src, Route dst); +extern void removeAllRoutes(Route src, Route dst); extern Route name2route(const QString&, bool dst, int rtype = -1); -extern bool checkRoute(const QString&, const QString&); -//extern bool isCircularRoutePath(Track* src, Track* dst); // Recursive. DELETETHIS +// Returns true if the routes are found and they are connected. +extern bool routeCanDisconnect(const Route& src, const Route& dst); +// Returns true if the routes are found and they are not connected and CAN be connected. +extern bool routeCanConnect(const Route& src, const Route& dst); +// Returns true if the routes are found and they CAN be connected (even if they are already connected). +// If check_types_only is true, it only compares route types. +// Otherwise other parameters such as channels are also compared. +extern bool routesCompatible(const Route& src, const Route& dst, bool check_types_only = false); } // namespace MusECore // Allow Routes to be a QVariant -Q_DECLARE_METATYPE(MusECore::Route) ; +Q_DECLARE_METATYPE(MusECore::Route) #endif diff -Nru muse-2.1.2/muse/seqmsg.cpp muse-3.0.2+ds1/muse/seqmsg.cpp --- muse-2.1.2/muse/seqmsg.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/seqmsg.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -24,6 +24,7 @@ #include #include "song.h" +#include "midiseq.h" #include "midiport.h" #include "minstrument.h" #include "app.h" @@ -41,6 +42,8 @@ #include "driver/jackmidi.h" #include "midi_warn_init_pending_impl.h" #include "gconfig.h" +#include "operations.h" +#include "ctrl.h" namespace MusECore { @@ -69,7 +72,7 @@ } else { // if audio is not running (during initialization) - // process commands immediatly + // process commands immediately processMsg(m); } } @@ -96,52 +99,9 @@ void Audio::msgRemoveRoute(Route src, Route dst) { - msgRemoveRoute1(src, dst); - if (src.type == Route::JACK_ROUTE) - { - if (!MusEGlobal::checkAudioDevice()) return; - - if(dst.type == Route::MIDI_DEVICE_ROUTE) - { - if(dst.device) - { - if(dst.device->deviceType() == MidiDevice::JACK_MIDI) - MusEGlobal::audioDevice->disconnect(src.jackPort, dst.device->inClientPort()); // p3.3.55 - //else DELETETHIS - //{ - // TODO... - //MidiAlsaDevice* amd = dynamic_cast(dst.device); - //if(amd) - //} - } - } - else - MusEGlobal::audioDevice->disconnect(src.jackPort, ((AudioInput*)dst.track)->jackPort(dst.channel)); - } - else if (dst.type == Route::JACK_ROUTE) - { - if (!MusEGlobal::checkAudioDevice()) return; - - //if(src.type == Route::JACK_MIDI_ROUTE) DELETETHIS - if(src.type == Route::MIDI_DEVICE_ROUTE) - { - //MidiJackDevice* jmd = dynamic_cast(src.device); DELETETHIS - //if(jmd) - if(src.device) - { - if(src.device->deviceType() == MidiDevice::JACK_MIDI) - MusEGlobal::audioDevice->disconnect(src.device->outClientPort(), dst.jackPort); // p3.3.55 - //else DELETETHIS - //{ - // TODO... - //MidiAlsaDevice* amd = dynamic_cast(src.device); - //if(amd) - //} - } - } - else - MusEGlobal::audioDevice->disconnect(((AudioOutput*)src.track)->jackPort(src.channel), dst.jackPort); - } + //fprintf(stderr, "Audio::msgRemoveRoute:\n"); + msgRemoveRoute1(src, dst); + MusEGlobal::song->connectJackRoutes(src, dst, true); } //--------------------------------------------------------- @@ -158,126 +118,15 @@ } //--------------------------------------------------------- -// msgRemoveRoutes -//--------------------------------------------------------- - -// p3.3.55 -void Audio::msgRemoveRoutes(Route src, Route dst) -{ - msgRemoveRoutes1(src, dst); - - // TODO or DELETETHIS? looks old. - /* - //if (!MusEGlobal::checkAudioDevice()) return; - if (src.type == Route::JACK_ROUTE) - { - if (!MusEGlobal::checkAudioDevice()) return; - - //if(dst.type == Route::JACK_MIDI_ROUTE) - if(dst.type == Route::MIDI_DEVICE_ROUTE) - { - //MidiJackDevice* jmd = dynamic_cast(dst.device); - //if(jmd) - if(dst.device) - { - if(dst.device->deviceType() == MidiDevice::JACK_MIDI) - //MusEGlobal::audioDevice->disconnect(src.jackPort, dst.device->clientPort()); - MusEGlobal::audioDevice->disconnect(src.jackPort, dst.device->inClientPort()); - //else - //{ - // TODO... - //MidiAlsaDevice* amd = dynamic_cast(dst.device); - //if(amd) - //} - } - } - else - MusEGlobal::audioDevice->disconnect(src.jackPort, ((AudioInput*)dst.track)->jackPort(dst.channel)); - } - else if (dst.type == Route::JACK_ROUTE) - { - if (!MusEGlobal::checkAudioDevice()) return; - - //if(src.type == Route::JACK_MIDI_ROUTE) - if(src.type == Route::MIDI_DEVICE_ROUTE) - { - //MidiJackDevice* jmd = dynamic_cast(src.device); - //if(jmd) - if(src.device) - { - if(src.device->deviceType() == MidiDevice::JACK_MIDI) - //MusEGlobal::audioDevice->disconnect(src.device->clientPort(), dst.jackPort); - MusEGlobal::audioDevice->disconnect(src.device->outClientPort(), dst.jackPort); - //else - //{ - // TODO... - //MidiAlsaDevice* amd = dynamic_cast(src.device); - //if(amd) - //} - } - } - else - MusEGlobal::audioDevice->disconnect(((AudioOutput*)src.track)->jackPort(src.channel), dst.jackPort); - } - - */ -} - -//--------------------------------------------------------- -// msgRemoveRoutes1 -//--------------------------------------------------------- - -void Audio::msgRemoveRoutes1(Route src, Route dst) - { - AudioMsg msg; - msg.id = AUDIO_REMOVEROUTES; - msg.sroute = src; - msg.droute = dst; - sendMsg(&msg); - } - -//--------------------------------------------------------- // msgAddRoute //--------------------------------------------------------- void Audio::msgAddRoute(Route src, Route dst) - { - if (src.type == Route::JACK_ROUTE) - { - if (!MusEGlobal::checkAudioDevice()) return; - if (isRunning()) - { - if(dst.type == Route::MIDI_DEVICE_ROUTE) - { - if(dst.device) - { - if(dst.device->deviceType() == MidiDevice::JACK_MIDI) - MusEGlobal::audioDevice->connect(src.jackPort, dst.device->inClientPort()); - } - } - else - MusEGlobal::audioDevice->connect(src.jackPort, ((AudioInput*)dst.track)->jackPort(dst.channel)); - } - } - else if (dst.type == Route::JACK_ROUTE) - { - if (!MusEGlobal::checkAudioDevice()) return; - if (MusEGlobal::audio->isRunning()) - { - if(src.type == Route::MIDI_DEVICE_ROUTE) - { - if(src.device) - { - if(src.device->deviceType() == MidiDevice::JACK_MIDI) - MusEGlobal::audioDevice->connect(src.device->outClientPort(), dst.jackPort); - } - } - else - MusEGlobal::audioDevice->connect(((AudioOutput*)src.track)->jackPort(dst.channel), dst.jackPort); - } - } - msgAddRoute1(src, dst); - } +{ + //fprintf(stderr, "Audio::msgAddRoute:\n"); + msgAddRoute1(src, dst); + MusEGlobal::song->connectJackRoutes(src, dst); +} //--------------------------------------------------------- // msgAddRoute1 @@ -307,51 +156,6 @@ } //--------------------------------------------------------- -// msgSetRecord -//--------------------------------------------------------- - -void Audio::msgSetRecord(AudioTrack* node, bool val) - { - AudioMsg msg; - msg.id = AUDIO_RECORD; - msg.snode = node; - msg.ival = int(val); - sendMsg(&msg); - } - -/* DELETETHIS 34 -//--------------------------------------------------------- -// msgSetVolume -//--------------------------------------------------------- - -void Audio::msgSetVolume(AudioTrack* src, double val) - { - AudioMsg msg; - msg.id = AUDIO_VOL; - msg.snode = src; - msg.dval = val; - sendMsg(&msg); - //muse->arranger->controllerChanged(src); - MusEGlobal::song->controllerChange(src); - } - -//--------------------------------------------------------- -// msgSetPan -//--------------------------------------------------------- - -void Audio::msgSetPan(AudioTrack* node, double val) - { - AudioMsg msg; - msg.id = AUDIO_PAN; - msg.snode = node; - msg.dval = val; - sendMsg(&msg); - //muse->arranger->controllerChanged(node); - MusEGlobal::song->controllerChange(node); - } -*/ - -//--------------------------------------------------------- // msgSetPrefader //--------------------------------------------------------- @@ -492,26 +296,6 @@ sendMsg(&msg); } -/* DELETETHIS 20 -//--------------------------------------------------------- -// msgSetPluginCtrlVal -//--------------------------------------------------------- - -void Audio::msgSetPluginCtrlVal(AudioTrack* track, int param, double val) -{ - AudioMsg msg; - - msg.id = AUDIO_SET_PLUGIN_CTRL_VAL; - msg.ival = param; - msg.dval = val; - //msg.plugin = plugin; - msg.snode = track; - sendMsg(&msg); - //muse->arranger->controllerChanged(track); - MusEGlobal::song->controllerChange(track); -} -*/ - //--------------------------------------------------------- // msgSwapControllerIDX //--------------------------------------------------------- @@ -533,12 +317,35 @@ void Audio::msgClearControllerEvents(AudioTrack* node, int acid) { - AudioMsg msg; - - msg.id = AUDIO_CLEAR_CONTROLLER_EVENTS; - msg.snode = node; - msg.ival = acid; - sendMsg(&msg); +// AudioMsg msg; +// +// msg.id = AUDIO_CLEAR_CONTROLLER_EVENTS; +// msg.snode = node; +// msg.ival = acid; +// sendMsg(&msg); + + + ciCtrlList icl = node->controller()->find(acid); + if(icl == node->controller()->end()) + return; + + CtrlList* cl = icl->second; + if(cl->empty()) + return; + + CtrlList& clr = *icl->second; + + // The Undo system will take 'ownership' of these and delete them at the appropriate time. + CtrlList* erased_list_items = new CtrlList(clr, CtrlList::ASSIGN_PROPERTIES); + erased_list_items->insert(cl->begin(), cl->end()); + + if(erased_list_items->empty()) + { + delete erased_list_items; + return; + } + + MusEGlobal::song->applyOperation(UndoOp(UndoOp::ModifyAudioCtrlValList, node->controller(), erased_list_items, 0)); } //--------------------------------------------------------- @@ -575,13 +382,7 @@ void Audio::msgEraseACEvent(AudioTrack* node, int acid, int frame) { - AudioMsg msg; - - msg.id = AUDIO_ERASE_AC_EVENT; - msg.snode = node; - msg.ival = acid; - msg.a = frame; - sendMsg(&msg); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteAudioCtrlVal, node, acid, frame)); } //--------------------------------------------------------- @@ -590,14 +391,41 @@ void Audio::msgEraseRangeACEvents(AudioTrack* node, int acid, int frame1, int frame2) { - AudioMsg msg; - - msg.id = AUDIO_ERASE_RANGE_AC_EVENTS; - msg.snode = node; - msg.ival = acid; - msg.a = frame1; - msg.b = frame2; - sendMsg(&msg); + ciCtrlList icl = node->controller()->find(acid); + if(icl == node->controller()->end()) + return; + + CtrlList* cl = icl->second; + if(cl->empty()) + return; + + if(frame2 < frame1) + { + const int tmp = frame1; + frame1 = frame2; + frame2 = tmp; + } + + iCtrl s = cl->lower_bound(frame1); + iCtrl e = cl->lower_bound(frame2); + + // No elements to erase? + if(s == cl->end()) + return; + + CtrlList& clr = *icl->second; + + // The Undo system will take 'ownership' of these and delete them at the appropriate time. + CtrlList* erased_list_items = new CtrlList(clr, CtrlList::ASSIGN_PROPERTIES); + erased_list_items->insert(s, e); + + if(erased_list_items->empty()) + { + delete erased_list_items; + return; + } + + MusEGlobal::song->applyOperation(UndoOp(UndoOp::ModifyAudioCtrlValList, node->controller(), erased_list_items, 0)); } //--------------------------------------------------------- @@ -606,14 +434,7 @@ void Audio::msgAddACEvent(AudioTrack* node, int acid, int frame, double val) { - AudioMsg msg; - - msg.id = AUDIO_ADD_AC_EVENT; - msg.snode = node; - msg.ival = acid; - msg.a = frame; - msg.dval = val; - sendMsg(&msg); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddAudioCtrlVal, node, acid, frame, val)); } //--------------------------------------------------------- @@ -634,32 +455,6 @@ } //--------------------------------------------------------- -// msgSetSolo -//--------------------------------------------------------- - -void Audio::msgSetSolo(Track* track, bool val) -{ - AudioMsg msg; - msg.id = AUDIO_SET_SOLO; - msg.track = track; - msg.ival = int(val); - sendMsg(&msg); -} - -//--------------------------------------------------------- -// msgSetSegSize -//--------------------------------------------------------- - -void Audio::msgSetSegSize(int bs, int sr) - { - AudioMsg msg; - msg.id = AUDIO_SET_SEG_SIZE; - msg.ival = bs; - msg.iival = sr; - sendMsg(&msg); - } - -//--------------------------------------------------------- // msgSeek //--------------------------------------------------------- @@ -670,26 +465,59 @@ } //--------------------------------------------------------- -// msgUndo +// msgExecuteOperationGroup //--------------------------------------------------------- -void Audio::msgUndo() - { - AudioMsg msg; - msg.id = SEQM_UNDO; - sendMsg(&msg); - } +void Audio::msgExecuteOperationGroup(Undo& operations) +{ + MusEGlobal::song->executeOperationGroup1(operations); + + AudioMsg msg; + msg.id = SEQM_EXECUTE_OPERATION_GROUP; + msg.operations=&operations; + sendMsg(&msg); + + MusEGlobal::song->executeOperationGroup3(operations); +} //--------------------------------------------------------- -// msgRedo +// msgRevertOperationGroup //--------------------------------------------------------- -void Audio::msgRedo() - { - AudioMsg msg; - msg.id = SEQM_REDO; - sendMsg(&msg); - } +void Audio::msgRevertOperationGroup(Undo& operations) +{ + MusEGlobal::song->revertOperationGroup1(operations); + + + AudioMsg msg; + msg.id = SEQM_REVERT_OPERATION_GROUP; + msg.operations=&operations; + sendMsg(&msg); + + MusEGlobal::song->revertOperationGroup3(operations); +} + +//--------------------------------------------------------- +// msgExecutePendingOperations +// Bypass the Undo system and directly execute the pending operations. +//--------------------------------------------------------- + +void Audio::msgExecutePendingOperations(PendingOperationList& operations, bool doUpdate, SongChangedFlags_t extraFlags) +{ + if(operations.empty()) + return; + AudioMsg msg; + msg.id = SEQM_EXECUTE_PENDING_OPERATIONS; + msg.pendingOps=&operations; + sendMsg(&msg); + operations.executeNonRTStage(); + const SongChangedFlags_t flags = operations.flags() | extraFlags; + if(doUpdate && flags != 0) + { + MusEGlobal::song->update(flags); + MusEGlobal::song->setDirty(); + } +} //--------------------------------------------------------- // msgPlay @@ -714,56 +542,35 @@ } } -/* DELETETHIS 31 -//--------------------------------------------------------- -// msgShowInstrumentGui -//--------------------------------------------------------- - -void Audio::msgShowInstrumentGui(MidiInstrument* instr, bool val) - { - instr->showGui(val); - // No need for this - it called msgUpdatePollFd which has nothing - // to do with showing a gui. - //AudioMsg msg; - //msg.id = MIDI_SHOW_INSTR_GUI; - //msg.p1 = instr; - //msg.a = val; - //sendMessage(&msg, false); - } //--------------------------------------------------------- -// msgShowInstrumentNativeGui +// msgExternalPlay //--------------------------------------------------------- -void Audio::msgShowInstrumentNativeGui(MidiInstrument* instr, bool val) +void Audio::msgExternalPlay(bool val, bool doRewind) { - instr->showNativeGui(val); - //AudioMsg msg; - //msg.id = MIDI_SHOW_INSTR_NATIVE_GUI; - //msg.p1 = instr; - //msg.a = val; - //sendMessage(&msg, false); + if (val) { + // Force the state to play immediately. + state = PLAY; + if (MusEGlobal::audioDevice) + { + //unsigned sfr = MusEGlobal::song->cPos().frame(); + //unsigned dcfr = MusEGlobal::audioDevice->getCurFrame(); + //if(dcfr != sfr) + if(doRewind) + MusEGlobal::audioDevice->seekTransport(0); + MusEGlobal::audioDevice->startTransport(); + } + + }else { + state = STOP; + if (MusEGlobal::audioDevice) + MusEGlobal::audioDevice->stopTransport(); + _bounce = false; + } } -*/ -//--------------------------------------------------------- -// msgAddTrack -//--------------------------------------------------------- -void Song::msgInsertTrack(Track* track, int idx, bool doUndoFlag) - { - AudioMsg msg; - msg.id = SEQM_ADD_TRACK; - msg.track = track; - msg.ival = idx; - if (doUndoFlag) { - MusEGlobal::song->startUndo(); - addUndo(UndoOp(UndoOp::AddTrack, idx, track)); - } - MusEGlobal::audio->sendMsg(&msg); - if (doUndoFlag) - endUndo(SC_TRACK_INSERTED); - } //--------------------------------------------------------- // msgRemoveTrack @@ -771,10 +578,7 @@ void Audio::msgRemoveTrack(Track* track, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_REMOVE_TRACK; - msg.track = track; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(track), track), doUndoFlag); } //--------------------------------------------------------- @@ -784,73 +588,23 @@ void Audio::msgRemoveTracks() { - bool loop; - do - { - loop = false; - TrackList* tl = MusEGlobal::song->tracks(); - for (iTrack t = tl->begin(); t != tl->end(); ++t) - { - Track* tr = *t; - if (tr->selected()) - { - MusEGlobal::song->removeTrack1(tr); - msgRemoveTrack(tr, false); - MusEGlobal::song->removeTrack3(tr); - loop = true; - break; - } - } - } - while (loop); - - /* DELETETHIS 28 - // TESTED: DIDN'T WORK: It still skipped some selected tracks ! - // Quote from SGI STL: "Erasing an element from a map also does not invalidate any iterators, - // except, of course, for iterators that actually point to the element - // that is being erased." - // Well that doesn't seem true here... - - TrackList* tl = MusEGlobal::song->tracks(); - for(ciTrack t = tl->begin(); t != tl->end() ; ) - { - if((*t)->selected()) - { - // Changed 20070102: - Iterator t becomes invalid after msgRemoveTrack. - ciTrack tt = t; - ++t; - Track* tr = *tt; - - MusEGlobal::song->removeTrack1(tr); - msgRemoveTrack(tr, false); - MusEGlobal::song->removeTrack3(tr); - - } - else - ++t; - - } - */ - -} - -/* DELETETHIS 18 -//--------------------------------------------------------- -// msgChangeTrack -// oldTrack - copy of the original track befor modification -// newTrack - modified original track -//--------------------------------------------------------- + Undo operations; + TrackList* tl = MusEGlobal::song->tracks(); -void Audio::msgChangeTrack(Track* oldTrack, Track* newTrack, bool doUndoFlag) - { - AudioMsg msg; - msg.id = SEQM_CHANGE_TRACK; - msg.p1 = oldTrack; - msg.p2 = newTrack; - sendMessage(&msg, doUndoFlag); + // NOTICE: This must be done in reverse order so that + // 'undo' will repopulate in ascending index order! + ciTrack it = tl->end(); + while(it != tl->begin()) + { + --it; + Track* tr = *it; + if(tr->selected()) + operations.push_back(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(tr), tr)); } -*/ - + + MusEGlobal::song->applyOperationGroup(operations); +} + //--------------------------------------------------------- // msgMoveTrack // move track idx1 to slot idx2 @@ -863,11 +617,7 @@ int n = MusEGlobal::song->tracks()->size(); if (idx1 >= n || idx2 >= n) // sanity check return; - AudioMsg msg; - msg.id = SEQM_MOVE_TRACK; - msg.a = idx1; - msg.b = idx2; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::MoveTrack, idx1, idx2), doUndoFlag); } //--------------------------------------------------------- @@ -876,10 +626,7 @@ void Audio::msgAddPart(Part* part, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_ADD_PART; - msg.p1 = part; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddPart, part), doUndoFlag); } //--------------------------------------------------------- @@ -888,54 +635,9 @@ void Audio::msgRemovePart(Part* part, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_REMOVE_PART; - msg.p1 = part; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeletePart, part), doUndoFlag); } -//--------------------------------------------------------- -// msgRemoveParts -// remove selected parts; return true if any part was -// removed -//--------------------------------------------------------- - -bool Song::msgRemoveParts() - { - Undo operations; - bool partSelected = false; - - TrackList* tl = MusEGlobal::song->tracks(); - - for (iTrack it = tl->begin(); it != tl->end(); ++it) { - PartList* pl = (*it)->parts(); - for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { - if (ip->second->selected()) { - operations.push_back(UndoOp(UndoOp::DeletePart,ip->second)); - partSelected = true; - } - } - } - - MusEGlobal::song->applyOperationGroup(operations); - - return partSelected; - } - -//--------------------------------------------------------- -// msgChangePart -//--------------------------------------------------------- - -void Audio::msgChangePart(Part* oldPart, Part* newPart, bool doUndoFlag, bool doCtrls, bool doClones) - { - AudioMsg msg; - msg.id = SEQM_CHANGE_PART; - msg.p1 = oldPart; - msg.p2 = newPart; - msg.a = doCtrls; - msg.b = doClones; - sendMessage(&msg, doUndoFlag); - } //--------------------------------------------------------- // msgAddEvent @@ -943,13 +645,7 @@ void Audio::msgAddEvent(Event& event, Part* part, bool doUndoFlag, bool doCtrls, bool doClones) { - AudioMsg msg; - msg.id = SEQM_ADD_EVENT; - msg.ev1 = event; - msg.p2 = part; - msg.a = doCtrls; - msg.b = doClones; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddEvent, event,part, doCtrls, doClones), doUndoFlag); } //--------------------------------------------------------- @@ -958,13 +654,7 @@ void Audio::msgDeleteEvent(Event& event, Part* part, bool doUndoFlag, bool doCtrls, bool doClones) { - AudioMsg msg; - msg.id = SEQM_REMOVE_EVENT; - msg.ev1 = event; - msg.p2 = part; - msg.a = doCtrls; - msg.b = doClones; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent, event,part, doCtrls, doClones), doUndoFlag); } //--------------------------------------------------------- @@ -973,14 +663,7 @@ void Audio::msgChangeEvent(Event& oe, Event& ne, Part* part, bool doUndoFlag, bool doCtrls, bool doClones) { - AudioMsg msg; - msg.id = SEQM_CHANGE_EVENT; - msg.ev1 = oe; - msg.ev2 = ne; - msg.p3 = part; - msg.a = doCtrls; - msg.b = doClones; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::ModifyEvent, ne,oe, part, doCtrls, doClones), doUndoFlag); } //--------------------------------------------------------- @@ -989,11 +672,7 @@ void Audio::msgAddTempo(int tick, int tempo, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_ADD_TEMPO; - msg.a = tick; - msg.b = tempo; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddTempo, tick, tempo), doUndoFlag); } //--------------------------------------------------------- @@ -1002,23 +681,16 @@ void Audio::msgSetTempo(int tick, int tempo, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_SET_TEMPO; - msg.a = tick; - msg.b = tempo; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddTempo, tick, tempo), doUndoFlag); } //--------------------------------------------------------- // msgSetGlobalTempo //--------------------------------------------------------- -void Audio::msgSetGlobalTempo(int val) +void Audio::msgSetGlobalTempo(int val, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_SET_GLOBAL_TEMPO; - msg.a = val; - sendMessage(&msg, false); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::SetGlobalTempo, val, 0), doUndoFlag); } //--------------------------------------------------------- @@ -1027,11 +699,7 @@ void Audio::msgDeleteTempo(int tick, int tempo, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_REMOVE_TEMPO; - msg.a = tick; - msg.b = tempo; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteTempo, tick, tempo), doUndoFlag); } //--------------------------------------------------------- @@ -1040,12 +708,7 @@ void Audio::msgAddSig(int tick, int z, int n, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_ADD_SIG; - msg.a = tick; - msg.b = z; - msg.c = n; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddSig, tick, z, n), doUndoFlag); } //--------------------------------------------------------- @@ -1055,12 +718,7 @@ void Audio::msgRemoveSig(int tick, int z, int n, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_REMOVE_SIG; - msg.a = tick; - msg.b = z; - msg.c = n; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteSig, tick, z, n), doUndoFlag); } //--------------------------------------------------------- @@ -1069,11 +727,7 @@ void Audio::msgAddKey(int tick, int key, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_ADD_KEY; - msg.a = tick; - msg.b = key; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddKey, tick, key), doUndoFlag); } //--------------------------------------------------------- @@ -1083,23 +737,9 @@ void Audio::msgRemoveKey(int tick, int key, bool doUndoFlag) { - AudioMsg msg; - msg.id = SEQM_REMOVE_KEY; - msg.a = tick; - msg.b = key; - sendMessage(&msg, doUndoFlag); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteKey, tick, key), doUndoFlag); } -//--------------------------------------------------------- -// msgScanAlsaMidiPorts -//--------------------------------------------------------- - -void Audio::msgScanAlsaMidiPorts() - { - AudioMsg msg; - msg.id = SEQM_SCAN_ALSA_MIDI_PORTS; - sendMessage(&msg, false); - } //--------------------------------------------------------- // msgResetMidiDevices @@ -1130,7 +770,7 @@ MidiPort* mp = &MusEGlobal::midiPorts[MusEGlobal::clickPort]; if(mp->device() && (mp->device()->openFlags() & 1) && - mp->instrument() && !mp->instrument()->midiInit()->empty() && + mp->instrument() && !mp->instrument()->midiInit()->empty() && !mp->initSent()) found = true; } @@ -1141,7 +781,7 @@ { MidiPort* mp = &MusEGlobal::midiPorts[i]; if(mp->device() && (mp->device()->openFlags() & 1) && - mp->instrument() && !mp->instrument()->midiInit()->empty() && + mp->instrument() && !mp->instrument()->midiInit()->empty() && !mp->initSent()) { found = true; @@ -1158,6 +798,7 @@ if(warn != MusEGlobal::config.warnInitPending) { MusEGlobal::config.warnInitPending = warn; + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //MusEGlobal::muse->changeConfig(true); // Save settings? No, wait till close. } if(rv != QDialog::Accepted) @@ -1275,7 +916,7 @@ void Audio::msgSetHwCtrlStates(MidiPort* port, int ch, int ctrl, int val, int lastval) { AudioMsg msg; - msg.id = SEQM_SET_HW_CTRL_STATE; + msg.id = SEQM_SET_HW_CTRL_STATES; msg.p1 = port; msg.a = ch; msg.b = ctrl; @@ -1285,32 +926,6 @@ } //--------------------------------------------------------- -// msgSetTrackOutChannel -//--------------------------------------------------------- - -void Audio::msgSetTrackOutChannel(MidiTrack* track, int ch) -{ - AudioMsg msg; - msg.id = SEQM_SET_TRACK_OUT_CHAN; - msg.p1 = track; - msg.a = ch; - sendMessage(&msg, false); -} - -//--------------------------------------------------------- -// msgSetTrackOutPort -//--------------------------------------------------------- - -void Audio::msgSetTrackOutPort(MidiTrack* track, int port) -{ - AudioMsg msg; - msg.id = SEQM_SET_TRACK_OUT_PORT; - msg.p1 = track; - msg.a = port; - sendMessage(&msg, false); -} - -//--------------------------------------------------------- // msgSetTrackAutomationType //--------------------------------------------------------- @@ -1324,34 +939,6 @@ } //--------------------------------------------------------- -// msgRemapPortDrumCtlEvents -//--------------------------------------------------------- - -void Audio::msgRemapPortDrumCtlEvents(int mapidx, int newnote, int newchan, int newport) -{ - AudioMsg msg; - msg.id = SEQM_REMAP_PORT_DRUM_CTL_EVS; - msg.ival = mapidx; - msg.a = newnote; - msg.b = newchan; - msg.c = newport; - sendMessage(&msg, false); -} - -//--------------------------------------------------------- -// msgChangeAllPortDrumCtlEvents -//--------------------------------------------------------- - -void Audio::msgChangeAllPortDrumCtrlEvents(bool add, bool drumonly) -{ - AudioMsg msg; - msg.id = SEQM_CHANGE_ALL_PORT_DRUM_CTL_EVS; - msg.a = (int)add; - msg.b = (int)drumonly; - sendMessage(&msg, false); -} - -//--------------------------------------------------------- // msgSetSendMetronome //--------------------------------------------------------- @@ -1400,4 +987,38 @@ sendMessage(&msg, false); } +//--------------------------------------------------------- +// msgAudioWait +//--------------------------------------------------------- + +void Audio::msgAudioWait() + { + AudioMsg msg; + msg.id = AUDIO_WAIT; + sendMsg(&msg); + } + +//--------------------------------------------------------- +// msgSetMidiDevice +// to avoid timeouts in the RT-thread, setMidiDevice +// is done in GUI context after setting the audio and midi threads +// into idle mode +//--------------------------------------------------------- + +void Audio::msgSetMidiDevice(MidiPort* port, MidiDevice* device) +{ + MusECore::AudioMsg msg; + msg.id = MusECore::SEQM_IDLE; + msg.a = true; + //MusEGlobal::midiSeq->sendMsg(&msg); + sendMsg(&msg); // Idle both audio and midi. + + port->setMidiDevice(device); + + msg.id = MusECore::SEQM_IDLE; + msg.a = false; + //MusEGlobal::midiSeq->sendMsg(&msg); + sendMsg(&msg); // Idle both audio and midi. +} + } // namespace MusECore diff -Nru muse-2.1.2/muse/shortcuts.cpp muse-3.0.2+ds1/muse/shortcuts.cpp --- muse-2.1.2/muse/shortcuts.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/shortcuts.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -55,6 +55,8 @@ defShrt(SHRT_STOP, Qt::Key_Insert,QT_TRANSLATE_NOOP("shortcuts", "Transport: Stop Playback"), GLOBAL_SHRT, "stop"); defShrt(SHRT_GOTO_START, Qt::Key_W, QT_TRANSLATE_NOOP("shortcuts", "Transport: Goto Start"), GLOBAL_SHRT, "goto_start"); defShrt(SHRT_PLAY_TOGGLE, Qt::Key_Space, QT_TRANSLATE_NOOP("shortcuts", "Transport: Play, Stop, Rewind"), GLOBAL_SHRT, "play_toggle"); + defShrt(SHRT_REC_RESTART, Qt::CTRL + Qt::Key_Space, QT_TRANSLATE_NOOP("shortcuts", "Transport: Restart recording"), GLOBAL_SHRT, "rec_restart"); + defShrt(SHRT_REC_RESTART_MULTI, Qt::SHIFT + Qt::Key_Space, QT_TRANSLATE_NOOP("shortcuts", "Transport: Restart recording (multi take)"), GLOBAL_SHRT, "rec_restart_multi"); defShrt(SHRT_GOTO_LEFT, Qt::Key_End, QT_TRANSLATE_NOOP("shortcuts", "Transport: Goto left marker") , GLOBAL_SHRT, "goto_left"); defShrt(SHRT_GOTO_RIGHT, Qt::Key_PageDown, QT_TRANSLATE_NOOP("shortcuts", "Transport: Goto right marker") , GLOBAL_SHRT, "goto_right"); defShrt(SHRT_TOGGLE_LOOP, Qt::Key_Slash, QT_TRANSLATE_NOOP("shortcuts", "Transport: Toggle Loop section"), GLOBAL_SHRT, "toggle_loop"); @@ -90,7 +92,7 @@ defShrt(SHRT_EXPORT_MIDI, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Export midi file"), ARRANG_SHRT, "export_midi"); defShrt(SHRT_IMPORT_PART, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Import midi part"), ARRANG_SHRT, "import_part"); defShrt(SHRT_IMPORT_AUDIO, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Import audio file"), ARRANG_SHRT, "import_audio"); - defShrt(SHRT_QUIT, Qt::CTRL + Qt::Key_Q, QT_TRANSLATE_NOOP("shortcuts", "File: Quit MusE"), ARRANG_SHRT, "quit"); + defShrt(SHRT_QUIT, 0, QT_TRANSLATE_NOOP("shortcuts", "File: Quit MusE"), ARRANG_SHRT, "quit"); // defShrt(SHRT_DESEL_PARTS, Qt::CTRL + Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Deselect all parts"), ARRANG_SHRT, "deselect_parts"); defShrt(SHRT_SELECT_PRTSTRACK, Qt::CTRL+ Qt::ALT + Qt::Key_P, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select parts on track"), ARRANG_SHRT, "select_parts_on_track"); defShrt(SHRT_OPEN_PIANO, Qt::CTRL + Qt::Key_E, QT_TRANSLATE_NOOP("shortcuts", "Open pianoroll"), ARRANG_SHRT, "open_pianoroll"); @@ -103,7 +105,7 @@ defShrt(SHRT_ADD_MIDI_TRACK, Qt::CTRL + Qt::Key_J, QT_TRANSLATE_NOOP("shortcuts", "Add midi track"), ARRANG_SHRT, "add_midi_track"); defShrt(SHRT_ADD_DRUM_TRACK, 0, QT_TRANSLATE_NOOP("shortcuts", "Add drum track"), ARRANG_SHRT, "add_drum_track"); defShrt(SHRT_ADD_NEW_STYLE_DRUM_TRACK, 0, QT_TRANSLATE_NOOP("shortcuts", "Add new style drum track"), ARRANG_SHRT, "add_new_style_drum_track"); - defShrt(SHRT_ADD_WAVE_TRACK, 0, QT_TRANSLATE_NOOP("shortcuts", "Add wave track"), ARRANG_SHRT, "add_wave_track"); + defShrt(SHRT_ADD_WAVE_TRACK, Qt::CTRL + Qt::Key_K, QT_TRANSLATE_NOOP("shortcuts", "Add wave track"), ARRANG_SHRT, "add_wave_track"); defShrt(SHRT_ADD_AUDIO_OUTPUT, 0, QT_TRANSLATE_NOOP("shortcuts", "Add audio output"), ARRANG_SHRT, "add_audio_output"); defShrt(SHRT_ADD_AUDIO_GROUP, 0, QT_TRANSLATE_NOOP("shortcuts", "Add audio group"), ARRANG_SHRT, "add_audio_group"); defShrt(SHRT_ADD_AUDIO_INPUT, 0, QT_TRANSLATE_NOOP("shortcuts", "Add audio input"), ARRANG_SHRT, "add_audio_input"); @@ -163,10 +165,15 @@ defShrt(SHRT_SEL_BELOW, Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select nearest part on track below"), ARRANG_SHRT, "sel_part_below"); defShrt(SHRT_SEL_BELOW_ADD, Qt::SHIFT + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Edit: Add nearest part on track below"), ARRANG_SHRT, "sel_part_below_add"); - defShrt(SHRT_INSERTMEAS, Qt::CTRL+Qt::SHIFT+ Qt::Key_O, QT_TRANSLATE_NOOP("shortcuts", "Edit: Insert empty measure"), ARRANG_SHRT, "insert_measure"); + defShrt(SHRT_INSERTMEAS, Qt::CTRL + Qt::SHIFT + Qt::Key_O, QT_TRANSLATE_NOOP("shortcuts", "Edit: Insert empty measure"), ARRANG_SHRT, "insert_measure"); + defShrt(SHRT_DUPLICATE_TRACK, Qt::CTRL + Qt::SHIFT + Qt::Key_D, QT_TRANSLATE_NOOP("shortcuts", "Edit: Duplicate track"), ARRANG_SHRT, "duplicate_track"); defShrt(SHRT_SEL_TRACK_ABOVE, Qt::CTRL + Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Select track above"), ARRANG_SHRT, "sel_track_above"); defShrt(SHRT_SEL_TRACK_BELOW, Qt::CTRL + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Select track below"), ARRANG_SHRT, "sel_track_below"); + defShrt(SHRT_EDIT_TRACK_NAME, Qt::Key_F2, QT_TRANSLATE_NOOP("shortcuts", "Edit selected track name"), ARRANG_SHRT, "edit_track_name"); + defShrt(SHRT_MUTE_CURRENT_TRACKS, Qt::SHIFT + Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Mute all currently selected tracks"), ARRANG_SHRT, "mute_curren_tracks"); + defShrt(SHRT_SOLO_CURRENT_TRACKS, Qt::SHIFT + Qt::Key_S, QT_TRANSLATE_NOOP("shortcuts", "Solo all currently selected tracks"), ARRANG_SHRT, "solo_curren_tracks"); + //----------------------------------------------------------- @@ -179,8 +186,8 @@ defShrt(SHRT_SELECT_INVERT, Qt::CTRL + Qt::Key_I, QT_TRANSLATE_NOOP("shortcuts", "Edit: Invert Selection"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "sel_inv"); defShrt(SHRT_SELECT_ILOOP, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select events/parts inside locators"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "sel_ins_loc"); defShrt(SHRT_SELECT_OLOOP, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select events/parts outside locators"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "sel_out_loc"); - defShrt(SHRT_SELECT_PREV_PART, Qt::ALT + Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select previous part"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_prv_prt"); - defShrt(SHRT_SELECT_NEXT_PART, Qt::ALT + Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select next part"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_nxt_prt"); + defShrt(SHRT_SELECT_PREV_PART, Qt::ALT + Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select previous part"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_prv_prt"); + defShrt(SHRT_SELECT_NEXT_PART, Qt::ALT + Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select next part"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_nxt_prt"); defShrt(SHRT_SEL_LEFT, Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select nearest part/event to the left or move cursor"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_left"); defShrt(SHRT_SEL_LEFT_ADD, Qt::Key_Left + Qt::SHIFT, QT_TRANSLATE_NOOP("shortcuts", "Edit: Add nearest part/event to the left to selection"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_left_add"); defShrt(SHRT_SEL_RIGHT, Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select nearest part/event to the right or move cursor"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT,"sel_right"); @@ -192,10 +199,16 @@ defShrt(SHRT_DEC_POS, Qt::CTRL + Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Edit: Decrease event position"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_dec_pos"); defShrt(SHRT_ZOOM_IN, Qt::CTRL + Qt::Key_PageUp, QT_TRANSLATE_NOOP("shortcuts", "View: Zoom in"), PROLL_SHRT + DEDIT_SHRT + ARRANG_SHRT + WAVE_SHRT, "zoom_in"); defShrt(SHRT_ZOOM_OUT, Qt::CTRL + Qt::Key_PageDown, QT_TRANSLATE_NOOP("shortcuts", "View: Zoom out"), PROLL_SHRT + DEDIT_SHRT + ARRANG_SHRT + WAVE_SHRT, "zoom_out"); - defShrt(SHRT_GOTO_CPOS, Qt::Key_C, QT_TRANSLATE_NOOP("shortcuts", "View: Goto Current Position"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "goto_cpos"); + defShrt(SHRT_GOTO_CPOS, Qt::Key_I, QT_TRANSLATE_NOOP("shortcuts", "View: Goto Current Position"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "goto_cpos"); defShrt(SHRT_SCROLL_LEFT, Qt::Key_H, QT_TRANSLATE_NOOP("shortcuts", "View: Scroll left"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "scroll_left"); defShrt(SHRT_SCROLL_RIGHT, Qt::Key_L, QT_TRANSLATE_NOOP("shortcuts", "View: Scroll left"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "scroll_right"); + defShrt(SHRT_STEP_RECORD, Qt::CTRL + Qt::Key_R, QT_TRANSLATE_NOOP("shortcuts", "Transport: Step record"), PROLL_SHRT + DEDIT_SHRT, "step_record"); + defShrt(SHRT_MIDI_INPUT, Qt::CTRL + Qt::Key_U, QT_TRANSLATE_NOOP("shortcuts", "Transport: Midi input"), PROLL_SHRT + DEDIT_SHRT, "midi_input"); + defShrt(SHRT_PLAY_EVENTS, Qt::CTRL + Qt::Key_L, QT_TRANSLATE_NOOP("shortcuts", "Transport: Play events"), PROLL_SHRT + DEDIT_SHRT, "play_events"); + defShrt(SHRT_INC_VELOCITY, Qt::ALT + Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Edit: Increase velocity"), PROLL_SHRT + DEDIT_SHRT, "increase_velocity"); + defShrt(SHRT_DEC_VELOCITY, Qt::ALT + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Edit: Decrease velocity"), PROLL_SHRT + DEDIT_SHRT, "decrease_velocity"); + //----------------------------------------------------------- //Drum: //----------------------------------------------------------- @@ -220,6 +233,7 @@ defShrt(SHRT_DELETE_MEASURE, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Delete Measure"), PROLL_SHRT + DEDIT_SHRT, "midi_delete_measure"); defShrt(SHRT_CREATE_MEASURE, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Create Measure"), PROLL_SHRT + DEDIT_SHRT, "midi_create_measure"); defShrt(SHRT_EVENT_COLOR, Qt::Key_E, QT_TRANSLATE_NOOP("shortcuts", "Edit: Change Event Color"), PROLL_SHRT + SCORE_SHRT, "change_event_color"); + defShrt(SHRT_MOVE_PLAY_TO_NOTE, Qt::CTRL + Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Move: Move to selected note"), PROLL_SHRT + DEDIT_SHRT, "move_to_note"); // Shortcuts for tools @@ -250,7 +264,7 @@ // arranger defShrt(SHRT_TOOL_SCISSORS, Qt::Key_S, QT_TRANSLATE_NOOP("shortcuts", "Tool: Scissor"), ARRANG_SHRT, "scissor_tool"); defShrt(SHRT_TOOL_GLUE, Qt::Key_G, QT_TRANSLATE_NOOP("shortcuts", "Tool: Glue"), ARRANG_SHRT, "glue_tool"); - defShrt(SHRT_TOOL_MUTE, 0, QT_TRANSLATE_NOOP("shortcuts", "Tool: Mute"), ARRANG_SHRT, "mute_tool"); + defShrt(SHRT_TOOL_MUTE, Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Tool: Mute"), ARRANG_SHRT, "mute_tool"); //Increase/decrease current position, is going to be in arranger & drumeditor as well // p4.0.10 Editors and arranger handle these by themselves, otherwise global handler will now use them, too. @@ -331,6 +345,8 @@ // because they are handled there, and affect the whole app. defShrt(SHRT_NEXT_MARKER, Qt::Key_F6, QT_TRANSLATE_NOOP("shortcuts", "Goto Next Marker"), GLOBAL_SHRT, "me_sel_next"); defShrt(SHRT_PREV_MARKER, Qt::Key_F5, QT_TRANSLATE_NOOP("shortcuts", "Goto Prev Marker"), GLOBAL_SHRT, "me_sel_prev"); + + defShrt(SHRT_PART_NORMALIZE, Qt::ALT + Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Normalize"), ARRANG_SHRT, "wave_part_normalize"); } @@ -378,7 +394,7 @@ switch (token) { case MusECore::Xml::TagStart: { if (tag.length()) { - int index = getShrtByTag(tag.toAscii().constData()); + int index = getShrtByTag(tag.toLatin1().constData()); if (index == -1) //No such tag found printf("Config file might be corrupted. Unknown shortcut: %s\n",tag.toLatin1().constData()); else { @@ -388,6 +404,7 @@ } } } + break; case MusECore::Xml::TagEnd: if (tag == "shortcuts") return; @@ -397,4 +414,9 @@ } } +QString shrtToStr(long int shrt) +{ + return QKeySequence(shortcuts[shrt].key).toString(); +} + } // namespace MusEGui diff -Nru muse-2.1.2/muse/shortcuts.h muse-3.0.2+ds1/muse/shortcuts.h --- muse-2.1.2/muse/shortcuts.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/shortcuts.h 2017-12-04 21:01:18.000000000 +0000 @@ -73,12 +73,16 @@ const char* name; }; +QString shrtToStr(long int shrt); + typedef struct shortcut ShortCut ; enum { //Transport/Positioning SHRT_PLAY_SONG, //Enter SHRT_PLAY_TOGGLE, //Space + SHRT_REC_RESTART, //Ctrl+Space + SHRT_REC_RESTART_MULTI, //Shift+Space SHRT_STOP, //Insert SHRT_GOTO_START, // W SHRT_GOTO_LEFT, //End-keypad @@ -197,10 +201,14 @@ SHRT_SEL_BELOW_ADD, //move down and add to selection SHRT_INSERTMEAS, //Ctrl+Shift+M - insert measures + SHRT_DUPLICATE_TRACK, // Ctrl+Shift+D //Arranger tracks SHRT_SEL_TRACK_BELOW, SHRT_SEL_TRACK_ABOVE, + SHRT_EDIT_TRACK_NAME, + SHRT_MUTE_CURRENT_TRACKS, + SHRT_SOLO_CURRENT_TRACKS, //To be in arranger, pianoroll & drumeditor. p4.0.10 now globally handled, too. SHRT_SELECT_ALL, //Ctrl+A @@ -218,6 +226,12 @@ SHRT_DEC_PITCH, SHRT_INC_POS, SHRT_DEC_POS, + + SHRT_STEP_RECORD, + SHRT_MIDI_INPUT, + SHRT_PLAY_EVENTS, + SHRT_INC_VELOCITY, + SHRT_DEC_VELOCITY, SHRT_POS_INC_NOSNAP, SHRT_POS_DEC_NOSNAP, @@ -299,6 +313,8 @@ SHRT_EVENT_COLOR, //e + SHRT_MOVE_PLAY_TO_NOTE, + // Shortcuts for tools // global SHRT_TOOL_POINTER, // @@ -349,6 +365,9 @@ SHRT_NEXT_MARKER, SHRT_PREV_MARKER, + //arragner + SHRT_PART_NORMALIZE, // Alt+N + //Last item: SHRT_NUM_OF_ELEMENTS // must be last }; diff -Nru muse-2.1.2/muse/song.cpp muse-3.0.2+ds1/muse/song.cpp --- muse-2.1.2/muse/song.cpp 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/song.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,8 @@ #include #include #include +#include +#include #include "app.h" #include "driver/jackmidi.h" @@ -64,6 +67,11 @@ #include "keyevent.h" #include #include "tempo.h" +#include "route.h" +#include "libs/strntcpy.h" + +// Undefine if and when multiple output routes are added to midi tracks. +#define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ namespace MusEGlobal { MusECore::Song* song = 0; @@ -71,6 +79,9 @@ namespace MusECore { +LockFreeMPSCRingBuffer *Song::_ipcInEventBuffers = + new LockFreeMPSCRingBuffer(16384); + extern void clearMidiTransforms(); extern void clearMidiInputTransforms(); @@ -82,7 +93,12 @@ :QObject(0) { setObjectName(name); - _arrangerRaster = 0; // Set to measure, the same as Arranger intial value. Arranger snap combo will set this. + + _fCpuLoad = 0.0; + _fDspLoad = 0.0; + _xRunsCount = 0; + + _arrangerRaster = 0; // Set to measure, the same as Arranger initial value. Arranger snap combo will set this. noteFifoSize = 0; noteFifoWindex = 0; noteFifoRindex = 0; @@ -172,6 +188,12 @@ if(ntype >= Synth::SYNTH_TYPE_END) return 0; + // if we ever support Wine VSTs through some other means than through dssi-vst this must be adapted + if (ntype == MusECore::Synth::VST_SYNTH) + ntype=MusECore::Synth::DSSI_SYNTH; + if (ntype == MusECore::Synth::LV2_EFFECT) + ntype=MusECore::Synth::LV2_SYNTH; // the LV2_EFFECT is a specialization used in the menu only, we reassign it to regular LV2_SYNTH + n %= MENU_ADD_SYNTH_ID_BASE; if(n >= (int)MusEGlobal::synthis.size()) return 0; @@ -182,6 +204,7 @@ SynthI* si = createSynthI(MusEGlobal::synthis[n]->baseName(), MusEGlobal::synthis[n]->name(), (Synth::Type)ntype, insertAt); if(!si) return 0; + if (MusEGlobal::config.unhideTracks) SynthI::setVisible(true); // Add instance last in midi device list. @@ -191,10 +214,11 @@ MidiDevice* dev = port->device(); if (dev==0) { - MusEGlobal::midiSeq->msgSetMidiDevice(port, si); - MusEGlobal::muse->changeConfig(true); // save configuration file + MusEGlobal::audio->msgSetMidiDevice(port, si); + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + MusEGlobal::muse->changeConfig(true); if (SynthI::visible()) { - deselectTracks(); + selectAllTracks(false); si->setSelected(true); update(); } @@ -202,9 +226,9 @@ } } if (SynthI::visible()) { - deselectTracks(); + selectAllTracks(false); si->setSelected(true); - update(SC_SELECTION); + update(SC_TRACK_SELECTION); } return si; } @@ -216,13 +240,11 @@ if((Track::TrackType)n >= Track::AUDIO_SOFTSYNTH) return 0; - Undo operations; - Track* t = addTrack(operations, (Track::TrackType)n, insertAt); - applyOperationGroup(operations); + Track* t = addTrack((Track::TrackType)n, insertAt); if (t->isVisible()) { - deselectTracks(); + selectAllTracks(false); t->setSelected(true); - update(SC_SELECTION); + update(SC_TRACK_SELECTION); } return t; } @@ -236,7 +258,7 @@ // If insertAt is valid, inserts before insertAt. Else at the end after all tracks. //--------------------------------------------------------- -Track* Song::addTrack(Undo& /*operations*/, Track::TrackType type, Track* insertAt) +Track* Song::addTrack(Track::TrackType type, Track* insertAt) { Track* track = 0; int lastAuxIdx = _auxs.size(); @@ -293,60 +315,89 @@ int idx = insertAt ? _tracks.index(insertAt) : -1; - insertTrack1(track, idx); // this and the below are replaced - msgInsertTrack(track, idx, true); // by the UndoOp-operation - insertTrack3(track, idx); // does nothing - // No, can't do this. insertTrack2 needs to be called now, not later, otherwise it sees - // that the track may have routes, and reciprocates them, causing duplicate routes. - ///operations.push_back(UndoOp(UndoOp::AddTrack, idx, track)); - // Add default track <-> midiport routes. if(track->isMidiTrack()) { MidiTrack* mt = (MidiTrack*)track; - int c, cbi, ch; + int c; bool defOutFound = false; /// TODO: Remove this if and when multiple output routes supported. + const int chmask = (1 << MIDI_CHANNELS) - 1; for(int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; - - if(mp->device()) // Only if device is valid. p4.0.17 + if(!mp->device()) // Only if device is valid. + continue; + if(mp->device()->rwFlags() & 0x02) // Readable { c = mp->defaultInChannels(); if(c) { - MusEGlobal::audio->msgAddRoute(Route(i, c), Route(track, c)); - updateFlags |= SC_ROUTE; + // All channels set or Omni? Use an Omni route: + if(c == -1 || c == chmask) + track->inRoutes()->push_back(Route(i)); + else + // Add individual channels: + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(c & (1 << ch)) + track->inRoutes()->push_back(Route(i, ch)); + } } - } + } - if(!defOutFound) /// + if(mp->device()->rwFlags() & 0x01) // Writeable { - c = mp->defaultOutChannels(); - if(c) + if(!defOutFound) /// { - - /// TODO: Switch if and when multiple output routes supported. - #if 0 - MusEGlobal::audio->msgAddRoute(Route(track, c), Route(i, c)); - updateFlags |= SC_ROUTE; - #else - for(ch = 0; ch < MIDI_CHANNELS; ++ch) + c = mp->defaultOutChannels(); + if(c) { - cbi = 1 << ch; - if(c & cbi) + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + if(c == -1) + c = 1; // Just to be safe, shouldn't happen, default to channel 0. + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(c & (1 << ch)) + { + defOutFound = true; + mt->setOutPort(i); + if(type != Track::DRUM && type != Track::NEW_DRUM) // Leave drum tracks at channel 10. + mt->setOutChannel(ch); + //updateFlags |= SC_ROUTE; + break; + } + } +#else + // All channels set or Omni? Use an Omni route: + if(c == -1 || c == chmask) + track->outRoutes()->push_back(Route(i)); + else + // Add individual channels: + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { - defOutFound = true; - mt->setOutPort(i); - if(type != Track::DRUM && type != Track::NEW_DRUM) // p4.0.17 Leave drum tracks at channel 10. - mt->setOutChannel(ch); - updateFlags |= SC_ROUTE; - break; + if(c & (1 << ch)) + track->outRoutes()->push_back(Route(i, ch)); } +#endif } - #endif + } + } + } + + if (!defOutFound) { // no default port found + // set it to the port with highest number + + for(int i = MIDI_PORTS-1; i >= 0; --i) { + + MidiPort* mp = &MusEGlobal::midiPorts[i]; + + if (mp->device() != NULL) { + + mt->setOutPort(i); + break; } - } + } } } @@ -359,19 +410,20 @@ switch(type) { case Track::WAVE: case Track::AUDIO_AUX: - MusEGlobal::audio->msgAddRoute(Route((AudioTrack*)track, -1), Route(ao, -1)); - updateFlags |= SC_ROUTE; + //fprintf(stderr, "Song::addTrack(): WAVE or AUDIO_AUX type:%d name:%s pushing default route to master\n", track->type(), track->name().toLatin1().constData()); + track->outRoutes()->push_back(Route(ao)); break; - // p3.3.38 It should actually never get here now, but just in case. + // It should actually never get here now, but just in case. case Track::AUDIO_SOFTSYNTH: - MusEGlobal::audio->msgAddRoute(Route((AudioTrack*)track, 0, ((AudioTrack*)track)->channels()), Route(ao, 0, ((AudioTrack*)track)->channels())); - updateFlags |= SC_ROUTE; + track->outRoutes()->push_back(Route(ao)); break; default: break; } } - MusEGlobal::audio->msgUpdateSoloStates(); + + applyOperation(UndoOp(UndoOp::AddTrack, idx, track)); + return track; } @@ -393,10 +445,6 @@ if((*it)->selected()) { Track::TrackType type = (*it)->type(); - // TODO: Handle synths. p4.0.47 - if(type == Track::AUDIO_SOFTSYNTH) - continue; - if(type == Track::DRUM) ++drum_found; else if(type == Track::NEW_DRUM) @@ -410,9 +458,8 @@ if(audio_found == 0 && midi_found == 0 && drum_found == 0 && new_drum_found==0) return; - MusEGui::DuplicateTracksDialog* dlg = new MusEGui::DuplicateTracksDialog(audio_found, midi_found, drum_found, new_drum_found); - + int rv = dlg->exec(); if(rv == QDialog::Rejected) { @@ -433,202 +480,186 @@ flags |= Track::ASSIGN_ROUTES; if(dlg->defaultRoutes()) flags |= Track::ASSIGN_DEFAULT_ROUTES; - if(dlg->copyParts()) - flags |= Track::ASSIGN_PARTS; + + // These three are exclusive. + if(dlg->duplicateParts()) + flags |= Track::ASSIGN_DUPLICATE_PARTS; + else if(dlg->copyParts()) + flags |= Track::ASSIGN_COPY_PARTS; + else if(dlg->cloneParts()) + flags |= Track::ASSIGN_CLONE_PARTS; + if(dlg->copyDrumlist()) - flags |= Track::ASSIGN_DRUMLIST; + flags |= Track::ASSIGN_DRUMLIST; delete dlg; QString track_name; int idx; int trackno = tl.size(); - MusEGlobal::song->startUndo(); + + Undo operations; for(TrackList::reverse_iterator it = tl.rbegin(); it != tl.rend(); ++it) { Track* track = *it; if(track->selected()) { track_name = track->name(); - + int counter=0; + int numberIndex=0; for(int cp = 0; cp < copies; ++cp) { - // There are two ways to copy a track now. Using the copy constructor or using new + assign(). - // Tested: Both ways seem OK. Prefer copy constructors for simplicity. But new + assign() may be - // required for fine-grained control over initializing various track types. - // - - // Set to 0 to use the copy constructor. Set to 1 to use new + assign(). - // DELETETHIS is this still necessary to keep around? - // also consider removing and adding a hint to a revision number instead - #if 0 - - Track* new_track = 0; - int lastAuxIdx = _auxs.size(); - switch(track->type()) - { - case Track::AUDIO_SOFTSYNTH: // TODO: Handle synths. p4.0.47 - // ((AudioTrack*)new_track)->addAuxSend(lastAuxIdx); DELETETHIS? - break; - - case Track::MIDI: - new_track = new MidiTrack(); - new_track->setType(Track::MIDI); - break; - case Track::DRUM: - new_track = new MidiTrack(); - new_track->setType(Track::DRUM); - //((MidiTrack*)new_track)->setOutChannel(9); DELETETHIS? - break; - case Track::WAVE: - new_track = new MusECore::WaveTrack(); - //((AudioTrack*)new_track)->addAuxSend(lastAuxIdx); DELETETHIS? - break; - case Track::AUDIO_OUTPUT: - new_track = new AudioOutput(); - break; - case Track::AUDIO_GROUP: - new_track = new AudioGroup(); - //((AudioTrack*)new_track)->addAuxSend(lastAuxIdx); DELETETHIS? - break; - case Track::AUDIO_AUX: - new_track = new AudioAux(); - break; - case Track::AUDIO_INPUT: - new_track = new AudioInput(); - //((AudioTrack*)new_track)->addAuxSend(lastAuxIdx); DELETETHIS? - break; - default: - printf("Song::duplicateTracks: Illegal type %d\n", track->type()); - break; - } - - if(new_track) - { - new_track->assign(*track, flags); - #else - if(track->type() != Track::AUDIO_SOFTSYNTH) // TODO: Handle synths. p4.0.47 - { - Track* new_track = track->clone(flags); - #endif - - //new_track->setDefaultName(track_name); // Handled in class now. + Track* new_track = track->clone(flags); + + // assign new names to copied tracks. there is still a gaping hole in the logic + // making multiple duplicates of multiple tracks still does not produce valid results. + if (cp == 0) { // retrieve the first index for renaming the following tracks + numberIndex = new_track->name().lastIndexOf("#"); + if (numberIndex == -1 || numberIndex > track_name.size()) { // according to Qt doc for lastIndexOf it should return -1 when not found + track_name += " #"; // apparently it returns str_size+1 ?! Let's catch both + numberIndex = track_name.size(); + counter=1; + } + else { + counter = new_track->name().right(new_track->name().size()-numberIndex-1).toInt(); + } + } + QString tempName; + while(true) { + tempName = track_name.left(numberIndex+1) + QString::number(++counter); + Track* track = findTrack(tempName); + if(track == 0) + { + new_track->setName(tempName); + break; + } + } idx = trackno + cp; - insertTrack1(new_track, idx); - addUndo(MusECore::UndoOp(MusECore::UndoOp::AddTrack, idx, new_track)); - msgInsertTrack(new_track, idx, false); // No undo. - insertTrack3(new_track, idx); - } + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTrack, idx, new_track)); } } --trackno; } - MusECore::SongChangedFlags_t update_flags = SC_TRACK_INSERTED; - if(flags & (Track::ASSIGN_ROUTES | Track::ASSIGN_DEFAULT_ROUTES)) - update_flags |= SC_ROUTE; - MusEGlobal::song->endUndo(update_flags); - MusEGlobal::audio->msgUpdateSoloStates(); + applyOperationGroup(operations); } -//--------------------------------------------------------- -// cmdRemoveTrack -//--------------------------------------------------------- - -void Song::cmdRemoveTrack(Track* track) - { - int idx = _tracks.index(track); - addUndo(UndoOp(UndoOp::DeleteTrack, idx, track)); - removeTrack2(track); - updateFlags |= SC_TRACK_REMOVED; - } - -//--------------------------------------------------------- -// removeMarkedTracks -//--------------------------------------------------------- - -void Song::removeMarkedTracks() - { - bool loop; - do { - loop = false; - for (iTrack t = _tracks.begin(); t != _tracks.end(); ++t) { - if ((*t)->selected()) { - removeTrack2(*t); - loop = true; - break; - } - } - } while (loop); - } - -//--------------------------------------------------------- -// deselectTracks -//--------------------------------------------------------- +bool Song::addEventOperation(const Event& event, Part* part, bool do_port_ctrls, bool do_clone_port_ctrls) +{ + Event ev(event); + bool added = false; + Part* p = part; + while(1) + { + // NOTE: Multiple events with the same event base pointer or the same id number, in one event list, are FORBIDDEN. + // This precludes using them for 'pattern groups' such as arpeggios or chords. Instead, create a new event type. + iEvent ie = p->nonconst_events().findWithId(event); + if(ie == p->nonconst_events().end()) + { + added = true; + pendingOperations.add(PendingOperationItem(p, ev, PendingOperationItem::AddEvent)); + } + // Port controller values... + if(do_port_ctrls && (do_clone_port_ctrls || (!do_clone_port_ctrls && p == part))) + addPortCtrlEvents(ev, p, p->tick(), p->lenTick(), p->track(), pendingOperations); + + p = p->nextClone(); + if(p == part) + break; + + ev = event.clone(); // Makes a new copy with the same id. + } + return added; +} -void Song::deselectTracks() - { - for (iTrack t = _tracks.begin(); t != _tracks.end(); ++t) - (*t)->setSelected(false); - } +void Song::changeEventOperation(const Event& oldEvent, const Event& newEvent, Part* part, bool do_port_ctrls, bool do_clone_port_ctrls) +{ + // If position is changed we need to reinsert into the list, and all clone lists. + Part* p = part; + do + { + bool found = false; + iEvent ie = p->nonconst_events().findWithId(oldEvent); + if(ie != p->nonconst_events().end()) + { + pendingOperations.add(PendingOperationItem(p, ie, PendingOperationItem::DeleteEvent)); + found = true; + } + + pendingOperations.add(PendingOperationItem(p, newEvent, PendingOperationItem::AddEvent)); + if(do_port_ctrls && (do_clone_port_ctrls || (!do_clone_port_ctrls && p == part))) + { + if(found) + modifyPortCtrlEvents(oldEvent, newEvent, p, pendingOperations); // Port controller values. + else + addPortCtrlEvents(newEvent, p, p->tick(), p->lenTick(), p->track(), pendingOperations); // Port controller values. + } + + p = p->nextClone(); + } + while(p != part); +} //--------------------------------------------------------- -// addEvent -// return true if event was added +// deleteEvent //--------------------------------------------------------- -bool Song::addEvent(Event& event, Part* part) - { - // Return false if the event is already found. - // (But allow a port controller value, above, in case it is not already stored.) - if(part->events()->find(event) != part->events()->end()) - { - // This can be normal for some (redundant) operations. - if(MusEGlobal::debugMsg) - printf("Song::addEvent event already found in part:%s size:%zd\n", part->name().toLatin1().constData(), part->events()->size()); - return false; - } - - part->events()->add(event); - return true; - } +void Song::deleteEventOperation(const Event& event, Part* part, bool do_port_ctrls, bool do_clone_port_ctrls) +{ + Part* p = part; + do + { + if(do_port_ctrls && (do_clone_port_ctrls || (!do_clone_port_ctrls && p == part))) + removePortCtrlEvents(event, p, p->track(), pendingOperations); // Port controller values. + iEvent ie = p->nonconst_events().findWithId(event); + if(ie != p->nonconst_events().end()) + pendingOperations.add(PendingOperationItem(p, ie, PendingOperationItem::DeleteEvent)); + + p = p->nextClone(); + } + while(p != part); +} //--------------------------------------------------------- -// changeEvent +// selectEvent //--------------------------------------------------------- -void Song::changeEvent(Event& oldEvent, Event& newEvent, Part* part) +void Song::selectEvent(Event& event, Part* part, bool select) { - iEvent i = part->events()->find(oldEvent); - - if (i == part->events()->end()) { - // This can be normal for some (redundant) operations. - if(MusEGlobal::debugMsg) - printf("Song::changeEvent event not found in part:%s size:%zd\n", part->name().toLatin1().constData(), part->events()->size()); - // no "return;" because: Allow it to add the new event. (And remove the old one from the midi port controller!) (tim) - } - else - part->events()->erase(i); - - part->events()->add(newEvent); + Part* p = part; + do + { + iEvent ie = p->nonconst_events().findWithId(event); + if(ie == p->nonconst_events().end()) + { + // This can be normal for some (redundant) operations. + if(MusEGlobal::debugMsg) + printf("Song::selectEvent event not found in part:%s size:%zd\n", p->name().toLatin1().constData(), p->nonconst_events().size()); + } + else + ie->second.setSelected(select); + p = p->nextClone(); + } + while(p != part); } //--------------------------------------------------------- -// deleteEvent +// selectAllEvents //--------------------------------------------------------- -void Song::deleteEvent(Event& event, Part* part) - { - iEvent ev = part->events()->find(event); - if (ev == part->events()->end()) { - // This can be normal for some (redundant) operations. - if(MusEGlobal::debugMsg) - printf("Song::deleteEvent event not found in part:%s size:%zd\n", part->name().toLatin1().constData(), part->events()->size()); - return; - } - part->events()->erase(ev); - } +void Song::selectAllEvents(Part* part, bool select) +{ + Part* p = part; + do + { + EventList& el = p->nonconst_events(); + for(iEvent ie = el.begin(); ie != el.end(); ++ie) + ie->second.setSelected(select); + p = p->nextClone(); + } + while(p != part); +} //--------------------------------------------------------- // remapPortDrumCtrlEvents @@ -651,8 +682,8 @@ for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { MidiPart* part = (MidiPart*)(ip->second); - const EventList* el = part->cevents(); - for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + const EventList& el = part->events(); + for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { const Event& ev = ie->second; if(ev.type() != Controller) @@ -670,30 +701,31 @@ if(note == mapidx) { int tick = ev.tick() + part->tick(); - // Default to track port if -1 and track channel if -1. - int ch = MusEGlobal::drumMap[note].channel; - if(ch == -1) - ch = mt->outChannel(); - int port = MusEGlobal::drumMap[note].port; - if(port == -1) - port = mt->outPort(); - MidiPort* mp = &MusEGlobal::midiPorts[port]; - cntrl = (cntrl & ~0xff) | MusEGlobal::drumMap[note].anote; - - // Remove the port controller value. - mp->deleteController(ch, tick, cntrl, part); - - if(newnote != -1 && newnote != MusEGlobal::drumMap[note].anote) - cntrl = (cntrl & ~0xff) | newnote; - if(newchan != -1 && newchan != ch) - ch = newchan; - if(newport != -1 && newport != port) - port = newport; - - mp = &MusEGlobal::midiPorts[port]; - - // Add the port controller value. - mp->setControllerVal(ch, tick, cntrl, ev.dataB(), part); + + if(mt->type() == Track::DRUM) + { + // Default to track port if -1 and track channel if -1. + int ch = MusEGlobal::drumMap[note].channel; + if(ch == -1) + ch = mt->outChannel(); + int port = MusEGlobal::drumMap[note].port; + if(port == -1) + port = mt->outPort(); + MidiPort* mp = &MusEGlobal::midiPorts[port]; + cntrl = (cntrl & ~0xff) | MusEGlobal::drumMap[note].anote; + // Remove the port controller value. + mp->deleteController(ch, tick, cntrl, part); + + if(newnote != -1 && newnote != MusEGlobal::drumMap[note].anote) + cntrl = (cntrl & ~0xff) | newnote; + if(newchan != -1 && newchan != ch) + ch = newchan; + if(newport != -1 && newport != port) + port = newport; + mp = &MusEGlobal::midiPorts[port]; + // Add the port controller value. + mp->setControllerVal(ch, tick, cntrl, ev.dataB(), part); + } } } } @@ -722,17 +754,10 @@ for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { MidiPart* part = (MidiPart*)(ip->second); - const EventList* el = part->cevents(); - // unsigned len = part->lenTick(); // Commented out by flo, see below - for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + for(ciEvent ie = part->events().begin(); ie != part->events().end(); ++ie) { const Event& ev = ie->second; - // Added by T356. Do not handle events which are past the end of the part. - // Commented out by flo: yes, DO handle them! these are "hidden events" - // which may be revealed later again! - // if(ev.tick() >= len) - // break; - + if(ev.type() != Controller) continue; @@ -743,13 +768,16 @@ // Is it a drum controller event, according to the track port's instrument? if(trackmp->drumController(cntrl)) { - int note = cntrl & 0x7f; - // Default to track port if -1 and track channel if -1. - if(MusEGlobal::drumMap[note].channel != -1) - ch = MusEGlobal::drumMap[note].channel; - if(MusEGlobal::drumMap[note].port != -1) - mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; - cntrl = (cntrl & ~0xff) | MusEGlobal::drumMap[note].anote; + if(mt->type() == Track::DRUM) + { + int note = cntrl & 0x7f; + // Default to track port if -1 and track channel if -1. + if(MusEGlobal::drumMap[note].channel != -1) + ch = MusEGlobal::drumMap[note].channel; + if(MusEGlobal::drumMap[note].port != -1) + mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; + cntrl = (cntrl & ~0xff) | MusEGlobal::drumMap[note].anote; + } } else { @@ -770,45 +798,35 @@ } } -void Song::addACEvent(AudioTrack* t, int acid, int frame, double val) -{ - MusEGlobal::audio->msgAddACEvent(t, acid, frame, val); -} - -void Song::changeACEvent(AudioTrack* t, int acid, int frame, int newFrame, double val) -{ - MusEGlobal::audio->msgChangeACEvent(t, acid, frame, newFrame, val); -} - //--------------------------------------------------------- // cmdAddRecordedEvents // add recorded Events into part //--------------------------------------------------------- -void Song::cmdAddRecordedEvents(MidiTrack* mt, EventList* events, unsigned startTick) +void Song::cmdAddRecordedEvents(MidiTrack* mt, const EventList& events, unsigned startTick, Undo& operations) { - if (events->empty()) { + if (events.empty()) { if (MusEGlobal::debugMsg) printf("no events recorded\n"); return; } - iEvent s; - iEvent e; + ciEvent s; + ciEvent e; unsigned endTick; if((MusEGlobal::audio->loopCount() > 0 && startTick > lPos().tick()) || (punchin() && startTick < lPos().tick())) { startTick = lpos(); - s = events->lower_bound(startTick); + s = events.lower_bound(startTick); } else { - s = events->begin(); + s = events.begin(); } // search for last noteOff: endTick = 0; - for (iEvent i = events->begin(); i != events->end(); ++i) { + for (ciEvent i = events.begin(); i != events.end(); ++i) { Event ev = i->second; unsigned l = ev.endTick(); if (l > endTick) @@ -818,10 +836,10 @@ if((MusEGlobal::audio->loopCount() > 0) || (punchout() && endTick > rPos().tick()) ) { endTick = rpos(); - e = events->lower_bound(endTick); + e = events.lower_bound(endTick); } else - e = events->end(); + e = events.end(); if (startTick > endTick) { if (MusEGlobal::debugMsg) @@ -837,7 +855,7 @@ //--------------------------------------------------- PartList* pl = mt->parts(); - MidiPart* part = 0; + const MidiPart* part = 0; iPart ip; for (ip = pl->begin(); ip != pl->end(); ++ip) { part = (MidiPart*)(ip->second); @@ -850,39 +868,37 @@ if (MusEGlobal::debugMsg) printf("create new part for recorded events\n"); // create new part - part = new MidiPart(mt); + MidiPart* newpart; + newpart = new MidiPart(mt); // Round the start down using the Arranger part snap raster value. startTick = AL::sigmap.raster1(startTick, arrangerRaster()); // Round the end up using the Arranger part snap raster value. endTick = AL::sigmap.raster2(endTick, arrangerRaster()); - part->setTick(startTick); - part->setLenTick(endTick - startTick); - part->setName(mt->name()); + newpart->setTick(startTick); + newpart->setLenTick(endTick - startTick); + newpart->setName(mt->name()); // copy events - for (iEvent i = s; i != e; ++i) { - Event old = i->second; + for (ciEvent i = s; i != e; ++i) { + const Event& old = i->second; Event event = old.clone(); event.setTick(old.tick() - startTick); // addEvent also adds port controller values. So does msgAddPart, below. Let msgAddPart handle them. //addEvent(event, part); - if(part->events()->find(event) == part->events()->end()) - part->events()->add(event); + if(newpart->events().find(event) == newpart->events().end()) + newpart->addEvent(event); } - MusEGlobal::audio->msgAddPart(part); - updateFlags |= SC_PART_INSERTED; + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart, newpart)); return; } - updateFlags |= SC_EVENT_INSERTED; - unsigned partTick = part->tick(); if (endTick > part->endTick()) { // Determine new part length... endTick = 0; - for (iEvent i = s; i != e; ++i) { - Event event = i->second; + for (ciEvent i = s; i != e; ++i) { + const Event& event = i->second; unsigned tick = event.tick() - partTick + event.lenTick(); if (endTick < tick) endTick = tick; @@ -891,97 +907,36 @@ // Round the end up (again) using the Arranger part snap raster value. endTick = AL::sigmap.raster2(endTick, arrangerRaster()); + operations.push_back(UndoOp(UndoOp::ModifyPartLength, part, part->lenValue(), endTick, Pos::TICKS)); + } - removePortCtrlEvents(part, false); // Remove all of the part's port controller values. Don't do clone parts. - // Clone the part. This doesn't increment aref count, and doesn't chain clones. - // It also gives the new part a new serial number, but it is - // overwritten with the old one by Song::changePart(), below. - Part* newPart = part->clone(); - - newPart->setLenTick(endTick); // Set the new part's length. - changePart(part, newPart); // Change the part. - - part->events()->incARef(-1); // Manually adjust reference counts. HACK! - newPart->events()->incARef(1); - - replaceClone(part, newPart); // Replace the part in the clone chain with the new part. - - // Now add all of the new part's port controller values. Indicate do not do clone parts. - addPortCtrlEvents(newPart, false); - - // Create an undo op. Indicate do port controller values but not clone parts. - addUndo(UndoOp(UndoOp::ModifyPart, part, newPart, true, false)); - updateFlags |= SC_PART_MODIFIED; - - if (_recMode == REC_REPLACE) - { - iEvent si = newPart->events()->lower_bound(startTick - newPart->tick()); - iEvent ei = newPart->events()->lower_bound(newPart->endTick() - newPart->tick()); - for (iEvent i = si; i != ei; ++i) - { - Event event = i->second; - // Indicate do port controller values and clone parts. - addUndo(UndoOp(UndoOp::DeleteEvent, event, newPart, true, true)); - // Remove the event from the new part's port controller values, and do all clone parts. - removePortCtrlEvents(event, newPart, true); - } - newPart->events()->erase(si, ei); - } - - for (iEvent i = s; i != e; ++i) { - Event event = i->second; - event.setTick(event.tick() - partTick); - Event e; - // Create an undo op. Indicate do port controller values and clone parts. - addUndo(UndoOp(UndoOp::AddEvent, e, event, newPart, true, true)); - - if(newPart->events()->find(event) == newPart->events()->end()) - newPart->events()->add(event); - - // Add the event to the new part's port controller values, and do all clone parts. - addPortCtrlEvents(event, newPart, true); - } - } - else { - if (_recMode == REC_REPLACE) { - iEvent si = part->events()->lower_bound(startTick - part->tick()); - iEvent ei = part->events()->lower_bound(endTick - part->tick()); - - for (iEvent i = si; i != ei; ++i) { - Event event = i->second; - // Indicate that controller values and clone parts were handled. - addUndo(UndoOp(UndoOp::DeleteEvent, event, part, true, true)); - // Remove the event from the part's port controller values, and do all clone parts. - removePortCtrlEvents(event, part, true); - } - part->events()->erase(si, ei); - } - for (iEvent i = s; i != e; ++i) { - Event event = i->second; - int tick = event.tick() - partTick; - event.setTick(tick); - + if (_recMode == REC_REPLACE) { + ciEvent si = part->events().lower_bound(startTick - part->tick()); + ciEvent ei = part->events().lower_bound(endTick - part->tick()); + + for (ciEvent i = si; i != ei; ++i) { + const Event& event = i->second; // Indicate that controller values and clone parts were handled. - addUndo(UndoOp(UndoOp::AddEvent, event, part, true, true)); - - if(part->events()->find(event) == part->events()->end()) - part->events()->add(event); - - // Add the event to the part's port controller values, and do all clone parts. - addPortCtrlEvents(event, part, true); - } + operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, true, true)); } } + for (ciEvent i = s; i != e; ++i) { + Event event = i->second.clone(); + event.setTick(event.tick() - partTick); + // Indicate that controller values and clone parts were handled. + operations.push_back(UndoOp(UndoOp::AddEvent, event, part, true, true)); + } +} //--------------------------------------------------------- // findTrack //--------------------------------------------------------- -MidiTrack* Song::findTrack(const Part* part) const +Track* Song::findTrack(const Part* part) const { for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) { - MidiTrack* track = dynamic_cast(*t); + Track* track = *t; if (track == 0) continue; PartList* pl = track->parts(); @@ -1026,8 +981,17 @@ //--------------------------------------------------------- void Song::clearTrackRec() { - for (iTrack it = tracks()->begin(); it != tracks()->end(); ++it) - setRecordFlag(*it,false); + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + for(iTrack it = tracks()->begin(); it != tracks()->end(); ++it) + { + if(!(*it)->setRecordFlag1(false)) + { + //continue; + } + operations.add(MusECore::PendingOperationItem((*it), false, MusECore::PendingOperationItem::SetTrackRecord)); + } + MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- @@ -1046,7 +1010,7 @@ if (recordFlag != f) { if (f && autoRecEnable) { bool alreadyRecEnabled = false; - Track *selectedTrack = 0; + TrackList selectedTracks; // loop through list and check if any track is rec enabled // if not then rec enable the selected track MusECore::WaveTrackList* wtl = waves(); @@ -1057,7 +1021,7 @@ break; } if((*i)->selected()) - selectedTrack = (*i); + selectedTracks.push_back(*i); } if (!alreadyRecEnabled) { MidiTrackList* mtl = midis(); @@ -1068,11 +1032,20 @@ break; } if((*it)->selected()) - selectedTrack = (*it); + selectedTracks.push_back(*it); } } - if (!alreadyRecEnabled && selectedTrack) { - setRecordFlag(selectedTrack, true); + if (!alreadyRecEnabled && selectedTracks.size() >0) { + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + foreach (Track *t, selectedTracks) + { + if(!t->setRecordFlag1(true)) + continue; + operations.add(MusECore::PendingOperationItem(t, true, MusECore::PendingOperationItem::SetTrackRecord)); + } + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } else if (alreadyRecEnabled) { // do nothing @@ -1086,7 +1059,7 @@ } // prepare recording of wave files for all record enabled wave tracks for (MusECore::iWaveTrack i = wtl->begin(); i != wtl->end(); ++i) { - if((*i)->recordFlag() || (selectedTrack == (*i) && autoRecEnable)) // prepare if record flag or if it is set to recenable + if((*i)->recordFlag()) // || (selectedTracks.find(*i)!=wtl->end() && autoRecEnable)) // prepare if record flag or if it is set to recenable { // setRecordFlag may take too long time to complete // so we try this case specifically (*i)->prepareRecording(); @@ -1233,18 +1206,6 @@ } //--------------------------------------------------------- -// swapTracks -//--------------------------------------------------------- - -void Song::swapTracks(int i1, int i2) - { - addUndo(UndoOp(UndoOp::SwapTrack, i1, i2)); - Track* track = _tracks[i1]; - _tracks[i1] = _tracks[i2]; - _tracks[i2] = track; - } - -//--------------------------------------------------------- // seekTo // setPos slot, only active when not doing playback //--------------------------------------------------------- @@ -1405,23 +1366,6 @@ } //--------------------------------------------------------- -// setChannelMute -// mute all midi tracks associated with channel -//--------------------------------------------------------- - -void Song::setChannelMute(int channel, bool val) - { - for (iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { - MidiTrack* track = dynamic_cast(*i); - if (track == 0) - continue; - if (track->outChannel() == channel) - track->setMute(val); - } - emit songChanged(SC_MUTE); - } - -//--------------------------------------------------------- // len //--------------------------------------------------------- @@ -1429,7 +1373,7 @@ { _len = AL::sigmap.bar2tick(40, 0, 0); // default song len for (iTrack t = _tracks.begin(); t != _tracks.end(); ++t) { - MidiTrack* track = dynamic_cast(*t); + Track* track = dynamic_cast(*t); if (track == 0) continue; PartList* parts = track->parts(); @@ -1443,15 +1387,6 @@ } //--------------------------------------------------------- -// tempoChanged -//--------------------------------------------------------- - -void Song::tempoChanged() -{ - emit songChanged(SC_TEMPO); -} - -//--------------------------------------------------------- // roundUpBar //--------------------------------------------------------- @@ -1521,8 +1456,7 @@ // collect marked parts for (ciMidiTrack t = _midis.begin(); t != _midis.end(); ++t) { - MidiTrack* track = *t; - PartList* pl = track->parts(); + PartList* pl = (*t)->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { if (p->second->selected()) { parts->add(p->second); @@ -1533,12 +1467,9 @@ // and collect all parts of this track if (parts->empty()) { - for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) { + for (ciMidiTrack t = _midis.begin(); t != _midis.end(); ++t) { if ((*t)->selected()) { - MidiTrack* track = dynamic_cast(*t); - if (track == 0) - continue; - PartList* pl = track->parts(); + PartList* pl = (*t)->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) parts->add(p->second); break; @@ -1562,12 +1493,9 @@ */ // collect selected parts - for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) { - MusECore::WaveTrack* track = dynamic_cast(*t); - if (track == 0) - continue; - PartList* pl = track->parts(); - for (iPart p = pl->begin(); p != pl->end(); ++p) { + for (ciWaveTrack t = _waves.begin(); t != _waves.end(); ++t) { + PartList* pl = (*t)->parts(); + for (ciPart p = pl->begin(); p != pl->end(); ++p) { if (p->second->selected()) { parts->add(p->second); } @@ -1577,21 +1505,138 @@ // and collect all parts in this track if (parts->empty()) { - for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) { + for (ciWaveTrack t = _waves.begin(); t != _waves.end(); ++t) { if ((*t)->selected()) { - MusECore::WaveTrack* track = dynamic_cast(*t); - if (track == 0) - continue; - PartList* pl = track->parts(); - for (iPart p = pl->begin(); p != pl->end(); ++p) + PartList* pl = (*t)->parts(); + for (ciPart p = pl->begin(); p != pl->end(); ++p) parts->add(p->second); break; } } } return parts; +} + +void Song::normalizePart(MusECore::Part *part) +{ + const MusECore::EventList& evs = part->events(); + for(MusECore::ciEvent it = evs.begin(); it != evs.end(); ++it) + { + const Event& ev = (*it).second; + if(ev.empty()) + continue; + MusECore::SndFileR file = ev.sndFile(); + if(file.isNull()) + continue; + + QString tmpWavFile = QString::null; + if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", tmpWavFile)) + { + return; + } + + MusEGlobal::audio->msgIdle(true); // Not good with playback during operations + MusECore::SndFile tmpFile(tmpWavFile); + unsigned int file_channels = file.channels(); + tmpFile.setFormat(file.format(), file_channels, file.samplerate()); + if (tmpFile.openWrite()) + { + MusEGlobal::audio->msgIdle(false); + printf("Could not open temporary file...\n"); + return; + } + float* tmpdata[file_channels]; + unsigned tmpdatalen = file.samples(); + for (unsigned i=0; i loudest) + { + loudest = tmpdata[i][j]; + } + } + } + + double scale = 0.99 / (double)loudest; + for (unsigned i=0; icmdChangeWave(ev, tmpWavFile, 0, tmpdatalen); + MusEGlobal::audio->msgIdle(false); // Not good with playback during operations + //sf.update(); + } +} + +void Song::normalizeWaveParts(Part *partCursor) +{ + MusECore::TrackList* tracks=MusEGlobal::song->tracks(); + bool undoStarted = false; + for (MusECore::TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++) + { + if((*t_it)->type() != MusECore::Track::WAVE) + { + continue; + } + const MusECore::PartList* parts=(*t_it)->cparts(); + for (MusECore::ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++) + { + if (p_it->second->selected()) + { + MusECore::Part* part = p_it->second; + if(!undoStarted) + { + undoStarted = true; + MusEGlobal::song->startUndo(); + } + + normalizePart(part); + + } + } + } + //if nothing selected, normilize current part under mouse (if given) + if(!undoStarted && partCursor) + { + undoStarted = true; + MusEGlobal::song->startUndo(); + normalizePart(partCursor); + } + if(undoStarted) + { + MusEGlobal::song->endUndo(SC_CLIP_MODIFIED); + } +} + //--------------------------------------------------------- // beat //--------------------------------------------------------- @@ -1615,6 +1660,14 @@ _heartbeatRateTimer = t; #endif + //First: update cpu load toolbar + + _fCpuLoad = MusEGlobal::muse->getCPULoad(); + _fDspLoad = 0.0f; + if (MusEGlobal::audioDevice) + _fDspLoad = MusEGlobal::audioDevice->getDSP_Load(); + _xRunsCount = MusEGlobal::audio->getXruns(); + // Keep the sync detectors running... for(int port = 0; port < MIDI_PORTS; ++port) MusEGlobal::midiPorts[port].syncInfo().setTime(); @@ -1676,10 +1729,11 @@ // setLen //--------------------------------------------------------- -void Song::setLen(unsigned l) +void Song::setLen(unsigned l, bool do_update) { _len = l; - update(); + if(do_update) + update(); } //--------------------------------------------------------- @@ -1746,30 +1800,25 @@ // setRecordFlag //--------------------------------------------------------- -void Song::setRecordFlag(Track* track, bool val) - { - if (track->type() == Track::WAVE) { - MusECore::WaveTrack* audioTrack = (MusECore::WaveTrack*)track; - if(!audioTrack->setRecordFlag1(val)) - return; - MusEGlobal::audio->msgSetRecord(audioTrack, val); - } - else { - track->setRecordFlag1(val); - track->setRecordFlag2(val); - } - update(SC_RECFLAG); - - } - -//--------------------------------------------------------- -// rescanAlsaPorts -//--------------------------------------------------------- - -void Song::rescanAlsaPorts() - { - emit midiPortsChanged(); - } +void Song::setRecordFlag(Track* track, bool val, Undo* operations) +{ + if(operations) + { + // The undo system calls setRecordFlag1 for us. + operations->push_back(UndoOp(UndoOp::SetTrackRecord, track, val)); + //operations->push_back(UndoOp(UndoOp::SetTrackRecord, track, val, true)); // No undo. + } + else + { + // The pending operations system does not call setRecordFlag1 for us. Call it now. + if(!track->setRecordFlag1(val)) + return; + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackRecord)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + } +} //--------------------------------------------------------- // endMsgCmd @@ -1779,8 +1828,14 @@ { if (updateFlags) { redoList->clearDelete(); - MusEGlobal::undoAction->setEnabled(true); - MusEGlobal::redoAction->setEnabled(false); + + // It is possible the undo list is empty after removal of an empty undo, + // either by optimization or no given operations. + if(MusEGlobal::undoAction) + MusEGlobal::undoAction->setEnabled(!undoList->empty()); + + if(MusEGlobal::redoAction) + MusEGlobal::redoAction->setEnabled(false); setUndoRedoText(); emit songChanged(updateFlags); } @@ -1791,42 +1846,64 @@ //--------------------------------------------------------- void Song::undo() - { +{ + if (MusEGlobal::audio->isRecording()) { + return; + } + updateFlags = 0; - if (doUndo1()) + + Undo& opGroup = undoList->back(); + + if (opGroup.empty()) return; - MusEGlobal::audio->msgUndo(); - doUndo3(); - MusEGlobal::redoAction->setEnabled(true); - MusEGlobal::undoAction->setEnabled(!undoList->empty()); - setUndoRedoText(); + + MusEGlobal::audio->msgRevertOperationGroup(opGroup); + + redoList->push_back(opGroup); + undoList->pop_back(); - if(updateFlags && (SC_TRACK_REMOVED | SC_TRACK_INSERTED)) - MusEGlobal::audio->msgUpdateSoloStates(); + if(MusEGlobal::redoAction) + MusEGlobal::redoAction->setEnabled(true); + if(MusEGlobal::undoAction) + MusEGlobal::undoAction->setEnabled(!undoList->empty()); + setUndoRedoText(); emit songChanged(updateFlags); - } + emit sigDirty(); +} //--------------------------------------------------------- // redo //--------------------------------------------------------- void Song::redo() - { +{ + if (MusEGlobal::audio->isRecording()) { + return; + } + updateFlags = 0; - if (doRedo1()) + + Undo& opGroup = redoList->back(); + + if (opGroup.empty()) return; - MusEGlobal::audio->msgRedo(); - doRedo3(); - MusEGlobal::undoAction->setEnabled(true); - MusEGlobal::redoAction->setEnabled(!redoList->empty()); + + MusEGlobal::audio->msgExecuteOperationGroup(opGroup); + + undoList->push_back(opGroup); + redoList->pop_back(); + + if(MusEGlobal::undoAction) + MusEGlobal::undoAction->setEnabled(true); + if(MusEGlobal::redoAction) + MusEGlobal::redoAction->setEnabled(!redoList->empty()); setUndoRedoText(); - if(updateFlags && (SC_TRACK_REMOVED | SC_TRACK_INSERTED)) - MusEGlobal::audio->msgUpdateSoloStates(); - emit songChanged(updateFlags); - } + emit sigDirty(); +} //--------------------------------------------------------- // processMsg @@ -1839,124 +1916,15 @@ case SEQM_UPDATE_SOLO_STATES: updateSoloStates(); break; - case SEQM_UNDO: - doUndo2(); - break; - case SEQM_REDO: - doRedo2(); + case SEQM_EXECUTE_PENDING_OPERATIONS: + msg->pendingOps->executeRTStage(); break; - case SEQM_MOVE_TRACK: - if (msg->a > msg->b) { - for (int i = msg->a; i > msg->b; --i) { - swapTracks(i, i-1); - } - } - else { - for (int i = msg->a; i < msg->b; ++i) { - swapTracks(i, i+1); - } - } - updateFlags = SC_TRACK_MODIFIED; - break; - case SEQM_ADD_EVENT: - updateFlags = SC_EVENT_INSERTED; - if (addEvent(msg->ev1, (MidiPart*)msg->p2)) { - Event ev; - addUndo(UndoOp(UndoOp::AddEvent, ev, msg->ev1, (Part*)msg->p2, msg->a, msg->b)); - } - else - updateFlags = 0; - if(msg->a) - addPortCtrlEvents(msg->ev1, (Part*)msg->p2, msg->b); - break; - case SEQM_REMOVE_EVENT: - { - Event event = msg->ev1; - MidiPart* part = (MidiPart*)msg->p2; - if(msg->a) - removePortCtrlEvents(event, part, msg->b); - Event e; - addUndo(UndoOp(UndoOp::DeleteEvent, e, event, (Part*)part, msg->a, msg->b)); - deleteEvent(event, part); - updateFlags = SC_EVENT_REMOVED; - } - break; - case SEQM_CHANGE_EVENT: - if(msg->a) - removePortCtrlEvents(msg->ev1, (MidiPart*)msg->p3, msg->b); - changeEvent(msg->ev1, msg->ev2, (MidiPart*)msg->p3); - if(msg->a) - addPortCtrlEvents(msg->ev2, (Part*)msg->p3, msg->b); - addUndo(UndoOp(UndoOp::ModifyEvent, msg->ev2, msg->ev1, (Part*)msg->p3, msg->a, msg->b)); - updateFlags = SC_EVENT_MODIFIED; - break; - - // Moved here from MidiSeq::processMsg p4.0.34 - case SEQM_ADD_TRACK: - insertTrack2(msg->track, msg->ival); - break; - case SEQM_REMOVE_TRACK: - cmdRemoveTrack(msg->track); - break; - //case SEQM_CHANGE_TRACK: DELETETHIS 3 - // changeTrack((Track*)(msg->p1), (Track*)(msg->p2)); - // break; - case SEQM_ADD_PART: - cmdAddPart((Part*)msg->p1); - break; - case SEQM_REMOVE_PART: - cmdRemovePart((Part*)msg->p1); + case SEQM_EXECUTE_OPERATION_GROUP: + executeOperationGroup2(*msg->operations); break; - case SEQM_CHANGE_PART: - cmdChangePart((Part*)msg->p1, (Part*)msg->p2, msg->a, msg->b); - break; - - case SEQM_ADD_TEMPO: - addUndo(UndoOp(UndoOp::AddTempo, msg->a, msg->b)); - MusEGlobal::tempomap.addTempo(msg->a, msg->b); - updateFlags = SC_TEMPO; - break; - - case SEQM_SET_TEMPO: - addUndo(UndoOp(UndoOp::AddTempo, msg->a, msg->b)); - MusEGlobal::tempomap.setTempo(msg->a, msg->b); - updateFlags = SC_TEMPO; + case SEQM_REVERT_OPERATION_GROUP: + revertOperationGroup2(*msg->operations); break; - - case SEQM_SET_GLOBAL_TEMPO: - MusEGlobal::tempomap.setGlobalTempo(msg->a); - break; - - case SEQM_REMOVE_TEMPO: - addUndo(UndoOp(UndoOp::DeleteTempo, msg->a, msg->b)); - MusEGlobal::tempomap.delTempo(msg->a); - updateFlags = SC_TEMPO; - break; - - case SEQM_ADD_SIG: - addUndo(UndoOp(UndoOp::AddSig, msg->a, msg->b, msg->c)); - AL::sigmap.add(msg->a, AL::TimeSignature(msg->b, msg->c)); - updateFlags = SC_SIG; - break; - - case SEQM_REMOVE_SIG: - addUndo(UndoOp(UndoOp::DeleteSig, msg->a, msg->b, msg->c)); - AL::sigmap.del(msg->a); - updateFlags = SC_SIG; - break; - - case SEQM_ADD_KEY: - addUndo(UndoOp(UndoOp::AddKey, msg->a, msg->b)); - MusEGlobal::keymap.addKey(msg->a, (key_enum) msg->b); - updateFlags = SC_KEY; - break; - - case SEQM_REMOVE_KEY: - addUndo(UndoOp(UndoOp::DeleteKey, msg->a, msg->b)); - MusEGlobal::keymap.delKey(msg->a); - updateFlags = SC_KEY; - break; - default: printf("unknown seq message %d\n", msg->id); break; @@ -1964,56 +1932,6 @@ } //--------------------------------------------------------- -// cmdAddPart -//--------------------------------------------------------- - -void Song::cmdAddPart(Part* part) - { - addPart(part); - addUndo(UndoOp(UndoOp::AddPart, part)); - updateFlags = SC_PART_INSERTED; - } - -//--------------------------------------------------------- -// cmdRemovePart -//--------------------------------------------------------- - -void Song::cmdRemovePart(Part* part) - { - removePart(part); - addUndo(UndoOp(UndoOp::DeletePart, part)); - part->events()->incARef(-1); - unchainClone(part); - updateFlags = SC_PART_REMOVED; - } - -//--------------------------------------------------------- -// cmdChangePart -//--------------------------------------------------------- - -void Song::cmdChangePart(Part* oldPart, Part* newPart, bool doCtrls, bool doClones) - { - if(doCtrls) - removePortCtrlEvents(oldPart, doClones); - - changePart(oldPart, newPart); - - addUndo(UndoOp(UndoOp::ModifyPart, oldPart, newPart, doCtrls, doClones)); - - // Changed by T356. Do not decrement ref count if the new part is a clone of the old part, since the event list - // will still be active. - if(oldPart->cevents() != newPart->cevents()) - oldPart->events()->incARef(-1); - - replaceClone(oldPart, newPart); - - if(doCtrls) - addPortCtrlEvents(newPart, doClones); - - updateFlags = SC_PART_MODIFIED; - } - -//--------------------------------------------------------- // panic //--------------------------------------------------------- @@ -2086,6 +2004,7 @@ break; } } +#ifdef ALSA_SUPPORT else if(dynamic_cast< MidiAlsaDevice* >(*imd)) { // With alsa devices, we must not delete them (they're always in the list). But we must @@ -2093,6 +2012,7 @@ (*imd)->inRoutes()->clear(); (*imd)->outRoutes()->clear(); } +#endif } } while (loop); @@ -2215,7 +2135,10 @@ // Clear all midi port controllers and values. for(int i = 0; i < MIDI_PORTS; ++i) + { MusEGlobal::midiPorts[i].controller()->clearDelete(true); // Remove the controllers and the values. + MusEGlobal::midiPorts[i].setMidiDevice(0); + } // Can't do this here. Jack isn't running. Fixed. Test OK so far. DELETETHIS (the comment and #if/#endif) #if 1 @@ -2267,19 +2190,14 @@ printf("...finished cleaning up.\n"); } -//--------------------------------------------------------- -// seqSignal -// sequencer message to GUI -// execution environment: gui thread -//--------------------------------------------------------- - void Song::seqSignal(int fd) { - char buffer[16]; + const int buf_size = 256; + char buffer[buf_size]; - int n = ::read(fd, buffer, 16); + int n = ::read(fd, buffer, buf_size); if (n < 0) { - printf("Song: seqSignal(): READ PIPE failed: %s\n", + fprintf(stderr, "Song: seqSignal(): READ PIPE failed: %s\n", strerror(errno)); return; } @@ -2298,7 +2216,7 @@ abortRolling(); break; case 'P': // alsa ports changed - rescanAlsaPorts(); + alsaScanMidiPorts(); break; case 'G': clearRecAutomation(true); @@ -2326,7 +2244,7 @@ "To proceed check the status of Jack and try to restart it and then .\n" "click on the Restart button."), "restart", "cancel"); if (btn == 0) { - printf("restarting!\n"); + fprintf(stderr, "restarting!\n"); MusEGlobal::muse->seqRestart(); } } @@ -2334,7 +2252,7 @@ break; case 'f': // start freewheel if(MusEGlobal::debugMsg) - printf("Song: seqSignal: case f: setFreewheel start\n"); + fprintf(stderr, "Song: seqSignal: case f: setFreewheel start\n"); if(MusEGlobal::config.freewheelMode) MusEGlobal::audioDevice->setFreewheel(true); @@ -2343,7 +2261,7 @@ case 'F': // stop freewheel if(MusEGlobal::debugMsg) - printf("Song: seqSignal: case F: setFreewheel stop\n"); + fprintf(stderr, "Song: seqSignal: case F: setFreewheel stop\n"); if(MusEGlobal::config.freewheelMode) MusEGlobal::audioDevice->setFreewheel(false); @@ -2366,8 +2284,37 @@ MusEGlobal::audioDevice->registrationChanged(); break; + case 'J': // Port connections changed + if (MusEGlobal::audioDevice) + MusEGlobal::audioDevice->connectionsChanged(); + break; + +// case 'U': // Send song changed signal +// { +// int d_len = sizeof(SongChangedFlags_t); +// if((n - (i + 1)) < d_len) // i + 1 = data after this 'U' +// { +// fprintf(stderr, "Song: seqSignal: case U: Not enough bytes read for SongChangedFlags_t !\n"); +// break; +// } +// SongChangedFlags_t f; +// memcpy(&f, &buffer[i + 1], d_len); +// i += d_len; // Move pointer ahead. Loop will also add one ++i. +// update(f); +// } +// break; + + case 'D': // Drum map changed + update(SC_DRUMMAP); + break; + +// case 'E': // Midi events are available in the ipc event buffer. +// if(MusEGlobal::song) +// MusEGlobal::song->processIpcInEventBuffers(); +// break; + default: - printf("unknown Seq Signal <%c>\n", buffer[i]); + fprintf(stderr, "unknown Seq Signal <%c>\n", buffer[i]); break; } } @@ -2388,7 +2335,7 @@ unsigned tick = event.tick(); PartList* pl = mt->parts(); - MidiPart* part = 0; + const MidiPart* part = 0; iPart ip; for (ip = pl->begin(); ip != pl->end(); ++ip) { part = (MidiPart*)(ip->second); @@ -2400,14 +2347,14 @@ updateFlags |= SC_EVENT_INSERTED; if (ip == pl->end()) { // create new part - part = new MidiPart(mt); + MidiPart* part = new MidiPart(mt); int startTick = roundDownBar(tick); int endTick = roundUpBar(tick + 1); part->setTick(startTick); part->setLenTick(endTick - startTick); part->setName(mt->name()); event.move(-startTick); - part->events()->add(event); + part->addEvent(event); MusEGlobal::audio->msgAddPart(part); return; } @@ -2418,23 +2365,21 @@ Event ev; if(event.type() == Controller) { - EventRange range = part->events()->equal_range(tick); - for(iEvent i = range.first; i != range.second; ++i) + EventRange range = part->events().equal_range(tick); + for(ciEvent i = range.first; i != range.second; ++i) { ev = i->second; if(ev.type() == Controller && ev.dataA() == event.dataA()) { if(ev.dataB() == event.dataB()) // Don't bother if already set. return; - // Indicate do undo, and do port controller values and clone parts. - MusEGlobal::audio->msgChangeEvent(ev, event, part, true, true, true); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::ModifyEvent,event,ev,part,true,true)); return; } } } - // Indicate do undo, and do port controller values and clone parts. - MusEGlobal::audio->msgAddEvent(event, part, true, true, true); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddEvent, event, part, true,true)); } //--------------------------------------------------------- @@ -2458,14 +2403,10 @@ { CtrlList *cl = icl->second; canAdd = true; - frame = MusEGlobal::audio->pos().frame(); - - bool en1, en2; - track->controllersEnabled(acid, &en1, &en2); - + bool en = track->controllerEnabled(acid); AutomationType at = track->automationType(); - if(!MusEGlobal::automation || at == AUTO_OFF || !en1 || !en2) + if(!MusEGlobal::automation || at == AUTO_OFF || !en) ctlval = cl->curVal(); else ctlval = cl->value(frame); @@ -2566,6 +2507,8 @@ int sel = act->data().toInt(); delete menu; + Undo operations; + switch(sel) { case ADD_EVENT: @@ -2638,6 +2581,9 @@ break; } + if(!operations.empty()) + MusEGlobal::song->applyOperationGroup(operations); + return sel; } @@ -2650,9 +2596,7 @@ if(!track && !part) return -1; - enum { ADD_EVENT, CLEAR_EVENT }; - QMenu* menu = new QMenu; - + enum { BYPASS_CONTROLLER, ADD_EVENT, CLEAR_EVENT }; bool isEvent = false; MidiTrack* mt; @@ -2666,8 +2610,8 @@ int dctl = ctlnum; // Is it a drum controller, according to the track port's instrument? - MidiController *mc = mp->drumController(ctlnum); - if(mc) + MidiController *dmc = mp->drumController(ctlnum); + if(dmc) { // Change the controller event's index into the drum map to an instrument note. int note = ctlnum & 0x7f; @@ -2714,8 +2658,8 @@ unsigned partEnd = partStart + part->lenTick(); if(tick >= partStart && tick < partEnd) { - EventRange range = part->events()->equal_range(tick - partStart); - for(iEvent i = range.first; i != range.second; ++i) + EventRange range = part->events().equal_range(tick - partStart); + for(ciEvent i = range.first; i != range.second; ++i) { ev = i->second; if(ev.type() == Controller) @@ -2730,6 +2674,38 @@ } } + int initval = 0; + MidiController* mc = mp->midiController(ctlnum, false); + if(mc) + { + const int bias = mc->bias(); + initval = mc->initVal(); + if(initval == CTRL_VAL_UNKNOWN) + { + if(ctlnum == CTRL_PROGRAM) + // Special for program controller: Set HBank and LBank off (0xff), and program to 0. + initval = 0xffff00; + else + // Otherwise start with the bias. + initval = bias; + } + else + // Auto bias. + initval += bias; + } + const int cur_val = mp->hwCtrlState(channel, dctl); + + QMenu* menu = new QMenu; + + menu->addAction(new MusEGui::MenuTitleItem(tr("Controller:"), menu)); + QAction* bypassEvent = new QAction(menu); + menu->addAction(bypassEvent); + bypassEvent->setText(tr("bypass")); + bypassEvent->setData(BYPASS_CONTROLLER); + bypassEvent->setEnabled(true); + bypassEvent->setCheckable(true); + bypassEvent->setChecked(cur_val == CTRL_VAL_UNKNOWN); + menu->addAction(new MusEGui::MenuTitleItem(tr("Automation:"), menu)); QAction* addEvent = new QAction(menu); @@ -2752,23 +2728,42 @@ return -1; } - int sel = act->data().toInt(); + const int sel = act->data().toInt(); + const bool checked = act->isChecked(); delete menu; switch(sel) { + case BYPASS_CONTROLLER: + { + if(checked) + MusEGlobal::audio->msgSetHwCtrlState(mp, channel, dctl, MusECore::CTRL_VAL_UNKNOWN); + else + { + int v = mp->lastValidHWCtrlState(channel, dctl); + if(v == MusECore::CTRL_VAL_UNKNOWN) + v = initval; + MusEGlobal::audio->msgSetHwCtrlState(mp, channel, dctl, v); + } + } + break; + case ADD_EVENT: { - int val = mp->hwCtrlState(channel, dctl); - if(val == CTRL_VAL_UNKNOWN) - return -1; + int v = cur_val; + if(v == CTRL_VAL_UNKNOWN) + { + v = mp->lastValidHWCtrlState(channel, dctl); + if(v == MusECore::CTRL_VAL_UNKNOWN) + v = initval; + } Event e(Controller); e.setA(ctlnum); - e.setB(val); + e.setB(v); // Do we replace an old event? if(isEvent) { - if(ev.dataB() == val) // Don't bother if already set. + if(ev.dataB() == v) // Don't bother if already set. return -1; e.setTick(tick - part->tick()); @@ -2794,7 +2789,7 @@ part->setLenTick(endTick - startTick); part->setName(mt->name()); e.setTick(tick - startTick); - part->events()->add(e); + part->addEvent(e); // Allow undo. MusEGlobal::audio->msgAddPart(part); } @@ -2815,6 +2810,136 @@ } //--------------------------------------------------------- +// putIpcInEvent +// Put an event into the IPC event ring buffer for the gui thread to process. Returns true on success. +// NOTE: Although the ring buffer is multi-writer, call this from audio thread only for now, unless +// you know what you are doing because the thread needs to ask whether the controller exists before +// calling, and that may not be safe from threads other than gui or audio. +//--------------------------------------------------------- + +bool Song::putIpcInEvent(const MidiPlayEvent& ev) +{ + if(!_ipcInEventBuffers->put(ev)) + { + fprintf(stderr, "Error: Song::putIpcInEvent: Buffer overflow\n"); + return false; + } + return true; +} + +bool Song::processIpcInEventBuffers() +{ + PendingOperationList operations; + MidiPlayEvent buf_ev; + int port, chan, ctrl; + MidiPort* mp; + iMidiCtrlValList imcvl; + MidiCtrlValListList* mcvll; + MidiCtrlValList* mcvl; + + //----------------------------------------------------------- + // First pass: Peek into the buffers and find out if any + // controllers need to be created here in the gui thread. + //----------------------------------------------------------- + + // False = don't use the size snapshot, but update it. + const unsigned int sz = _ipcInEventBuffers->getSize(false); + for(unsigned int i = 0; i < sz; ++i) + { + buf_ev = _ipcInEventBuffers->peek(i); + port = buf_ev.port(); + if(port < 0 || port >= MIDI_PORTS) + continue; + chan = buf_ev.channel(); + if(chan < 0 || chan >= MIDI_CHANNELS) + continue; + + ctrl = buf_ev.translateCtrlNum(); + // Event translates to a controller? + if(ctrl < 0) + continue; + + mp = &MusEGlobal::midiPorts[port]; + mcvll = mp->controller(); + + // Does the controller exist? + imcvl = mcvll->find(chan, ctrl); + if(imcvl == mcvll->end()) + { + // Controller does not exist. Prepare a pending operation. + PendingOperationItem poi(mcvll, 0, chan, ctrl, PendingOperationItem::AddMidiCtrlValList); + // Have we already created and prepared this controller? Look in the operations list. + iPendingOperation ipos = operations.findAllocationOp(poi); + if(ipos == operations.end()) + { + // We have not created and prepared the controller. Create it now. + mcvl = new MidiCtrlValList(ctrl); + // Set the operation controller member now. + poi._mcvl = mcvl; + // Add the operation to the pending operations. + operations.add(poi); + } + } + } + + // Execute any operations to create controllers. + // This waits for audio process thread to execute it. + if(!operations.empty()) + MusEGlobal::audio->msgExecutePendingOperations(operations, true); + + //----------------------------------------------------------- + // Second pass: Read the buffers and set the controller values. + // For the moment, the writer threads may have also put some more events + // into these buffers while they checked if the controller existed. + //----------------------------------------------------------- + + for(unsigned int i = 0; i < sz; ++i) + { + if(!_ipcInEventBuffers->get(buf_ev)) + continue; + + port = buf_ev.port(); + if(port < 0 || port >= MIDI_PORTS) + continue; + chan = buf_ev.channel(); + if(chan < 0 || chan >= MIDI_CHANNELS) + continue; + + ctrl = buf_ev.translateCtrlNum(); + // Event translates to a controller? + if(ctrl < 0) + continue; + + mp = &MusEGlobal::midiPorts[port]; + mcvll = mp->controller(); + + // Put the event BACK INTO the midi port's event buffer so that + // the port will process it 'where it left off' before it put + // this controller creation event into this ring buffer. + // It also allows the port to call updateDrumMap in the audio thread. + // Keep the time intact, so the driver will at least play them in + // sequence even though they will all be 'bunched up' at frame zero. + // Make sure the controller REALLY was created before proceeding, + // otherwise the mechanism might get stuck in a continuous loop. +// imcvl = mcvll->find(chan, ctrl); +// if(imcvl != mcvll->end()) + { + //mp->putHwCtrlEvent(buf_ev); + // Let's bypass the putHwCtrlEvent and save some time - + // put directly into the midi port's controller event buffers. + // This will also prevent getting stuck in continuous loop. + if(!mp->eventBuffers()->put(buf_ev)) + { + fprintf(stderr, "Error: Song::processIpcInEventBuffers(): Midi port controller fifo overflow\n"); + continue; + } + } + } + + return true; +} + +//--------------------------------------------------------- // updateSoloStates // This will properly set all soloing variables (including other tracks) based entirely // on the current values of all the tracks' _solo members. @@ -2830,6 +2955,25 @@ } //--------------------------------------------------------- +// reenableTouchedControllers +// Enable all track and plugin controllers, and synth controllers if applicable, which are NOT in AUTO_WRITE mode. +//--------------------------------------------------------- + +void Song::reenableTouchedControllers() +{ + for(iTrack it = _tracks.begin(); it != _tracks.end(); ++it) + { + if((*it)->isMidiTrack()) + continue; + AudioTrack* t = static_cast(*it); + AutomationType at = t->automationType(); + if(at == AUTO_WRITE) // Exclude write mode because controls need to remain disabled if pressed before play. + continue; + t->enableAllControllers(); + } +} + +//--------------------------------------------------------- // clearRecAutomation //--------------------------------------------------------- @@ -2844,26 +2988,24 @@ // processAutomationEvents //--------------------------------------------------------- -void Song::processAutomationEvents() +void Song::processAutomationEvents(Undo* operations) { - MusEGlobal::audio->msgIdle(true); // gain access to all data structures - - // Just clear all pressed and touched flags, not rec event lists. - clearRecAutomation(false); - if (!MusEGlobal::automation) - { - MusEGlobal::audio->msgIdle(false); - return; - } + Undo ops; + Undo* opsp = operations ? operations : &ops; + + // Clear all pressed and touched flags. + // This is a non-undoable 'one-time' operation, removed after execution. + opsp->push_back(UndoOp(UndoOp::EnableAllAudioControllers)); for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { if(!(*i)->isMidiTrack()) // Process (and clear) rec events. - ((AudioTrack*)(*i))->processAutomationEvents(); + ((AudioTrack*)(*i))->processAutomationEvents(opsp); } - MusEGlobal::audio->msgIdle(false); + if(!operations) + MusEGlobal::song->applyOperationGroup(ops); } //--------------------------------------------------------- @@ -2872,52 +3014,50 @@ void Song::processMasterRec() { - bool do_tempo = false; +// bool do_tempo = false; // Wait a few seconds for the tempo fifo to be empty. - int tout = 30; + int tout = 100; // Ten seconds. Otherwise we gotta move on. while(!_tempoFifo.isEmpty()) { usleep(100000); --tout; if(tout == 0) + { + fprintf(stderr, "Song::processMasterRec: Error: Timeout waiting for _tempoFifo to empty!\n"); break; + } } - int tempo_rec_list_sz = MusEGlobal::tempo_rec_list.size(); + const 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()); + { + // FIXME TODO: Change the tempomap and tempo_rec_list to allocated pointers so they can be quickly swapped in realtime without idling. + MusEGlobal::audio->msgIdle(true); // gain access to all data structures - // 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(); + // 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::audio->msgIdle(false); + update(SC_TEMPO); + } + // It should be safe to do this here in the GUI thread, the driver should not be touching it anymore. + MusEGlobal::tempo_rec_list.clear(); } - - MusEGlobal::tempo_rec_list.clear(); - - MusEGlobal::audio->msgIdle(false); - - if(do_tempo) - update(SC_TEMPO); } //--------------------------------------------------------- @@ -2935,91 +3075,243 @@ // stopRolling //--------------------------------------------------------- -void Song::stopRolling() +void Song::stopRolling(Undo* operations) { + Undo ops; + Undo* opsp = operations ? operations : &ops; + if (record()) - MusEGlobal::audio->recordStop(); + MusEGlobal::audio->recordStop(false, opsp); setStopPlay(false); - processAutomationEvents(); + processAutomationEvents(opsp); + + if(!operations) + MusEGlobal::song->applyOperationGroup(ops); } //--------------------------------------------------------- // connectJackRoutes //--------------------------------------------------------- -void Song::connectJackRoutes(AudioTrack* track, bool disconnect) +bool Song::connectJackRoutes(const MusECore::Route& src, const MusECore::Route& dst, bool disconnect) { - switch(track->type()) + //fprintf(stderr, "connectJackRoutes:\n"); + + if(!MusEGlobal::checkAudioDevice() || !MusEGlobal::audio->isRunning()) + return false; + + switch(src.type) { - case Track::AUDIO_OUTPUT: - { - AudioOutput* ao = (AudioOutput*)track; - // This will re-register the track's jack ports. - if(!disconnect) - ao->setName(ao->name()); - // Now reconnect the output routes. - if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) - { - for(int ch = 0; ch < ao->channels(); ++ch) - { - RouteList* ir = ao->outRoutes(); - for (ciRoute ii = ir->begin(); ii != ir->end(); ++ii) - { - Route r = *ii; - if ((r.type == Route::JACK_ROUTE) && (r.channel == ch)) - { - if(disconnect) - MusEGlobal::audioDevice->disconnect(ao->jackPort(ch), r.jackPort); - else - MusEGlobal::audioDevice->connect(ao->jackPort(ch), r.jackPort); - break; - } - } - if(disconnect) - { - MusEGlobal::audioDevice->unregisterPort(ao->jackPort(ch)); - ao->setJackPort(ch, 0); - } - } - } - } - break; - case Track::AUDIO_INPUT: - { - AudioInput* ai = (AudioInput*)track; - // This will re-register the track's jack ports. - if(!disconnect) - ai->setName(ai->name()); - // Now reconnect the input routes. - if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) - { - for(int ch = 0; ch < ai->channels(); ++ch) - { - RouteList* ir = ai->inRoutes(); - for (ciRoute ii = ir->begin(); ii != ir->end(); ++ii) - { - Route r = *ii; - if ((r.type == Route::JACK_ROUTE) && (r.channel == ch)) - { - if(disconnect) - MusEGlobal::audioDevice->disconnect(r.jackPort, ai->jackPort(ch)); - else - MusEGlobal::audioDevice->connect(r.jackPort, ai->jackPort(ch)); - break; - } - } - if(disconnect) - { - MusEGlobal::audioDevice->unregisterPort(ai->jackPort(ch)); - ai->setJackPort(ch, 0); - } - } - } - } - break; - default: - break; + case Route::JACK_ROUTE: + switch(dst.type) + { + case Route::JACK_ROUTE: + if(disconnect) + return MusEGlobal::audioDevice->disconnect(src.persistentJackPortName, dst.persistentJackPortName); + else + return MusEGlobal::audioDevice->connect(src.persistentJackPortName, dst.persistentJackPortName); + break; + case Route::MIDI_DEVICE_ROUTE: + if(dst.device && dst.device->deviceType() == MidiDevice::JACK_MIDI && dst.device->inClientPort()) + { + if(disconnect) + return MusEGlobal::audioDevice->disconnect(src.persistentJackPortName, MusEGlobal::audioDevice->canonicalPortName(dst.device->inClientPort())); + else + return MusEGlobal::audioDevice->connect(src.persistentJackPortName, MusEGlobal::audioDevice->canonicalPortName(dst.device->inClientPort())); + } + break; + case Route::TRACK_ROUTE: + if(dst.track && dst.track->type() == Track::AUDIO_INPUT && dst.channel >= 0) + { + AudioInput* ai = static_cast(dst.track); + if(ai->jackPort(dst.channel)) + { + if(disconnect) + return MusEGlobal::audioDevice->disconnect(src.persistentJackPortName, MusEGlobal::audioDevice->canonicalPortName(ai->jackPort(dst.channel))); + else + return MusEGlobal::audioDevice->connect(src.persistentJackPortName, MusEGlobal::audioDevice->canonicalPortName(ai->jackPort(dst.channel))); + } + } + break; + case Route::MIDI_PORT_ROUTE: + break; + } + break; + + case Route::MIDI_DEVICE_ROUTE: + switch(dst.type) + { + case Route::JACK_ROUTE: + if(src.device && src.device->deviceType() == MidiDevice::JACK_MIDI && src.device->outClientPort()) + { + if(disconnect) + return MusEGlobal::audioDevice->disconnect(MusEGlobal::audioDevice->canonicalPortName(src.device->outClientPort()), dst.persistentJackPortName); + else + return MusEGlobal::audioDevice->connect(MusEGlobal::audioDevice->canonicalPortName(src.device->outClientPort()), dst.persistentJackPortName); + } + break; + case Route::MIDI_DEVICE_ROUTE: + case Route::TRACK_ROUTE: + case Route::MIDI_PORT_ROUTE: + break; + } + break; + case Route::TRACK_ROUTE: + switch(dst.type) + { + case Route::JACK_ROUTE: + if(src.track && src.track->type() == Track::AUDIO_OUTPUT && src.channel >= 0) + { + AudioOutput* ao = static_cast(src.track); + if(ao->jackPort(src.channel)) + { + if(disconnect) + return MusEGlobal::audioDevice->disconnect(MusEGlobal::audioDevice->canonicalPortName(ao->jackPort(src.channel)), dst.persistentJackPortName); + else + return MusEGlobal::audioDevice->connect(MusEGlobal::audioDevice->canonicalPortName(ao->jackPort(src.channel)), dst.persistentJackPortName); + } + } + break; + case Route::MIDI_DEVICE_ROUTE: + case Route::TRACK_ROUTE: + case Route::MIDI_PORT_ROUTE: + break; + } + break; + case Route::MIDI_PORT_ROUTE: + break; + } + + return false; +} + +//--------------------------------------------------------- +// connectMidiPorts +//--------------------------------------------------------- + +void Song::connectMidiPorts() +{ + // Connect midi device ports to Jack ports... + for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + MidiDevice* md = *i; + if(md->deviceType() != MidiDevice::JACK_MIDI) + continue; + + // Midi outputs... + if(md->rwFlags() & 1) + { + void* our_port = md->outClientPort(); + if(our_port) + { + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); + if(our_port_name) + { + RouteList* rl = md->outRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE) + continue; + const char* route_name = ir->persistentJackPortName; + if(!MusEGlobal::audioDevice->findPort(route_name)) + continue; + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + MusEGlobal::audioDevice->connect(our_port_name, route_name); + } + } + } + } + + // Midi inputs... + if(md->rwFlags() & 2) + { + void* our_port = md->inClientPort(); + if(our_port) + { + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); + if(our_port_name) + { + RouteList* rl = md->inRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE) + continue; + const char* route_name = ir->persistentJackPortName; + if(!MusEGlobal::audioDevice->findPort(route_name)) + continue; + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + MusEGlobal::audioDevice->connect(route_name, our_port_name); + } + } + } + } + } +} + +//--------------------------------------------------------- +// connectAudioPorts +//--------------------------------------------------------- + +void Song::connectAudioPorts() +{ + if(!MusEGlobal::audioDevice) + return; + + // Connect audio output ports to Jack ports... + OutputList* ol = outputs(); + for(iAudioOutput i = ol->begin(); i != ol->end(); ++i) + { + AudioOutput* ao = *i; + int channel = ao->channels(); + for(int ch = 0; ch < channel; ++ch) + { + void* our_port = ao->jackPort(ch); + if(!our_port) + continue; + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); + if(!our_port_name) + continue; + RouteList* rl = ao->outRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE || ir->channel != ch) + continue; + const char* route_name = ir->persistentJackPortName; + if(!MusEGlobal::audioDevice->findPort(route_name)) + continue; + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + MusEGlobal::audioDevice->connect(our_port_name, route_name); + } + } + } + + // Connect Jack ports to audio input ports... + InputList* il = inputs(); + for(iAudioInput i = il->begin(); i != il->end(); ++i) + { + AudioInput* ai = *i; + int channel = ai->channels(); + for(int ch = 0; ch < channel; ++ch) + { + void* our_port = ai->jackPort(ch); + if(!our_port) + continue; + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); + if(!our_port_name) + continue; + RouteList* rl = ai->inRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE || ir->channel != ch) + continue; + const char* route_name = ir->persistentJackPortName; + if(!MusEGlobal::audioDevice->findPort(route_name)) + continue; + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + MusEGlobal::audioDevice->connect(route_name, our_port_name); + } + } } } @@ -3030,7 +3322,7 @@ void Song::insertTrack0(Track* track, int idx) { insertTrack1(track, idx); - insertTrack2(track, idx); // MusEGlobal::audio->msgInsertTrack(track, idx, false); DELETETHIS or is this somehow explanatory? + insertTrack2(track, idx); // the same as MusEGlobal::audio->msgInsertTrack(track, idx, false); insertTrack3(track, idx); } @@ -3077,11 +3369,6 @@ break; case Track::AUDIO_OUTPUT: _outputs.push_back((AudioOutput*)track); - // set default master & monitor if not defined - if (MusEGlobal::audio->audioMaster() == 0) - MusEGlobal::audio->setMaster((AudioOutput*)track); - if (MusEGlobal::audio->audioMonitor() == 0) - MusEGlobal::audio->setMonitor((AudioOutput*)track); break; case Track::AUDIO_GROUP: _groups.push_back((AudioGroup*)track); @@ -3122,51 +3409,37 @@ // add routes - if (track->type() == Track::AUDIO_OUTPUT) + if (track->isMidiTrack()) // p3.3.50 { const RouteList* rl = track->inRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { - Route src(track, r->channel, r->channels); - src.remoteChannel = r->remoteChannel; - r->track->outRoutes()->push_back(src); - // Is the source an Aux Track or else does it have Aux Tracks routed to it? - // Update the Audio Output track's aux ref count. p4.0.37 - if(r->track->auxRefCount()) - track->updateAuxRoute( r->track->auxRefCount(), NULL ); - else if(r->track->type() == Track::AUDIO_AUX) - track->updateAuxRoute( 1, NULL ); - } - } - else if (track->type() == Track::AUDIO_INPUT) - { - const RouteList* rl = track->outRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - Route src(track, r->channel, r->channels); - src.remoteChannel = r->remoteChannel; - r->track->inRoutes()->push_back(src); - // Is this track an Aux Track or else does it have Aux Tracks routed to it? - // Update the other track's aux ref count and all tracks it is connected to. p4.0.37 - if(track->auxRefCount()) - r->track->updateAuxRoute( track->auxRefCount(), NULL ); - else if(track->type() == Track::AUDIO_AUX) - r->track->updateAuxRoute( 1, NULL ); - } - } - else if (track->isMidiTrack()) // p3.3.50 - { - const RouteList* rl = track->inRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - Route src(track, r->channel); - MusEGlobal::midiPorts[r->midiPort].outRoutes()->push_back(src); + switch(r->type) + { + case Route::MIDI_PORT_ROUTE: { + Route src(track, r->channel); + MusEGlobal::midiPorts[r->midiPort].outRoutes()->push_back(src); } + break; + case Route::TRACK_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } } rl = track->outRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { - Route src(track, r->channel); - MusEGlobal::midiPorts[r->midiPort].inRoutes()->push_back(src); + switch(r->type) + { + case Route::MIDI_PORT_ROUTE: { + Route src(track, r->channel); + MusEGlobal::midiPorts[r->midiPort].inRoutes()->push_back(src); } + break; + case Route::TRACK_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } } } else @@ -3174,28 +3447,48 @@ const RouteList* rl = track->inRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { - Route src(track, r->channel, r->channels); - src.remoteChannel = r->remoteChannel; - r->track->outRoutes()->push_back(src); - // Is the source an Aux Track or else does it have Aux Tracks routed to it? - // Update this track's aux ref count. p4.0.37 - if(r->track->auxRefCount()) - track->updateAuxRoute( r->track->auxRefCount(), NULL ); - else if(r->track->type() == Track::AUDIO_AUX) - track->updateAuxRoute( 1, NULL ); + switch(r->type) + { + case Route::TRACK_ROUTE: { + Route src(track, r->remoteChannel, r->channels); + src.remoteChannel = r->channel; + r->track->outRoutes()->push_back(src); + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update this track's aux ref count. p4.0.37 + if(r->track->auxRefCount()) + track->updateAuxRoute( r->track->auxRefCount(), NULL ); + else if(r->track->type() == Track::AUDIO_AUX) + track->updateAuxRoute( 1, NULL ); + } + break; + case Route::MIDI_PORT_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } } rl = track->outRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { - Route src(track, r->channel, r->channels); - src.remoteChannel = r->remoteChannel; - r->track->inRoutes()->push_back(src); - // Is this track an Aux Track or else does it have Aux Tracks routed to it? - // Update the other track's aux ref count and all tracks it is connected to. p4.0.37 - if(track->auxRefCount()) - r->track->updateAuxRoute( track->auxRefCount(), NULL ); - else if(track->type() == Track::AUDIO_AUX) - r->track->updateAuxRoute( 1, NULL ); + switch(r->type) + { + case Route::TRACK_ROUTE: { + Route src(track, r->remoteChannel, r->channels); + src.remoteChannel = r->channel; + r->track->inRoutes()->push_back(src); + // Is this track an Aux Track or else does it have Aux Tracks routed to it? + // Update the other track's aux ref count and all tracks it is connected to. + if(track->auxRefCount()) + r->track->updateAuxRoute( track->auxRefCount(), NULL ); + else if(track->type() == Track::AUDIO_AUX) + r->track->updateAuxRoute( 1, NULL ); + } + break; + case Route::MIDI_PORT_ROUTE: + case Route::JACK_ROUTE: + case Route::MIDI_DEVICE_ROUTE: + break; + } } } } @@ -3211,253 +3504,215 @@ } //--------------------------------------------------------- -// removeTrack0 -//--------------------------------------------------------- - -void Song::removeTrack0(Track* track) - { - removeTrack1(track); - MusEGlobal::audio->msgRemoveTrack(track); - removeTrack3(track); - update(SC_TRACK_REMOVED); - } - -//--------------------------------------------------------- -// removeTrack1 -// non realtime part of removeTrack +// insertTrackOperation //--------------------------------------------------------- -void Song::removeTrack1(Track* track) - { - switch(track->type()) - { - case Track::WAVE: - case Track::AUDIO_OUTPUT: - case Track::AUDIO_INPUT: - case Track::AUDIO_GROUP: - case Track::AUDIO_AUX: - case Track::AUDIO_SOFTSYNTH: - ((AudioTrack*)track)->deleteAllEfxGuis(); - break; - default: - break; - } - - switch(track->type()) - { - case Track::AUDIO_OUTPUT: - case Track::AUDIO_INPUT: - connectJackRoutes((AudioTrack*)track, true); - break; - case Track::AUDIO_SOFTSYNTH: - { - SynthI* si = (SynthI*)track; - if(si->hasGui()) - si->showGui(false); - if(si->hasNativeGui()) - si->showNativeGui(false); - } - break; - default: - break; - } - } - -//--------------------------------------------------------- -// removeTrack -// called from RT context -//--------------------------------------------------------- - -void Song::removeTrack2(Track* track) +void Song::insertTrackOperation(Track* track, int idx, PendingOperationList& ops) { + //int n; + void* sec_track_list = 0; switch(track->type()) { case Track::MIDI: case Track::DRUM: case Track::NEW_DRUM: - removePortCtrlEvents(((MidiTrack*)track)); - unchainTrackParts(track, true); - - _midis.erase(track); + sec_track_list = &_midis; break; case Track::WAVE: - unchainTrackParts(track, true); - - _waves.erase(track); + sec_track_list = &_waves; break; case Track::AUDIO_OUTPUT: - _outputs.erase(track); - break; - case Track::AUDIO_INPUT: - _inputs.erase(track); + sec_track_list = &_outputs; break; case Track::AUDIO_GROUP: - _groups.erase(track); + sec_track_list = &_groups; break; case Track::AUDIO_AUX: - _auxs.erase(track); + sec_track_list = &_auxs; + break; + case Track::AUDIO_INPUT: + sec_track_list = &_inputs; break; case Track::AUDIO_SOFTSYNTH: { - SynthI* s = (SynthI*) track; - s->deactivate2(); - _synthIs.erase(track); + SynthI* s = static_cast(track); + MusEGlobal::midiDevices.addOperation(s, ops); + ops.add(PendingOperationItem(&midiInstruments, s, PendingOperationItem::AddMidiInstrument)); + sec_track_list = &_synthIs; } break; + default: + fprintf(stderr, "unknown track type %d\n", track->type()); + return; } - _tracks.erase(track); - - // remove routes - if (track->type() == Track::AUDIO_OUTPUT) - { - const RouteList* rl = track->inRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - Route src(track, r->channel, r->channels); - src.remoteChannel = r->remoteChannel; - r->track->outRoutes()->removeRoute(src); - // Is the source an Aux Track or else does it have Aux Tracks routed to it? - // Update the Audio Output track's aux ref count. p4.0.37 - if(r->track->auxRefCount()) - track->updateAuxRoute( -r->track->auxRefCount(), NULL ); - else if(r->track->type() == Track::AUDIO_AUX) - track->updateAuxRoute( -1, NULL ); - } - } - else if (track->type() == Track::AUDIO_INPUT) - { - const RouteList* rl = track->outRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - Route src(track, r->channel, r->channels); - src.remoteChannel = r->remoteChannel; - r->track->inRoutes()->removeRoute(src); - // Is this track an Aux Track or else does it have Aux Tracks routed to it? - // Update the other track's aux ref count and all tracks it is connected to. p4.0.37 - if(track->auxRefCount()) - r->track->updateAuxRoute( -track->auxRefCount(), NULL ); - else if(track->type() == Track::AUDIO_AUX) - r->track->updateAuxRoute( -1, NULL ); - } - } - else if (track->isMidiTrack()) // p3.3.50 - { - const RouteList* rl = track->inRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - Route src(track, r->channel); - MusEGlobal::midiPorts[r->midiPort].outRoutes()->removeRoute(src); - } - rl = track->outRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - Route src(track, r->channel); - MusEGlobal::midiPorts[r->midiPort].inRoutes()->removeRoute(src); - } - } - else - { - const RouteList* rl = track->inRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - Route src(track, r->channel, r->channels); - src.remoteChannel = r->remoteChannel; - r->track->outRoutes()->removeRoute(src); - // Is the source an Aux Track or else does it have Aux Tracks routed to it? - // Update this track's aux ref count. p4.0.37 - if(r->track->auxRefCount()) - track->updateAuxRoute( -r->track->auxRefCount(), NULL ); - else if(r->track->type() == Track::AUDIO_AUX) - track->updateAuxRoute( -1, NULL ); - } - rl = track->outRoutes(); - for (ciRoute r = rl->begin(); r != rl->end(); ++r) - { - Route src(track, r->channel, r->channels); - src.remoteChannel = r->remoteChannel; - r->track->inRoutes()->removeRoute(src); - // Is this track an Aux Track or else does it have Aux Tracks routed to it? - // Update the other track's aux ref count and all tracks it is connected to. p4.0.37 - if(track->auxRefCount()) - r->track->updateAuxRoute( -track->auxRefCount(), NULL ); - else if(track->type() == Track::AUDIO_AUX) - r->track->updateAuxRoute( -1, NULL ); - } - } + ops.add(PendingOperationItem(&_tracks, track, idx, PendingOperationItem::AddTrack, sec_track_list)); + addPortCtrlEvents(track, ops); + + // NOTE: Aux sends: + // Initializing of this track and/or others' aux sends is done at the end of Song::execute/revertOperationGroup2(). + // NOTE: Routes: + // Routes are added in the PendingOperationItem::AddTrack section of PendingOperationItem::executeRTStage(). +} + +void writeStringToFile(FILE *filePointer, const char *writeString) +{ + if (MusEGlobal::debugMsg) + std::cout << writeString; + fputs(writeString, filePointer); } //--------------------------------------------------------- -// removeTrack3 -// non realtime part of removeTrack +// removeTrackOperation //--------------------------------------------------------- -//empty. gets executed after the realtime part -void Song::removeTrack3(Track* /*track*/)//prevent of compiler warning: unused parameter +void Song::removeTrackOperation(Track* track, PendingOperationList& ops) { + removePortCtrlEvents(track, ops); + void* sec_track_list = 0; + switch(track->type()) { + case Track::MIDI: + case Track::DRUM: + case Track::NEW_DRUM: + sec_track_list = &_midis; + break; + case Track::WAVE: + sec_track_list = &_waves; + break; + case Track::AUDIO_OUTPUT: + sec_track_list = &_outputs; + break; + case Track::AUDIO_INPUT: + sec_track_list = &_inputs; + break; + case Track::AUDIO_GROUP: + sec_track_list = &_groups; + break; + case Track::AUDIO_AUX: + sec_track_list = &_auxs; + break; + case Track::AUDIO_SOFTSYNTH: + { + SynthI* s = static_cast(track); + iMidiInstrument imi = midiInstruments.find(s); + if(imi != midiInstruments.end()) + ops.add(PendingOperationItem(&midiInstruments, imi, PendingOperationItem::DeleteMidiInstrument)); + + iMidiDevice imd = MusEGlobal::midiDevices.find(s); + if(imd != MusEGlobal::midiDevices.end()) + ops.add(PendingOperationItem(&MusEGlobal::midiDevices, imd, PendingOperationItem::DeleteMidiDevice)); + + if(s->midiPort() != -1) + { + // synthi is attached + // Oops, hey this was wrong before, should have been zero. + MusEGlobal::audio->msgSetMidiDevice(&MusEGlobal::midiPorts[s->midiPort()], 0); + } + + sec_track_list = &_synthIs; + } + break; + } + + ops.add(PendingOperationItem(&_tracks, track, PendingOperationItem::DeleteTrack, sec_track_list)); + + // NOTE: Routes: + // Routes are removed in the PendingOperationItem::DeleteTrack section of PendingOperationItem::executeRTStage(). } //--------------------------------------------------------- // executeScript //--------------------------------------------------------- -void Song::executeScript(const char* scriptfile, PartList* parts, int quant, bool onlyIfSelected) +void Song::executeScript(QWidget *parent, const char* scriptfile, PartList* parts, int quant, bool onlyIfSelected) { // a simple format for external processing // will be extended if there is a need // // Semantics: + // TIMESIG // PARTLEN // BEATLEN // QUANTLEN // NOTE // CONTROLLER // + + if (onlyIfSelected) // if this is set means we are probably inside a midi editor and we ask again to be sure + { + if(QMessageBox::question(parent, QString("Process events"), + tr("Do you want to process ALL or only selected events?"), tr("&Selected"), tr("&All"), + QString::null, 0, 1 ) == 1) + { + onlyIfSelected = false; + } + } + QProgressDialog progress(parent); + progress.setLabelText("Process parts"); + progress.setRange(0,parts->size()); + progress.setValue(0); + progress.setCancelButton(0); MusEGlobal::song->startUndo(); // undo this entire block for (iPart i = parts->begin(); i != parts->end(); i++) { //const char* tmp = tmpnam(NULL); char tmp[16] = "muse-tmp-XXXXXX"; + char tempStr[200]; int fd = mkstemp(tmp); if (MusEGlobal::debugMsg) printf("executeScript: script input filename=%s\n",tmp); + FILE *fp = fdopen(fd , "w"); MidiPart *part = (MidiPart*)(i->second); - int partStart = part->endTick()-part->lenTick(); + if (MusEGlobal::debugMsg) + printf("SENDING TO SCRIPT, part start: %d\n", part->tick()); + int z, n; - AL::sigmap.timesig(0, z, n); - fprintf(fp, "TIMESIG %d %d\n", z, n); - fprintf(fp, "PART %d %d\n", partStart, part->lenTick()); - fprintf(fp, "BEATLEN %d\n", AL::sigmap.ticksBeat(0)); - fprintf(fp, "QUANTLEN %d\n", quant); + AL::sigmap.timesig(part->tick(), z, n); + sprintf(tempStr, "TIMESIG %d %d\n", z, n); + writeStringToFile(fp,tempStr); + sprintf(tempStr, "PART %d %d\n", part->tick(), part->lenTick()); + writeStringToFile(fp,tempStr); + sprintf(tempStr, "BEATLEN %d\n", AL::sigmap.ticksBeat(part->tick())); + writeStringToFile(fp,tempStr); + sprintf(tempStr, "QUANTLEN %d\n", quant); + writeStringToFile(fp,tempStr); - for (iEvent e = part->events()->begin(); e != part->events()->end(); e++) { - Event ev = e->second; + if (MusEGlobal::debugMsg) + std::cout << "Events in part " << part->events().size() << std::endl; - if (ev.isNote()) - { - if (onlyIfSelected && ev.selected() == false) - continue; + EventList elist = part->events(); + for (ciEvent e = elist.begin(); e != elist.end(); e++) + { + Event ev = e->second; - fprintf(fp,"NOTE %d %d %d %d\n", ev.tick(), ev.dataA(), ev.lenTick(), ev.dataB()); - // Indicate no undo, and do not do port controller values and clone parts. - MusEGlobal::audio->msgDeleteEvent(ev, part, false, false, false); - } else if (ev.type()==Controller) { - fprintf(fp,"CONTROLLER %d %d %d %d\n", ev.tick(), ev.dataA(), ev.dataB(), ev.dataC()); - // Indicate no undo, and do not do port controller values and clone parts. - MusEGlobal::audio->msgDeleteEvent(ev, part, false, false, false); - } + if (ev.isNote()) + { + if (onlyIfSelected && ev.selected() == false) + continue; + + sprintf(tempStr,"NOTE %d %d %d %d\n", ev.tick(), ev.dataA(), ev.lenTick(), ev.dataB()); + writeStringToFile(fp,tempStr); + + // Indicate no undo, and do not do port controller values and clone parts. + MusEGlobal::audio->msgDeleteEvent(ev, part, false, false, false); + } else if (ev.type()==Controller) { + sprintf(tempStr,"CONTROLLER %d %d %d %d\n", ev.tick(), ev.dataA(), ev.dataB(), ev.dataC()); + writeStringToFile(fp,tempStr); + // Indicate no undo, and do not do port controller values and clone parts. + MusEGlobal::audio->msgDeleteEvent(ev, part, false, false, false); + } } fclose(fp); QStringList arguments; arguments << tmp; - QProcess *myProcess = new QProcess(MusEGlobal::muse); + QProcess *myProcess = new QProcess(parent); myProcess->start(scriptfile, arguments); myProcess->waitForFinished(); QByteArray errStr = myProcess->readAllStandardError(); if (myProcess->exitCode()) { - QMessageBox::warning(MusEGlobal::muse, tr("MusE - external script failed"), + QMessageBox::warning(parent, tr("MusE - external script failed"), tr("MusE was unable to launch the script, error message:\n%1").arg(QString(errStr))); endUndo(SC_EVENT_REMOVED); return; @@ -3466,48 +3721,61 @@ printf("script execution produced the following error:\n%s\n", QString(errStr).toLatin1().data()); } QFile file(tmp); - if ( file.open( QIODevice::ReadOnly ) ) { - QTextStream stream( &file ); - QString line; - while ( !stream.atEnd() ) { - line = stream.readLine(); // line of text excluding '\n' - if (line.startsWith("NOTE")) - { - QStringList sl = line.split(" "); - - Event e(Note); - int tick = sl[1].toInt(); - int pitch = sl[2].toInt(); - int len = sl[3].toInt(); - int velo = sl[4].toInt(); - e.setTick(tick); - e.setPitch(pitch); - e.setVelo(velo); - e.setLenTick(len); - // Indicate no undo, and do not do port controller values and clone parts. - MusEGlobal::audio->msgAddEvent(e, part, false, false, false); - } - if (line.startsWith("CONTROLLER")) - { - QStringList sl = line.split(" "); - - Event e(Controller); - int a = sl[2].toInt(); - int b = sl[3].toInt(); - int c = sl[4].toInt(); - e.setA(a); - e.setB(b); - e.setB(c); - // Indicate no undo, and do not do port controller values and clone parts. - MusEGlobal::audio->msgAddEvent(e, part, false, false, false); - } + if (MusEGlobal::debugMsg) + file.copy(file.fileName() + "_input"); + + if ( file.open( QIODevice::ReadOnly ) ) + { + QTextStream stream( &file ); + QString line; + if (MusEGlobal::debugMsg) + printf("RECEIVED FROM SCRIPT:\n"); + while ( !stream.atEnd() ) + { + line = stream.readLine(); // line of text excluding '\n' + if (MusEGlobal::debugMsg) { + std::cout << line.toStdString() << std::endl; } - file.close(); - } + if (line.startsWith("NOTE")) + { + QStringList sl = line.split(" "); - remove(tmp); - } + Event e(Note); + int tick = sl[1].toInt(); + int pitch = sl[2].toInt(); + int len = sl[3].toInt(); + int velo = sl[4].toInt(); + printf ("extraced %d %d %d %d\n", tick, pitch, len, velo); + e.setTick(tick); + e.setPitch(pitch); + e.setVelo(velo); + e.setLenTick(len); + // Indicate no undo, and do not do port controller values and clone parts. + MusEGlobal::audio->msgAddEvent(e, part, false, false, false); + } + if (line.startsWith("CONTROLLER")) + { + QStringList sl = line.split(" "); + + Event e(Controller); + int a = sl[2].toInt(); + int b = sl[3].toInt(); + int c = sl[4].toInt(); + e.setA(a); + e.setB(b); + e.setB(c); + // Indicate no undo, and do not do port controller values and clone parts. + MusEGlobal::audio->msgAddEvent(e, part, false, false, false); + } + } + file.close(); + } + + if (!MusEGlobal::debugMsg) // if we are writing debug info we also keep the script data + remove(tmp); + progress.setValue(progress.value()+1); + } // for endUndo(SC_EVENT_REMOVED); } @@ -3556,7 +3824,113 @@ connect(distSignalMapper, SIGNAL(mapped(int)), receiver, SLOT(execDeliveredScript(int))); connect(userSignalMapper, SIGNAL(mapped(int)), receiver, SLOT(execUserScript(int))); } - return; + return; +} + +//--------------------------------------------------------- +// restartRecording +// Called from gui thread only. +//--------------------------------------------------------- + +void Song::restartRecording(bool discard) +{ + // FIXME After recording, it never makes it past here because recording has long since stopped. + // Although, it should work WHILE recording. + // We may need a track flag like 'justRecorded' or 'lastRecorded' something... + if(!MusEGlobal::audio->isRecording() || !MusEGlobal::audio->isRunning()) + return; + + // Do not copy parts or controller graphs. When ASSIGN_STD_CTRLS is NOT included, it will + // copy just the standard controller current values, but not the graphs. + // FIXME: Although we would like to copy plugins, that may get expensive after a while. + // So instead try to create a group track with the plugins and route the track and all + // the retake tracks through the group track. + const int clone_flags = Track::ASSIGN_PROPERTIES | Track::ASSIGN_ROUTES | Track::ASSIGN_DEFAULT_ROUTES | Track::ASSIGN_DRUMLIST; + + MusECore::Undo operations; + + if(!discard) + { + MusEGlobal::audio->recordStop(true /*restart record*/, &operations); + processAutomationEvents(&operations); + } + + //clear all recorded midi events and wave files + QStringList new_track_names; + + int idx_cnt = 0; + for(size_t i = 0; i < _tracks.size(); i++) + { + Track *cTrk = _tracks[i]; + if(!cTrk->recordFlag()) + continue; + Track *nTrk = NULL; + if(!discard) + { + nTrk = cTrk->clone(clone_flags); + + QString track_name = cTrk->name(); + int counter=0; + int numberIndex=0; + // Assign a new name to the cloned track. + numberIndex = track_name.lastIndexOf("#"); + // according to Qt doc for lastIndexOf it should return -1 when not found + // apparently it returns str_size+1 ?! Let's catch both + if (numberIndex == -1 || numberIndex > track_name.size()) { + track_name += " #"; + numberIndex = track_name.size(); + counter=1; + } + else { + counter = track_name.right(track_name.size()-numberIndex-1).toInt(); + } + QString tempName; + while(true) { + tempName = track_name.left(numberIndex+1) + QString::number(++counter); + if(new_track_names.indexOf(tempName) >= 0) + continue; + Track* track = findTrack(tempName); + if(track == 0) + { + nTrk->setName(tempName); + break; + } + } + + new_track_names.push_back(nTrk->name()); + + const unsigned int idx = _tracks.index(cTrk) + idx_cnt++; + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTrack, idx + 1, nTrk)); + operations.push_back(UndoOp(UndoOp::SetTrackMute, cTrk, true)); + operations.push_back(UndoOp(UndoOp::SetTrackRecord, cTrk, false)); + setRecordFlag(nTrk, true, &operations); + } + if (cTrk->isMidiTrack()) + { + if(discard) + { + ((MidiTrack *)cTrk)->mpevents.clear(); + } + } + else if (cTrk->type() == Track::WAVE) + { + if(discard) + { + ((WaveTrack*)cTrk)->setRecFile(NULL); + ((WaveTrack*)cTrk)->resetMeter(); + ((WaveTrack*)cTrk)->prepareRecording(); + } + else + { + ((WaveTrack*)nTrk)->prepareRecording(); + } + } + } + + applyOperationGroup(operations); + + MusEGlobal::song->setPos(Song::CPOS, MusEGlobal::audio->getStartRecordPos()); + //MusEGlobal::audioDevice->startTransport(); } //--------------------------------------------------------- @@ -3573,14 +3947,14 @@ return path; } -void Song::informAboutNewParts(const std::map< Part*, std::set >& param) +void Song::informAboutNewParts(const std::map< const Part*, std::set >& param) { emit newPartsCreated(param); } -void Song::informAboutNewParts(Part* orig, Part* p1, Part* p2, Part* p3, Part* p4, Part* p5, Part* p6, Part* p7, Part* p8, Part* p9) +void Song::informAboutNewParts(const Part* orig, const Part* p1, const Part* p2, const Part* p3, const Part* p4, const Part* p5, const Part* p6, const Part* p7, const Part* p8, const Part* p9) { - std::map< Part*, std::set > temp; + std::map > temp; temp[orig].insert(p1); temp[orig].insert(p2); @@ -3591,7 +3965,7 @@ temp[orig].insert(p7); temp[orig].insert(p8); temp[orig].insert(p9); - temp[orig].erase(static_cast(NULL)); + temp[orig].erase(static_cast(NULL)); temp[orig].erase(orig); informAboutNewParts(temp); diff -Nru muse-2.1.2/muse/songfile.cpp muse-3.0.2+ds1/muse/songfile.cpp --- muse-2.1.2/muse/songfile.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/songfile.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -21,9 +21,11 @@ // //========================================================= -#include +#include #include #include +#include +#include #include "app.h" #include "song.h" @@ -56,49 +58,6 @@ namespace MusECore { -/* DELETETHIS 42 -//--------------------------------------------------------- -// updateCloneList -//--------------------------------------------------------- - -void updateCloneList(Part* oPart, Part* nPart) -{ - for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) - { - if(i->cp == oPart) - { - i->cp = nPart; - break; - } - } -} - -void updateCloneList(PartList* oParts, PartList* nParts) -{ - for(iPart ip = oParts->begin(); ip != oParts->end(); ++ip) - { - for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) - { - if(i->cp == oPart) - { - i->cp = nPart; - break; - } - } - } -} - -//--------------------------------------------------------- -// clearClipboardAndCloneList -//--------------------------------------------------------- - -void clearClipboardAndCloneList() -{ - //QApplication::clipboard()->clear(QClipboard::Clipboard); - MusEGlobal::cloneList.clear(); -} -*/ - //--------------------------------------------------------- // NKey::write //--------------------------------------------------------- @@ -167,15 +126,14 @@ } //--------------------------------------------------------- -// readXmlPart +// Part::readFromXml //--------------------------------------------------------- -Part* readXmlPart(Xml& xml, Track* track, bool doClone, bool toTrack) +Part* Part::readFromXml(Xml& xml, Track* track, bool doClone, bool toTrack) { int id = -1; Part* npart = 0; - uuid_t uuid; - uuid_clear(uuid); + QUuid uuid; bool uuidvalid = false; bool clone = true; bool wave = false; @@ -193,39 +151,41 @@ { if(id != -1) // If an id was found... { - for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) + for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { + if(i->is_deleted) // Is the clone item marked as deleted? Ignore it. + continue; if(i->id == id) // Is a matching part found in the clone list? { - // This makes a clone, chains the part, and increases ref counts. + // Create a clone. It must still be added later in a operationgroup npart = track->newPart((Part*)i->cp, true); break; } } - } + } else if(uuidvalid) // If a uuid was found... { - for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) + for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { - if(uuid_compare(uuid, i->uuid) == 0) // Is a matching part found in the clone list? + if(uuid == i->_uuid) // Is a matching part found in the clone list? { Track* cpt = i->cp->track(); if(toTrack) // If we want to paste to the given track... { - // If the given track type is not the same as the part's + // If the given track type is not the same as the part's // original track type, we can't continue. Just return. if(!track || cpt->type() != track->type()) { xml.skip("part"); return 0; - } + } } else // ...else we want to paste to the part's original track. { // Make sure the track exists (has not been deleted). - if((cpt->isMidiTrack() && MusEGlobal::song->midis()->find(cpt) != MusEGlobal::song->midis()->end()) || + if((cpt->isMidiTrack() && MusEGlobal::song->midis()->find(cpt) != MusEGlobal::song->midis()->end()) || (cpt->type() == Track::WAVE && MusEGlobal::song->waves()->find(cpt) != MusEGlobal::song->waves()->end())) - track = cpt; + track = cpt; else // Track was not found. Try pasting to the given track, as above... { if(!track || cpt->type() != track->type()) @@ -233,43 +193,46 @@ // No luck. Just return. xml.skip("part"); return 0; - } + } } } - + + if(i->is_deleted) // Is the clone item marked as deleted? Don't create a clone, create a copy. + break; + // If it's a regular paste (not paste clone), and the original part is // not a clone, defer so that a new copy is created in TagStart above. if(!doClone && !isclone) break; - - // This makes a clone, chains the part, and increases ref counts. + + // Create a clone. It must still be added later in a operationgroup npart = track->newPart((Part*)i->cp, true); break; } - } + } } - + if(!npart) // If the part still has not been created yet... { - + if(!track) // A clone was not created from any matching { // part. Create a non-clone part now. xml.skip("part"); return 0; - } - // If we're pasting to selected track and the 'wave' + } + // If we're pasting to selected track and the 'wave' // variable is valid, check for mismatch... if(toTrack && uuidvalid) { // If both the part and track are not midi or wave... - if((wave && track->isMidiTrack()) || + if((wave && track->isMidiTrack()) || (!wave && track->type() == Track::WAVE)) { xml.skip("part"); return 0; - } + } } - + if (track->isMidiTrack()) npart = new MidiPart((MidiTrack*)track); else if (track->type() == Track::WAVE) @@ -278,31 +241,31 @@ { xml.skip("part"); return 0; - } - + } + // Signify a new non-clone part was created. // Even if the original part was itself a clone, clear this because the // attribute section did not create a clone from any matching part. clone = false; - - // If an id or uuid was found, add the part to the clone list + + // If an id or uuid was found, add the part to the clone list // so that subsequent parts can look it up and clone from it... if(id != -1) { ClonePart ncp(npart, id); MusEGlobal::cloneList.push_back(ncp); } - else + else if(uuidvalid) { ClonePart ncp(npart); // New ClonePart creates its own uuid, but we need to replace it. - uuid_copy(ncp.uuid, uuid); + ncp._uuid = uuid; // OK for non-windows? MusEGlobal::cloneList.push_back(ncp); } } - } - + } + if (tag == "name") npart->setName(xml.parse1()); else if (tag == "poslen") { @@ -324,7 +287,7 @@ npart->setColorIndex(xml.parseInt()); else if (tag == "mute") npart->setMute(xml.parseInt()); - else if (tag == "event") + else if (tag == "event") { // If a new non-clone part was created, accept the events... if(!clone) @@ -338,25 +301,17 @@ // tickpos is relative to start of part, we substract tick(). // TODO: better handling for wave event e.move( -npart->tick() ); - int tick = e.tick(); - - // DELETETHIS 7 - // Do not discard events belonging to clone parts, - // at least not yet. A later clone might have a longer, - // fully accommodating part length! - //if ((tick < 0) || (tick >= (int) lenTick())) { - //if ((tick < 0) || ( id == -1 && !clone && (tick >= (int)lenTick()) )) - // No way to tell at the moment whether there will be clones referencing this... - // No choice but to accept all events past 0. - if(tick < 0) + int tick = e.tick(); + + if(tick < 0) { printf("readClone: warning: event at tick:%d not in part:%s, discarded\n", tick, npart->name().toLatin1().constData()); } - else + else { - npart->events()->add(e); - } + npart->addEvent(e); + } } else // ...Otherwise a clone was created, so we don't need the events. xml.skip(tag); @@ -373,177 +328,106 @@ else if (tag == "cloneId") { id = xml.s2().toInt(); - //if(id != -1) DELETETHIS 19 - //{ - // for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) - // { - // Is a matching part found in the clone list? - // if(i->id == id) - // { - // If it's a regular paste (not paste clone), and the original part is - // not a clone, defer so that a new copy is created in TagStart above. - //if(!doClone && i->cp->cevents()->arefCount() <= 1) - //if(!doClone && !isclone) - // break; - - // This makes a clone, chains the part, and increases ref counts. - // npart = track->newPart((Part*)i->cp, true); - // break; - // } - // } - //} - } + } else if (tag == "uuid") { - uuid_parse(xml.s2().toLatin1().constData(), uuid); - if(!uuid_is_null(uuid)) + uuid = QUuid(xml.s2()); + if(!uuid.isNull()) { uuidvalid = true; - /* DELETETHIS 50 - for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) - { - // Is a matching part found in the clone list? - if(uuid_compare(uuid, i->uuid) == 0) - { - Track* cpt = i->cp->track(); - // If we want to paste to the given track... - if(toTrack) - { - // If the given track type is not the same as the part's - // original track type, we can't continue. Just return. - if(!track || cpt->type() != track->type()) - { - xml.skip("part"); - return 0; - } - } - else - // ...else we want to paste to the part's original track. - { - // Make sure the track exists (has not been deleted). - if((cpt->isMidiTrack() && MusEGlobal::song->midis()->find(cpt) != MusEGlobal::song->midis()->end()) || - (cpt->type() == Track::WAVE && MusEGlobal::song->waves()->find(cpt) != MusEGlobal::song->waves()->end())) - track = cpt; - else - // Track was not found. Try pasting to the given track, as above... - { - if(!track || cpt->type() != track->type()) - { - // No luck. Just return. - xml.skip("part"); - return 0; - } - } - } - - // If it's a regular paste (not paste clone), and the original part is - // not a clone, defer so that a new copy is created in TagStart above. - //if(!doClone && i->cp->cevents()->arefCount() <= 1) - if(!doClone && !isclone) - break; - - // This makes a clone, chains the part, and increases ref counts. - npart = track->newPart((Part*)i->cp, true); - break; - } - } - */ } - } - else if(tag == "isclone") + } + else if(tag == "isclone") isclone = xml.s2().toInt(); break; case Xml::TagEnd: - if (tag == "part") + if (tag == "part") return npart; default: break; } } - return npart; + return npart; } //--------------------------------------------------------- // Part::write -// If isCopy is true, write the xml differently so that -// we can have 'Paste Clone' feature. +// If isCopy is true, write the xml differently so that +// we can have 'Paste Clone' feature. //--------------------------------------------------------- void Part::write(int level, Xml& xml, bool isCopy, bool forceWavePaths) const { - const EventList* el = cevents(); int id = -1; - uuid_t uuid; - uuid_clear(uuid); + QUuid uuid; bool dumpEvents = true; bool wave = _track->type() == Track::WAVE; - + + // NOTE ::write() should never be called on a deleted part, so no checking here for cloneList items marked as deleted. + // Checking is awkward anyway and doesn't fit well here. + if(isCopy) { - for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) + for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { - if(i->cp->cevents() == el) + if(i->cp->isCloneOf(this)) { - uuid_copy(uuid, i->uuid); + uuid = i->_uuid; dumpEvents = false; break; } } - if(uuid_is_null(uuid)) + if(uuid.isNull()) { ClonePart cp(this); - uuid_copy(uuid, cp.uuid); + uuid = cp._uuid; MusEGlobal::cloneList.push_back(cp); } - } + } else { - if (el->arefCount() > 1) + if (this->hasClones()) { - for (iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) + for (iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { - if (i->cp->cevents() == el) + if (i->cp->isCloneOf(this)) { id = i->id; dumpEvents = false; break; } } - if (id == -1) + if (id == -1) { id = MusEGlobal::cloneList.size(); ClonePart cp(this, id); MusEGlobal::cloneList.push_back(cp); } } - } + } - // Special markers if this is a copy operation and the + // Special markers if this is a copy operation and the // part is a clone. if(isCopy) { - char sid[40]; // uuid string is 36 chars. Try 40 for good luck. - sid[0] = 0; - uuid_unparse_lower(uuid, sid); - if(wave) - xml.nput(level, "arefCount() > 1) + xml.nput(level, ""); - level++; + level++; } else if (id != -1) { xml.tag(level++, "part cloneId=\"%d\"", id); - } + } else xml.tag(level++, "part"); - + xml.strTag(level, "name", _name); PosLen::write(level, xml, "poslen"); @@ -552,292 +436,12 @@ if (_mute) xml.intTag(level, "mute", _mute); if (dumpEvents) { - for (ciEvent e = el->begin(); e != el->end(); ++e) + for (ciEvent e = events().begin(); e != events().end(); ++e) e->second.write(level, xml, *this, forceWavePaths); } xml.etag(level, "part"); } -// DELETETHIS 280! whoa! -/* -//--------------------------------------------------------- -// Part::read -//--------------------------------------------------------- - -void Part::read(Xml& xml, int, bool toTrack) // int newPartOffset - { - int id = -1; - bool containsEvents = false; - uuid_t uuid; - uuid_clear(uuid); - bool uuidvalid = false; - bool clone = false; - - for (;;) { - Xml::Token token = xml.parse(); - const QString& tag = xml.s1(); - switch (token) { - case Xml::Error: - case Xml::End: - return; - case Xml::TagStart: - if (tag == "name") - _name = xml.parse1(); - else if (tag == "poslen") { - PosLen::read(xml, "poslen"); - } - else if (tag == "pos") { - Pos pos; - pos.read(xml, "pos"); // obsolete - setTick(pos.tick()); - } - else if (tag == "len") { - Pos len; - len.read(xml, "len"); // obsolete - setLenTick(len.tick()); - } - else if (tag == "selected") - _selected = xml.parseInt(); - else if (tag == "color") - _colorIndex = xml.parseInt(); - else if (tag == "mute") - _mute = xml.parseInt(); - else if (tag == "event") { - containsEvents = true; - EventType type = Wave; - if (_track->isMidiTrack()) - type = Note; - Event e(type); - e.read(xml); - // stored tickpos for event has absolute value. However internally - // tickpos is relative to start of part, we substract tick(). - // TODO: better handling for wave event - e.move(-tick()); - int tick = e.tick(); - - // Changed by T356. Do not discard events belonging to clone parts, - // at least not yet. A later clone might have a longer, - // fully accommodating part length! - //if ((tick < 0) || (tick >= (int) lenTick())) { - if ((tick < 0) || ( id == -1 && !clone && (tick >= (int)lenTick()) )) - { - //printf("Part::read: warning: event not in part: %d - %d -%d, discarded\n", - printf("Part::read: warning: event at tick:%d not in part:%s, discarded\n", - tick, name().toLatin1().constData()); - } - else { - _events->add(e); -*/ - - - /* - // TODO: This should NOT be done here since the event list - // might be deleted below. Since after reading a part it - // likely (always?) that (msg)AddPart() or (msg)ChangePart() - // will be called (must check if they're ever called BEFORE - // Part::read), then those routines will take care of it, - // they are already coded to do so. - // Note the redundancy of doing it here AND (msg)Add/ChangePart ! - // Try to eliminate this section altogether by verifying that - // (msg)Add/ChangePart (or one of the other routines which add port - // controller values) is always called after Part::read... - if (e.type() == Controller) { - MidiTrack* mt = (MidiTrack*)_track; - int channel = mt->outChannel(); - MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; - // tick is relative to part, controller needs an absolute value hence - // part offset is added. If newPartOffset was given we use that instead of - // the recorded offset! - if (!newPartOffset) - newPartOffset=this->tick(); - - int ctl = e.dataA(); - if(mt->type() == Track::DRUM) - { - // Is it a drum controller event, according to the track port's instrument? - MidiController* mc = mp->drumController(ctl); - if(mc) - { - int note = ctl & 0x7f; - ctl &= ~0xff; - channel = drumMap[note].channel; - mp = &MusEGlobal::midiPorts[drumMap[note].port]; - ctl |= drumMap[note].anote; - } - } - - // Removed by T356 - // check if controller exists - //if (mp->hwCtrlState(channel, e.dataA()) == CTRL_VAL_UNKNOWN) { - // mp->addManagedController(channel, e.dataA()); - // } - - // Changed by T356 - // add controller value - //mp->setCtrl(channel, tick+newPartOffset, e.dataA(), e.dataB()); - mp->setControllerVal(channel, tick+newPartOffset, ctl, e.dataB(), this); - } - */ -/* - } - } - else - xml.unknown("Part::read"); - break; - case Xml::Attribut: - if (tag == "cloneId") - id = xml.s2().toInt(); - else if (tag == "uuid") - { - uuid_parse(xml.s2().toLatin1().constData(), uuid); - if(!uuid_is_null(uuid)) - uuidvalid = true; - } - else if (tag == "isclone") - clone = xml.s2().toInt(); - break; - case Xml::TagEnd: - if (tag == "part") - { -*/ - /* - if (id != -1) - { - - // clone part - if (containsEvents) { - // add to MusEGlobal::cloneList: - //ClonePart cp(_events, id); - ClonePart cp(this, id); - MusEGlobal::cloneList.push_back(cp); - } - else { - // replace event list with clone event - // list - for (iClone i = MusEGlobal::cloneList.begin(); - i != MusEGlobal::cloneList.end(); ++i) { - if (i->id == id) { - delete _events; - //_events = (EventList*)(i->el); - _events = (EventList*)(i->cp->cevents()); - _events->incRef(1); - _events->incARef(1); - //i->cp->chainClone(this); - chainClone((Part*)i->cp, this); - break; - } - } - } - */ - -/* - if(id != -1) - { - // See if the part exists in the clone list. - // The clone list is also the copy/paste clone list. - // Care must be taken to ensure the list is ALWAYS EMPTY - // before loading or dropping parts INTO muse, because the - // current song parts are NOT the same as when the imported parts - // were created, (even if they were created from the current session, - // we should NOT look them up). Always back up the list, clear it, - // read part(s), then restore the list so that paste works after. - Part* cp = 0; - for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) - { - if(i->id == id) - { - cp = (Part*)i->cp; - break; - } - } - // Was a matching part found in the clone list? - if(cp) - { - // Make this part a clone of that part. Use its event list... - delete _events; - _events = (EventList*)(cp->cevents()); - _events->incRef(1); - _events->incARef(1); - chainClone(cp, this); - } - else - { - // No matching part to clone was found in the clone list. - // Does the part contain some events? - //if(containsEvents) - { - // Add the part to the clone list so that subsequent parts - // can look it up and clone from it... - ClonePart ncp(this, id); - MusEGlobal::cloneList.push_back(ncp); - } - // Otherwise this part has no matching part in the clone list - // and no events of its own. Nothing left to do, we now have - // a blank part with the original offset, colour etc. - } - } - else - // If a uuid was found, do the same as above. Using uuids - // allows foolproof rejection of copied parts not found - // in the clone list, particularly when copying parts from - // another instance of muse. - if(uuidvalid) - { - Part* cp = 0; - for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) - { - if(uuid_compare(uuid, i->uuid) == 0) - { - cp = (Part*)i->cp; - break; - } - } - // If a matching part was found, and we want to paste to the original track... - if(cp && !toTrack) - { - // Make sure the track exists (has not been deleted). - if((cp->track()->isMidiTrack() && MusEGlobal::song->midis()->find(cp->track()) != MusEGlobal::song->midis()->end()) || - (cp->track()->type() == Track::WAVE && MusEGlobal::song->waves()->find(cp->track()) != MusEGlobal::song->waves()->end())) - setTrack(cp->track()); - } - // Was a matching part found in the clone list, and was it - // originally a clone part? - if(cp && clone) - { - // Make this part a clone of that part. Use its event list... - delete _events; - _events = (EventList*)(cp->cevents()); - _events->incRef(1); - _events->incARef(1); - // Chain the clone. - // Use the slower function which makes sure it chains to a part - // within a valid (non-deleted) track. - //chainClone(cp, this); - chainClone(this); - } - else - { - // No matching part to clone was found in the clone list. - // Does the part contain some events? - //if(containsEvents) - { - // Add the part to the clone list so that subsequent parts - // can look it up and clone from it... - ClonePart ncp(this); - // New ClonePart creates its own uuid, but we need to replace it. - uuid_copy(ncp.uuid, uuid); - MusEGlobal::cloneList.push_back(ncp); - } - } - } - return; - } - default: - break; - } - } - } -*/ //--------------------------------------------------------- // writeFont @@ -910,8 +514,9 @@ { MusEGlobal::cloneList.clear(); for (;;) { - if (MusEGlobal::muse->progress) + if (MusEGlobal::muse->progress) { MusEGlobal::muse->progress->setValue(MusEGlobal::muse->progress->value()+1); + } Xml::Token token; token = xml.parse(); @@ -937,7 +542,7 @@ setRecord(xml.parseInt()); else if (tag == "solo") soloFlag = xml.parseInt(); - else if (tag == "type") // Obsolete. + else if (tag == "type") // Obsolete. xml.parseInt(); else if (tag == "recmode") _recMode = xml.parseInt(); @@ -953,8 +558,23 @@ _follow = FollowMode(xml.parseInt()); else if (tag == "sampleRate") { int sRate = xml.parseInt(); - if (!isTemplate && MusEGlobal::audioDevice->deviceType() != AudioDevice::DUMMY_AUDIO && sRate != MusEGlobal::sampleRate) - QMessageBox::warning(MusEGlobal::muse,"Wrong sample rate", "The sample rate in this project and the current system setting differs, the project may not work as intended!"); + if (!isTemplate && MusEGlobal::audioDevice->deviceType() != AudioDevice::DUMMY_AUDIO && sRate != MusEGlobal::sampleRate) { + + if (MusEGlobal::audioDevice->deviceType() == AudioDevice::RTAUDIO_AUDIO) { + // restart audio with selected sample rate + MusEGlobal::sampleRate = sRate; + MusEGlobal::audioDevice->stop(); + MusEGlobal::audioDevice->start(0); + if (MusEGlobal::sampleRate == sRate) { + QMessageBox::warning(MusEGlobal::muse,"Sample rate", "Changing sample rate to song setting " + QString::number(sRate) + "!"); + } else { + QMessageBox::warning(MusEGlobal::muse,"Sample rate", "Tried changing sample rate to song setting " + + QString::number(sRate) + " but driver set it to " + QString::number(MusEGlobal::sampleRate) + "!"); + } + } else { + QMessageBox::warning(MusEGlobal::muse,"Wrong sample rate", "The sample rate in this project and the current system setting differs, the project may not work as intended!"); + } + } } else if (tag == "tempolist") { MusEGlobal::tempomap.read(xml); @@ -970,10 +590,11 @@ track->read(xml); insertTrack0(track, -1); } - else if (tag == "drumtrack") { + else if (tag == "drumtrack") { // Old drumtrack is obsolete. MidiTrack* track = new MidiTrack(); - track->setType(Track::DRUM); + track->setType(Track::NEW_DRUM); track->read(xml); + track->convertToType(Track::NEW_DRUM); // Convert the notes and controllers. insertTrack0(track, -1); } else if (tag == "newdrumtrack") { @@ -986,7 +607,7 @@ MusECore::WaveTrack* track = new MusECore::WaveTrack(); track->read(xml); insertTrack0(track,-1); - // Now that the track has been added to the lists in insertTrack2(), + // Now that the track has been added to the lists in insertTrack2(), // OSC can find the track and its plugins, and start their native guis if required... track->showPendingPluginNativeGuis(); } @@ -1063,8 +684,8 @@ } } dirty = false; - - // Since cloneList is also used for copy/paste operations, + + // Since cloneList is also used for copy/paste operations, // clear the copy clone list again. MusEGlobal::cloneList.clear(); } @@ -1114,11 +735,11 @@ // Write midi device routing. for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) (*i)->writeRouting(level, xml); - + // Write midi port routing. for (int i = 0; i < MIDI_PORTS; ++i) MusEGlobal::midiPorts[i].writeRouting(level, xml); - + MusEGlobal::tempomap.write(level, xml); AL::sigmap.write(level, xml); MusEGlobal::keymap.write(level, xml); @@ -1127,7 +748,7 @@ writeDrumMap(level, xml, false); MusEGlobal::global_drum_ordering.write(level, xml); xml.tag(level, "/song"); - + // Restore backup of the clone list, to retain any 'copy' items, // so that pasting works properly after. MusEGlobal::cloneList.clear(); @@ -1157,7 +778,12 @@ { int trackIdx, partIdx; sscanf(tag.toLatin1().constData(), "%d:%d", &trackIdx, &partIdx); - MusECore::Track* track = MusEGlobal::song->tracks()->index(trackIdx); + MusECore::Track* track = NULL; + //check if track index is in bounds before getting it (danvd) + if(trackIdx < (int)MusEGlobal::song->tracks()->size()) + { + track = MusEGlobal::song->tracks()->index(trackIdx); + } if (track) part = track->parts()->find(partIdx); } @@ -1208,7 +834,7 @@ startPianoroll(pl); toplevels.back()->readStatus(xml); pl = new MusECore::PartList; - } + } } else if (tag == "scoreedit") { MusEGui::ScoreEdit* score = new MusEGui::ScoreEdit(this, 0, _arranger->cursorValue()); @@ -1224,7 +850,7 @@ startDrumEditor(pl); toplevels.back()->readStatus(xml); pl = new MusECore::PartList; - } + } } else if (tag == "listeditor") { if(!pl->empty()) @@ -1232,7 +858,7 @@ startListEditor(pl); toplevels.back()->readStatus(xml); pl = new MusECore::PartList; - } + } } else if (tag == "master") { startMasterEditor(); @@ -1264,7 +890,7 @@ startWaveEditor(pl); toplevels.back()->readStatus(xml); pl = new MusECore::PartList; - } + } } else if (tag == "cliplist") { startClipList(true); @@ -1415,9 +1041,9 @@ void MusE::read(MusECore::Xml& xml, bool doReadMidiPorts, bool isTemplate) { bool skipmode = true; - + writeTopwinState=true; - + for (;;) { if (progress) progress->setValue(progress->value()+1); @@ -1442,7 +1068,7 @@ * parts (but not all) of his global config (like MDI/SDI, toolbar states etc.) * with the data stored in the song. (it IS stored there. dunny why, i find it pretty * senseless.) - * + * * If you've a problem which seems to be solved by replacing "false" with "true", i've * a better solution for you: go into conf.cpp, in void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig) * (around line 525), look for a comment like this: @@ -1455,14 +1081,19 @@ * (it might happen that it formerly worked because it was written to the song instead * of the global config by mistake, and now isn't loaded anymore. write it to the * correct location.) - * + * * -- flo93 */ else if (tag == "song") { MusEGlobal::song->read(xml, isTemplate); MusEGlobal::audio->msgUpdateSoloStates(); - } + // Inform the rest of the app that the song (may) have changed, using these flags. + // After this function is called, the caller can do a general Song::update() MINUS these flags, + // like in MusE::loadProjectFile1() - the only place calling so far, as of this writing. + // Some existing windows need this, like arranger, some don't which are dynamically created after this. + MusEGlobal::song->update(SC_TRACK_INSERTED); + } else if (tag == "midiport") readMidiport(xml); else if (tag == "Controller") { // obsolete @@ -1475,13 +1106,13 @@ else if (tag == "toplevels") readToplevels(xml); else if (tag == "no_toplevels") - { + { if (!isTemplate) writeTopwinState=false; - + xml.skip("no_toplevels"); } - + else xml.unknown("muse"); break; @@ -1493,6 +1124,37 @@ } break; case MusECore::Xml::TagEnd: + if(!xml.isVersionEqualToLatest()) + { + fprintf(stderr, "\n***WARNING***\nLoaded file version is %d.%d\nCurrent version is %d.%d\n" + "Conversions may be applied if file is saved!\n\n", + xml.majorVersion(), xml.minorVersion(), + xml.latestMajorVersion(), xml.latestMinorVersion()); + // Cannot construct QWidgets until QApplication created! + // Check MusEGlobal::muse which is created shortly after the application... + if(MusEGlobal::muse && MusEGlobal::config.warnOnFileVersions) + { + QString txt = tr("File version is %1.%2\nCurrent version is %3.%4\n" + "Conversions may be applied if file is saved!") + .arg(xml.majorVersion()).arg(xml.minorVersion()) + .arg(xml.latestMajorVersion()).arg(xml.latestMinorVersion()); + QMessageBox* mb = new QMessageBox(QMessageBox::Warning, + tr("Opening file"), + txt, + QMessageBox::Ok, MusEGlobal::muse); + QCheckBox* cb = new QCheckBox(tr("Do not warn again")); + cb->setChecked(!MusEGlobal::config.warnOnFileVersions); + mb->setCheckBox(cb); + mb->exec(); + if(!mb->checkBox()->isChecked() != MusEGlobal::config.warnOnFileVersions) + { + MusEGlobal::config.warnOnFileVersions = !mb->checkBox()->isChecked(); + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + //MusEGlobal::muse->changeConfig(true); // Save settings? No, wait till close. + } + delete mb; + } + } if (!skipmode && tag == "muse") return; default: @@ -1512,7 +1174,8 @@ xml.header(); int level = 0; - xml.tag(level++, "muse version=\"2.0\""); + xml.nput(level++, "\n", xml.latestMajorVersion(), xml.latestMinorVersion()); + writeConfiguration(level, xml); writeStatusMidiInputTransformPlugins(level, xml); diff -Nru muse-2.1.2/muse/song.h muse-3.0.2+ds1/muse/song.h --- muse-2.1.2/muse/song.h 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/song.h 2018-01-06 20:31:35.000000000 +0000 @@ -29,6 +29,7 @@ #include #include +#include #include "type_defs.h" #include "pos.h" @@ -38,6 +39,7 @@ #include "undo.h" #include "track.h" #include "synth.h" +#include "operations.h" class QAction; class QFont; @@ -46,7 +48,6 @@ namespace MusECore { class SynthI; -struct MidiMsg; class Event; class Xml; class Sequencer; @@ -58,6 +59,7 @@ class MarkerList; class Marker; class SNode; +class RouteList; struct AudioMsg; @@ -68,37 +70,55 @@ class AudioPort; class AudioDevice; -#define SC_TRACK_INSERTED 1 -#define SC_TRACK_REMOVED 2 -#define SC_TRACK_MODIFIED 4 -#define SC_PART_INSERTED 8 -#define SC_PART_REMOVED 0x10 -#define SC_PART_MODIFIED 0x20 -#define SC_EVENT_INSERTED 0x40 -#define SC_EVENT_REMOVED 0x80 -#define SC_EVENT_MODIFIED 0x100 -#define SC_SIG 0x200 // timing signature -#define SC_TEMPO 0x400 // tempo map changed -#define SC_MASTER 0x800 // master flag changed -#define SC_SELECTION 0x1000 -#define SC_MIDI_CONTROLLER 0x2000 // must update midi mixer -#define SC_MUTE 0x4000 -#define SC_SOLO 0x8000 -#define SC_RECFLAG 0x10000 -#define SC_ROUTE 0x20000 -#define SC_CHANNELS 0x40000 -#define SC_CONFIG 0x80000 // midiPort-midiDevice -#define SC_DRUMMAP 0x100000 // must update drumeditor -#define SC_MIXER_VOLUME 0x200000 -#define SC_MIXER_PAN 0x400000 -#define SC_AUTOMATION 0x800000 -#define SC_AUX 0x1000000 // mixer aux changed -#define SC_RACK 0x2000000 // mixer rack changed -#define SC_CLIP_MODIFIED 0x4000000 -#define SC_MIDI_CONTROLLER_ADD 0x8000000 // a hardware midi controller was added or deleted -#define SC_MIDI_TRACK_PROP 0x10000000 // a midi track's properties changed (channel, compression etc) -#define SC_KEY 0x40000000 // key map changed -#define SC_EVERYTHING -1 // global update +// Song changed flags: +// These are flags, usually passed by connecting to the songChanged() signal, +// which inform that various things have changed and appropriate action should +// be taken (redraw, refill lists etc.) upon the signal's reception. +// NOTE: Use the SongChangedFlags_t typedef in type_defs.h to support all the bits. + +#define SC_TRACK_INSERTED 1 +#define SC_TRACK_REMOVED 2 +#define SC_TRACK_MODIFIED 4 +#define SC_PART_INSERTED 8 +#define SC_PART_REMOVED 0x10 +#define SC_PART_MODIFIED 0x20 +#define SC_EVENT_INSERTED 0x40 +#define SC_EVENT_REMOVED 0x80 +#define SC_EVENT_MODIFIED 0x100 +#define SC_SIG 0x200 // timing signature +#define SC_TEMPO 0x400 // tempo map changed +#define SC_MASTER 0x800 // master flag changed +#define SC_SELECTION 0x1000 // event selection. part and track selection have their own. +#define SC_MUTE 0x2000 // A track's mute or off state changed. +#define SC_SOLO 0x4000 +#define SC_RECFLAG 0x8000 +#define SC_ROUTE 0x10000 // A route was added, changed, or deleted. Or a midi track's out channel/port was changed. +#define SC_CHANNELS 0x20000 +#define SC_CONFIG 0x40000 // midiPort-midiDevice +#define SC_DRUMMAP 0x80000 // must update drumeditor +#define SC_MIDI_INSTRUMENT 0x100000 // A midi port or device's instrument has changed +#define SC_AUDIO_CONTROLLER 0x200000 // An audio controller value was added deleted or modified. +#define SC_AUTOMATION 0x400000 // A track's automation mode setting changed (off, read, touch, write etc). +#define SC_AUX 0x800000 // A mixer aux was added or deleted. Not adjusted. +#define SC_RACK 0x1000000 // mixer rack changed +#define SC_CLIP_MODIFIED 0x2000000 +#define SC_MIDI_CONTROLLER_ADD 0x4000000 // a hardware midi controller was added or deleted +// SC_MIDI_TRACK_PROP: A midi track's properties changed (name, thru etc). +// For fairly 'static' properties, not frequently changing transp del compr velo or len, +// nor output channel/port (use SC_ROUTE). +#define SC_MIDI_TRACK_PROP 0x8000000 +#define SC_PART_SELECTION 0x10000000 // part selection changed +#define SC_KEY 0x20000000 // key map changed +#define SC_TRACK_SELECTION 0x40000000 // track selection changed +#define SC_PORT_ALIAS_PREFERENCE 0x80000000 // (Jack) port alias viewing preference has changed +#define SC_ROUTER_CHANNEL_GROUPING 0x100000000 // Router channel grouping changed +#define SC_AUDIO_CONTROLLER_LIST 0x200000000 // An audio controller list was added deleted or modified. +#define SC_PIANO_SELECTION 0x400000000 // Piano keyboard selected note changed. +#define SC_DRUM_SELECTION 0x800000000 // Drum list selected note changed. +#define SC_TRACK_REC_MONITOR 0x1000000000 // Audio or midi track's record monitor changed. +#define SC_TRACK_MOVED 0x2000000000 // Audio or midi track's position in track list or mixer changed. +#define SC_TRACK_RESIZED 0x4000000000 // Audio or midi track was resized in the arranger. +#define SC_EVERYTHING -1 // global update #define REC_NOTE_FIFO_SIZE 16 @@ -142,10 +162,21 @@ UndoList* undoList; UndoList* redoList; + // New items created in GUI thread awaiting addition in audio thread. + PendingOperationList pendingOperations; + Pos pos[3]; Pos _vcpos; // virtual CPOS (locate in progress) MarkerList* _markerList; + float _fCpuLoad; + float _fDspLoad; + long _xRunsCount; + + // Receives events from any threads. For now, specifically for creating new + // controllers in the gui thread and adding them safely to the controller lists. + static LockFreeMPSCRingBuffer *_ipcInEventBuffers; + bool _masterFlag; bool loopFlag; bool punchinFlag; @@ -167,13 +198,38 @@ QStringList deliveredScriptNames; QStringList userScriptNames; + // These are called from non-RT thread operations execution stage 1. + void insertTrackOperation(Track* track, int idx, PendingOperationList& ops); + void removeTrackOperation(Track* track, PendingOperationList& ops); + bool addEventOperation(const Event&, Part*, bool do_port_ctrls = true, bool do_clone_port_ctrls = true); + void changeEventOperation(const Event&, const Event&, Part*, bool do_port_ctrls = true, bool do_clone_port_ctrls = true); + void deleteEventOperation(const Event&, Part*, bool do_port_ctrls = true, bool do_clone_port_ctrls = true); + public: Song(const char* name = 0); ~Song(); - bool applyOperationGroup(Undo& group, bool doUndo=true); - void informAboutNewParts(const std::map< Part*, std::set >&); - void informAboutNewParts(Part* orig, Part* p1, Part* p2=NULL, Part* p3=NULL, Part* p4=NULL, Part* p5=NULL, Part* p6=NULL, Part* p7=NULL, Part* p8=NULL, Part* p9=NULL); + /** It is not allowed nor checked(!) to AddPart a clone, and + * to AddEvent/DeleteEvent/ModifyEvent/SelectEvent events which + * would need to be replicated to the newly added clone part! + */ + bool applyOperationGroup(Undo& group, bool doUndo=true); // group may be changed! prepareOperationGroup is called on group! + bool applyOperation(const UndoOp& op, bool doUndo=true); + + /** this sends emits a signal to each MidiEditor or whoever is interested. + * For each part which is 1) opened in this MidiEditor and 2) which is + * a key in this map, the Editors shall no more edit this part, but instead + * all parts in the_map[old_part] (which is a std::set) + */ + void informAboutNewParts(const std::map< const Part*, std::set >&); + /** this sends emits a signal to each MidiEditor or whoever is interested. + * For each part which is 1) opened in this MidiEditor and 2) which is + * a key in this map, the Editors shall no more edit this part, but instead + * all parts in the_map[old_part] (which is a std::set) + * this is a special case of the general function, which only replaces one part + * by up to nine different. + */ + void informAboutNewParts(const Part* orig, const Part* p1, const Part* p2=NULL, const Part* p3=NULL, const Part* p4=NULL, const Part* p5=NULL, const Part* p6=NULL, const Part* p7=NULL, const Part* p8=NULL, const Part* p9=NULL); void putEvent(int pv); void endMsgCmd(); @@ -243,59 +299,65 @@ bool click() const { return _click; } bool quantize() const { return _quantize; } void setStopPlay(bool); - void stopRolling(); + // Fills operations if given, otherwise creates and executes its own operations list. + void stopRolling(Undo* operations = 0); void abortRolling(); + float cpuLoad() const { return _fCpuLoad; } + float dspLoad() const { return _fDspLoad; } + long xRunsCount() const { return _xRunsCount; } + //----------------------------------------- // access tempomap/sigmap (Mastertrack) //----------------------------------------- unsigned len() const { return _len; } - void setLen(unsigned l); // set songlen in ticks + void setLen(unsigned l, bool do_update = true); // set songlen in ticks int roundUpBar(int tick) const; int roundUpBeat(int tick) const; int roundDownBar(int tick) const; void initLen(); - void tempoChanged(); //----------------------------------------- // event manipulations //----------------------------------------- - void cmdAddRecordedWave(WaveTrack* track, Pos, Pos); - void cmdAddRecordedEvents(MidiTrack*, EventList*, unsigned); - bool addEvent(Event&, Part*); - void changeEvent(Event&, Event&, Part*); - void deleteEvent(Event&, Part*); - void cmdChangeWave(QString original, QString tmpfile, unsigned sx, unsigned ex); - void remapPortDrumCtrlEvents(int mapidx, int newnote, int newchan, int newport); - void changeAllPortDrumCtrlEvents(bool add, bool drumonly = false); + void cmdAddRecordedWave(WaveTrack* track, Pos, Pos, Undo& operations); + void cmdAddRecordedEvents(MidiTrack*, const EventList&, unsigned, Undo& operations); + + // May be called from GUI or audio thread. Also selects events in clone parts. Safe for now because audio/midi processing doesn't + // depend on it, and all calls to part altering functions from GUI are synchronized with (wait for) audio thread. + void selectEvent(Event&, Part*, bool select); + void selectAllEvents(Part*, bool select); // See selectEvent(). + + void cmdChangeWave(const Event& original, QString tmpfile, unsigned sx, unsigned ex); + void remapPortDrumCtrlEvents(int mapidx, int newnote, int newchan, int newport); // called from GUI thread + void changeAllPortDrumCtrlEvents(bool add, bool drumonly = false); // called from GUI thread - void addACEvent(AudioTrack* t, int acid, int frame, double val); - void changeACEvent(AudioTrack* t, int acid, int frame, int newFrame, double val); void addExternalTempo(const TempoRecEvent& e) { _tempoFifo.put(e); } //----------------------------------------- // part manipulations //----------------------------------------- - void cmdResizePart(Track* t, Part* p, unsigned int size, bool doClones=false); - void cmdSplitPart(Track* t, Part* p, int tick); - void cmdGluePart(Track* t, Part* p); + void cmdResizePart(Track* t, Part* p, unsigned int size, bool doMove, int newPos, bool doClones=false); // called from GUI thread, calls applyOperationGroup. FIXME TODO: better move that into functions.cpp or whatever. void addPart(Part* part); void removePart(Part* part); void changePart(Part*, Part*); - PartList* getSelectedMidiParts() const; + + + PartList* getSelectedMidiParts() const; // FIXME TODO move functionality into function.cpp PartList* getSelectedWaveParts() const; - bool msgRemoveParts(); - void cmdChangePart(Part* oldPart, Part* newPart, bool doCtrls, bool doClones); - void cmdRemovePart(Part* part); - void cmdAddPart(Part* part); int arrangerRaster() { return _arrangerRaster; } // Used by Song::cmdAddRecordedWave to snap new wave parts void setArrangerRaster(int r) { _arrangerRaster = r; } // Used by Arranger snap combo box +private: + void normalizePart(MusECore::Part *part); +public: + void normalizeWaveParts(Part *partCursor = NULL); + //----------------------------------------- // track manipulations //----------------------------------------- @@ -309,49 +371,67 @@ AuxList* auxs() { return &_auxs; } SynthIList* syntis() { return &_synthIs; } - void cmdRemoveTrack(Track* track); - void removeTrack0(Track* track); - void removeTrack1(Track* track); - void removeTrack2(Track* track); - void removeTrack3(Track* track); - void removeMarkedTracks(); - //void changeTrack(Track* oldTrack, Track* newTrack); DELETETHIS - MidiTrack* findTrack(const Part* part) const; + Track* findTrack(const Part* part) const; Track* findTrack(const QString& name) const; - void swapTracks(int i1, int i2); - void setChannelMute(int channel, bool flag); - void setRecordFlag(Track*, bool); + bool trackExists(Track* t) const { return _tracks.find(t) != _tracks.end(); } + + void setRecordFlag(Track*, bool val, Undo* operations = 0); void insertTrack0(Track*, int idx); void insertTrack1(Track*, int idx); void insertTrack2(Track*, int idx); void insertTrack3(Track*, int idx); - void deselectTracks(); + + // The currently selected track (in a multi-selection the last one selected), or null. + Track* selectedTrack() const { return _tracks.currentSelection(); } + // Total number of selected tracks. + int countSelectedTracks() const { return _tracks.countSelected(); } + // Selects or deselects all tracks. + void selectAllTracks(bool select) + { + _tracks.selectAll(select); + if(!select) // Not essential, but if unselecting ALL tracks, clear the static counter. + Track::clearSelectionOrderCounter(); + } + void readRoute(Xml& xml); void recordEvent(MidiTrack*, Event&); - void msgInsertTrack(Track* track, int idx, bool u = true); + // Enable all track and plugin controllers, and synth controllers if applicable, which are NOT in AUTO_WRITE mode. + void reenableTouchedControllers(); void clearRecAutomation(bool clearList); - void processAutomationEvents(); + // Fills operations if given, otherwise creates and executes its own operations list. + void processAutomationEvents(Undo* operations = 0); void processMasterRec(); int execAutomationCtlPopup(AudioTrack*, const QPoint&, int); int execMidiAutomationCtlPopup(MidiTrack*, MidiPart*, const QPoint&, int); - void connectJackRoutes(AudioTrack* track, bool disconnect); + bool connectJackRoutes(const MusECore::Route& src, const MusECore::Route& dst, bool disconnect = false); + void connectAudioPorts(); + void connectMidiPorts(); + void connectAllPorts() { connectAudioPorts(); connectMidiPorts(); } void updateSoloStates(); + // Put an event into the IPC event ring buffer for the gui thread to process. Returns true on success. + // NOTE: Although the ring buffer is multi-writer, call this from audio thread only for now, unless + // you know what you are doing because the thread needs to ask whether the controller exists before + // calling, and that may not be safe from threads other than gui or audio. + bool putIpcInEvent(const MidiPlayEvent& ev); + // Process any special IPC audio thread - to - gui thread messages. Called by gui thread only. + // Returns true on success. + bool processIpcInEventBuffers(); //----------------------------------------- - // undo, redo + // undo, redo, operation groups //----------------------------------------- void startUndo(); void endUndo(MusECore::SongChangedFlags_t); - void undoOp(UndoOp::UndoType type, const char* changedFile, const char* changeData, int startframe, int endframe); + void undoOp(UndoOp::UndoType type, const Event& changedEvent, const QString& changeData, int startframe, int endframe); // FIXME FINDMICHJETZT what's that?! remove it! - bool doUndo1(); - void doUndo2(); - void doUndo3(); - bool doRedo1(); - void doRedo2(); - void doRedo3(); + void executeOperationGroup1(Undo& operations); + void executeOperationGroup2(Undo& operations); + void executeOperationGroup3(Undo& operations); + void revertOperationGroup1(Undo& operations); + void revertOperationGroup2(Undo& operations); + void revertOperationGroup3(Undo& operations); void addUndo(UndoOp i); void setUndoRedoText(); @@ -362,8 +442,6 @@ SynthI* createSynthI(const QString& sclass, const QString& label = QString(), Synth::Type type = Synth::SYNTH_TYPE_END, Track* insertAt = 0); - void rescanAlsaPorts(); - //----------------------------------------- // Debug //----------------------------------------- @@ -377,7 +455,7 @@ #ifdef ENABLE_PYTHON virtual bool event (QEvent* e ); #endif - void executeScript(const char* scriptfile, PartList* parts, int quant, bool onlyIfSelected); + void executeScript(QWidget *parent, const char* scriptfile, PartList* parts, int quant, bool onlyIfSelected); public slots: void seekTo(int tick); @@ -410,11 +488,20 @@ void setQuantize(bool val); void panic(); void seqSignal(int fd); - Track* addTrack(Undo& operations, Track::TrackType type, Track* insertAt = 0); + Track* addTrack(Track::TrackType type, Track* insertAt = 0); Track* addNewTrack(QAction* action, Track* insertAt = 0); void duplicateTracks(); QString getScriptPath(int id, bool delivered); void populateScriptMenu(QMenu* menuPlugins, QObject* receiver); + void setDirty() { emit sigDirty(); } + + /* restarts recording from last start position + * if discard is true (default) then + * recording will start on existing tracks, + * else new copies of armed tracks will be created + * and current armed tracks will be muted and unarmed + * Called from gui thread only. */ + void restartRecording(bool discard = true); signals: void songChanged(MusECore::SongChangedFlags_t); @@ -427,10 +514,10 @@ void clickChanged(bool); void quantizeChanged(bool); void markerChanged(int); - void midiPortsChanged(); void midiNote(int pitch, int velo); void controllerChanged(MusECore::Track*, int); - void newPartsCreated(const std::map< MusECore::Part*, std::set >&); + void newPartsCreated(const std::map< const MusECore::Part*, std::set >&); + void sigDirty(); }; } // namespace MusECore diff -Nru muse-2.1.2/muse/steprec.cpp muse-3.0.2+ds1/muse/steprec.cpp --- muse-2.1.2/muse/steprec.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/steprec.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -55,7 +55,7 @@ } } -void StepRec::record(Part* part, int pitch, int len, int step, int velo, bool ctrl, bool shift, int incoming_pitch) +void StepRec::record(const Part* part, int pitch, int len, int step, int velo, bool ctrl, bool shift, int incoming_pitch) { unsigned tick = MusEGlobal::song->cpos(); unsigned lasttick=0; @@ -76,12 +76,12 @@ chord_timer->stop(); // extend len of last note? - EventList* events = part->events(); + const EventList& events = part->events(); if (ctrl) { - for (iEvent i = events->begin(); i != events->end(); ++i) + for (ciEvent i = events.begin(); i != events.end(); ++i) { - Event ev = i->second; + const Event& ev = i->second; if (ev.isNote() && ev.pitch() == pitch && ((ev.tick() + ev.lenTick() + part->tick()) == tick)) { Event e = ev.clone(); @@ -106,13 +106,13 @@ // if we would find a note after part->lenTick(), the above "if" // avoids this. this has to be avoided because then part->hasHiddenEvents() is true // which results in forbidding any action beyond its end - EventRange range = events->equal_range(tick - part->tick()); - for (iEvent i = range.first; i != range.second; ++i) + EventRange range = events.equal_range(tick - part->tick()); + for (ciEvent i = range.first; i != range.second; ++i) { - Event ev = i->second; + const Event& ev = i->second; if (ev.isNote() && ev.pitch() == pitch) { - MusEGlobal::audio->msgDeleteEvent(ev, part, true, false, false); + MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent,ev, part, true,true)); if (!shift) { @@ -140,7 +140,7 @@ chord_timer->start(); } - goto steprec_record_foot; // this is actually unneccessary, but for clarity + goto steprec_record_foot; // this is actually unnecessary, but for clarity } else // equals if (incoming_pitch==MusEGlobal::rcSteprecNote) { @@ -161,18 +161,18 @@ // extend len of last note(s) using std::set; - set extend_set; - EventList* events = part->events(); - for (iEvent i = events->begin(); i != events->end(); ++i) + set extend_set; + const EventList& events = part->events(); + for (ciEvent i = events.begin(); i != events.end(); ++i) { - Event& ev = i->second; + const Event& ev = i->second; if (ev.isNote() && note_held_down[ev.pitch()] && ((ev.tick() + ev.lenTick() + part->tick()) == tick)) extend_set.insert(&ev); } - for (set::iterator it=extend_set.begin(); it!=extend_set.end(); it++) + for (set::iterator it=extend_set.begin(); it!=extend_set.end(); it++) { - Event& ev=**it; + const Event& ev=**it; Event e = ev.clone(); e.setLenTick(ev.lenTick() + len); operations.push_back(UndoOp(UndoOp::ModifyEvent,e, ev, part, false, false)); @@ -185,7 +185,7 @@ } lasttick=tick+len - part->tick(); - goto steprec_record_foot; // this is actually unneccessary, but for clarity + goto steprec_record_foot; // this is actually unnecessary, but for clarity } else // equals if (!held_notes) { diff -Nru muse-2.1.2/muse/steprec.h muse-3.0.2+ds1/muse/steprec.h --- muse-2.1.2/muse/steprec.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/steprec.h 2017-12-04 21:01:18.000000000 +0000 @@ -37,7 +37,7 @@ public: StepRec(bool* note_held_down_array); - void record(Part* part, int recorded_pitch, int len, int step, int velo=80, bool ctrl=false, bool shift=false, int incoming_pitch=1337); + void record(const Part* part, int recorded_pitch, int len, int step, int velo=80, bool ctrl=false, bool shift=false, int incoming_pitch=1337); private slots: void timeout(); diff -Nru muse-2.1.2/muse/stringparam.cpp muse-3.0.2+ds1/muse/stringparam.cpp --- muse-2.1.2/muse/stringparam.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/stringparam.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -77,7 +77,7 @@ case Xml::End: return; case Xml::TagStart: - xml.unknown(name.toAscii().constData()); + xml.unknown(name.toLatin1().constData()); break; case Xml::Attribut: if(tag == "name") @@ -86,7 +86,7 @@ if(tag == "val") value = xml.s2(); else - xml.unknown(name.toAscii().constData()); + xml.unknown(name.toLatin1().constData()); break; case Xml::TagEnd: if(tag == name) diff -Nru muse-2.1.2/muse/structure.cpp muse-3.0.2+ds1/muse/structure.cpp --- muse-2.1.2/muse/structure.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/structure.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,343 +0,0 @@ -//========================================================= -// MusE -// Linux Music Editor -// $Id: structure.cpp,v 1.113.2.68 2009/12/21 14:51:51 spamatica Exp $ -// -// (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Robert Jonsson (rj@spamatica.se) -// -// 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 -#include "app.h" -#include "track.h" -#include "song.h" -#include "tempo.h" -#include "al/sig.h" -#include "keyevent.h" -#include "audio.h" -#include "marker/marker.h" -#include "structure.h" -#include "globals.h" - -#include -using std::set; - -namespace MusECore { - -//--------------------------------------------------------- -// adjustGlobalLists -// helper that adjusts tempo, sig, key and marker -// lists everything from startPos is adjusted -// 'diff' number of ticks. -//--------------------------------------------------------- - -void adjustGlobalLists(Undo& operations, int startPos, int diff) -{ - const TempoList* t = &MusEGlobal::tempomap; - const AL::SigList* s = &AL::sigmap; - const KeyList* k = &MusEGlobal::keymap; - - criTEvent it = t->rbegin(); - AL::criSigEvent is = s->rbegin(); - criKeyEvent ik = k->rbegin(); - - // key - for (; ik != k->rend(); ik++) { - const KeyEvent &ev = (KeyEvent)ik->second; - int tick = ev.tick; - int key = ev.key; - if (tick < startPos ) - break; - - if (tick > startPos && tick +diff < startPos ) { // remove - operations.push_back(UndoOp(UndoOp::DeleteKey, tick, key)); - } - else { - operations.push_back(UndoOp(UndoOp::DeleteKey,tick, key)); - operations.push_back(UndoOp(UndoOp::AddKey,tick+diff, key)); - } - } - - // tempo - for (; it != t->rend(); it++) { - const TEvent* ev = (TEvent*)it->second; - int tick = ev->tick; - int tempo = ev->tempo; - if (tick < startPos ) - break; - - if (tick > startPos && tick +diff < startPos ) { // remove - operations.push_back(UndoOp(UndoOp::DeleteTempo,tick, tempo)); - } - else { - operations.push_back(UndoOp(UndoOp::DeleteTempo,tick, tempo)); - operations.push_back(UndoOp(UndoOp::AddTempo,tick+diff, tempo)); - } - } - - // sig - for (; is != s->rend(); is++) { - const AL::SigEvent* ev = (AL::SigEvent*)is->second; - int tick = ev->tick; - if (tick < startPos ) - break; - - int z = ev->sig.z; - int n = ev->sig.n; - if (tick > startPos && tick +diff < startPos ) { // remove - operations.push_back(UndoOp(UndoOp::DeleteSig,tick, z, n)); - } - else { - operations.push_back(UndoOp(UndoOp::DeleteSig,tick, z, n)); - operations.push_back(UndoOp(UndoOp::AddSig,tick+diff, z, n)); - } - } - - MarkerList *markerlist = MusEGlobal::song->marker(); - for(iMarker i = markerlist->begin(); i != markerlist->end(); ++i) - { - Marker* m = &i->second; - int tick = m->tick(); - if (tick > startPos) - { - if (tick + diff < startPos ) { // these ticks should be removed - operations.push_back(UndoOp(UndoOp::ModifyMarker, 0, m)); - } else { - Marker *newMarker = new Marker(); - *newMarker = *m; - newMarker->setTick(tick + diff); - operations.push_back(UndoOp(UndoOp::ModifyMarker, newMarker, m)); - } - } - } - -} - -//--------------------------------------------------------- -// globalCut -// - remove area between left and right locator -// - cut master track -//--------------------------------------------------------- - -void globalCut(bool onlySelectedTracks) - { - int lpos = MusEGlobal::song->lpos(); - int rpos = MusEGlobal::song->rpos(); - if ((lpos - rpos) >= 0) - return; - - Undo operations; - TrackList* tracks = MusEGlobal::song->tracks(); - - for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { - Track* track = *it; - if (track == 0 || (onlySelectedTracks && !track->selected())) - continue; - PartList* pl = track->parts(); - for (iPart p = pl->begin(); p != pl->end(); ++p) { - Part* part = p->second; - int t = part->tick(); - int l = part->lenTick(); - if (t + l <= lpos) - continue; - if ((t >= lpos) && ((t+l) <= rpos)) { - operations.push_back(UndoOp(UndoOp::DeletePart,part)); - } - else if ((t < lpos) && ((t+l) > lpos) && ((t+l) <= rpos)) { - // remove part tail - int len = lpos - t; - Part *nPart; - if (track->isMidiTrack()) - nPart = new MidiPart(*(MidiPart*)part); - else - nPart = new WavePart(*(WavePart*)part); - - nPart->setLenTick(len); - // cut Events in nPart - EventList* el = nPart->events(); - for (iEvent ie = el->lower_bound(len); ie != el->end(); ++ie) - operations.push_back(UndoOp(UndoOp::DeleteEvent,ie->second, nPart, false, false)); - - operations.push_back(UndoOp(UndoOp::ModifyPart,part, nPart, true, true)); - } - else if ((t < lpos) && ((t+l) > lpos) && ((t+l) > rpos)) { - //---------------------- - // remove part middle - //---------------------- - Part* p1; - Part* p2; - Part* p3; - track->splitPart(part, lpos, p1, p2); - delete p2; - track->splitPart(part, rpos, p2, p3); - delete p2; - p3->setTick(lpos); - p1->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ - p3->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ - - // Indicate no undo, and do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart,part, p1, true, true)); - operations.push_back(UndoOp(UndoOp::AddPart,p3)); - } - else if ((t >= lpos) && (t < rpos) && (t+l) > rpos) { - // remove part head - - Part* p1; - Part* p2; - track->splitPart(part, rpos, p1, p2); - delete p1; - p2->setTick(lpos); - p2->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ - operations.push_back(UndoOp(UndoOp::ModifyPart,part, p2, true, true)); - } - else if (t >= rpos) { - // move part to the left - Part *nPart; - if (track->isMidiTrack()) - nPart = new MidiPart(*(MidiPart*)part); - else - nPart = new WavePart(*(WavePart*)part); - int nt = part->tick(); - nPart->setTick(nt - (rpos -lpos)); - // Indicate no undo, and do port controller values but not clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart,part, nPart, true, false)); - } - } - } - int diff = lpos - rpos; - adjustGlobalLists(operations, lpos, diff); - - MusEGlobal::song->applyOperationGroup(operations); - } - -//--------------------------------------------------------- -// globalInsert -// - insert empty space at left locator position upto -// right locator -// - insert in master track -//--------------------------------------------------------- - -void globalInsert(bool onlySelectedTracks) -{ - Undo operations=movePartsTotheRight(MusEGlobal::song->lpos(), MusEGlobal::song->rpos()-MusEGlobal::song->lpos(), onlySelectedTracks); - MusEGlobal::song->applyOperationGroup(operations); -} - -Undo movePartsTotheRight(unsigned int startTicks, int moveTicks, bool only_selected, set* tracklist) -{ - if (moveTicks<=0) - return Undo(); - - Undo operations; - TrackList* tracks = MusEGlobal::song->tracks(); - - for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { - Track* track = *it; - if ( (track == 0) || - (only_selected && !track->selected()) || - (tracklist && tracklist->find(track)==tracklist->end()) ) - continue; - PartList* pl = track->parts(); - for (riPart p = pl->rbegin(); p != pl->rend(); ++p) { - Part* part = p->second; - unsigned t = part->tick(); - int l = part->lenTick(); - if (t + l <= startTicks) - continue; - if (startTicks > t && startTicks < (t+l)) { - // split part to insert new space - Part* p1; - Part* p2; - track->splitPart(part, startTicks, p1, p2); - p2->setTick(startTicks+moveTicks); - p2->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ - p1->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ - - operations.push_back(UndoOp(UndoOp::ModifyPart, part, p1, true, true)); - operations.push_back(UndoOp(UndoOp::AddPart, p2)); - } - else if (t >= startTicks) { - Part *nPart; - if (track->isMidiTrack()) - nPart = new MidiPart(*(MidiPart*)part); - else - nPart = new WavePart(*(WavePart*)part); - nPart->setTick(t + moveTicks); - operations.push_back(UndoOp(UndoOp::ModifyPart, part, nPart, true, false)); - } - } - } - - adjustGlobalLists(operations, startTicks, moveTicks); - - return operations; - } - - -//--------------------------------------------------------- -// globalSplit -// - split all parts at the song position pointer -//--------------------------------------------------------- - -void globalSplit(bool onlySelectedTracks) -{ - Undo operations=partSplitter(MusEGlobal::song->cpos(), onlySelectedTracks); - MusEGlobal::song->applyOperationGroup(operations); -} - -Undo partSplitter(unsigned int pos, bool onlySelectedTracks) -{ - Undo operations; - TrackList* tracks = MusEGlobal::song->tracks(); - - for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { - Track* track = *it; - if (track == 0 || (onlySelectedTracks && !track->selected())) - continue; - - PartList* pl = track->parts(); - for (iPart p = pl->begin(); p != pl->end(); ++p) { - Part* part = p->second; - unsigned int p1 = part->tick(); - unsigned int l0 = part->lenTick(); - if (pos > p1 && pos < (p1+l0)) { - Part* p1; - Part* p2; - track->splitPart(part, pos, p1, p2); - - p1->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it - p2->events()->incARef(-1); // so we must decrement it first :/ - - //MusEGlobal::song->informAboutNewParts(part, p1); // is unneccessary because of ModifyPart - MusEGlobal::song->informAboutNewParts(part, p2); - operations.push_back(UndoOp(UndoOp::ModifyPart,part, p1, true, false)); - operations.push_back(UndoOp(UndoOp::AddPart,p2)); - if (MusEGlobal::debugMsg) - { - printf("in partSplitter: part1 %d\n",p1->events()->refCount()); - printf("in partSplitter: part2 %d\n",p2->events()->refCount()); - } - break; - } - } - } - return operations; -} - - -} // namespace MusECore diff -Nru muse-2.1.2/muse/structure.h muse-3.0.2+ds1/muse/structure.h --- muse-2.1.2/muse/structure.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/structure.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -//========================================================= -// MusE -// Linux Music Editor -// structure.h -// (C) Copyright 2011 Florian Jung (flo93@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 __STRUCTURE_H__ -#define __STRUCTURE_H__ - -#include "undo.h" -#include - -namespace MusECore { -Undo movePartsTotheRight(unsigned int startTick, int moveTick, bool only_selected=false, std::set* tracklist=NULL); -Undo partSplitter(unsigned int tick, bool onlySelectedTracks=false); -void adjustGlobalLists(Undo& operations, int startPos, int diff); -void globalCut(bool onlySelectedTracks=false); -void globalInsert(bool onlySelectedTracks=false); -void globalSplit(bool onlySelectedTracks=false); -} - -#endif diff -Nru muse-2.1.2/muse/sync.cpp muse-3.0.2+ds1/muse/sync.cpp --- muse-2.1.2/muse/sync.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/sync.cpp 2017-12-17 21:07:38.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: sync.cpp,v 1.6.2.12 2009/06/20 22:20:41 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge.net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -38,8 +39,6 @@ namespace MusEGlobal { -int volatile curMidiSyncInPort = -1; - bool debugSync = false; int mtcType = 1; @@ -54,22 +53,23 @@ static int mtcLost; static bool mtcSync; // receive complete mtc frame? -static bool playPendingFirstClock = false; unsigned int syncSendFirstClockDelay = 1; // In milliseconds. -static unsigned int curExtMidiSyncTick = 0; +unsigned int volatile curExtMidiSyncTick = 0; unsigned int volatile lastExtMidiSyncTick = 0; -double volatile curExtMidiSyncTime = 0.0; -double volatile lastExtMidiSyncTime = 0.0; +unsigned int volatile curExtMidiSyncFrame = 0; +unsigned int volatile lastExtMidiSyncFrame = 0; MusECore::MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset = MusECore::MidiSyncInfo::SMALL; double syncRecTempoValQuant = 1.0; +MusECore::MidiSyncContainer midiSyncContainer; + // Not used yet. DELETETHIS? // static bool mcStart = false; // static int mcStartTick; // From the "Introduction to the Volatile Keyword" at Embedded dot com -/* A variable should be declared volatile whenever its value could change unexpectedly. - ... global variables within a multi-threaded application +/* A variable should be declared volatile whenever its value could change unexpectedly. + ... global variables within a multi-threaded application ... So all shared global variables should be declared volatile */ unsigned int volatile midiExtSyncTicks = 0; @@ -94,7 +94,7 @@ _recMRT = false; _recMMC = false; _recMTC = false; - + _lastClkTime = 0.0; _lastTickTime = 0.0; _lastMRTTime = 0.0; @@ -128,7 +128,7 @@ MidiSyncInfo& MidiSyncInfo::operator=(const MidiSyncInfo &sp) { copyParams(sp); - + _lastClkTime = sp._lastClkTime; _lastTickTime = sp._lastTickTime; _lastMRTTime = sp._lastMRTTime; @@ -178,91 +178,79 @@ // setTime //--------------------------------------------------------- -void MidiSyncInfo::setTime() -{ - // Note: CurTime() makes a system call to gettimeofday(), +void MidiSyncInfo::setTime() +{ + // Note: CurTime() makes a system call to gettimeofday(), // which apparently can be slow in some cases. So I avoid calling this function // too frequently by calling it (at the heartbeat rate) in Song::beat(). T356 double t = curTime(); - + if(_clockTrig) { _clockTrig = false; - _lastClkTime = t; + _lastClkTime = t; } else if(_clockDetect && (t - _lastClkTime >= 1.0)) // Set detect indicator timeout to about 1 second. { _clockDetect = false; - // Give up the current midi sync in port number if we took it... - if(MusEGlobal::curMidiSyncInPort == _port) - MusEGlobal::curMidiSyncInPort = -1; } - + if(_tickTrig) { _tickTrig = false; - _lastTickTime = t; + _lastTickTime = t; } else if(_tickDetect && (t - _lastTickTime) >= 1.0) // Set detect indicator timeout to about 1 second. _tickDetect = false; - + if(_MRTTrig) { _MRTTrig = false; - _lastMRTTime = t; + _lastMRTTime = t; } else if(_MRTDetect && (t - _lastMRTTime) >= 1.0) // Set detect indicator timeout to about 1 second. { _MRTDetect = false; - // Give up the current midi sync in port number if we took it... DELETETHIS 3 - //if(MusEGlobal::curMidiSyncInPort == _port) - // MusEGlobal::curMidiSyncInPort = -1; } - + if(_MMCTrig) { _MMCTrig = false; - _lastMMCTime = t; + _lastMMCTime = t; } else if(_MMCDetect && (t - _lastMMCTime) >= 1.0) // Set detect indicator timeout to about 1 second. { _MMCDetect = false; - // Give up the current midi sync in port number if we took it... DELETETHIS 3 - //if(MusEGlobal::curMidiSyncInPort == _port) - // MusEGlobal::curMidiSyncInPort = -1; } - + if(_MTCTrig) { _MTCTrig = false; - _lastMTCTime = t; + _lastMTCTime = t; } else if(_MTCDetect && (t - _lastMTCTime) >= 1.0) // Set detect indicator timeout to about 1 second. { _MTCDetect = false; - // Give up the current midi sync in port number if we took it... - if(MusEGlobal::curMidiSyncInPort == _port) - MusEGlobal::curMidiSyncInPort = -1; } - + for(int i = 0; i < MIDI_CHANNELS; i++) { if(_actTrig[i]) { _actTrig[i] = false; - _lastActTime[i] = t; + _lastActTime[i] = t; } else if(_actDetect[i] && (t - _lastActTime[i]) >= 1.0) // Set detect indicator timeout to about 1 second. { _actDetect[i] = false; _actDetectBits &= ~(1 << i); - } + } } } @@ -270,147 +258,121 @@ // setMCIn //--------------------------------------------------------- -void MidiSyncInfo::setMCIn(const bool v) -{ - _recMC = v; - // If sync receive was turned off, clear the current midi sync in port number so another port can grab it. - if(!_recMC && _port != -1 && MusEGlobal::curMidiSyncInPort == _port) - MusEGlobal::curMidiSyncInPort = -1; +void MidiSyncInfo::setMCIn(const bool v) +{ + _recMC = v; } //--------------------------------------------------------- // setMRTIn //--------------------------------------------------------- -void MidiSyncInfo::setMRTIn(const bool v) -{ - _recMRT = v; - // DELETETHIS 4 - // If sync receive was turned off, clear the current midi sync in port number so another port can grab it. - //if(!_recMRT && _port != -1 && MusEGlobal::curMidiSyncInPort == _port) - // MusEGlobal::curMidiSyncInPort = -1; +void MidiSyncInfo::setMRTIn(const bool v) +{ + _recMRT = v; } //--------------------------------------------------------- // setMMCIn //--------------------------------------------------------- -void MidiSyncInfo::setMMCIn(const bool v) -{ - _recMMC = v; - // DELETETHIS 4 - // If sync receive was turned off, clear the current midi sync in port number so another port can grab it. - //if(!_recMMC && _port != -1 && MusEGlobal::curMidiSyncInPort == _port) - // MusEGlobal::curMidiSyncInPort = -1; +void MidiSyncInfo::setMMCIn(const bool v) +{ + _recMMC = v; } //--------------------------------------------------------- // setMTCIn //--------------------------------------------------------- -void MidiSyncInfo::setMTCIn(const bool v) -{ - _recMTC = v; - // If sync receive was turned off, clear the current midi sync in port number so another port can grab it. - if(!_recMTC && _port != -1 && MusEGlobal::curMidiSyncInPort == _port) - MusEGlobal::curMidiSyncInPort = -1; +void MidiSyncInfo::setMTCIn(const bool v) +{ + _recMTC = v; } //--------------------------------------------------------- // trigMCSyncDetect //--------------------------------------------------------- -void MidiSyncInfo::trigMCSyncDetect() -{ +void MidiSyncInfo::trigMCSyncDetect() +{ _clockDetect = true; _clockTrig = true; - // Set the current midi sync in port number if it's not taken... - if(_recMC && MusEGlobal::curMidiSyncInPort == -1) - MusEGlobal::curMidiSyncInPort = _port; } //--------------------------------------------------------- // trigTickDetect //--------------------------------------------------------- -void MidiSyncInfo::trigTickDetect() -{ +void MidiSyncInfo::trigTickDetect() +{ _tickDetect = true; _tickTrig = true; } - + //--------------------------------------------------------- // trigMRTDetect //--------------------------------------------------------- -void MidiSyncInfo::trigMRTDetect() -{ +void MidiSyncInfo::trigMRTDetect() +{ _MRTDetect = true; _MRTTrig = true; - // Set the current midi sync in port number if it's not taken... //DELETETHIS 3 - //if(_recMRT && MusEGlobal::curMidiSyncInPort == -1) - // MusEGlobal::curMidiSyncInPort = _port; } - + //--------------------------------------------------------- // trigMMCDetect //--------------------------------------------------------- -void MidiSyncInfo::trigMMCDetect() -{ +void MidiSyncInfo::trigMMCDetect() +{ _MMCDetect = true; _MMCTrig = true; - // Set the current midi sync in port number if it's not taken... DELETETHIS 3 - //if(_recMMC && MusEGlobal::curMidiSyncInPort == -1) - // MusEGlobal::curMidiSyncInPort = _port; } - + //--------------------------------------------------------- // trigMTCDetect //--------------------------------------------------------- -void MidiSyncInfo::trigMTCDetect() -{ +void MidiSyncInfo::trigMTCDetect() +{ _MTCDetect = true; _MTCTrig = true; - // Set the current midi sync in port number if it's not taken... - if(_recMTC && MusEGlobal::curMidiSyncInPort == -1) - MusEGlobal::curMidiSyncInPort = _port; } - + //--------------------------------------------------------- // actDetect //--------------------------------------------------------- bool MidiSyncInfo::actDetect(const int ch) const -{ +{ if(ch < 0 || ch >= MIDI_CHANNELS) return false; - - return _actDetect[ch]; -} + + return _actDetect[ch]; +} //--------------------------------------------------------- // trigActDetect //--------------------------------------------------------- -void MidiSyncInfo::trigActDetect(const int ch) -{ +void MidiSyncInfo::trigActDetect(const int ch) +{ if(ch < 0 || ch >= MIDI_CHANNELS) return; - + _actDetectBits |= (1 << ch); _actDetect[ch] = true; _actTrig[ch] = true; } - + //--------------------------------------------------------- // isDefault //--------------------------------------------------------- bool MidiSyncInfo::isDefault() const { - return(_idOut == 127 && _idIn == 127 && !_sendMC && !_sendMRT && !_sendMMC && !_sendMTC && + return(_idOut == 127 && _idIn == 127 && !_sendMC && !_sendMRT && !_sendMMC && !_sendMTC && !_recMC && !_recMRT && !_recMMC && !_recMTC && _recRewOnStart); } @@ -468,16 +430,16 @@ void MidiSyncInfo::write(int level, Xml& xml) { - if(isDefault()) + if(isDefault()) return; - + xml.tag(level++, "midiSyncInfo"); if(_idOut != 127) xml.intTag(level, "idOut", _idOut); if(_idIn != 127) xml.intTag(level, "idIn", _idIn); - + if(_sendMC) xml.intTag(level, "sendMC", true); if(_sendMRT) @@ -486,7 +448,7 @@ xml.intTag(level, "sendMMC", true); if(_sendMTC) xml.intTag(level, "sendMTC", true); - + if(_recMC) xml.intTag(level, "recMC", true); if(_recMRT) @@ -497,21 +459,59 @@ xml.intTag(level, "recMTC", true); if(!_recRewOnStart) xml.intTag(level, "recRewStart", false); - + xml.etag(level, "midiSyncInfo"); } + +//--------------------------------------------------------- +// MidiSyncContainer +//--------------------------------------------------------- + +MidiSyncContainer::MidiSyncContainer() +{ + _midiClock = 0; + mclock1 = 0.0; + mclock2 = 0.0; + songtick1 = songtick2 = 0; + lastTempo = 0; + storedtimediffs = 0; + playStateExt = ExtMidiClock::ExternStopped; + recTick = 0; + recTick1 = 0; + recTick2 = 0; + + _clockAveragerStages = new int[16]; // Max stages is 16! + + _syncRecFilterPreset = MidiSyncInfo::SMALL; + setSyncRecFilterPresetArrays(); + + for(int i = 0; i < _clockAveragerPoles; ++i) + { + _avgClkDiffCounter[i] = 0; + _averagerFull[i] = false; + } + _tempoQuantizeAmount = 1.0; + _lastRealTempo = 0.0; +} + +MidiSyncContainer::~MidiSyncContainer() +{ + if(_clockAveragerStages) + delete[] _clockAveragerStages; +} + //--------------------------------------------------------- // mmcInput // Midi Machine Control Input received //--------------------------------------------------------- -void MidiSeq::mmcInput(int port, const unsigned char* p, int n) +void MidiSyncContainer::mmcInput(int port, const unsigned char* p, int n) { if (MusEGlobal::debugSync) - printf("mmcInput: n:%d %02x %02x %02x %02x\n", + fprintf(stderr, "mmcInput: n:%d %02x %02x %02x %02x\n", n, p[2], p[3], p[4], p[5]); - + MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiSyncInfo& msync = mp->syncInfo(); // Trigger MMC detect in. @@ -519,61 +519,62 @@ // MMC locate SMPTE time code may contain format type bits. Grab them. if(p[3] == 0x44 && p[4] == 6 && p[5] == 1) msync.setRecMTCtype((p[6] >> 5) & 3); - + // MMC in not turned on? Forget it. if(!msync.MMCIn()) return; - + switch(p[3]) { case 1: if (MusEGlobal::debugSync) - printf(" MMC: STOP\n"); - - MusEGlobal::playPendingFirstClock = false; - - if (MusEGlobal::audio->isPlaying()) + fprintf(stderr, " MMC: STOP\n"); + + playStateExt = ExtMidiClock::ExternStopped; + + if (MusEGlobal::audio->isPlaying()) { MusEGlobal::audio->msgPlay(false); - playStateExt = false; - alignAllTicks(); + } + + alignAllTicks(); + break; case 2: if (MusEGlobal::debugSync) - printf(" MMC: PLAY\n"); + fprintf(stderr, " MMC: PLAY\n"); + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH case 3: if (MusEGlobal::debugSync) - printf(" MMC: DEFERRED PLAY\n"); + fprintf(stderr, " MMC: DEFERRED PLAY\n"); MusEGlobal::mtcState = 0; MusEGlobal::mtcValid = false; MusEGlobal::mtcLost = 0; MusEGlobal::mtcSync = false; alignAllTicks(); - MusEGlobal::audio->msgPlay(true); - playStateExt = true; + playStateExt = ExtMidiClock::ExternStarting; + if(MusEGlobal::audio->isRunning() && !MusEGlobal::audio->isPlaying() && MusEGlobal::checkAudioDevice()) + MusEGlobal::audioDevice->startTransport(); + break; case 4: - printf("MMC: FF not implemented\n"); - MusEGlobal::playPendingFirstClock = false; + fprintf(stderr, "MMC: FF not implemented\n"); break; case 5: - printf("MMC: REWIND not implemented\n"); - MusEGlobal::playPendingFirstClock = false; + fprintf(stderr, "MMC: REWIND not implemented\n"); break; case 6: - printf("MMC: REC STROBE not implemented\n"); - MusEGlobal::playPendingFirstClock = false; + fprintf(stderr, "MMC: REC STROBE not implemented\n"); break; case 7: - printf("MMC: REC EXIT not implemented\n"); - MusEGlobal::playPendingFirstClock = false; + fprintf(stderr, "MMC: REC EXIT not implemented\n"); break; case 0xd: - printf("MMC: RESET not implemented\n"); - MusEGlobal::playPendingFirstClock = false; + fprintf(stderr, "MMC: RESET not implemented\n"); break; case 0x44: if (p[5] == 0) { - printf("MMC: LOCATE IF not implemented\n"); + fprintf(stderr, "MMC: LOCATE IF not implemented\n"); break; } else if (p[5] == 1) { @@ -586,15 +587,15 @@ MusEGlobal::audioDevice->seekTransport(tp); alignAllTicks(); if (MusEGlobal::debugSync) { - printf("MMC: LOCATE mtc type:%d time:%lf frame:%d mtc: ", type, mtc.time(), mmcPos); + fprintf(stderr, "MMC: LOCATE mtc type:%d time:%lf frame:%d mtc: ", type, mtc.time(), mmcPos); mtc.print(); - printf("\n"); + fprintf(stderr, "\n"); } break; } // fall through default: - printf("MMC %x %x, unknown\n", p[3], p[4]); break; + fprintf(stderr, "MMC %x %x, unknown\n", p[3], p[4]); break; } } @@ -603,10 +604,10 @@ // process Quarter Frame Message //--------------------------------------------------------- -void MidiSeq::mtcInputQuarter(int port, unsigned char c) +void MidiSyncContainer::mtcInputQuarter(int port, unsigned char c) { static int hour, min, sec, frame; - + int valL = c & 0xf; int valH = valL << 4; @@ -647,12 +648,12 @@ int type = (hour >> 5) & 3; hour &= 0x1f; - if(MusEGlobal::mtcState == 8) + if(MusEGlobal::mtcState == 8) { MusEGlobal::mtcValid = (MusEGlobal::mtcLost == 0); MusEGlobal::mtcState = 0; MusEGlobal::mtcLost = 0; - if(MusEGlobal::mtcValid) + if(MusEGlobal::mtcValid) { MusEGlobal::mtcCurTime.set(hour, min, sec, frame); if(port != -1) @@ -662,17 +663,17 @@ msync.setRecMTCtype(type); msync.trigMTCDetect(); // Not for the current in port? External sync not turned on? MTC in not turned on? Forget it. - if(port == MusEGlobal::curMidiSyncInPort && MusEGlobal::extSyncFlag.value() && msync.MTCIn()) + if(port == MusEGlobal::config.curMidiSyncInPort && MusEGlobal::extSyncFlag.value() && msync.MTCIn()) { if(MusEGlobal::debugSync) - printf("MidiSeq::mtcInputQuarter hour byte:%hx\n", tmphour); + fprintf(stderr, "MidiSyncContainer::mtcInputQuarter hour byte:%x\n", (unsigned int)tmphour); mtcSyncMsg(MusEGlobal::mtcCurTime, type, !MusEGlobal::mtcSync); - } + } } MusEGlobal::mtcSync = true; } - } - else if (MusEGlobal::mtcValid && (MusEGlobal::mtcLost == 0)) + } + else if (MusEGlobal::mtcValid && (MusEGlobal::mtcLost == 0)) { MusEGlobal::mtcCurTime.incQuarter(type); } @@ -683,14 +684,14 @@ // process Frame Message //--------------------------------------------------------- -void MidiSeq::mtcInputFull(int port, const unsigned char* p, int n) +void MidiSyncContainer::mtcInputFull(int port, const unsigned char* p, int n) { if (MusEGlobal::debugSync) - printf("mtcInputFull\n"); + fprintf(stderr, "mtcInputFull\n"); if (p[3] != 1) { if (p[3] != 2) { // silently ignore user bits - printf("unknown mtc msg subtype 0x%02x\n", p[3]); + fprintf(stderr, "unknown mtc msg subtype 0x%02x\n", p[3]); dump(p, n); } return; @@ -710,10 +711,10 @@ MusEGlobal::mtcState = 0; MusEGlobal::mtcValid = true; MusEGlobal::mtcLost = 0; - + // Added by Tim. if(MusEGlobal::debugSync) - printf("mtcInputFull: time:%lf stime:%lf hour byte (all bits):%hx\n", MusEGlobal::mtcCurTime.time(), MusEGlobal::mtcCurTime.time(type), p[4]); + fprintf(stderr, "mtcInputFull: time:%lf stime:%lf hour byte (all bits):%hhx\n", MusEGlobal::mtcCurTime.time(), MusEGlobal::mtcCurTime.time(type), p[4]); if(port != -1) { MidiPort* mp = &MusEGlobal::midiPorts[port]; @@ -727,21 +728,21 @@ MusEGlobal::audioDevice->seekTransport(tp); alignAllTicks(); } - } + } } //--------------------------------------------------------- // nonRealtimeSystemSysex //--------------------------------------------------------- -void MidiSeq::nonRealtimeSystemSysex(int /*port*/, const unsigned char* p, int n) +void MidiSyncContainer::nonRealtimeSystemSysex(int /*port*/, const unsigned char* p, int n) { switch(p[3]) { case 4: - printf("NRT Setup\n"); + fprintf(stderr, "NRT Setup\n"); break; default: - printf("unknown NRT Msg 0x%02x\n", p[3]); + fprintf(stderr, "unknown NRT Msg 0x%02x\n", p[3]); dump(p, n); break; } @@ -755,32 +756,32 @@ // quarter note). //--------------------------------------------------------- -void MidiSeq::setSongPosition(int port, int midiBeat) +void MidiSyncContainer::setSongPosition(int port, int midiBeat) { if (MusEGlobal::midiInputTrace) - printf("set song position port:%d %d\n", port, midiBeat); - + fprintf(stderr, "set song position port:%d %d\n", port, midiBeat); + MusEGlobal::midiPorts[port].syncInfo().trigMRTDetect(); - + if(!MusEGlobal::extSyncFlag.value() || !MusEGlobal::midiPorts[port].syncInfo().MRTIn()) return; - + // Re-transmit song position to other devices if clock out turned on. for(int p = 0; p < MIDI_PORTS; ++p) if(p != port && MusEGlobal::midiPorts[p].syncInfo().MRTOut()) MusEGlobal::midiPorts[p].sendSongpos(midiBeat); - + MusEGlobal::curExtMidiSyncTick = (MusEGlobal::config.division * midiBeat) / 4; MusEGlobal::lastExtMidiSyncTick = MusEGlobal::curExtMidiSyncTick; - + Pos pos(MusEGlobal::curExtMidiSyncTick, true); - + if (!MusEGlobal::checkAudioDevice()) return; MusEGlobal::audioDevice->seekTransport(pos); alignAllTicks(pos.frame()); if (MusEGlobal::debugSync) - printf("setSongPosition %d\n", pos.tick()); + fprintf(stderr, "setSongPosition %d\n", pos.tick()); } @@ -788,10 +789,10 @@ //--------------------------------------------------------- // set all runtime variables to the "in sync" value //--------------------------------------------------------- -void MidiSeq::alignAllTicks(int frameOverride) +void MidiSyncContainer::alignAllTicks(int frameOverride) { unsigned curFrame; - if (!frameOverride) + if (!frameOverride && MusEGlobal::audio) curFrame = MusEGlobal::audio->pos().frame(); else curFrame = frameOverride; @@ -821,8 +822,8 @@ if (recTick2 < 0) 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); - + fprintf(stderr, "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) { @@ -836,473 +837,28 @@ // realtimeSystemInput // real time message received //--------------------------------------------------------- -void MidiSeq::realtimeSystemInput(int port, int c, double time) +void MidiSyncContainer::realtimeSystemInput(int port, int c, double time) { if (MusEGlobal::midiInputTrace) - printf("realtimeSystemInput port:%d 0x%x time:%f\n", port+1, c, time); + fprintf(stderr, "realtimeSystemInput port:%d 0x%x time:%f\n", port+1, c, time); MidiPort* mp = &MusEGlobal::midiPorts[port]; - - // Trigger on any tick, clock, or realtime command. + + // Trigger on any tick, clock, or realtime command. if(c == ME_TICK) // Tick mp->syncInfo().trigTickDetect(); else - if(c == ME_CLOCK) // Clock - mp->syncInfo().trigMCSyncDetect(); - else mp->syncInfo().trigMRTDetect(); // Other - + // External sync not on? Clock in not turned on? Otherwise realtime in not turned on? if(!MusEGlobal::extSyncFlag.value()) return; - if(c == ME_CLOCK) - { - if(!mp->syncInfo().MCIn()) - return; - } - else - if(!mp->syncInfo().MRTIn()) + if(!mp->syncInfo().MRTIn()) return; - - - switch(c) { - case ME_CLOCK: // midi clock (24 ticks / quarter note) - { - // Not for the current in port? Forget it. - if(port != MusEGlobal::curMidiSyncInPort) - break; - - // Re-transmit clock to other devices if clock out turned on. - // Must be careful not to allow more than one clock input at a time. - // Would re-transmit mixture of multiple clocks - confusing receivers. - // Solution: Added MusEGlobal::curMidiSyncInPort. - // Maybe in MidiSeq::processTimerTick(), call sendClock for the other devices, instead of here. - for(int p = 0; p < MIDI_PORTS; ++p) - 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; - // Hopefully the transport will be ready by now, the seek upon start should mean the - // audio prefetch has already finished or at least started... - // Must comfirm that play does not force a complete prefetch again, but don't think so... - if(!MusEGlobal::audio->isPlaying()) - MusEGlobal::audioDevice->startTransport(); - } - //else DELETETHIS? - // This part will be run on the second and subsequent clocks, after start. - // Can't check audio state, might not be playing yet, we might miss some increments. - if(playStateExt) - { - 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 - /* - double mclock0 = curTime(); - // Difference in time last 2 rounds: - double tdiff0 = mclock0 - mclock1; - double tdiff1 = mclock1 - mclock2; - double averagetimediff = 0.0; - - if (mclock1 != 0.0) { - if (storedtimediffs < 24) - { - timediff[storedtimediffs] = mclock0 - mclock1; - storedtimediffs++; - } - else { - for (int i=0; i<23; i++) { - timediff[i] = timediff[i+1]; - } - timediff[23] = mclock0 - mclock1; - } - // Calculate average timediff: - for (int i=0; i < storedtimediffs; i++) { - averagetimediff += timediff[i]/storedtimediffs; - } - } - - // Compare w audio if playing: - if (playStateExt == true ) { //MusEGlobal::audio->isPlaying() state == PLAY - //BEGIN standard setup: - recTick += MusEGlobal::config.division / 24; // The one we're syncing to - int tempo = MusEGlobal::tempomap.tempo(0); - unsigned curFrame = MusEGlobal::audio->pos().frame(); - double songtick = (double(curFrame)/double(MusEGlobal::sampleRate)) * - double(MusEGlobal::config.division * 1000000.0) / double(tempo); - - double scale = double(tdiff0/averagetimediff); - double tickdiff = songtick - ((double) recTick - 24 + scale*24.0); - - //END standard setup - if (MusEGlobal::debugSync) { - int m, b, t; - MusEGlobal::audio->pos().mbt(&m, &b, &t); - int song_beat = b + m*4; // if the time-signature is different than 4/4, this will be wrong. - int sync_beat = recTick/MusEGlobal::config.division; - printf("pT=%.3f rT=%d diff=%.3f songB=%d syncB=%d scale=%.3f, curFrame=%d", - songtick, recTick, tickdiff, song_beat, sync_beat, scale, curFrame); - } - - //if ((mclock2 !=0.0) && (tdiff1 > 0.0) && fabs(tickdiff) > 0.5 && lastTempo != 0) { - if ((mclock2 !=0.0) && (tdiff1 > 0.0) && lastTempo != 0) { - // Interpolate: - double tickdiff1 = songtick1 - recTick1; - double tickdiff2 = songtick2 - recTick2; - double newtickdiff = (tickdiff1+tickdiff2)/250; - //tickdiff/5.0 + - tickdiff1/16.0 + - tickdiff2/24.0; //5 mins 30 secs on 116BPM, -p 512 jackd - - if (newtickdiff != 0.0) { - int newTempo = MusEGlobal::tempomap.tempo(0); - //newTempo += int(24.0 * newtickdiff * scale); - newTempo += int(24.0 * newtickdiff); - if (MusEGlobal::debugSync) - printf(" tdiff=%f ntd=%f lt=%d tmpo=%.3f", - tdiff0, newtickdiff, lastTempo, (float)((1000000.0 * 60.0)/newTempo)); - //syncTempo = newTempo; - MusEGlobal::tempomap.setTempo(0,newTempo); - } - if (MusEGlobal::debugSync) - printf("\n"); - } - else if (MusEGlobal::debugSync) - printf("\n"); - //BEGIN post calc - lastTempo = tempo; - recTick2 = recTick1; - recTick1 = recTick; - mclock2 = mclock1; - mclock1 = mclock0; - songtick2 = songtick1; - songtick1 = songtick; - //END post calc - break; - } // END state play - // - // Pre-sync (when audio is not running) - // Calculate tempo depending on time per pulse - // - if (mclock1 == 0.0) { - mp->device()->discardInput(); - if (MusEGlobal::debugSync) - printf("Discarding input from port %d\n", port); - } - if ((mclock2 != 0.0) && (tdiff0 > 0.0)) { - int tempo0 = int(24000000.0 * tdiff0 + .5); - int tempo1 = int(24000000.0 * tdiff1 + .5); - int tempo = MusEGlobal::tempomap.tempo(0); - - int diff0 = tempo0 - tempo; - int diff1 = tempo1 - tempo0; - if (diff0) { - int newTempo = tempo + diff0/8 + diff1/16; - if (MusEGlobal::debugSync) - printf("setting new tempo %d = %f\n", newTempo, (float)((1000000.0 * 60.0)/newTempo)); - MusEGlobal::tempomap.setTempo(0, newTempo); - } - } - mclock2 = mclock1; - mclock1 = mclock0; - */ -//END : Original Code - -//BEGIN : Using external tempo map: - /* - double mclock0 = curTime(); - // Difference in time last 2 rounds: - double tdiff0 = mclock0 - mclock1; - double tdiff1 = mclock1 - mclock2; - double averagetimediff = 0.0; - - if (mclock1 != 0.0) { - if (storedtimediffs < 24) - { - timediff[storedtimediffs] = mclock0 - mclock1; - storedtimediffs++; - } - else { - for (int i=0; i<23; i++) { - timediff[i] = timediff[i+1]; - } - timediff[23] = mclock0 - mclock1; - } - // Calculate average timediff: - for (int i=0; i < storedtimediffs; i++) { - averagetimediff += timediff[i]/storedtimediffs; - } - } - - // Compare w audio if playing: - //if (playStateExt == true ) { //MusEGlobal::audio->isPlaying() state == PLAY - if (0) { - //BEGIN standard setup: - recTick += MusEGlobal::config.division / 24; // The one we're syncing to - int tempo = MusEGlobal::tempomap.tempo(0); - //unsigned curFrame = MusEGlobal::audio->pos().frame(); - //double songtick = (double(curFrame)/double(MusEGlobal::sampleRate)) * - // double(MusEGlobal::config.division * 1000000.0) / double(tempo); - double songtick = MusEGlobal::tempomap.curTickExt(mclock0); - - double scale = double(tdiff0/averagetimediff); - double tickdiff = songtick - ((double) recTick - 24 + scale*24.0); - - //END standard setup - if (MusEGlobal::debugSync) { - int m, b, t; - MusEGlobal::audio->pos().mbt(&m, &b, &t); - - int song_beat = b + m*4; // if the time-signature is different than 4/4, this will be wrong. - int sync_beat = recTick/MusEGlobal::config.division; - printf("pT=%.3f rT=%d diff=%.3f songB=%d syncB=%d scale=%.3f, curFrame=%d averagetimediff:%.3lf", - songtick, recTick, tickdiff, song_beat, sync_beat, scale, MusEGlobal::audio->pos().frame(), averagetimediff); - } - - //if ((mclock2 !=0.0) && (tdiff1 > 0.0) && fabs(tickdiff) > 0.5 && lastTempo != 0) { - if ((mclock2 !=0.0) && (tdiff1 > 0.0) && lastTempo != 0) { - // Interpolate: - double tickdiff1 = songtick1 - recTick1; - double tickdiff2 = songtick2 - recTick2; - double newtickdiff = (tickdiff1+tickdiff2)/250; - ////double newtickdiff = (tickdiff1+tickdiff2) / 10.0; - //double newtickdiff = tickdiff/5.0 + - // tickdiff1/16.0 + - // tickdiff2/24.0; //5 mins 30 secs on 116BPM, -p 512 jackd - - if (newtickdiff != 0.0) { - //int newTempo = MusEGlobal::tempomap.tempo(0); - int newTempo = tempo; - //newTempo += int(24.0 * newtickdiff * scale); - newTempo += int(24.0 * newtickdiff); - if (MusEGlobal::debugSync) - printf(" tdiff=%f ntd=%f lt=%d tmpo=%.3f", - tdiff0, newtickdiff, lastTempo, (float)((1000000.0 * 60.0)/newTempo)); - //syncTempo = newTempo; - //MusEGlobal::tempomap.setTempo(0,newTempo); - // Don't set the last stable tempo. - //MusEGlobal::tempomap.setTempo(0, newTempo, false); - MusEGlobal::tempomap.setExtTempo(newTempo); - } - if (MusEGlobal::debugSync) - printf("\n"); - } - else if (MusEGlobal::debugSync) - printf("\n"); - - //BEGIN post calc - lastTempo = tempo; - recTick2 = recTick1; - recTick1 = recTick; - mclock2 = mclock1; - mclock1 = mclock0; - songtick2 = songtick1; - songtick1 = songtick; - //END post calc - break; - } // END state play - // - // Pre-sync (when audio is not running) - // Calculate tempo depending on time per pulse - // - if (mclock1 == 0.0) { - mp->device()->discardInput(); - if (MusEGlobal::debugSync) - printf("Discarding input from port %d\n", port); - } - if ((mclock2 != 0.0) && (tdiff0 > 0.0)) { - - //int tempo0 = int(24000000.0 * tdiff0 + .5); - //int tempo1 = int(24000000.0 * tdiff1 + .5); - //int tempo = MusEGlobal::tempomap.tempo(0); - //int diff0 = tempo0 - tempo; - //int diff1 = tempo1 - tempo0; - - //if (diff0) { - // int newTempo = tempo + diff0/8 + diff1/16; - // if (MusEGlobal::debugSync) - // printf("setting new tempo %d = %f\n", newTempo, (float)((1000000.0 * 60.0)/newTempo)); - //MusEGlobal::tempomap.setTempo(0, newTempo); - // Don't set the last stable tempo. - //MusEGlobal::tempomap.setTempo(0, newTempo, false); - // MusEGlobal::tempomap.setExtTempo(newTempo); - // } - - //double tempo0 = 24000000.0 * tdiff0; - //double tempo1 = 24000000.0 * tdiff1; - //int newTempo = int((tempo0 + tempo1) / 2.0); - int newTempo = int(averagetimediff * 24000000.0); - if(MusEGlobal::debugSync) - printf("setting new tempo %d = %f\n", newTempo, (float)((1000000.0 * 60.0)/newTempo)); - MusEGlobal::tempomap.setExtTempo(newTempo); - } - - mclock2 = mclock1; - mclock1 = mclock0; - */ -//END : Using external tempo map - - } - break; + switch(c) { case ME_TICK: // midi tick (every 10 msec) //DELETETHIS 6 // FIXME: Unfinished? mcStartTick is uninitialized and Song::setPos doesn't set it either. Dangerous to allow this. @@ -1321,16 +877,18 @@ // Re-send continue instead, for consistency. if(MusEGlobal::midiPorts[port].syncInfo().recRewOnStart()) MusEGlobal::midiPorts[p].sendStart(); - else + else MusEGlobal::midiPorts[p].sendContinue(); } if (MusEGlobal::debugSync) - printf(" start\n"); - - + fprintf(stderr, " start\n"); + + // DELETETHIS, remove the wrapping if(true) if (1 /* !MusEGlobal::audio->isPlaying()*/ /*state == IDLE*/) { if (!MusEGlobal::checkAudioDevice()) return; + + playStateExt = ExtMidiClock::ExternStarting; // Rew on start option. if(MusEGlobal::midiPorts[port].syncInfo().recRewOnStart()) @@ -1338,22 +896,11 @@ MusEGlobal::curExtMidiSyncTick = 0; MusEGlobal::lastExtMidiSyncTick = MusEGlobal::curExtMidiSyncTick; MusEGlobal::audioDevice->seekTransport(Pos(0, false)); - } + } alignAllTicks(); - storedtimediffs = 0; - - // p3.3.26 1/23/10 DELETETHIS 6 - // Changed because msgPlay calls MusEGlobal::audioDevice->seekTransport(song->cPos()) - // and song->cPos() may not be changed to 0 yet, causing tranport not to go to 0. - //MusEGlobal::audio->msgPlay(true); - //MusEGlobal::audioDevice->startTransport(); - // p3.3.28 - MusEGlobal::playPendingFirstClock = true; - MusEGlobal::midiExtSyncTicks = 0; - playStateExt = true; } break; case ME_CONTINUE: // continue @@ -1361,86 +908,311 @@ for(int p = 0; p < MIDI_PORTS; ++p) if(p != port && MusEGlobal::midiPorts[p].syncInfo().MRTOut()) MusEGlobal::midiPorts[p].sendContinue(); - + if (MusEGlobal::debugSync) - printf("realtimeSystemInput continue\n"); - + fprintf(stderr, "realtimeSystemInput continue\n"); + //printf("continue:%f\n", curTime()); - + if (1 /* !MusEGlobal::audio->isPlaying() */ /*state == IDLE */) { - //unsigned curFrame = MusEGlobal::audio->curFrame(); - //recTick = MusEGlobal::tempomap.frame2tick(curFrame); // don't think this will work... (ml) - //alignAllTicks(); - - // p3.3.28 - //MusEGlobal::audio->msgPlay(true); - // p3.3.31 // Begin incrementing immediately upon first clock reception. - MusEGlobal::playPendingFirstClock = true; - - playStateExt = true; + playStateExt = ExtMidiClock::ExternContinuing; } break; case ME_STOP: // stop { // Stop the increment right away. MusEGlobal::midiExtSyncTicks = 0; - playStateExt = false; - MusEGlobal::playPendingFirstClock = false; - + playStateExt = ExtMidiClock::ExternStopped; + // Re-transmit stop to other devices if clock out turned on. for(int p = 0; p < MIDI_PORTS; ++p) if(p != port && MusEGlobal::midiPorts[p].syncInfo().MRTOut()) MusEGlobal::midiPorts[p].sendStop(); - - + + if (MusEGlobal::audio->isPlaying()) MusEGlobal::audio->msgPlay(false); - + if (MusEGlobal::debugSync) - printf("realtimeSystemInput stop\n"); + fprintf(stderr, "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 + // Just in case the process still runs a cycle or two and causes the + // audio tick position to increment, reset the incrementer and force // the transport position to what the hardware thinks is the current position. //MusEGlobal::midiExtSyncTicks = 0; //Pos pos((MusEGlobal::config.division * lastStoppedBeat) / 4, true); //Pos pos(MusEGlobal::curExtMidiSyncTick, true); //MusEGlobal::audioDevice->seekTransport(pos); } - + break; //case 0xfd: // unknown DELETETHIS 3 //case ME_SENSE: // active sensing //case ME_META: // system reset (reset is 0xff same enumeration as file meta event) default: - break; + break; } } //--------------------------------------------------------- +// midiClockInput +// Midi clock (24 ticks / quarter note) +// Starts transport if necessary. Adds clock to tempo list. +// Returns whether the clock was a 'first clock' after a start or continue message. +//--------------------------------------------------------- + +ExtMidiClock MidiSyncContainer::midiClockInput(int port, unsigned int frame) +{ + if(port < 0 || port >= MIDI_PORTS) + return ExtMidiClock(); + + MidiPort* mp = &MusEGlobal::midiPorts[port]; + + mp->syncInfo().trigMCSyncDetect(); + + // External sync not on? Clock in not turned on? Otherwise realtime in not turned on? + if(!MusEGlobal::extSyncFlag.value()) + return ExtMidiClock(); + if(!mp->syncInfo().MCIn()) + return ExtMidiClock(); + + // Not for the current in port? Forget it. + if(port != MusEGlobal::config.curMidiSyncInPort) + return ExtMidiClock(); + + //fprintf(stderr, "MidiSyncContainer::midiClockInput: CLOCK port:%d time:%u\n", port, frame); + + // Re-transmit clock to other devices if clock out turned on. + // Must be careful not to allow more than one clock input at a time. + // Would re-transmit mixture of multiple clocks - confusing receivers. + // Solution: Added MusEGlobal::curMidiSyncInPort. + // Maybe in MidiSyncContainer::processTimerTick(), call sendClock for the other devices, instead of here. + for(int p = 0; p < MIDI_PORTS; ++p) + if(p != port && MusEGlobal::midiPorts[p].syncInfo().MCOut()) + MusEGlobal::midiPorts[p].sendClock(); + + MusEGlobal::lastExtMidiSyncFrame = MusEGlobal::curExtMidiSyncFrame; + MusEGlobal::curExtMidiSyncFrame = frame; + + if(MusEGlobal::lastExtMidiSyncFrame > MusEGlobal::curExtMidiSyncFrame) + { + fprintf(stderr, + "MusE: Warning: MidiSyncContainer::midiClockInput(): lastExtMidiSyncFrame:%u > curExtMidiSyncFrame:%u Setting last to cur...\n", + MusEGlobal::lastExtMidiSyncFrame, MusEGlobal::curExtMidiSyncFrame); + MusEGlobal::lastExtMidiSyncFrame = MusEGlobal::curExtMidiSyncFrame; + } + + const int div = MusEGlobal::config.division/24; + + //------------------------------- + // State changes: + //------------------------------- + bool first_clock = false; + if(playStateExt == ExtMidiClock::ExternStarting || playStateExt == ExtMidiClock::ExternContinuing) + { + first_clock = true; + if(playStateExt == ExtMidiClock::ExternStarting) + playStateExt = ExtMidiClock::ExternStarted; + if(playStateExt == ExtMidiClock::ExternContinuing) + playStateExt = ExtMidiClock::ExternContinued; + if(MusEGlobal::audio->isRunning() && !MusEGlobal::audio->isPlaying() && MusEGlobal::checkAudioDevice()) + MusEGlobal::audioDevice->startTransport(); + } + + //else DELETETHIS? + // This part will be run on the second and subsequent clocks, after start. + // Can't check audio state, might not be playing yet, we might miss some increments. + if(isRunning()) + { + MusEGlobal::midiExtSyncTicks += div; + MusEGlobal::lastExtMidiSyncTick = MusEGlobal::curExtMidiSyncTick; + MusEGlobal::curExtMidiSyncTick += div; + + if(MusEGlobal::song->record() && MusEGlobal::curExtMidiSyncFrame > MusEGlobal::lastExtMidiSyncFrame) + { + double diff = double(MusEGlobal::curExtMidiSyncFrame - MusEGlobal::lastExtMidiSyncFrame) / double(MusEGlobal::sampleRate); + 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) + fprintf(stderr, "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 = fabs(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) + { + fprintf(stderr, "FIXME sync: adding restart tempo curExtMidiSyncTick:%d: add_tick:%d < 0 !\n", + MusEGlobal::curExtMidiSyncTick, add_tick); + add_tick = 0; + } + if(MusEGlobal::debugSync) + fprintf(stderr, + "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 = fabs(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) + { + fprintf(stderr, "FIXME sync: adding new tempo curExtMidiSyncTick:%d: add_tick:%d < 0 !\n", + MusEGlobal::curExtMidiSyncTick, add_tick); + add_tick = 0; + } + if(MusEGlobal::debugSync) + fprintf(stderr, "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)); + } + } + } + } + } + } + } + } + } + return ExtMidiClock(frame, playStateExt, first_clock); +} + +//--------------------------------------------------------- // MusEGlobal::mtcSyncMsg // process received mtc Sync // seekFlag - first complete mtc frame received after // start //--------------------------------------------------------- -void MidiSeq::mtcSyncMsg(const MTC& mtc, int type, bool seekFlag) +void MidiSyncContainer::mtcSyncMsg(const MTC& mtc, int type, bool seekFlag) { double time = mtc.time(); double stime = mtc.time(type); if (MusEGlobal::debugSync) - printf("MidiSeq::MusEGlobal::mtcSyncMsg time:%lf stime:%lf seekFlag:%d\n", time, stime, seekFlag); + fprintf(stderr, "MidiSyncContainer::mtcSyncMsg time:%lf stime:%lf seekFlag:%d\n", + time, stime, seekFlag); - if (seekFlag && MusEGlobal::audio->isRunning()) { - if (!MusEGlobal::checkAudioDevice()) return; - if (MusEGlobal::debugSync) - printf("MidiSeq::MusEGlobal::mtcSyncMsg starting transport.\n"); - MusEGlobal::audioDevice->startTransport(); - return; - } + if (seekFlag && MusEGlobal::audio->isRunning() && !MusEGlobal::audio->isPlaying() && MusEGlobal::checkAudioDevice()) + { + if (MusEGlobal::debugSync) + fprintf(stderr, "MidiSyncContainer::mtcSyncMsg starting transport.\n"); + MusEGlobal::audioDevice->startTransport(); + return; + } /*if (tempoSN != MusEGlobal::tempomap.tempoSN()) { DELETETHIS 13 double cpos = MusEGlobal::tempomap.tick2time(_midiTick, 0); @@ -1458,4 +1230,71 @@ */ } +//--------------------------------------------------------- +// setSyncRecFilterPresetArrays +// To be called in realtime thread only. +//--------------------------------------------------------- +void MidiSyncContainer::setSyncRecFilterPresetArrays() +{ + 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: + fprintf(stderr, "MidiSyncContainer::setSyncRecFilterPresetArrays unknown preset type:%d\n", (int)_syncRecFilterPreset); + } +} + +//--------------------------------------------------------- +// setSyncRecFilterPreset +// To be called in realtime thread only. +//--------------------------------------------------------- +void MidiSyncContainer::setSyncRecFilterPreset(MidiSyncInfo::SyncRecFilterPresetType type) +{ + _syncRecFilterPreset = type; + setSyncRecFilterPresetArrays(); + alignAllTicks(); +} + + } // namespace MusECore diff -Nru muse-2.1.2/muse/sync.h muse-3.0.2+ds1/muse/sync.h --- muse-2.1.2/muse/sync.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/sync.h 2017-12-17 21:07:38.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: sync.h,v 1.1.1.1.2.2 2009/04/01 01:37:11 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge.net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -140,6 +141,168 @@ void write(int level, Xml& xml); }; + +//--------------------------------------------------------- +// ExtMidiClock +// Holds the frame of each external clock, +// and play state at that time. +//--------------------------------------------------------- + +class ExtMidiClock +{ + public: + enum ExternState { ExternStopped = 0, ExternStarting, ExternContinuing, ExternStarted, ExternContinued }; + + private: + // The frame at which this clock arrived. + unsigned int _frame; + // The play state of the external device when this clock arrived. + ExternState _externState; + // Whether this clock is the first clock after a start or continue. + bool _isFirstClock; + // Whether this is a valid structure. + bool _isValid; + + public: + ExtMidiClock() : _frame(0), _externState(ExternStopped), _isFirstClock(false), _isValid(false) { }; + ExtMidiClock(unsigned int frame, ExternState extState, bool firstClock) : + _frame(frame), _externState(extState), _isFirstClock(firstClock), _isValid(true) { }; + + // The frame at which this clock arrived. + unsigned int frame() const { return _frame; } + // The play state of the external device when this clock arrived. + ExternState externState() const { return _externState; } + // Whether this clock is the first clock after a start or continue. + bool isFirstClock() const { return _isFirstClock; } + // Whether this is a valid structure. + bool isValid() const { return _isValid; } + bool isPlaying() const + { + switch(_externState) + { + case ExternStopped: + case ExternStarting: + case ExternContinuing: + return false; + break; + + case ExternStarted: + case ExternContinued: + return true; + break; + }; + return false; + } + bool isRunning() const + { + switch(_externState) + { + case ExternStopped: + return false; + break; + + case ExternStarting: + case ExternContinuing: + case ExternStarted: + case ExternContinued: + return true; + break; + }; + return false; + } +}; + +//--------------------------------------------------------- +// MidiSyncContainer +//--------------------------------------------------------- + +class MidiSyncContainer { + private: + int _midiClock; // Accumulator for clock output. + +/* Testing */ + ExtMidiClock::ExternState playStateExt; // used for keeping play state in sync functions + int recTick; // ext sync tick position + double mclock1, mclock2; + double songtick1, songtick2; + int recTick1, recTick2; + int lastTempo; + 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 setSyncRecFilterPresetArrays(); + void alignAllTicks(int frameOverride = 0); +/* Testing */ + + void mtcSyncMsg(const MTC&, int, bool); + + public: + MidiSyncContainer(); + virtual ~MidiSyncContainer(); + + int midiClock() const { return _midiClock; } + void setMidiClock(int val) { _midiClock = val; } + ExtMidiClock::ExternState externalPlayState() const { return playStateExt; } + void setExternalPlayState(ExtMidiClock::ExternState v) { playStateExt = v; } + bool isPlaying() const + { + switch(playStateExt) + { + case ExtMidiClock::ExternStopped: + case ExtMidiClock::ExternStarting: + case ExtMidiClock::ExternContinuing: + return false; + break; + + case ExtMidiClock::ExternStarted: + case ExtMidiClock::ExternContinued: + return true; + break; + }; + return false; + } + bool isRunning() const + { + switch(playStateExt) + { + case ExtMidiClock::ExternStopped: + return false; + break; + + case ExtMidiClock::ExternStarting: + case ExtMidiClock::ExternContinuing: + case ExtMidiClock::ExternStarted: + case ExtMidiClock::ExternContinued: + return true; + break; + }; + return false; + } + void realtimeSystemInput(int port, int type, double time = 0.0); + // Starts transport if necessary. Adds clock to tempo list. + // Returns a clock structure including frame, state, and whether the clock was a + // 'first clock' after a start or continue message. + ExtMidiClock midiClockInput(int port, unsigned int frame); + 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); + + MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset() const { return _syncRecFilterPreset; } + void setSyncRecFilterPreset(MidiSyncInfo::SyncRecFilterPresetType type); + double recTempoValQuant() const { return _tempoQuantizeAmount; } + void setRecTempoValQuant(double q) { _tempoQuantizeAmount = q; } +}; + } // namespace MusECore namespace MusEGlobal { @@ -149,14 +312,16 @@ extern int mtcType; extern MusECore::MTC mtcOffset; extern MusECore::BValue extSyncFlag; -extern int volatile curMidiSyncInPort; extern MusECore::BValue useJackTransport; extern bool volatile jackTransportMaster; extern unsigned int syncSendFirstClockDelay; // In milliseconds. extern unsigned int volatile lastExtMidiSyncTick; +extern unsigned int volatile curExtMidiSyncTick; extern MusECore::MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset; extern double syncRecTempoValQuant; +extern MusECore::MidiSyncContainer midiSyncContainer; + } // namespace MusEGlobal #endif diff -Nru muse-2.1.2/muse/synth.cpp muse-3.0.2+ds1/muse/synth.cpp --- muse-2.1.2/muse/synth.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/synth.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: synth.cpp,v 1.43.2.23 2009/12/15 03:39:58 terminator356 Exp $ // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -20,6 +21,8 @@ // //========================================================= +#include + #include "config.h" #include #include @@ -31,6 +34,7 @@ #include #include #include +#include #include #include @@ -42,7 +46,7 @@ #include "midi.h" #include "midiport.h" #include "mididev.h" -#include "synti/libsynti/mess.h" +#include "synti/libsynti/mess.h" #include "song.h" #include "audio.h" #include "event.h" @@ -52,6 +56,14 @@ #include "midictrl.h" #include "popupmenu.h" #include "globaldefs.h" +#include "midiitransform.h" +#include "mitplugin.h" +#include "helper.h" +#include "gconfig.h" +#include "globals.h" + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_SYNTH(dev, format, args...) //fprintf(dev, format, ##args); namespace MusEGlobal { std::vector synthis; // array of available MusEGlobal::synthis @@ -62,28 +74,63 @@ extern void connectNodes(AudioTrack*, AudioTrack*); bool SynthI::_isVisible=false; -const char* synthTypes[] = { "METRONOME", "MESS", "DSSI", "VST", "VST_NATIVE", "UNKNOWN" }; -QString synthType2String(Synth::Type type) { return QString(synthTypes[type]); } +const char* synthTypes[] = { "METRONOME", "MESS", "DSSI", "Wine VST", "Native VST (synths)", "Native VST (effects)", "LV2 (synths)", "LV2 (effects)", "UNKNOWN" }; +QString synthType2String(Synth::Type type) { return QString(synthTypes[type]); } -Synth::Type string2SynthType(const QString& type) -{ +Synth::Type string2SynthType(const QString& type) +{ for(int i = 0; i < Synth::SYNTH_TYPE_END; ++i) - { + { if(synthType2String((Synth::Type)i) == type) - return (Synth::Type)i; - } + return (Synth::Type)i; + } return Synth::SYNTH_TYPE_END; -} +} + +//-------------------------------- +// SynthIF +//-------------------------------- + +void SynthIF::getMapItem(int channel, int patch, int index, DrumMap& dest_map, int +#ifdef _USE_INSTRUMENT_OVERRIDES_ + overrideType +#endif +) const +{ + // Not found? Search the global mapping list. + const patch_drummap_mapping_list_t* def_pdml = genericMidiInstrument->get_patch_drummap_mapping(channel, true); // Include default. + if(def_pdml) + { + ciPatchDrummapMapping_t ipdm = def_pdml->find(patch, true); // Include default. + if(ipdm == def_pdml->end()) + { + // Not found? Is there a default patch mapping? + #ifdef _USE_INSTRUMENT_OVERRIDES_ + if(overrideType & WorkingDrumMapEntry::InstrumentDefaultOverride) + #endif + ipdm = def_pdml->find(CTRL_PROGRAM_VAL_DONT_CARE, true); // Include default. + + if(ipdm != def_pdml->end()) + { + dest_map = (*ipdm).drummap[index]; + return; + } + } + } + + dest_map = iNewDrumMap[index];; +} //-------------------------------- // Methods for PluginIBase: //-------------------------------- +Plugin::PluginFeatures SynthIF::requiredFeatures() const { return Plugin::NoFeatures; } bool SynthIF::on() const { return true; } // Synth is not part of a rack plugin chain. Always on. void SynthIF::setOn(bool /*val*/) { } unsigned long SynthIF::pluginID() { return 0; } -int SynthIF::id() { return MAX_PLUGINS; } // Set for special block reserved for synth. -QString SynthIF::pluginLabel() const { return QString(); } +int SynthIF::id() { return MAX_PLUGINS; } // Set for special block reserved for synth. +QString SynthIF::pluginLabel() const { return QString(); } QString SynthIF::name() const { return synti->name(); } QString SynthIF::lib() const { return QString(); } QString SynthIF::dirPath() const { return QString(); } @@ -92,10 +139,7 @@ MusECore::AudioTrack* SynthIF::track() { return static_cast < MusECore::AudioTrack* > (synti); } void SynthIF::enableController(unsigned long, bool) { } bool SynthIF::controllerEnabled(unsigned long) const { return true;} -void SynthIF::enable2Controller(unsigned long, bool) { } -bool SynthIF::controllerEnabled2(unsigned long) const { return true; } void SynthIF::enableAllControllers(bool) { } -void SynthIF::enable2AllControllers(bool) { } void SynthIF::updateControllers() { } void SynthIF::activate() { } void SynthIF::deactivate() { } @@ -103,9 +147,9 @@ bool SynthIF::readConfiguration(Xml& /*xml*/, bool /*readPreset*/) { return false; } unsigned long SynthIF::parameters() const { return 0; } unsigned long SynthIF::parametersOut() const { return 0; } -void SynthIF::setParam(unsigned long, float) { } -float SynthIF::param(unsigned long) const { return 0.0; } -float SynthIF::paramOut(unsigned long) const { return 0.0; } +void SynthIF::setParam(unsigned long, double) { } +double SynthIF::param(unsigned long) const { return 0.0; } +double SynthIF::paramOut(unsigned long) const { return 0.0; } const char* SynthIF::paramName(unsigned long) { return NULL; } const char* SynthIF::paramOutName(unsigned long) { return NULL; } LADSPA_PortRangeHint SynthIF::range(unsigned long) @@ -124,6 +168,7 @@ h.UpperBound = 1.0; return h; } +float SynthIF::latency() { return 0.0; } CtrlValueType SynthIF::ctrlValueType(unsigned long) const { return VAL_LINEAR; } CtrlList::Mode SynthIF::ctrlMode(unsigned long) const { return CtrlList::INTERPOLATE; }; @@ -151,6 +196,12 @@ return false; } +void MessSynthIF::guiHeartBeat() +{ + if(_mess) + _mess->guiHeartBeat(); +} + MidiPlayEvent MessSynthIF::receiveEvent() { if (_mess) @@ -165,18 +216,6 @@ return 0; } -void MessSynthIF::getGeometry(int* x, int* y, int* w, int* h) const - { - if (_mess) - _mess->getGeometry(x, y, w, h); - } - -void MessSynthIF::setGeometry(int x, int y, int w, int h) - { - if (_mess) - _mess->setGeometry(x, y, w, h); - } - void MessSynthIF::getNativeGeometry(int* x, int* y, int* w, int* h) const { if (_mess) @@ -197,14 +236,16 @@ static Synth* findSynth(const QString& sclass, const QString& label, Synth::Type type = Synth::SYNTH_TYPE_END) { for (std::vector::iterator i = MusEGlobal::synthis.begin(); - i != MusEGlobal::synthis.end(); ++i) + i != MusEGlobal::synthis.end(); ++i) { - if( ((*i)->baseName() == sclass) && + if( ((*i)->baseName() == sclass) && (label.isEmpty() || ((*i)->name() == label)) && - (type == Synth::SYNTH_TYPE_END || type == (*i)->synthType()) ) + (type == Synth::SYNTH_TYPE_END || type == (*i)->synthType() || (type == Synth::LV2_SYNTH && (*i)->synthType() == Synth::LV2_EFFECT)) ) return *i; } fprintf(stderr, "synthi type:%d class:%s label:%s not found\n", type, sclass.toLatin1().constData(), label.toLatin1().constData()); + QMessageBox::warning(0,"Synth not found!", + "Synth: " + label + " not found, if the project is saved it will be removed from the project"); return 0; } @@ -222,13 +263,21 @@ QString n; n.setNum(s->instances()); QString instance_name = s->name() + "-" + n; - if (si->initInstance(s, instance_name)) { + //Andrew Deryabin: check si->_sif for NULL as synth instance may not be created. + if (si->initInstance(s, instance_name)) { delete si; + fprintf(stderr, "createSynthInstance: synthi class:%s label:%s can not be created\n", sclass.toLatin1().constData(), label.toLatin1().constData()); + QMessageBox::warning(0,"Synth instantiation error!", + "Synth: " + label + " can not be created!"); return 0; - } + } } - else + else { fprintf(stderr, "createSynthInstance: synthi class:%s label:%s not found\n", sclass.toLatin1().constData(), label.toLatin1().constData()); + QMessageBox::warning(0,"Synth not found!", + "Synth: " + label + " not found, if the project is saved it will be removed from the project"); + } + return si; } @@ -236,9 +285,10 @@ // Synth //--------------------------------------------------------- -Synth::Synth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver) - : info(fi), _name(label), _description(descr), _maker(maker), _version(ver) +Synth::Synth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver, Plugin::PluginFeatures reqFeatures) + : info(fi), _name(label), _description(descr), _maker(maker), _version(ver), _requiredFeatures(reqFeatures) { + _requiredFeatures = Plugin::NoFeatures; _instances = 0; } @@ -249,7 +299,7 @@ void* MessSynth::instantiate(const QString& instanceName) { ++_instances; - + MusEGlobal::doSetuid(); QByteArray ba = info.filePath().toLatin1(); const char* path = ba.constData(); @@ -272,7 +322,7 @@ "Unable to find msynth_descriptor() function in plugin " "library file \"%s\": %s.\n" "Are you sure this is a MESS plugin file?\n", - info.filePath().toAscii().constData(), txt); + info.filePath().toLatin1().constData(), txt); MusEGlobal::undoSetuid(); return 0; } @@ -283,7 +333,25 @@ MusEGlobal::undoSetuid(); return 0; } - Mess* mess = _descr->instantiate(MusEGlobal::sampleRate, MusEGlobal::muse, &MusEGlobal::museProject, instanceName.toLatin1().constData()); + QByteArray configPathBA = MusEGlobal::configPath.toLatin1(); + QByteArray museGlobalLibBA = MusEGlobal::museGlobalLib.toLatin1(); + QByteArray museGlobalShareBA = MusEGlobal::museGlobalShare.toLatin1(); + QByteArray museUserBA = MusEGlobal::museUser.toLatin1(); + QByteArray museProjectBA = MusEGlobal::museProject.toLatin1(); + MessConfig mcfg(MusEGlobal::segmentSize, + MusEGlobal::sampleRate, + MusEGlobal::config.minMeter, + MusEGlobal::config.useDenormalBias, + MusEGlobal::denormalBias, + MusEGlobal::config.leftMouseButtonCanDecrease, + configPathBA.constData(), + museGlobalLibBA.constData(), + museGlobalShareBA.constData(), + museUserBA.constData(), + museProjectBA.constData()); + Mess* mess = _descr->instantiate((unsigned long long)MusEGlobal::muse->winId(), + instanceName.toLatin1().constData(), &mcfg); + MusEGlobal::undoSetuid(); return mess; } @@ -297,19 +365,53 @@ { synthesizer = 0; _sif = 0; - _rwFlags = 1; - _openFlags = 1; + + // Allow synths to be readable, ie send midi back to the host. + _rwFlags = 3; + _openFlags = 3; + _readEnable = false; _writeEnable = false; - - _curBankH = 0; - _curBankL = 0; - _curProgram = 0; + setVolume(1.0); + setPan(0.0); + } + +SynthI::SynthI(const SynthI& si, int flags) + : AudioTrack(si, flags) + { + synthesizer = 0; + _sif = 0; + // Allow synths to be readable, ie send midi back to the host. + _rwFlags = 3; + _openFlags = 3; + + _readEnable = false; + _writeEnable = false; setVolume(1.0); setPan(0.0); + + Synth* s = si.synth(); + if (s) { + QString n; + n.setNum(s->instances()); + QString instance_name = s->name() + "-" + n; + if(!initInstance(s, instance_name)) { // false if success + return; + } + } + fprintf(stderr, "SynthI copy ctor: error initializing synth s:%p\n", s); } +//--------------------------------------------------------- +// ~SynthI +//--------------------------------------------------------- + +SynthI::~SynthI() + { + deactivate2(); + deactivate3(); + } //--------------------------------------------------------- // height in arranger @@ -326,13 +428,14 @@ // open //--------------------------------------------------------- -QString SynthI::open() -{ +QString SynthI::open() +{ // Make it behave like a regular midi device. _readEnable = false; _writeEnable = (_openFlags & 0x01); - - return QString("OK"); + + _state = QString("OK"); + return _state; } //--------------------------------------------------------- @@ -343,34 +446,15 @@ { _readEnable = false; _writeEnable = false; -} - -//--------------------------------------------------------- -// putMidiEvent -//--------------------------------------------------------- - -bool SynthI::putEvent(const MidiPlayEvent& ev) -{ - if(_writeEnable) - { - if (MusEGlobal::midiOutputTrace) - { - fprintf(stderr, "MidiOut: Synth: <%s>: ", name().toLatin1().constData()); - ev.dump(); - } - return _sif->putEvent(ev); - } - - return false; + _state = QString("Closed"); } //--------------------------------------------------------- // processMidi //--------------------------------------------------------- -void SynthI::processMidi() +void SynthI::processMidi(unsigned int /*curFrame*/) { - processStuckNotes(); } //--------------------------------------------------------- @@ -383,18 +467,108 @@ MidiDevice::setName(s); } + //--------------------------------------------------------- -// currentProg +// recordEvent //--------------------------------------------------------- -void SynthI::currentProg(unsigned long *prog, unsigned long *bankL, unsigned long *bankH) -{ - if(prog) - *prog = _curProgram; - if(bankL) - *bankL = _curBankL; - if(bankH) - *bankH = _curBankH; +void SynthI::recordEvent(MidiRecordEvent& event) + { + if(MusEGlobal::audio->isPlaying()) + event.setLoopNum(MusEGlobal::audio->loopCount()); + + if (MusEGlobal::midiInputTrace) { + fprintf(stderr, "MidiInput from synth: "); + dumpMPEvent(&event); + } + + int typ = event.type(); + + if(_port != -1) + { + int idin = MusEGlobal::midiPorts[_port].syncInfo().idIn(); + + //--------------------------------------------------- + // filter some SYSEX events + //--------------------------------------------------- + + if (typ == ME_SYSEX) { + const unsigned char* p = event.data(); + int n = event.len(); + if (n >= 4) { + if ((p[0] == 0x7f) + && ((p[1] == 0x7f) || (idin == 0x7f) || (p[1] == idin))) { + if (p[2] == 0x06) { + MusEGlobal::midiSyncContainer.mmcInput(_port, p, n); + return; + } + if (p[2] == 0x01) { + MusEGlobal::midiSyncContainer.mtcInputFull(_port, p, n); + return; + } + } + else if (p[0] == 0x7e) { + MusEGlobal::midiSyncContainer.nonRealtimeSystemSysex(_port, p, n); + return; + } + } + } + else + // Trigger general activity indicator detector. Sysex has no channel, don't trigger. + MusEGlobal::midiPorts[_port].syncInfo().trigActDetect(event.channel()); + } + + // + // process midi event input filtering and + // transformation + // + + processMidiInputTransformPlugins(event); + + if (filterEvent(event, MusEGlobal::midiRecordType, false)) + return; + + if (!applyMidiInputTransformation(event)) { + if (MusEGlobal::midiInputTrace) + fprintf(stderr, " midi input transformation: event filtered\n"); + return; + } + +// TODO Maybe support this later, but for now it's not a good idea to control from the synths. +// Especially since buggy ones may repeat events multiple times. +#if 1 + // + // transfer noteOn and Off events to gui for step recording and keyboard + // remote control (changed by flo93: added noteOff-events) + // + if (typ == ME_NOTEON) { + int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); + MusEGlobal::song->putEvent(pv); + } + else if (typ == ME_NOTEOFF) { + int pv = ((event.dataA() & 0xff)<<8) + (0x00); //send an event with velo=0 + MusEGlobal::song->putEvent(pv); + } +#endif + + // Do not bother recording if it is NOT actually being used by a port. + // Because from this point on, process handles things, by selected port. + if(_port == -1) + return; + + // 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(event)) + fprintf(stderr, "SynthI::recordEvent: fifo channel %d overflow\n", ch); + } + + +RouteCapabilitiesStruct SynthI::routeCapabilities() const +{ + RouteCapabilitiesStruct s = AudioTrack::routeCapabilities(); + s._trackChannels._inChannels = totalInChannels(); + s._trackChannels._inRoutable = (s._trackChannels._inChannels != 0); + return s; } //--------------------------------------------------------- @@ -404,7 +578,7 @@ bool MessSynthIF::init(Synth* s, SynthI* si) { _mess = (Mess*)((MessSynth*)s)->instantiate(si->name()); - + return (_mess == 0); } @@ -442,10 +616,16 @@ setName(instanceName); // set midi device name setIName(instanceName); // set instrument name _sif = s->createSIF(this); - + + //Andrew Deryabin: add check for NULL here to get rid of segfaults + if(_sif == NULL) + { + return true; //true if error (?) + } + AudioTrack::setTotalOutChannels(_sif->totalOutChannels()); AudioTrack::setTotalInChannels(_sif->totalInChannels()); - + //--------------------------------------------------- // read available controller from synti //--------------------------------------------------- @@ -453,7 +633,7 @@ int id = 0; MidiControllerList* cl = MidiInstrument::controller(); for (;;) { - const char* name; + QString name; int ctrl; int min; int max; @@ -461,25 +641,22 @@ id = _sif->getControllerInfo(id, &name, &ctrl, &min, &max, &initval); if (id == 0) break; - - - // Added by T356. Override existing program controller. + // Override existing program controller. iMidiController i = cl->end(); if(ctrl == CTRL_PROGRAM) { - for(i = cl->begin(); i != cl->end(); ++i) + for(i = cl->begin(); i != cl->end(); ++i) { if(i->second->num() == CTRL_PROGRAM) { delete i->second; - cl->erase(i); - + cl->del(i); break; } } - } - - MidiController* c = new MidiController(QString(name), ctrl, min, max, initval); + } + + MidiController* c = new MidiController(name, ctrl, min, max, initval, initval); cl->add(c); } @@ -488,7 +665,7 @@ if (!iel->empty()) { for (iEvent i = iel->begin(); i != iel->end(); ++i) { Event ev = i->second; - + // p4.0.27 A kludge to support old midistates by wrapping them in the proper header. if(ev.type() == Sysex && _tmpMidiStateVersion < SYNTH_MIDI_STATE_SAVE_VERSION) { @@ -507,36 +684,120 @@ memcpy(d + hdrsz, data, len); ev.setData(d, newlen); delete[] d; - } + } } } - - MidiPlayEvent pev(0, 0, 0, ev); - if (_sif->putEvent(pev)) - break; // try later + + MidiPlayEvent pev = ev.asMidiPlayEvent(0, 0, 0); + _userEventBuffers->put(pev); } iel->clear(); } unsigned long idx = 0; - for (std::vector::iterator i = initParams.begin(); i != initParams.end(); ++i, ++idx) + for (std::vector::iterator i = initParams.begin(); i != initParams.end(); ++i, ++idx) _sif->setParameter(idx, *i); - - // p3.3.40 Since we are done with the (sometimes huge) initial parameters list, clear it. + + // p3.3.40 Since we are done with the (sometimes huge) initial parameters list, clear it. // TODO: Decide: Maybe keep them around for a 'reset to previously loaded values' (revert) command? ... - initParams.clear(); - + initParams.clear(); + + //call SynthIF::setCustomData(...) with accumulated custom params + _sif->setCustomData(accumulatedCustomParams); + + accumulatedCustomParams.clear(); + return false; } //--------------------------------------------------------- +// pbForwardShiftFrames +//--------------------------------------------------------- + +unsigned int SynthI::pbForwardShiftFrames() const +{ + return MusEGlobal::segmentSize; +} + +//--------------------------------------------------------- // getControllerInfo //--------------------------------------------------------- -int MessSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval) +int MessSynthIF::getControllerInfo(int id, QString* name, int* ctrl, int* min, int* max, int* initval) { - return _mess->getControllerInfo(id, name, ctrl, min, max, initval); - } + int i_ctrl; + int i_min; + int i_max; + int i_initval; + const char* s_name; + + int ret = _mess->getControllerInfo(id, &s_name, &i_ctrl, &i_min, &i_max, &i_initval); + + if(ctrl) + *ctrl = i_ctrl; + if(min) + *min = i_min; + if(max) + *max = i_max; + if(initval) + *initval = i_initval; + if(name) + *name = QString(s_name); + + return ret; + } + +void MessSynthIF::getMapItem(int channel, int patch, int index, DrumMap& dest_map, int +#ifdef _USE_INSTRUMENT_OVERRIDES_ + overrideType +#endif +) const +{ + // Could just call the ancestor, but we can save the double string copy by optimizing below... + // SynthIF::getMapItem(channel, patch, index, dest_map); + + DrumMap* dm = NULL; + // Not found? Search the global mapping list. + patch_drummap_mapping_list_t* def_pdml = genericMidiInstrument->get_patch_drummap_mapping(channel, true); // Include default. + if(def_pdml) + { + ciPatchDrummapMapping_t ipdm = def_pdml->find(patch, true); // Include default. + if(ipdm == def_pdml->end()) + { + // Not found? Is there a default patch mapping? + #ifdef _USE_INSTRUMENT_OVERRIDES_ + if(overrideType & WorkingDrumMapEntry::InstrumentDefaultOverride) + #endif + ipdm = def_pdml->find(CTRL_PROGRAM_VAL_DONT_CARE, true); // Include default. + + if(ipdm != def_pdml->end()) + dm = &(*ipdm).drummap[index]; + } + } + if(!dm) + dm = &iNewDrumMap[index]; + DrumMap& base_dm = *dm; + dest_map.vol = base_dm.vol; + dest_map.quant = base_dm.quant; + dest_map.len = base_dm.len; + dest_map.anote = base_dm.anote; + dest_map.enote = base_dm.enote; + dest_map.channel = base_dm.channel; + dest_map.port = base_dm.port; + dest_map.lv1 = base_dm.lv1; + dest_map.lv2 = base_dm.lv2; + dest_map.lv3 = base_dm.lv3; + dest_map.lv4 = base_dm.lv4; + dest_map.hide = base_dm.hide; + dest_map.mute = base_dm.mute; + + const char* str; + // true = Want percussion names, not melodic. + if(_mess->getNoteSampleName(true, channel, patch, index, &str)) + dest_map.name = QString(str); + else + dest_map.name = base_dm.name; +} //--------------------------------------------------------- // SynthI::deactivate @@ -557,20 +818,30 @@ void SynthI::deactivate3() { - _sif->deactivate3(); - + + //Andrew Deryabin: add checks for NULLness of _sif and syntheeizer instances + if(_sif) + { + _sif->deactivate3(); + } + //synthesizer->incInstances(-1); // Moved below by Tim. p3.3.14 - + if(MusEGlobal::debugMsg) fprintf(stderr, "SynthI::deactivate3 deleting _sif...\n"); - - delete _sif; - _sif = 0; - + + if(_sif) + { + delete _sif; + _sif = 0; + } + if(MusEGlobal::debugMsg) fprintf(stderr, "SynthI::deactivate3 decrementing synth instances...\n"); - - synthesizer->incInstances(-1); + + if(synthesizer) + synthesizer->incInstances(-1); + } void MessSynthIF::deactivate3() @@ -582,17 +853,6 @@ } //--------------------------------------------------------- -// ~SynthI -//--------------------------------------------------------- - -SynthI::~SynthI() - { - deactivate2(); - deactivate3(); - } - - -//--------------------------------------------------------- // initMidiSynth // search for software MusEGlobal::synthis and advertise //--------------------------------------------------------- @@ -610,10 +870,10 @@ QFileInfo* fi; while(it!=list.end()) { fi = &*it; - + QByteArray ba = fi->filePath().toLatin1(); const char* path = ba.constData(); - + // load Synti dll void* handle = dlopen(path, RTLD_NOW); if (handle == 0) { @@ -623,7 +883,7 @@ } typedef const MESS* (*MESS_Function)(); MESS_Function msynth = (MESS_Function)dlsym(handle, "mess_descriptor"); - + if (!msynth) { #if 1 const char *txt = dlerror(); @@ -634,7 +894,7 @@ "Are you sure this is a MESS plugin file?\n", path, txt); } - #endif + #endif dlclose(handle); ++it; continue; @@ -646,9 +906,9 @@ ++it; continue; } - + MusEGlobal::synthis.push_back(new MessSynth(*fi, QString(descr->name), QString(descr->description), QString(""), QString(descr->version))); - + dlclose(handle); ++it; } @@ -669,32 +929,19 @@ SynthI* si = createSynthInstance(sclass, label, type); if(!si) return 0; - + int idx = insertAt ? _tracks.index(insertAt) : -1; - - insertTrack1(si, idx); - - msgInsertTrack(si, idx, true); // add to instance list - - insertTrack3(si, idx); OutputList* ol = MusEGlobal::song->outputs(); - // add default route to master (first audio output) + // Add an omnibus default route to master (first audio output) if (!ol->empty()) { AudioOutput* ao = ol->front(); - // Make sure the route channel and channels are valid. - MusEGlobal::audio->msgAddRoute(Route((AudioTrack*)si, 0, ((AudioTrack*)si)->channels()), Route(ao, 0, ((AudioTrack*)si)->channels())); - - MusEGlobal::audio->msgUpdateSoloStates(); + // AddTrack operation 'mirrors' the route. + static_cast(si)->outRoutes()->push_back(Route(ao)); } - // DELETETHIS 5 - // Now that the track has been added to the lists in insertTrack2(), - // if it's a dssi synth, OSC can find the synth, and initialize (and show) its native gui. - // No, initializing OSC without actually showing the gui doesn't work, at least for - // dssi-vst plugins - without showing the gui they exit after ten seconds. - //si->initGui(); - + MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddTrack, idx, si)); + return si; } @@ -709,11 +956,13 @@ xml.strTag(level, "synthType", synthType2String(synth()->synthType())); xml.strTag(level, "class", synth()->baseName()); - + // To support plugins like dssi-vst where all the baseNames are the same 'dssi-vst' and the label is the name of the dll file. - // Added by Tim. p3.3.16 xml.strTag(level, "label", synth()->name()); + if(openFlags() != 1) + xml.intTag(level, "openFlags", openFlags()); + //--------------------------------------------- // if soft synth is attached to a midi port, // write out port number @@ -744,8 +993,6 @@ _stringParamMap.write(level, xml, "stringParam"); - xml.tag(level, "curProgram bankH=\"%ld\" bankL=\"%ld\" prog=\"%ld\"/", _curBankH, _curBankL, _curProgram); - _sif->write(level, xml); xml.etag(level, "SynthI"); } @@ -779,45 +1026,6 @@ } //--------------------------------------------------------- -// SynthI::readProgram -//--------------------------------------------------------- - -void SynthI::readProgram(Xml& xml, const QString& name) -{ - for (;;) - { - Xml::Token token = xml.parse(); - const QString tag = xml.s1(); - switch (token) - { - case Xml::Error: - case Xml::End: - return; - case Xml::TagStart: - xml.unknown(name.toAscii().constData()); - break; - case Xml::Attribut: - if(tag == "bankH") - _curBankH = xml.s2().toUInt(); - else - if(tag == "bankL") - _curBankL = xml.s2().toUInt(); - else - if(tag == "prog") - _curProgram = xml.s2().toUInt(); - else - xml.unknown(name.toAscii().constData()); - break; - case Xml::TagEnd: - if(tag == name) - return; - default: - break; - } - } -} - -//--------------------------------------------------------- // SynthI::read //--------------------------------------------------------- @@ -831,6 +1039,7 @@ bool startgui = false; bool startngui = false; QRect r, nr; + int oflags = 1; for (;;) { Xml::Token token = xml.parse(); @@ -846,6 +1055,9 @@ sclass = xml.parse1(); else if (tag == "label") label = xml.parse1(); + else if (tag == "openFlags") + oflags = xml.parseInt(); + else if (tag == "port") port = xml.parseInt(); else if (tag == "guiVisible") @@ -855,58 +1067,64 @@ else if (tag == "midistate") readMidiState(xml); else if (tag == "param") { - float val = xml.parseFloat(); + double val = xml.parseDouble(); initParams.push_back(val); } - else if (tag == "stringParam") + else if (tag == "stringParam") _stringParamMap.read(xml, tag); - else if (tag == "curProgram") - readProgram(xml, tag); else if (tag == "geometry") r = readGeometry(xml, tag); else if (tag == "nativeGeometry") nr = readGeometry(xml, tag); + else if (tag == "customData") { //just place tag contents in accumulatedCustomParams + QString customData = xml.parse1(); + if(!customData.isEmpty()){ + accumulatedCustomParams.push_back(customData); + } + } else if (AudioTrack::readProperties(xml, tag)) xml.unknown("softSynth"); break; case Xml::TagEnd: if (tag == "SynthI") { - + // NOTICE: This is a hack to quietly change songs to use the new 'fluid_synth' name instead of 'fluidsynth'. // Recent linker changes required the name change in fluidsynth's cmakelists. Nov 8, 2011 By Tim. - if(sclass == QString("fluidsynth") && + if(sclass == QString("fluidsynth") && (type == Synth::SYNTH_TYPE_END || type == Synth::MESS_SYNTH) && (label.isEmpty() || label == QString("FluidSynth")) ) sclass = QString("fluid_synth"); - + Synth* s = findSynth(sclass, label, type); if (s == 0) return; if (initInstance(s, name())) return; - MusEGlobal::song->insertTrack0(this, -1); + setOpenFlags(oflags); + MusEGlobal::song->insertTrack0(this, -1); + if (port != -1 && port < MIDI_PORTS) MusEGlobal::midiPorts[port].setMidiDevice(this); - + // DELETETHIS 5 // Now that the track has been added to the lists in insertTrack2(), // if it's a dssi synth, OSC can find the synth, and initialize (and show) its native gui. - // No, initializing OSC without actually showing the gui doesn't work, at least for + // No, initializing OSC without actually showing the gui doesn't work, at least for // dssi-vst plugins - without showing the gui they exit after ten seconds. //initGui(); - showNativeGui(startngui); setNativeGeometry(nr.x(), nr.y(), nr.width(), nr.height()); - + showNativeGui(startngui); + mapRackPluginsToControllers(); - - showGui(startgui); + setGeometry(r.x(), r.y(), r.width(), r.height()); - - // Now that the track has been added to the lists in insertTrack2(), if it's a dssi synth + showGui(startgui); + + // Now that the track has been added to the lists in insertTrack2(), if it's a dssi synth // OSC can find the track and its plugins, and start their native guis if required... showPendingPluginNativeGuis(); - + return; } default: @@ -923,7 +1141,7 @@ QString MessSynthIF::getPatchName(int channel, int prog, bool drum) const { if (_mess) - return _mess->getPatchName(channel, prog, drum); + return QString(_mess->getPatchName(channel, prog, drum)); return ""; } @@ -976,33 +1194,29 @@ { if(_sif) _sif->preProcessAlways(); - _processed = false; - - // TODO: p4.0.15 Tim. Erasure of already-played events was moved from Audio::processMidi() - // to each of the midi devices - ALSA, Jack, or Synth in SynthI::getData() below. + _processed = false; + + // TODO: p4.0.15 Tim. Erasure of already-played events was moved from Audio::processMidi() + // to each of the midi devices - ALSA, Jack, or Synth in SynthI::getData() below. // If a synth track is 'off', AudioTrack::copyData() does not call our getData(). // So there is no processing of midi play events, or putEvent FIFOs. // Hence the play events list and putEvent FIFOs will then accumulate events, sometimes - // thousands. Only when the Synth track is turned on again, are all these events + // thousands. Only when the Synth track is turned on again, are all these events // processed. Whether or not we want this is a question. // // If we DON'T want the events to accumulate, we NEED this following piece of code. // Without this code: When a song is loaded, if a Synth track is off, various controller init events - // can remain queued up so that when the Synth track is turned on, those initializations + // can remain queued up so that when the Synth track is turned on, those initializations // will be processed. Otherwise we, or the user, will have to init every time the track is turned on. - // Con: Thousands of events can accumulate. For example selecting "midi -> Reset Instr." sends a flood - // of 2048 note-off events, one for each note in each channel! Each time, the 2048, 4096, 8192 etc. + // Con: Thousands of events can accumulate. For example selecting "midi -> Reset Instr." sends a flood + // of 2048 note-off events, one for each note in each channel! Each time, the 2048, 4096, 8192 etc. // events remain in the list. // Variation: Maybe allow certain types, or groups, of events through, especially bulk init or note offs. if(off()) { - // Clear any accumulated play events. - //playEvents()->clear(); DELETETHIS - _playEvents.clear(); - // Eat up any fifo events. - //while(!eventFifo.isEmpty()) DELETETHIS - // eventFifo.get(); - eventFifo.clear(); // Clear is the same but faster AND safer, right? + // Eat up any buffer events. + _playbackEventBuffers->clearRead(); + //_userEventBuffers->clearRead(); } } @@ -1023,75 +1237,122 @@ int p = midiPort(); MidiPort* mp = (p != -1) ? &MusEGlobal::midiPorts[p] : 0; - - iMPEvent ie = _playEvents.begin(); - - ie = _sif->getData(mp, &_playEvents, ie, pos, ports, n, buffer); - - // p4.0.15 We are done with these events. Let us erase them here instead of Audio::processMidi. - // That way we can simply set the next play event to the beginning. - // This also allows other events to be inserted without the problems caused by the next play event - // being at the 'end' iterator and not being *easily* set to some new place beginning of the newer insertions. - // The way that MPEventList sorts made it difficult to predict where the iterator of the first newly inserted items was. - // The erasure in Audio::processMidi was missing some events because of that. - _playEvents.erase(_playEvents.begin(), ie); - + + _sif->getData(mp, pos, ports, n, buffer); + return true; } -iMPEvent MessSynthIF::getData(MidiPort* mp, MPEventList* el, iMPEvent i, unsigned pos, int /*ports*/, unsigned n, float** buffer) +bool MessSynthIF::getData(MidiPort* /*mp*/, unsigned pos, int /*ports*/, unsigned n, float** buffer) { - //prevent compiler warning: comparison of signed/unsigned - int curPos = pos; - int endPos = pos + n; - int off = pos; - int frameOffset = MusEGlobal::audio->getFrameOffset(); - - for (; i != el->end(); ++i) { - int evTime = i->time(); - if (evTime == 0) - evTime=abs(frameOffset); // will cause frame to be zero, problem? - - int frame = evTime - abs(frameOffset); - - if (frame >= endPos) { - fprintf(stderr, "frame > endPos!! frame = %d >= endPos %d, i->time() %d, frameOffset %d curPos=%d\n", frame, endPos, i->time(), frameOffset,curPos); - continue; - } - - if (frame > curPos) { - if (frame < (int) pos) - fprintf(stderr, "should not happen: missed event %d\n", pos -frame); - else - { - if (!_mess) - fprintf(stderr, "should not happen - no _mess\n"); - else - { - _mess->process(buffer, curPos-pos, frame - curPos); - } - } - curPos = frame; - } - - if (mp) - mp->sendEvent(*i); - else { - if (putEvent(*i)) - break; - } + const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); + unsigned int curPos = 0; + unsigned int frame = 0; + + // Get the state of the stop flag. + const bool do_stop = synti->stopFlag(); + + MidiPlayEvent buf_ev; + + // Transfer the user lock-free buffer events to the user sorted multi-set. + // False = don't use the size snapshot, but update it. + const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); + for(unsigned int i = 0; i < usr_buf_sz; ++i) + { + if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) + synti->_outUserEvents.insert(buf_ev); + } + + // Transfer the playback lock-free buffer events to the playback sorted multi-set. + const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); + for(unsigned int i = 0; i < pb_buf_sz; ++i) + { + // Are we stopping? Just remove the item. + if(do_stop) + synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); + // Otherwise get the item. + else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) + synti->_outPlaybackEvents.insert(buf_ev); } - - if (endPos - curPos) + + // Are we stopping? + if(do_stop) { - if (!_mess) - fprintf(stderr, "should not happen - no _mess\n"); - else - { - _mess->process(buffer, curPos - off, endPos - curPos); - } + // Transport has stopped, purge ALL further scheduled playback events now. + synti->_outPlaybackEvents.clear(); + // Reset the flag. + synti->setStopFlag(false); + } + + iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); + iMPEvent impe_us = synti->_outUserEvents.begin(); + bool using_pb; + + while(1) + { + if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) + using_pb = *impe_pb < *impe_us; + else if(impe_pb != synti->_outPlaybackEvents.end()) + using_pb = true; + else if(impe_us != synti->_outUserEvents.end()) + using_pb = false; + else break; + + const MidiPlayEvent& ev = using_pb ? *impe_pb : *impe_us; + + const unsigned int evTime = ev.time(); + if(evTime < syncFrame) + { + if(evTime != 0) + fprintf(stderr, "MessSynthIF::getData() evTime:%u < syncFrame:%u!! curPos=%d\n", + evTime, syncFrame, curPos); + frame = 0; + } + else + frame = evTime - syncFrame; + + // Event is for future? + if(frame >= n) + { + DEBUG_SYNTH(stderr, "MessSynthIF::getData(): Event for future, breaking loop: frame:%u n:%d evTime:%u syncFrame:%u curPos:%d\n", + frame, n, evTime, syncFrame, curPos); + //continue; + break; + } + + if(frame > curPos) + { + if (!_mess) + fprintf(stderr, "MessSynthIF::getData() should not happen - no _mess\n"); + else + _mess->process(pos, buffer, curPos, frame - curPos); + curPos = frame; + } + + // If putEvent fails, although we would like to not miss events by keeping them + // until next cycle and trying again, that can lead to a large backup of events + // over a long time. So we'll just... miss them. + //putEvent(ev); + //synti->putEvent(ev); + processEvent(ev); + + // Done with ring buffer event. Remove it from FIFO. + // C++11. + if(using_pb) + impe_pb = synti->_outPlaybackEvents.erase(impe_pb); + else + impe_us = synti->_outUserEvents.erase(impe_us); + } + + if(curPos < n) + { + if (!_mess) + fprintf(stderr, "MessSynthIF::getData() should not happen - no _mess\n"); + else + _mess->process(pos, buffer, curPos, n - curPos); } - return i; + + return true; } //--------------------------------------------------------- @@ -1099,28 +1360,128 @@ // return true on error (busy) //--------------------------------------------------------- -bool MessSynthIF::putEvent(const MidiPlayEvent& ev) +bool MessSynthIF::processEvent(const MidiPlayEvent& ev) +{ + if (!_mess) + return true; + + if (MusEGlobal::midiOutputTrace) { - //if (MusEGlobal::midiOutputTrace) DELETETHIS or re-enable? - //{ - // printf("MidiOut: MESS: <%s>: ", synti->name().toLatin1().constData()); - // ev.dump(); - //} - if (_mess) - return _mess->processEvent(ev); - return true; + fprintf(stderr, "MidiOut: MESS: <%s>: ", synti->name().toLatin1().constData()); + dumpMPEvent(&ev); } + + int chn = ev.channel(); + int a = ev.dataA(); + int b = ev.dataB(); + + switch(ev.type()) + { + // Special for program, hi bank, and lo bank: Virtually all synths encapsulate banks and program together + // call rather than breaking them out into three separate controllers. Therefore we need to 'compose' a + // CTRL_PROGRAM which supports the full complement of hi/lo bank and program. The synths should therefore NEVER + // be allowed to receive ME_PROGRAM or CTRL_HBANK or CTRL_LBANK alone (it also saves them the management trouble)... + // TODO: Try to move this into the individual synths and since we must not talk directly to them, rely on feedback + // from them (in midi.cpp) to update our HOST current program absolutely when they change their own program ! + case ME_PROGRAM: + { + int hb; + int lb; + synti->currentProg(chn, NULL, &lb, &hb); + synti->setCurrentProg(chn, a & 0xff, lb, hb); + // Only if there's something to change... + //if(hb < 128 || lb < 128 || a < 128) + //{ + if(hb > 127) // Map "dont care" to 0 + hb = 0; + if(lb > 127) + lb = 0; + if(a > 127) + a = 0; + const int full_prog = (hb << 16) | (lb << 8) | a; + return _mess->processEvent(MidiPlayEvent(ev.time(), ev.port(), chn, ME_CONTROLLER, CTRL_PROGRAM, full_prog)); + //} + //return false; + } + break; + case ME_CONTROLLER: + { + // Our internal hwCtrl controllers support the 'unknown' value. + // Don't send 'unknown' values to the driver. Ignore and return no error. + if(b == CTRL_VAL_UNKNOWN) + return false; + + if(a == CTRL_PROGRAM) + { + int hb = (b >> 16) & 0xff; + int lb = (b >> 8) & 0xff; + int pr = b & 0xff; + synti->setCurrentProg(chn, pr, lb, hb); + // Only if there's something to change... + //if(hb < 128 || lb < 128 || pr < 128) + //{ + if(hb > 127) + hb = 0; + if(lb > 127) + lb = 0; + if(pr > 127) + pr = 0; + const int full_prog = (hb << 16) | (lb << 8) | pr; + return _mess->processEvent(MidiPlayEvent(ev.time(), ev.port(), chn, ME_CONTROLLER, CTRL_PROGRAM, full_prog)); + //} + //return false; + } + + if(a == CTRL_HBANK) + { + int lb; + int pr; + synti->currentProg(chn, &pr, &lb, NULL); + synti->setCurrentProg(chn, pr, lb, b & 0xff); + // Only if there's something to change... + //if(b < 128 || lb < 128 || pr < 128) + //{ + if(b > 127) + b = 0; + if(lb > 127) + lb = 0; + if(pr > 127) + pr = 0; + const int full_prog = (b << 16) | (lb << 8) | pr; + return _mess->processEvent(MidiPlayEvent(ev.time(), ev.port(), chn, ME_CONTROLLER, CTRL_PROGRAM, full_prog)); + //} + //return false; + } + + if(a == CTRL_LBANK) + { + int hb; + int pr; + synti->currentProg(chn, &pr, NULL, &hb); + synti->setCurrentProg(chn, pr, b & 0xff, hb); + // Only if there's something to change... + //if(hb < 128 || b < 128 || pr < 128) + //{ + if(hb > 127) + hb = 0; + if(b > 127) + b = 0; + if(pr > 127) + pr = 0; + const int full_prog = (hb << 16) | (b << 8) | pr; + return _mess->processEvent(MidiPlayEvent(ev.time(), ev.port(), chn, ME_CONTROLLER, CTRL_PROGRAM, full_prog)); + //} + //return false; + } + } + break; + + default: + break; + } + return _mess->processEvent(ev); +} -//unsigned long MessSynthIF::uniqueID() const DELETETHIS -//{ -// return _mess ? _mess->uniqueID() : 0; -//} - -//MidiPlayEvent& MessSynthIF::wrapOldMidiStateVersion(MidiPlayEvent& e) const -//{ -// return _mess ? _mess->wrapOldMidiStateVersion(e) : e; -//} - int MessSynthIF::oldMidiStateHeader(const unsigned char** data) const { return _mess ? _mess->oldMidiStateHeader(data) : 0; diff -Nru muse-2.1.2/muse/synth.h muse-3.0.2+ds1/muse/synth.h --- muse-2.1.2/muse/synth.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/synth.h 2018-01-29 20:07:03.000000000 +0000 @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: synth.h,v 1.22.2.12 2009/12/06 10:05:00 terminator356 Exp $ // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 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 @@ -66,15 +67,19 @@ QString _description; QString _maker; QString _version; + Plugin::PluginFeatures _requiredFeatures; public: - enum Type { METRO_SYNTH=0, MESS_SYNTH, DSSI_SYNTH, VST_SYNTH, VST_NATIVE_SYNTH, SYNTH_TYPE_END }; + enum Type { METRO_SYNTH=0, MESS_SYNTH, DSSI_SYNTH, VST_SYNTH, VST_NATIVE_SYNTH, VST_NATIVE_EFFECT, LV2_SYNTH, LV2_EFFECT, SYNTH_TYPE_END }; + + Synth(const QFileInfo& fi, + QString label, QString descr, QString maker, QString ver, + Plugin::PluginFeatures reqFeatures = Plugin::NoFeatures); - Synth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver); - virtual ~Synth() {} virtual Type synthType() const = 0; + virtual Plugin::PluginFeatures requiredFeatures() const { return _requiredFeatures; } int instances() const { return _instances; } virtual void incInstances(int val) { _instances += val; } QString completeBaseName() { return info.completeBaseName(); } // ddskrjo @@ -87,7 +92,7 @@ QString description() const { return _description; } QString version() const { return _version; } QString maker() const { return _maker; } - + virtual SynthIF* createSIF(SynthI*) = 0; }; @@ -99,14 +104,14 @@ const MESS* _descr; public: - MessSynth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver) : + MessSynth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver) : Synth(fi, label, descr, maker, ver) { _descr = 0; } - + virtual ~MessSynth() {} virtual Type synthType() const { return MESS_SYNTH; } virtual void* instantiate(const QString&); - + virtual SynthIF* createSIF(SynthI*); }; @@ -114,42 +119,33 @@ //--------------------------------------------------------- // SynthIF // synth instance interface -// NOTICE: If implementing sysex support, be sure to make a unique ID and use +// NOTICE: If implementing sysex support, be sure to make a unique ID and use // it to filter out unrecognized sysexes. Headers should be constructed as: -// MUSE_SYNTH_SYSEX_MFG_ID The MusE SoftSynth Manufacturer ID byte (0x7C) found in midi.h +// MUSE_SYNTH_SYSEX_MFG_ID The MusE SoftSynth Manufacturer ID byte (0x7C) found in midi.h // 0xNN The synth's unique ID byte //--------------------------------------------------------- class SynthIF : public PluginIBase { - + protected: SynthI* synti; - + public: SynthIF(SynthI* s) { synti = s; } virtual ~SynthIF() {} // This is only a kludge required to support old songs' midistates. Do not use in any new synth. - virtual int oldMidiStateHeader(const unsigned char** /*data*/) const { return 0; } + virtual int oldMidiStateHeader(const unsigned char** /*data*/) const { return 0; } - virtual bool initGui() = 0; virtual void guiHeartBeat() = 0; - virtual bool guiVisible() const = 0; - virtual void showGui(bool v) = 0; + virtual void showGui(bool v) { if(synti && hasGui()) PluginIBase::showGui(v); } virtual bool hasGui() const = 0; - virtual bool nativeGuiVisible() const = 0; - virtual void showNativeGui(bool v) = 0; virtual bool hasNativeGui() const = 0; - virtual void getGeometry(int*, int*, int*, int*) const = 0; - virtual void setGeometry(int, int, int, int) = 0; - virtual void getNativeGeometry(int*, int*, int*, int*) const = 0; - virtual void setNativeGeometry(int, int, int, int) = 0; virtual void preProcessAlways() = 0; - virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned n, float** buffer) = 0; - virtual bool putEvent(const MidiPlayEvent& ev) = 0; + virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned n, float** buffer) = 0; virtual MidiPlayEvent receiveEvent() = 0; virtual int eventsPending() const = 0; - + virtual int channels() const = 0; virtual int totalOutChannels() const = 0; virtual int totalInChannels() const = 0; @@ -157,14 +153,19 @@ virtual QString getPatchName(int, int, bool) const = 0; virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool) = 0; virtual void write(int level, Xml& xml) const = 0; - virtual float getParameter(unsigned long idx) const = 0; - virtual void setParameter(unsigned long idx, float value) = 0; - virtual int getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval) = 0; + virtual double getParameter(unsigned long idx) const = 0; + virtual void setParameter(unsigned long idx, double value) = 0; + virtual int getControllerInfo(int id, QString* name, int* ctrl, int* min, int* max, int* initval) = 0; + // Returns a map item with members filled from either the original or working map item, + // depending on which Field flags are set. The returned map includes any requested + // WorkingDrumMapEntry::OverrideType instrument overrides. Channel can be -1 meaning default. + virtual void getMapItem(int /*channel*/, int /*patch*/, int /*index*/, DrumMap& /*dest_map*/, int /*overrideType*/ = WorkingDrumMapEntry::AllOverrides) const; //------------------------- // Methods for PluginIBase: //------------------------- - + + virtual Plugin::PluginFeatures requiredFeatures() const; virtual bool on() const; virtual void setOn(bool val); virtual unsigned long pluginID(); @@ -175,13 +176,10 @@ virtual QString dirPath() const; virtual QString fileName() const; virtual QString titlePrefix() const; - virtual MusECore::AudioTrack* track(); + virtual AudioTrack* track(); virtual void enableController(unsigned long i, bool v = true); virtual bool controllerEnabled(unsigned long i) const; - virtual void enable2Controller(unsigned long i, bool v = true); - virtual bool controllerEnabled2(unsigned long i) const; virtual void enableAllControllers(bool v = true); - virtual void enable2AllControllers(bool v = true); virtual void updateControllers(); virtual void activate(); virtual void deactivate(); @@ -191,14 +189,15 @@ virtual unsigned long parameters() const; virtual unsigned long parametersOut() const; - virtual void setParam(unsigned long i, float val); - virtual float param(unsigned long i) const; - virtual float paramOut(unsigned long i) const; + virtual void setParam(unsigned long i, double val); + virtual double param(unsigned long i) const; + virtual double paramOut(unsigned long i) const; virtual const char* paramName(unsigned long i); virtual const char* paramOutName(unsigned long i); // FIXME TODO: Either find a way to agnosticize these two ranges, or change them from ladspa ranges to a new MusE range class. virtual LADSPA_PortRangeHint range(unsigned long i); virtual LADSPA_PortRangeHint rangeOut(unsigned long i); + virtual float latency(); virtual CtrlValueType ctrlValueType(unsigned long i) const; virtual CtrlList::Mode ctrlMode(unsigned long i) const; }; @@ -219,48 +218,52 @@ protected: Synth* synthesizer; - // MidiFifo putFifo; // Moved into MidiDevice p4.0.15 + + MPEventList _outPlaybackEvents; + MPEventList _outUserEvents; - // List of initial floating point parameters, for synths which use them. + // List of initial floating point parameters, for synths which use them. // Used once upon song reload, then discarded. - std::vector initParams; + std::vector initParams; + //custom params in xml song file , synth tag, that will be passed to new SynthIF:setCustomData(Xml &) method + //now only lv2host uses them, others simply ignore + std::vector accumulatedCustomParams; // Initial, and running, string parameters for synths which use them, like dssi. - StringParamMap _stringParamMap; - - // Current bank and program for synths which use them, like dssi. - // In cases like dssi which have no 'hi' and 'lo' bank, just use _curBankL. - unsigned long _curBankH; - unsigned long _curBankL; - unsigned long _curProgram; + StringParamMap _stringParamMap; void preProcessAlways(); bool getData(unsigned a, int b, unsigned c, float** data); - + // Returns the number of frames to shift forward output event scheduling times when putting events + // into the eventFifos. + virtual unsigned int pbForwardShiftFrames() const; + virtual QString open(); virtual void close(); - - virtual bool putMidiEvent(const MidiPlayEvent&) {return true;} - virtual Track* newTrack() const { return 0; } public: friend class SynthIF; friend class MessSynthIF; friend class DssiSynthIF; + friend class LV2SynthIF; friend class VstSynthIF; friend class VstNativeSynthIF; - + friend class MetronomeSynthIF; + SynthI(); + SynthI(const SynthI& si, int flags); virtual ~SynthI(); - SynthI* clone(int /*flags*/) const { return new SynthI(*this); } + SynthI* clone(int flags) const { return new SynthI(*this, flags); } + + virtual inline MidiDeviceType deviceType() const { return SYNTH_MIDI; } + // Virtual so that inheriters (synths etc) can return whatever they want. + virtual inline NoteOffMode noteOffMode() const { return NoteOffAll; } - virtual inline int deviceType() const { return SYNTH_MIDI; } - SynthIF* sif() const { return _sif; } bool initInstance(Synth* s, const QString& instanceName); + virtual float latency(int channel) { return _sif->latency() + AudioTrack::latency(channel); } - void readProgram(Xml&, const QString&); void read(Xml&); virtual void write(int, Xml&) const; @@ -270,18 +273,36 @@ Synth* synth() const { return synthesizer; } virtual bool isSynti() const { return true; } - virtual QString getPatchName(int ch, int prog, bool dr) const { + // Event time and tick must be set by caller beforehand. + // Overridden here because input from synths may need to be treated specially. + virtual void recordEvent(MidiRecordEvent&); + + virtual Plugin::PluginFeatures pluginFeatures() const { return _sif->requiredFeatures(); } + + // Number of routable inputs/outputs for each Route::RouteType. + virtual RouteCapabilitiesStruct routeCapabilities() const; + + virtual QString getPatchName(int ch, int prog, bool dr, bool /*includeDefault*/ = true) const { return _sif->getPatchName(ch, prog, dr); } - + + // Returns a map item with members filled from either the original or working map item, + // depending on which Field flags are set. The returned map includes any requested + // WorkingDrumMapEntry::OverrideType instrument overrides. Channel can be -1 meaning default. + virtual void getMapItem(int channel, int patch, int index, DrumMap& dest_map, int overrideType = WorkingDrumMapEntry::AllOverrides) const { + return _sif->getMapItem(channel, patch, index, dest_map, overrideType); + } + virtual void populatePatchPopup(MusEGui::PopupMenu* m, int i, bool d) { _sif->populatePatchPopup(m, i, d); } - - void currentProg(unsigned long *prog, unsigned long *bankL, unsigned long *bankH); + + void currentProg(int chan, int *prog, int *bankL, int *bankH) + { _curOutParamNums[chan].currentProg(prog, bankL, bankH); } + void setCurrentProg(int chan, int prog, int bankL, int bankH) + { _curOutParamNums[chan].setCurrentProg(prog, bankL, bankH); } void guiHeartBeat() { return _sif->guiHeartBeat(); } - bool initGui() const { return _sif->initGui(); } bool guiVisible() const { return _sif->guiVisible(); } void showGui(bool v) { _sif->showGui(v); } bool hasGui() const { return _sif->hasGui(); } @@ -301,9 +322,8 @@ _sif->setNativeGeometry(x, y, w, h); } - bool putEvent(const MidiPlayEvent& ev); - virtual void processMidi(); - + virtual void processMidi(unsigned int /*curFrame*/ = 0); + MidiPlayEvent receiveEvent() { return _sif->receiveEvent(); } int eventsPending() const { return _sif->eventsPending(); } void deactivate2(); @@ -319,56 +339,57 @@ //--------------------------------------------------------- // MessSynthIF // mess synthesizer instance -// NOTICE: If implementing sysex support, be sure to make a unique ID and use +// NOTICE: If implementing sysex support, be sure to make a unique ID and use // it to filter out unrecognized sysexes. Headers should be constructed as: -// MUSE_SYNTH_SYSEX_MFG_ID The MusE SoftSynth Manufacturer ID byte (0x7C) found in midi.h +// MUSE_SYNTH_SYSEX_MFG_ID The MusE SoftSynth Manufacturer ID byte (0x7C) found in midi.h // 0xNN The synth's unique ID byte //--------------------------------------------------------- class MessSynthIF : public SynthIF { Mess* _mess; + bool processEvent(const MidiPlayEvent& ev); + public: MessSynthIF(SynthI* s) : SynthIF(s) { _mess = 0; } virtual ~MessSynthIF() { } // This is only a kludge required to support old songs' midistates. Do not use in any new synth. - virtual int oldMidiStateHeader(const unsigned char** data) const; + virtual int oldMidiStateHeader(const unsigned char** data) const; - virtual bool initGui() { return true; } - virtual void guiHeartBeat() { } + virtual void guiHeartBeat(); virtual bool guiVisible() const { return false; } - virtual void showGui(bool) { }; virtual bool hasGui() const { return false; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool v); virtual bool hasNativeGui() const; - virtual void getGeometry(int*, int*, int*, int*) const; - virtual void setGeometry(int, int, int, int); virtual void getNativeGeometry(int*, int*, int*, int*) const; virtual void setNativeGeometry(int, int, int, int); virtual void preProcessAlways(); - virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned n, float** buffer); - virtual bool putEvent(const MidiPlayEvent& ev); + virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned n, float** buffer); virtual MidiPlayEvent receiveEvent(); virtual int eventsPending() const; bool init(Synth* s, SynthI* si); - + virtual int channels() const; virtual int totalOutChannels() const; virtual int totalInChannels() const; virtual void deactivate3(); - virtual QString getPatchName(int, int, bool) const; + virtual QString getPatchName(int, int, bool) const; virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool); virtual void write(int level, Xml& xml) const; - virtual float getParameter(unsigned long) const { return 0.0; } - virtual void setParameter(unsigned long, float) {} - virtual int getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval); + virtual double getParameter(unsigned long) const { return 0.0; } + virtual void setParameter(unsigned long, double) {} + virtual int getControllerInfo(int id, QString* name, int* ctrl, int* min, int* max, int* initval); + // Returns a map item with members filled from either the original or working map item, + // depending on which Field flags are set. The returned map includes any requested + // WorkingDrumMapEntry::OverrideType instrument overrides. Channel can be -1 meaning default. + virtual void getMapItem(int /*channel*/, int /*patch*/, int /*index*/, DrumMap& /*dest_map*/, int /*overrideType*/ = WorkingDrumMapEntry::AllOverrides) const; }; extern QString synthType2String(Synth::Type); -extern Synth::Type string2SynthType(const QString&); - +extern Synth::Type string2SynthType(const QString&); + } // namespace MusECore namespace MusEGlobal { diff -Nru muse-2.1.2/muse/sysex_helper.cpp muse-3.0.2+ds1/muse/sysex_helper.cpp --- muse-2.1.2/muse/sysex_helper.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/sysex_helper.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,48 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// sysex_helper.cpp +// (C) Copyright 2018 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 +// 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 "sysex_helper.h" + +namespace MusECore { + +//--------------------------------------------------------- +// sysexDuration +//--------------------------------------------------------- + +unsigned int sysexDuration(unsigned int len, int sampleRate) +{ + // Midi transmission characters per second, based on standard fixed bit rate of 31250 Hz. + // According to ALSA (aplaymidi.c), although the midi standard says one stop bit, + // two are commonly used. We will use two just to be sure. + const unsigned int midi_cps = 31250 / (1 + 8 + 2); + // Estimate the number of audio frames it should take (or took) to transmit the current midi chunk. + unsigned int frames = (len * sampleRate) / midi_cps; + // Add a slight delay between chunks just to be sure there's no overlap, rather a small space, and let devices catch up. + frames += sampleRate / 200; // 1 / 200 = 5 milliseconds. + // Let's be realistic, spread by at least one frame. + if(frames == 0) + frames = 1; + return frames; +} + +} // namespace MusECore diff -Nru muse-2.1.2/muse/sysex_helper.h muse-3.0.2+ds1/muse/sysex_helper.h --- muse-2.1.2/muse/sysex_helper.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/sysex_helper.h 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,38 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// sysex_helper.h +// (C) Copyright 2018 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 +// 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 __SYSEX_HELPER_H__ +#define __SYSEX_HELPER_H__ + +namespace MusECore { + +// Expected duration in frames, at the current sample rate, of the +// given length of sysex data. Based on 31250Hz midi baud rate in +// 1-8-2 format. (Midi specs say 1 stop bit, but ALSA says +// 2 stop bits are common.) A small gap time is added as well. +// If the data includes any start/end bytes, len should also include them. +extern unsigned int sysexDuration(unsigned int len, int sampleRate); + +} // namespace MusECore + +#endif diff -Nru muse-2.1.2/muse/tempo.cpp muse-3.0.2+ds1/muse/tempo.cpp --- muse-2.1.2/muse/tempo.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/tempo.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -29,6 +29,7 @@ #include "globals.h" #include "gconfig.h" #include "xml.h" +#include "operations.h" namespace MusEGlobal { MusECore::TempoList tempomap; @@ -79,6 +80,63 @@ normalize(); } + +void TempoList::add(unsigned tick, TEvent* e, bool do_normalize) +{ + int tempo = e->tempo; + std::pair res = insert(std::pair (tick, e)); + if(!res.second) + { + fprintf(stderr, "TempoList::add insert failed: tempolist:%p tempo:%p %d tick:%d\n", + this, e, tempo, e->tick); + } + else + { + iTEvent ine = res.first; + ++ine; // There is always a 'next' tempo event - there is always one at index MAX_TICK + 1. + TEvent* ne = ine->second; + + // Swap the values. (This is how the tempo list works.) + e->tempo = ne->tempo; + e->tick = ne->tick; + ne->tempo = tempo; + ne->tick = tick; + + if(do_normalize) + normalize(); + } +} + +//--------------------------------------------------------- +// addOperation +//--------------------------------------------------------- + +void TempoList::addOperation(unsigned tick, int tempo, PendingOperationList& ops) +{ + if (tick > MAX_TICK) + tick = MAX_TICK; + iTEvent e = upper_bound(tick); + + if(tick == e->second->tick) + ops.add(PendingOperationItem(this, e, tempo, PendingOperationItem::ModifyTempo)); + else + { + PendingOperationItem poi(this, 0, tick, PendingOperationItem::AddTempo); + iPendingOperation ipo = ops.findAllocationOp(poi); + if(ipo != ops.end()) + { + PendingOperationItem& poi = *ipo; + // Simply replace the value. + poi._tempo_event->tempo = tempo; + } + else + { + poi._tempo_event = new TEvent(tempo, tick); // These are the desired tick and tempo but... + ops.add(poi); // add will do the proper swapping with next event. + } + } +} + //--------------------------------------------------------- // TempoList::normalize //--------------------------------------------------------- @@ -185,18 +243,18 @@ // del //--------------------------------------------------------- -void TempoList::del(unsigned tick) +void TempoList::del(unsigned tick, bool do_normalize) { iTEvent e = find(tick); if (e == end()) { printf("TempoList::del(%d): not found\n", tick); return; } - del(e); + del(e, do_normalize); ++_tempoSN; } -void TempoList::del(iTEvent e) +void TempoList::del(iTEvent e, bool do_normalize) { iTEvent ne = e; ++ne; @@ -207,21 +265,26 @@ ne->second->tempo = e->second->tempo; ne->second->tick = e->second->tick; erase(e); - normalize(); + if(do_normalize) + normalize(); ++_tempoSN; } //--------------------------------------------------------- -// change +// delOperation //--------------------------------------------------------- -void TempoList::change(unsigned tick, int newTempo) - { - iTEvent e = find(tick); - e->second->tempo = newTempo; - normalize(); - ++_tempoSN; - } +void TempoList::delOperation(unsigned tick, PendingOperationList& ops) +{ + iTEvent e = find(tick); + if (e == end()) { + printf("TempoList::delOperation tick:%d not found\n", tick); + return; + } + PendingOperationItem poi(this, e, PendingOperationItem::DeleteTempo); + // NOTE: Deletion is done in post-RT stage 3. + ops.add(poi); +} //--------------------------------------------------------- // setTempo @@ -263,19 +326,9 @@ // delTempo //--------------------------------------------------------- -void TempoList::delTempo(unsigned tick) - { - del(tick); - ++_tempoSN; - } - -//--------------------------------------------------------- -// changeTempo -//--------------------------------------------------------- - -void TempoList::changeTempo(unsigned tick, int newTempo) +void TempoList::delTempo(unsigned tick, bool do_normalize) { - change(tick, newTempo); + del(tick, do_normalize); ++_tempoSN; } diff -Nru muse-2.1.2/muse/tempo.h muse-3.0.2+ds1/muse/tempo.h --- muse-2.1.2/muse/tempo.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/tempo.h 2017-12-04 21:01:18.000000000 +0000 @@ -38,6 +38,8 @@ namespace MusECore { class Xml; +class PendingOperationList; +struct PendingOperationItem; //--------------------------------------------------------- // Tempo Event @@ -70,15 +72,18 @@ typedef TEMPOLIST::const_reverse_iterator criTEvent; class TempoList : public TEMPOLIST { + + friend struct PendingOperationItem; + int _tempoSN; // serial no to track tempo changes bool useList; int _tempo; // tempo if not using tempo list int _globalTempo; // %percent 50-200% void add(unsigned tick, int tempo, bool do_normalize = true); - void change(unsigned tick, int newTempo); - void del(iTEvent); - void del(unsigned tick); + void add(unsigned tick, TEvent* e, bool do_normalize = true); + void del(iTEvent, bool do_normalize = true); + void del(unsigned tick, bool do_normalize = true); public: TempoList(); @@ -103,12 +108,14 @@ int tempoSN() const { return _tempoSN; } void setTempo(unsigned tick, int newTempo); void addTempo(unsigned t, int tempo, bool do_normalize = true); - void delTempo(unsigned tick); - void changeTempo(unsigned tick, int newTempo); + void delTempo(unsigned tick, bool do_normalize = true); bool masterFlag() const { return useList; } bool setMasterFlag(unsigned tick, bool val); int globalTempo() const { return _globalTempo; } void setGlobalTempo(int val); + + void addOperation(unsigned tick, int tempo, PendingOperationList& ops); + void delOperation(unsigned tick, PendingOperationList& ops); }; //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/thread.cpp muse-3.0.2+ds1/muse/thread.cpp --- muse-2.1.2/muse/thread.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/thread.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -269,7 +269,7 @@ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 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. + policy = 0; // Now set the true desired initial value. if ((policy = sched_getscheduler (0)) < 0) { printf("Thread: Cannot get current client scheduler: %s\n", strerror(errno)); } diff -Nru muse-2.1.2/muse/thread.h muse-3.0.2+ds1/muse/thread.h --- muse-2.1.2/muse/thread.h 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/thread.h 2017-12-17 21:07:38.000000000 +0000 @@ -77,8 +77,6 @@ int toThreadFdw; // message to thread (app write) PollList plist; -// pthread_mutex_t lock; DELETETHIS 2 -// pthread_cond_t ready; void* userPtr; protected: diff -Nru muse-2.1.2/muse/ticksynth.cpp muse-3.0.2+ds1/muse/ticksynth.cpp --- muse-2.1.2/muse/ticksynth.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/ticksynth.cpp 2017-12-17 21:07:38.000000000 +0000 @@ -20,21 +20,27 @@ // //========================================================= +#include + #include "audio.h" #include "ticksynth.h" #include "default_click.h" -#include "klick1.h" -#include "klick2.h" -#include "klick3.h" -#include "klick4.h" #include "midi.h" #include "popupmenu.h" - -// If sysex support is ever added, make sure this number is unique among all the MESS synths. +#include "gconfig.h" +#include "wave.h" +#include "operations.h" + +// If sysex support is ever added, make sure this number is unique among all the +// MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. //#define METRONOME_UNIQUE_ID 7 //#define METRONOME_DEBUG +// For debugging output: Uncomment the fprintf section. +#define DEBUG_TICKSYNTH(dev, format, args...) //fprintf(dev, format, ##args); + namespace MusECore { MetronomeSynthI* metronome = 0; @@ -77,27 +83,39 @@ int len; float volume; void process(float** buffer, int offset, int n); + void initSamples(); + + float *measSamples; + int measLen; + float *beatSamples; + int beatLen; + float *accent1Samples; + int accent1Len; + float *accent2Samples; + int accent2Len; + bool processEvent(const MidiPlayEvent& ev); + public: MetronomeSynthIF(SynthI* s) : SynthIF(s) { data = 0; + beatLen = 0; + measLen = 0; + accent1Len = 0; + accent2Len = 0; + initSamples(); } - virtual bool initGui() { return true; }; virtual void guiHeartBeat() { } virtual bool guiVisible() const { return false; } - virtual void showGui(bool) {} virtual bool hasGui() const { return false; } virtual bool nativeGuiVisible() const { return false; } - virtual void showNativeGui(bool) { }; + virtual void showNativeGui(bool) { } virtual bool hasNativeGui() const { return false; } - virtual void getGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } - virtual void setGeometry(int, int, int, int) {} virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } virtual void setNativeGeometry(int, int, int, int) {} - virtual void preProcessAlways() { }; - virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned n, float** buffer); - virtual bool putEvent(const MidiPlayEvent& ev); + virtual void preProcessAlways() { } + virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned n, float** buffer); virtual MidiPlayEvent receiveEvent() { return MidiPlayEvent(); } virtual int eventsPending() const { return 0; } @@ -106,62 +124,256 @@ virtual int totalInChannels() const { return 0; } virtual void deactivate3() {} virtual QString getPatchName(int, int, bool) const { return ""; } - virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool) {}; + virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool) {} virtual void write(int, Xml&) const {} - virtual float getParameter(unsigned long) const { return 0.0; } - virtual void setParameter(unsigned long, float) {} - virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) { return 0; } + virtual double getParameter(unsigned long) const { return 0.0; } + virtual void setParameter(unsigned long, double) {} + virtual int getControllerInfo(int, QString*, int*, int*, int*, int*) { return 0; } + + void initSamplesOperation(MusECore::PendingOperationList&); + + //------------------------- + // Methods for PluginIBase: + //------------------------- + + virtual bool addScheduledControlEvent(unsigned long /*i*/, double /*val*/, unsigned /*frame*/) { return true; } // returns true if event cannot be delivered }; //--------------------------------------------------------- // getData //--------------------------------------------------------- -iMPEvent MetronomeSynthIF::getData(MidiPort*, MPEventList* el, iMPEvent i, unsigned pos, int/*ports*/, unsigned n, float** buffer) +bool MetronomeSynthIF::getData(MidiPort*, unsigned /*pos*/, int/*ports*/, unsigned n, float** buffer) { - // Added by Tim. p3.3.18 #ifdef METRONOME_DEBUG - printf("MusE: MetronomeSynthIF::getData\n"); + fprintf(stderr, "MusE: MetronomeSynthIF::getData\n"); #endif - //set type to unsigned , due to compiler warning: comparison signed/unsigned - unsigned int curPos = pos; //prevent compiler warning: comparison signed/unsigned - unsigned int endPos = pos + n; //prevent compiler warning: comparison signed/unsigned - unsigned int off = pos; //prevent compiler warning: comparison signed/unsigned - int frameOffset = MusEGlobal::audio->getFrameOffset(); - - for (; i != el->end(); ++i) { - unsigned int frame = i->time() - frameOffset; //prevent compiler warning: comparison signed /unsigned - if (frame >= endPos) - break; - if (frame > curPos) { - if (frame < pos) - printf("should not happen: missed event %d\n", pos -frame); - else - process(buffer, curPos-pos, frame - curPos); - curPos = frame; - } - putEvent(*i); - } - if (endPos - curPos) - process(buffer, curPos - off, endPos - curPos); - return el->end(); + const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); + unsigned int curPos = 0; + unsigned int frame = 0; + + // Get the state of the stop flag. + const bool do_stop = synti->stopFlag(); + + MidiPlayEvent buf_ev; + + // Transfer the user lock-free buffer events to the user sorted multi-set. + // False = don't use the size snapshot, but update it. + const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); + for(unsigned int i = 0; i < usr_buf_sz; ++i) + { + if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) + synti->_outUserEvents.insert(buf_ev); + } + + // Transfer the playback lock-free buffer events to the playback sorted multi-set. + const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); + for(unsigned int i = 0; i < pb_buf_sz; ++i) + { + // Are we stopping? Just remove the item. + if(do_stop) + synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); + // Otherwise get the item. + else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) + synti->_outPlaybackEvents.insert(buf_ev); + } + + // Are we stopping? + if(do_stop) + { + // Transport has stopped, purge ALL further scheduled playback events now. + synti->_outPlaybackEvents.clear(); + // Reset the flag. + synti->setStopFlag(false); + } + + iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); + iMPEvent impe_us = synti->_outUserEvents.begin(); + bool using_pb; + + while(1) + { + if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) + using_pb = *impe_pb < *impe_us; + else if(impe_pb != synti->_outPlaybackEvents.end()) + using_pb = true; + else if(impe_us != synti->_outUserEvents.end()) + using_pb = false; + else break; + + const MidiPlayEvent& ev = using_pb ? *impe_pb : *impe_us; + + const unsigned int evTime = ev.time(); + if(evTime < syncFrame) + { + fprintf(stderr, "MetronomeSynthIF::getData() evTime:%u < syncFrame:%u!! curPos=%d\n", + evTime, syncFrame, curPos); + frame = 0; + } + else + frame = evTime - syncFrame; + + // Event is for future? + if(frame >= n) + { + DEBUG_TICKSYNTH(stderr, "MetronomeSynthIF::getData(): Event for future, breaking loop: frame:%u n:%d evTime:%u syncFrame:%u curPos:%d\n", + frame, n, evTime, syncFrame, curPos); + //continue; + break; + } + + if(frame > curPos) + { + process(buffer, curPos, frame - curPos); + curPos = frame; + } + + // If putEvent fails, although we would like to not miss events by keeping them + // until next cycle and trying again, that can lead to a large backup of events + // over a long time. So we'll just... miss them. + //putEvent(ev); + //synti->putEvent(ev); + processEvent(ev); + + // Done with ring buffer event. Remove it from FIFO. + // C++11. + if(using_pb) + impe_pb = synti->_outPlaybackEvents.erase(impe_pb); + else + impe_us = synti->_outUserEvents.erase(impe_us); } + if(curPos < n) + process(buffer, curPos, n - curPos); + + return true; + } + +//--------------------------------------------------------- +// initSamples +//--------------------------------------------------------- + +void MetronomeSynthIF::initSamples() +{ + if (beatLen) + delete beatSamples; + if (measLen) + delete measSamples; + if (accent1Len) + delete accent1Samples; + if (accent2Len) + delete accent2Samples; + beatLen = 0; + measLen = 0; + accent1Len = 0; + accent2Len = 0; + + SndFile beat(MusEGlobal::museGlobalShare + "/metronome/" + MusEGlobal::config.beatSample); + if (!beat.openRead(false)) { + beatLen = beat.samples(); + beatSamples = new float[beatLen]; + beat.read(1, &beatSamples, beatLen); + } + + SndFile meas(MusEGlobal::museGlobalShare + "/metronome/" + MusEGlobal::config.measSample); + if (!meas.openRead(false)) { + measLen = meas.samples(); + measSamples = new float[measLen]; + meas.read(1, &measSamples, measLen); + } + + SndFile accent1(MusEGlobal::museGlobalShare + "/metronome/" + MusEGlobal::config.accent1Sample); + if (!accent1.openRead(false)) { + accent1Len = accent1.samples(); + accent1Samples = new float[accent1Len]; + accent1.read(1, &accent1Samples, accent1Len); + } + + SndFile accent2(MusEGlobal::museGlobalShare + "/metronome/" + MusEGlobal::config.accent2Sample); + if (!accent2.openRead(false)) { + accent2Len = accent2.samples(); + accent2Samples = new float[accent2Len]; + accent2.read(1, &accent2Samples, accent2Len); + } + +} + +//--------------------------------------------------------- +// initSamplesOperation +//--------------------------------------------------------- + +void MetronomeSynthIF::initSamplesOperation(MusECore::PendingOperationList& operations) +{ + SndFile beat(MusEGlobal::museGlobalShare + "/metronome/" + MusEGlobal::config.beatSample); + if (!beat.openRead(false)) { + const sf_count_t newBeatLen = beat.samples(); + if(newBeatLen != 0) + { + float* newBeatSamples = new float[newBeatLen]; + beat.read(1, &newBeatSamples, newBeatLen); + operations.add(PendingOperationItem(&beatSamples, newBeatSamples, + &beatLen, newBeatLen, + PendingOperationItem::ModifyAudioSamples)); + } + } + + SndFile meas(MusEGlobal::museGlobalShare + "/metronome/" + MusEGlobal::config.measSample); + if (!meas.openRead(false)) { + const sf_count_t newMeasLen = meas.samples(); + if(newMeasLen != 0) + { + float* newMeasSamples = new float[newMeasLen]; + meas.read(1, &newMeasSamples, newMeasLen); + operations.add(PendingOperationItem(&measSamples, newMeasSamples, + &measLen, newMeasLen, + PendingOperationItem::ModifyAudioSamples)); + } + } + + SndFile accent1(MusEGlobal::museGlobalShare + "/metronome/" + MusEGlobal::config.accent1Sample); + if (!accent1.openRead(false)) { + const sf_count_t newAccent1Len = accent1.samples(); + if(newAccent1Len != 0) + { + float* newAccent1Samples = new float[newAccent1Len]; + accent1.read(1, &newAccent1Samples, newAccent1Len); + operations.add(PendingOperationItem(&accent1Samples, newAccent1Samples, + &accent1Len, newAccent1Len, + PendingOperationItem::ModifyAudioSamples)); + } + } + + SndFile accent2(MusEGlobal::museGlobalShare + "/metronome/" + MusEGlobal::config.accent2Sample); + if (!accent2.openRead(false)) { + const sf_count_t newAccent2Len = accent2.samples(); + if(newAccent2Len != 0) + { + float* newAccent2Samples = new float[newAccent2Len]; + accent2.read(1, &newAccent2Samples, newAccent2Len); + operations.add(PendingOperationItem(&accent2Samples, newAccent2Samples, + &accent2Len, newAccent2Len, + PendingOperationItem::ModifyAudioSamples)); + } + } +} + //--------------------------------------------------------- // putEvent //--------------------------------------------------------- -bool MetronomeSynthIF::putEvent(const MidiPlayEvent& ev) +bool MetronomeSynthIF::processEvent(const MidiPlayEvent& ev) { + if(ev.type() != MusECore::ME_NOTEON) + return false; if (ev.dataA() == MusECore::measureSound) { if (MusEGlobal::clickSamples == MusEGlobal::origSamples) { data = defaultClickEmphasis; len = defaultClickEmphasisLength; } else { - data = defaultKlick3; - len = defaultKlick3Length; + data = measSamples; + len = measLen; } volume = MusEGlobal::measClickVolume; } @@ -170,22 +382,22 @@ data = defaultClick; len = defaultClickLength; } else { - data = defaultKlick1; - len = defaultKlick1Length; + data = beatSamples; + len = beatLen; } volume = MusEGlobal::beatClickVolume; } else if (ev.dataA() == MusECore::accent1Sound) { - data = defaultKlick4; - len = defaultKlick4Length; + data = accent1Samples; + len = accent1Len; volume = MusEGlobal::accent1ClickVolume; if (MusEGlobal::clickSamples == MusEGlobal::origSamples) { volume=0.0; } } else if (ev.dataA() == MusECore::accent2Sound) { - data = defaultKlick2; - len = defaultKlick2Length; + data = accent2Samples; + len = accent2Len; volume = MusEGlobal::accent2ClickVolume; if (MusEGlobal::clickSamples == MusEGlobal::origSamples) { volume=0.0; @@ -219,7 +431,7 @@ #endif if (data == 0) - return; + return; const float* s = data + pos; float* d = *buffer + offset; @@ -234,6 +446,16 @@ } //--------------------------------------------------------- +// MetronomeSynthI +//--------------------------------------------------------- + +void MetronomeSynthI::initSamplesOperation(PendingOperationList& operations) +{ + if(sif()) + dynamic_cast(sif())->initSamplesOperation(operations); +} + +//--------------------------------------------------------- // initMetronome //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/ticksynth.h muse-3.0.2+ds1/muse/ticksynth.h --- muse-2.1.2/muse/ticksynth.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/ticksynth.h 2017-12-17 21:07:38.000000000 +0000 @@ -26,13 +26,33 @@ #include "synth.h" namespace MusECore { +class PendingOperationList; extern void initMetronome(); extern void exitMetronome(); class MetronomeSynthI : public SynthI { - virtual bool hasAuxSend() const { return false; } - + virtual bool hasAuxSend() const { return false; } + +public: + void initSamplesOperation(PendingOperationList& operations); + + //------------------------------------------------------------------------ + // The metronome synth is special - it cannot be routed like other tracks, + // and thus cannot be muted, soloed, recorded, or monitored. + //------------------------------------------------------------------------ + virtual void setMute(bool) { } + virtual void setOff(bool) { } + virtual void setSolo(bool) { } + virtual bool isMute() const { return false; } + virtual unsigned int internalSolo() const { return 0; } + virtual bool soloMode() const { return false; } + virtual bool solo() const { return false; } + virtual bool mute() const { return false; } + virtual bool off() const { return false; } + virtual bool recordFlag() const { return false; } + virtual void setRecMonitor(bool) { } + virtual bool recMonitor() const { return false; } }; extern MetronomeSynthI* metronome; diff -Nru muse-2.1.2/muse/track.cpp muse-3.0.2+ds1/muse/track.cpp --- muse-2.1.2/muse/track.cpp 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/track.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: track.cpp,v 1.34.2.11 2009/11/30 05:05:49 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011, 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,6 +24,8 @@ #include "track.h" #include "event.h" +#include "mpevent.h" +#include "midi.h" #include "mididev.h" #include "midiport.h" #include "song.h" @@ -39,14 +41,20 @@ #include "limits.h" #include "dssihost.h" #include "gconfig.h" +#include "operations.h" +#include "icons.h" #include +// Undefine if and when multiple output routes are added to midi tracks. +#define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + namespace MusECore { unsigned int Track::_soloRefCnt = 0; Track* Track::_tmpSoloChainTrack = 0; bool Track::_tmpSoloChainDoIns = false; bool Track::_tmpSoloChainNoDec = false; +int Track::_selectionOrderCounter = 0; const char* Track::_cname[] = { "Midi", "Drum", "NewStyleDrum", "Wave", @@ -56,6 +64,7 @@ bool MidiTrack::_isVisible=true; + //--------------------------------------------------------- // addPortCtrlEvents //--------------------------------------------------------- @@ -66,9 +75,9 @@ for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* part = ip->second; - const EventList* el = part->cevents(); + const EventList& el = part->events(); unsigned len = part->lenTick(); - for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { const Event& ev = ie->second; // Added by T356. Do not add events which are past the end of the part. @@ -99,13 +108,25 @@ cntrl |= MusEGlobal::drumMap[note].anote; } } - + mp->setControllerVal(ch, tick, cntrl, val, part); } } } } +void addPortCtrlEvents(Track* track, PendingOperationList& ops) +{ + if(!track || !track->isMidiTrack()) + return; + const PartList* pl = track->cparts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + Part* part = ip->second; + addPortCtrlEvents(part, part->tick(), part->lenTick(), track, ops); + } +} + //--------------------------------------------------------- // removePortCtrlEvents //--------------------------------------------------------- @@ -116,8 +137,8 @@ for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* part = ip->second; - const EventList* el = part->cevents(); - for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + const EventList& el = part->events(); + for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { const Event& ev = ie->second; @@ -144,13 +165,25 @@ cntrl |= MusEGlobal::drumMap[note].anote; } } - + mp->deleteController(ch, tick, cntrl, part); } } } } +void removePortCtrlEvents(Track* track, PendingOperationList& ops) +{ + if(!track || !track->isMidiTrack()) + return; + const PartList* pl = track->cparts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + Part* part = ip->second; + removePortCtrlEvents(part, track, ops); + } +} + //--------------------------------------------------------- // isVisible //--------------------------------------------------------- @@ -217,18 +250,15 @@ _internalSolo = 0; _off = false; _channels = 0; // 1 - mono, 2 - stereo - - _volumeEnCtrl = true; - _volumeEn2Ctrl = true; - _panEnCtrl = true; - _panEn2Ctrl = true; - _selected = false; + _selectionOrder = 0; _height = MusEGlobal::config.trackHeight; _locked = false; + _recMonitor = false; for (int i = 0; i < MAX_CHANNELS; ++i) { _meter[i] = 0.0; _peak[i] = 0.0; + _isClipped[i] = false; } } @@ -240,11 +270,16 @@ Track::Track(const Track& t, int flags) { - internal_assign(t, flags | ASSIGN_PROPERTIES); - for (int i = 0; i < MAX_CHANNELS; ++i) { - _meter[i] = 0.0; - _peak[i] = 0.0; - } + _type = t.type(); + // moved setting the unique name to Song::duplicateTracks() + // we'll see if there is any draw back to that. + _name = t.name(); + internal_assign(t, flags | ASSIGN_PROPERTIES); + for (int i = 0; i < MAX_CHANNELS; ++i) { + _meter[i] = 0.0; + _peak[i] = 0.0; + _isClipped[i] = false; + } } Track::~Track() @@ -275,36 +310,110 @@ _internalSolo = t._internalSolo; _off = t._off; _channels = t._channels; - - _volumeEnCtrl = t._volumeEnCtrl; - _volumeEn2Ctrl = t._volumeEn2Ctrl; - _panEnCtrl = t._panEnCtrl; - _panEn2Ctrl = t._panEn2Ctrl; - _selected = t.selected(); + _selectionOrder = t.selectionOrder(); _y = t._y; _height = t._height; _comment = t.comment(); - _type = t.type(); _locked = t.locked(); + _recMonitor = t._recMonitor; + } +} - _name = t.name() + " #"; - for(int i = 2; true; ++i) - { - QString n; - n.setNum(i); - QString s = _name + n; - Track* track = MusEGlobal::song->findTrack(s); - if(track == 0) - { - // Do not call setName here. Audio Input and Output override it and try to set - // Jack ports, which have not been initialized yet here. Must wait until - // Audio Input and Output copy constructors or assign are called. - _name = s; - break; - } +//--------------------------------------------------------- +// trackTypeIcon +// Static +//--------------------------------------------------------- + +QPixmap* Track::trackTypeIcon(TrackType type) +{ + switch(type) { + case MusECore::Track::MIDI: + return MusEGui::addtrack_addmiditrackIcon; + case MusECore::Track::NEW_DRUM: + return MusEGui::addtrack_newDrumtrackIcon; + case MusECore::Track::DRUM: + return MusEGui::addtrack_drumtrackIcon; + case MusECore::Track::WAVE: + return MusEGui::addtrack_wavetrackIcon; + case MusECore::Track::AUDIO_OUTPUT: + return MusEGui::addtrack_audiooutputIcon; + case MusECore::Track::AUDIO_INPUT: + return MusEGui::addtrack_audioinputIcon; + case MusECore::Track::AUDIO_GROUP: + return MusEGui::addtrack_audiogroupIcon; + case MusECore::Track::AUDIO_AUX: + return MusEGui::addtrack_auxsendIcon; + case MusECore::Track::AUDIO_SOFTSYNTH: + return MusEGui::synthIcon; + default: + break; } - } + return 0; +} + +//--------------------------------------------------------- +// trackTypeColor +// Static +//--------------------------------------------------------- + +QColor Track::trackTypeColor(TrackType type) +{ + switch(type) { + case MusECore::Track::MIDI: + return MusEGlobal::config.midiTrackBg; + case MusECore::Track::NEW_DRUM: + return MusEGlobal::config.newDrumTrackBg; + case MusECore::Track::DRUM: + return MusEGlobal::config.drumTrackBg; + case MusECore::Track::WAVE: + return MusEGlobal::config.waveTrackBg; + case MusECore::Track::AUDIO_OUTPUT: + return MusEGlobal::config.outputTrackBg; + case MusECore::Track::AUDIO_INPUT: + return MusEGlobal::config.inputTrackBg; + case MusECore::Track::AUDIO_GROUP: + return MusEGlobal::config.groupTrackBg; + case MusECore::Track::AUDIO_AUX: + return MusEGlobal::config.auxTrackBg; + case MusECore::Track::AUDIO_SOFTSYNTH: + return MusEGlobal::config.synthTrackBg; + default: + break; + } + return QColor(); +} + +//--------------------------------------------------------- +// trackTypeLabelColor +// Static +//--------------------------------------------------------- + +QColor Track::trackTypeLabelColor(TrackType type) +{ + switch(type) { + case MusECore::Track::MIDI: + return MusEGlobal::config.midiTrackLabelBg; + case MusECore::Track::NEW_DRUM: + return MusEGlobal::config.newDrumTrackLabelBg; + case MusECore::Track::DRUM: + return MusEGlobal::config.drumTrackLabelBg; + case MusECore::Track::WAVE: + return MusEGlobal::config.waveTrackLabelBg; + case MusECore::Track::AUDIO_OUTPUT: + return MusEGlobal::config.outputTrackLabelBg; + case MusECore::Track::AUDIO_INPUT: + return MusEGlobal::config.inputTrackLabelBg; + case MusECore::Track::AUDIO_GROUP: + return MusEGlobal::config.groupTrackLabelBg; + case MusECore::Track::AUDIO_AUX: + return MusEGlobal::config.auxTrackLabelBg; + case MusECore::Track::AUDIO_SOFTSYNTH: + return MusEGlobal::config.synthTrackLabelBg; + default: + break; + } + return QColor(); } //--------------------------------------------------------- @@ -366,45 +475,34 @@ void Track::clearRecAutomation(bool clearList) { - _volumeEnCtrl = true; - _volumeEn2Ctrl = true; - _panEnCtrl = true; - _panEn2Ctrl = true; - if(isMidiTrack()) return; - - AudioTrack *t = (AudioTrack*)this; - Pipeline *pl = t->efxPipe(); - PluginI *p; - for(iPluginI i = pl->begin(); i != pl->end(); ++i) - { - p = *i; - if(!p) - continue; - p->enableAllControllers(true); - } - - if(type() == AUDIO_SOFTSYNTH) - { - const SynthI* synth = static_cast(this); - SynthIF* sif = synth->sif(); - if(sif) - sif->enableAllControllers(true); - } - + AudioTrack *t = static_cast(this); + // Re-enable all track and plugin controllers, and synth controllers if applicable. + t->enableAllControllers(); if(clearList) t->recEvents()->clear(); } //--------------------------------------------------------- +// setSelected +//--------------------------------------------------------- + +void Track::setSelected(bool f) +{ + if(f && !_selected) + _selectionOrder = _selectionOrderCounter++; + _selected = f; +} + +//--------------------------------------------------------- // dump //--------------------------------------------------------- void Track::dump() const { - printf("Track <%s>: typ %d, parts %zd sel %d\n", - _name.toLatin1().constData(), _type, _parts.size(), _selected); + printf("Track <%s>: typ %d, parts %zd sel %d sel order%d\n", + _name.toLatin1().constData(), _type, _parts.size(), _selected, _selectionOrder); } //--------------------------------------------------------- @@ -495,6 +593,15 @@ return rv; } +RouteCapabilitiesStruct Track::routeCapabilities() const +{ + RouteCapabilitiesStruct s; + s._trackChannels._inChannels = s._trackChannels._outChannels = _channels; + s._trackChannels._inRoutable = s._trackChannels._outRoutable = (_channels != 0); + return s; +} + + //--------------------------------------------------------- // MidiTrack //--------------------------------------------------------- @@ -503,25 +610,20 @@ : Track(MIDI) { init(); - _events = new EventList; - _mpevents = new MPEventList; clefType=trebleClef; _drummap=new DrumMap[128]; - _drummap_hidden=new bool[128]; - + _workingDrumMapPatchList = new WorkingDrumMapPatchList(); + init_drummap(true /* write drummap ordering information as well */); } MidiTrack::MidiTrack(const MidiTrack& mt, int flags) : Track(mt, flags) { - _events = new EventList; - _mpevents = new MPEventList; - _drummap=new DrumMap[128]; - _drummap_hidden=new bool[128]; - + _workingDrumMapPatchList = new WorkingDrumMapPatchList(); + init_drummap(true /* write drummap ordering information as well */); internal_assign(mt, flags | Track::ASSIGN_PROPERTIES); @@ -543,7 +645,6 @@ delay = mt.delay; len = mt.len; compression = mt.compression; - _recEcho = mt.recEcho(); clefType = mt.clefType; } @@ -568,8 +669,9 @@ else if(flags & ASSIGN_DEFAULT_ROUTES) { // Add default track <-> midiport routes. - int c, cbi, ch; + int c; bool defOutFound = false; /// TODO: Remove this if and when multiple output routes supported. + const int chmask = (1 << MIDI_CHANNELS) - 1; for(int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; @@ -578,8 +680,19 @@ { c = mp->defaultInChannels(); if(c) + { // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. - _inRoutes.push_back(Route(i, c)); + // All channels set or Omni? Use an Omni route: + if(c == -1 || c == chmask) + _inRoutes.push_back(Route(i)); + else + // Add individual channels: + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(c & (1 << ch)) + _inRoutes.push_back(Route(i, ch)); + } + } } if(!defOutFound) @@ -588,24 +701,34 @@ if(c) { - /// TODO: Switch if and when multiple output routes supported. - #if 0 - // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. - _outRoutes.push_back(Route(i, c)); - #else - for(ch = 0; ch < MIDI_CHANNELS; ++ch) - { - cbi = 1 << ch; - if(c & cbi) +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + if(c == -1) + c = 1; // Just to be safe, shouldn't happen, default to channel 0. + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { - defOutFound = true; - _outPort = i; - if(type() != Track::DRUM) // Leave drum tracks at channel 10. - _outChannel = ch; - break; + if(c & (1 << ch)) + { + defOutFound = true; + _outPort = i; + if(type() != Track::DRUM) //&& type != Track::NEW_DRUM) // Leave drum tracks at channel 10. TODO: Want new drum too? + _outChannel = ch; + break; + } } - } - #endif +#else + // All channels set or Omni? Use an Omni route: + if(c == -1 || c == chmask) + _outRoutes.push_back(Route(i)); + else + // Add individual channels: + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(c & (1 << ch)) + _outRoutes.push_back(Route(i, ch)); + } + +#endif + } } } @@ -615,41 +738,34 @@ { for (int i=0;i<128;i++) // no memcpy allowed here. dunno exactly why, _drummap[i]=mt._drummap[i]; // seems QString-related. - memcpy(_drummap_hidden, mt._drummap_hidden, 128*sizeof(bool)); update_drum_in_map(); - _drummap_tied_to_patch=mt._drummap_tied_to_patch; _drummap_ordering_tied_to_patch=mt._drummap_ordering_tied_to_patch; // TODO FINDMICH "assign" ordering as well + + if(mt._workingDrumMapPatchList) + *_workingDrumMapPatchList = *mt._workingDrumMapPatchList; } - if(flags & ASSIGN_PARTS) + const bool dup = flags & ASSIGN_DUPLICATE_PARTS; + const bool cpy = flags & ASSIGN_COPY_PARTS; + const bool cln = flags & ASSIGN_CLONE_PARTS; + if(dup || cpy || cln) { const PartList* pl = t.cparts(); for (ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* spart = ip->second; - bool clone = spart->events()->arefCount() > 1; - // This increments aref count if cloned, and chains clones. - // It also gives the new part a new serial number. - Part* dpart = newPart(spart, clone); - if(!clone) { - // Copy Events - MusECore::EventList* se = spart->events(); - MusECore::EventList* de = dpart->events(); - for (MusECore::iEvent i = se->begin(); i != se->end(); ++i) { - MusECore::Event oldEvent = i->second; - MusECore::Event ev = oldEvent.clone(); - de->add(ev); - } - } - - // TODO: Should we include the parts in the undo? - // dpart->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it - // // so we must decrement it first :/ - // // These will not increment ref count, and will not chain clones... - // // DELETETHIS: is the above comment still correct (by flo93)? i doubt it! - // operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart,dpart)); - - parts()->add(dpart); + Part* dpart = 0; + if(dup) + dpart = spart->hasClones() ? spart->createNewClone() : spart->duplicate(); + else if(cpy) + dpart = spart->duplicate(); + else if(cln) + dpart = spart->createNewClone(); + if(dpart) + { + dpart->setTrack(this); + parts()->add(dpart); + } } } @@ -663,14 +779,103 @@ MidiTrack::~MidiTrack() { - delete _events; - delete _mpevents; + if(_workingDrumMapPatchList) + delete _workingDrumMapPatchList; delete [] _drummap; - delete [] _drummap_hidden; - remove_ourselves_from_drum_ordering(); } + +bool MidiTrack::setRecordFlag2AndCheckMonitor(bool f) +{ + if(canRecord()) + _recordFlag = f; + + if(MusEGlobal::config.monitorOnRecord && canRecordMonitor()) + { + if(f != _recMonitor) + { + _recMonitor = f; + return true; + } + } + return false; +} + +void MidiTrack::convertToType(TrackType trackType) +{ + if(trackType == MusECore::Track::MIDI || trackType == MusECore::Track::NEW_DRUM) + { + // + // Drum -> Midi + // + MusECore::PartList* pl = parts(); + for (MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) { + for (MusECore::ciEvent ie = ip->second->events().begin(); ie != ip->second->events().end(); ++ie) { + MusECore::Event ev = ie->second; + if(ev.type() == MusECore::Note) + { + int pitch = ev.pitch(); + pitch = MusEGlobal::drumMap[pitch].enote; + ev.setPitch(pitch); + } + else + if(ev.type() == MusECore::Controller) + { + int ctl = ev.dataA(); + // Is it a drum controller event, according to the track port's instrument? + MusECore::MidiController *mc = MusEGlobal::midiPorts[outPort()].drumController(ctl); + if(mc) + // Change the controller event's index into the drum map to an instrument note. + ev.setA((ctl & ~0xff) | MusEGlobal::drumMap[ctl & 0x7f].enote); + } + } + } + setType(trackType); + } + else if(trackType == MusECore::Track::DRUM) + { + // + // Midi -> Drum + // + + // Default to track port if -1 and track channel if -1. No need anymore to ask to change all items. + + // Delete all port controller events. + MusEGlobal::song->changeAllPortDrumCtrlEvents(false); + + MusECore::PartList* pl = parts(); + for (MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) { + for (MusECore::ciEvent ie = ip->second->events().begin(); ie != ip->second->events().end(); ++ie) { + MusECore::Event ev = ie->second; + if (ev.type() == MusECore::Note) + { + int pitch = ev.pitch(); + pitch = MusEGlobal::drumInmap[pitch]; + ev.setPitch(pitch); + } + else + { + if(ev.type() == MusECore::Controller) + { + int ctl = ev.dataA(); + // Is it a drum controller event, according to the track port's instrument? + MusECore::MidiController *mc = MusEGlobal::midiPorts[outPort()].drumController(ctl); + if(mc) + // Change the controller event's instrument note to an index into the drum map. + ev.setA((ctl & ~0xff) | MusEGlobal::drumInmap[ctl & 0x7f]); + } + } + } + } + + setType(MusECore::Track::DRUM); + + // Add all port controller events. + MusEGlobal::song->changeAllPortDrumCtrlEvents(true); + } +} + void MidiTrack::remove_ourselves_from_drum_ordering() { for (MusEGlobal::global_drum_ordering_t::iterator it=MusEGlobal::global_drum_ordering.begin(); it!=MusEGlobal::global_drum_ordering.end();) @@ -687,14 +892,28 @@ void MidiTrack::init() { _outPort = 0; + + // let's set the port to the last instantiated device + // if midi-channel defaults are set in the configuration it + // will override this setting + for (int i = MIDI_PORTS - 1; i > -1; i--) + { + if (MusEGlobal::midiPorts[i].device() != NULL) + { + _outPort = i; + break; + } + } + _outChannel = (type()==NEW_DRUM) ? 9 : 0; + _curDrumPatchNumber = CTRL_VAL_UNKNOWN; + transposition = 0; velocity = 0; delay = 0; len = 100; // percent compression = 100; // percent - _recEcho = true; } void MidiTrack::init_drum_ordering() @@ -721,18 +940,14 @@ init_drum_ordering(); update_drum_in_map(); - - for (int i=0;i<128;i++) - _drummap_hidden[i]=false; - _drummap_tied_to_patch=true; _drummap_ordering_tied_to_patch=true; } void MidiTrack::update_drum_in_map() { - for (int i=0;i<127;i++) - drum_in_map[(int)_drummap[i].enote]=i; + for (int i = 0; i < 128; ++i) + drum_in_map[(int)_drummap[i].enote] = i; } //--------------------------------------------------------- @@ -746,46 +961,121 @@ } //--------------------------------------------------------- +// routeCapabilities +//--------------------------------------------------------- + +RouteCapabilitiesStruct MidiTrack::routeCapabilities() const +{ + RouteCapabilitiesStruct s; + s._midiPortChannels._inRoutable = true; + s._midiPortChannels._inChannels = MIDI_CHANNELS; + s._trackChannels._outRoutable = true; // Support Midi Track to Audio Input Track routes (for soloing chain). + +#ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + s._midiPortChannels._outChannels = MIDI_CHANNELS; +#endif + + return s; +} + +//--------------------------------------------------------- +// noOutRoute +//--------------------------------------------------------- + +bool MidiTrack::noOutRoute() const +{ + + return _outRoutes.empty() + +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + && (outChannel() < 0 || outPort() < 0 || !MusEGlobal::midiPorts[outPort()].device()) +#endif + ; +} + +//--------------------------------------------------------- +// setOutChannel +//--------------------------------------------------------- + +MidiTrack::ChangedType_t MidiTrack::setOutChannel(int i, bool doSignal) +{ + if(_outChannel == i) + return NothingChanged; + _outChannel = i; + ChangedType_t res = ChannelChanged; + if(updateDrummap(doSignal)) + res |= DrumMapChanged; + return res; +} + +//--------------------------------------------------------- +// setOutPort +//--------------------------------------------------------- + +MidiTrack::ChangedType_t MidiTrack::setOutPort(int i, bool doSignal) +{ + if(_outPort == i) + return NothingChanged; + _outPort = i; + ChangedType_t res = PortChanged; + if(updateDrummap(doSignal)) + res |= DrumMapChanged; + return res; +} + +//--------------------------------------------------------- // setOutChanAndUpdate //--------------------------------------------------------- -void MidiTrack::setOutChanAndUpdate(int i) +MidiTrack::ChangedType_t MidiTrack::setOutChanAndUpdate(int i, bool doSignal) { if(_outChannel == i) - return; + return NothingChanged; removePortCtrlEvents(this); _outChannel = i; + ChangedType_t res = ChannelChanged; + if(updateDrummap(doSignal)) + res |= DrumMapChanged; addPortCtrlEvents(this); + return res; } //--------------------------------------------------------- // setOutPortAndUpdate //--------------------------------------------------------- -void MidiTrack::setOutPortAndUpdate(int i) +MidiTrack::ChangedType_t MidiTrack::setOutPortAndUpdate(int i, bool doSignal) { if(_outPort == i) - return; + return NothingChanged; removePortCtrlEvents(this); _outPort = i; + ChangedType_t res = PortChanged; + if(updateDrummap(doSignal)) + res |= DrumMapChanged; addPortCtrlEvents(this); + return res; } //--------------------------------------------------------- // setOutPortAndChannelAndUpdate //--------------------------------------------------------- -void MidiTrack::setOutPortAndChannelAndUpdate(int port, int ch) +MidiTrack::ChangedType_t MidiTrack::setOutPortAndChannelAndUpdate(int port, int ch, bool doSignal) { if(_outPort == port && _outChannel == ch) - return; + return NothingChanged; removePortCtrlEvents(this); _outPort = port; _outChannel = ch; + ChangedType_t res = PortChanged | ChannelChanged; + if(updateDrummap(doSignal)) + res |= DrumMapChanged; addPortCtrlEvents(this); + return res; } //--------------------------------------------------------- @@ -796,7 +1086,8 @@ void MidiTrack::setInPortAndChannelMask(unsigned int portmask, int chanmask) { - bool changed = false; + //bool changed = false; + PendingOperationList operations; for(int port = 0; port < 32; ++port) // 32 is the old maximum number of ports. { @@ -805,145 +1096,133 @@ if(!MusEGlobal::midiPorts[port].foundInSongFile()) continue; - //if(!(portmask & (1 << port))) DELETETHIS 8 - // continue; - - // Removed. Allow to connect to port with no device so user can change device later. - //MidiPort* mp = &MusEGlobal::midiPorts[port]; - //MidiDevice* md = mp->device(); - //if(!md) - // continue; - - Route aRoute(port, chanmask); - Route bRoute(this, chanmask); - + const int allch = (1 << MIDI_CHANNELS) - 1; + // Check if Omni route will do... + if(chanmask == allch) + { // Route wanted? - if(portmask & (1 << port)) - { - MusEGlobal::audio->msgAddRoute(aRoute, bRoute); - changed = true; - } + if(portmask & (1 << port)) + operations.add(MusECore::PendingOperationItem(MusECore::Route(port), MusECore::Route(this), + MusECore::PendingOperationItem::AddRoute)); else - { - MusEGlobal::audio->msgRemoveRoute(aRoute, bRoute); - changed = true; - } - //} DELETETHIS + operations.add(MusECore::PendingOperationItem(MusECore::Route(port), MusECore::Route(this), + MusECore::PendingOperationItem::DeleteRoute)); + } + else + // Add individual channels: + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + // Route wanted? + if(portmask & (1 << port) && (chanmask & (1 << ch))) + operations.add(MusECore::PendingOperationItem(MusECore::Route(port, ch), MusECore::Route(this, ch), + MusECore::PendingOperationItem::AddRoute)); + else + operations.add(MusECore::PendingOperationItem(MusECore::Route(port, ch), MusECore::Route(this, ch), + MusECore::PendingOperationItem::DeleteRoute)); + } } - if(changed) +// if(changed) +// { +// MusEGlobal::audio->msgUpdateSoloStates(); +// MusEGlobal::song->update(SC_ROUTE); +// } + + if(!operations.empty()) { - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); - } + MusEGlobal::audio->msgExecutePendingOperations(operations, true); +// MusEGlobal::song->update(SC_ROUTE); + } } -/* DELETETHIS 84 + //--------------------------------------------------------- -// addPortCtrlEvents +// newPart //--------------------------------------------------------- -void MidiTrack::addPortCtrlEvents() -{ - const PartList* pl = cparts(); - for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) - { - Part* part = ip->second; - const EventList* el = part->cevents(); - for(ciEvent ie = el->begin(); ie != el->end(); ++ie) - { - const Event& ev = ie->second; - if(ev.type() == Controller) +Part* MidiTrack::newPart(Part*p, bool clone) { - int tick = ev.tick() + part->tick(); - int cntrl = ev.dataA(); - int val = ev.dataB(); - int ch = _outChannel; - - MidiPort* mp = &MusEGlobal::midiPorts[_outPort]; - // Is it a drum controller event, according to the track port's instrument? - if(type() == DRUM) - { - MidiController* mc = mp->drumController(cntrl); - if(mc) - { - int note = cntrl & 0x7f; - cntrl &= ~0xff; - ch = MusEGlobal::drumMap[note].channel; - mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; - cntrl |= MusEGlobal::drumMap[note].anote; - } - } - - mp->setControllerVal(ch, tick, cntrl, val, part); + MidiPart* part; + if (clone) + { + part = (MidiPart*)p->createNewClone(); + part->setTrack(this); } - } - } + else + { + part = (MidiPart*)p->duplicate(); + part->setTrack(this); + } + return part; + } + +//--------------------------------------------------------- +// addStuckNote +//--------------------------------------------------------- + +bool MidiTrack::addStuckNote(const MidiPlayEvent& ev) +{ + stuckNotes.add(ev); + return true; +} + +//--------------------------------------------------------- +// addStuckLiveNote +// Return true if note was added. +//--------------------------------------------------------- + +bool MidiTrack::addStuckLiveNote(int port, int chan, int note, int vel) +{ +// for(ciMPEvent k = stuckLiveNotes.begin(); k != stuckLiveNotes.end(); ++k) +// { +// // We're looking for port, channel, and note. Time and velocity are not relevant. +// if((*k).port() == port && +// (*k).channel() == chan && +// (*k).dataA() == note) +// return false; +// } + stuckLiveNotes.add(MidiPlayEvent(0, port, chan, ME_NOTEOFF, note, vel)); // Mark for immediate playback + return true; } //--------------------------------------------------------- -// removePortCtrlEvents +// removeStuckLiveNote +// Return true if note was removed. //--------------------------------------------------------- -void MidiTrack::removePortCtrlEvents() +bool MidiTrack::removeStuckLiveNote(int port, int chan, int note) { - const PartList* pl = cparts(); - for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + for(ciMPEvent k = stuckLiveNotes.begin(); k != stuckLiveNotes.end(); ++k) { - Part* part = ip->second; - const EventList* el = part->cevents(); - for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + // We're looking for port, channel, and note. Time and velocity are not relevant. + if((*k).port() == port && + (*k).channel() == chan && + (*k).dataA() == note) { - const Event& ev = ie->second; - if(ev.type() == Controller) - { - int tick = ev.tick() + part->tick(); - int cntrl = ev.dataA(); - int ch = _outChannel; - - MidiPort* mp = &MusEGlobal::midiPorts[_outPort]; - // Is it a drum controller event, according to the track port's instrument? - if(type() == DRUM) - { - MidiController* mc = mp->drumController(cntrl); - if(mc) - { - int note = cntrl & 0x7f; - cntrl &= ~0xff; - ch = MusEGlobal::drumMap[note].channel; - mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[note].port]; - cntrl |= MusEGlobal::drumMap[note].anote; - } - } - - mp->deleteController(ch, tick, cntrl, part); - } + stuckLiveNotes.erase(k); + return true; } } + return false; } -*/ //--------------------------------------------------------- -// newPart +// stuckLiveNoteExists +// Return true if note exists. //--------------------------------------------------------- -Part* MidiTrack::newPart(Part*p, bool clone) - { - MidiPart* part = clone ? new MidiPart(this, p->events()) : new MidiPart(this); - if (p) { - part->setName(p->name()); - part->setColorIndex(p->colorIndex()); - - *(PosLen*)part = *(PosLen*)p; - part->setMute(p->mute()); - } - - if(clone) - //p->chainClone(part); - chainClone(p, part); - - return part; - } +bool MidiTrack::stuckLiveNoteExists(int port, int chan, int note) +{ + for(ciMPEvent k = stuckLiveNotes.begin(); k != stuckLiveNotes.end(); ++k) + { + // We're looking for port, channel, and note. Time and velocity are not relevant. + if((*k).port() == port && + (*k).channel() == chan && + (*k).dataA() == note) + return true; + } + return false; +} //--------------------------------------------------------- // automationType @@ -979,8 +1258,10 @@ tag = "miditrack"; else if (type() == NEW_DRUM) tag = "newdrumtrack"; - else + else { printf("THIS SHOULD NEVER HAPPEN: non-midi-type in MidiTrack::write()\n"); + tag=""; + } xml.tag(level++, tag); Track::writeProperties(level, xml); @@ -988,7 +1269,6 @@ xml.intTag(level, "device", outPort()); xml.intTag(level, "channel", outChannel()); xml.intTag(level, "locked", _locked); - xml.intTag(level, "echo", _recEcho); xml.intTag(level, "transposition", transposition); xml.intTag(level, "velocity", velocity); @@ -1010,20 +1290,214 @@ void MidiTrack::writeOurDrumSettings(int level, Xml& xml) const { xml.tag(level++, "our_drum_settings"); - - writeOurDrumMap(level, xml, false); - - xml.intTag(level, "tied", _drummap_tied_to_patch); + _workingDrumMapPatchList->write(level, xml); xml.intTag(level, "ordering_tied", _drummap_ordering_tied_to_patch); - xml.etag(level, "our_drum_settings"); } -void MidiTrack::writeOurDrumMap(int level, Xml& xml, bool full) const +void MidiTrack::MidiCtrlRemapOperation(int index, int newPort, int newChan, int newNote, MidiCtrlValRemapOperation* rmop) { - write_new_style_drummap(level, xml, "our_drummap", _drummap, _drummap_hidden, full); + if(type() != Track::NEW_DRUM || _outPort < 0 || _outPort >= MIDI_PORTS) + return; + + // Default to track port if -1 and track channel if -1. + if(newPort == -1) + newPort = _outPort; + + if(newChan == -1) + newChan = _outChannel; + + MidiPort* trackmp = &MusEGlobal::midiPorts[_outPort]; + + int dm_ch = _drummap[index].channel; + if(dm_ch == -1) + dm_ch = _outChannel; + int dm_port = _drummap[index].port; + if(dm_port == -1) + dm_port = _outPort; + MidiPort* dm_mp = &MusEGlobal::midiPorts[dm_port]; + + MidiCtrlValListList* dm_mcvll = dm_mp->controller(); + MidiCtrlValList* v_mcvl; + int v_ch, v_ctrl, v_idx; + for(iMidiCtrlValList idm_mcvl = dm_mcvll->begin(); idm_mcvl != dm_mcvll->end(); ++idm_mcvl) + { + v_ch = idm_mcvl->first >> 24; + if(v_ch != dm_ch) + continue; + v_mcvl = idm_mcvl->second; + v_ctrl = v_mcvl->num(); + + // Is it a drum controller, according to the track port's instrument? + if(!trackmp->drumController(v_ctrl)) + continue; + + v_idx = v_ctrl & 0xff; + if(v_idx != _drummap[index].anote) + continue; + + // Does this midi control value list need to be changed (values moved etc)? + iMidiCtrlVal imcv = v_mcvl->begin(); + for( ; imcv != v_mcvl->end(); ++imcv) + { + const MidiCtrlVal& mcv = imcv->second; + if(mcv.part && mcv.part->track() == this) + break; + } + if(imcv != v_mcvl->end()) + { + // A contribution from a part on this track was found. + // We must compose a new list, or get an existing one and schedule the existing + // one for iterator erasure and pointer deletion. + // Add the erase iterator. Add will ignore if the erase iterator already exists. + rmop->_midiCtrlValLists2bErased.add(dm_port, idm_mcvl); + // Insert the delete pointer. Insert will ignore if the delete pointer already exists. + rmop->_midiCtrlValLists2bDeleted.insert(v_mcvl); + + MidiCtrlValListList* op_mcvll; + iMidiCtrlValLists2bAdded_t imcvla = rmop->_midiCtrlValLists2bAdded.find(dm_port); + if(imcvla == rmop->_midiCtrlValLists2bAdded.end()) + { + op_mcvll = new MidiCtrlValListList(); + rmop->_midiCtrlValLists2bAdded.insert(MidiCtrlValLists2bAddedInsertPair_t(dm_port, op_mcvll)); + } + else + op_mcvll = imcvla->second; + + MidiCtrlValList* op_mcvl; + iMidiCtrlValList imcvl = op_mcvll->find(dm_ch, v_ctrl); + if(imcvl == op_mcvll->end()) + { + op_mcvl = new MidiCtrlValList(v_ctrl); + op_mcvll->add(dm_ch, op_mcvl); + // Assign the contents of the original list to the new list. + *op_mcvl = *v_mcvl; + } + else + op_mcvl = imcvl->second; + + // Remove from the list any contributions from this track. + iMidiCtrlVal iopmcv = op_mcvl->begin(); + for( ; iopmcv != op_mcvl->end(); ) + { + const MidiCtrlVal& mcv = iopmcv->second; + if(mcv.part && mcv.part->track() == this) + { + iMidiCtrlVal iopmcv_save = iopmcv; + ++iopmcv_save; + op_mcvl->erase(iopmcv); + iopmcv = iopmcv_save; + } + else + ++iopmcv; + } + } + + // We will be making changes to the list pointed to by the new settings. + // We must schedule the existing one for iterator erasure and pointer deletion. + MidiPort* dm_mp_new = &MusEGlobal::midiPorts[newPort]; + MidiCtrlValListList* dm_mcvll_new = dm_mp_new->controller(); + MidiCtrlValList* v_mcvl_new = 0; + const int v_ctrl_new = (v_ctrl & ~0xff) | newNote; + iMidiCtrlValList idm_mcvl_new = dm_mcvll_new->find(newChan, v_ctrl_new); + if(idm_mcvl_new != dm_mcvll_new->end()) + { + v_mcvl_new = idm_mcvl_new->second; + // Add the erase iterator. Add will ignore if the erase iterator already exists. + rmop->_midiCtrlValLists2bErased.add(newPort, idm_mcvl_new); + // Insert the delete pointer. Insert will ignore if the delete pointer already exists. + rmop->_midiCtrlValLists2bDeleted.insert(v_mcvl_new); + } + + // Create a new list of lists, or get an existing one. + MidiCtrlValListList* op_mcvll_new; + iMidiCtrlValLists2bAdded_t imcvla_new = rmop->_midiCtrlValLists2bAdded.find(newPort); + if(imcvla_new == rmop->_midiCtrlValLists2bAdded.end()) + { + op_mcvll_new = new MidiCtrlValListList(); + rmop->_midiCtrlValLists2bAdded.insert(MidiCtrlValLists2bAddedInsertPair_t(newPort, op_mcvll_new)); + } + else + op_mcvll_new = imcvla_new->second; + + // Compose a new list for replacement, or get an existing one. + MidiCtrlValList* op_mcvl_new; + iMidiCtrlValList imcvl_new = op_mcvll_new->find(newChan, v_ctrl_new); + if(imcvl_new == op_mcvll_new->end()) + { + op_mcvl_new = new MidiCtrlValList(v_ctrl_new); + op_mcvll_new->add(newChan, op_mcvl_new); + // Assign the contents of the original list to the new list. + if(v_mcvl_new) + *op_mcvl_new = *v_mcvl_new; + } + else + op_mcvl_new = imcvl_new->second; + + // Add to the list any contributions from this track. + for(ciMidiCtrlVal imcv_new = v_mcvl->begin(); imcv_new != v_mcvl->end(); ++imcv_new) + { + const MidiCtrlVal& mcv = imcv_new->second; + if(mcv.part && mcv.part->track() == this) + { + op_mcvl_new->addMCtlVal(imcv_new->first, mcv.val, mcv.part); + } + } + } +} + +void MidiTrack::dumpMap() +{ + if(type() != NEW_DRUM) + return; + const int port = outPort(); + if(port < 0 || port >= MIDI_PORTS) + return; + MidiPort* mp = &MusEGlobal::midiPorts[port]; + const int chan = outChannel(); + const int patch = mp->hwCtrlState(chan, MusECore::CTRL_PROGRAM); + + fprintf(stderr, "Drum map for patch:%d\n\n", patch); + + fprintf(stderr, "name\t\tvol\tqnt\tlen\tchn\tprt\tlv1\tlv2\tlv3\tlv4\tenote\t\tanote\\ttmute\thide\n"); + + DrumMap all_dm, +#ifdef _USE_INSTRUMENT_OVERRIDES_ + instr_dm, instrdef_dm, +#endif + track_dm, trackdef_dm; + + for(int index = 0; index < 128; ++index) + { + getMapItem(patch, index, all_dm, WorkingDrumMapEntry::AllOverrides); + getMapItem(patch, index, track_dm, WorkingDrumMapEntry::TrackOverride); + getMapItem(patch, index, trackdef_dm, WorkingDrumMapEntry::TrackDefaultOverride); +#ifdef _USE_INSTRUMENT_OVERRIDES_ + getMapItem(patch, index, instr_dm, WorkingDrumMapEntry::InstrumentOverride); + getMapItem(patch, index, instrdef_dm, WorkingDrumMapEntry::InstrumentDefaultOverride); +#endif + + fprintf(stderr, "Index:%d ", index); + fprintf(stderr, "All overrides:\n"); + all_dm.dump(); + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + fprintf(stderr, "Instrument override:\n"); + instr_dm.dump(); + fprintf(stderr, "Instrument default override:\n"); + instrdef_dm.dump(); +#endif + + fprintf(stderr, "Track override:\n"); + track_dm.dump(); + fprintf(stderr, "Track default override:\n"); + trackdef_dm.dump(); + + fprintf(stderr, "\n"); + } } + //--------------------------------------------------------- // MidiTrack::read //--------------------------------------------------------- @@ -1032,6 +1506,8 @@ { unsigned int portmask = 0; int chanmask = 0; + bool portmask_found = false; + bool chanmask_found = false; for (;;) { Xml::Token token = xml.parse(); @@ -1039,7 +1515,7 @@ switch (token) { case Xml::Error: case Xml::End: - return; + goto out_of_MidiTrackRead_forloop; case Xml::TagStart: if (tag == "transposition") transposition = xml.parseInt(); @@ -1052,10 +1528,7 @@ else if (tag == "compression") compression = xml.parseInt(); else if (tag == "part") { - //Part* p = newPart(); - //p->read(xml); - Part* p = 0; - p = readXmlPart(xml, this); + Part* p = Part::readFromXml(xml, this); if(p) parts()->add(p); } @@ -1103,13 +1576,19 @@ setOutChannel(chan); } else if (tag == "inportMap") + { portmask = xml.parseUInt(); // Obsolete but support old files. + portmask_found = true; + } else if (tag == "inchannelMap") + { chanmask = xml.parseInt(); // Obsolete but support old files. + chanmask_found = true; + } else if (tag == "locked") _locked = xml.parseInt(); - else if (tag == "echo") - _recEcho = xml.parseInt(); + else if (tag == "echo") // Obsolete but support old files. + setRecMonitor(xml.parseInt()); else if (tag == "automation") setAutomationType(AutomationType(xml.parseInt())); else if (tag == "clef") @@ -1128,57 +1607,68 @@ case Xml::TagEnd: if (tag == "miditrack" || tag == "drumtrack" || tag == "newdrumtrack") { - setInPortAndChannelMask(portmask, chanmask); // Support old files. - return; + if(portmask_found && chanmask_found) + setInPortAndChannelMask(portmask, chanmask); // Support old files. + goto out_of_MidiTrackRead_forloop; } default: break; } } + +out_of_MidiTrackRead_forloop: + chainTrackParts(this); } void MidiTrack::readOurDrumSettings(Xml& xml) { - for (;;) - { - Xml::Token token = xml.parse(); - if (token == Xml::Error || token == Xml::End) - break; - const QString& tag = xml.s1(); - switch (token) - { - case Xml::TagStart: - if (tag == "tied") - _drummap_tied_to_patch = xml.parseInt(); - else if (tag == "ordering_tied") - _drummap_ordering_tied_to_patch = xml.parseInt(); - else if (tag == "our_drummap") - readOurDrumMap(xml, tag); - else if (tag == "drummap") - readOurDrumMap(xml, tag, false); - else - xml.unknown("MidiTrack::readOurDrumSettings"); - break; + bool doUpdateDrummap = false; + for (;;) + { + Xml::Token token = xml.parse(); + if (token == Xml::Error || token == Xml::End) + break; + const QString& tag = xml.s1(); + switch (token) + { + case Xml::TagStart: + if (tag == "tied") + xml.parseInt(); // Obsolete. + else if (tag == "ordering_tied") + _drummap_ordering_tied_to_patch = xml.parseInt(); + + else if (tag == "our_drummap" || // OBSOLETE. Support old files. + tag == "drummap" || // OBSOLETE. Support old files. + tag == "drumMapPatch") + { + // false = Do not fill in unused items. + _workingDrumMapPatchList->read(xml, false); + doUpdateDrummap = true; + } - case Xml::TagEnd: - if (tag == "our_drum_settings") - return; + else + xml.unknown("our_drum_settings"); + break; - default: - break; - } - } -} + case Xml::TagEnd: + if (tag == "our_drum_settings") + { + if(doUpdateDrummap) + { + // We must ensure that there are NO duplicate enote fields, + // since the instrument map may have changed by now. + //normalizeWorkingDrumMapPatchList(); -void MidiTrack::readOurDrumMap(Xml& xml, QString tag, bool dont_init, bool compatibility) -{ - if (!dont_init) init_drummap(false); - _drummap_tied_to_patch=false; - _drummap_ordering_tied_to_patch=false; - read_new_style_drummap(xml, tag.toLatin1().data(), _drummap, _drummap_hidden, compatibility); - update_drum_in_map(); -} + updateDrummap(false); + } + return; + } + default: + break; + } + } +} //--------------------------------------------------------- // addPart @@ -1220,8 +1710,12 @@ xml.intTag(level, "channels", _channels); xml.intTag(level, "height", _height); xml.intTag(level, "locked", _locked); + xml.intTag(level, "recMonitor", _recMonitor); if (_selected) + { xml.intTag(level, "selected", _selected); + xml.intTag(level, "selectionOrder", _selectionOrder); + } } //--------------------------------------------------------- @@ -1255,8 +1749,12 @@ } else if (tag == "locked") _locked = xml.parseInt(); + else if (tag == "recMonitor") + setRecMonitor(xml.parseInt()); else if (tag == "selected") _selected = xml.parseInt(); + else if (tag == "selectionOrder") + _selectionOrder = xml.parseInt(); else return true; return false; @@ -1274,37 +1772,20 @@ const RouteList* rl = &_inRoutes; for (ciRoute r = rl->begin(); r != rl->end(); ++r) { - // Support Midi Port to Audio Input track routes. p4.0.14 Tim. - if(r->type == Route::MIDI_PORT_ROUTE) - { - s = "Route"; - if(r->channel != -1 && r->channel != 0) - s += QString(" channelMask=\"%1\"").arg(r->channel); // Use new channel mask. - xml.tag(level++, s.toLatin1().constData()); - - xml.tag(level, "source mport=\"%d\"/", r->midiPort); - - s = "dest"; - s += QString(" name=\"%1\"/").arg(Xml::xmlString(name())); - xml.tag(level, s.toLatin1().constData()); - - xml.etag(level--, "Route"); - } - else if(!r->name().isEmpty()) { s = "Route"; if(r->channel != -1) s += QString(" channel=\"%1\"").arg(r->channel); - xml.tag(level++, s.toAscii().constData()); + xml.tag(level++, s.toLatin1().constData()); // New routing scheme. s = "source"; if(r->type != Route::TRACK_ROUTE) s += QString(" type=\"%1\"").arg(r->type); s += QString(" name=\"%1\"/").arg(Xml::xmlString(r->name())); - xml.tag(level, s.toAscii().constData()); + xml.tag(level, s.toLatin1().constData()); xml.tag(level, "dest name=\"%s\"/", Xml::xmlString(name()).toLatin1().constData()); @@ -1316,7 +1797,7 @@ const RouteList* rl = &_outRoutes; for (ciRoute r = rl->begin(); r != rl->end(); ++r) { - // p4.0.14 Ignore Audio Output to Audio Input routes. + // Ignore Audio Output to Audio Input routes. // They are taken care of by Audio Input in the section above. if(r->type == Route::TRACK_ROUTE && r->track && r->track->type() == Track::AUDIO_INPUT) continue; @@ -1324,25 +1805,17 @@ if(r->midiPort != -1 || !r->name().isEmpty()) { s = "Route"; - if(r->type == Route::MIDI_PORT_ROUTE) - { - if(r->channel != -1 && r->channel != 0) - s += QString(" channelMask=\"%1\"").arg(r->channel); // Use new channel mask. - } - else - { - if(r->channel != -1) - s += QString(" channel=\"%1\"").arg(r->channel); - } + if(r->channel != -1) + s += QString(" channel=\"%1\"").arg(r->channel); if(r->channels != -1) s += QString(" channels=\"%1\"").arg(r->channels); if(r->remoteChannel != -1) s += QString(" remch=\"%1\"").arg(r->remoteChannel); - xml.tag(level++, s.toAscii().constData()); + xml.tag(level++, s.toLatin1().constData()); // Allow for a regular mono or stereo track to feed a multi-channel synti. - xml.tag(level, "source name=\"%s\"/", Xml::xmlString(name()).toLatin1().constData()); + xml.tag(level, "source name=\"%s\"/", Xml::xmlString(name()).toLocal8Bit().constData()); s = "dest"; @@ -1354,7 +1827,7 @@ else s += QString(" name=\"%1\"/").arg(Xml::xmlString(r->name())); - xml.tag(level, s.toAscii().constData()); + xml.tag(level, s.toLatin1().constData()); xml.etag(level--, "Route"); } @@ -1370,7 +1843,7 @@ { Part* part=pit->second; if (part->tick() > tick) break; // ignore this and the rest. we won't find anything new. - for (iEvent eit=part->events()->begin(); eit!=part->events()->end(); eit++) + for (ciEvent eit=part->events().begin(); eit!=part->events().end(); eit++) { if (eit->first+part->tick() >= tick) break; if (eit->first > part->lenTick()) break; // ignore events past the end of the part @@ -1394,7 +1867,7 @@ Part* part=pit->second; if (part->tick() > tick) break; // ignore this and the rest. we'd find nothing any more if (part->endTick() < tick) continue; // ignore only this. - for (iEvent eit=part->events()->begin(); eit!=part->events()->end(); eit++) + for (ciEvent eit=part->events().begin(); eit!=part->events().end(); eit++) { if (eit->first+part->tick() > tick) break; // we won't find anything in this part from now on. if (eit->first > part->lenTick()) break; // ignore events past the end of the part @@ -1409,7 +1882,7 @@ return def; } -// returns the tick where this CC gets overriden by a new one +// returns the tick where this CC gets overridden by a new one // returns UINT_MAX for "never" unsigned MidiTrack::getControllerValueLifetime(unsigned tick, int ctrl) { @@ -1420,7 +1893,7 @@ Part* part=pit->second; if (part->tick() > result) break; // ignore this and the rest. we won't find anything new. if (part->endTick() < tick) continue; // ignore only this part, we won't find anything there. - for (iEvent eit=part->events()->begin(); eit!=part->events()->end(); eit++) + for (ciEvent eit=part->events().begin(); eit!=part->events().end(); eit++) { if (eit->first+part->tick() >= result) break; if (eit->first > part->lenTick()) break; // ignore events past the end of the part @@ -1437,43 +1910,713 @@ return result; } -// returns true if the autoupdate changed something -bool MidiTrack::auto_update_drummap() +//--------------------------------------------------------- +// updateDrummap +// If audio is running (and not idle) this should only be called by the rt audio thread. +// Returns true if map was changed. +//--------------------------------------------------------- + +bool MidiTrack::updateDrummap(int doSignal) +{ + if(type() != Track::NEW_DRUM || _outPort < 0 || _outPort >= MIDI_PORTS) + return false; + MidiPort* mp = &MusEGlobal::midiPorts[_outPort]; + const int patch = mp->hwCtrlState(_outChannel, CTRL_PROGRAM); + bool map_changed; + DrumMap ndm; + + map_changed = false; + for(int i = 0; i < 128; i++) + { + getMapItem(patch, i, ndm, WorkingDrumMapEntry::AllOverrides); + DrumMap& tdm = _drummap[i]; + if(ndm != tdm) + { + tdm = ndm; + map_changed = true; + } + // Be sure to update the drum input note map. Probably wise (and easy) to do it always. + drum_in_map[(int)tdm.enote] = i; + } + + // Ensure there are NO duplicate enote fields. Returns true if somethng was changed. + if(normalizeDrumMap(patch)) + map_changed = true; + + if(map_changed) + { + // Update the drum in (enote) map. + update_drum_in_map(); + + // TODO Move this to gui thread where it's safe to do so - this is only gui stuff. + if(drummap_ordering_tied_to_patch()) + // TODO This is not exactly rt friendly since it may de/allocate. + init_drum_ordering(); + } + + // TODO Do this outside since we may be called as part of multiple tracks operations. + if(map_changed && doSignal) + { + // It is possible we are being called from gui thread already, in audio idle mode. + // Will this still work, and not conflict with audio sending the same message? + // Are we are not supposed to write to an fd from different threads? + if(!MusEGlobal::audio || MusEGlobal::audio->isIdle()) + // Directly emit SC_DRUMMAP song changed signal. + MusEGlobal::song->update(SC_DRUMMAP); + else + // Tell the gui to emit SC_DRUMMAP song changed signal. + MusEGlobal::audio->sendMsgToGui('D'); // Drum map changed. + + return true; + } + + return map_changed; +} + +void MidiTrack::set_drummap_ordering_tied_to_patch(bool val) { - if (_drummap_tied_to_patch) + _drummap_ordering_tied_to_patch=val; + if (val) init_drum_ordering(); +} + +void MidiTrack::modifyWorkingDrumMap(WorkingDrumMapList& list, bool isReset, bool includeDefault, bool +#ifdef _USE_INSTRUMENT_OVERRIDES_ +isInstrumentMod +#endif +, bool doWholeMap) +{ + //if(!isDrumTrack()) + if(type() != NEW_DRUM) + return; + const int port = outPort(); + if(port < 0 || port >= MIDI_PORTS) + return; + MidiPort* mp = &MusEGlobal::midiPorts[port]; + const int chan = outChannel(); + const int patch = mp->hwCtrlState(chan, MusECore::CTRL_PROGRAM); + + int index; + int idx_end; + int other_index; + int fields; + int cur_enote; + int new_enote; +// DrumMap orig_dm; + DrumMap other_dm; + WorkingDrumMapEntry other_wdme; +#ifdef _USE_INSTRUMENT_OVERRIDES_ + MidiInstrument* instr = mp->instrument(); +#endif + for(iWorkingDrumMapPatch_t iwdp = list.begin(); iwdp != list.end(); ++iwdp) { - int patch = getFirstControllerValue(CTRL_PROGRAM,0); - const DrumMap* new_drummap = MusEGlobal::midiPorts[_outPort].instrument()->drummap_for_patch(patch); - - if (!drummaps_almost_equal(new_drummap, this->drummap(), 128)) + index = doWholeMap ? 0 : iwdp->first; + idx_end = doWholeMap ? 128 : index + 1; + for( ; index < idx_end; ++index) { - for (int i=0;i<128;i++) + DrumMap& dm = _drummap[index]; + WorkingDrumMapEntry& wdme = iwdp->second; + + fields = wdme._fields; + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + if(isInstrumentMod) { - bool temp_mute=_drummap[i].mute; - _drummap[i]=new_drummap[i]; - _drummap[i].mute=temp_mute; + if(instr) + instr->setWorkingDrumMapItem(patch, index, wdme, isReset); + } + else +#endif + + // FIXME Possible non realtime-friendly allocation. There will be adding new list and copying of 'name' QString here. + if(isReset) + { +// cur_enote = dm.enote; + _workingDrumMapPatchList->remove(patch, index, wdme._fields, includeDefault); + getMapItem(patch, index, dm, WorkingDrumMapEntry::AllOverrides); +// REMOVE Tim. newdrums. Removed. +// new_enote = dm.enote; +// other_index = drum_in_map[new_enote]; +// +// if(fields & WorkingDrumMapEntry::ENoteField && other_index != index) +// { +// // In doWholeMap mode, a previous index iteration may have already cleared the other ENote field. +// // So do this only if there is a track override on the ENote field. +// if(//doWholeMap && +// (isWorkingMapItem(other_index, WorkingDrumMapEntry::ENoteField, patch) & +// (WorkingDrumMapEntry::TrackOverride | WorkingDrumMapEntry::TrackDefaultOverride))) +// { +// // Here we need to see the original map item value /before/ any overrides, so that we can +// // tell whether this other_index brute-force 'reset' value is still technically an +// // override, and either remove or add (modify) the list appropriately. +// getMapItem(patch, other_index, other_dm, WorkingDrumMapEntry::NoOverride); +// if(other_dm.enote == cur_enote) +// { +// // The values are equal. This is technically no longer a track override and we may remove it. +// _workingDrumMapPatchList->remove(patch, other_index, WorkingDrumMapEntry::ENoteField, includeDefault); +// } +// else +// { +// // The values are not equal. This is technically still a track override, so add (modify) it. +// other_dm.enote = cur_enote; +// WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField); +// _workingDrumMapPatchList->add(patch, other_index, other_wdme); +// } +// +// _drummap[other_index].enote = cur_enote; +// drum_in_map[cur_enote] = other_index; +// } +// drum_in_map[new_enote] = index; +// } + } + else + { + cur_enote = dm.enote; + if(includeDefault) + { + // We are 'promoting' the fields to default patch list... + other_wdme._fields = fields; + other_wdme._mapItem = dm; + // Add the item to the default patch drum list. + _workingDrumMapPatchList->add(CTRL_PROGRAM_VAL_DONT_CARE, index, other_wdme); + // Now remove the item from the non-default patch drum list. + if(patch != CTRL_PROGRAM_VAL_DONT_CARE) + _workingDrumMapPatchList->remove(patch, index, WorkingDrumMapEntry::AllFields, false); // Do not include defaults. + } + else + { + if(doWholeMap) + { + if(fields == WorkingDrumMapEntry::AllFields) + { + other_wdme._fields = fields; + other_wdme._mapItem = dm; + _workingDrumMapPatchList->add(patch, index, other_wdme); + } + else + _workingDrumMapPatchList->add(patch, index, wdme); + } + else + { + _workingDrumMapPatchList->add(patch, index, wdme); + getMapItem(patch, index, dm, WorkingDrumMapEntry::AllOverrides); + } + } + + if(!doWholeMap && (fields & WorkingDrumMapEntry::ENoteField)) + { + new_enote = dm.enote; + other_index = drum_in_map[new_enote]; + // If there is already another track override on the other index we must change it. + if(isWorkingMapItem(other_index, WorkingDrumMapEntry::ENoteField, patch) != WorkingDrumMapEntry::NoOverride) + { + other_dm.enote = cur_enote; + //WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField); + other_wdme._mapItem = other_dm; + other_wdme._fields = WorkingDrumMapEntry::ENoteField; + if(includeDefault) + { + _workingDrumMapPatchList->add(CTRL_PROGRAM_VAL_DONT_CARE, other_index, other_wdme); + // Now remove the item from the non-default patch drum list. + if(patch != CTRL_PROGRAM_VAL_DONT_CARE) + _workingDrumMapPatchList->remove(patch, other_index, WorkingDrumMapEntry::ENoteField, false); // Do not include defaults. + } + else + _workingDrumMapPatchList->add(patch, other_index, other_wdme); + + //_drummap[other_index].enote = cur_enote; + //drum_in_map[cur_enote] = other_index; + //drum_in_map[new_enote] = index; + } + } } - - if (_drummap_ordering_tied_to_patch) - init_drum_ordering(); - - return true; } } - - return false; + + // Ensure there are NO duplicate enote fields. + //if(normalizeDrumMap(patch)) + // If anything changed, update the drum in map. + // update_drum_in_map(); + updateDrummap(false); // No signal. } -void MidiTrack::set_drummap_tied_to_patch(bool val) +void MidiTrack::setWorkingDrumMap(WorkingDrumMapPatchList* list, bool +#ifdef _USE_INSTRUMENT_OVERRIDES_ +isInstrumentMod +#endif +) { - _drummap_tied_to_patch=val; - if (val) auto_update_drummap(); + //if(!isDrumTrack()) + if(type() != NEW_DRUM) + return; + +#ifdef _USE_INSTRUMENT_OVERRIDES_ + if(isInstrumentMod) + { +// TODO +// const int port = outPort(); +// if(port < 0 || port >= MIDI_PORTS) +// return; +// MidiPort* mp = &MusEGlobal::midiPorts[port]; +// MidiInstrument* instr = mp->instrument(); +// instr->setWorkingDrumMap(); + return; + } +#endif + + _workingDrumMapPatchList = list; + + // We must ensure that there are NO duplicate enote fields, + // since the instrument map may have changed by now. + //normalizeWorkingDrumMapPatchList(); + + updateDrummap(false); // No signal. + update_drum_in_map(); } -void MidiTrack::set_drummap_ordering_tied_to_patch(bool val) +void MidiTrack::getMapItemAt(int tick, int index, DrumMap& dest_map, int overrideType) const { - _drummap_ordering_tied_to_patch=val; - if (val && _drummap_tied_to_patch) init_drum_ordering(); + //if(!isDrumTrack()) + if(type() != NEW_DRUM) + { + dest_map = iNewDrumMap[index]; + return; + } + const int port = outPort(); + if(port < 0 || port >= MIDI_PORTS) + { + dest_map = iNewDrumMap[index]; + return; + } + const MidiPort* mp = &MusEGlobal::midiPorts[port]; + const int track_chan = outChannel(); + + // Get the patch number at tick, contributed by any part, + // ignoring values outside of their parts. We must include + // muted or off parts or tracks in the search since this is an + // operation that must not be affected by mute or off. + const int track_patch = mp->getVisibleCtrl(track_chan, tick, MusECore::CTRL_PROGRAM, true, true, true); + + // Get the instrument's map item, and include any requested overrides. + getMapItem(track_patch, index, dest_map, overrideType); +} + +void MidiTrack::getMapItem(int patch, int index, DrumMap& dest_map, int overrideType) const +{ + //if(!isDrumTrack()) + if(type() != NEW_DRUM) + { + dest_map = iNewDrumMap[index]; + return; + } + const int port = outPort(); + if(port < 0 || port >= MIDI_PORTS) + { + dest_map = iNewDrumMap[index]; + return; + } + const MidiPort* mp = &MusEGlobal::midiPorts[port]; + const MidiInstrument* midi_instr = mp->instrument(); + if(!midi_instr) + { + dest_map = iNewDrumMap[index]; + return; + } + + // Get the instrument's map item, and include any requested overrides. + const int channel = outChannel(); + midi_instr->getMapItem(channel, patch, index, dest_map, overrideType); + + // Did we request to include any track default patch overrides? + if(overrideType & WorkingDrumMapEntry::TrackDefaultOverride) + { + // Get any track default patch overrides. + const WorkingDrumMapEntry* def_wdm = _workingDrumMapPatchList->find(CTRL_PROGRAM_VAL_DONT_CARE, index, false); // No default. + if(def_wdm) + { + if(def_wdm->_fields & WorkingDrumMapEntry::NameField) + dest_map.name = def_wdm->_mapItem.name; + + if(def_wdm->_fields & WorkingDrumMapEntry::VolField) + dest_map.vol = def_wdm->_mapItem.vol; + + if(def_wdm->_fields & WorkingDrumMapEntry::QuantField) + dest_map.quant = def_wdm->_mapItem.quant; + + if(def_wdm->_fields & WorkingDrumMapEntry::LenField) + dest_map.len = def_wdm->_mapItem.len; + + if(def_wdm->_fields & WorkingDrumMapEntry::ChanField) + dest_map.channel = def_wdm->_mapItem.channel; + + if(def_wdm->_fields & WorkingDrumMapEntry::PortField) + dest_map.port = def_wdm->_mapItem.port; + + if(def_wdm->_fields & WorkingDrumMapEntry::Lv1Field) + dest_map.lv1 = def_wdm->_mapItem.lv1; + + if(def_wdm->_fields & WorkingDrumMapEntry::Lv2Field) + dest_map.lv2 = def_wdm->_mapItem.lv2; + + if(def_wdm->_fields & WorkingDrumMapEntry::Lv3Field) + dest_map.lv3 = def_wdm->_mapItem.lv3; + + if(def_wdm->_fields & WorkingDrumMapEntry::Lv4Field) + dest_map.lv4 = def_wdm->_mapItem.lv4; + + if(def_wdm->_fields & WorkingDrumMapEntry::ENoteField) + dest_map.enote = def_wdm->_mapItem.enote; + + if(def_wdm->_fields & WorkingDrumMapEntry::ANoteField) + dest_map.anote = def_wdm->_mapItem.anote; + + if(def_wdm->_fields & WorkingDrumMapEntry::MuteField) + dest_map.mute = def_wdm->_mapItem.mute; + + if(def_wdm->_fields & WorkingDrumMapEntry::HideField) + dest_map.hide = def_wdm->_mapItem.hide; + } + } + + // Did we request to include any track overrides? + if(!(overrideType & WorkingDrumMapEntry::TrackOverride)) + return; + + // Get any track overrides. + const WorkingDrumMapEntry* wdm = _workingDrumMapPatchList->find(patch, index, false); // No default. + if(!wdm) + return; + + if(wdm->_fields & WorkingDrumMapEntry::NameField) + dest_map.name = wdm->_mapItem.name; + + if(wdm->_fields & WorkingDrumMapEntry::VolField) + dest_map.vol = wdm->_mapItem.vol; + + if(wdm->_fields & WorkingDrumMapEntry::QuantField) + dest_map.quant = wdm->_mapItem.quant; + + if(wdm->_fields & WorkingDrumMapEntry::LenField) + dest_map.len = wdm->_mapItem.len; + + if(wdm->_fields & WorkingDrumMapEntry::ChanField) + dest_map.channel = wdm->_mapItem.channel; + + if(wdm->_fields & WorkingDrumMapEntry::PortField) + dest_map.port = wdm->_mapItem.port; + + if(wdm->_fields & WorkingDrumMapEntry::Lv1Field) + dest_map.lv1 = wdm->_mapItem.lv1; + + if(wdm->_fields & WorkingDrumMapEntry::Lv2Field) + dest_map.lv2 = wdm->_mapItem.lv2; + + if(wdm->_fields & WorkingDrumMapEntry::Lv3Field) + dest_map.lv3 = wdm->_mapItem.lv3; + + if(wdm->_fields & WorkingDrumMapEntry::Lv4Field) + dest_map.lv4 = wdm->_mapItem.lv4; + + if(wdm->_fields & WorkingDrumMapEntry::ENoteField) + dest_map.enote = wdm->_mapItem.enote; + + if(wdm->_fields & WorkingDrumMapEntry::ANoteField) + dest_map.anote = wdm->_mapItem.anote; + + if(wdm->_fields & WorkingDrumMapEntry::MuteField) + dest_map.mute = wdm->_mapItem.mute; + + if(wdm->_fields & WorkingDrumMapEntry::HideField) + dest_map.hide = wdm->_mapItem.hide; +} + +int MidiTrack::isWorkingMapItem(int index, int fields, int patch) const +{ + int ret = WorkingDrumMapEntry::NoOverride; + if(type() != NEW_DRUM) + return ret; + + // Is there an instrument override for this drum map item? + const int port = outPort(); + if(port >= 0 && port < MIDI_PORTS) + { + const MidiPort* mp = &MusEGlobal::midiPorts[port]; + // Grab the patch number while we are here, if we asked for it. + if(patch == -1) + { + const int chan = outChannel(); + patch = mp->hwCtrlState(chan, CTRL_PROGRAM); + } +#ifdef _USE_INSTRUMENT_OVERRIDES_ + const MidiInstrument* midi_instr = mp->instrument(); + if(midi_instr) + ret |= midi_instr->isWorkingMapItem(patch, index, fields); +#endif + } + + // Is there a local track default patch override for this drum map item? + const WorkingDrumMapEntry* def_wdm = _workingDrumMapPatchList->find(CTRL_PROGRAM_VAL_DONT_CARE, index, false); // No default. + if(def_wdm && (def_wdm->_fields & fields)) + ret |= WorkingDrumMapEntry::TrackDefaultOverride; + + if(patch != -1) + { + // Is there a local track override for this drum map item? + const WorkingDrumMapEntry* wdm = _workingDrumMapPatchList->find(patch, index, false); // No default. + if(wdm && (wdm->_fields & fields)) + ret |= WorkingDrumMapEntry::TrackOverride; + } + + return ret; +} + +bool MidiTrack::normalizeDrumMap(int patch) +{ + if(type() != NEW_DRUM) + return false; + //WorkingDrumMapList* wdml = _workingDrumMapPatchList->find(patch, true); + WorkingDrumMapList* wdml = _workingDrumMapPatchList->find(patch, false); + WorkingDrumMapList* def_wdml = 0; + if(patch != CTRL_PROGRAM_VAL_DONT_CARE) + def_wdml = _workingDrumMapPatchList->find(CTRL_PROGRAM_VAL_DONT_CARE, false); + + int index = 0; + DrumMap dm; + char enote; + bool changed = false; + + bool used_index[128]; + int used_enotes[128]; + for(int i = 0; i < 128; ++i) + { + used_index[i] = false; + used_enotes[i] = 0; + } + char unused_enotes[128]; + int unused_enotes_sz = 0; + char unused_index[128]; + int unused_index_sz = 0; + int unused_enotes_cnt = 0; + + // Find all the used enote fields and their indexes in the working list. + if(wdml) + { + for(iWorkingDrumMapPatch_t iwdml = wdml->begin(); iwdml != wdml->end(); ++iwdml) + { + WorkingDrumMapEntry& wdme = iwdml->second; + if(wdme._fields & WorkingDrumMapEntry::ENoteField) + { + used_index[iwdml->first] = true; + //++used_enotes[(unsigned char)wdme._mapItem.enote]; + } + } + } + + // Add all the used enote fields and their indexes in the default patch working list. + if(def_wdml) + { + for(iWorkingDrumMapPatch_t iwdml = def_wdml->begin(); iwdml != def_wdml->end(); ++iwdml) + { + WorkingDrumMapEntry& wdme = iwdml->second; + if(wdme._fields & WorkingDrumMapEntry::ENoteField) + { + used_index[iwdml->first] = true; + //++used_enotes[(unsigned char)wdme._mapItem.enote]; + } + } + } + + // Find all the used enote fields and their indexes in the working list. + if(wdml) + { + for(iWorkingDrumMapPatch_t iwdml = wdml->begin(); iwdml != wdml->end(); ++iwdml) + { + WorkingDrumMapEntry& wdme = iwdml->second; + if(wdme._fields & WorkingDrumMapEntry::ENoteField) + { + //used_index[iwdml->first] = true; + ++used_enotes[(unsigned char)wdme._mapItem.enote]; + } + } + } + + // Find all the unused indexes and enotes so far in the working list. + unused_index_sz = 0; + unused_enotes_sz = 0; + for(int i = 0; i < 128; ++i) + { + if(!used_index[i]) + unused_index[unused_index_sz++] = i; + if(used_enotes[i] == 0) + unused_enotes[unused_enotes_sz++] = i; + } + + // Ensure there are NO duplicate enotes in the existing working list items so far. + unused_enotes_cnt = 0; + if(wdml) + { + for(iWorkingDrumMapPatch_t iwdml = wdml->begin(); iwdml != wdml->end(); ++iwdml) + { + WorkingDrumMapEntry& wdme = iwdml->second; + if(wdme._fields & WorkingDrumMapEntry::ENoteField) + { + // More than 1 (this) usage? + if(used_enotes[(unsigned char)wdme._mapItem.enote] > 1) + { + fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Warning: Duplicate enote:%d found. Overriding it.\n", + wdme._mapItem.enote); + if(unused_enotes_cnt >= unused_enotes_sz) + { + fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Error: unused_enotes_cnt >= unused_enotes_sz:%d\n", + unused_enotes_sz); + break; + } + --used_enotes[(unsigned char)wdme._mapItem.enote]; + //wdme._mapItem.enote = unused_enotes[unused_enotes_cnt++]; + // Get the instrument item. + index = iwdml->first; + // Modify the enote field. + enote = unused_enotes[unused_enotes_cnt++]; + _drummap[index].enote = enote; + ++used_enotes[(unsigned char)enote]; + changed = true; + } + } + } + } + + // Find all the used enote fields and their indexes in the default patch working list. + if(def_wdml) + { + for(iWorkingDrumMapPatch_t iwdml = def_wdml->begin(); iwdml != def_wdml->end(); ++iwdml) + { + WorkingDrumMapEntry& wdme = iwdml->second; + if(wdme._fields & WorkingDrumMapEntry::ENoteField) + { + //used_index[iwdml->first] = true; + // If there is already a non-default patch enote override for this index, + // do not increment used_notes, the non-default one takes priority over this default one. + if(wdml) + { + ciWorkingDrumMapPatch_t def_iwdml = wdml->find(iwdml->first); + if(def_iwdml != wdml->end()) + { + const WorkingDrumMapEntry& def_wdme = def_iwdml->second; + if(def_wdme._fields & WorkingDrumMapEntry::ENoteField) + continue; + } + } + ++used_enotes[(unsigned char)wdme._mapItem.enote]; + } + } + } + + // Find all the unused indexes and enotes so far in the working list. + unused_enotes_sz = 0; + for(int i = 0; i < 128; ++i) + { + if(used_enotes[i] == 0) + unused_enotes[unused_enotes_sz++] = i; + } + + // Ensure there are NO duplicate enotes in the existing default patch working list items so far. + unused_enotes_cnt = 0; + if(def_wdml) + { + for(iWorkingDrumMapPatch_t iwdml = def_wdml->begin(); iwdml != def_wdml->end(); ++iwdml) + { + WorkingDrumMapEntry& wdme = iwdml->second; + if(wdme._fields & WorkingDrumMapEntry::ENoteField) + { + // If there is already a non-default patch enote override for this index, + // skip this one, the non-default one takes priority over this default one. + if(wdml) + { + ciWorkingDrumMapPatch_t def_iwdml = wdml->find(iwdml->first); + if(def_iwdml != wdml->end()) + { + const WorkingDrumMapEntry& def_wdme = def_iwdml->second; + if(def_wdme._fields & WorkingDrumMapEntry::ENoteField) + continue; + } + } + + // More than 1 (this) usage? + if(used_enotes[(unsigned char)wdme._mapItem.enote] > 1) + { + fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Warning: Duplicate default enote:%d found. Overriding it.\n", + wdme._mapItem.enote); + if(unused_enotes_cnt >= unused_enotes_sz) + { + fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Error: Default unused_enotes_cnt >= unused_enotes_sz:%d\n", + unused_enotes_sz); + break; + } + --used_enotes[(unsigned char)wdme._mapItem.enote]; + //wdme._mapItem.enote = unused_enotes[unused_enotes_cnt++]; + // Get the instrument item. + index = iwdml->first; + // Modify the enote field. + enote = unused_enotes[unused_enotes_cnt++]; + _drummap[index].enote = enote; + ++used_enotes[(unsigned char)enote]; + changed = true; + } + } + } + } + + // Add all used enotes in the unused enote indexes (the instrument fields). + for(int i = 0; i < unused_index_sz; ++i) + ++used_enotes[(unsigned char)_drummap[(unsigned char)unused_index[i]].enote]; + + // Find all the unused enotes. + unused_enotes_sz = 0; + for(int i = 0; i < 128; ++i) + { + if(used_enotes[i] == 0) + unused_enotes[unused_enotes_sz++] = i; + } + + // Ensure there are NO duplicate enotes in the unused enote map fields (the instrument fields). + unused_enotes_cnt = 0; + for(int i = 0; i < unused_index_sz; ++i) + { + // Get the instrument item. + index = unused_index[i]; + enote = _drummap[index].enote; + + // More than 1 (this) usage? + if(used_enotes[(unsigned char)enote] > 1) + { + if(unused_enotes_cnt >= unused_enotes_sz) + { + fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Error filling background items: unused_enotes_cnt >= unused_enotes_sz:%d\n", + unused_enotes_sz); + break; + } + + --used_enotes[(unsigned char)enote]; + + // Modify the enote field. + _drummap[index].enote = unused_enotes[unused_enotes_cnt++]; + ++used_enotes[(unsigned char)_drummap[index].enote]; + changed = true; + } + } + + return changed; +} + +bool MidiTrack::normalizeDrumMap() +{ + if(type() != NEW_DRUM) + return false; + const int port = outPort(); + if(port < 0 || port >= MIDI_PORTS) + return false; + const int chan = outChannel(); + const int patch = MusEGlobal::midiPorts[port].hwCtrlState(chan, MusECore::CTRL_PROGRAM); + return normalizeDrumMap(patch); } } // namespace MusECore diff -Nru muse-2.1.2/muse/trackdrummapupdater.cpp muse-3.0.2+ds1/muse/trackdrummapupdater.cpp --- muse-2.1.2/muse/trackdrummapupdater.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/trackdrummapupdater.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -//========================================================= -// MusE -// Linux Music Editor -// $Id: trackdrummapupdater.cpp,v 1.59.2.52 2011/12/27 20:25:58 flo93 Exp $ -// -// (C) Copyright 2011 Florian Jung (florian.a.jung (at) web.de) -// -// 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 "trackdrummapupdater.h" -#include "song.h" -#include "globals.h" - -namespace MusECore { - -using MusEGlobal::song; - -TrackDrummapUpdater::TrackDrummapUpdater(QObject* parent) : QObject(parent) -{ - connect(song,SIGNAL(songChanged(MusECore::SongChangedFlags_t)), this, SLOT(songChanged(MusECore::SongChangedFlags_t))); -} - -void TrackDrummapUpdater::songChanged(MusECore::SongChangedFlags_t flags) -{ - if (flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | - SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | - SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED ) ) - { - bool changed=false; - for (iTrack t=song->tracks()->begin(); t!=song->tracks()->end(); t++) - { - MidiTrack* track=dynamic_cast(*t); - if (track && track->auto_update_drummap()) - changed=true; - } - - if (changed) - { - // allow recursion. there will be no more recursion, because this - // is only executed when something other than SC_DRUMMAP happens - song->update(SC_DRUMMAP, true); - } - - } -} - -} //namespace MusECore diff -Nru muse-2.1.2/muse/trackdrummapupdater.h muse-3.0.2+ds1/muse/trackdrummapupdater.h --- muse-2.1.2/muse/trackdrummapupdater.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/trackdrummapupdater.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -//========================================================= -// MusE -// Linux Music Editor -// $Id: trackdrummapupdater.h,v 1.59.2.52 2011/12/27 20:25:58 flo93 Exp $ -// -// (C) Copyright 2011 Florian Jung (florian.a.jung (at) web.de) -// -// 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 __TRACKDRUMMAPUPDATER_H__ -#define __TRACKDRUMMAPUPDATER_H__ - -#include -#include "type_defs.h" - -namespace MusECore { - -class TrackDrummapUpdater : public QObject -{ - Q_OBJECT - - public: - TrackDrummapUpdater(QObject* parent = 0); - - private slots: - void songChanged(MusECore::SongChangedFlags_t flags); -}; - -} //namespace MusECore -#endif diff -Nru muse-2.1.2/muse/track.h muse-3.0.2+ds1/muse/track.h --- muse-2.1.2/muse/track.h 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/track.h 2018-01-26 21:59:38.000000000 +0000 @@ -4,7 +4,7 @@ // $Id: track.h,v 1.39.2.17 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013, 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -32,21 +32,34 @@ #include "wave.h" // for SndFileR #include "part.h" +#include "mpevent.h" #include "key.h" #include "node.h" #include "route.h" #include "ctrl.h" #include "globaldefs.h" #include "cleftypes.h" +#include "controlfifo.h" + +class QPixmap; +class QColor; namespace MusECore { -class MPEventList; class Pipeline; class PluginI; class SynthI; class Xml; -class DrumMap; -class ControlEvent; +struct DrumMap; +struct ControlEvent; +struct Port; +class PendingOperationList; +class Undo; +class WorkingDrumMapList; +class WorkingDrumMapPatchList; +struct MidiCtrlValRemapOperation; + +typedef std::vector AuxSendValueList; +typedef std::vector::iterator iAuxSendValue; //--------------------------------------------------------- // Track @@ -58,8 +71,16 @@ MIDI=0, DRUM, NEW_DRUM, WAVE, AUDIO_OUTPUT, AUDIO_INPUT, AUDIO_GROUP, AUDIO_AUX, AUDIO_SOFTSYNTH }; + // NOTE: ASSIGN_DUPLICATE_PARTS ASSIGN_COPY_PARTS and ASSIGN_CLONE_PARTS are not allowed together - choose one. + // (Safe, but it will choose one action over the other.) enum AssignFlags { - ASSIGN_PROPERTIES=1, ASSIGN_PARTS=2, ASSIGN_PLUGINS=4, ASSIGN_STD_CTRLS=8, ASSIGN_PLUGIN_CTRLS=16, ASSIGN_ROUTES=32, ASSIGN_DEFAULT_ROUTES=64, ASSIGN_DRUMLIST=128 }; + ASSIGN_PROPERTIES=1, + ASSIGN_DUPLICATE_PARTS=2, ASSIGN_COPY_PARTS=4, ASSIGN_CLONE_PARTS=8, + ASSIGN_PLUGINS=16, + ASSIGN_STD_CTRLS=32, ASSIGN_PLUGIN_CTRLS=64, + ASSIGN_ROUTES=128, ASSIGN_DEFAULT_ROUTES=256, + ASSIGN_DRUMLIST=512 }; + private: TrackType _type; QString _comment; @@ -75,6 +96,11 @@ static bool _tmpSoloChainDoIns; static bool _tmpSoloChainNoDec; + // Every time a track is selected, the track's _selectionOrder is set to this value, + // and then this value is incremented. The value is reset to zero occasionally, + // for example whenever Song::selectAllTracks(false) is called. + static int _selectionOrderCounter; + RouteList _inRoutes; RouteList _outRoutes; bool _nodeTraversed; // Internal anti circular route traversal flag. @@ -82,27 +108,28 @@ QString _name; bool _recordFlag; + bool _recMonitor; // For midi and audio. Whether to pass the input through to output. bool _mute; bool _solo; unsigned int _internalSolo; bool _off; int _channels; // 1 - mono, 2 - stereo - bool _volumeEnCtrl; - bool _volumeEn2Ctrl; - bool _panEnCtrl; - bool _panEn2Ctrl; - int _activity; int _lastActivity; double _meter[MAX_CHANNELS]; double _peak[MAX_CHANNELS]; + bool _isClipped[MAX_CHANNELS]; //used in audio mixer strip. Persistent. int _y; int _height; // visual height in arranger bool _locked; bool _selected; + // The selection order of this track, compared to other selected tracks. + // The selected track with the highest selected order is the most recent selected. + int _selectionOrder; + bool readProperties(Xml& xml, const QString& tag); void writeProperties(int level, Xml& xml) const; @@ -113,7 +140,13 @@ virtual void assign(const Track&, int flags); static const char* _cname[]; - + static QPixmap* trackTypeIcon(TrackType); + static QColor trackTypeColor(TrackType); + static QColor trackTypeLabelColor(TrackType); + QPixmap* icon() const { return trackTypeIcon(type()); } + QColor color() const { return trackTypeColor(type()); } + QColor labelColor() const { return trackTypeLabelColor(type()); } + QString comment() const { return _comment; } void setComment(const QString& s) { _comment = s; } @@ -123,22 +156,25 @@ void setHeight(int n) { _height = n; } bool selected() const { return _selected; } - void setSelected(bool f) { _selected = f; } + // Try to always call this instead of setting _selected, because it also sets _selectionOrder. + void setSelected(bool f); + // The order of selection of this track, compared to other selected tracks. + // The selected track with the highest selected order is the most recent selected. + int selectionOrder() const { return _selectionOrder; } + // Resets the static selection counter. Optional. (Range is huge, unlikely would have to call). + // Called for example whenever Song::selectAllTracks(false) is called. + static void clearSelectionOrderCounter(){ _selectionOrderCounter = 0; } + bool locked() const { return _locked; } void setLocked(bool b) { _locked = b; } - bool volumeControllerEnabled() const { return _volumeEnCtrl; } - bool volumeControllerEnabled2() const { return _volumeEn2Ctrl; } - bool panControllerEnabled() const { return _panEnCtrl; } - bool panControllerEnabled2() const { return _panEn2Ctrl; } - void enableVolumeController(bool b) { _volumeEnCtrl = b; } - void enable2VolumeController(bool b) { _volumeEn2Ctrl = b; } - void enablePanController(bool b) { _panEnCtrl = b; } - void enable2PanController(bool b) { _panEn2Ctrl = b; } void clearRecAutomation(bool clearList); const QString& name() const { return _name; } + // setName can be overloaded to do other things like setting port names, while setNameText just sets the text. virtual void setName(const QString& s) { _name = s; } + // setNameText just sets the text, while setName can be overloaded to do other things like setting port names. + void setNameText(const QString& s) { _name = s; } TrackType type() const { return _type; } void setType(TrackType t) { _type = t; } @@ -147,12 +183,14 @@ // routing RouteList* inRoutes() { return &_inRoutes; } RouteList* outRoutes() { return &_outRoutes; } - bool noInRoute() const { return _inRoutes.empty(); } - bool noOutRoute() const { return _outRoutes.empty(); } + virtual bool noInRoute() const { return _inRoutes.empty(); } + virtual bool noOutRoute() const { return _outRoutes.empty(); } void writeRouting(int, Xml&) const; bool isCircularRoute(Track* dst); int auxRefCount() const { return _auxRouteCount; } // Number of Aux Tracks with routing paths to this track. void updateAuxRoute(int refInc, Track* dst); // Internal use. + // Number of routable inputs/outputs for each Route::RouteType. + virtual RouteCapabilitiesStruct routeCapabilities() const; PartList* parts() { return &_parts; } const PartList* cparts() const { return &_parts; } @@ -163,25 +201,62 @@ virtual Track* newTrack() const = 0; virtual Track* clone(int flags) const = 0; - - virtual bool setRecordFlag1(bool f) = 0; - virtual void setRecordFlag2(bool f) = 0; + // Returns true if any event in any part was opened. Does not operate on the part's clones, if any. + virtual bool openAllParts() { return false; }; + // Returns true if any event in any part was closed. Does not operate on the part's clones, if any. + virtual bool closeAllParts() { return false; }; + + // Called from gui thread only. + virtual bool setRecordFlag1(bool) = 0; + // Called from audio thread only. + virtual void setRecordFlag2(bool) = 0; + // Same as setRecordFlag2 except it can automatically set the monitor flag + // depending on the global config.monitorOnRecord. Called from audio thread only. + // Returns whether the monitor was changed. + virtual bool setRecordFlag2AndCheckMonitor(bool r) = 0; virtual Part* newPart(Part*p=0, bool clone = false) = 0; void dump() const; - virtual void splitPart(Part*, int, Part*&, Part*&); - virtual void setMute(bool val); - virtual void setOff(bool val); + virtual void setMute(bool val) { _mute = val; } + virtual void setOff(bool val) { _off = val; } void setInternalSolo(unsigned int val); virtual void setSolo(bool val) = 0; - virtual bool isMute() const = 0; - unsigned int internalSolo() const { return _internalSolo; } - bool soloMode() const { return _soloRefCnt; } - bool solo() const { return _solo; } - bool mute() const { return _mute; } - bool off() const { return _off; } - bool recordFlag() const { return _recordFlag; } + + // Returns true if playback ultimately is muted, depending on + // other factors such as soloing. + virtual bool isMute() const { + if(_solo || (_internalSolo && !_mute)) + return false; + if(_soloRefCnt) + return true; + return _mute; + } + // Returns true if playback ultimately is monitored, depending on + // other factors such as soloing. + virtual bool isRecMonitored() const { + if(_off || !_recMonitor) + return false; + if(_solo || _internalSolo) + return true; + return _soloRefCnt == 0; + } + // Returns true (>= 1) if proxy-soloed. + virtual unsigned int internalSolo() const { return _internalSolo; } + // Returns true if proxy-muted. + virtual bool soloMode() const { return _soloRefCnt; } + // Returns true if soloed. + virtual bool solo() const { return _solo; } + // Returns true if muted. + virtual bool mute() const { return _mute; } + // Returns true if track is off. + virtual bool off() const { return _off; } + // Returns true if rec-armed. + virtual bool recordFlag() const { return _recordFlag; } + // Sets monitor. + virtual void setRecMonitor(bool b) { if(canRecordMonitor()) _recMonitor = b; } + // Returns true if monitored. + virtual bool recMonitor() const { return _recMonitor; } // Internal use... static void clearSoloRefCounts(); @@ -206,12 +281,15 @@ virtual void setChannels(int n); bool isMidiTrack() const { return type() == MIDI || type() == DRUM || type() == NEW_DRUM; } bool isDrumTrack() const { return type() == DRUM || type() == NEW_DRUM; } + bool isSynthTrack() const { return type() == AUDIO_SOFTSYNTH; } virtual bool canRecord() const { return false; } + virtual bool canRecordMonitor() const { return false; } virtual AutomationType automationType() const = 0; virtual void setAutomationType(AutomationType t) = 0; static void setVisible(bool) { } bool isVisible(); - + inline bool isClipped(int ch) const { if(ch >= MAX_CHANNELS) return false; return _isClipped[ch]; } + void resetClipper() { for(int ch = 0; ch < MAX_CHANNELS; ++ch) _isClipped[ch] = false; } }; //--------------------------------------------------------- @@ -221,37 +299,40 @@ class MidiTrack : public Track { int _outPort; int _outChannel; - bool _recEcho; // For midi (and audio). Whether to echo incoming record events to output device. - EventList* _events; // tmp Events during midi import - MPEventList* _mpevents; // tmp Events druring recording + private: static bool _isVisible; clefTypes clefType; - DrumMap* _drummap; // _drummap[foo].anote is always equal to foo - bool* _drummap_hidden; // _drummap und _drummap_hidden will be an array[128] - bool _drummap_tied_to_patch; //if true, changing patch also changes drummap + + // A list of user-altered drum map items. + WorkingDrumMapPatchList* _workingDrumMapPatchList; + bool _drummap_ordering_tied_to_patch; //if true, changing patch also changes drummap-ordering int drum_in_map[128]; + int _curDrumPatchNumber; // Can be CTRL_VAL_UNKNOWN. void init(); void internal_assign(const Track&, int flags); void init_drummap(bool write_ordering); // function without argument in public void remove_ourselves_from_drum_ordering(); - void init_drum_ordering(); void writeOurDrumSettings(int level, Xml& xml) const; void readOurDrumSettings(Xml& xml); - //void writeOurDrumMap(int level, Xml& xml, bool full) const; //below in public: - //void readOurDrumMap(Xml& xml, bool dont_init=false); //below in public: public: + EventList events; // tmp Events during midi import + MPEventList mpevents; // tmp Events druring recording + MPEventList stuckLiveNotes; // Live (rec): Currently sounding note-ons that we don't know the note-off time yet. Event times = 0. + MPEventList stuckNotes; // Playback: Currently sounding note-ons contributed by track - not sent directly to device + MidiTrack(); MidiTrack(const MidiTrack&, int flags); virtual ~MidiTrack(); virtual void assign(const Track&, int flags); + virtual void convertToType(TrackType trackType); virtual AutomationType automationType() const; virtual void setAutomationType(AutomationType); @@ -263,11 +344,14 @@ int len; int compression; - virtual bool setRecordFlag1(bool f) { _recordFlag = f; return true;} - virtual void setRecordFlag2(bool) {} - - EventList* events() const { return _events; } - MPEventList* mpevents() const { return _mpevents; } + // Called from gui thread only. + virtual bool setRecordFlag1(bool) { return canRecord(); } + // Called from audio thread only. + virtual void setRecordFlag2(bool f) { if(canRecord()) _recordFlag = f; } + // Same as setRecordFlag2 except it can automatically set the monitor flag + // depending on the global config.monitorOnRecord. Called from audio thread only. + // Returns whether the monitor was changed. + virtual bool setRecordFlag2AndCheckMonitor(bool); virtual void read(Xml&); virtual void write(int, Xml&) const; @@ -278,56 +362,102 @@ virtual MidiTrack* clone(int flags) const { return new MidiTrack(*this, flags); } virtual Part* newPart(Part*p=0, bool clone=false); - void setOutChannel(int i) { _outChannel = i; } - void setOutPort(int i) { _outPort = i; } - // These will transfer controller data to the new selected port and/or channel. - void setOutChanAndUpdate(int chan); - void setOutPortAndUpdate(int port); - // Combines both port and channel operations. - void setOutPortAndChannelAndUpdate(int port, int chan); + // Number of routable inputs/outputs for each Route::RouteType. + virtual RouteCapabilitiesStruct routeCapabilities() const; + + // This enum describes what has changed in the following port/channel methods. + enum ChangedType { NothingChanged = 0x0, PortChanged = 0x1, ChannelChanged = 0x2, DrumMapChanged = 0x4 }; + // OR'd ChangedType flags. + typedef int ChangedType_t; + // Sets the output port, and for a drum track updates any drum map. Returns true if anything changed. + // If doSignal is true, automatically emits SC_DRUM_MAP or sends audio message if audio is running and not idle. + ChangedType_t setOutPort(int i, bool doSignal = false); + // Sets the output channel, and for a drum track updates any drum map. Returns true if anything changed. + // If doSignal is true, automatically emits SC_DRUM_MAP or sends audio message if audio is running and not idle. + ChangedType_t setOutChannel(int i, bool doSignal = false); + // Same as setOutPort, but also transfers controller data to the new selected port. + ChangedType_t setOutPortAndUpdate(int port, bool doSignal = false); + // Same as setOutChannel, but also transfers controller data to the new selected channel. + ChangedType_t setOutChanAndUpdate(int chan, bool doSignal = false); + // Combines both setOutChannel and setOutPort operations, and transfers controller data to the new selected port/channel. + ChangedType_t setOutPortAndChannelAndUpdate(int port, int chan, bool doSignal = false); // Backward compatibility: For reading old songs. void setInPortAndChannelMask(unsigned int portmask, int chanmask); - void setRecEcho(bool b) { _recEcho = b; } + // Overridden for special midi output behaviour. + virtual bool noOutRoute() const; + int outPort() const { return _outPort; } int outChannel() const { return _outChannel; } - bool recEcho() const { return _recEcho; } - virtual bool isMute() const; virtual void setSolo(bool val); virtual void updateSoloStates(bool noDec); virtual void updateInternalSoloStates(); - + + virtual bool addStuckNote(const MidiPlayEvent& ev); + // These are only for 'live' (rec) notes for which we don't have a note-off time yet. Even times = 0. + virtual bool addStuckLiveNote(int port, int chan, int note, int vel = 64); + virtual bool removeStuckLiveNote(int port, int chan, int note); + virtual bool stuckLiveNoteExists(int port, int chan, int note); + virtual bool canRecord() const { return true; } + virtual bool canRecordMonitor() const { return true; } static void setVisible(bool t) { _isVisible = t; } static bool visible() { return _isVisible; } int getFirstControllerValue(int ctrl, int def=-1); int getControllerChangeAtTick(unsigned tick, int ctrl, int def=-1); - unsigned getControllerValueLifetime(unsigned tick, int ctrl); // returns the tick where this CC gets overriden by a new one + unsigned getControllerValueLifetime(unsigned tick, int ctrl); // returns the tick where this CC gets overridden by a new one // returns UINT_MAX for "never" void setClef(clefTypes i) { clefType = i; } clefTypes getClef() { return clefType; } DrumMap* drummap() { return _drummap; } - bool* drummap_hidden() { return _drummap_hidden; } int map_drum_in(int enote) { return drum_in_map[enote]; } void update_drum_in_map(); + void init_drum_ordering(); void init_drummap() { init_drummap(false); } // function with argument in private - bool auto_update_drummap(); - void set_drummap_tied_to_patch(bool); - bool drummap_tied_to_patch() { return _drummap_tied_to_patch; } + // For drum tracks, updates the drum map and returns true if anything changed. + // If doSignal is true, automatically emits SC_DRUM_MAP or sends audio message if audio is running and not idle. + bool updateDrummap(int doSignal); + //void workingDrumMapOperation(int index, bool updateDruminmap, const WorkingDrumMapEntry& item, PendingOperationList& ops); + WorkingDrumMapPatchList* workingDrumMap() const { return _workingDrumMapPatchList; } + void setWorkingDrumMap(WorkingDrumMapPatchList* list, bool isInstrumentMod); + void modifyWorkingDrumMap(WorkingDrumMapList& list, bool isReset, bool includeDefault, bool isInstrumentMod, bool doWholeMap); + //void modifyWorkingDrumMap(WorkingDrumMapPatchList& list, bool clear, bool isReset, bool isInstrumentMod); + // Returns a map item with members filled from either the original or working map item, + // depending on which Field flags are set. The returned map includes any requested + // WorkingDrumMapEntry::OverrideType track or instrument overrides. + void getMapItem(int patch, int index, DrumMap& dest_map, int overrideType) const; + // Returns a map item with members filled from either the original or working map item, + // depending on which Field flags are set. The returned map includes any requested + // WorkingDrumMapEntry::OverrideType track or instrument overrides. + // Same as getMapItem(), but determines patch at supplied tick. + void getMapItemAt(int tick, int index, DrumMap& dest_map, int overrideType) const; + // Returns OR'd WorkingDrumMapEntry::OverrideType flags indicating whether a map item's members, + // given by 'fields' (OR'd WorkingDrumMapEntry::Fields), are either the original or working map item. + // Here in MidiTrack the flags can be NoOverride, InstrumentOverride, and TrackOverride. See corresponding + // function in MidiInstrument. If patch is -1 it uses the track's current patch (midi controller hwCtrlVal). + int isWorkingMapItem(int index, int fields, int patch = -1) const; + + // Ensures there are NO duplicate enote fields in the final drum map array. + // Returns true if anything changed. + bool normalizeDrumMap(); + // Ensures there are NO duplicate enote fields in the final drum map array for the given patch. + // Returns true if anything changed. + bool normalizeDrumMap(int patch); + void set_drummap_ordering_tied_to_patch(bool); bool drummap_ordering_tied_to_patch() { return _drummap_ordering_tied_to_patch; } - //void writeOurDrumSettings(int level, Xml& xml) const; // above in private: - //void readOurDrumSettings(Xml& xml); // above in private: - void writeOurDrumMap(int level, Xml& xml, bool full) const; - void readOurDrumMap(Xml& xml, QString tag, bool dont_init=false, bool compatibility=false); + void MidiCtrlRemapOperation(int index, int newPort, int newChan, int newNote, MidiCtrlValRemapOperation* rmop); + + // Prints a handy debug table of drum map values and overrides etc. + void dumpMap(); }; //--------------------------------------------------------- @@ -340,27 +470,54 @@ class AudioTrack : public Track { bool _haveData; // Whether we have data from a previous process call during current cycle. - CtrlListList _controller; + CtrlListList _controller; // Holds all controllers including internal, plugin and synth. + ControlFifo _controlFifo; // For internal controllers like volume and pan. Plugins/synths have their own. CtrlRecList _recEvents; // recorded automation events + unsigned long _controlPorts; + Port* _controls; // For internal controllers like volume and pan. Plugins/synths have their own. + + double _curVolume; + double _curVol1; + double _curVol2; + bool _prefader; // prefader metering - std::vector _auxSend; + AuxSendValueList _auxSend; void readAuxSend(Xml& xml); int recFileNumber; bool _sendMetronome; AutomationType _automationType; - Pipeline* _efxPipe; double _gain; + + void initBuffers(); void internal_assign(const Track&, int flags); + void processTrackCtrls(unsigned pos, int trackChans, unsigned nframes, float** buffer); protected: + // Cached audio data for all channels. If prefader is not on, the first two channels + // have volume and pan applied if track is stereo, or the first channel has just + // volume applied if track is mono. float** outBuffers; + // Extra cached audio data. + float** outBuffersExtraMix; + // Just all zeros all the time, so we don't have to clear for silence. + float* audioInSilenceBuf; + // Just a place to connect all unused audio outputs. + float* audioOutDummyBuf; + // Internal temporary buffers for getData(). + float** _dataBuffers; + + // These two are not the same as the number of track channels which is always either 1 (mono) or 2 (stereo): + // Total number of output channels. int _totalOutChannels; + // Total number of input channels. int _totalInChannels; - unsigned bufferPos; + Pipeline* _efxPipe; + virtual bool getData(unsigned, int, unsigned, float**); + SndFileR _recFile; Fifo fifo; // fifo -> _recFile bool _processed; @@ -376,9 +533,15 @@ virtual AudioTrack* clone(int flags) const = 0; virtual Part* newPart(Part*p=0, bool clone=false); - virtual bool setRecordFlag1(bool f); - virtual void setRecordFlag2(bool f); + // Called from gui thread only. + virtual bool setRecordFlag1(bool); + // Called from audio thread only. + virtual void setRecordFlag2(bool); bool prepareRecording(); + // Same as setRecordFlag2 except it can automatically set the monitor flag + // depending on the global config.monitorOnRecord. Called from audio thread only. + // Returns whether the monitor was changed. + virtual bool setRecordFlag2AndCheckMonitor(bool); bool processed() { return _processed; } @@ -393,24 +556,35 @@ void showPendingPluginNativeGuis(); SndFileR recFile() const { return _recFile; } - void setRecFile(SndFileR sf) { _recFile = sf; } + void setRecFile(SndFileR sf) { _recFile = sf; } CtrlListList* controller() { return &_controller; } + // For setting/getting the _controls 'port' values. + unsigned long parameters() const { return _controlPorts; } + + void setParam(unsigned long i, double val); + double param(unsigned long i) const; virtual void setChannels(int n); virtual void setTotalOutChannels(int num); - virtual int totalOutChannels() { return _totalOutChannels; } + virtual int totalOutChannels() const { return _totalOutChannels; } virtual void setTotalInChannels(int num); - virtual int totalInChannels() { return _totalInChannels; } + virtual int totalInChannels() const { return _totalInChannels; } + // Number of routable inputs/outputs for each Route::RouteType. + virtual RouteCapabilitiesStruct routeCapabilities() const; + // Number of required processing buffers. + virtual int totalProcessBuffers() const { return (channels() == 1) ? 1 : totalOutChannels(); } - virtual bool isMute() const; virtual void setSolo(bool val); virtual void updateSoloStates(bool noDec); virtual void updateInternalSoloStates(); + // Puts to the recording fifo. void putFifo(int channels, unsigned long n, float** bp); - + // Transfers the recording fifo to _recFile. void record(); + // Returns the recording fifo current count. + int recordFifoCount() { return fifo.getCount(); } virtual void setMute(bool val); virtual void setOff(bool val); @@ -429,12 +603,16 @@ double auxSend(int idx) const; void setAuxSend(int idx, double v); void addAuxSend(int n); + void addAuxSendOperation(int n, PendingOperationList& ops); void setPrefader(bool val); Pipeline* efxPipe() { return _efxPipe; } void deleteAllEfxGuis(); void clearEfxList(); + // Removes any existing plugin and inserts plugin into effects rack, and calls setupPlugin. void addPlugin(PluginI* plugin, int idx); + // Assigns valid ID and track to plugin, and creates controllers for plugin. + void setupPlugin(PluginI* plugin, int idx); double pluginCtrlVal(int ctlID) const; void setPluginCtrlVal(int param, double val); @@ -442,18 +620,40 @@ void readVolume(Xml& xml); virtual void preProcessAlways() { _processed = false; } - virtual void addData(unsigned samplePos, int channels, int srcStartChan, int srcChannels, unsigned frames, float** buffer); - virtual void copyData(unsigned samplePos, int channels, int srcStartChan, int srcChannels, unsigned frames, float** buffer, bool add=false); + // Gathers this track's audio data and either copies or adds it to a supplied destination buffer. + // If the per-channel 'addArray' is supplied, whether to copy or add each channel is given in the array, + // otherwise it is given by the bulk 'add' flag. + // The range of buffers in 'dstBuffer' given by 'dstStartChan' and 'availDstChannels' are filled with data. + // If 'availDstChannels' is greater than 'requestedDstChannels', the excess buffers are filled with silence. + // If 'requestedDstChannels' is greater than 'availDstChannels', copyData() acts AS IF 'requestedDstChannels' + // was the real availDstChannels except it only fills up to 'availDstChannels'. This is to allow copyData() + // to mix properly even when some routes are not available (ex. switching a track from stereo to mono, + // which has an existing one-channel route on the right channel). + // The 'srcStartChan' and 'srcChannels' give the range of channels to copy or add from this track. + // If 'srcStartChan' is -1 it will be set to zero. If 'srcChannels' is -1`it will be set to this track's output channels. + // The 'dstStartChan' can also be -1, but 'requestedDstChannels' and availDstChannels cannot. + virtual void copyData(unsigned samplePos, + int dstStartChan, int requestedDstChannels, int availDstChannels, + int srcStartChan, int srcChannels, + unsigned frames, float** dstBuffer, + bool add = false, + const bool* addArray = 0); + virtual bool hasAuxSend() const { return false; } + + virtual float latency(int channel); // automation virtual AutomationType automationType() const { return _automationType; } virtual void setAutomationType(AutomationType t); - void processAutomationEvents(); + // Fills operations if given, otherwise creates and executes its own operations list. + void processAutomationEvents(Undo* operations = 0); 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; + bool addScheduledControlEvent(int track_ctrl_id, double val, unsigned frame); // return true if event cannot be delivered + void enableController(int track_ctrl_id, bool en); + bool controllerEnabled(int track_ctrl_id) const; + // Enable all track and plugin controllers, and synth controllers if applicable. + void enableAllControllers(); void recordAutomation(int n, double v); void startAutoRecord(int, double); void stopAutoRecord(int, double); @@ -465,6 +665,7 @@ void eraseRangeACEvents(int, int, int); void addACEvent(int, int, double); void changeACEvent(int id, int frame, int newframe, double newval); + const AuxSendValueList &getAuxSendValueList() { return _auxSend; } }; //--------------------------------------------------------- @@ -482,6 +683,7 @@ AudioInput(const AudioInput&, int flags); virtual ~AudioInput(); + virtual float latency(int channel); virtual void assign(const Track&, int flags); AudioInput* clone(int flags) const { return new AudioInput(*this, flags); } virtual AudioInput* newTrack() const { return new AudioInput(); } @@ -492,6 +694,8 @@ void setJackPort(int channel, void*p) { jackPorts[channel] = p; } virtual void setChannels(int n); virtual bool hasAuxSend() const { return true; } + // Number of routable inputs/outputs for each Route::RouteType. + virtual RouteCapabilitiesStruct routeCapabilities() const; static void setVisible(bool t) { _isVisible = t; } virtual int height() const; static bool visible() { return _isVisible; } @@ -507,7 +711,6 @@ float* buffer1[MAX_CHANNELS]; unsigned long _nframes; static bool _isVisible; - float* _monitorBuffer[MAX_CHANNELS]; void internal_assign(const Track& t, int flags); public: @@ -524,13 +727,14 @@ void* jackPort(int channel) { return jackPorts[channel]; } void setJackPort(int channel, void*p) { jackPorts[channel] = p; } virtual void setChannels(int n); + // Number of routable inputs/outputs for each Route::RouteType. + virtual RouteCapabilitiesStruct routeCapabilities() const; void processInit(unsigned); void process(unsigned pos, unsigned offset, unsigned); void processWrite(); void silence(unsigned); virtual bool canRecord() const { return true; } - float** monitorBuffer() { return _monitorBuffer; } static void setVisible(bool t) { _isVisible = t; } static bool visible() { return _isVisible; } virtual int height() const; @@ -575,6 +779,12 @@ virtual void write(int, Xml&) const; virtual bool getData(unsigned, int, unsigned, float**); virtual void setChannels(int n); + // Number of routable inputs/outputs for each Route::RouteType. + virtual RouteCapabilitiesStruct routeCapabilities() const { + RouteCapabilitiesStruct s = AudioTrack::routeCapabilities(); + s._trackChannels._inRoutable = false; + s._trackChannels._inChannels = 0; + return s; } float** sendBuffer() { return buffer; } static void setVisible(bool t) { _isVisible = t; } virtual int height() const; @@ -604,11 +814,16 @@ virtual WaveTrack* clone(int flags) const { return new WaveTrack(*this, flags); } virtual WaveTrack* newTrack() const { return new WaveTrack(); } virtual Part* newPart(Part*p=0, bool clone=false); + // Returns true if any event in any part was opened. Does not operate on the part's clones, if any. + bool openAllParts(); + // Returns true if any event in any part was closed. Does not operate on the part's clones, if any. + bool closeAllParts(); virtual void read(Xml&); virtual void write(int, Xml&) const; - virtual void fetchData(unsigned pos, unsigned frames, float** bp, bool doSeek); + // If overwrite is true, copies the data. If false, adds the data. + virtual void fetchData(unsigned pos, unsigned frames, float** bp, bool doSeek, bool overwrite); virtual bool getData(unsigned, int ch, unsigned, float** bp); @@ -618,6 +833,7 @@ virtual bool hasAuxSend() const { return true; } bool canEnableRecord() const; virtual bool canRecord() const { return true; } + virtual bool canRecordMonitor() const { return true; } static void setVisible(bool t) { _isVisible = t; } virtual int height() const; static bool visible() { return _isVisible; } @@ -686,6 +902,9 @@ const_iterator find(const Track* t) const { return std::find(begin(), end(), t); } + bool contains(const Track* t) const { + return std::find(begin(), end(), t) != end(); + } unsigned index(const Track* t) const { unsigned n = 0; for (vlist::const_iterator i = begin(); i != end(); ++i, ++n) { @@ -716,6 +935,39 @@ } } } + // Returns the number of selected tracks in this list. + int countSelected() const { + int c = 0; + for (vlist::const_iterator i = begin(); i != end(); ++i) { + if ((*i)->selected()) { + ++c; + } + } + return c; + } + // Returns the current (most recent) selected track, or null if none. + // It returns the track with the highest _selectionOrder. + // This helps with multi-selection common-property editing. + T currentSelection() const { + T cur = 0; + int c = 0; + int so; + for (vlist::const_iterator i = begin(); i != end(); ++i) { + T t = *i; + so = t->selectionOrder(); + if (t->selected() && so >= c) { + cur = t; + c = so; + } + } + return cur; + } + // Selects or unselects all tracks in this list. + void selectAll(bool select) { + for (vlist::iterator i = begin(); i != end(); ++i) { + (*i)->setSelected(select); + } + } }; typedef tracklist TrackList; @@ -752,7 +1004,8 @@ extern void addPortCtrlEvents(MidiTrack* t); extern void removePortCtrlEvents(MidiTrack* t); - +extern void addPortCtrlEvents(Track* track, PendingOperationList& ops); +extern void removePortCtrlEvents(Track* track, PendingOperationList& ops); } // namespace MusECore #endif diff -Nru muse-2.1.2/muse/transport.cpp muse-3.0.2+ds1/muse/transport.cpp --- muse-2.1.2/muse/transport.cpp 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/transport.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -74,6 +74,18 @@ return button; } +static QToolButton* newButton(const QIcon* icon, const QString& tt, + bool toggle=false, QWidget* parent=0) + { + QToolButton* button = new QToolButton(parent); + button->setFixedHeight(25); + button->setIcon(*icon); + button->setCheckable(toggle); + button->setToolTip(tt); + button->setFocusPolicy(Qt::NoFocus); + return button; + } + //--------------------------------------------------------- // Handle // allows moving a root-window with the mouse @@ -384,25 +396,25 @@ tb = new QHBoxLayout; tb->setSpacing(0); - buttons[0] = newButton(startIcon, tr("rewind to start")); + buttons[0] = newButton(rewindToStartSVGIcon, tr("rewind to start")); buttons[0]->setWhatsThis(tr("Click this button to rewind to start position")); - buttons[1] = newButton(frewindIcon, tr("rewind")); + buttons[1] = newButton(rewindSVGIcon, tr("rewind")); buttons[1]->setAutoRepeat(true); buttons[1]->setWhatsThis(tr("Click this button to rewind")); - buttons[2] = newButton(fforwardIcon, tr("forward")); + buttons[2] = newButton(fastForwardSVGIcon, tr("forward")); buttons[2]->setAutoRepeat(true); buttons[2]->setWhatsThis(tr("Click this button to forward current play position")); - buttons[3] = newButton(stopIcon, tr("stop"), true); + buttons[3] = newButton(stopSVGIcon, tr("stop"), true); buttons[3]->setChecked(true); // set STOP buttons[3]->setWhatsThis(tr("Click this button to stop playback")); - buttons[4] = newButton(playIcon, tr("play"), true); + buttons[4] = newButton(playSVGIcon, tr("play"), true); buttons[4]->setWhatsThis(tr("Click this button to start playback")); - buttons[5] = newButton(record_on_Icon, tr("record"), true); + buttons[5] = newButton(recMasterSVGIcon, tr("record"), true); buttons[5]->setWhatsThis(tr("Click this button to enable recording")); for (int i = 0; i < 6; ++i) @@ -437,7 +449,7 @@ syncButton = newButton(tr("Sync"), tr("external sync on/off"), true,19); - jackTransportButton = newButton(tr("Jack"), tr("Jack transport sync on/off"), true,19); + jackTransportButton = newButton(tr("Jack"), tr("Use Jack Transport"), true,19); quantizeButton->setChecked(MusEGlobal::song->quantize()); clickButton->setChecked(MusEGlobal::song->click()); @@ -668,7 +680,9 @@ void Transport::setMasterFlag(bool f) { + masterButton->blockSignals(true); masterButton->setChecked(f); + masterButton->blockSignals(false); } //--------------------------------------------------------- @@ -688,7 +702,9 @@ void Transport::setQuantizeFlag(bool f) { + quantizeButton->blockSignals(true); quantizeButton->setChecked(f); + quantizeButton->blockSignals(false); } //--------------------------------------------------------- @@ -697,7 +713,9 @@ void Transport::setSyncFlag(bool f) { + syncButton->blockSignals(true); syncButton->setChecked(f); + syncButton->blockSignals(false); } //--------------------------------------------------------- @@ -724,10 +742,6 @@ void Transport::songChanged(MusECore::SongChangedFlags_t flags) { - // Is it simply a midi controller value adjustment? Forget it. - if(flags == SC_MIDI_CONTROLLER) - return; - slider->setRange(0, MusEGlobal::song->len()); int cpos = MusEGlobal::song->cpos(); int t = MusEGlobal::tempomap.tempo(cpos); @@ -744,7 +758,11 @@ setTimesig(z, n); } if (flags & SC_MASTER) + { + masterButton->blockSignals(true); masterButton->setChecked(MusEGlobal::song->masterFlag()); + masterButton->blockSignals(false); + } } //--------------------------------------------------------- @@ -753,7 +771,9 @@ void Transport::syncChanged(bool flag) { + syncButton->blockSignals(true); syncButton->setChecked(flag); + syncButton->blockSignals(false); buttons[0]->setEnabled(!flag); // goto start buttons[1]->setEnabled(!flag); // rewind buttons[2]->setEnabled(!flag); // forward @@ -762,7 +782,9 @@ slider->setEnabled(!flag); masterButton->setEnabled(!flag); if (flag) { + masterButton->blockSignals(true); masterButton->setChecked(false); + masterButton->blockSignals(false); MusEGlobal::song->setMasterFlag(false); tempo->setTempo(0); // slave mode: show "extern" } @@ -777,7 +799,9 @@ void Transport::jackSyncChanged(bool flag) { + jackTransportButton->blockSignals(true); jackTransportButton->setChecked(flag); + jackTransportButton->blockSignals(false); } //--------------------------------------------------------- // stopToggled diff -Nru muse-2.1.2/muse/type_defs.h muse-3.0.2+ds1/muse/type_defs.h --- muse-2.1.2/muse/type_defs.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/type_defs.h 2017-12-04 21:01:18.000000000 +0000 @@ -29,6 +29,8 @@ namespace MusECore { typedef int64_t SongChangedFlags_t; +typedef int64_t EventID_t; +#define MUSE_INVALID_EVENT_ID -1 } // namespace MusECore diff -Nru muse-2.1.2/muse/undo.cpp muse-3.0.2+ds1/muse/undo.cpp --- muse-2.1.2/muse/undo.cpp 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse/undo.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -21,7 +21,8 @@ // //========================================================= -//#include "sig.h" +#include "assert.h" + #include "al/sig.h" #include "keyevent.h" @@ -29,11 +30,20 @@ #include "song.h" #include "globals.h" #include "audio.h" +#include "operations.h" +#include "tempo.h" +#include "part.h" +#include "audiodev.h" +#include "track.h" #include #include +#include #include +// Enable for debugging: +//#define _UNDO_DEBUG_ + namespace MusECore { // iundo points to last Undo() in Undo-list @@ -49,16 +59,20 @@ const char* UndoOp::typeName() { static const char* name[] = { + "AddRoute", "DeleteRoute", "AddTrack", "DeleteTrack", - "AddPart", "DeletePart", "ModifyPart", - "AddEvent", "DeleteEvent", "ModifyEvent", - "AddTempo", "DeleteTempo", - "AddSig", "DeleteSig", - "AddKey", "DeleteKey", + "AddPart", "DeletePart", "MovePart", "ModifyPartLength", "ModifyPartName", "SelectPart", + "AddEvent", "DeleteEvent", "ModifyEvent", "SelectEvent", + "AddAudioCtrlVal", "DeleteAudioCtrlVal", "ModifyAudioCtrlVal", "ModifyAudioCtrlValList", + "AddTempo", "DeleteTempo", "ModifyTempo", "SetGlobalTempo", + "AddSig", "DeleteSig", "ModifySig", + "AddKey", "DeleteKey", "ModifyKey", "ModifyTrackName", "ModifyTrackChannel", - "SwapTrack", + "SetTrackRecord", "SetTrackMute", "SetTrackSolo", "SetTrackRecMonitor", "SetTrackOff", + "MoveTrack", "ModifyClip", "ModifyMarker", - "ModifySongLen", "DoNothing" + "ModifySongLen", "DoNothing", + "EnableAllAudioControllers" }; return name[type]; } @@ -75,10 +89,6 @@ case DeleteTrack: printf("%d %s\n", trackno, track->name().toLatin1().constData()); break; - case AddPart: - case DeletePart: - case ModifyPart: - break; case AddEvent: case DeleteEvent: printf("old event:\n"); @@ -90,23 +100,25 @@ part->dump(5); break; case ModifyTrackName: - printf("<%s>-<%s>\n", _oldName, _newName); + printf("<%s>-<%s>\n", _oldName->toLocal8Bit().data(), _newName->toLocal8Bit().data()); break; case ModifyTrackChannel: - printf("<%d>-<%d>\n", _oldPropValue, _newPropValue); + printf("%s <%d>-<%d>\n", track->name().toLatin1().constData(), _oldPropValue, _newPropValue); + break; + case SetTrackRecord: + printf("%s %d\n", track->name().toLatin1().constData(), a); break; - case ModifyEvent: - case AddTempo: - case DeleteTempo: - case AddSig: - case SwapTrack: - case DeleteSig: - case ModifyClip: - case ModifyMarker: - case AddKey: - case DeleteKey: - case ModifySongLen: - case DoNothing: + case SetTrackMute: + printf("%s %d\n", track->name().toLatin1().constData(), a); + break; + case SetTrackSolo: + printf("%s %d\n", track->name().toLatin1().constData(), a); + break; + case SetTrackRecMonitor: + printf("%s %d\n", track->name().toLatin1().constData(), a); + break; + case SetTrackOff: + printf("%s %d\n", track->name().toLatin1().constData(), a); break; default: break; @@ -132,15 +144,11 @@ { case UndoOp::DeleteTrack: if(i->track) - delete i->track; + delete const_cast(i->track); break; case UndoOp::DeletePart: - delete i->oPart; - break; - - case UndoOp::ModifyPart: - delete i->oPart; + delete const_cast(i->part); break; case UndoOp::ModifyMarker: @@ -148,13 +156,21 @@ delete i->copyMarker; break; + case UndoOp::ModifyPartName: case UndoOp::ModifyTrackName: if (i->_oldName) - delete [] i->_oldName; + delete i->_oldName; if (i->_newName) - delete [] i->_newName; + delete i->_newName; break; + case UndoOp::ModifyAudioCtrlValList: + if (i->_eraseCtrlList) + delete i->_eraseCtrlList; + if (i->_addCtrlList) + delete i->_addCtrlList; + break; + default: break; } @@ -176,24 +192,29 @@ break; case UndoOp::AddPart: - delete i->oPart; + delete i->part; break; - case UndoOp::ModifyPart: - delete i->nPart; - break; case UndoOp::ModifyMarker: if (i->realMarker) delete i->realMarker; break; + case UndoOp::ModifyPartName: case UndoOp::ModifyTrackName: if (i->_oldName) - delete [] i->_oldName; + delete i->_oldName; if (i->_newName) - delete [] i->_newName; + delete i->_newName; break; + case UndoOp::ModifyAudioCtrlValList: + if (i->_eraseCtrlList) + delete i->_eraseCtrlList; + if (i->_addCtrlList) + delete i->_addCtrlList; + break; + default: break; } @@ -213,9 +234,9 @@ void Song::startUndo() { redoList->clearDelete(); // redo must be invalidated when a new undo is started - MusEGlobal::redoAction->setEnabled(false); + MusEGlobal::redoAction->setEnabled(false); setUndoRedoText(); - + undoList->push_back(Undo()); updateFlags = 0; undoMode = true; @@ -227,6 +248,26 @@ void Song::endUndo(SongChangedFlags_t flags) { + // It is possible the current list may be empty after our optimizations during appending + // of given operations to the current list. (Or if no operations were pushed between startUndo and endUndo). + // Get rid of an empty current list now. + if(undoList->back().empty()) + undoList->pop_back(); + else + { + riUndo prev_undo = undoList->rbegin(); + prev_undo++; + if (prev_undo!=undoList->rend()) + { + // try to merge the current Undo with the last one + if (prev_undo->merge_combo(undoList->back())) + undoList->pop_back(); + } + } + + // Even if the current list was empty, or emptied during appending of given operations to the current list, + // the given operations were executed so we still need to inform that something may have changed. + updateFlags |= flags; endMsgCmd(); undoMode = false; @@ -275,61 +316,938 @@ } } +void Undo::push_back(const UndoOp& op) +{ + insert(end(), op); +} -void cleanOperationGroup(Undo& group) +void Undo::insert(Undo::iterator position, Undo::const_iterator first, Undo::const_iterator last) { - using std::set; - - set processed_tracks; - set processed_parts; - - for (iUndoOp op=group.begin(); op!=group.end();) - { - iUndoOp op_=op; - op_++; - - if (op->type==UndoOp::DeleteTrack) - { - if (processed_tracks.find(op->track)!=processed_tracks.end()) - group.erase(op); - else - processed_tracks.insert(op->track); - } - else if ((op->type==UndoOp::ModifyPart) || (op->type==UndoOp::DeletePart)) - { - if (processed_parts.find(op->oPart)!=processed_parts.end()) - group.erase(op); - else - processed_parts.insert(op->oPart); - } - - op=op_; - } + for(Undo::const_iterator iuo = first; iuo != last; ++iuo) + insert(position, *iuo); } +void Undo::insert(Undo::iterator position, Undo::size_type n, const UndoOp& op) +{ + for(Undo::size_type i = 0; i != n; ++i) + insert(position, op); +} -bool Song::applyOperationGroup(Undo& group, bool doUndo) +void Undo::insert(Undo::iterator position, const UndoOp& op) { - if (!group.empty()) + UndoOp n_op = op; + +#ifdef _UNDO_DEBUG_ + switch(n_op.type) + { + case UndoOp::AddRoute: + fprintf(stderr, "Undo::insert: AddRoute\n"); + break; + case UndoOp::DeleteRoute: + fprintf(stderr, "Undo::insert: DeleteRoute\n"); + break; + + + case UndoOp::AddTrack: + fprintf(stderr, "Undo::insert: AddTrack\n"); + break; + case UndoOp::DeleteTrack: + fprintf(stderr, "Undo::insert: DeleteTrack\n"); + break; + case UndoOp::MoveTrack: + fprintf(stderr, "Undo::insert: MoveTrack\n"); + break; + case UndoOp::ModifyTrackName: + fprintf(stderr, "Undo::insert: ModifyTrackName\n"); + break; + case UndoOp::ModifyTrackChannel: + fprintf(stderr, "Undo::insert: ModifyTrackChannel\n"); + break; + case UndoOp::SetTrackRecord: + fprintf(stderr, "Undo::insert: SetTrackRecord\n"); + break; + case UndoOp::SetTrackMute: + fprintf(stderr, "Undo::insert: SetTrackMute\n"); + break; + case UndoOp::SetTrackSolo: + fprintf(stderr, "Undo::insert: SetTrackSolo\n"); + break; + case UndoOp::SetTrackRecMonitor: + fprintf(stderr, "Undo::insert: SetTrackRecMonitor\n"); + break; + case UndoOp::SetTrackOff: + fprintf(stderr, "Undo::insert: SetTrackOff\n"); + break; + + + case UndoOp::AddPart: + fprintf(stderr, "Undo::insert: AddPart\n"); + break; + case UndoOp::DeletePart: + fprintf(stderr, "Undo::insert: DeletePart\n"); + break; + case UndoOp::MovePart: + fprintf(stderr, "Undo::insert: MovePart\n"); + break; + case UndoOp::SelectPart: + fprintf(stderr, "Undo::insert: SelectPart\n"); + break; + case UndoOp::ModifyPartName: + fprintf(stderr, "Undo::insert: ModifyPartName\n"); + break; + case UndoOp::ModifyPartLength: + fprintf(stderr, "Undo::insert: ModifyPartLength\n"); + break; + + + case UndoOp::AddEvent: + fprintf(stderr, "Undo::insert: AddEvent\n"); + break; + case UndoOp::DeleteEvent: + fprintf(stderr, "Undo::insert: DeleteEvent\n"); + break; + case UndoOp::ModifyEvent: + fprintf(stderr, "Undo::insert: ModifyEvent\n"); + break; + case UndoOp::SelectEvent: + fprintf(stderr, "Undo::insert: SelectEvent\n"); + break; + + + case UndoOp::AddAudioCtrlVal: + fprintf(stderr, "Undo::insert: AddAudioCtrlVal\n"); + break; + case UndoOp::DeleteAudioCtrlVal: + fprintf(stderr, "Undo::insert: DeleteAudioCtrlVal\n"); + break; + case UndoOp::ModifyAudioCtrlVal: + fprintf(stderr, "Undo::insert: ModifyAudioCtrlVal\n"); + break; + case UndoOp::ModifyAudioCtrlValList: + fprintf(stderr, "Undo::insert: ModifyAudioCtrlValList\n"); + break; + + + case UndoOp::AddTempo: + fprintf(stderr, "Undo::insert: AddTempo tempo:%d tick:%d\n", n_op.b, n_op.a); + break; + case UndoOp::DeleteTempo: + fprintf(stderr, "Undo::insert: DeleteTempo old val:%d tick:%d\n", n_op.b, n_op.a); + break; + case UndoOp::ModifyTempo: + fprintf(stderr, "Undo::insert: ModifyTempo old:%d new:%d tick:%d\n", n_op.b, n_op.c, n_op.a); + break; + case UndoOp::SetGlobalTempo: + fprintf(stderr, "Undo::insert: SetGlobalTempo\n"); + break; + + + case UndoOp::AddSig: + fprintf(stderr, "Undo::insert: AddSig\n"); + break; + case UndoOp::DeleteSig: + fprintf(stderr, "Undo::insert: DeleteSig\n"); + break; + case UndoOp::ModifySig: + fprintf(stderr, "Undo::insert: ModifySig\n"); + break; + + + case UndoOp::AddKey: + fprintf(stderr, "Undo::insert: AddKey\n"); + break; + case UndoOp::DeleteKey: + fprintf(stderr, "Undo::insert: DeleteKey\n"); + break; + case UndoOp::ModifyKey: + fprintf(stderr, "Undo::insert: ModifyKey\n"); + break; + + + case UndoOp::ModifyClip: + fprintf(stderr, "Undo::insert: ModifyClip\n"); + break; + + + case UndoOp::ModifyMarker: + fprintf(stderr, "Undo::insert: ModifyMarker\n"); + break; + + + case UndoOp::ModifySongLen: + fprintf(stderr, "Undo::insert: ModifySongLen\n"); + break; + + + case UndoOp::DoNothing: + fprintf(stderr, "Undo::insert: DoNothing\n"); + break; + + case UndoOp::EnableAllAudioControllers: + fprintf(stderr, "Undo::insert: EnableAllAudioControllers\n"); + break; + + + default: + break; + } +#endif + + // (NOTE: Use this handy speed-up 'if' line to exclude unhandled operation types) + if(n_op.type != UndoOp::ModifyTrackChannel && n_op.type != UndoOp::ModifyClip && n_op.type != UndoOp::ModifyMarker && n_op.type != UndoOp::DoNothing) + { + // TODO FIXME: Must look beyond position and optimize in that direction too ! + //for(Undo::iterator iuo = begin(); iuo != position; ++iuo) + iterator iuo = position; + while(iuo != begin()) + { + --iuo; + UndoOp& uo = *iuo; + + switch(n_op.type) { - cleanOperationGroup(group); - //this is a HACK! but it works :) (added by flo93) - redoList->push_back(group); - redo(); - - if (!doUndo) + case UndoOp::AddRoute: + if(uo.type == UndoOp::AddRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo) + { + fprintf(stderr, "MusE error: Undo::insert(): Double AddRoute. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::DeleteRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo) + { + // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. + erase(iuo); + return; + } + break; + + case UndoOp::DeleteRoute: + if(uo.type == UndoOp::DeleteRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo) + { + fprintf(stderr, "MusE error: Undo::insert(): Double DeleteRoute. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::AddRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(iuo); + return; + } + break; + + + case UndoOp::ModifyTrackName: + if(uo.type == UndoOp::ModifyTrackName && uo.track == n_op.track) + { + fprintf(stderr, "MusE error: Undo::insert(): Double ModifyTrackName. Ignoring.\n"); + return; + } + break; + + case UndoOp::MoveTrack: + if(uo.type == UndoOp::MoveTrack && uo.a == n_op.a) + { + // Simply replace the 'to track' value. + uo.b = n_op.b; + return; + } + break; + + case UndoOp::SetTrackRecord: + if(uo.type == UndoOp::SetTrackRecord && uo.track == n_op.track) + { + if(uo.a == n_op.a) + { + fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackRecord. Ignoring.\n"); + return; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(iuo); + return; + } + } + break; + + case UndoOp::SetTrackMute: + if(uo.type == UndoOp::SetTrackMute && uo.track == n_op.track) + { + if(uo.a == n_op.a) + { + fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackMute. Ignoring.\n"); + return; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(iuo); + return; + } + } + break; + + case UndoOp::SetTrackSolo: + if(uo.type == UndoOp::SetTrackSolo && uo.track == n_op.track) + { + if(uo.a == n_op.a) + { + fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackSolo. Ignoring.\n"); + return; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(iuo); + return; + } + } + break; + + case UndoOp::SetTrackRecMonitor: + if(uo.type == UndoOp::SetTrackRecMonitor && uo.track == n_op.track) + { + if(uo.a == n_op.a) + { + fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackRecMonitor. Ignoring.\n"); + return; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(iuo); + return; + } + } + break; + + case UndoOp::SetTrackOff: + if(uo.type == UndoOp::SetTrackOff && uo.track == n_op.track) + { + if(uo.a == n_op.a) + { + fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackOff. Ignoring.\n"); + return; + } + else + { + // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. + erase(iuo); + return; + } + } + break; + + case UndoOp::ModifyPartName: + if(uo.type == UndoOp::ModifyPartName && uo.part == n_op.part) + { + fprintf(stderr, "MusE error: Undo::insert(): Double ModifyPartName. Ignoring.\n"); + return; + } + break; + + case UndoOp::ModifyPartLength: + if(uo.type == UndoOp::ModifyPartLength && uo.part == n_op.part) + { + // Simply replace the new value. + uo.new_partlen_or_pos = n_op.new_partlen_or_pos; + return; + } + break; + + case UndoOp::MovePart: + if(uo.type == UndoOp::MovePart && uo.part == n_op.part) + { + // Simply replace the new value and new track. + uo.new_partlen_or_pos = n_op.new_partlen_or_pos; + uo.track = n_op.track; + return; + } + break; + + case UndoOp::AddPart: + if(uo.type == UndoOp::AddPart && uo.part == n_op.part) + { + fprintf(stderr, "MusE error: Undo::insert(): Double AddPart. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::DeletePart && uo.part == n_op.part) + { + // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. + erase(iuo); + return; + } + break; + + case UndoOp::DeletePart: + if(uo.type == UndoOp::DeletePart && uo.part == n_op.part) + { + fprintf(stderr, "MusE error: Undo::insert(): Double DeletePart. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::AddPart && uo.part == n_op.part) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(iuo); + return; + } + break; + + + case UndoOp::AddEvent: + if(uo.type == UndoOp::AddEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part) + { + fprintf(stderr, "MusE error: Undo::insert(): Double AddEvent. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::DeleteEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part) + { + // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. + erase(iuo); + return; + } + else if(uo.type == UndoOp::ModifyEvent && uo.part == n_op.part) + { + if(uo.nEvent == n_op.nEvent) + { + // Modify followed by adding of the modify's new event, is equivalent to just modifying with the added event. + fprintf(stderr, "MusE error: Undo::insert(): ModifyEvent, then AddEvent same new event (double AddEvent). Ignoring.\n"); + return; + } + else if(uo.oEvent == n_op.nEvent) + { + // Modify followed by adding of the modify's old event, is equivalent to just adding the event. + // Transform the ModifyEvent operation into an AddEvent. + uo.type = UndoOp::AddEvent; + uo.nEvent = uo.oEvent; + return; + } + } + break; + + case UndoOp::DeleteEvent: + if(uo.type == UndoOp::DeleteEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part) + { + fprintf(stderr, "MusE error: Undo::insert(): Double DeleteEvent. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::AddEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(iuo); + return; + } + else if(uo.type == UndoOp::ModifyEvent && uo.part == n_op.part) + { + if(uo.oEvent == n_op.nEvent) + { + // Modify followed by delete of the modify's old event, is an error - two deletes of the same event. + fprintf(stderr, "MusE error: Undo::insert(): ModifyEvent, then DeleteEvent same old event (double DeleteEvent). Ignoring.\n"); + return; + } + else if(uo.nEvent == n_op.nEvent) + { + // Modify followed by delete of the modify's new event, is equivalent to just deleting the old event. + // Transform the operation into a DeleteEvent. + uo.type = UndoOp::DeleteEvent; + uo.nEvent = uo.oEvent; + return; + } + } + break; + + case UndoOp::ModifyEvent: + if(n_op.oEvent == n_op.nEvent) + { + // Equivalent to deleting then adding the same event - useless, cancels out. + return; + } + else if(uo.type == UndoOp::ModifyEvent && uo.part == n_op.part) + { + if(uo.oEvent == n_op.oEvent && uo.nEvent == n_op.nEvent) + { + fprintf(stderr, "MusE error: Undo::insert(): Double ModifyEvent. Ignoring.\n"); + return; + } + // Are inner add/delete pair the same event? + else if(uo.nEvent == n_op.oEvent) + { + // Are outer delete/add pair the same event? + if(uo.oEvent == n_op.nEvent) + { + // First ModifyEvent old event and second ModifyEvent new event are both the same, equivalent to doing nothing. + // Cancel out the two ModifyEvent operations by erasing the existing ModifyEvent command. + erase(iuo); + return; + } + else + { + // Outer delete/add pair are not the same event... + // Transform the existing ModifyEvent operation into a DeleteEvent. + uo.type = UndoOp::DeleteEvent; + uo.nEvent = uo.oEvent; + // Transform the requested ModifyEvent operation into an AddEvent. + n_op.type = UndoOp::AddEvent; + // Allow it to add... + } + } + // Inner add/delete pair are not the same event. Are outer delete/add pair the same event? + else if(uo.oEvent == n_op.nEvent) + { + // Transform the existing ModifyEvent operation into an AddEvent. + uo.type = UndoOp::AddEvent; + // Transform the requested ModifyEvent operation into a DeleteEvent. + n_op.type = UndoOp::DeleteEvent; + n_op.nEvent = n_op.oEvent; + // Allow it to add... + } + } + else if(uo.type == UndoOp::AddEvent && uo.part == n_op.part) + { + if(uo.nEvent == n_op.oEvent) + { + // Add followed by modify with old event same as added event, is equivalent to just adding modify's new event. + // Replace the existing AddEvent command's event with the requested ModifyEvent command's new event. + uo.nEvent = n_op.nEvent; + return; + } + if(uo.nEvent == n_op.nEvent) + { + // Add followed by modify with new event same as added event, is a caller error. + fprintf(stderr, "MusE error: Undo::insert(): AddEvent, then ModifyEvent same new event (double AddEvent). Ignoring.\n"); + return; + } + } + if(uo.type == UndoOp::DeleteEvent && uo.part == n_op.part) + { + if(uo.nEvent == n_op.oEvent) + { + // Delete followed by modify with old event same as deleted event, is an error. + fprintf(stderr, "MusE error: Undo::insert(): DeleteEvent, then ModifyEvent same old event (double DeleteEvent). Ignoring.\n"); + return; + } + if(uo.nEvent == n_op.nEvent) + { + // Delete followed by modify with new event same as deleted event, is equivalent to just deleting modify's old event. + // Replace the existing DeleteEvent command's event with the requested ModifyEvent command's old event. + uo.nEvent = n_op.oEvent; + } + } + break; + + case UndoOp::AddAudioCtrlVal: + if(uo.type == UndoOp::AddAudioCtrlVal && uo.track == n_op.track && + uo._audioCtrlID == n_op._audioCtrlID && + uo._audioCtrlFrame == n_op._audioCtrlFrame) + { + // Simply replace the original value and frame. + uo._audioCtrlVal = n_op._audioCtrlVal; + return; + } +// TODO If possible. +// else if(uo.type == UndoOp::DeleteAudioCtrlVal && uo.track == n_op.track && +// uo._audioCtrlID == n_op._audioCtrlID && +// uo._audioCtrlFrame == n_op._audioCtrlFrame) +// { +// // Delete followed by add, at the same frame. Transform the delete into a modify. +// uo.type = UndoOp::ModifyAudioCtrlVal; +// uo._audioCtrlVal = n_op._audioCtrlVal; +// uo._audioNewCtrlFrame = +// return; +// } + break; + + case UndoOp::DeleteAudioCtrlVal: + if(uo.type == UndoOp::DeleteAudioCtrlVal && uo.track == n_op.track && + uo._audioCtrlID == n_op._audioCtrlID && + uo._audioCtrlFrame == n_op._audioCtrlFrame) + { + fprintf(stderr, "MusE error: Undo::insert(): Double DeleteAudioCtrlVal. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::AddAudioCtrlVal && uo.track == n_op.track && + uo._audioCtrlID == n_op._audioCtrlID && + uo._audioCtrlFrame == n_op._audioCtrlFrame) + { + // Add followed by delete, at the same frame, is useless. Cancel out the add + delete by erasing the add command. + erase(iuo); + return; + } + break; + + case UndoOp::ModifyAudioCtrlVal: + if(uo.type == UndoOp::ModifyAudioCtrlVal && uo.track == n_op.track && + uo._audioCtrlID == n_op._audioCtrlID && + uo._audioNewCtrlFrame == n_op._audioCtrlFrame) + { + // Simply replace the original new value and new frame. + uo._audioNewCtrlVal = n_op._audioNewCtrlVal; + uo._audioNewCtrlFrame = n_op._audioNewCtrlFrame; + return; + } + break; + + case UndoOp::ModifyAudioCtrlValList: + // Check the sanity of the requested op. + if(n_op._eraseCtrlList == n_op._addCtrlList) + { + fprintf(stderr, "MusE error: Undo::insert(): ModifyAudioCtrlValList: Erase and add lists are the same. Ignoring.\n"); + return; + } + + if(uo.type == UndoOp::ModifyAudioCtrlValList) + { + if(uo._ctrlListList == n_op._ctrlListList) { - undoList->pop_back(); - MusEGlobal::undoAction->setEnabled(!undoList->empty()); - setUndoRedoText(); + if(uo._addCtrlList == n_op._addCtrlList && uo._eraseCtrlList == n_op._eraseCtrlList) + { + fprintf(stderr, "MusE error: Undo::insert(): Double ModifyAudioCtrlValList. Ignoring.\n"); + return; + } + else if(uo._addCtrlList == n_op._eraseCtrlList) + { + // Delete the existing ModifyAudioCtrlValList command's _addCtrlList and replace it + // with the requested ModifyAudioCtrlValList command's _addCtrlList. + if(uo._addCtrlList) + delete uo._addCtrlList; + uo._addCtrlList = n_op._addCtrlList; + return; + } } + // Seems possible... remove? But maybe dangerous to have two undo ops pointing to the same lists - they will be self-deleted. else { - redoList->clearDelete(); // redo must be invalidated when a new undo is started - MusEGlobal::redoAction->setEnabled(false); - setUndoRedoText(); + if(uo._addCtrlList == n_op._addCtrlList) + { + fprintf(stderr, "MusE error: Undo::insert(): ModifyAudioCtrlValList: Attempting to add same list to different containers. Ignoring.\n"); + return; + } + else if(uo._eraseCtrlList == n_op._eraseCtrlList) + { + fprintf(stderr, "MusE error: Undo::insert(): ModifyAudioCtrlValList: Attempting to erase same list from different containers. Ignoring.\n"); + return; + } + } + } + break; + + + case UndoOp::AddTempo: + if(uo.type == UndoOp::AddTempo && uo.a == n_op.a) + { + // Simply replace the value. + uo.b = n_op.b; + return; + } + else if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a) + { + // Delete followed by add. Transform the existing DeleteTempo operation into a ModifyTempo. + uo.type = UndoOp::ModifyTempo; + // a is already the tick, b is already the existing value from DeleteTempo, c is the new value. + uo.c = n_op.b; + return; + } + else if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a) + { + // Modify followed by add. Simply replace the value. + // a is already the tick, b is already the existing value from ModifyTempo, c is the new value. + uo.c = n_op.b; + return; + } + break; + + case UndoOp::DeleteTempo: + if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a) + { + fprintf(stderr, "MusE error: Undo::insert(): Double DeleteTempo. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::AddTempo && uo.a == n_op.a) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(iuo); + return; + } + else if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a) + { + // Modify followed by delete. Equivalent to delete. Transform existing ModifyTempo operation into a DeleteTempo. + uo.type = UndoOp::DeleteTempo; + // a is already the tick, b is already the existing old value from ModifyTempo. + return; + } + break; + + case UndoOp::ModifyTempo: + if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a) + { + // Simply replace c with the new value. + uo.c = n_op.c; + return; + } + else if(uo.type == UndoOp::AddTempo && uo.a == n_op.a) + { + // Add followed by modify. Simply replace the add value. + uo.b = n_op.c; + return; + } + else if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a) + { + // Delete followed by modify. Equivalent to modify. Transform existing DeleteTempo operation into a ModifyTempo. + uo.type = UndoOp::ModifyTempo; + // a is already the tick, b is already the existing value from DeleteTempo. c is the new value from ModifyTempo. + uo.c = n_op.c; + return; + } + break; + + case UndoOp::SetGlobalTempo: + if(uo.type == UndoOp::SetGlobalTempo) + { + // Simply replace a with the new value. + uo.a = n_op.a; + return; + } + break; + + + case UndoOp::AddSig: + if(uo.type == UndoOp::AddSig && uo.a == n_op.a) + { + // Simply replace the value. + uo.b = n_op.b; + uo.c = n_op.c; + return; + } + else if(uo.type == UndoOp::DeleteSig && uo.a == n_op.a) + { + // Delete followed by add. Transform the existing DeleteSig operation into a ModifySig. + uo.type = UndoOp::ModifySig; + // a is already the tick, b + c is already the existing value from DeleteSig, d + e is the new value. + uo.d = n_op.b; + uo.e = n_op.c; + return; + } + else if(uo.type == UndoOp::ModifySig && uo.a == n_op.a) + { + // Modify followed by add. Simply replace the value. + // a is already the tick, b + c is already the existing value from ModifySig, d + e is the new value. + uo.d = n_op.b; + uo.e = n_op.c; + return; + } + break; + + case UndoOp::DeleteSig: + if(uo.type == UndoOp::DeleteSig && uo.a == n_op.a) + { + fprintf(stderr, "MusE error: Undo::insert(): Double DeleteSig. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::AddSig && uo.a == n_op.a) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(iuo); + return; + } + else if(uo.type == UndoOp::ModifySig && uo.a == n_op.a) + { + // Modify followed by delete. Equivalent to delete. Transform existing ModifySig operation into a DeleteSig. + uo.type = UndoOp::DeleteSig; + // a is already the tick, b + c is already the existing old value from ModifySig. + return; + } + break; + + case UndoOp::ModifySig: + if(uo.type == UndoOp::ModifySig && uo.a == n_op.a) + { + // Simply replace d + e with the new value. + uo.d = n_op.d; + uo.e = n_op.e; + return; + } + else if(uo.type == UndoOp::AddSig && uo.a == n_op.a) + { + // Add followed by modify. Simply replace the add value. + uo.b = n_op.d; + uo.c = n_op.e; + return; + } + else if(uo.type == UndoOp::DeleteSig && uo.a == n_op.a) + { + // Delete followed by modify. Equivalent to modify. Transform existing DeleteSig operation into a ModifySig. + uo.type = UndoOp::ModifySig; + // a is already the tick, b + c is already the existing value from DeleteSig. d + e is the new value from ModifySig. + uo.d = n_op.d; + uo.e = n_op.e; + return; + } + break; + + + case UndoOp::AddKey: + if(uo.type == UndoOp::AddKey && uo.a == n_op.a) + { + // Simply replace the value. + uo.b = n_op.b; + return; + } + else if(uo.type == UndoOp::DeleteKey && uo.a == n_op.a) + { + // Delete followed by add. Transform the existing DeleteKey operation into a ModifyKey. + uo.type = UndoOp::ModifyKey; + // a is already the tick, b is already the existing value from DeleteKey, c is the new value. + uo.c = n_op.b; + return; + } + else if(uo.type == UndoOp::ModifyKey && uo.a == n_op.a) + { + // Modify followed by add. Simply replace the value. + // a is already the tick, b is already the existing value from ModifyKey, c is the new value. + uo.c = n_op.b; + return; + } + break; + + case UndoOp::DeleteKey: + if(uo.type == UndoOp::DeleteKey && uo.a == n_op.a) + { + fprintf(stderr, "MusE error: Undo::insert(): Double DeleteKey. Ignoring.\n"); + return; + } + else if(uo.type == UndoOp::AddKey && uo.a == n_op.a) + { + // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. + erase(iuo); + return; + } + else if(uo.type == UndoOp::ModifyKey && uo.a == n_op.a) + { + // Modify followed by delete. Equivalent to delete. Transform existing ModifyKey operation into a DeleteKey. + uo.type = UndoOp::DeleteKey; + // a is already the tick, b is already the existing old value from ModifyKey. + return; + } + break; + + case UndoOp::ModifyKey: + if(uo.type == UndoOp::ModifyKey && uo.a == n_op.a) + { + // Simply replace c with the new value. + uo.c = n_op.c; + return; + } + else if(uo.type == UndoOp::AddKey && uo.a == n_op.a) + { + // Add followed by modify. Simply replace the add value. + uo.b = n_op.c; + return; + } + else if(uo.type == UndoOp::DeleteKey && uo.a == n_op.a) + { + // Delete followed by modify. Equivalent to modify. Transform existing DeleteSig operation into a ModifySig. + uo.type = UndoOp::ModifyKey; + // a is already the tick, b is already the existing value from DeleteKey. c is the new value from ModifyKey. + uo.c = n_op.c; + return; + } + break; + + + case UndoOp::ModifySongLen: + if(uo.type == UndoOp::ModifySongLen) + { + // Simply replace a with the new value. + uo.a = n_op.a; + return; + } + break; + + case UndoOp::EnableAllAudioControllers: + if(uo.type == UndoOp::EnableAllAudioControllers) + { + fprintf(stderr, "MusE error: Undo::insert(): Double EnableAllAudioControllers. Ignoring.\n"); + return; + } + break; + + // NOTE Some other undo op types may need treatment as well ! + + default: + break; + } + } + } + + std::list::insert(position, n_op); +} + +bool Undo::merge_combo(const Undo& other) +{ + if (other.combobreaker) + return false; + + int has_other=0x01; + int has_select_event=0x02; + int has_select_part=0x04; + int has_modify_aud_ctrl_val=0x08; + + int has = 0; + for (ciUndoOp op=this->begin(); op!=this->end(); op++) + switch(op->type) + { + case UndoOp::DoNothing: break; + case UndoOp::SelectEvent: has |= has_select_event; break; + case UndoOp::SelectPart: has |= has_select_part; break; + case UndoOp::ModifyAudioCtrlVal: has |= has_modify_aud_ctrl_val; break; + default: has |= has_other; break; + } + + for (ciUndoOp op=other.begin(); op!=other.end(); op++) + switch(op->type) + { + case UndoOp::DoNothing: break; + case UndoOp::SelectEvent: has |= has_select_event; break; + case UndoOp::SelectPart: has |= has_select_part; break; + case UndoOp::ModifyAudioCtrlVal: has |= has_modify_aud_ctrl_val; break; + default: has |= has_other; break; + } + + bool mergeable = (has == has_select_event || has == has_select_part || has == has_modify_aud_ctrl_val); + + if (mergeable) + this->insert(this->end(), other.begin(), other.end()); + + return mergeable; +} + +bool Song::applyOperation(const UndoOp& op, bool doUndo) +{ + Undo operations; + operations.push_back(op); + return applyOperationGroup(operations, doUndo); +} + +bool Song::applyOperationGroup(Undo& group, bool doUndo) +{ + if (!group.empty()) + { + if (doUndo) + startUndo(); + + MusEGlobal::audio->msgExecuteOperationGroup(group); + + // append all elements from "group" to the end of undoList->back(). + if(!undoList->empty()) + { + Undo& curUndo = undoList->back(); + curUndo.insert(curUndo.end(), group.begin(), group.end()); + if (group.combobreaker) + curUndo.combobreaker=true; } + if (doUndo) + endUndo(0); + return doUndo; } else @@ -339,337 +1257,468 @@ //--------------------------------------------------------- -// doUndo2 +// revertOperationGroup2 // real time part //--------------------------------------------------------- -void Song::doUndo2() +void Song::revertOperationGroup2(Undo& /*operations*/) { - Undo& u = undoList->back(); - for (riUndoOp i = u.rbegin(); i != u.rend(); ++i) { - switch(i->type) { - case UndoOp::AddTrack: - removeTrack2(i->track); - updateFlags |= SC_TRACK_REMOVED; - break; - case UndoOp::DeleteTrack: - insertTrack2(i->track, i->trackno); - chainTrackParts(i->track, true); - - updateFlags |= SC_TRACK_INSERTED; - break; - - case UndoOp::SwapTrack: - { - updateFlags |= SC_TRACK_MODIFIED; - Track* track = _tracks[i->a]; - _tracks[i->a] = _tracks[i->b]; - _tracks[i->b] = track; - updateFlags |= SC_TRACK_MODIFIED; - } - break; - case UndoOp::AddPart: - { - Part* part = i->oPart; - removePart(part); - updateFlags |= SC_PART_REMOVED; - i->oPart->events()->incARef(-1); - unchainClone(i->oPart); - } - break; - case UndoOp::DeletePart: - addPart(i->oPart); - updateFlags |= SC_PART_INSERTED; - i->oPart->events()->incARef(1); - chainClone(i->oPart); - break; - case UndoOp::ModifyPart: - if(i->doCtrls) - removePortCtrlEvents(i->nPart, i->doClones); - changePart(i->nPart, i->oPart); - i->nPart->events()->incARef(-1); - i->oPart->events()->incARef(1); - replaceClone(i->nPart, i->oPart); - if(i->doCtrls) - addPortCtrlEvents(i->oPart, i->doClones); - updateFlags |= SC_PART_MODIFIED; - break; - case UndoOp::AddEvent: - if(i->doCtrls) - removePortCtrlEvents(i->nEvent, i->part, i->doClones); - deleteEvent(i->nEvent, i->part); - updateFlags |= SC_EVENT_REMOVED; - break; - case UndoOp::DeleteEvent: - addEvent(i->nEvent, i->part); - if(i->doCtrls) - addPortCtrlEvents(i->nEvent, i->part, i->doClones); - updateFlags |= SC_EVENT_INSERTED; - break; - case UndoOp::ModifyEvent: - if(i->doCtrls) - removePortCtrlEvents(i->oEvent, i->part, i->doClones); - changeEvent(i->oEvent, i->nEvent, i->part); - if(i->doCtrls) - addPortCtrlEvents(i->nEvent, i->part, i->doClones); - updateFlags |= SC_EVENT_MODIFIED; - break; - case UndoOp::AddTempo: - MusEGlobal::tempomap.delTempo(i->a); - updateFlags |= SC_TEMPO; - break; - case UndoOp::DeleteTempo: - MusEGlobal::tempomap.addTempo(i->a, i->b); - updateFlags |= SC_TEMPO; - break; - case UndoOp::AddSig: - AL::sigmap.del(i->a); - updateFlags |= SC_SIG; - break; - case UndoOp::DeleteSig: - AL::sigmap.add(i->a, AL::TimeSignature(i->b, i->c)); - updateFlags |= SC_SIG; - break; - case UndoOp::AddKey: - ///sigmap.del(i->a); - MusEGlobal::keymap.delKey(i->a); - updateFlags |= SC_KEY; - break; - case UndoOp::DeleteKey: - ///sigmap.add(i->a, i->b, i->c); - MusEGlobal::keymap.addKey(i->a, (key_enum)i->b); - updateFlags |= SC_KEY; - break; - case UndoOp::ModifySongLen: - _len=i->b; - updateFlags = -1; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. - break; - case UndoOp::ModifyClip: - case UndoOp::ModifyMarker: - case UndoOp::DoNothing: - break; - default: - break; - } - } + pendingOperations.executeRTStage(); + + // Special for tempo: Need to normalize the tempo list, and resync audio. + // To save time this is done here, not item by item. + if(updateFlags & SC_TEMPO) + { + MusEGlobal::tempomap.normalize(); + MusEGlobal::audio->reSyncAudio(); + } + // Special for sig: Need to normalize the signature list. + // To save time this is done here, not item by item. + if(updateFlags & SC_SIG) + AL::sigmap.normalize(); + + // Special for track inserted: If it's an aux track, need to add missing aux sends to all tracks, + // else if it's another audio track need to add aux sends to it. + // To save from complexity this is done here, after all the operations. + if(updateFlags & SC_TRACK_INSERTED) + { + int n = _auxs.size(); + for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i) + { + if((*i)->isMidiTrack()) + continue; + MusECore::AudioTrack* at = static_cast(*i); + if(at->hasAuxSend()) + at->addAuxSend(n); + } + } } //--------------------------------------------------------- -// Song::doRedo2 +// Song::executeOperationGroup2 //--------------------------------------------------------- -void Song::doRedo2() +void Song::executeOperationGroup2(Undo& /*operations*/) { - Undo& u = redoList->back(); - for (iUndoOp i = u.begin(); i != u.end(); ++i) { - switch(i->type) { - case UndoOp::AddTrack: - insertTrack2(i->track, i->trackno); - chainTrackParts(i->track, true); - - updateFlags |= SC_TRACK_INSERTED; - break; - case UndoOp::DeleteTrack: - removeTrack2(i->track); - updateFlags |= SC_TRACK_REMOVED; - break; - - - case UndoOp::SwapTrack: - { - Track* track = _tracks[i->a]; - _tracks[i->a] = _tracks[i->b]; - _tracks[i->b] = track; - updateFlags |= SC_TRACK_MODIFIED; - } - break; - case UndoOp::AddPart: - addPart(i->oPart); - updateFlags |= SC_PART_INSERTED; - i->oPart->events()->incARef(1); - chainClone(i->oPart); - break; - case UndoOp::DeletePart: - removePart(i->oPart); - updateFlags |= SC_PART_REMOVED; - i->oPart->events()->incARef(-1); - unchainClone(i->oPart); - break; - case UndoOp::ModifyPart: - if(i->doCtrls) - removePortCtrlEvents(i->oPart, i->doClones); - changePart(i->oPart, i->nPart); - i->nPart->events()->incARef(1); - i->oPart->events()->incARef(-1); - replaceClone(i->oPart, i->nPart); - if(i->doCtrls) - addPortCtrlEvents(i->nPart, i->doClones); - updateFlags |= SC_PART_MODIFIED; - break; - case UndoOp::AddEvent: - addEvent(i->nEvent, i->part); - if(i->doCtrls) - addPortCtrlEvents(i->nEvent, i->part, i->doClones); - updateFlags |= SC_EVENT_INSERTED; - break; - case UndoOp::DeleteEvent: - if(i->doCtrls) - removePortCtrlEvents(i->nEvent, i->part, i->doClones); - deleteEvent(i->nEvent, i->part); - updateFlags |= SC_EVENT_REMOVED; - break; - case UndoOp::ModifyEvent: - if(i->doCtrls) - removePortCtrlEvents(i->nEvent, i->part, i->doClones); - changeEvent(i->nEvent, i->oEvent, i->part); - if(i->doCtrls) - addPortCtrlEvents(i->oEvent, i->part, i->doClones); - updateFlags |= SC_EVENT_MODIFIED; - break; - case UndoOp::AddTempo: - MusEGlobal::tempomap.addTempo(i->a, i->b); - updateFlags |= SC_TEMPO; - break; - case UndoOp::DeleteTempo: - MusEGlobal::tempomap.delTempo(i->a); - updateFlags |= SC_TEMPO; - break; - case UndoOp::AddSig: - AL::sigmap.add(i->a, AL::TimeSignature(i->b, i->c)); - updateFlags |= SC_SIG; - break; - case UndoOp::DeleteSig: - AL::sigmap.del(i->a); - updateFlags |= SC_SIG; - break; - case UndoOp::AddKey: - MusEGlobal::keymap.addKey(i->a, (key_enum)i->b); - updateFlags |= SC_KEY; - break; - case UndoOp::DeleteKey: - MusEGlobal::keymap.delKey(i->a); - updateFlags |= SC_KEY; - break; - case UndoOp::ModifySongLen: - _len=i->a; - updateFlags = -1; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. - break; - case UndoOp::ModifyClip: - case UndoOp::ModifyMarker: - case UndoOp::DoNothing: - break; - default: - break; - } - } + pendingOperations.executeRTStage(); + + // Special for tempo if altered: Need to normalize the tempo list, and resync audio. + // To save time this is done here, not item by item. + if(updateFlags & SC_TEMPO) + { + MusEGlobal::tempomap.normalize(); + MusEGlobal::audio->reSyncAudio(); + } + // Special for sig: Need to normalize the signature list. + // To save time this is done here, not item by item. + if(updateFlags & SC_SIG) + AL::sigmap.normalize(); + + // Special for track inserted: If it's an aux track, need to add missing aux sends to all tracks, + // else if it's another audio track need to add aux sends to it. + // To save from complexity this is done here, after all the operations. + if(updateFlags & SC_TRACK_INSERTED) + { + int n = _auxs.size(); + for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i) + { + if((*i)->isMidiTrack()) + continue; + MusECore::AudioTrack* at = static_cast(*i); + if(at->hasAuxSend()) + at->addAuxSend(n); + } + } } UndoOp::UndoOp() { type=UndoOp::DoNothing; + _noUndo = true; } -UndoOp::UndoOp(UndoType type_) -{ - type = type_; -} - -UndoOp::UndoOp(UndoType type_, int a_, int b_, int c_) +UndoOp::UndoOp(UndoType type_, int a_, int b_, int c_, bool noUndo) { + assert(type_==AddKey || type_==DeleteKey || type_== ModifyKey || + type_==AddTempo || type_==DeleteTempo || type_==ModifyTempo || type_==SetGlobalTempo || + type_==AddSig || type_==DeleteSig || + type_==ModifySongLen || type_==MoveTrack); + type = type_; a = a_; b = b_; c = c_; + _noUndo = noUndo; + + switch(type) + { + case UndoOp::SetGlobalTempo: + // a is already the new tempo, b is the existing tempo. + b = MusEGlobal::tempomap.globalTempo(); + break; + + // For these operations, we must check if a value already exists and transform them into modify operations... + case UndoOp::AddTempo: + { + int t = a; + if(t > MAX_TICK) + t = MAX_TICK; + iTEvent ite = MusEGlobal::tempomap.upper_bound(t); + if((int)ite->second->tick == t) + { + // Transform the AddTempo operation into a ModifyTempo. + // a is already the tick, b is the existing value, c is the new value. + type = UndoOp::ModifyTempo; + c = b; + b = ite->second->tempo; + } + } + break; + + case UndoOp::AddSig: + { + //if(t > MAX_TICK) + // t = MAX_TICK; + + // Must rasterize the tick value HERE instead of in SigMap::addOperation(), + // so that the rasterized value is recorded in the undo item. + a = AL::sigmap.raster1(a, 0); + + AL::iSigEvent ise = AL::sigmap.upper_bound(a); + if((int)ise->second->tick == a) + { + // Transform the AddSig operation into a ModifySig. + // a is already the tick, b + c is the existing value, d + e is the new value. + type = UndoOp::ModifySig; + d = b; + e = c; + b = ise->second->sig.z; + c = ise->second->sig.n; + } + } + break; + + case UndoOp::AddKey: + { + int t = a; + if(t > MAX_TICK) + t = MAX_TICK; + iKeyEvent ike = MusEGlobal::keymap.upper_bound(t); + if((int)ike->second.tick == t) + { + // Transform the AddKey operation into a ModifyKey. + // a is already the tick, b is the existing value, c is the new value. + type = UndoOp::ModifyKey; + c = b; + b = ike->second.key; + } + } + break; + + default: + break; + } + } +UndoOp::UndoOp(UndoType type_, int tick, const AL::TimeSignature old_sig, const AL::TimeSignature new_sig, bool noUndo) +{ + assert(type_==ModifySig); + type = type_; + a = tick; + b = old_sig.z; + c = old_sig.n; + d = new_sig.z; + e = new_sig.n; + _noUndo = noUndo; +} -UndoOp::UndoOp(UndoType type_, int n, Track* track_) +UndoOp::UndoOp(UndoType type_, int n, const Track* track_, bool noUndo) { + assert(type_==AddTrack || type_==DeleteTrack); + assert(track_); + type = type_; trackno = n; track = track_; + _noUndo = noUndo; + } + +UndoOp::UndoOp(UndoType type_, const Track* track_, bool value, bool noUndo) + { + assert(type_ == SetTrackRecord || type_ == SetTrackMute || type_ == SetTrackSolo || + type_ == SetTrackRecMonitor || type_ == SetTrackOff); + assert(track_); + + type = type_; + track = track_; + a = value; + _noUndo = noUndo; } -UndoOp::UndoOp(UndoType type_, Part* part) +UndoOp::UndoOp(UndoType type_, const Part* part_, bool noUndo) { + assert(type_==AddPart || type_==DeletePart); + assert(part_); + type = type_; - oPart = part; + part = part_; + _noUndo = noUndo; } + +UndoOp::UndoOp(UndoType type_, const Part* part_, bool selected_, bool sel_old_, bool noUndo) +{ + assert(type_==SelectPart); + assert(part_); + + type=type_; + part = part_; + selected=selected_; + selected_old=sel_old_; + _noUndo = noUndo; +} + +UndoOp::UndoOp(UndoType type_, const Part* part_, int old_len_or_pos, int new_len_or_pos, Pos::TType new_time_type_, const Track* oTrack, const Track* nTrack, bool noUndo) +{ + assert(type_== ModifyPartLength || type_== MovePart); + assert(part_); + + type = type_; + part = part_; + _noUndo = noUndo; + if(type_== MovePart) + { + track = nTrack; + oldTrack = oTrack; + // Make sure both tracks exist. + if(!track && !oldTrack) + track = oldTrack = part->track(); + else if(!oldTrack) + oldTrack = track; + else if(!track) + track = oldTrack; + assert(oldTrack); + assert(track); + } + old_partlen_or_pos = old_len_or_pos; + new_partlen_or_pos = new_len_or_pos; + switch(part->type()) + { + case Pos::FRAMES: + switch(new_time_type_) + { + case Pos::FRAMES: + break; + + case Pos::TICKS: + if(type_== ModifyPartLength) + new_partlen_or_pos = MusEGlobal::tempomap.deltaTick2frame(part->tick(), part->tick() + new_partlen_or_pos); + else + new_partlen_or_pos = MusEGlobal::tempomap.tick2frame(new_partlen_or_pos); + break; + } + break; + + case Pos::TICKS: + switch(new_time_type_) + { + case Pos::FRAMES: + if(type_== ModifyPartLength) + new_partlen_or_pos = MusEGlobal::tempomap.deltaFrame2tick(part->frame(), part->frame() + new_partlen_or_pos); + else + new_partlen_or_pos = MusEGlobal::tempomap.frame2tick(new_partlen_or_pos); + break; + + case Pos::TICKS: + break; + } + break; + } +} -UndoOp::UndoOp(UndoType type_, Event& oev, Event& nev, Part* part_, bool doCtrls_, bool doClones_) +UndoOp::UndoOp(UndoType type_, const Event& nev, const Event& oev, const Part* part_, bool doCtrls_, bool doClones_, bool noUndo) { + assert(type_==ModifyEvent); + assert(part_); + type = type_; nEvent = nev; oEvent = oev; part = part_; doCtrls = doCtrls_; doClones = doClones_; + _noUndo = noUndo; } -UndoOp::UndoOp(UndoType type_, Event& nev, Part* part_, bool doCtrls_, bool doClones_) +UndoOp::UndoOp(UndoType type_, const Event& nev, const Part* part_, bool a_, bool b_, bool noUndo) { + assert(type_==DeleteEvent || type_==AddEvent || type_==SelectEvent); + assert(part_); + type = type_; nEvent = nev; part = part_; - doCtrls = doCtrls_; - doClones = doClones_; - } - -UndoOp::UndoOp(UndoType type_, Part* oPart_, Part* nPart_, bool doCtrls_, bool doClones_) + _noUndo = noUndo; + if(type_==SelectEvent) { - type = type_; - oPart = oPart_; - nPart = nPart_; - doCtrls = doCtrls_; - doClones = doClones_; + selected = a_; + selected_old = b_; } - -UndoOp::UndoOp(UndoType type_, int c, int ctrl_, int ov, int nv) + else { - type = type_; - channel = c; - ctrl = ctrl_; - oVal = ov; - nVal = nv; + doCtrls = a_; + doClones = b_; } - -UndoOp::UndoOp(UndoType type_, Marker* copyMarker_, Marker* realMarker_) + } + +UndoOp::UndoOp(UndoType type_, Marker* copyMarker_, Marker* realMarker_, bool noUndo) { + assert(type_==ModifyMarker); +// REMOVE Tim. global cut. Changed. copyMarker_ or realMarker_ can be null. +// assert(copyMarker_); +// assert(realMarker_); + assert(copyMarker_ || realMarker_); + type = type_; realMarker = realMarker_; copyMarker = copyMarker_; + _noUndo = noUndo; } -UndoOp::UndoOp(UndoType type_, const char* changedFile, const char* changeData, int startframe_, int endframe_) +UndoOp::UndoOp(UndoType type_, const Event& changedEvent, const QString& changeData, int startframe_, int endframe_, bool noUndo) { + assert(type_==ModifyClip); + type = type_; - filename = changedFile; - tmpwavfile = changeData; + _noUndo = noUndo; + //filename = new QString(changedFile); + nEvent = changedEvent; + tmpwavfile = new QString(changeData); startframe = startframe_; endframe = endframe_; } -UndoOp::UndoOp(UndoOp::UndoType type_, Track* track_, const char* old_name, const char* new_name) +UndoOp::UndoOp(UndoOp::UndoType type_, const Part* part_, const QString& old_name, const QString& new_name, bool noUndo) +{ + assert(type_==ModifyPartName); + assert(part_); +// assert(old_name); +// assert(new_name); + + type=type_; + part=part_; + _noUndo = noUndo; + _oldName = new QString(old_name); + _newName = new QString(new_name); + //strcpy(_oldName, old_name); + //strcpy(_newName, new_name); +} + +UndoOp::UndoOp(UndoOp::UndoType type_, const Track* track_, const QString& old_name, const QString& new_name, bool noUndo) +{ + assert(type_==ModifyTrackName); + assert(track_); +// assert(old_name); +// assert(new_name); + + type = type_; + track = track_; + _noUndo = noUndo; + _oldName = new QString(old_name); + _newName = new QString(new_name); +// strcpy(_oldName, old_name); +// strcpy(_newName, new_name); +} + +UndoOp::UndoOp(UndoOp::UndoType type_, const Track* track_, int oldChanOrCtrlID, int newChanOrCtrlFrame, bool noUndo) +{ + assert(type_ == ModifyTrackChannel || type_ == DeleteAudioCtrlVal); + assert(track_); + + type = type_; + track = track_; + + if(type_ == ModifyTrackChannel) + { + _propertyTrack = track_; + _oldPropValue = oldChanOrCtrlID; + _newPropValue = newChanOrCtrlFrame; + } + else + { + _audioCtrlID = oldChanOrCtrlID; + _audioCtrlFrame = newChanOrCtrlFrame; + } + _noUndo = noUndo; +} + +UndoOp::UndoOp(UndoType type_, const Track* track_, int ctrlID, int frame, double value, bool noUndo) +{ + assert(type_== AddAudioCtrlVal); + assert(track_); + + type = type_; + track = track_; + _audioCtrlID = ctrlID; + _audioCtrlFrame = frame; + _audioCtrlVal = value; + _noUndo = noUndo; +} + +UndoOp::UndoOp(UndoType type_, const Track* track_, int ctrlID, int oldFrame, int newFrame, double oldValue, double newValue, bool noUndo) +{ + assert(type_== ModifyAudioCtrlVal); + assert(track_); + + type = type_; + track = track_; + _audioCtrlID = ctrlID; + _audioCtrlFrame = oldFrame; + _audioNewCtrlFrame = newFrame; + _audioCtrlVal = oldValue; + _audioNewCtrlVal = newValue; + _noUndo = noUndo; +} + +UndoOp::UndoOp(UndoOp::UndoType type_, CtrlListList* ctrl_ll, CtrlList* eraseCtrlList, CtrlList* addCtrlList, bool noUndo) { + assert(type_== ModifyAudioCtrlValList); + assert(ctrl_ll); + //assert(eraseCtrlList); + //assert(addCtrlList); + assert(eraseCtrlList || addCtrlList); + type = type_; - _renamedTrack = track_; - _oldName = new char[strlen(old_name) + 1]; - _newName = new char[strlen(new_name) + 1]; - strcpy(_oldName, old_name); - strcpy(_newName, new_name); + _ctrlListList = ctrl_ll; + _eraseCtrlList = eraseCtrlList; + _addCtrlList = addCtrlList; + _noUndo = noUndo; } -UndoOp::UndoOp(UndoOp::UndoType type_, Track* track_, int old_chan, int new_chan) + +UndoOp::UndoOp(UndoOp::UndoType type_) { + assert(type_== EnableAllAudioControllers); + type = type_; - _propertyTrack = track_; - _oldPropValue = old_chan; - _newPropValue = new_chan; + // Cannot be undone. 'One-time' operation only, removed after execution. + _noUndo = true; } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +UndoOp::UndoOp(UndoOp::UndoType type_, const Route& route_from_, const Route& route_to_, bool noUndo) + { + assert(type_ == AddRoute || type_ == DeleteRoute); + _noUndo = noUndo; + routeFrom = route_from_; + routeTo = route_to_; + } +#pragma GCC diagnostic pop -void Song::undoOp(UndoOp::UndoType type, const char* changedFile, const char* changeData, int startframe, int endframe) +void Song::undoOp(UndoOp::UndoType type, const Event& changedEvent, const QString& changeData, int startframe, int endframe) { - addUndo(UndoOp(type,changedFile,changeData,startframe,endframe)); + addUndo(UndoOp(type,changedEvent,changeData,startframe,endframe)); temporaryWavFiles.push_back(QString(changeData)); } @@ -684,83 +1733,216 @@ return; } undoList->back().push_back(i); - dirty = true; + emit sigDirty(); } //--------------------------------------------------------- -// doUndo1 +// revertOperationGroup1 // non realtime context // return true if nothing to do //--------------------------------------------------------- -bool Song::doUndo1() +void Song::revertOperationGroup1(Undo& operations) { - if (undoList->empty()) - return true; - Undo& u = undoList->back(); - for (riUndoOp i = u.rbegin(); i != u.rend(); ++i) { + for (riUndoOp i = operations.rbegin(); i != operations.rend(); ++i) { + Track* editable_track = const_cast(i->track); + Track* editable_property_track = const_cast(i->_propertyTrack); + Part* editable_part = const_cast(i->part); switch(i->type) { + case UndoOp::SelectPart: + editable_part->setSelected(i->selected_old); + updateFlags |= SC_PART_SELECTION; + break; + case UndoOp::SelectEvent: + selectEvent(i->nEvent, editable_part, i->selected_old); + updateFlags |= SC_SELECTION; + break; + case UndoOp::AddTrack: - removeTrack1(i->track); + switch(editable_track->type()) + { + case Track::AUDIO_SOFTSYNTH: + { + SynthI* si = (SynthI*)editable_track; + if(si->hasGui()) + si->showGui(false); + if(si->hasNativeGui()) + si->showNativeGui(false); + }// Fall through. + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + ((AudioTrack*)editable_track)->deleteAllEfxGuis(); + updateFlags |= SC_RACK; + break; + + default: + break; + } + + switch(editable_track->type()) + { + case Track::AUDIO_OUTPUT: + { + AudioOutput* ao = (AudioOutput*)editable_track; + for(int ch = 0; ch < ao->channels(); ++ch) + { + MusEGlobal::audioDevice->unregisterPort(ao->jackPort(ch)); + //ao->setJackPort(ch, 0); // Done in RT stage. + updateFlags |= SC_ROUTE; + } + } + break; + + case Track::AUDIO_INPUT: + { + AudioOutput* ai = (AudioOutput*)editable_track; + for(int ch = 0; ch < ai->channels(); ++ch) + { + MusEGlobal::audioDevice->unregisterPort(ai->jackPort(ch)); + //ai->setJackPort(ch, 0); // Done in RT stage. + updateFlags |= SC_ROUTE; + } + } + break; + + case Track::AUDIO_AUX: + updateFlags |= SC_AUX; + break; + + default: + break; + } + removeTrackOperation(editable_track, pendingOperations); + updateFlags |= SC_TRACK_REMOVED; break; + case UndoOp::DeleteTrack: - insertTrack1(i->track, i->trackno); - - // FIXME: Would like to put this part in Undo2, but indications - // elsewhere are that (dis)connecting jack routes must not be - // done in the realtime thread. The result is that we get a few - // "PANIC Process init: No buffer from audio device" messages - // before the routes are (dis)connected. So far seems to do no harm though... - switch(i->track->type()) - { - case Track::AUDIO_OUTPUT: - case Track::AUDIO_INPUT: - connectJackRoutes((AudioTrack*)i->track, false); - break; - //case Track::AUDIO_SOFTSYNTH: DELETETHIS 4 - //SynthI* si = (SynthI*)i->track; - //si->synth()->init( - // break; - default: - break; + switch(editable_track->type()) + { + case Track::AUDIO_SOFTSYNTH: + { + SynthI* s = (SynthI*)editable_track; + Synth* sy = s->synth(); + if(!s->isActivated()) + s->initInstance(sy, s->name()); + } + break; + + case Track::AUDIO_OUTPUT: + { + AudioOutput* ao = (AudioOutput*)editable_track; + if(MusEGlobal::checkAudioDevice()) + { + for(int ch = 0; ch < ao->channels(); ++ch) + { + // Register the track's jack port, or just set the name. + char buffer[128]; + snprintf(buffer, 128, "%s-%d", ao->name().toLatin1().constData(), ch); + // REMOVE Tim. Persistent routes. Added. Don't think we need this here. + if(ao->jackPort(ch)) + { + MusEGlobal::audioDevice->setPortName(ao->jackPort(ch), buffer); + updateFlags |= SC_ROUTE; + } + else + { + // This should be OK since the track has not yet been added in the realtime stage. + ao->setJackPort(ch, MusEGlobal::audioDevice->registerOutPort(buffer, false)); + updateFlags |= SC_ROUTE; + } + + // Set the route Jack ports now to relieve our graph callback handler from having to do it. + RouteList* rl = ao->outRoutes(); + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + if(ir->type == Route::JACK_ROUTE && ir->channel == ch) + { + ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName); + updateFlags |= SC_ROUTE; + } + } + } + } + break; + + case Track::AUDIO_INPUT: + { + AudioInput* ai = (AudioInput*)editable_track; + if(MusEGlobal::checkAudioDevice()) + { + for(int ch = 0; ch < ai->channels(); ++ch) + { + // Register the track's jack port, or just set the name. + char buffer[128]; + snprintf(buffer, 128, "%s-%d", ai->name().toLatin1().constData(), ch); + // REMOVE Tim. Persistent routes. Added. Don't think we need this here. + if(ai->jackPort(ch)) + { + MusEGlobal::audioDevice->setPortName(ai->jackPort(ch), buffer); + updateFlags |= SC_ROUTE; + } + else + { + // This should be OK since the track has not yet been added in the realtime stage. + ai->setJackPort(ch, MusEGlobal::audioDevice->registerInPort(buffer, false)); + updateFlags |= SC_ROUTE; + } + + // Set the route Jack ports now to relieve our graph callback handler from having to do it. + RouteList* rl = ai->inRoutes(); + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + if(ir->type == Route::JACK_ROUTE && ir->channel == ch) + { + ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName); + updateFlags |= SC_ROUTE; + } + } + } + } + break; + + case Track::AUDIO_AUX: + updateFlags |= SC_AUX; + break; + + default: + break; } - - break; - case UndoOp::ModifyTrackName: - i->_renamedTrack->setName(i->_oldName); - updateFlags |= SC_TRACK_MODIFIED; + + // Ensure that wave event sndfile file handles are opened. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + editable_track->openAllParts(); + + insertTrackOperation(editable_track, i->trackno, pendingOperations); + updateFlags |= SC_TRACK_INSERTED; break; + case UndoOp::ModifyClip: - MusECore::SndFile::applyUndoFile(i->filename, i->tmpwavfile, i->startframe, i->endframe); + MusECore::SndFile::applyUndoFile(i->nEvent, i->tmpwavfile, i->startframe, i->endframe); + updateFlags |= SC_CLIP_MODIFIED; break; case UndoOp::ModifyTrackChannel: - if (i->_propertyTrack->isMidiTrack()) + if (editable_property_track->isMidiTrack()) { - MusECore::MidiTrack* mt = dynamic_cast(i->_propertyTrack); - if (mt == 0 || mt->type() == MusECore::Track::DRUM) - break; + MusECore::MidiTrack* mt = static_cast(editable_property_track); if (i->_oldPropValue != mt->outChannel()) { + MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; MusEGlobal::audio->msgIdle(true); - mt->setOutChanAndUpdate(i->_oldPropValue); + changed |= mt->setOutChanAndUpdate(i->_oldPropValue, false); MusEGlobal::audio->msgIdle(false); - // DELETETHIS 6 - //if (mt->type() == MusECore::MidiTrack::DRUM) {//Change channel on all drum instruments - // for (int i=0; i_oldPropValue; - // } - //updateFlags |= SC_CHANNELS; MusEGlobal::audio->msgUpdateSoloStates(); - updateFlags |= SC_MIDI_TRACK_PROP; + updateFlags |= (SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); } } else { - if(i->_propertyTrack->type() != MusECore::Track::AUDIO_SOFTSYNTH) + if(editable_property_track->type() != MusECore::Track::AUDIO_SOFTSYNTH) { - MusECore::AudioTrack* at = dynamic_cast(i->_propertyTrack); - if (at == 0) - break; + MusECore::AudioTrack* at = static_cast(editable_property_track); if (i->_oldPropValue != at->channels()) { MusEGlobal::audio->msgSetChannels(at, i->_oldPropValue); updateFlags |= SC_CHANNELS; @@ -769,35 +1951,454 @@ } break; - default: + case UndoOp::SetTrackRecord: + if(!editable_track->setRecordFlag1(!i->a)) + break; + pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackRecord)); + // FIXME: No choice but to include monitor flag. Really should try to merge pending ops flags + // with undo flags after executing the pending ops in revertOperationGroup3... + updateFlags |= (SC_RECFLAG | SC_TRACK_REC_MONITOR); break; - } - } - return false; - } -//--------------------------------------------------------- -// doUndo3 -// non realtime context -//--------------------------------------------------------- + case UndoOp::SetTrackMute: + pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackMute)); + updateFlags |= SC_MUTE; + break; -void Song::doUndo3() - { - Undo& u = undoList->back(); - for (riUndoOp i = u.rbegin(); i != u.rend(); ++i) { - switch(i->type) { - case UndoOp::AddTrack: - removeTrack3(i->track); + case UndoOp::SetTrackSolo: + pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackSolo)); + updateFlags |= SC_SOLO; break; - case UndoOp::DeleteTrack: - insertTrack3(i->track, i->trackno); + + case UndoOp::SetTrackRecMonitor: + pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackRecMonitor)); + updateFlags |= SC_TRACK_REC_MONITOR; break; - case UndoOp::ModifyMarker: - { - if (i->realMarker) { - Marker tmpMarker = *i->realMarker; - *i->realMarker = *i->copyMarker; // swap them - *i->copyMarker = tmpMarker; + + case UndoOp::SetTrackOff: + pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackOff)); + updateFlags |= SC_MUTE; + break; + + + case UndoOp::AddRoute: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:AddRoute\n"); +#endif + pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::DeleteRoute)); + updateFlags |= SC_ROUTE; + break; + + case UndoOp::DeleteRoute: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:DeleteRoute\n"); +#endif + pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::AddRoute)); + updateFlags |= SC_ROUTE; + break; + + case UndoOp::ModifyTrackName: + pendingOperations.add(PendingOperationItem(editable_track, i->_oldName, PendingOperationItem::ModifyTrackName)); + updateFlags |= (SC_TRACK_MODIFIED | SC_MIDI_TRACK_PROP); + // If it's an aux track, notify aux UI controls to reload, or change their names etc. + if(editable_track->type() == Track::AUDIO_AUX) + updateFlags |= SC_AUX; + break; + + case UndoOp::MoveTrack: + pendingOperations.add(PendingOperationItem(&_tracks, i->b, i->a, PendingOperationItem::MoveTrack)); + updateFlags |= SC_TRACK_MOVED; + break; + + case UndoOp::ModifyPartName: + pendingOperations.add(PendingOperationItem(editable_part, i->_oldName, PendingOperationItem::ModifyPartName)); + updateFlags |= SC_PART_MODIFIED; + break; + + case UndoOp::ModifyPartLength: + removePortCtrlEvents(editable_part, editable_part->track(), pendingOperations); + pendingOperations.add(PendingOperationItem(editable_part, i->old_partlen_or_pos, PendingOperationItem::ModifyPartLength)); + addPortCtrlEvents(editable_part, editable_part->tick(), i->old_partlen_or_pos, editable_part->track(), pendingOperations); + updateFlags |= SC_PART_MODIFIED; + break; + + case UndoOp::MovePart: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:MovePart ** calling parts->movePartOperation\n"); +#endif + editable_part->track()->parts()->movePartOperation(editable_part, i->old_partlen_or_pos, pendingOperations, const_cast(i->oldTrack)); + if(const_cast(i->oldTrack)) + updateFlags |= SC_PART_INSERTED | SC_PART_REMOVED; + updateFlags |= SC_PART_MODIFIED; + break; + + case UndoOp::AddPart: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:AddPart ** calling parts->delOperation\n"); +#endif + editable_part->track()->parts()->delOperation(editable_part, pendingOperations); + updateFlags |= SC_PART_REMOVED; + break; + + case UndoOp::DeletePart: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:DeletePart ** calling parts->addOperation\n"); +#endif + // Ensure that wave event sndfile file handles are opened. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + editable_part->openAllEvents(); + + editable_part->track()->parts()->addOperation(editable_part, pendingOperations); + updateFlags |= SC_PART_INSERTED; + break; + + + case UndoOp::AddEvent: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:AddEvent ** calling deleteEvent\n"); +#endif + deleteEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones); + updateFlags |= SC_EVENT_REMOVED; + break; + + case UndoOp::DeleteEvent: + { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:DeleteEvent ** calling addEvent\n"); +#endif + if(!i->nEvent.empty()) + { + SndFileR f = i->nEvent.sndFile(); + // Ensure that wave event sndfile file handle is opened. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + if(!f.isNull() && !f.isOpen()) + f->openRead(); + } + + addEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones); + updateFlags |= SC_EVENT_INSERTED; + } + break; + + case UndoOp::ModifyEvent: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:ModifyEvent ** calling changeEvent\n"); +#endif + changeEventOperation(i->nEvent, i->oEvent, editable_part, i->doCtrls, i->doClones); + updateFlags |= SC_EVENT_MODIFIED; + break; + + + case UndoOp::AddAudioCtrlVal: + { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:AddAudioCtrlVal\n"); +#endif + CtrlListList* cll = static_cast(editable_track)->controller(); + iCtrlList icl = cll->find(i->_audioCtrlID); + if(icl != cll->end()) + { + CtrlList* cl = icl->second; + iCtrl ic = cl->find(i->_audioCtrlFrame); + if(ic != cl->end()) + { + pendingOperations.add(PendingOperationItem(cl, ic, PendingOperationItem::DeleteAudioCtrlVal)); + updateFlags |= SC_AUDIO_CONTROLLER; + } + } + } + break; + + case UndoOp::DeleteAudioCtrlVal: + { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:DeleteAudioCtrlVal\n"); +#endif + CtrlListList* cll = static_cast(editable_track)->controller(); + iCtrlList icl = cll->find(i->_audioCtrlID); + if(icl != cll->end()) + { + //CtrlList* cl = icl->second; + //iCtrl ic = cl->find(i->_audioCtrlFrame); + //if(ic != cl->end()) + // // An existing value was found (really shouldn't happen!). Replace it with the old value. + // pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioCtrlVal, PendingOperationItem::ModifyAudioCtrlVal)); + //else + // Restore the old value. + pendingOperations.add(PendingOperationItem(icl->second, i->_audioCtrlFrame, i->_audioCtrlVal, PendingOperationItem::AddAudioCtrlVal)); + updateFlags |= SC_AUDIO_CONTROLLER; + } + } + break; + + case UndoOp::ModifyAudioCtrlVal: + { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:ModifyAudioCtrlVal\n"); +#endif + CtrlListList* cll = static_cast(editable_track)->controller(); + iCtrlList icl = cll->find(i->_audioCtrlID); + if(icl != cll->end()) + { + CtrlList* cl = icl->second; + iCtrl ic = cl->find(i->_audioNewCtrlFrame); + if(ic != cl->end()) + { + // Restore the old value. + pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioCtrlFrame, i->_audioCtrlVal, PendingOperationItem::ModifyAudioCtrlVal)); + updateFlags |= SC_AUDIO_CONTROLLER; + } + } + } + break; + + case UndoOp::ModifyAudioCtrlValList: + { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:ModifyAudioCtrlValList\n"); +#endif + // Take either id. At least one list must be valid. + const int id = i->_eraseCtrlList ? i->_eraseCtrlList->id() : i->_addCtrlList->id(); + iCtrlList icl = i->_ctrlListList->find(id); + if(icl != i->_ctrlListList->end()) + { + // Make a complete copy of the controller list. The list will be quickly switched in the realtime stage. + // The Pending Operations system will take 'ownership' of this and delete it at the appropriate time. + CtrlList* new_list = new CtrlList(*icl->second, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); + + // Erase any items in the add list that were added... + //if(i->_addCtrlList) + if(i->_addCtrlList && !i->_addCtrlList->empty()) + { + //const std::size_t sz = i->_addCtrlList->size(); + //if(sz != 0) + //{ + //const CtrlList& cl_r = *i->_addCtrlList; + // Both of these should be valid. + //ciCtrl n_s = new_list->find(cl_r[0].frame); // The first item to be erased. + //ciCtrl n_e = new_list->find(cl_r[sz - 1].frame); // The last item to be erased. + iCtrl n_s = new_list->find(i->_addCtrlList->begin()->second.frame); // The first item to be erased. + ciCtrl e_e = i->_addCtrlList->end(); + --e_e; + //ciCtrl n_e = new_list->find((--i->_eraseCtrlList->end())->second.frame); // The last item to be erased. + iCtrl n_e = new_list->find(e_e->second.frame); // The last item to be erased. + if(n_s != new_list->end() && n_e != new_list->end()) + { + // Since std range does NOT include the last iterator, increment n_e so erase will get all items. + ++n_e; + new_list->erase(n_s, n_e); + } + //} + } + + // Re-add any items in the erase list that were erased... + if(i->_eraseCtrlList && !i->_eraseCtrlList->empty()) + new_list->insert(i->_eraseCtrlList->begin(), i->_eraseCtrlList->end()); + + // The operation will quickly switch the list in the RT stage then the delete the old list in the non-RT stage. + pendingOperations.add(PendingOperationItem(icl, new_list, PendingOperationItem::ModifyAudioCtrlValList)); + updateFlags |= SC_AUDIO_CONTROLLER_LIST; + } + } + break; + + + case UndoOp::DeleteTempo: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:DeleteTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b); +#endif + MusEGlobal::tempomap.addOperation(i->a, i->b, pendingOperations); + updateFlags |= SC_TEMPO; + break; + + case UndoOp::AddTempo: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:AddTempo ** calling tempomap.delOperation tick:%d\n", i->a); +#endif + MusEGlobal::tempomap.delOperation(i->a, pendingOperations); + updateFlags |= SC_TEMPO; + break; + + case UndoOp::ModifyTempo: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:ModifyTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b); +#endif + MusEGlobal::tempomap.addOperation(i->a, i->b, pendingOperations); + updateFlags |= SC_TEMPO; + break; + + case UndoOp::SetGlobalTempo: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:SetGlobalTempo ** adding SetGlobalTempo operation\n"); +#endif + pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->b, PendingOperationItem::SetGlobalTempo)); + updateFlags |= SC_TEMPO; + break; + + case UndoOp::DeleteSig: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:DeleteSig ** calling sigmap.addOperation\n"); +#endif + AL::sigmap.addOperation(i->a, AL::TimeSignature(i->b, i->c), pendingOperations); + updateFlags |= SC_SIG; + break; + + case UndoOp::AddSig: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:AddSig ** calling sigmap.delOperation\n"); +#endif + AL::sigmap.delOperation(i->a, pendingOperations); + updateFlags |= SC_SIG; + break; + + case UndoOp::ModifySig: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:ModifySig ** calling sigmap.addOperation\n"); +#endif + AL::sigmap.addOperation(i->a, AL::TimeSignature(i->b, i->c), pendingOperations); + updateFlags |= SC_SIG; + break; + + + case UndoOp::DeleteKey: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:DeleteKey ** calling keymap.addOperation\n"); +#endif + MusEGlobal::keymap.addOperation(i->a, key_enum(i->b), pendingOperations); + updateFlags |= SC_KEY; + break; + + case UndoOp::AddKey: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:AddKey ** calling keymap.delOperation\n"); +#endif + MusEGlobal::keymap.delOperation(i->a, pendingOperations); + updateFlags |= SC_KEY; + break; + + case UndoOp::ModifyKey: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:ModifyKey ** calling keymap.addOperation\n"); +#endif + MusEGlobal::keymap.addOperation(i->a, key_enum(i->b), pendingOperations); + updateFlags |= SC_KEY; + break; + + case UndoOp::ModifySongLen: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup1:ModifySongLen ** adding ModifySongLen operation\n"); +#endif + pendingOperations.add(PendingOperationItem(i->b, PendingOperationItem::ModifySongLength)); + updateFlags |= -1; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. + //updateFlags |= SC_SONG_LEN; + break; + + default: + break; + } + } + return; + } + +//--------------------------------------------------------- +// revertOperationGroup3 +// non realtime context +//--------------------------------------------------------- + +void Song::revertOperationGroup3(Undo& operations) + { + pendingOperations.executeNonRTStage(); +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::revertOperationGroup3 *** Calling pendingOperations.clear()\n"); +#endif + pendingOperations.clear(); + for (riUndoOp i = operations.rbegin(); i != operations.rend(); ++i) { + Track* editable_track = const_cast(i->track); +// uncomment if needed Track* editable_property_track = const_cast(i->_propertyTrack); + Part* editable_part = const_cast(i->part); // uncomment if needed + switch(i->type) { + case UndoOp::AddTrack: + // Ensure that wave event sndfile file handles are closed. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + editable_track->closeAllParts(); + break; + case UndoOp::DeleteTrack: + switch(editable_track->type()) + { + case Track::AUDIO_OUTPUT: + // Connect audio output ports to Jack ports... + if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) + { + AudioOutput* ao = (AudioOutput*)editable_track; + for(int ch = 0; ch < ao->channels(); ++ch) + { + void* our_port = ao->jackPort(ch); + if(!our_port) + continue; + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); + if(!our_port_name) + continue; + RouteList* rl = ao->outRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE || ir->channel != ch) + continue; + const char* route_name = ir->persistentJackPortName; + //if(ir->jackPort) + if(!MusEGlobal::audioDevice->findPort(route_name)) + continue; + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + MusEGlobal::audioDevice->connect(our_port_name, route_name); + updateFlags |= SC_ROUTE; + } + } + } + break; + + case Track::AUDIO_INPUT: + // Connect Jack ports to audio input ports... + if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) + { + AudioInput* ai = (AudioInput*)editable_track; + for(int ch = 0; ch < ai->channels(); ++ch) + { + void* our_port = ai->jackPort(ch); + if(!our_port) + continue; + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); + if(!our_port_name) + continue; + RouteList* rl = ai->inRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE || ir->channel != ch) + continue; + const char* route_name = ir->persistentJackPortName; + //if(ir->jackPort) + if(!MusEGlobal::audioDevice->findPort(route_name)) + continue; + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + MusEGlobal::audioDevice->connect(route_name, our_port_name); + updateFlags |= SC_ROUTE; + } + } + } + break; + + default: + break; + } + + break; + case UndoOp::ModifyMarker: + { + if (i->realMarker) { + Marker tmpMarker = *i->realMarker; + *i->realMarker = *i->copyMarker; // swap them + *i->copyMarker = tmpMarker; } else { i->realMarker = _markerList->add(*i->copyMarker); @@ -806,85 +2407,244 @@ } } break; + case UndoOp::AddPart: + // Ensure that wave event sndfile file handles are closed. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + editable_part->closeAllEvents(); + break; + case UndoOp::AddEvent: { + if(!i->nEvent.empty()) + { + SndFileR f = i->nEvent.sndFile(); + // Ensure that wave event sndfile file handle is closed. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + if(!f.isNull() && f.isOpen()) + f->close(); + } + } + break; default: break; } } - redoList->push_back(u); // put item on redo list - undoList->pop_back(); - dirty = true; + + if(!operations.empty()) + emit sigDirty(); } //--------------------------------------------------------- -// doRedo1 +// executeOperationGroup1 // non realtime context // return true if nothing to do //--------------------------------------------------------- -bool Song::doRedo1() +void Song::executeOperationGroup1(Undo& operations) { - if (redoList->empty()) - return true; - Undo& u = redoList->back(); - for (iUndoOp i = u.begin(); i != u.end(); ++i) { + unsigned song_len = MusEGlobal::song->len(); + + for (iUndoOp i = operations.begin(); i != operations.end(); ++i) { + Track* editable_track = const_cast(i->track); + Track* editable_property_track = const_cast(i->_propertyTrack); + Part* editable_part = const_cast(i->part); switch(i->type) { + case UndoOp::SelectPart: + editable_part->setSelected(i->selected); + updateFlags |= SC_PART_SELECTION; + break; + case UndoOp::SelectEvent: + selectEvent(i->nEvent, editable_part, i->selected); + updateFlags |= SC_SELECTION; + break; + case UndoOp::AddTrack: - insertTrack1(i->track, i->trackno); - - // FIXME: See comments in Undo1. - switch(i->track->type()) + switch(editable_track->type()) { - case Track::AUDIO_OUTPUT: - case Track::AUDIO_INPUT: - connectJackRoutes((AudioTrack*)i->track, false); - break; - //case Track::AUDIO_SOFTSYNTH: DELETETHIS 4 - //SynthI* si = (SynthI*)i->track; - //si->synth()->init( - // break; - default: - break; + case Track::AUDIO_SOFTSYNTH: + { + SynthI* s = (SynthI*)editable_track; + Synth* sy = s->synth(); + if(!s->isActivated()) + s->initInstance(sy, s->name()); + } + break; + + case Track::AUDIO_OUTPUT: + { + AudioOutput* ao = (AudioOutput*)editable_track; + if(MusEGlobal::checkAudioDevice()) + { + for(int ch = 0; ch < ao->channels(); ++ch) + { + // Register the track's jack port, or just set the name. + char buffer[128]; + snprintf(buffer, 128, "%s-%d", ao->name().toLatin1().constData(), ch); + // REMOVE Tim. Persistent routes. Added. Don't think we need this here. + if(ao->jackPort(ch)) + { + MusEGlobal::audioDevice->setPortName(ao->jackPort(ch), buffer); + updateFlags |= SC_ROUTE; + } + else + { + // This should be OK since the track has not yet been added in the realtime stage. + ao->setJackPort(ch, MusEGlobal::audioDevice->registerOutPort(buffer, false)); + updateFlags |= SC_ROUTE; + } + + // Set the route Jack ports now to relieve our graph callback handler from having to do it. + RouteList* rl = ao->outRoutes(); + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + if(ir->type == Route::JACK_ROUTE && ir->channel == ch) + { + ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName); + updateFlags |= SC_ROUTE; + } + } + } + + + } + break; + + case Track::AUDIO_INPUT: + { + AudioInput* ai = (AudioInput*)editable_track; + if(MusEGlobal::checkAudioDevice()) + { + for(int ch = 0; ch < ai->channels(); ++ch) + { + // Register the track's jack port, or just set the name. + char buffer[128]; + snprintf(buffer, 128, "%s-%d", ai->name().toLatin1().constData(), ch); + // REMOVE Tim. Persistent routes. Added. Don't think we need this here. + if(ai->jackPort(ch)) + { + MusEGlobal::audioDevice->setPortName(ai->jackPort(ch), buffer); + updateFlags |= SC_ROUTE; + } + else + { + // This should be OK since the track has not yet been added in the realtime stage. + ai->setJackPort(ch, MusEGlobal::audioDevice->registerInPort(buffer, false)); + updateFlags |= SC_ROUTE; + } + + // Set the route Jack ports now to relieve our graph callback handler from having to do it. + RouteList* rl = ai->inRoutes(); + for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + if(ir->type == Route::JACK_ROUTE && ir->channel == ch) + { + ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName); + updateFlags |= SC_ROUTE; + } + } + } + } + break; + + case Track::AUDIO_AUX: + updateFlags |= SC_AUX; + break; + + default: + break; } + + // Ensure that wave event sndfile file handles are opened. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + editable_track->openAllParts(); + insertTrackOperation(editable_track, i->trackno, pendingOperations); + updateFlags |= SC_TRACK_INSERTED; break; + case UndoOp::DeleteTrack: - removeTrack1(i->track); - break; - case UndoOp::ModifyTrackName: - i->_renamedTrack->setName(i->_newName); - updateFlags |= SC_TRACK_MODIFIED; + switch(editable_track->type()) + { + case Track::AUDIO_SOFTSYNTH: + { + SynthI* si = (SynthI*)editable_track; + if(si->hasGui()) + si->showGui(false); + if(si->hasNativeGui()) + si->showNativeGui(false); + }// Fall through. + case Track::WAVE: + case Track::AUDIO_OUTPUT: + case Track::AUDIO_INPUT: + case Track::AUDIO_GROUP: + case Track::AUDIO_AUX: + ((AudioTrack*)editable_track)->deleteAllEfxGuis(); + updateFlags |= SC_RACK; + break; + + default: + break; + } + + switch(editable_track->type()) + { + case Track::AUDIO_OUTPUT: + { + AudioOutput* ao = (AudioOutput*)editable_track; + for(int ch = 0; ch < ao->channels(); ++ch) + { + MusEGlobal::audioDevice->unregisterPort(ao->jackPort(ch)); + //ao->setJackPort(ch, 0); // Done in RT stage. + updateFlags |= SC_ROUTE; + } + } + break; + + case Track::AUDIO_INPUT: + { + AudioOutput* ai = (AudioOutput*)editable_track; + for(int ch = 0; ch < ai->channels(); ++ch) + { + MusEGlobal::audioDevice->unregisterPort(ai->jackPort(ch)); + //ai->setJackPort(ch, 0); // Done in RT stage. + updateFlags |= SC_ROUTE; + } + } + break; + + case Track::AUDIO_AUX: + updateFlags |= SC_AUX; + break; + + default: + break; + } + removeTrackOperation(editable_track, pendingOperations); + updateFlags |= SC_TRACK_REMOVED; break; + case UndoOp::ModifyClip: - MusECore::SndFile::applyUndoFile(i->filename, i->tmpwavfile, i->startframe, i->endframe); + MusECore::SndFile::applyUndoFile(i->nEvent, i->tmpwavfile, i->startframe, i->endframe); + updateFlags |= SC_CLIP_MODIFIED; break; case UndoOp::ModifyTrackChannel: - if (i->_propertyTrack->isMidiTrack()) + if (editable_property_track->isMidiTrack()) { - MusECore::MidiTrack* mt = dynamic_cast(i->_propertyTrack); - if (mt == 0 || mt->type() == MusECore::Track::DRUM) - break; + MusECore::MidiTrack* mt = static_cast(editable_property_track); if (i->_newPropValue != mt->outChannel()) { + MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; MusEGlobal::audio->msgIdle(true); - mt->setOutChanAndUpdate(i->_newPropValue); + changed |= mt->setOutChanAndUpdate(i->_newPropValue, false); MusEGlobal::audio->msgIdle(false); - // DELETETHIS 5 - //if (mt->type() == MusECore::MidiTrack::DRUM) {//Change channel on all drum instruments - // for (int i=0; i_newPropValue; - // } - //updateFlags |= SC_CHANNELS; MusEGlobal::audio->msgUpdateSoloStates(); - updateFlags |= SC_MIDI_TRACK_PROP; + updateFlags |= (SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); } } else { - if(i->_propertyTrack->type() != MusECore::Track::AUDIO_SOFTSYNTH) + if(editable_property_track->type() != MusECore::Track::AUDIO_SOFTSYNTH) { - MusECore::AudioTrack* at = dynamic_cast(i->_propertyTrack); - if (at == 0) - break; + MusECore::AudioTrack* at = static_cast(editable_property_track); if (i->_newPropValue != at->channels()) { MusEGlobal::audio->msgSetChannels(at, i->_newPropValue); updateFlags |= SC_CHANNELS; @@ -892,29 +2652,505 @@ } } break; + + case UndoOp::SetTrackRecord: + if(!editable_track->setRecordFlag1(i->a)) + break; + pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackRecord)); + // FIXME: No choice but to include monitor flag. Really should try to merge pending ops flags + // with undo flags after executing the pending ops in executeOperationGroup3... + updateFlags |= (SC_RECFLAG | SC_TRACK_REC_MONITOR); + break; + + case UndoOp::SetTrackMute: + pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackMute)); + updateFlags |= SC_MUTE; + break; + + case UndoOp::SetTrackSolo: + pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackSolo)); + updateFlags |= SC_SOLO; + break; + + case UndoOp::SetTrackRecMonitor: + pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackRecMonitor)); + updateFlags |= SC_TRACK_REC_MONITOR; + break; + + case UndoOp::SetTrackOff: + pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackOff)); + updateFlags |= SC_MUTE; + break; + + + case UndoOp::AddRoute: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:AddRoute\n"); +#endif + pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::AddRoute)); + updateFlags |= SC_ROUTE; + break; + + case UndoOp::DeleteRoute: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:DeleteEvent\n"); +#endif + pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::DeleteRoute)); + updateFlags |= SC_ROUTE; + break; + + case UndoOp::ModifyTrackName: + pendingOperations.add(PendingOperationItem(editable_track, i->_newName, PendingOperationItem::ModifyTrackName)); + updateFlags |= (SC_TRACK_MODIFIED | SC_MIDI_TRACK_PROP); + // If it's an aux track, notify aux UI controls to reload, or change their names etc. + if(editable_track->type() == Track::AUDIO_AUX) + updateFlags |= SC_AUX; + break; + break; + + case UndoOp::MoveTrack: + pendingOperations.add(PendingOperationItem(&_tracks, i->a, i->b, PendingOperationItem::MoveTrack)); + updateFlags |= SC_TRACK_MOVED; + break; + + case UndoOp::ModifyPartName: + pendingOperations.add(PendingOperationItem(editable_part, i->_newName, PendingOperationItem::ModifyPartName)); + updateFlags |= SC_PART_MODIFIED; + break; + + case UndoOp::ModifyPartLength: + { + unsigned p = Pos::convert(editable_part->posValue() + i->new_partlen_or_pos, editable_part->type(), Pos::TICKS); + // >= for good luck, since this (simpler) comparison is in the TICKS domain. + if(p >= song_len) + { + song_len = p + 1; + // Insert a ModifySongLen operation BEFORE this one. If insert finds an existing ModifySongLen, + // possibly long before this one, it REPLACES that one's values. + operations.insert(i, UndoOp(UndoOp::ModifySongLen, song_len, MusEGlobal::song->len())); + // Since the ModifySongLen above will not be iterated now, act like the operation had just been iterated. + // The same REPLACEMENT rules apply here. + pendingOperations.add(PendingOperationItem(song_len, PendingOperationItem::ModifySongLength)); + updateFlags |= -1; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. + //updateFlags |= SC_SONG_LEN; + } + removePortCtrlEvents(editable_part, editable_part->track(), pendingOperations); + pendingOperations.add(PendingOperationItem(editable_part, i->new_partlen_or_pos, PendingOperationItem::ModifyPartLength)); + addPortCtrlEvents(editable_part, editable_part->posValue(), i->new_partlen_or_pos, editable_part->track(), pendingOperations); + updateFlags |= SC_PART_MODIFIED; + } + break; + + case UndoOp::MovePart: + { + unsigned p = Pos::convert(editable_part->lenValue() + i->new_partlen_or_pos, editable_part->type(), Pos::TICKS); + // >= for good luck, since this (simpler) comparison is in the TICKS domain. + if(p >= song_len) + { + song_len = p + 1; + // Insert a ModifySongLen operation BEFORE this one. If insert finds an existing ModifySongLen, + // possibly long before this one, it REPLACES that one's values. + operations.insert(i, UndoOp(UndoOp::ModifySongLen, song_len, MusEGlobal::song->len())); + // Since the ModifySongLen above will not be iterated now, act like the operation had just been iterated. + // The same REPLACEMENT rules apply here. + pendingOperations.add(PendingOperationItem(song_len, PendingOperationItem::ModifySongLength)); + updateFlags |= -1; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. + //updateFlags |= SC_SONG_LEN; + } +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:MovePart ** calling parts->movePartOperation\n"); +#endif + editable_part->track()->parts()->movePartOperation(editable_part, i->new_partlen_or_pos, pendingOperations, editable_track); + if(editable_track) + updateFlags |= SC_PART_INSERTED | SC_PART_REMOVED; + updateFlags |= SC_PART_MODIFIED; + } + break; + + case UndoOp::AddPart: + { + unsigned p = Pos::convert(editable_part->lenValue() + editable_part->posValue(), editable_part->type(), Pos::TICKS); + // >= for good luck, since this (simpler) comparison is in the TICKS domain. + if(p >= song_len) + { + song_len = p + 1; + // Insert a ModifySongLen operation BEFORE this one. If insert finds an existing ModifySongLen, + // possibly long before this one, it REPLACES that one's values. + operations.insert(i, UndoOp(UndoOp::ModifySongLen, song_len, MusEGlobal::song->len())); + // Since the ModifySongLen above will not be iterated now, act like the operation had just been iterated. + // The same REPLACEMENT rules apply here. + pendingOperations.add(PendingOperationItem(song_len, PendingOperationItem::ModifySongLength)); + updateFlags |= -1; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. + //updateFlags |= SC_SONG_LEN; + } +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:addPart ** calling parts->addOperation\n"); +#endif + // Ensure that wave event sndfile file handles are opened. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + editable_part->openAllEvents(); + + editable_part->track()->parts()->addOperation(editable_part, pendingOperations); + updateFlags |= SC_PART_INSERTED; + } + break; + + case UndoOp::DeletePart: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:deletePart ** calling parts->delOperation\n"); +#endif + editable_part->track()->parts()->delOperation(editable_part, pendingOperations); + updateFlags |= SC_PART_REMOVED; + break; + + case UndoOp::AddEvent: { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:AddEvent ** calling addEvent\n"); +#endif + if(!i->nEvent.empty()) + { + SndFileR f = i->nEvent.sndFile(); + // Ensure that wave event sndfile file handle is opened. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + if(!f.isNull() && !f.isOpen()) + f.openRead(); + } + + addEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones); + updateFlags |= SC_EVENT_INSERTED; + } + break; + + case UndoOp::DeleteEvent: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:DeleteEvent ** calling deleteEvent\n"); +#endif + deleteEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones); + updateFlags |= SC_EVENT_REMOVED; + break; + + case UndoOp::ModifyEvent: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:ModifyEvent ** calling changeEvent\n"); +#endif + changeEventOperation(i->oEvent, i->nEvent, editable_part, i->doCtrls, i->doClones); + updateFlags |= SC_EVENT_MODIFIED; + break; + + + case UndoOp::AddAudioCtrlVal: + { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:AddAudioCtrlVal\n"); +#endif + CtrlListList* cll = static_cast(editable_track)->controller(); + iCtrlList icl = cll->find(i->_audioCtrlID); + if(icl != cll->end()) + { + //CtrlList* cl = icl->second; + //iCtrl ic = cl->find(i->_audioCtrlFrame); + //if(ic != cl->end()) + // // An existing value was found. Replace it with the new value. + // pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioCtrlVal, PendingOperationItem::ModifyAudioCtrlVal)); + //else + // Add the new value. + pendingOperations.add(PendingOperationItem(icl->second, i->_audioCtrlFrame, i->_audioCtrlVal, PendingOperationItem::AddAudioCtrlVal)); + updateFlags |= SC_AUDIO_CONTROLLER; + } + } + break; + + case UndoOp::DeleteAudioCtrlVal: + { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:DeleteAudioCtrlVal\n"); +#endif + CtrlListList* cll = static_cast(editable_track)->controller(); + iCtrlList icl = cll->find(i->_audioCtrlID); + if(icl != cll->end()) + { + CtrlList* cl = icl->second; + iCtrl ic = cl->find(i->_audioCtrlFrame); + if(ic != cl->end()) + { + i->_audioCtrlVal = ic->second.val; // Store the existing value so it can be restored. + pendingOperations.add(PendingOperationItem(cl, ic, PendingOperationItem::DeleteAudioCtrlVal)); + updateFlags |= SC_AUDIO_CONTROLLER; + } + } + } + break; + + case UndoOp::ModifyAudioCtrlVal: + { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:ModifyAudioCtrlVal\n"); +#endif + CtrlListList* cll = static_cast(editable_track)->controller(); + iCtrlList icl = cll->find(i->_audioCtrlID); + if(icl != cll->end()) + { + CtrlList* cl = icl->second; + iCtrl ic = cl->find(i->_audioCtrlFrame); + if(ic != cl->end()) + { + i->_audioCtrlVal = ic->second.val; // Store the existing value so it can be restored. + pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioNewCtrlFrame, i->_audioNewCtrlVal, PendingOperationItem::ModifyAudioCtrlVal)); + updateFlags |= SC_AUDIO_CONTROLLER; + } + } + } + break; + + case UndoOp::ModifyAudioCtrlValList: + { +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:ModifyAudioCtrlValList\n"); +#endif + // Take either id. At least one list must be valid. + const int id = i->_eraseCtrlList ? i->_eraseCtrlList->id() : i->_addCtrlList->id(); + iCtrlList icl = i->_ctrlListList->find(id); + if(icl != i->_ctrlListList->end()) + { + // Make a complete copy of the controller list. The list will be quickly switched in the realtime stage. + // The Pending Operations system will take 'ownership' of this and delete it at the appropriate time. + CtrlList* new_list = new CtrlList(*icl->second, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); + + // Erase any items in the erase list... + //if(i->_eraseCtrlList) + if(i->_eraseCtrlList && !i->_eraseCtrlList->empty()) + { + //const std::size_t sz = i->_eraseCtrlList->size(); + //if(sz != 0) + //{ + //const CtrlList& cl_r = *i->_eraseCtrlList; + // Both of these should be valid. + //ciCtrl n_s = new_list->find(cl_r[0].frame); // The first item to be erased. + //ciCtrl n_e = new_list->find(cl_r[sz - 1].frame); // The last item to be erased. + iCtrl n_s = new_list->find(i->_eraseCtrlList->begin()->second.frame); // The first item to be erased. + ciCtrl e_e = i->_eraseCtrlList->end(); + --e_e; + //ciCtrl n_e = new_list->find((--i->_eraseCtrlList->end())->second.frame); // The last item to be erased. + iCtrl n_e = new_list->find(e_e->second.frame); // The last item to be erased. + if(n_s != new_list->end() && n_e != new_list->end()) + { + // Since std range does NOT include the last iterator, increment n_e so erase will get all items. + ++n_e; + new_list->erase(n_s, n_e); + } + //} + } + + // Add any items in the add list... + if(i->_addCtrlList && !i->_addCtrlList->empty()) + new_list->insert(i->_addCtrlList->begin(), i->_addCtrlList->end()); + //new_list->insert(const_cast(i->_addCtrlList)->begin(), const_cast(i->_addCtrlList)->end()); + + // The operation will quickly switch the list in the RT stage then the delete the old list in the non-RT stage. + pendingOperations.add(PendingOperationItem(icl, new_list, PendingOperationItem::ModifyAudioCtrlValList)); + updateFlags |= SC_AUDIO_CONTROLLER_LIST; + } + } + break; + + + case UndoOp::AddTempo: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:AddTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b); +#endif + MusEGlobal::tempomap.addOperation(i->a, i->b, pendingOperations); + updateFlags |= SC_TEMPO; + break; + + case UndoOp::DeleteTempo: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:DeleteTempo ** calling tempomap.delOperation tick:%d\n", i->a); +#endif + MusEGlobal::tempomap.delOperation(i->a, pendingOperations); + updateFlags |= SC_TEMPO; + break; + + case UndoOp::ModifyTempo: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:ModifyTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->c); +#endif + MusEGlobal::tempomap.addOperation(i->a, i->c, pendingOperations); + updateFlags |= SC_TEMPO; + break; + + case UndoOp::SetGlobalTempo: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:SetGlobalTempo ** adding SetGlobalTempo operation\n"); +#endif + pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->a, PendingOperationItem::SetGlobalTempo)); + updateFlags |= SC_TEMPO; + break; + + case UndoOp::AddSig: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:AddSig ** calling sigmap.addOperation\n"); +#endif + AL::sigmap.addOperation(i->a, AL::TimeSignature(i->b, i->c), pendingOperations); + updateFlags |= SC_SIG; + break; + + case UndoOp::DeleteSig: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:DeleteSig ** calling sigmap.delOperation\n"); +#endif + AL::sigmap.delOperation(i->a, pendingOperations); + updateFlags |= SC_SIG; + break; + + case UndoOp::ModifySig: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:ModifySig ** calling sigmap.addOperation\n"); +#endif + AL::sigmap.addOperation(i->a, AL::TimeSignature(i->d, i->e), pendingOperations); + updateFlags |= SC_SIG; + break; + + + case UndoOp::AddKey: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:AddKey ** calling keymap.addOperation\n"); +#endif + MusEGlobal::keymap.addOperation(i->a, key_enum(i->b), pendingOperations); + updateFlags |= SC_KEY; + break; + + case UndoOp::DeleteKey: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:DeleteKey ** calling keymap.delOperation\n"); +#endif + MusEGlobal::keymap.delOperation(i->a, pendingOperations); + updateFlags |= SC_KEY; + break; + + case UndoOp::ModifyKey: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:ModifyKey ** calling keymap.addOperation\n"); +#endif + MusEGlobal::keymap.addOperation(i->a, key_enum(i->c), pendingOperations); + updateFlags |= SC_KEY; + break; + + case UndoOp::ModifySongLen: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:ModifySongLen ** adding ModifySongLen operation\n"); +#endif + pendingOperations.add(PendingOperationItem(i->a, PendingOperationItem::ModifySongLength)); + updateFlags |= -1; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. + //updateFlags |= SC_SONG_LEN; + break; + + case UndoOp::EnableAllAudioControllers: +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup1:EnableAllAudioControllers\n"); +#endif + pendingOperations.add(PendingOperationItem(PendingOperationItem::EnableAllAudioControllers)); + updateFlags |= SC_AUDIO_CONTROLLER; + break; default: break; } } - return false; } //--------------------------------------------------------- -// doRedo3 +// executeOperationGroup3 // non realtime context //--------------------------------------------------------- -void Song::doRedo3() +void Song::executeOperationGroup3(Undo& operations) { - Undo& u = redoList->back(); - for (iUndoOp i = u.begin(); i != u.end(); ++i) { + pendingOperations.executeNonRTStage(); +#ifdef _UNDO_DEBUG_ + fprintf(stderr, "Song::executeOperationGroup3 *** Calling pendingOperations.clear()\n"); +#endif + pendingOperations.clear(); + //bool song_has_changed = !operations.empty(); + for (iUndoOp i = operations.begin(); i != operations.end(); ) { + Track* editable_track = const_cast(i->track); +// uncomment if needed Track* editable_property_track = const_cast(i->_propertyTrack); + Part* editable_part = const_cast(i->part); // uncomment if needed switch(i->type) { case UndoOp::AddTrack: - insertTrack3(i->track, i->trackno); + switch(editable_track->type()) + { + case Track::AUDIO_OUTPUT: + // Connect audio output ports to Jack ports... + if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) + { + AudioOutput* ao = (AudioOutput*)editable_track; + for(int ch = 0; ch < ao->channels(); ++ch) + { + void* our_port = ao->jackPort(ch); + if(!our_port) + continue; + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); + if(!our_port_name) + continue; + RouteList* rl = ao->outRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE || ir->channel != ch) + continue; + const char* route_name = ir->persistentJackPortName; + //if(ir->jackPort) + if(!MusEGlobal::audioDevice->findPort(route_name)) + continue; + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + MusEGlobal::audioDevice->connect(our_port_name, route_name); + updateFlags |= SC_ROUTE; + } + } + } + break; + + case Track::AUDIO_INPUT: + // Connect Jack ports to audio input ports... + if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) + { + AudioInput* ai = (AudioInput*)editable_track; + for(int ch = 0; ch < ai->channels(); ++ch) + { + void* our_port = ai->jackPort(ch); + if(!our_port) + continue; + const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); + if(!our_port_name) + continue; + RouteList* rl = ai->inRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type != Route::JACK_ROUTE || ir->channel != ch) + continue; + const char* route_name = ir->persistentJackPortName; + //if(ir->jackPort) + if(!MusEGlobal::audioDevice->findPort(route_name)) + continue; + //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) + MusEGlobal::audioDevice->connect(route_name, our_port_name); + updateFlags |= SC_ROUTE; + } + } + } + break; + + default: + break; + } + break; case UndoOp::DeleteTrack: - removeTrack3(i->track); + // Ensure that wave event sndfile file handles are closed. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + editable_track->closeAllParts(); break; case UndoOp::ModifyMarker: { @@ -929,13 +3165,41 @@ } } break; + case UndoOp::DeletePart: + // Ensure that wave event sndfile file handles are closed. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + editable_part->closeAllEvents(); + break; + case UndoOp::DeleteEvent: { + if(!i->nEvent.empty()) + { + SndFileR f = i->nEvent.sndFile(); + // Ensure that wave event sndfile file handle is closed. + // It should not be the job of the pending operations list to do this. + // TODO Coordinate close/open with part mute and/or track off. + if(!f.isNull() && f.isOpen()) + f.close(); + } + } + break; default: break; } + + // Is the operation marked as non-undoable? Remove it from the list. + if(i->_noUndo) + i = operations.erase(i); + else + ++i; } - undoList->push_back(u); // put item on undo list - redoList->pop_back(); - dirty = true; + + // If some operations marked as non-undoable were removed, it is OK, + // because we only want dirty if an undoable operation was executed, right? + if(!operations.empty()) + // Hm, no. ANY operation actually changes things, so yes, the song is dirty. + //if(song_has_changed) + emit sigDirty(); } diff -Nru muse-2.1.2/muse/undo.h muse-3.0.2+ds1/muse/undo.h --- muse-2.1.2/muse/undo.h 2013-03-28 15:17:38.000000000 +0000 +++ muse-3.0.2+ds1/muse/undo.h 2017-12-17 21:07:38.000000000 +0000 @@ -28,15 +28,21 @@ #include "event.h" #include "marker/marker.h" +#include "route.h" class QString; +namespace AL { +struct TimeSignature; +} + namespace MusECore { class Track; -class TEvent; -class SigEvent; class Part; +class CtrlListList; +class CtrlList; +struct CtrlVal; extern std::list temporaryWavFiles; //!< Used for storing all tmp-files, for cleanup on shutdown //--------------------------------------------------------- @@ -45,37 +51,40 @@ struct UndoOp { enum UndoType { + AddRoute, DeleteRoute, AddTrack, DeleteTrack, - AddPart, DeletePart, ModifyPart, - AddEvent, DeleteEvent, ModifyEvent, - AddTempo, DeleteTempo, - AddSig, DeleteSig, - AddKey, DeleteKey, + AddPart, DeletePart, MovePart, ModifyPartLength, ModifyPartName, SelectPart, + AddEvent, DeleteEvent, ModifyEvent, SelectEvent, + AddAudioCtrlVal, DeleteAudioCtrlVal, ModifyAudioCtrlVal, ModifyAudioCtrlValList, + AddTempo, DeleteTempo, ModifyTempo, SetGlobalTempo, + AddSig, DeleteSig, ModifySig, + AddKey, DeleteKey, ModifyKey, ModifyTrackName, ModifyTrackChannel, - SwapTrack, + SetTrackRecord, SetTrackMute, SetTrackSolo, SetTrackRecMonitor, SetTrackOff, + MoveTrack, ModifyClip, ModifyMarker, ModifySongLen, // a = new len, b = old len - DoNothing + DoNothing, + + // These operation cannot be undone. They are 'one time' operations, removed after execution. + EnableAllAudioControllers }; UndoType type; union { + //U() { memset( this, 0, sizeof( U ) ); } struct { int a; int b; int c; + int d; + int e; }; struct { - Track* track; - int trackno; - }; - struct { - Part* oPart; - Part* nPart; - }; - struct { - Part* part; // this part is only relevant for EVENT operations, NOT for part ops! + const Part* part; + unsigned old_partlen_or_pos; // FIXME FINDMICHJETZT XTicks!! + unsigned new_partlen_or_pos; }; struct { int channel; @@ -86,54 +95,107 @@ struct { int startframe; //!< Start frame of changed data int endframe; //!< End frame of changed data - const char* filename; //!< The file that is changed - const char* tmpwavfile; //!< The file with the changed data + QString* tmpwavfile; //!< The file with the changed data }; struct { - Marker* realMarker; + Marker* realMarker; Marker* copyMarker; }; struct { - Track* _renamedTrack; - char* _oldName; - char* _newName; - }; - struct { - Track* _propertyTrack; + const Track* _propertyTrack; int _oldPropValue; int _newPropValue; }; + struct { + CtrlListList* _ctrlListList; + CtrlList* _eraseCtrlList; + CtrlList* _addCtrlList; + }; + struct { + int _audioCtrlID; + int _audioCtrlFrame; + int _audioNewCtrlFrame; + double _audioCtrlVal; + double _audioNewCtrlVal; + }; }; + + QString* _oldName; + QString* _newName; Event oEvent; Event nEvent; + bool selected; + bool selected_old; bool doCtrls; bool doClones; + const Track* track; + const Track* oldTrack; + int trackno; + Route routeFrom; + Route routeTo; + + // If _noUndo is set, the operation cannot be undone. It is a 'one time' operation, removed after execution. + // It allows mixed undoable and non-undoable operations in one list, all executed in one RT cycle. + bool _noUndo; const char* typeName(); void dump(); UndoOp(); - UndoOp(UndoType type, int a, int b, int c=0); - UndoOp(UndoType type, int n, Track* track); - UndoOp(UndoType type, Part* part); - UndoOp(UndoType type, Event& oev, Event& nev, Part* part, bool doCtrls, bool doClones); - UndoOp(UndoType type, Event& nev, Part* part, bool doCtrls, bool doClones); - UndoOp(UndoType type, Part* oPart, Part* nPart, bool doCtrls, bool doClones); - UndoOp(UndoType type, int c, int ctrl, int ov, int nv); - UndoOp(UndoType type, const char* changedFile, const char* changeData, int startframe, int endframe); - UndoOp(UndoType type, Marker* copyMarker, Marker* realMarker); - UndoOp(UndoType type, Track* track, const char* old_name, const char* new_name); - UndoOp(UndoType type, Track* track, int old_chan, int new_chan); + // NOTE: In these constructors, if noUndo is set, the operation cannot be undone. It is a 'one time' operation, removed after execution. + // It allows mixed undoable and non-undoable operations in one list, all executed in one RT cycle. + UndoOp(UndoType type, int a, int b, int c=0, bool noUndo = false); + UndoOp(UndoType type, int n, const Track* track, bool noUndo = false); + UndoOp(UndoType type, const Part* part, bool noUndo = false); + UndoOp(UndoType type, const Part* part, const QString& old_name, const QString& new_name, bool noUndo = false); + UndoOp(UndoType type, const Part* part, bool selected, bool selected_old, bool noUndo = false); + UndoOp(UndoType type, const Part* part, int old_len_or_pos, int new_len_or_pos, Pos::TType new_time_type = Pos::TICKS, const Track* oTrack = 0, const Track* nTrack = 0, bool noUndo = false); + UndoOp(UndoType type, const Event& nev, const Event& oev, const Part* part, bool doCtrls, bool doClones, bool noUndo = false); + UndoOp(UndoType type, const Event& nev, const Part* part, bool, bool, bool noUndo = false); + UndoOp(UndoType type, const Event& changedEvent, const QString& changeData, int startframe, int endframe, bool noUndo = false); + UndoOp(UndoType type, Marker* copyMarker, Marker* realMarker, bool noUndo = false); + UndoOp(UndoType type, const Track* track, const QString& old_name, const QString& new_name, bool noUndo = false); + UndoOp(UndoType type, const Track* track, int old_chan, int new_chan, bool noUndo = false); + //UndoOp(UndoType type, const Track* track, int ctrlID, int frame, bool noUndo = false); // Same as above. + UndoOp(UndoType type, const Track* track, int ctrlID, int oldFrame, int newFrame, double oldValue, double newValue, bool noUndo = false); + UndoOp(UndoType type, const Track* track, int ctrlID, int frame, double value, bool noUndo = false); + UndoOp(UndoType type, const Track* track, bool value, bool noUndo = false); + UndoOp(UndoType type, CtrlListList* ctrl_ll, CtrlList* eraseCtrlList, CtrlList* addCtrlList, bool noUndo = false); + UndoOp(UndoType type, int tick, const AL::TimeSignature old_sig, const AL::TimeSignature new_sig, bool noUndo = false); + UndoOp(UndoType type, const Route& route_from, const Route& route_to, bool noUndo = false); UndoOp(UndoType type); }; class Undo : public std::list { public: + Undo() : std::list() { combobreaker=false; } + Undo(const Undo& other) : std::list(other) { this->combobreaker=other.combobreaker; } + Undo& operator=(const Undo& other) { std::list::operator=(other); this->combobreaker=other.combobreaker; return *this;} + bool empty() const; + + + /** if set, forbid merging (below). + * Defaults to false */ + bool combobreaker; + + /** is possible, merges itself and other by appending + * all contents of other at this->end(). + * returns true if merged, false otherwise. + * in case of success, the caller has to ensure that + * other is deleted from the UndoList. */ + bool merge_combo(const Undo& other); + + void push_back(const UndoOp& op); + void insert(iterator position, const_iterator first, const_iterator last); + void insert(iterator position, const UndoOp& op); + void insert (iterator position, size_type n, const UndoOp& op); }; typedef Undo::iterator iUndoOp; typedef Undo::reverse_iterator riUndoOp; +typedef Undo::const_iterator ciUndoOp; +typedef Undo::const_reverse_iterator criUndoOp; class UndoList : public std::list { protected: diff -Nru muse-2.1.2/muse/vst.cpp muse-3.0.2+ds1/muse/vst.cpp --- muse-2.1.2/muse/vst.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/vst.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -85,7 +85,7 @@ return 0; case audioMasterPinConnected: - // inquire if an input or output is beeing connected; + // inquire if an input or output is being connected; // index enumerates input or output counting from zero: // value is 0 for input and != 0 otherwise. note: the // return value is 0 for such that older versions @@ -532,7 +532,7 @@ // getParameter //--------------------------------------------------------- -float VstSynthIF::getParameter(unsigned long idx) const +double VstSynthIF::getParameter(unsigned long idx) const { return _fst->plugin->getParameter(_fst->plugin, idx); } @@ -541,7 +541,7 @@ // setParameter //--------------------------------------------------------- -void VstSynthIF::setParameter(unsigned long idx, float value) +void VstSynthIF::setParameter(unsigned long idx, double value) { _fst->plugin->setParameter(_fst->plugin, idx, value); } @@ -561,8 +561,7 @@ AEffect* plugin = _fst->plugin; int params = plugin->numParams; for (int i = 0; i < params; ++i) { - float f = plugin->getParameter(plugin, i); - xml.floatTag(level, "param", f); + xml.doubleTag(level, "param", plugin->getParameter(plugin, i)); } } diff -Nru muse-2.1.2/muse/vst.h muse-3.0.2+ds1/muse/vst.h --- muse-2.1.2/muse/vst.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/vst.h 2017-12-04 21:01:18.000000000 +0000 @@ -71,7 +71,6 @@ _guiVisible = false; } - virtual bool initGui() { return true; }; virtual void guiHeartBeat() { } virtual bool guiVisible() const { return false; } virtual void showGui(bool) { } @@ -96,9 +95,15 @@ virtual QString getPatchName(int, int, bool) const { return ""; } virtual void populatePatchPopup(PopupMenu*, int, bool) {}; virtual void write(int level, Xml& xml) const; - virtual float getParameter(unsigned long idx) const; - virtual void setParameter(unsigned long idx, float value); - virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) { return 0; } + virtual double getParameter(unsigned long idx) const; + virtual void setParameter(unsigned long idx, double value); + virtual int getControllerInfo(int, QString*, int*, int*, int*, int*) { return 0; } + + //------------------------- + // Methods for PluginIBase: + //------------------------- + + virtual bool addScheduledControlEvent(unsigned long /*i*/, double /*val*/, unsigned /*frame*/) { return true; } // returns true if event cannot be delivered }; } // namespace MusECore diff -Nru muse-2.1.2/muse/vst_native.cpp muse-3.0.2+ds1/muse/vst_native.cpp --- muse-2.1.2/muse/vst_native.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/vst_native.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // // vst_native.cpp -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012-2013 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,12 +29,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include "globals.h" #include "gconfig.h" @@ -49,6 +51,7 @@ #include "tempo.h" #include "sync.h" #include "al/sig.h" +#include "minstrument.h" #include "vst_native.h" @@ -63,6 +66,9 @@ extern JackAudioDevice* jackAudio; +static VstIntPtr currentPluginId = 0; +static sem_t _vstIdLock; + //----------------------------------------------------------------------------------------- // vstHostCallback // This must be a function, it cannot be a class method so we dispatch to various objects from here. @@ -71,11 +77,13 @@ VstIntPtr VSTCALLBACK vstNativeHostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) { // Is this callback for an actual instance? Hand-off to the instance if so. - VSTPlugin* plugin; + //VSTPlugin* plugin; if(effect && effect->user) { - plugin = (VSTPlugin*)(effect->user); - return ((VstNativeSynthIF*)plugin)->hostCallback(opcode, index, value, ptr, opt); + VstNativeSynthOrPlugin *userData = (VstNativeSynthOrPlugin*)(effect->user); + //return ((VstNativeSynthIF*)plugin)->hostCallback(opcode, index, value, ptr, opt); + return VstNativeSynth::pluginHostCallback(userData, opcode, index, value, ptr, opt); + } // No instance found. So we are just scanning for plugins... @@ -96,7 +104,7 @@ case audioMasterCurrentId: // returns the unique id of a plug that's currently // loading - return 0; + return currentPluginId; case audioMasterIdle: // call application idle routine (this will @@ -211,13 +219,14 @@ return 0; #ifdef VST_FORCE_DEPRECATED +#ifndef VST_2_4_EXTENSIONS // deprecated in 2.4 case audioMasterGetSpeakerArrangement: // (long)input in , output in return 0; case audioMasterPinConnected: - // inquire if an input or output is beeing connected; + // inquire if an input or output is being connected; // index enumerates input or output counting from zero: // value is 0 for input and != 0 otherwise. note: the // return value is 0 for such that older versions @@ -280,7 +289,7 @@ case audioMasterCloseWindow: // close window, platform specific handle in return 0; - +#endif #endif default: @@ -297,23 +306,114 @@ // loadPluginLib //--------------------------------------------------------- +static bool scanSubPlugin(QFileInfo& fi, AEffect *plugin, int id, void *handle) +{ + char buffer[128]; + QString effectName; + QString vendorString; + QString productString; + int vendorVersion; + QString vendorVersionString; + std::vector::iterator is; + int vst_version = 0; + VstNativeSynth* new_synth = NULL; + + if(!(plugin->flags & effFlagsHasEditor)) + { + if(MusEGlobal::debugMsg) + fprintf(stderr, "Plugin has no GUI\n"); + } + else if(MusEGlobal::debugMsg) + fprintf(stderr, "Plugin has a GUI\n"); + + if(!(plugin->flags & effFlagsCanReplacing)) + fprintf(stderr, "Plugin does not support processReplacing\n"); + else if(MusEGlobal::debugMsg) + fprintf(stderr, "Plugin supports processReplacing\n"); + + plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0); + + buffer[0] = 0; + plugin->dispatcher(plugin, effGetEffectName, 0, 0, buffer, 0); + if(buffer[0]) + effectName = QString(buffer); + + buffer[0] = 0; + plugin->dispatcher(plugin, effGetVendorString, 0, 0, buffer, 0); + if (buffer[0]) + vendorString = QString(buffer); + + buffer[0] = 0; + plugin->dispatcher(plugin, effGetProductString, 0, 0, buffer, 0); + if (buffer[0]) + productString = QString(buffer); + + vendorVersion = plugin->dispatcher(plugin, effGetVendorVersion, 0, 0, NULL, 0); + + // Some (older) plugins don't have any of these strings. We only have the filename to use. + if(effectName.isEmpty()) + effectName = fi.completeBaseName(); + if(productString.isEmpty()) + //productString = fi.completeBaseName(); + productString = effectName; + + // Make sure it doesn't already exist. + for(is = MusEGlobal::synthis.begin(); is != MusEGlobal::synthis.end(); ++is) + if((*is)->name() == effectName && (*is)->baseName() == fi.completeBaseName()) + { + fprintf(stderr, "VST %s already exists!\n", (char *)effectName.toUtf8().constData()); + return false; + } + + // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far. + vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f); + bool isSynth = true; + if(!((plugin->flags & effFlagsIsSynth) || (vst_version >= 2 && plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0))) + { + isSynth = false; + } + + vendorVersionString = QString("%1.%2.%3").arg((vendorVersion >> 16) & 0xff).arg((vendorVersion >> 8) & 0xff).arg(vendorVersion & 0xff); + + Plugin::PluginFeatures reqfeat = Plugin::NoFeatures; + + new_synth = new VstNativeSynth(fi, plugin, + effectName, productString, vendorString, vendorVersionString, + id, handle, isSynth, reqfeat); + + if(MusEGlobal::debugMsg) + fprintf(stderr, "scanVstNativeLib: adding vst synth plugin:%s name:%s effectName:%s vendorString:%s productString:%s vstver:%d\n", + fi.filePath().toLatin1().constData(), + fi.completeBaseName().toLatin1().constData(), + effectName.toLatin1().constData(), + vendorString.toLatin1().constData(), + productString.toLatin1().constData(), + vst_version + ); + + MusEGlobal::synthis.push_back(new_synth); + + if(new_synth->inPorts() > 0 && new_synth->outPorts() > 0) + { + MusEGlobal::plugins.push_back(new VstNativePluginWrapper(new_synth, reqfeat)); + } + + return true; + +} + static void scanVstNativeLib(QFileInfo& fi) { - void* handle = dlopen(fi.filePath().toAscii().constData(), RTLD_NOW); + sem_wait(&_vstIdLock); + currentPluginId = 0; + bool bDontDlCLose = false; + AEffect *plugin = NULL; + void* handle = dlopen(fi.filePath().toLatin1().constData(), RTLD_NOW); if (handle == NULL) { - fprintf(stderr, "scanVstNativeLib: dlopen(%s) failed: %s\n", fi.filePath().toAscii().constData(), dlerror()); - return; + fprintf(stderr, "scanVstNativeLib: dlopen(%s) failed: %s\n", fi.filePath().toLatin1().constData(), dlerror()); + goto _end; } - - char buffer[128]; - QString effectName; - QString vendorString; - QString productString; - int vendorVersion; - std::vector::iterator is; - int vst_version = 0; - VstNativeSynth* new_synth = NULL; AEffect *(*getInstance)(audioMasterCallback); getInstance = (AEffect*(*)(audioMasterCallback))dlsym(handle, NEW_PLUGIN_ENTRY_POINT); @@ -322,7 +422,7 @@ if(MusEGlobal::debugMsg) { fprintf(stderr, "VST 2.4 entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" not found in library %s, looking for \"" - OLD_PLUGIN_ENTRY_POINT "\"\n", fi.filePath().toAscii().constData()); + OLD_PLUGIN_ENTRY_POINT "\"\n", fi.filePath().toLatin1().constData()); } getInstance = (AEffect*(*)(audioMasterCallback))dlsym(handle, OLD_PLUGIN_ENTRY_POINT); @@ -330,8 +430,7 @@ { fprintf(stderr, "ERROR: VST entrypoints \"" NEW_PLUGIN_ENTRY_POINT "\" or \"" OLD_PLUGIN_ENTRY_POINT "\" not found in library\n"); - dlclose(handle); - return; + goto _end; } else if(MusEGlobal::debugMsg) { @@ -343,120 +442,114 @@ fprintf(stderr, "VST entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" found\n"); } - AEffect *plugin = getInstance(vstNativeHostCallback); + plugin = getInstance(vstNativeHostCallback); if(!plugin) { - fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\"\n", fi.filePath().toAscii().constData()); - dlclose(handle); - return; + fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\"\n", fi.filePath().toLatin1().constData()); + goto _end; } else if(MusEGlobal::debugMsg) fprintf(stderr, "plugin instantiated\n"); if(plugin->magic != kEffectMagic) { - fprintf(stderr, "Not a VST plugin in library \"%s\"\n", fi.filePath().toAscii().constData()); - dlclose(handle); - return; + fprintf(stderr, "Not a VST plugin in library \"%s\"\n", fi.filePath().toLatin1().constData()); + goto _end; } else if(MusEGlobal::debugMsg) fprintf(stderr, "plugin is a VST\n"); - if(!(plugin->flags & effFlagsHasEditor)) + if(plugin->dispatcher(plugin, 24 + 11 /* effGetCategory */, 0, 0, 0, 0) == 10 /* kPlugCategShell */) { - if(MusEGlobal::debugMsg) - fprintf(stderr, "Plugin has no GUI\n"); - } - else if(MusEGlobal::debugMsg) - fprintf(stderr, "Plugin has a GUI\n"); + bDontDlCLose = true; + std::map shellPlugs; + char cPlugName [128]; + do + { + memset(cPlugName, 0, sizeof(cPlugName)); + VstIntPtr id = plugin->dispatcher(plugin, 24 + 46 /* effShellGetNextPlugin */, 0, 0, cPlugName, 0); + if(id != 0 && cPlugName [0] != 0) + { + shellPlugs.insert(std::make_pair(id, std::string(cPlugName))); + } + else + break; + } + while(true); - if(!(plugin->flags & effFlagsCanReplacing)) - fprintf(stderr, "Plugin does not support processReplacing\n"); - else if(MusEGlobal::debugMsg) - fprintf(stderr, "Plugin supports processReplacing\n"); + for(std::map::iterator it = shellPlugs.begin(); it != shellPlugs.end(); ++it) + { + if(plugin) + { + plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); + plugin = NULL; + } - plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0); - - buffer[0] = 0; - plugin->dispatcher(plugin, effGetEffectName, 0, 0, buffer, 0); - if(buffer[0]) - effectName = QString(buffer); - - buffer[0] = 0; - plugin->dispatcher(plugin, effGetVendorString, 0, 0, buffer, 0); - if (buffer[0]) - vendorString = QString(buffer); - - buffer[0] = 0; - plugin->dispatcher(plugin, effGetProductString, 0, 0, buffer, 0); - if (buffer[0]) - productString = QString(buffer); - - vendorVersion = plugin->dispatcher(plugin, effGetVendorVersion, 0, 0, NULL, 0); - - // Some (older) plugins don't have any of these strings. We only have the filename to use. - if(effectName.isEmpty()) - effectName = fi.completeBaseName(); - if(productString.isEmpty()) - //productString = fi.completeBaseName(); - productString = effectName; - - // Make sure it doesn't already exist. - for(is = MusEGlobal::synthis.begin(); is != MusEGlobal::synthis.end(); ++is) - if((*is)->name() == effectName && (*is)->baseName() == fi.completeBaseName()) - goto _ending; - - // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far. - vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f); - if(!((plugin->flags & effFlagsIsSynth) || (vst_version >= 2 && plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0))) + currentPluginId = it->first; + getInstance = (AEffect*(*)(audioMasterCallback))dlsym(handle, NEW_PLUGIN_ENTRY_POINT); + if(!getInstance) + goto _end; + + AEffect *plugin = getInstance(vstNativeHostCallback); + if(!plugin) + { + fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\", shell id=%ld\n", fi.filePath().toLatin1().constData(), (long)currentPluginId); + goto _end; + } + scanSubPlugin(fi, plugin, currentPluginId, handle); + currentPluginId = 0; + } + } + else { - if(MusEGlobal::debugMsg) - fprintf(stderr, "Plugin is not a synth\n"); - goto _ending; + scanSubPlugin(fi, plugin, 0, 0); } - new_synth = new VstNativeSynth(fi, plugin, effectName, productString, vendorString, QString::number(vendorVersion)); - - if(MusEGlobal::debugMsg) - fprintf(stderr, "scanVstNativeLib: adding vst synth plugin:%s name:%s effectName:%s vendorString:%s productString:%s vstver:%d\n", - fi.filePath().toLatin1().constData(), - fi.completeBaseName().toLatin1().constData(), - effectName.toLatin1().constData(), - vendorString.toLatin1().constData(), - productString.toLatin1().constData(), - vst_version - ); - - MusEGlobal::synthis.push_back(new_synth); -_ending: ; - //plugin->dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0); - plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); - dlclose(handle); + if(plugin) + plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); + + _end: + if(handle && !bDontDlCLose) + dlclose(handle); + + sem_post(&_vstIdLock); } //--------------------------------------------------------- // scanVstDir //--------------------------------------------------------- -static void scanVstNativeDir(const QString& s) +static void scanVstNativeDir(const QString& s, int depth) { - if (MusEGlobal::debugMsg) - fprintf(stderr, "scan vst native plugin dir <%s>\n", s.toLatin1().constData()); - QDir pluginDir(s, QString("*.so"), QDir::Unsorted, QDir::Files); - if(!pluginDir.exists()) - return; - QStringList list = pluginDir.entryList(); - int count = list.count(); - for(int i = 0; i < count; ++i) - { - if(MusEGlobal::debugMsg) - fprintf(stderr, "scanVstNativeDir: found %s\n", (s + QString("/") + list[i]).toLatin1().constData()); - - QFileInfo fi(s + QString("/") + list[i]); - scanVstNativeLib(fi); + if(++depth > 2){ + return; + } + if (MusEGlobal::debugMsg) + fprintf(stderr, "scan vst native plugin dir <%s>\n", s.toLatin1().constData()); + QDir pluginDir(s, QString("*.so"), QDir::Unsorted, QDir::Files | QDir::AllDirs); + if(!pluginDir.exists()) + return; + QStringList list = pluginDir.entryList(); + int count = list.count(); + for(int i = 0; i < count; ++i) + { + QFileInfo fi(s + QString("/") + list[i]); + if(fi.isDir()) + { + if((list [i] != ".") && (list [i] != "..")) + { + scanVstNativeDir(fi.absoluteFilePath(), depth); + } + continue; } + if(MusEGlobal::debugMsg) + fprintf(stderr, "scanVstNativeDir: found %s\n", (s + QString("/") + list[i]).toLatin1().constData()); + + + scanVstNativeLib(fi); + } } //--------------------------------------------------------- @@ -465,8 +558,16 @@ void initVST_Native() { +#ifdef VST_NATIVE_SUPPORT + #ifdef VST_VESTIGE_SUPPORT + printf("Initializing Native VST support. Using VESTIGE compatibility implementation.\n"); + #else + printf("Initializing Native VST support. Using Steinberg VSTSDK.\n"); + #endif +#endif + sem_init(&_vstIdLock, 0, 1); std::string s; - const char* vstPath = getenv("VST_NATIVE_PATH"); + const char* vstPath = getenv("LINUX_VST_PATH"); if (vstPath) { if (MusEGlobal::debugMsg) @@ -491,7 +592,7 @@ if (MusEGlobal::debugMsg) fprintf(stderr, "scan native vst: VST_PATH not set\n"); const char* home = getenv("HOME"); - s = std::string(home) + std::string("/vst:/usr/local/lib64/vst:/usr/local/lib/vst:/usr/lib64/vst:/usr/lib/vst"); + s = std::string(home) + std::string("/.vst:") + std::string(home) + std::string("/vst:/usr/local/lib64/vst:/usr/local/lib/vst:/usr/lib64/vst:/usr/lib/vst"); vstPath = s.c_str(); if (MusEGlobal::debugMsg) fprintf(stderr, "scan native vst: defaulting to path: %s\n", vstPath); @@ -509,7 +610,7 @@ char* buffer = new char[n + 1]; strncpy(buffer, p, n); buffer[n] = '\0'; - scanVstNativeDir(QString(buffer)); + scanVstNativeDir(QString(buffer), 0); delete[] buffer; } p = pe; @@ -518,24 +619,28 @@ } } + + //--------------------------------------------------------- // VstNativeSynth //--------------------------------------------------------- -VstNativeSynth::VstNativeSynth(const QFileInfo& fi, AEffect* plugin, const QString& label, const QString& desc, const QString& maker, const QString& ver) - : Synth(fi, label, desc, maker, ver) +VstNativeSynth::VstNativeSynth(const QFileInfo& fi, AEffect* plugin, + const QString& label, const QString& desc, const QString& maker, const QString& ver, + VstIntPtr id, void *dlHandle, bool isSynth, Plugin::PluginFeatures reqFeatures) + : Synth(fi, label, desc, maker, ver, reqFeatures) { - _handle = NULL; + _handle = dlHandle; + _id = id; _hasGui = plugin->flags & effFlagsHasEditor; _inports = plugin->numInputs; _outports = plugin->numOutputs; _controlInPorts = plugin->numParams; - _inPlaceCapable = false; //(plugin->flags & effFlagsCanReplacing) && (_inports == _outports) && MusEGlobal::config.vstInPlace; -#ifndef VST_VESTIGE_SUPPORT - _hasChunks = plugin->flags & effFlagsProgramChunks; -#else - _hasChunks = false; -#endif +//#ifndef VST_VESTIGE_SUPPORT + _hasChunks = plugin->flags & 32 /*effFlagsProgramChunks*/; +//#else + // _hasChunks = false; +//#endif _flags = 0; _vst_version = 0; @@ -566,6 +671,7 @@ if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"midiProgramNames", 0.0f) > 0) _flags |= canMidiProgramNames; } + _isSynth = isSynth; } //--------------------------------------------------------- @@ -577,15 +683,15 @@ _instances += val; if(_instances == 0) { - if(_handle) + if(_handle && _id == 0) { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynth::incInstances no more instances, closing library\n"); #endif dlclose(_handle); + _handle = NULL; } - _handle = NULL; iIdx.clear(); oIdx.clear(); rpIdx.clear(); @@ -598,7 +704,7 @@ // instantiate //--------------------------------------------------------- -AEffect* VstNativeSynth::instantiate() +AEffect* VstNativeSynth::instantiate(void* userData) { int inst_num = _instances; inst_num++; @@ -608,7 +714,7 @@ QByteArray ba = info.filePath().toLatin1(); const char* path = ba.constData(); void* hnd = _handle; - int vst_version; + //int vst_version; if(hnd == NULL) { @@ -648,11 +754,19 @@ fprintf(stderr, "VST entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" found\n"); } + + sem_wait(&_vstIdLock); + + currentPluginId = _id; + AEffect *plugin = getInstance(vstNativeHostCallback); + + sem_post(&_vstIdLock); if(!plugin) { fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\"\n", path); - dlclose(hnd); + if(_id == 0) + dlclose(hnd); return NULL; } else if(MusEGlobal::debugMsg) @@ -661,7 +775,8 @@ if(plugin->magic != kEffectMagic) { fprintf(stderr, "Not a VST plugin in library \"%s\"\n", path); - dlclose(hnd); + if(_id == 0) + dlclose(hnd); return NULL; } else if(MusEGlobal::debugMsg) @@ -680,29 +795,39 @@ else if(MusEGlobal::debugMsg) fprintf(stderr, "Plugin supports processReplacing\n"); + plugin->user = userData; plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0); // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far. - vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f); - if(!((plugin->flags & effFlagsIsSynth) || (vst_version >= 2 && plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0))) + //vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f); + /*if(!((plugin->flags & effFlagsIsSynth) || (vst_version >= 2 && plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0))) { if(MusEGlobal::debugMsg) fprintf(stderr, "Plugin is not a synth\n"); goto _error; - } + }*/ + ++_instances; _handle = hnd; - - plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0); + + // work around to get airwave to work (author contacted so maybe another solution will + // reveal itself) + plugin->dispatcher(plugin, effSetSampleRate, 0, 0, NULL, MusEGlobal::sampleRate); + plugin->dispatcher(plugin, effSetBlockSize, 0, MusEGlobal::segmentSize, NULL, 0.0f); + plugin->dispatcher(plugin, effMainsChanged, 0, 1, NULL, 0.0f); + // + //plugin->dispatcher(plugin, effSetProgram, 0, 0, NULL, 0.0f); // REMOVE Tim. Or keep? return plugin; - +/* _error: //plugin->dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0); plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); - dlclose(hnd); + if(_id == 0) + dlclose(hnd); return NULL; + */ } //--------------------------------------------------------- @@ -715,7 +840,7 @@ sif->init(this); return sif; } - + //--------------------------------------------------------- // VstNativeSynthIF //--------------------------------------------------------- @@ -723,8 +848,10 @@ VstNativeSynthIF::VstNativeSynthIF(SynthI* s) : SynthIF(s) { _guiVisible = false; + _gw = NULL; _synth = NULL; _plugin = NULL; + _active = false; _editor = NULL; _inProcess = false; _controls = NULL; @@ -732,6 +859,8 @@ _audioInBuffers = NULL; _audioInSilenceBuf = NULL; _audioOutBuffers = NULL; + userData.pstate = 0; + userData.sif = this; } VstNativeSynthIF::~VstNativeSynthIF() @@ -767,6 +896,9 @@ if(_controls) delete[] _controls; + + if(_gw) + delete[] _gw; } //--------------------------------------------------------- @@ -776,10 +908,9 @@ bool VstNativeSynthIF::init(Synth* s) { _synth = (VstNativeSynth*)s; - _plugin = _synth->instantiate(); + _plugin = _synth->instantiate(&userData); if(!_plugin) return false; - _plugin->user = this; queryPrograms(); @@ -795,7 +926,13 @@ fprintf(stderr, "ERROR: VstNativeSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioOutBuffers[k][q] = MusEGlobal::denormalBias; + } + else + memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); } } @@ -811,8 +948,13 @@ fprintf(stderr, "ERROR: VstNativeSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); - _iUsedIdx.push_back(false); // Start out with all false. + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioInBuffers[k][q] = MusEGlobal::denormalBias; + } + else + memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); } int rv = posix_memalign((void**)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); @@ -821,20 +963,28 @@ fprintf(stderr, "ERROR: VstNativeSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioInSilenceBuf[q] = MusEGlobal::denormalBias; + } + else + memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); } + _controls = NULL; + _gw = NULL; unsigned long controlPorts = _synth->inControls(); if(controlPorts != 0) + { _controls = new Port[controlPorts]; - else - _controls = NULL; - - //_synth->midiCtl2PortMap.clear(); - //_synth->port2MidiCtlMap.clear(); + _gw = new VstNativeGuiWidgets[controlPorts]; + } for(unsigned long i = 0; i < controlPorts; ++i) { + _gw[i].pressed = false; + _controls[i].idx = i; //float val; // TODO //ladspaDefaultValue(ld, k, &val); // FIXME TODO @@ -842,7 +992,6 @@ _controls[i].val = val; _controls[i].tmpVal = val; _controls[i].enCtrl = true; - _controls[i].en2Ctrl = true; // Support a special block for synth ladspa controllers. // Put the ID at a special block after plugins (far after). @@ -855,7 +1004,7 @@ float min = 0.0, max = 1.0; CtrlList* cl; - CtrlListList* cll = ((MusECore::AudioTrack*)synti)->controller(); + CtrlListList* cll = track()->controller(); iCtrlList icl = cll->find(id); if (icl == cll->end()) { @@ -869,22 +1018,17 @@ cl = icl->second; _controls[i].val = cl->curVal(); -#ifndef VST_VESTIGE_SUPPORT - if(dispatch(effCanBeAutomated, i, 0, NULL, 0.0f) == 1) + if(dispatch(26 /*effCanBeAutomated*/, i, 0, NULL, 0.0f) == 1) { -#endif double v = cl->curVal(); if(v != _plugin->getParameter(_plugin, i)) _plugin->setParameter(_plugin, i, v); -#ifndef VST_VESTIGE_SUPPORT } #ifdef VST_NATIVE_DEBUG else fprintf(stderr, "VstNativeSynthIF::init %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), i); #endif - -#endif } cl->setRange(min, max); @@ -894,11 +1038,8 @@ //cl->setMode(ladspaCtrlMode(ld, k)); cl->setMode(ctrlMode(i)); } - - activate(); - doSelectProgram(synti->_curBankH, synti->_curBankL, synti->_curProgram); - //doSelectProgram(synti->_curProgram); + activate(); return true; } @@ -906,11 +1047,11 @@ // resizeEditor //--------------------------------------------------------- -bool VstNativeSynthIF::resizeEditor(int w, int h) +bool VstNativeSynth::resizeEditor(MusEGui::VstNativeEditor *editor, int w, int h) { - if(!_editor || w <= 0 || h <= 0) + if(!editor || w <= 0 || h <= 0) return false; - _editor->resize(w, h); + editor->setFixedSize(w, h); return true; } @@ -918,326 +1059,394 @@ // hostCallback //--------------------------------------------------------- -VstIntPtr VstNativeSynthIF::hostCallback(VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) - { - static VstTimeInfo _timeInfo; +VstIntPtr VstNativeSynth::pluginHostCallback(VstNativeSynthOrPlugin *userData, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) +{ + static VstTimeInfo _timeInfo; #ifdef VST_NATIVE_DEBUG - if(opcode != audioMasterGetTime) - fprintf(stderr, "VstNativeSynthIF::hostCallback %s opcode:%ld\n", name().toLatin1().constData(), opcode); + if(opcode != audioMasterGetTime) + fprintf(stderr, "VstNativeSynthIF::hostCallback %s opcode:%ld\n", name().toLatin1().constData(), opcode); #endif - switch (opcode) { - case audioMasterAutomate: - // index, value, returns 0 - ///_plugin->setParameter (_plugin, index, opt); - guiControlChanged(index, opt); - return 0; - - case audioMasterVersion: - // vst version, currently 2 (0 for older) - return 2300; - - case audioMasterCurrentId: - // returns the unique id of a plug that's currently - // loading - return 0; + switch (opcode) { + case audioMasterAutomate: + // index, value, returns 0 + ///_plugin->setParameter (_plugin, index, opt); + VstNativeSynth::guiControlChanged(userData, index, opt); + return 0; - case audioMasterIdle: - // call application idle routine (this will - // call effEditIdle for all open editors too) - //_plugin->updateParamValues(false); - //_plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); - idleEditor(); - return 0; + case audioMasterVersion: + // vst version, currently 2 (0 for older) + return 2300; + + case audioMasterCurrentId: + { + // returns the unique id of a plug that's currently + // loading + ///return 0; + AEffect *vstPlug = 0; + if(userData->sif) + vstPlug = userData->sif->_plugin; + else if(userData->pstate) + vstPlug = userData->pstate->plugin; + return vstPlug->uniqueID; + } + + case audioMasterIdle: + // call application idle routine (this will + // call effEditIdle for all open editors too) + //_plugin->updateParamValues(false); + //_plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); + ///idleEditor(); // REMOVE Tim. Or keep. + return 0; - case audioMasterGetTime: - { - // returns const VstTimeInfo* (or 0 if not supported) - // should contain a mask indicating which fields are required - // (see valid masks above), as some items may require extensive - // conversions + case audioMasterGetTime: + { + // returns const VstTimeInfo* (or 0 if not supported) + // should contain a mask indicating which fields are required + // (see valid masks above), as some items may require extensive + // conversions + + // FIXME TODO: Optimizations: This may be called many times in one process call + // due to our multi-run slices. Some of the (costly) info will be redundant. + // So try to add some flag to try to only call some or all of this once per cycle. #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::hostCallback master time: valid: nanos:%d ppqpos:%d tempo:%d bars:%d cyclepos:%d sig:%d smpte:%d clock:%d\n", // REMOVE Tim. - (bool)(value & kVstNanosValid), - (bool)(value & kVstPpqPosValid), - (bool)(value & kVstTempoValid), - (bool)(value & kVstBarsValid), - (bool)(value & kVstCyclePosValid), - (bool)(value & kVstTimeSigValid), - (bool)(value & kVstSmpteValid), - (bool)(value & kVstClockValid)); -#endif - memset(&_timeInfo, 0, sizeof(_timeInfo)); - - unsigned int curr_frame = MusEGlobal::audio->pos().frame(); - _timeInfo.samplePos = (double)curr_frame; - _timeInfo.sampleRate = (double)MusEGlobal::sampleRate; - _timeInfo.flags = 0; + fprintf(stderr, "VstNativeSynthIF::hostCallback master time: valid: nanos:%d ppqpos:%d tempo:%d bars:%d cyclepos:%d sig:%d smpte:%d clock:%d\n", + (bool)(value & kVstNanosValid), + (bool)(value & kVstPpqPosValid), + (bool)(value & kVstTempoValid), + (bool)(value & kVstBarsValid), + (bool)(value & kVstCyclePosValid), + (bool)(value & kVstTimeSigValid), + (bool)(value & kVstSmpteValid), + (bool)(value & kVstClockValid)); +#endif + memset(&_timeInfo, 0, sizeof(_timeInfo)); - Pos p(MusEGlobal::extSyncFlag.value() ? MusEGlobal::audio->tickPos() : curr_frame, MusEGlobal::extSyncFlag.value() ? true : false); + unsigned int curr_frame = MusEGlobal::audio->pos().frame(); + _timeInfo.samplePos = (double)curr_frame; + _timeInfo.sampleRate = (double)MusEGlobal::sampleRate; + _timeInfo.flags = 0; - if(value & kVstBarsValid) - { - int p_bar, p_beat, p_tick; - p.mbt(&p_bar, &p_beat, &p_tick); - _timeInfo.barStartPos = (double)Pos(p_bar, 0, 0).tick() / (double)MusEGlobal::config.division; - _timeInfo.flags |= kVstBarsValid; - } + Pos p(MusEGlobal::extSyncFlag.value() ? MusEGlobal::audio->tickPos() : curr_frame, MusEGlobal::extSyncFlag.value() ? true : false); - if(value & kVstTimeSigValid) - { - int z, n; - AL::sigmap.timesig(p.tick(), z, n); + if(value & kVstBarsValid) + { + int p_bar, p_beat, p_tick; + p.mbt(&p_bar, &p_beat, &p_tick); + _timeInfo.barStartPos = (double)Pos(p_bar, 0, 0).tick() / (double)MusEGlobal::config.division; + _timeInfo.flags |= kVstBarsValid; + } + + if(value & kVstTimeSigValid) + { + int z, n; + AL::sigmap.timesig(p.tick(), z, n); #ifndef VST_VESTIGE_SUPPORT - _timeInfo.timeSigNumerator = (long)z; - _timeInfo.timeSigDenominator = (long)n; + _timeInfo.timeSigNumerator = (long)z; + _timeInfo.timeSigDenominator = (long)n; #else - _timeInfo.timeSigNumerator = z; - _timeInfo.timeSigDenominator = n; + _timeInfo.timeSigNumerator = z; + _timeInfo.timeSigDenominator = n; #endif - _timeInfo.flags |= kVstTimeSigValid; - } - - if(value & kVstPpqPosValid) - { - _timeInfo.ppqPos = (double)MusEGlobal::audio->tickPos() / (double)MusEGlobal::config.division; - _timeInfo.flags |= kVstPpqPosValid; - } + _timeInfo.flags |= kVstTimeSigValid; + } + + if(value & kVstPpqPosValid) + { + _timeInfo.ppqPos = (double)MusEGlobal::audio->tickPos() / (double)MusEGlobal::config.division; + _timeInfo.flags |= kVstPpqPosValid; + } + + if(value & kVstTempoValid) + { + double tempo = MusEGlobal::tempomap.tempo(p.tick()); + _timeInfo.tempo = (60000000.0 / tempo) * double(MusEGlobal::tempomap.globalTempo())/100.0; + _timeInfo.flags |= kVstTempoValid; + } - if(value & kVstTempoValid) - { - double tempo = MusEGlobal::tempomap.tempo(p.tick()); - _timeInfo.tempo = (60000000.0 / tempo) * double(MusEGlobal::tempomap.globalTempo())/100.0; - _timeInfo.flags |= kVstTempoValid; - } - #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::hostCallback master time: sample pos:%f samplerate:%f sig num:%ld den:%ld tempo:%f\n", - _timeInfo.samplePos, _timeInfo.sampleRate, _timeInfo.timeSigNumerator, _timeInfo.timeSigDenominator, _timeInfo.tempo); + fprintf(stderr, "VstNativeSynthIF::hostCallback master time: sample pos:%f samplerate:%f sig num:%ld den:%ld tempo:%f\n", + _timeInfo.samplePos, _timeInfo.sampleRate, _timeInfo.timeSigNumerator, _timeInfo.timeSigDenominator, _timeInfo.tempo); #endif - - if(MusEGlobal::audio->isPlaying()) - _timeInfo.flags |= (kVstTransportPlaying | kVstTransportChanged); - // TODO - //if(MusEGlobal::audio->isRecording()) - // _timeInfo.flags |= (kVstTransportRecording | kVstTransportChanged); - - return (long)&_timeInfo; - } - - case audioMasterProcessEvents: - // VstEvents* in - return 0; // TODO: - case audioMasterIOChanged: - // numInputs and/or numOutputs has changed - return 0; + if(MusEGlobal::audio->isPlaying()) + _timeInfo.flags |= (kVstTransportPlaying | kVstTransportChanged); + // TODO + //if(MusEGlobal::audio->isRecording()) + // _timeInfo.flags |= (kVstTransportRecording | kVstTransportChanged); + + return (long)&_timeInfo; + } + + case audioMasterProcessEvents: + { + // VstEvents* in + VstEvents* ve = (VstEvents*)ptr; + int num_ev = ve->numEvents; +#ifdef VST_NATIVE_DEBUG + fprintf(stderr, "VstNativeSynthIF::hostCallback audioMasterProcessEvents: numEvents:%d\n", num_ev); +#endif + for(int i = 0; i < num_ev; ++i) + { + // Due to incomplete vestige midi type support, cast as VstMidiEvent first in order to get the type. + VstMidiEvent* vme = (VstMidiEvent*)ve->events[i]; + switch(vme->type) + { + // Is it really a midi event? + case kVstMidiType: + { +#ifdef VST_NATIVE_DEBUG + fprintf(stderr, " kVstMidiType: deltaFrames:%d midiData[0]:%u [1]:%u [2]:%u\n", + vme->deltaFrames, + (unsigned char)vme->midiData[0], (unsigned char)vme->midiData[1], (unsigned char)vme->midiData[2]); +#endif + if(userData->sif) + userData->sif->eventReceived(vme); + //else if(userData->pstate) // TODO Plugin midi + // vstPlug = userData->pstate->plugin; + } + break; - case audioMasterSizeWindow: - // index: width, value: height - if(resizeEditor(int(index), int(value))) - return 1; // supported. - return 0; +#ifndef VST_VESTIGE_SUPPORT + case kVstSysExType: + break; +#endif - case audioMasterGetSampleRate: - //return 0; - return MusEGlobal::sampleRate; + default: +#ifdef VST_NATIVE_DEBUG + fprintf(stderr, " unknown event type:%d\n", vme->type); +#endif + break; + } + } + //return 0; // TODO: + return 1; // Supported and processed. + } - case audioMasterGetBlockSize: - //return 0; - return MusEGlobal::segmentSize; + case audioMasterIOChanged: + // numInputs and/or numOutputs has changed + return 0; - case audioMasterGetInputLatency: - return 0; + case audioMasterSizeWindow: + { + // index: width, value: height + MusEGui::VstNativeEditor *editor = userData->sif ? userData->sif->_editor : userData->pstate->editor; + if(VstNativeSynth::resizeEditor(editor, int(index), int(value))) + return 1; // supported. + return 0; + } - case audioMasterGetOutputLatency: - return 0; + case audioMasterGetSampleRate: + //return 0; + return MusEGlobal::sampleRate; - case audioMasterGetCurrentProcessLevel: - // returns: 0: not supported, - // 1: currently in user thread (gui) - // 2: currently in audio thread (where process is called) - // 3: currently in 'sequencer' thread (midi, timer etc) - // 4: currently offline processing and thus in user thread - // other: not defined, but probably pre-empting user thread. - if(_inProcess) - return 2; - else - return 1; + case audioMasterGetBlockSize: + //return 0; + return MusEGlobal::segmentSize; - case audioMasterGetAutomationState: - // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write - // offline - return 1; // TODO: + case audioMasterGetInputLatency: + return 0; - case audioMasterOfflineStart: - case audioMasterOfflineRead: - // ptr points to offline structure, see below. return 0: error, 1 ok - return 0; + case audioMasterGetOutputLatency: + return 0; - case audioMasterOfflineWrite: - // same as read - return 0; + case audioMasterGetCurrentProcessLevel: + { + // returns: 0: not supported, + // 1: currently in user thread (gui) + // 2: currently in audio thread (where process is called) + // 3: currently in 'sequencer' thread (midi, timer etc) + // 4: currently offline processing and thus in user thread + // other: not defined, but probably pre-empting user thread. + bool inProcessNow = userData->sif ? userData->sif->_inProcess : userData->pstate->inProcess; + if(inProcessNow) + return 2; + else + return 1; + } - case audioMasterOfflineGetCurrentPass: - case audioMasterOfflineGetCurrentMetaPass: - return 0; + case audioMasterGetAutomationState: + // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write + // offline + return 1; // TODO: + + case audioMasterOfflineStart: + case audioMasterOfflineRead: + // ptr points to offline structure, see below. return 0: error, 1 ok + return 0; - case audioMasterGetVendorString: - // fills with a string identifying the vendor (max 64 char) - strcpy ((char*) ptr, "MusE"); - return 1; + case audioMasterOfflineWrite: + // same as read + return 0; - case audioMasterGetProductString: - // fills with a string with product name (max 64 char) - strcpy ((char*) ptr, "MusE Sequencer"); - return 1; + case audioMasterOfflineGetCurrentPass: + case audioMasterOfflineGetCurrentMetaPass: + return 0; - case audioMasterGetVendorVersion: - // returns vendor-specific version - return 2000; + case audioMasterGetVendorString: + // fills with a string identifying the vendor (max 64 char) + strcpy ((char*) ptr, "MusE"); + return 1; + + case audioMasterGetProductString: + // fills with a string with product name (max 64 char) + strcpy ((char*) ptr, "MusE Sequencer"); + return 1; + + case audioMasterGetVendorVersion: + // returns vendor-specific version + return 2000; - case audioMasterVendorSpecific: - // no definition, vendor specific handling - return 0; + case audioMasterVendorSpecific: + // no definition, vendor specific handling + return 0; - case audioMasterCanDo: - // string in ptr, see below - if(!strcmp((char*)ptr, "sendVstEvents") || - !strcmp((char*)ptr, "receiveVstMidiEvent") || - !strcmp((char*)ptr, "sendVstMidiEvent") || - !strcmp((char*)ptr, "sendVstTimeInfo") || - !strcmp((char*)ptr, "sizeWindow") || - !strcmp((char*)ptr, "supplyIdle")) - return 1; + case audioMasterCanDo: + // string in ptr, see below + if(!strcmp((char*)ptr, "sendVstEvents") || + !strcmp((char*)ptr, "receiveVstMidiEvent") || + !strcmp((char*)ptr, "sendVstMidiEvent") || + !strcmp((char*)ptr, "sendVstTimeInfo") || + !strcmp((char*)ptr, "sizeWindow") || + !strcmp((char*)ptr, "supplyIdle")) + return 1; #if 0 //ifndef VST_VESTIGE_SUPPORT - else - if(!strcmp((char*)ptr, "openFileSelector") || - !strcmp((char*)ptr, "closeFileSelector")) - return 1; + else + if(!strcmp((char*)ptr, "openFileSelector") || + !strcmp((char*)ptr, "closeFileSelector")) + return 1; #endif - return 0; - - case audioMasterGetLanguage: - // see enum - //return 0; - return kVstLangEnglish; + return 0; - case audioMasterGetDirectory: - // get plug directory, FSSpec on MAC, else char* - return 0; + case audioMasterGetLanguage: + // see enum + //return 0; + return kVstLangEnglish; - case audioMasterUpdateDisplay: - // something has changed, update 'multi-fx' display + case audioMasterGetDirectory: + // get plug directory, FSSpec on MAC, else char* + return 0; - //_plugin->updateParamValues(false); - //QApplication::processEvents(); // REMOVE Tim. Or keep. Commented in QTractor. + case audioMasterUpdateDisplay: + { + // something has changed, update 'multi-fx' display + + //_plugin->updateParamValues(false); + //QApplication::processEvents(); // REMOVE Tim. Or keep. Commented in QTractor. + AEffect *vstPlug = 0; + if(userData->sif) + vstPlug = userData->sif->_plugin; + else if(userData->pstate) + vstPlug = userData->pstate->plugin; - _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); // ? - - return 0; + vstPlug->dispatcher(vstPlug, effEditIdle, 0, 0, NULL, 0.0f); // ? - case audioMasterBeginEdit: - // begin of automation session (when mouse down), parameter index in - guiAutomationBegin(index); - return 1; + return 0; + } - case audioMasterEndEdit: - // end of automation session (when mouse up), parameter index in - guiAutomationEnd(index); - return 1; + case audioMasterBeginEdit: + // begin of automation session (when mouse down), parameter index in + VstNativeSynth::guiAutomationBegin(userData, index); + return 1; + + case audioMasterEndEdit: + // end of automation session (when mouse up), parameter index in + VstNativeSynth::guiAutomationEnd(userData, index); + return 1; #if 0 //ifndef VST_VESTIGE_SUPPORT - case audioMasterOpenFileSelector: - // open a fileselector window with VstFileSelect* in - return 0; - - case audioMasterCloseFileSelector: - return 0; + case audioMasterOpenFileSelector: + // open a fileselector window with VstFileSelect* in + return 0; + + case audioMasterCloseFileSelector: + return 0; #endif #ifdef VST_FORCE_DEPRECATED - - case audioMasterGetSpeakerArrangement: - // (long)input in , output in - return 0; +#ifndef VST_2_4_EXTENSIONS // deprecated in 2.4 - case audioMasterPinConnected: - // inquire if an input or output is beeing connected; - // index enumerates input or output counting from zero: - // value is 0 for input and != 0 otherwise. note: the - // return value is 0 for such that older versions - // will always return true. - //return 1; - return 0; + case audioMasterGetSpeakerArrangement: + // (long)input in , output in + return 0; - // VST 2.0 opcodes... - case audioMasterWantMidi: - // is a filter which is currently ignored - return 0; + case audioMasterPinConnected: + // inquire if an input or output is being connected; + // index enumerates input or output counting from zero: + // value is 0 for input and != 0 otherwise. note: the + // return value is 0 for such that older versions + // will always return true. + //return 1; + return 0; - case audioMasterSetTime: - // VstTimenfo* in , filter in , not supported - return 0; + // VST 2.0 opcodes... + case audioMasterWantMidi: + // is a filter which is currently ignored + return 0; - case audioMasterTempoAt: - // returns tempo (in bpm * 10000) at sample frame location passed in - return 0; // TODO: + case audioMasterSetTime: + // VstTimenfo* in , filter in , not supported + return 0; - case audioMasterGetNumAutomatableParameters: - return 0; + case audioMasterTempoAt: + // returns tempo (in bpm * 10000) at sample frame location passed in + return 0; // TODO: - case audioMasterGetParameterQuantization: - // returns the integer value for +1.0 representation, - // or 1 if full single float precision is maintained - // in automation. parameter index in (-1: all, any) - //return 0; - return 1; + case audioMasterGetNumAutomatableParameters: + return 0; - case audioMasterNeedIdle: - // plug needs idle calls (outside its editor window) - return 0; + case audioMasterGetParameterQuantization: + // returns the integer value for +1.0 representation, + // or 1 if full single float precision is maintained + // in automation. parameter index in (-1: all, any) + //return 0; + return 1; - case audioMasterGetPreviousPlug: - // input pin in (-1: first to come), returns cEffect* - return 0; + case audioMasterNeedIdle: + // plug needs idle calls (outside its editor window) + return 0; - case audioMasterGetNextPlug: - // output pin in (-1: first to come), returns cEffect* - return 0; + case audioMasterGetPreviousPlug: + // input pin in (-1: first to come), returns cEffect* + return 0; - case audioMasterWillReplaceOrAccumulate: - // returns: 0: not supported, 1: replace, 2: accumulate - //return 0; - return 1; + case audioMasterGetNextPlug: + // output pin in (-1: first to come), returns cEffect* + return 0; - case audioMasterSetOutputSampleRate: - // for variable i/o, sample rate in - return 0; + case audioMasterWillReplaceOrAccumulate: + // returns: 0: not supported, 1: replace, 2: accumulate + //return 0; + return 1; - case audioMasterSetIcon: - // void* in , format not defined yet - return 0; + case audioMasterSetOutputSampleRate: + // for variable i/o, sample rate in + return 0; - case audioMasterOpenWindow: - // returns platform specific ptr - return 0; + case audioMasterSetIcon: + // void* in , format not defined yet + return 0; - case audioMasterCloseWindow: - // close window, platform specific handle in - return 0; -#endif + case audioMasterOpenWindow: + // returns platform specific ptr + return 0; - - default: - break; - } + case audioMasterCloseWindow: + // close window, platform specific handle in return 0; - } +#endif +#endif + + + default: + break; + } + return 0; +} //--------------------------------------------------------- // idleEditor @@ -1249,50 +1458,50 @@ fprintf(stderr, "VstNativeSynthIF::idleEditor %p\n", this); #endif - _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); - if(_editor) - _editor->update(); + // REMOVE Tim. Or keep. + //_plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); + //if(_editor) + // _editor->update(); } //--------------------------------------------------------- -// nativeGuiVisible +// guiHeartBeat //--------------------------------------------------------- -bool VstNativeSynthIF::nativeGuiVisible() const - { - return _guiVisible; - } +void VstNativeSynthIF::guiHeartBeat() +{ +#ifdef VST_NATIVE_DEBUG + fprintf(stderr, "VstNativeSynthIF::guiHeartBeat %p\n", this); +#endif + + // REMOVE Tim. Or keep. + if(_plugin && _active) + { +//#ifdef VST_FORCE_DEPRECATED // REMOVE Tim. Or keep + //_plugin->dispatcher(_plugin, effIdle, 0, 0, NULL, 0.0f); +//#endif + if(_guiVisible) + { + _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); + if(_editor) + _editor->update(); + } + } +} //--------------------------------------------------------- -// guiVisible +// nativeGuiVisible //--------------------------------------------------------- -bool VstNativeSynthIF::guiVisible() const +bool VstNativeSynthIF::nativeGuiVisible() const { - return _gui && _gui->isVisible(); + return _guiVisible; } //--------------------------------------------------------- // showGui //--------------------------------------------------------- -void VstNativeSynthIF::showGui(bool v) -{ - if (v) { - if (_gui == 0) - makeGui(); - _gui->show(); - } - else { - if (_gui) - _gui->hide(); - } -} - -//--------------------------------------------------------- -// showGui -//--------------------------------------------------------- - void VstNativeSynthIF::showNativeGui(bool v) { if(!(_plugin->flags & effFlagsHasEditor)) // || v == nativeGuiVisible()) @@ -1315,14 +1524,14 @@ | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint; _editor = new MusEGui::VstNativeEditor(NULL, wflags); - _editor->open(this); + _editor->open(this, 0); } } else { if(_editor) { - delete _editor; + _editor->close(); //_editor = NULL; // No - done in editorDeleted. } } @@ -1333,6 +1542,36 @@ // editorOpened //--------------------------------------------------------- +void VstNativeSynthIF::getNativeGeometry(int*x, int*y, int*w, int*h) const +{ + if(!_editor) + { + *x=0;*y=0;*w=0;*h=0; + return; + } + + *x = _editor->x(); + *y = _editor->y(); + *w = _editor->width(); + *h = _editor->height(); +} + +//--------------------------------------------------------- +// editorOpened +//--------------------------------------------------------- + +void VstNativeSynthIF::setNativeGeometry(int x, int y, int w, int h) +{ + if(!_editor) + return; + + _editor->setGeometry(x, y, w, h); +} + +//--------------------------------------------------------- +// editorOpened +//--------------------------------------------------------- + void VstNativeSynthIF::editorOpened() { _guiVisible = true; @@ -1366,6 +1605,171 @@ } //--------------------------------------------------------- +// eventReceived +//--------------------------------------------------------- + +void VstNativeSynthIF::eventReceived(VstMidiEvent* ev) + { + const int port = synti->midiPort(); + + MidiRecordEvent event; + event.setB(0); + //event.setPort(_port); + event.setPort(port); + + // NOTE: From muse_qt4_evolution. Not done here in Muse-2 (yet). + // move all events 2*MusEGlobal::segmentSize into the future to get + // jitterfree playback + // + // cycle n-1 n n+1 + // -+----------+----------+----------+- + // ^ ^ ^ + // catch process play + // + + // 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 occurred 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->deltaFrames); +#else + event.setTime(MusEGlobal::audio->pos().frame() + ev->deltaFrames); +#endif + event.setTick(MusEGlobal::lastExtMidiSyncTick); + +// event.setChannel(*(ev->buffer) & 0xf); +// int type = *(ev->buffer) & 0xf0; +// int a = *(ev->buffer + 1) & 0x7f; +// int b = *(ev->buffer + 2) & 0x7f; + event.setChannel(ev->midiData[0] & 0xf); + int type = ev->midiData[0] & 0xf0; + int a = ev->midiData[1] & 0x7f; + int b = ev->midiData[2] & 0x7f; + event.setType(type); + + switch(type) { + case ME_NOTEON: + // REMOVE Tim. Noteoff. Added. + // Convert zero-velocity note ons to note offs as per midi spec. + if(b == 0) + event.setType(ME_NOTEOFF); + // Fall through. + + case ME_NOTEOFF: + case ME_CONTROLLER: + case ME_POLYAFTER: + //event.setA(*(ev->buffer + 1)); + //event.setB(*(ev->buffer + 2)); + event.setA(ev->midiData[1]); + event.setB(ev->midiData[2]); + break; + case ME_PROGRAM: + case ME_AFTERTOUCH: + //event.setA(*(ev->buffer + 1)); + event.setA(ev->midiData[1]); + break; + + case ME_PITCHBEND: + event.setA(((b << 7) + a) - 8192); + break; + + case ME_SYSEX: + { + //int type = *(ev->buffer) & 0xff; + int type = ev->midiData[0] & 0xff; + switch(type) + { +// TODO: Sysex NOT supported with Vestige ! +// case ME_SYSEX: +// +// // TODO: Deal with large sysex, which are broken up into chunks! +// // For now, do not accept if the last byte is not EOX, meaning it's a chunk with more chunks to follow. +// if(*(((unsigned char*)ev->buffer) + ev->size - 1) != ME_SYSEX_END) +// { +// if(MusEGlobal::debugMsg) +// printf("VstNativeSynthIF::eventReceived sysex chunks not supported!\n"); +// return; +// } +// +// //event.setTime(0); // mark as used +// event.setType(ME_SYSEX); +// event.setData((unsigned char*)(ev->buffer + 1), ev->size - 2); +// break; + case ME_MTC_QUARTER: + //if(_port != -1) + if(port != -1) + { + //MusEGlobal::midiSyncContainer.mtcInputQuarter(_port, *(ev->buffer + 1)); + MusEGlobal::midiSyncContainer.mtcInputQuarter(port, ev->midiData[1]); + } + return; + case ME_SONGPOS: + //if(_port != -1) + if(port != -1) + { + //MusEGlobal::midiSyncContainer.setSongPosition(_port, *(ev->buffer + 1) | (*(ev->buffer + 2) << 7 )); // LSB then MSB + MusEGlobal::midiSyncContainer.setSongPosition(port, ev->midiData[1] | (ev->midiData[2] << 7 )); // LSB then MSB + } + return; + //case ME_SONGSEL: + //case ME_TUNE_REQ: + //case ME_SENSE: + // return; + +// TODO: Hm, need the last frame time... Isn't that the same as audio->pos().frame() like above? +// case ME_CLOCK: +// const jack_nframes_t abs_ft = jack_last_frame_time(jc) - MusEGlobal::segmentSize + ev->time; +// midiClockInput(abs_ft); +// return; +// case ME_TICK: +// case ME_START: +// case ME_CONTINUE: +// case ME_STOP: +// { +// if(MusEGlobal::audioDevice && MusEGlobal::audioDevice->deviceType() == JACK_MIDI && _port != -1) +// { +// MusECore::JackAudioDevice* jad = static_cast(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::midiSyncContainer.realtimeSystemInput(_port, type, abs_ev_t); +// } +// } +// return; +// } + + //case ME_SYSEX_END: + //break; + // return; + default: + if(MusEGlobal::debugMsg) + printf("VstNativeSynthIF::eventReceived unsupported system event 0x%02x\n", type); + return; + } + } + //return; + break; + default: + if(MusEGlobal::debugMsg) + printf("VstNativeSynthIF::eventReceived unknown event 0x%02x\n", type); + //printf("VstNativeSynthIF::eventReceived unknown event 0x%02x size:%d buf:0x%02x 0x%02x 0x%02x ...0x%02x\n", type, ev->size, *(ev->buffer), *(ev->buffer + 1), *(ev->buffer + 2), *(ev->buffer + (ev->size - 1))); + return; + } + + #ifdef VST_NATIVE_DEBUG + printf("VstNativeSynthIF::eventReceived time:%d type:%d ch:%d A:%d B:%d\n", event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); + #endif + + // Let recordEvent handle it from here, with timestamps, filtering, gui triggering etc. + synti->recordEvent(event); + } + +//--------------------------------------------------------- // hasGui //--------------------------------------------------------- @@ -1380,7 +1784,6 @@ int VstNativeSynthIF::channels() const { - //return _plugin->numOutputs; return _plugin->numOutputs > MAX_CHANNELS ? MAX_CHANNELS : _plugin->numOutputs ; } @@ -1473,11 +1876,15 @@ fprintf(stderr, "VstNativeSynthIF::doSelectProgram bankH:%d bankL:%d prog:%d\n", bankH, bankL, prog); #endif - if(bankH == 0xff) +// // Only if there's something to change... +// if(bankH >= 128 && bankL >= 128 && prog >= 128) +// return; + + if(bankH > 127) // Map "dont care" to 0 bankH = 0; - if(bankL == 0xff) + if(bankL > 127) bankL = 0; - if(prog == 0xff) + if(prog > 127) prog = 0; int p = (bankH << 14) | (bankL << 7) | prog; @@ -1547,12 +1954,14 @@ QString VstNativeSynthIF::getPatchName(int /*chan*/, int prog, bool /*drum*/) const { - unsigned long program = prog & 0x7f; - unsigned long lbank = (prog >> 8) & 0xff; - unsigned long hbank = (prog >> 16) & 0xff; - if (lbank == 0xff) + unsigned long program = prog & 0xff; + unsigned long lbank = (prog >> 8) & 0xff; + unsigned long hbank = (prog >> 16) & 0xff; + if (program > 127) // Map "dont care" to 0 + program = 0; + if (lbank > 127) lbank = 0; - if (hbank == 0xff) + if (hbank > 127) hbank = 0; unsigned long p = (hbank << 16) | (lbank << 8) | program; unsigned long vp = (hbank << 14) | (lbank << 7) | program; @@ -1597,7 +2006,7 @@ // getParameter //--------------------------------------------------------- -float VstNativeSynthIF::getParameter(unsigned long idx) const +double VstNativeSynthIF::getParameter(unsigned long idx) const { if(idx >= _synth->inControls()) { @@ -1612,9 +2021,8 @@ // setParameter //--------------------------------------------------------- -void VstNativeSynthIF::setParameter(unsigned long idx, float value) +void VstNativeSynthIF::setParameter(unsigned long idx, double value) { - //_plugin->setParameter(_plugin, idx, value); addScheduledControlEvent(idx, value, MusEGlobal::audio->curFrame()); } @@ -1622,144 +2030,160 @@ // guiAutomationBegin //--------------------------------------------------------- -void VstNativeSynthIF::guiAutomationBegin(unsigned long param_idx) +void VstNativeSynth::guiAutomationBegin(VstNativeSynthOrPlugin *userData, unsigned long param_idx) { - AutomationType at = AUTO_OFF; - MusECore::AudioTrack* t = track(); - if(t) - at = t->automationType(); - - // FIXME TODO: This stuff should probably be sent as control FIFO events. - // And probably not safe - accessing from gui and audio threads... - - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - enableController(param_idx, false); - - int plug_id = id(); - - if(plug_id == -1) - return; - - plug_id = MusECore::genACnum(plug_id, param_idx); + //_gw[param_idx].pressed = true; //not used + AudioTrack* t = userData->sif ? userData->sif->track() : userData->pstate->pluginI->track(); + int plug_id = userData->sif ? userData->sif->id() : userData->pstate->pluginI->id(); + if(t && plug_id != -1) + { + plug_id = genACnum(plug_id, param_idx); - //if(params[param].type == GuiParam::GUI_SLIDER) - //{ - //double val = ((Slider*)params[param].actuator)->value(); - float val = param(param_idx); - // FIXME TODO: - //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) - // val = pow(10.0, val/20.0); - //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) - // val = rint(val); - //plugin->setParam(param, val); - //((DoubleLabel*)params[param].label)->setValue(val); + //if(params[param].type == GuiParam::GUI_SLIDER) + //{ + //double val = ((Slider*)params[param].actuator)->value(); + float val = userData->sif ? userData->sif->param(param_idx) : userData->pstate->pluginI->param(param_idx); + // FIXME TODO: + //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) + // val = pow(10.0, val/20.0); + //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) + // val = rint(val); + //plugin->setParam(param, val); + //((DoubleLabel*)params[param].label)->setValue(val); - if(t) - { - t->setPluginCtrlVal(plug_id, val); + //if(t) + //{ t->startAutoRecord(plug_id, val); - } - //} -// else if(params[param].type == GuiParam::GUI_SWITCH) -// { -// float val = (float)((CheckBox*)params[param].actuator)->isChecked(); -// plugin->setParam(param, val); -// -// if(t) -// { -// t->setPluginCtrlVal(plug_id, val); -// t->startAutoRecord(plug_id, val); -// } -// } + t->setPluginCtrlVal(plug_id, val); + //} + //} + // else if(params[param].type == GuiParam::GUI_SWITCH) + // { + // float val = (float)((CheckBox*)params[param].actuator)->isChecked(); + // plugin->setParam(param, val); + // + // //if(t) + // //{ + // t->startAutoRecord(plug_id, val); + // t->setPluginCtrlVal(plug_id, val); + // //} + // } + } + if(userData->sif) + { + userData->sif->enableController(param_idx, false); + } + else + { + userData->pstate->pluginI->enableController(param_idx, false); + } } //--------------------------------------------------------- // guiAutomationEnd //--------------------------------------------------------- -void VstNativeSynthIF::guiAutomationEnd(unsigned long param_idx) +void VstNativeSynth::guiAutomationEnd(VstNativeSynthOrPlugin *userData, unsigned long param_idx) { - AutomationType at = AUTO_OFF; - MusECore::AudioTrack* t = track(); - if(t) - at = t->automationType(); + AutomationType at = AUTO_OFF; + AudioTrack* t = userData->sif ? userData->sif->track() : userData->pstate->pluginI->track(); + int plug_id = userData->sif ? userData->sif->id() : userData->pstate->pluginI->id(); + if(t) + at = t->automationType(); + + if(t && plug_id != -1) + { + plug_id = genACnum(plug_id, param_idx); - // FIXME TODO: This stuff should probably be sent as control FIFO events. - // And probably not safe - accessing from gui and audio threads... - - // Special for switch - don't enable controller until transport stopped. - if ((at == AUTO_OFF) || - (at == AUTO_READ) || - (at == AUTO_TOUCH)) // && (params[param].type != GuiParam::GUI_SWITCH || // FIXME TODO - // !MusEGlobal::audio->isPlaying()) ) ) - enableController(param_idx, true); + //if(params[param].type == GuiParam::GUI_SLIDER) + //{ + //double val = ((Slider*)params[param].actuator)->value(); + float val = userData->sif ? userData->sif->param(param_idx) : userData->pstate->pluginI->param(param_idx); + // FIXME TODO: + //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) + // val = pow(10.0, val/20.0); + //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) + // val = rint(val); + t->stopAutoRecord(plug_id, val); + //} + } - int plug_id = id(); - if(!t || plug_id == -1) - return; - plug_id = MusECore::genACnum(plug_id, param_idx); + // Special for switch - don't enable controller until transport stopped. + if ((at == AUTO_OFF) || + (at == AUTO_TOUCH)) // && (params[param].type != GuiParam::GUI_SWITCH || // FIXME TODO + // !MusEGlobal::audio->isPlaying()) ) ) + { + if(userData->sif) + { + userData->sif->enableController(param_idx, true); + } + else + { + userData->pstate->pluginI->enableController(param_idx, true); + } + } - //if(params[param].type == GuiParam::GUI_SLIDER) - //{ - //double val = ((Slider*)params[param].actuator)->value(); - float val = param(param_idx); - // FIXME TODO: - //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) - // val = pow(10.0, val/20.0); - //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) - // val = rint(val); - t->stopAutoRecord(plug_id, val); - //} + //_gw[param_idx].pressed = false; //not used } - + //--------------------------------------------------------- -// guiControlEventHandler +// guiControlChanged //--------------------------------------------------------- -int VstNativeSynthIF::guiControlChanged(unsigned long param_idx, float value) +int VstNativeSynth::guiControlChanged(VstNativeSynthOrPlugin *userData, unsigned long param_idx, float value) { - #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::guiControlChanged received oscControl port:%lu val:%f\n", param_idx, value); - #endif - - if(param_idx >= _synth->inControls()) - { - fprintf(stderr, "VstNativeSynthIF::guiControlChanged: port number:%lu is out of range of index list size:%lu\n", param_idx, _synth->inControls()); - return 0; - } - - ControlEvent ce; - ce.unique = false; // Not used for native vst. - ce.fromGui = true; // It came form the plugin's own GUI. - ce.idx = param_idx; - ce.value = value; - // don't use timestamp(), because it's circular, which is making it impossible to deal - // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. - ce.frame = MusEGlobal::audio->curFrame(); - - if(_controlFifo.put(ce)) - { - fprintf(stderr, "VstNativeSynthIF::guiControlChanged: fifo overflow: in control number:%lu\n", param_idx); - } + VstNativeSynth *synth = userData->sif ? userData->sif->_synth : userData->pstate->pluginWrapper->_synth; +#ifdef VST_NATIVE_DEBUG + fprintf(stderr, "VstNativeSynth::guiControlChanged received oscControl port:%lu val:%f\n", param_idx, value); +#endif - // FIXME TODO: This stuff should probably be sent as control FIFO events. - // And probably not safe - accessing from gui and audio threads... - - // Record automation: - // Take care of this immediately rather than in the fifo processing. - if(id() != -1) - { - unsigned long pid = genACnum(id(), param_idx); - AutomationType at = synti->automationType(); + if(param_idx >= synth->inControls()) + { + fprintf(stderr, "VstNativeSynth::guiControlChanged: port number:%lu is out of range of index list size:%lu\n", param_idx, synth->inControls()); + return 0; + } - if ((at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying())) - enableController(param_idx, false); + // Record automation: + // Take care of this immediately rather than in the fifo processing. + int plugId = userData->sif ? userData->sif->id() : userData->pstate->pluginI->id(); + if(plugId != -1) + { + unsigned long pid = genACnum(plugId, param_idx); + if(userData->sif) + { + userData->sif->synti->recordAutomation(pid, value); + } + else + { + userData->pstate->pluginI->track()->recordAutomation(pid, value); + } + } - synti->recordAutomation(pid, value); - } + // Schedules a timed control change: + ControlEvent ce; + ce.unique = false; // Not used for native vst. + ce.fromGui = true; // It came from the plugin's own GUI. + ce.idx = param_idx; + ce.value = value; + // Don't use timestamp(), because it's circular, which is making it impossible to deal + // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. + ce.frame = MusEGlobal::audio->curFrame(); + + ControlFifo &cfifo = userData->sif ? userData->sif->_controlFifo : userData->pstate->pluginI->_controlFifo; + + if(cfifo.put(ce)) + fprintf(stderr, "VstNativeSynthIF::guiControlChanged: fifo overflow: in control number:%lu\n", param_idx); + + if(userData->sif) + { + userData->sif->enableController(param_idx, false); + } + else + { + userData->pstate->pluginI->enableController(param_idx, false); + } - return 0; + return 0; } //--------------------------------------------------------- @@ -1768,7 +2192,7 @@ void VstNativeSynthIF::write(int level, Xml& xml) const { -#ifndef VST_VESTIGE_SUPPORT +//#ifndef VST_VESTIGE_SUPPORT if(_synth->hasChunks()) { //--------------------------------------------- @@ -1777,7 +2201,7 @@ fprintf(stderr, "%s: commencing chunk data dump, plugin api version=%d\n", name().toLatin1().constData(), _synth->vstVersion()); unsigned long len = 0; void* p = 0; - len = dispatch(effGetChunk, 0, 0, &p, 0.0); // index 0: is bank 1: is program + len = dispatch(23 /* effGetChunk */, 0, 0, &p, 0.0); // index 0: is bank 1: is program if (len) { xml.tag(level++, "midistate version=\"%d\"", SYNTH_MIDI_STATE_SAVE_VERSION); @@ -1802,9 +2226,9 @@ xml.etag(level--, "midistate"); } } -#else - fprintf(stderr, "support for vst chunks not compiled in!\n"); -#endif +//#else +// fprintf(stderr, "support for vst chunks not compiled in!\n"); +//#endif //--------------------------------------------- // dump current state of synth @@ -1812,10 +2236,7 @@ int params = _plugin->numParams; for (int i = 0; i < params; ++i) - { - float f = _plugin->getParameter(_plugin, i); - xml.floatTag(level, "param", f); - } + xml.doubleTag(level, "param", _plugin->getParameter(_plugin, i)); } //--------------------------------------------------------- @@ -1844,7 +2265,7 @@ // getData //--------------------------------------------------------- -bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEvent* event) +bool VstNativeSynthIF::processEvent(const MidiPlayEvent& e, VstMidiEvent* event) { int type = e.type(); int chn = e.channel(); @@ -1855,92 +2276,162 @@ fprintf(stderr, "VstNativeSynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", type, chn, a, b); #endif + // REMOVE Tim. Noteoff. Added. + const MidiInstrument::NoteOffMode nom = synti->noteOffMode(); + switch(type) { - case MusECore::ME_NOTEON: + case ME_NOTEON: #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_NOTEON\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_NOTEON\n"); #endif - setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + + // REMOVE Tim. Noteoff. Added. + if(b == 0) + { + // Handle zero-velocity note ons. Technically this is an error because internal midi paths + // are now all 'note-off' without zero-vel note ons - they're converted to note offs. + // Nothing should be setting a Note type Event's on velocity to zero. + // But just in case... If we get this warning, it means there is still code to change. + fprintf(stderr, "VstNativeSynthIF::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", e.time(), e.type(), chn, a, b); + switch(nom) + { + // Instrument uses note offs. Convert to zero-vel note off. + case MidiInstrument::NoteOffAll: + //if(MusEGlobal::midiOutputTrace) + // fprintf(stderr, "MidiOut: VST_Native: Following event will be converted to zero-velocity note off:\n"); + setVstEvent(event, (ME_NOTEOFF | chn) & 0xff, a & 0x7f, 0); + break; + + // Instrument uses no note offs at all. Send as-is. + case MidiInstrument::NoteOffNone: + // Instrument converts all note offs to zero-vel note ons. Send as-is. + case MidiInstrument::NoteOffConvertToZVNoteOn: + setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + break; + } + } + else + + setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + break; - case MusECore::ME_NOTEOFF: + case ME_NOTEOFF: #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_NOTEOFF\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_NOTEOFF\n"); #endif - setVstEvent(event, (type | chn) & 0xff, a & 0x7f, 0); + + // REMOVE Tim. Noteoff. Changed. +// setVstEvent(event, (type | chn) & 0xff, a & 0x7f, 0); + switch(nom) + { + // Instrument uses note offs. Send as-is. + case MidiInstrument::NoteOffAll: + setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b); + break; + + // Instrument uses no note offs at all. Send nothing. Eat up the event - return false. + case MidiInstrument::NoteOffNone: + return false; + + // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. + case MidiInstrument::NoteOffConvertToZVNoteOn: + //if(MusEGlobal::midiOutputTrace) + // fprintf(stderr, "MidiOut: VST_Native: Following event will be converted to zero-velocity note on:\n"); + setVstEvent(event, (ME_NOTEON | chn) & 0xff, a & 0x7f, 0); + break; + } + break; - case MusECore::ME_PROGRAM: + // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore. + case ME_PROGRAM: { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_PROGRAM\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_PROGRAM\n"); #endif - int bankH = (a >> 16) & 0xff; - int bankL = (a >> 8) & 0xff; - int prog = a & 0xff; - synti->_curBankH = bankH; - synti->_curBankL = bankL; - synti->_curProgram = prog; - doSelectProgram(bankH, bankL, prog); + int hb, lb; + synti->currentProg(chn, NULL, &lb, &hb); + synti->setCurrentProg(chn, a & 0xff, lb, hb); + doSelectProgram(hb, lb, a); return false; // Event pointer not filled. Return false. } break; - case MusECore::ME_CONTROLLER: + case ME_CONTROLLER: { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER\n"); #endif - if((a == 0) || (a == 32)) + // Our internal hwCtrl controllers support the 'unknown' value. + // Don't send 'unknown' values to the driver. Ignore and return no error. + if(b == CTRL_VAL_UNKNOWN) return false; - - if(a == MusECore::CTRL_PROGRAM) + + if(a == CTRL_PROGRAM) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PROGRAM\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); #endif int bankH = (b >> 16) & 0xff; int bankL = (b >> 8) & 0xff; int prog = b & 0xff; - - synti->_curBankH = bankH; - synti->_curBankL = bankL; - synti->_curProgram = prog; + synti->setCurrentProg(chn, prog, bankL, bankH); doSelectProgram(bankH, bankL, prog); return false; // Event pointer not filled. Return false. } - if(a == MusECore::CTRL_PITCH) + if(a == CTRL_HBANK) + { + int lb, pr; + synti->currentProg(chn, &pr, &lb, NULL); + synti->setCurrentProg(chn, pr, lb, b & 0xff); + doSelectProgram(b, lb, pr); + // Event pointer not filled. Return false. + return false; + } + + if(a == CTRL_LBANK) + { + int hb, pr; + synti->currentProg(chn, &pr, NULL, &hb); + synti->setCurrentProg(chn, pr, b & 0xff, hb); + doSelectProgram(hb, b, pr); + // Event pointer not filled. Return false. + return false; + } + + if(a == CTRL_PITCH) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PITCH\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); #endif int v = b + 8192; - setVstEvent(event, (type | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f); + setVstEvent(event, (ME_PITCHBEND | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f); return true; } - if(a == MusECore::CTRL_AFTERTOUCH) + if(a == CTRL_AFTERTOUCH) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_AFTERTOUCH\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_AFTERTOUCH\n"); #endif - setVstEvent(event, (type | chn) & 0xff, b & 0x7f); + setVstEvent(event, (ME_AFTERTOUCH | chn) & 0xff, b & 0x7f); return true; } - if((a | 0xff) == MusECore::CTRL_POLYAFTER) + if((a | 0xff) == CTRL_POLYAFTER) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_POLYAFTER\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_POLYAFTER\n"); #endif - setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); + setVstEvent(event, (ME_POLYAFTER | chn) & 0xff, a & 0x7f, b & 0x7f); return true; } #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is:%d\n", a); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is:%d\n", a); #endif // Regular controller. Pass it on. @@ -1952,14 +2443,14 @@ // // const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; // -// MusECore::ciMidiCtl2LadspaPort ip = synth->midiCtl2PortMap.find(a); +// ciMidiCtl2LadspaPort ip = synth->midiCtl2PortMap.find(a); // // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)? // // NOTE: There's no way to tell which of these controllers is supported by the plugin. // // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port. // if(ip == synth->midiCtl2PortMap.end()) // { // int ctlnum = a; -// if(MusECore::midiControllerType(a) != MusECore::MidiController::Controller7) +// if(midiControllerType(a) != MidiController::Controller7) // return false; // Event pointer not filled. Return false. // else // { @@ -1994,7 +2485,7 @@ // return false; // // // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. -// ctlnum = k + (MusECore::CTRL_NRPN14_OFFSET + 0x2000); +// ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); // } // else // { @@ -2020,7 +2511,7 @@ // else // if(DSSI_IS_NRPN(ctlnum)) // { -// ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET; +// ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; // // #ifdef VST_NATIVE_DEBUG // fprintf(stderr, "VstNativeSynthIF::processEvent is NRPN ctlnum:%x(h) %d(d)\n", ctlnum, ctlnum); @@ -2047,22 +2538,22 @@ // return false; } break; - case MusECore::ME_PITCHBEND: + case ME_PITCHBEND: { int v = a + 8192; setVstEvent(event, (type | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f); } break; - case MusECore::ME_AFTERTOUCH: + case ME_AFTERTOUCH: setVstEvent(event, (type | chn) & 0xff, a & 0x7f); break; - case MusECore::ME_POLYAFTER: + case ME_POLYAFTER: setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; - case MusECore::ME_SYSEX: + case ME_SYSEX: { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_SYSEX\n"); #endif const unsigned char* data = e.data(); @@ -2079,7 +2570,7 @@ { if(_synth->hasChunks()) { -#ifndef VST_VESTIGE_SUPPORT +//#ifndef VST_VESTIGE_SUPPORT int chunk_flags = data[9]; if(chunk_flags & VST_NATIVE_CHUNK_FLAG_COMPRESSED) fprintf(stderr, "chunk flags:%x compressed chunks not supported yet.\n", chunk_flags); @@ -2087,11 +2578,11 @@ { fprintf(stderr, "%s: loading chunk from sysex!\n", name().toLatin1().constData()); // 10 = 2 bytes header + "VSTSAVE" + 1 byte flags (compression etc) - dispatch(effSetChunk, 0, e.len()-10, (void*)(data+10), 0.0); // index 0: is bank 1: is program + dispatch(24 /* effSetChunk */, 0, e.len()-10, (void*)(data+10), 0.0); // index 0: is bank 1: is program } -#else - fprintf(stderr, "support for vst chunks not compiled in!\n"); -#endif +//#else +// fprintf(stderr, "support for vst chunks not compiled in!\n"); +//#endif } // Event not filled. return false; @@ -2109,7 +2600,7 @@ if (QString((const char*)e.data()).startsWith("PARAMSAVE")) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX PARAMSAVE\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_SYSEX PARAMSAVE\n"); #endif unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE" @@ -2170,7 +2661,7 @@ //len += 2; // NOTE: There is a limit on the size of a sysex. Got this: - // "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX" + // "VstNativeSynthIF::processEvent midi event is ME_SYSEX" // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding" // That might be ALSA doing that. // snd_seq_ev_clear(event); @@ -2193,43 +2684,41 @@ //--------------------------------------------------------- // getData +// If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- -iMPEvent VstNativeSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent start_event, unsigned pos, int ports, unsigned nframes, float** buffer) +bool VstNativeSynthIF::getData(MidiPort* /*mp*/, unsigned pos, int ports, unsigned nframes, float** buffer) { - // We may not be using nevents all at once - this will be just the maximum. - unsigned long nevents = el->size() + synti->eventFifo.getSize(); - VstMidiEvent events[nevents]; - char evbuf[sizeof(VstMidiEvent*) * nevents + sizeof(VstEvents)]; - VstEvents *vst_events = (VstEvents*)evbuf; - vst_events->numEvents = 0; - vst_events->reserved = 0; - - int frameOffset = MusEGlobal::audio->getFrameOffset(); - unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); + const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); #ifdef VST_NATIVE_DEBUG_PROCESS - fprintf(stderr, "VstNativeSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu nevents:%lu\n", pos, ports, nframes, syncFrame, nevents); + fprintf(stderr, "VstNativeSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu\n", pos, ports, nframes, syncFrame); #endif - unsigned long nop, k; - nop = ((unsigned long) ports) > _synth->outPorts() ? _synth->outPorts() : ((unsigned long) ports); + // All ports must be connected to something! + const unsigned long in_ports = _synth->inPorts(); + const unsigned long out_ports = _synth->outPorts(); + const unsigned long nop = ((unsigned long) ports) > out_ports ? out_ports : ((unsigned long) ports); + unsigned long sample = 0; - // I read that some plugins do not like changing sample run length (compressors etc). Some are OK with it. - // TODO: In order to support this effectively, must be user selectable, per-plugin. ENABLED for now. - const bool usefixedrate = false; - unsigned long fixedsize = nframes; + const bool usefixedrate = (requiredFeatures() & Plugin::FixedBlockSize); // For now, the fixed size is clamped to the audio buffer size. // TODO: We could later add slower processing over several cycles - // so that users can select a small audio period but a larger control period. - if(fixedsize > nframes) - fixedsize = nframes; + const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; + const unsigned long min_per_mask = min_per-1; // min_per must be power of 2 - unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; // Must be power of 2 ! - if(min_per > nframes) - min_per = nframes; + AudioTrack* atrack = track(); + const AutomationType at = atrack->automationType(); + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + const unsigned long in_ctrls = _synth->inControls(); + CtrlListList* cll = atrack->controller(); + ciCtrlList icl_first; + const int plug_id = id(); + if(plug_id != -1 && ports != 0) // Don't bother if not 'running'. + icl_first = cll->lower_bound(genACnum(plug_id, 0)); // Inform the host callback we are in the audio thread. _inProcess = true; @@ -2237,73 +2726,46 @@ #ifdef VST_NATIVE_DEBUG_PROCESS fprintf(stderr, "VstNativeSynthIF::getData: Handling inputs...\n"); #endif - - // Handle inputs... - if(!((MusECore::AudioTrack*)synti)->noInRoute()) + + bool used_in_chan_array[in_ports]; // Don't bother initializing if not 'running'. + + // Don't bother if not 'running'. + if(ports != 0) { - RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes(); - iRoute i = irl->begin(); - if(!i->track->isMidiTrack()) + // Initialize the array. + for(unsigned long i = 0; i < in_ports; ++i) + used_in_chan_array[i] = false; + + if(!atrack->noInRoute()) { - int ch = i->channel == -1 ? 0 : i->channel; - int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; - int chs = i->channels == -1 ? 0 : i->channels; - - if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts()) + RouteList *irl = atrack->inRoutes(); + for(ciRoute i = irl->begin(); i != irl->end(); ++i) { - int h = remch + chs; - for(int j = remch; j < h; ++j) - _iUsedIdx[j] = true; - - ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); - } - } - - ++i; - for(; i != irl->end(); ++i) - { - if(i->track->isMidiTrack()) - continue; - - int ch = i->channel == -1 ? 0 : i->channel; - int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; - int chs = i->channels == -1 ? 0 : i->channels; - - if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts()) - { - bool u1 = _iUsedIdx[remch]; - if(chs >= 2) - { - bool u2 = _iUsedIdx[remch + 1]; - if(u1 && u2) - ((MusECore::AudioTrack*)i->track)->addData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); - else - if(!u1 && !u2) - ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); - else - { - if(u1) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]); - else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]); - - if(u2) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]); - else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]); - } - } - else - { - if(u1) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]); - else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]); - } - - int h = remch + chs; - for(int j = remch; j < h; ++j) - _iUsedIdx[j] = true; + if(i->track->isMidiTrack()) + continue; + // Only this synth knows how many destination channels there are, + // while only the track knows how many source channels there are. + // So take care of the destination channels here, and let the track handle the source channels. + const int dst_ch = i->channel <= -1 ? 0 : i->channel; + if((unsigned long)dst_ch >= in_ports) + continue; + const int dst_chs = i->channels <= -1 ? in_ports : i->channels; + //const int total_ins = atrack->totalRoutableInputs(Route::TRACK_ROUTE); + const int src_ch = i->remoteChannel <= -1 ? 0 : i->remoteChannel; + const int src_chs = i->channels; + + int fin_dst_chs = dst_chs; + if((unsigned long)(dst_ch + fin_dst_chs) > in_ports) + fin_dst_chs = in_ports - dst_ch; + + static_cast(i->track)->copyData(pos, + dst_ch, dst_chs, fin_dst_chs, + src_ch, src_chs, + nframes, &_audioInBuffers[0], + false, used_in_chan_array); + const int nxt_ch = dst_ch + fin_dst_chs; + for(int ch = dst_ch; ch < nxt_ch; ++ch) + used_in_chan_array[ch] = true; } } } @@ -2312,9 +2774,11 @@ fprintf(stderr, "VstNativeSynthIF::getData: Processing automation control values...\n"); #endif + int cur_slice = 0; while(sample < nframes) { - unsigned long nsamp = usefixedrate ? fixedsize : nframes - sample; + unsigned long nsamp = nframes - sample; + const unsigned long slice_frame = pos + sample; // // Process automation control values, while also determining the maximum acceptable @@ -2322,51 +2786,62 @@ // 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) + if(ports != 0) // Don't bother if not 'running'. { - unsigned long frame = pos + sample; - AutomationType at = AUTO_OFF; - at = synti->automationType(); - bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; - AudioTrack* track = (static_cast(synti)); - int nextFrame; - const unsigned long in_ctrls = _synth->inControls(); + ciCtrlList icl = icl_first; for(unsigned long k = 0; k < in_ctrls; ++k) { - const float v = track->controller()->value(genACnum(id(), k), frame, - no_auto || !_controls[k].enCtrl || !_controls[k].en2Ctrl, - &nextFrame); - if(_controls[k].val != v) + CtrlList* cl = (cll && plug_id != -1 && icl != cll->end()) ? icl->second : NULL; + CtrlInterpolate& ci = _controls[k].interp; + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)) ) ) { - _controls[k].val = v; -#ifndef VST_VESTIGE_SUPPORT - if(dispatch(effCanBeAutomated, k, 0, NULL, 0.0f) == 1) + if(cl && plug_id != -1 && (unsigned long)cl->id() == genACnum(plug_id, k)) { -#endif - if(_plugin->getParameter(_plugin, k) != v) - _plugin->setParameter(_plugin, k, v); -#ifndef VST_VESTIGE_SUPPORT + cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); + if(icl != cll->end()) + ++icl; } - #ifdef VST_NATIVE_DEBUG else - fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), k); - #endif -#endif + { + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = _controls[k].val; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; + } } - -#ifdef VST_NATIVE_DEBUG_PROCESS - fprintf(stderr, "VstNativeSynthIF::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) + else + { + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. + { + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + if(cl && cll && icl != cll->end()) + ++icl; + } + + if(!usefixedrate && MusEGlobal::audio->isPlaying()) { - // 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 samps = nsamp; + if(ci.eFrame != -1) + samps = (unsigned long)ci.eFrame - slice_frame; + + if(!ci.doInterp && samps > 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_mask; + if((samps & min_per_mask) != 0) samps += min_per; } else @@ -2374,12 +2849,38 @@ if(samps < nsamp) nsamp = samps; + + } + + float new_val; + if(ci.doInterp && cl) + new_val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); + else + new_val = ci.sVal; + if(_controls[k].val != new_val) + { + _controls[k].val = new_val; + if(dispatch(26 /*effCanBeAutomated*/, k, 0, NULL, 0.0f) == 1) + { + if(_plugin->getParameter(_plugin, k) != new_val) + _plugin->setParameter(_plugin, k, new_val); + } + #ifdef VST_NATIVE_DEBUG + else + fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), k); + #endif } + +#ifdef VST_NATIVE_DEBUG_PROCESS + fprintf(stderr, "VstNativeSynthIF::getData k:%lu sample:%lu frame:%lu ci.eFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); +#endif + } + } + #ifdef VST_NATIVE_DEBUG_PROCESS fprintf(stderr, "VstNativeSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp); #endif - } bool found = false; unsigned long frame = 0; @@ -2388,7 +2889,7 @@ // Get all control ring buffer items valid for this time period... while(!_controlFifo.isEmpty()) { - ControlEvent v = _controlFifo.peek(); + const ControlEvent& v = _controlFifo.peek(); // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; @@ -2409,236 +2910,229 @@ continue; } - 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. + 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)) // Fixed rate and must reply to all. break; - _controlFifo.remove(); // Done with the ring buffer's item. Remove it. - if(v.idx >= _synth->inControls()) // Sanity check. + if(v.idx >= in_ctrls) // Sanity check. + { + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. break; + } + found = true; frame = evframe; index = v.idx; - //if(_controls[v.idx].val != v.value) // Mm nah, force it always. REMOVE Tim. - //{ - _controls[v.idx].val = v.value; -#ifndef VST_VESTIGE_SUPPORT - if(dispatch(effCanBeAutomated, v.idx, 0, NULL, 0.0f) == 1) - { -#endif - if(v.value != _plugin->getParameter(_plugin, v.idx)) - _plugin->setParameter(_plugin, v.idx, v.value); -#ifndef VST_VESTIGE_SUPPORT - } - #ifdef VST_NATIVE_DEBUG - else - fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), v.idx); - #endif -#endif - // Need to update the automation value, otherwise it overwrites later with the last automation value. - if(id() != -1) - synti->setPluginCtrlVal(genACnum(id(), v.idx), v.value); - //} + if(ports == 0) // Don't bother if not 'running'. + { + _controls[v.idx].val = v.value; // Might as well at least update these. +// #ifndef VST_VESTIGE_SUPPORT +// if(dispatch(effCanBeAutomated, v.idx, 0, NULL, 0.0f) == 1) +// { +// #endif +// if(v.value != _plugin->getParameter(_plugin, v.idx)) +// _plugin->setParameter(_plugin, v.idx, v.value); +// #ifndef VST_VESTIGE_SUPPORT +// } +// #ifdef VST_NATIVE_DEBUG +// else +// fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), v.idx); +// #endif +// #endif + } + else + { + CtrlInterpolate* ci = &_controls[v.idx].interp; + // Tell it to stop the current ramp at this frame, when it does stop, set this value: + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; + } + + // Need to update the automation value, otherwise it overwrites later with the last automation value. + if(plug_id != -1) + synti->setPluginCtrlVal(genACnum(plug_id, v.idx), v.value); + + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. } 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. + if(sample + nsamp > nframes) // Safety check. nsamp = nframes - sample; // 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; - - nevents = 0; - // Process event list events... - for(; start_event != el->end(); ++start_event) + if(nsamp != 0) { - #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::getData eventlist event time:%d pos:%u sample:%lu nsamp:%lu frameOffset:%d\n", start_event->time(), pos, sample, nsamp, frameOffset); - #endif + unsigned long nevents = 0; + // Get the state of the stop flag. + const bool do_stop = synti->stopFlag(); - if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again... + MidiPlayEvent buf_ev; + + // Transfer the user lock-free buffer events to the user sorted multi-set. + // False = don't use the size snapshot, but update it. + const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); + for(unsigned int i = 0; i < usr_buf_sz; ++i) { - #ifdef VST_NATIVE_DEBUG - fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample); - #endif - break; + if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) + synti->_outUserEvents.insert(buf_ev); } - - // 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) - { - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[synti->midiPort()]; - if(start_event->type() == MusECore::ME_CONTROLLER) - { - int da = start_event->dataA(); - int db = start_event->dataB(); - db = mp->limitValToInstrCtlRange(da, db); - if(!mp->setHwCtrlState(start_event->channel(), da, db)) - continue; - } - else if(start_event->type() == MusECore::ME_PITCHBEND) - { - int da = mp->limitValToInstrCtlRange(MusECore::CTRL_PITCH, start_event->dataA()); - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PITCH, da)) - continue; - } - else if(start_event->type() == MusECore::ME_AFTERTOUCH) - { - int da = mp->limitValToInstrCtlRange(MusECore::CTRL_AFTERTOUCH, start_event->dataA()); - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_AFTERTOUCH, da)) - continue; - } - else if(start_event->type() == MusECore::ME_POLYAFTER) - { - int ctl = (MusECore::CTRL_POLYAFTER & ~0xff) | (start_event->dataA() & 0x7f); - int db = mp->limitValToInstrCtlRange(ctl, start_event->dataB()); - if(!mp->setHwCtrlState(start_event->channel(), ctl , db)) - continue; - } - else if(start_event->type() == MusECore::ME_PROGRAM) - { - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PROGRAM, start_event->dataA())) - continue; - } + + // Transfer the playback lock-free buffer events to the playback sorted multi-set. + const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); + for(unsigned int i = 0; i < pb_buf_sz; ++i) + { + // Are we stopping? Just remove the item. + if(do_stop) + synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); + // Otherwise get the item. + else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) + synti->_outPlaybackEvents.insert(buf_ev); } - - // Returns false if the event was not filled. It was handled, but some other way. - if(processEvent(*start_event, &events[nevents])) + + // Are we stopping? + if(do_stop) { - // Time-stamp the event. - int ft = start_event->time() - frameOffset - pos - sample; - if(ft < 0) - ft = 0; - - if (ft >= int(nsamp)) - { - fprintf(stderr, "VstNativeSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", start_event->time(), pos, frameOffset, ft, sample, nsamp); - ft = nsamp - 1; - } + // Transport has stopped, purge ALL further scheduled playback events now. + synti->_outPlaybackEvents.clear(); + // Reset the flag. + synti->setStopFlag(false); + } + + // Count how many events we need. + for(ciMPEvent impe = synti->_outPlaybackEvents.begin(); impe != synti->_outPlaybackEvents.end(); ++impe) + { + const MidiPlayEvent& e = *impe; + if(e.time() >= (syncFrame + sample + nsamp)) + break; + ++nevents; + } + for(ciMPEvent impe = synti->_outUserEvents.begin(); impe != synti->_outUserEvents.end(); ++impe) + { + const MidiPlayEvent& e = *impe; + if(e.time() >= (syncFrame + sample + nsamp)) + break; + ++nevents; + } + + VstMidiEvent events[nevents]; + char evbuf[sizeof(VstMidiEvent*) * nevents + sizeof(VstEvents)]; + VstEvents *vst_events = (VstEvents*)evbuf; + vst_events->numEvents = 0; + vst_events->reserved = 0; + + iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); + iMPEvent impe_us = synti->_outUserEvents.begin(); + bool using_pb; + + unsigned long event_counter = 0; + while(1) + { + if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) + using_pb = *impe_pb < *impe_us; + else if(impe_pb != synti->_outPlaybackEvents.end()) + using_pb = true; + else if(impe_us != synti->_outUserEvents.end()) + using_pb = false; + else break; + + const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us; #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::getData eventlist: ft:%d current nevents:%lu\n", ft, nevents); + fprintf(stderr, "VstNativeSynthIF::getData eventFifos event time:%d\n", e.time()); #endif - vst_events->events[nevents] = (VstEvent*)&events[nevents]; - events[nevents].deltaFrames = ft; - ++nevents; - } - } + // Event is for future? + if(e.time() >= (sample + nsamp + syncFrame)) + break; - // Now process putEvent events... - while(!synti->eventFifo.isEmpty()) - { - MusECore::MidiPlayEvent e = synti->eventFifo.peek(); + if(ports != 0) // Don't bother if not 'running'. + { + // Returns false if the event was not filled. It was handled, but some other way. + if(processEvent(e, &events[event_counter])) + { + // Time-stamp the event. + unsigned int ft = (e.time() < syncFrame) ? 0 : e.time() - syncFrame; + ft = (ft < sample) ? 0 : ft - sample; - #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::getData eventFifo event time:%d\n", e.time()); - #endif + if(ft >= nsamp) + { + fprintf(stderr, "VstNativeSynthIF::getData: eventFifos event time:%d out of range. pos:%d syncFrame:%u ft:%u sample:%lu nsamp:%lu\n", + e.time(), pos, syncFrame, ft, sample, nsamp); + ft = nsamp - 1; + } + vst_events->events[event_counter] = (VstEvent*)&events[event_counter]; + events[event_counter].deltaFrames = ft; - if(e.time() >= (pos + sample + nsamp + frameOffset)) - break; + ++event_counter; + } + } + // Done with ring buffer's event. Remove it. + // C++11. + if(using_pb) + impe_pb = synti->_outPlaybackEvents.erase(impe_pb); + else + impe_us = synti->_outUserEvents.erase(impe_us); + } + + if(event_counter < nevents) + nevents = event_counter; + + #ifdef VST_NATIVE_DEBUG_PROCESS + fprintf(stderr, "VstNativeSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); + #endif - synti->eventFifo.remove(); // Done with ring buffer's event. Remove it. - // Returns false if the event was not filled. It was handled, but some other way. - if(processEvent(e, &events[nevents])) - { - // Time-stamp the event. - int ft = e.time() - frameOffset - pos - sample; - if(ft < 0) - ft = 0; - if (ft >= int(nsamp)) + if(ports != 0) // Don't bother if not 'running'. + { + // Set the events pointer. + if(nevents > 0) { - fprintf(stderr, "VstNativeSynthIF::getData: eventFifo event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", e.time(), pos, frameOffset, ft, sample, nsamp); - ft = nsamp - 1; + vst_events->numEvents = nevents; + dispatch(effProcessEvents, 0, 0, vst_events, 0.0f); } - vst_events->events[nevents] = (VstEvent*)&events[nevents]; - events[nevents].deltaFrames = ft; - ++nevents; - } - } - - #ifdef VST_NATIVE_DEBUG_PROCESS - fprintf(stderr, "VstNativeSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); - #endif + float* in_bufs[in_ports]; + float* out_bufs[out_ports]; - // Set the events pointer. - if(nevents > 0) - { - vst_events->numEvents = nevents; - dispatch(effProcessEvents, 0, 0, vst_events, 0.0f); - } + // Connect the given buffers directly to the ports, up to a max of synth ports. + for(unsigned long k = 0; k < nop; ++k) + out_bufs[k] = buffer[k] + sample; + // Connect the remaining ports to some local buffers (not used yet). + for(unsigned long k = nop; k < out_ports; ++k) + out_bufs[k] = _audioOutBuffers[k] + sample; + // Connect all inputs either to some local buffers, or a silence buffer. + for(unsigned long k = 0; k < in_ports; ++k) + { + if(used_in_chan_array[k]) + in_bufs[k] = _audioInBuffers[k] + sample; + else + in_bufs[k] = _audioInSilenceBuf + sample; + } - float* in_bufs[_synth->inPorts()]; - float* out_bufs[_synth->outPorts()]; - - k = 0; - // Connect the given buffers directly to the ports, up to a max of synth ports. - for(; k < nop; ++k) - out_bufs[k] = buffer[k] + sample; - // Connect the remaining ports to some local buffers (not used yet). - const unsigned long op =_synth->outPorts(); - for(; k < op; ++k) - out_bufs[k] = _audioOutBuffers[k] + sample; - // Connect all inputs either to some local buffers, or a silence buffer. - const unsigned long ip =_synth->inPorts(); - for(k = 0; k < ip; ++k) - { - if(_iUsedIdx[k]) - { - _iUsedIdx[k] = false; // Reset - in_bufs[k] = _audioInBuffers[k] + sample; + // Run the synth for a period of time. This processes events and gets/fills our local buffers... + if((_plugin->flags & effFlagsCanReplacing) && _plugin->processReplacing) + { + _plugin->processReplacing(_plugin, in_bufs, out_bufs, nsamp); + } } - else - in_bufs[k] = _audioInSilenceBuf + sample; + + sample += nsamp; } - // Run the synth for a period of time. This processes events and gets/fills our local buffers... - if((_plugin->flags & effFlagsCanReplacing) && _plugin->processReplacing) - { - _plugin->processReplacing(_plugin, in_bufs, out_bufs, nsamp); - } -#if 0 //ifdef VST_FORCE_DEPRECATED - else - { - // TODO: This is not right, we don't accumulate data here. Depricated now, and frowned upon anyway... - if(_plugin->process) - _plugin->process(_plugin, in_bufs, out_bufs, nsamp); - } -#endif - - sample += nsamp; + ++cur_slice; // Slice is done. Moving on to any next slice now... } - - // Inform the host callback we will be no longer in the audio thread. - _inProcess = true; - return start_event; -} - -//--------------------------------------------------------- -// putEvent -//--------------------------------------------------------- - -bool VstNativeSynthIF::putEvent(const MidiPlayEvent& ev) - { - #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::putEvent midi event time:%d chn:%d a:%d b:%d\n", ev.time(), ev.channel(), ev.dataA(), ev.dataB()); - #endif - - if (MusEGlobal::midiOutputTrace) - ev.dump(); - return synti->eventFifo.put(ev); - } + // Inform the host callback we will be no longer in the audio thread. + _inProcess = false; + return true; +} //-------------------------------- // Methods for PluginIBase: @@ -2652,8 +3146,6 @@ QString VstNativeSynthIF::fileName() const { return _synth ? _synth->fileName() : QString(); } void VstNativeSynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; } bool VstNativeSynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl;} -void VstNativeSynthIF::enable2Controller(unsigned long i, bool v) { _controls[i].en2Ctrl = v; } -bool VstNativeSynthIF::controllerEnabled2(unsigned long i) const { return _controls[i].en2Ctrl; } void VstNativeSynthIF::enableAllControllers(bool v) { if(!_synth) @@ -2662,17 +3154,12 @@ for(unsigned long i = 0; i < sic; ++i) _controls[i].enCtrl = v; } -void VstNativeSynthIF::enable2AllControllers(bool v) -{ - if(!_synth) - return; - const unsigned long sic = _synth->inControls(); - for(unsigned long i = 0; i < sic; ++i) - _controls[i].en2Ctrl = v; -} void VstNativeSynthIF::updateControllers() { } void VstNativeSynthIF::activate() { + // Set some default properties + dispatch(effSetSampleRate, 0, 0, NULL, MusEGlobal::sampleRate); + dispatch(effSetBlockSize, 0, MusEGlobal::segmentSize, NULL, 0.0f); //for (unsigned short i = 0; i < instances(); ++i) { // dispatch(i, effMainsChanged, 0, 1, NULL, 0.0f); dispatch(effMainsChanged, 0, 1, NULL, 0.0f); @@ -2694,9 +3181,11 @@ // controls[i].tmpVal = controls[i].val; // } // } + _active = true; } void VstNativeSynthIF::deactivate() { + _active = false; //for (unsigned short i = 0; i < instances(); ++i) { #ifndef VST_VESTIGE_SUPPORT //dispatch(i, effStopProcess, 0, 0, NULL, 0.0f); @@ -2709,9 +3198,9 @@ unsigned long VstNativeSynthIF::parameters() const { return _synth ? _synth->inControls() : 0; } unsigned long VstNativeSynthIF::parametersOut() const { return 0; } -void VstNativeSynthIF::setParam(unsigned long i, float val) { setParameter(i, val); } -float VstNativeSynthIF::param(unsigned long i) const { return getParameter(i); } -float VstNativeSynthIF::paramOut(unsigned long) const { return 0.0; } +void VstNativeSynthIF::setParam(unsigned long i, double val) { setParameter(i, val); } +double VstNativeSynthIF::param(unsigned long i) const { return getParameter(i); } +double VstNativeSynthIF::paramOut(unsigned long) const { return 0.0; } const char* VstNativeSynthIF::paramName(unsigned long i) { if(!_plugin) @@ -2743,7 +3232,469 @@ } // FIXME TODO: CtrlValueType VstNativeSynthIF::ctrlValueType(unsigned long /*i*/) const { return VAL_LINEAR; } -CtrlList::Mode VstNativeSynthIF::ctrlMode(unsigned long /*i*/) const { return CtrlList::INTERPOLATE; }; +CtrlList::Mode VstNativeSynthIF::ctrlMode(unsigned long /*i*/) const { return CtrlList::INTERPOLATE; } + +VstNativePluginWrapper::VstNativePluginWrapper(VstNativeSynth *s, PluginFeatures reqFeatures) +{ + _synth = s; + + _requiredFeatures = reqFeatures; + + _fakeLd.Label = strdup(_synth->name().toUtf8().constData()); + _fakeLd.Name = strdup(_synth->name().toUtf8().constData()); + _fakeLd.UniqueID = _synth->_id; + _fakeLd.Maker = strdup(_synth->maker().toUtf8().constData()); + _fakeLd.Copyright = strdup(_synth->version().toUtf8().constData()); + _isVstNativePlugin = true; + _isVstNativeSynth = s->isSynth(); + int numPorts = _synth->inPorts() + + _synth->outPorts() + + _synth->inControls(); + _fakeLd.PortCount = numPorts; + _fakePds = new LADSPA_PortDescriptor [numPorts]; + memset(_fakePds, 0, sizeof(int) * numPorts); + + for(size_t i = 0; i < _synth->inPorts(); i++) + { + _fakePds [i] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + } + + for(size_t i = 0; i < _synth->outPorts(); i++) + { + _fakePds [i + _synth->inPorts()] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + } + + for(size_t i = 0; i < _synth->inControls(); i++) + { + _fakePds [i + _synth->inPorts() + _synth->outPorts()] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + } + + + _fakeLd.PortNames = NULL; + _fakeLd.PortRangeHints = NULL; + _fakeLd.PortDescriptors = _fakePds; + _fakeLd.Properties = 0; + plugin = &_fakeLd; + _isDssi = false; + _isDssiSynth = false; + _isLV2Plugin = false; + _isLV2Synth = false; + +#ifdef DSSI_SUPPORT + dssi_descr = NULL; +#endif + + fi = _synth->info; + ladspa = NULL; + _handle = 0; + _references = 0; + _instNo = 0; + _label = _synth->name(); + _name = _synth->description(); + _uniqueID = plugin->UniqueID; + _maker = _synth->maker(); + _copyright = _synth->version(); + + _portCount = plugin->PortCount; + + _inports = 0; + _outports = 0; + _controlInPorts = 0; + _controlOutPorts = 0; + + for(unsigned long k = 0; k < _portCount; ++k) + { + LADSPA_PortDescriptor pd = plugin->PortDescriptors[k]; + + if(pd & LADSPA_PORT_AUDIO) + { + if(pd & LADSPA_PORT_INPUT) + { + ++_inports; + } + else if(pd & LADSPA_PORT_OUTPUT) + { + ++_outports; + } + } + else if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + { + ++_controlInPorts; + } + else if(pd & LADSPA_PORT_OUTPUT) + { + ++_controlOutPorts; + } + } + } +} + +VstNativePluginWrapper::~VstNativePluginWrapper() +{ + free((void*)_fakeLd.Label); + free((void*)_fakeLd.Name); + free((void*)_fakeLd.Maker); + free((void*)_fakeLd.Copyright); + delete [] _fakePds; +} + +LADSPA_Handle VstNativePluginWrapper::instantiate(PluginI *pluginI) +{ + VstNativePluginWrapper_State *state = new VstNativePluginWrapper_State; + if(!state) + { + abort(); + } + state->plugin = _synth->instantiate(&state->userData); + if(!state->plugin) + { + delete state; + return 0; + } + + state->pluginI = pluginI; + state->pluginWrapper = this; + state->inPorts.resize(_inports); + state->outPorts.resize(_outports); + state->inControlPorts.resize(_controlInPorts); + state->inControlLastValues.resize(_controlInPorts); + bool refillDefCtrls = false; + if(inControlDefaults.size() == 0) + { + refillDefCtrls = true; + inControlDefaults.resize(_controlInPorts); + portNames.resize(_inports + _outports + _controlInPorts); + } + memset(&state->inPorts [0], 0, _inports * sizeof(float *)); + memset(&state->outPorts [0], 0, _outports * sizeof(float *)); + memset(&state->inControlPorts [0], 0, _controlInPorts * sizeof(float *)); + + if(refillDefCtrls) + { + for(size_t i = 0; i < _controlInPorts; i++) + { + if(state->plugin->getParameter) + { + state->inControlLastValues [i] = inControlDefaults [i] = state->plugin->getParameter(state->plugin, i); + } + } + + + for(size_t i = 0; i < portNames.size(); i++) + { + if(i < _inports) + { + std::stringstream ss; + ss << "input" << i; + portNames [i] = ss.str(); + } + else if(i < _inports + _outports) + { + std::stringstream ss; + ss << "output" << (i - _inports); + portNames [i] = ss.str(); + } + else if(i < _inports + _outports + _controlInPorts) + { + char buf[256]; + memset(buf, 0, sizeof(buf)); + dispatch(state, effGetParamName, i - _inports - _outports, 0, buf, 0); + if(strlen(buf) > 0) + { + portNames [i] = buf; + } + else + { + std::stringstream ss; + ss << "control" << (i - _inports - _outports); + portNames [i] = ss.str(); + } + } + } + } + + QObject::connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), state, SLOT(heartBeat())); + + return(LADSPA_Handle)state; + +} + +int VstNativePluginWrapper::incReferences(int ref) +{ + _synth->incInstances(ref); + return _synth->instances(); +} + +void VstNativePluginWrapper::activate(LADSPA_Handle handle) +{ + VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; + // Set some default properties + dispatch(state, effSetSampleRate, 0, 0, NULL, MusEGlobal::sampleRate); + dispatch(state, effSetBlockSize, 0, MusEGlobal::segmentSize, NULL, 0.0f); + //for (unsigned short i = 0; i < instances(); ++i) { + // dispatch(i, effMainsChanged, 0, 1, NULL, 0.0f); + dispatch(state, effMainsChanged, 0, 1, NULL, 0.0f); + dispatch(state, 71 /*effStartProcess*/, 0, 0, NULL, 0.0f); + + if(state->plugin->getParameter) + { + for(size_t i = 0; i < _controlInPorts; i++) + { + state->pluginI->controls [i].val = state->pluginI->controls [i].tmpVal = inControlDefaults [i]; + } + } + state->active = true; +} + +void VstNativePluginWrapper::deactivate(LADSPA_Handle handle) +{ + VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; + if(!state) + { + return; + } + state->active = false; + dispatch(state, 72 /*effStopProcess*/, 0, 0, NULL, 0.0f); + dispatch(state, effMainsChanged, 0, 0, NULL, 0.0f); +} + +void VstNativePluginWrapper::cleanup(LADSPA_Handle handle) +{ + VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; + if(!state) + { + return; + } + if(state->editor) + { + state->editor->close(); + state->editor = NULL; + state->guiVisible = false; + } + + if (state->plugin) + { + dispatch(state, effClose, 0, 0, NULL, 0); + state->plugin = 0; + } + + delete state; +} + +void VstNativePluginWrapper::connectPort(LADSPA_Handle handle, unsigned long port, float *value) +{ + VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; + if(port < _inports) + { + state->inPorts [port] = value; + } + else if(port < _inports + _outports) + { + state->outPorts [port - _inports] = value; + } + else if(port < _inports + _outports + _controlInPorts) + { + state->inControlPorts [port - _inports - _outports] = value; + } + +} + +void VstNativePluginWrapper::apply(LADSPA_Handle handle, unsigned long n) +{ + VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; + state->inProcess = true; + if(state->pluginI->controls) + { + for(size_t i = 0; i < _controlInPorts; i++) + { + if(state->pluginI->controls [i].val == state->inControlLastValues [i]) + { + continue; + } + state->inControlLastValues [i] = state->pluginI->controls [i].val; + if(dispatch(state, 26 /*effCanBeAutomated*/, i, 0, NULL, 0.0f) == 1) + { + if(state->plugin->getParameter && state->plugin->setParameter) + { + if(state->plugin->getParameter(state->plugin, i) != state->inControlLastValues [i]) + state->plugin->setParameter(state->plugin, i, state->inControlLastValues [i]); + } + } + + } + } + if((state->plugin->flags & effFlagsCanReplacing) && state->plugin->processReplacing) + { + state->plugin->processReplacing(state->plugin, &state->inPorts [0], &state->outPorts [0], n); + } + else if(state->plugin->process) + { + state->plugin->process(state->plugin, &state->inPorts [0], &state->outPorts [0], n); + } + state->inProcess = false; + +} + +LADSPA_PortDescriptor VstNativePluginWrapper::portd(unsigned long k) const +{ + return _fakeLd.PortDescriptors[k]; +} + +LADSPA_PortRangeHint VstNativePluginWrapper::range(unsigned long) +{ + LADSPA_PortRangeHint hint; + hint.HintDescriptor = 0; + hint.LowerBound = 0.0f; + hint.UpperBound = 1.0f; + + hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; + hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; + + return hint; +} + +void VstNativePluginWrapper::range(unsigned long, float *min, float *max) const +{ + *min = 0.0f; + *max = 1.0f; +} + +double VstNativePluginWrapper::defaultValue(unsigned long port) const +{ + return inControlDefaults [port]; +} + +const char *VstNativePluginWrapper::portName(unsigned long port) +{ + return portNames [port].c_str(); +} + +CtrlValueType VstNativePluginWrapper::ctrlValueType(unsigned long) const +{ + return VAL_LINEAR; +} + +CtrlList::Mode VstNativePluginWrapper::ctrlMode(unsigned long) const +{ + return CtrlList::INTERPOLATE; +} + +bool VstNativePluginWrapper::hasNativeGui() const +{ + return _synth->_hasGui; +} + +void VstNativePluginWrapper::showNativeGui(PluginI *p, bool bShow) +{ + assert(p->instances > 0); + VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)p->handle [0]; + if(!hasNativeGui()) + return; + if(bShow) + { + if(state->editor) + { + if(!state->editor->isVisible()) + state->editor->show(); + state->editor->raise(); + state->editor->activateWindow(); + } + else + { + Qt::WindowFlags wflags = Qt::Window + | Qt::CustomizeWindowHint + | Qt::WindowTitleHint + | Qt::WindowSystemMenuHint + | Qt::WindowMinMaxButtonsHint + | Qt::WindowCloseButtonHint; + state->editor = new MusEGui::VstNativeEditor(NULL, wflags); + state->editor->open(0, state); + } + } + else + { + if(state->editor) + { + state->editor->close(); + //_editor = NULL; // No - done in editorDeleted. + } + } + state->guiVisible = bShow; +} + +bool VstNativePluginWrapper::nativeGuiVisible(const PluginI *p) const +{ + assert(p->instances > 0); + VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)p->handle [0]; + return state->guiVisible; +} + +void VstNativePluginWrapper::writeConfiguration(LADSPA_Handle handle, int level, Xml &xml) +{ + VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; + if(_synth->hasChunks()) + { + //--------------------------------------------- + // dump current state of synth + //--------------------------------------------- + fprintf(stderr, "%s: commencing chunk data dump, plugin api version=%d\n", name().toLatin1().constData(), _synth->vstVersion()); + unsigned long len = 0; + void* p = 0; + len = dispatch(state, 23 /* effGetChunk */, 0, 0, &p, 0.0); // index 0: is bank 1: is program + if (len) + { + QByteArray arrOut = QByteArray::fromRawData((char *)p, len); + + QByteArray outEnc64 = arrOut.toBase64(); + QString customData(outEnc64); + for (int pos=0; pos < customData.size(); pos+=150) + { + customData.insert(pos++,'\n'); // add newlines for readability + } + xml.strTag(level, "customData", customData); + } + + } +} + +void VstNativePluginWrapper::setCustomData(LADSPA_Handle handle, const std::vector &customParams) +{ + if(customParams.size() == 0) + return; + + VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; + if(!_synth->hasChunks()) + { + return; + } + + for(size_t i = 0; i < customParams.size(); i++) + { + QString param = customParams [i]; + param.remove('\n'); // remove all linebreaks that may have been added to prettyprint the songs file + QByteArray paramIn; + paramIn.append(param); + QByteArray dec64 = QByteArray::fromBase64(paramIn); + dispatch(state, 24 /* effSetChunk */, 0, dec64.size(), (void*)dec64.data(), 0.0); // index 0: is bank 1: is program + break; //one customData tag includes all data in base64 + } +} + +void VstNativePluginWrapper_State::heartBeat() +{ + if(plugin && active) + { + if(guiVisible) + { + plugin->dispatcher(plugin, effEditIdle, 0, 0, NULL, 0.0f); + if(editor) + editor->update(); + } + } + +} } // namespace MusECore diff -Nru muse-2.1.2/muse/vst_native.h muse-3.0.2+ds1/muse/vst_native.h --- muse-2.1.2/muse/vst_native.h 2013-03-28 15:17:40.000000000 +0000 +++ muse-3.0.2+ds1/muse/vst_native.h 2017-12-17 21:07:38.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // // vst_native.h -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012-2013 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 @@ -26,7 +26,8 @@ #include "config.h" -// Make sure this number is unique among all the MESS synths and DSSI and VST host synths. +// Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. #define VST_NATIVE_SYNTH_UNIQUE_ID 9 // Midistate sysex initialization command. #define VST_NATIVE_INIT_DATA_CMD 1 @@ -42,6 +43,10 @@ typedef class VstNativeSynthIF VSTPlugin; +#ifdef VST_SDK_QUIRK +#define __cdecl +#endif + #include "aeffectx.h" #ifdef VST_VESTIGE_SUPPORT @@ -66,6 +71,8 @@ #include "plugin.h" #include "midictrl.h" +#include + #endif // VST_NATIVE_SUPPORT namespace MusEGui { @@ -94,7 +101,18 @@ // VstNativeSynth //--------------------------------------------------------- +class VstNativePluginWrapper; +class VstNativePluginWrapper_State; +class VstNativeSynthIF; + +struct VstNativeSynthOrPlugin +{ + VstNativeSynthIF *sif; + VstNativePluginWrapper_State *pstate; +}; + class VstNativeSynth : public Synth { + friend class VstNativePluginWrapper; enum VstPluginFlags { canSendVstEvents = 1 << 0, @@ -113,24 +131,27 @@ void* _handle; int _vst_version; unsigned int _flags; - + VstIntPtr _id; + bool _isSynth; + unsigned long /*_portCount,*/ _inports, _outports, _controlInPorts; //, _controlOutPorts; std::vector iIdx; // Audio input index to port number. std::vector oIdx; // Audio output index to port number. std::vector rpIdx; // Port number to control input index. Item is -1 if it's not a control input. - MusECore::MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to vst port numbers. - MusECore::MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps vst port numbers to midi controller numbers. + MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to vst port numbers. + MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps vst port numbers to midi controller numbers. bool _hasGui; - bool _inPlaceCapable; bool _hasChunks; - + public: - VstNativeSynth(const QFileInfo& fi, AEffect* plugin, const QString& label, const QString& desc, const QString& maker, const QString& ver); + VstNativeSynth(const QFileInfo& fi, AEffect* plugin, + const QString& label, const QString& desc, const QString& maker, const QString& ver, + VstIntPtr id, void *dlHandle, bool isSynth, Plugin::PluginFeatures reqFeatures = Plugin::NoFeatures); virtual ~VstNativeSynth() {} - virtual Type synthType() const { return VST_NATIVE_SYNTH; } + virtual Type synthType() const { return _isSynth ? VST_NATIVE_SYNTH : VST_NATIVE_EFFECT; } virtual void incInstances(int val); - virtual AEffect* instantiate(); + virtual AEffect* instantiate(void *userData); virtual SynthIF* createSIF(SynthI*); unsigned long inPorts() const { return _inports; } unsigned long outPorts() const { return _outports; } @@ -140,6 +161,29 @@ int vstVersion() const { return _vst_version; } bool hasChunks() const { return _hasChunks; } const std::vector* getRpIdx() { return &rpIdx; } + bool isSynth() { return _isSynth; } + + static VstIntPtr pluginHostCallback(VstNativeSynthOrPlugin *userData, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); + static int guiControlChanged(VstNativeSynthOrPlugin *userData, unsigned long param_idx, float value); + static void guiAutomationBegin(VstNativeSynthOrPlugin *userData, unsigned long param_idx); + static void guiAutomationEnd(VstNativeSynthOrPlugin *userData, unsigned long param_idx); + static bool resizeEditor(MusEGui::VstNativeEditor *editor, int w, int h); + + }; + +//--------------------------------------------------------- +// VstNativeGuiWidgets +//--------------------------------------------------------- + +struct VstNativeGuiWidgets { + // TODO: Remove Tim. Or keep. + //enum { + // SLIDER, DOUBLE_LABEL, QCHECKBOX, QCOMBOBOX + // }; + //QWidget* widget; + //int type; + //unsigned long param; + bool pressed; }; //--------------------------------------------------------- @@ -151,57 +195,55 @@ { friend class VstNativeSynth; friend class MusEGui::VstNativeEditor; - + VstNativeSynth* _synth; AEffect* _plugin; + bool _active; // Whether it's safe to call effIdle or effEditIdle. MusEGui::VstNativeEditor* _editor; bool _guiVisible; bool _inProcess; // To inform the callback of the 'process level' - are we in the audio thread? + // Struct array to keep track of pressed flags and so on. // TODO: Not used yet. REMOVE Tim. Or keep. + VstNativeGuiWidgets* _gw; Port* _controls; float** _audioOutBuffers; float** _audioInBuffers; - std::vector _iUsedIdx; // During process, tells whether an audio input port was used by any input routes. float* _audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. + VstNativeSynthOrPlugin userData; + std::vector programs; void queryPrograms(); void doSelectProgram(int bankH, int bankL, int prog); - bool processEvent(const MusECore::MidiPlayEvent&, VstMidiEvent*); + bool processEvent(const MidiPlayEvent&, VstMidiEvent*); void setVstEvent(VstMidiEvent* event, int a = 0, int b = 0, int c = 0, int d = 0); - + void editorDeleted(); void editorOpened(); void editorClosed(); - + + void eventReceived(VstMidiEvent*); + public: VstNativeSynthIF(SynthI* s); virtual ~VstNativeSynthIF(); virtual bool init(Synth*); - + AEffect* plugin() const { return _plugin; } - VstIntPtr hostCallback(VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); VstIntPtr dispatch(VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) const { - if(_plugin) return _plugin->dispatcher(_plugin, opcode, index, value, ptr, opt); return 0; } + if(_plugin) {return _plugin->dispatcher(_plugin, opcode, index, value, ptr, opt); } return 0; } void idleEditor(); - bool resizeEditor(int w, int h); - - virtual bool initGui() { return true; }; - virtual void guiHeartBeat() { } - virtual bool guiVisible() const; - virtual void showGui(bool); + + virtual void guiHeartBeat(); virtual bool hasGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool v); virtual bool hasNativeGui() const; - virtual void getGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } - virtual void setGeometry(int, int, int, int) {} - virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } - virtual void setNativeGeometry(int, int, int, int) {} - virtual void preProcessAlways() { }; - virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned nframes, float** buffer) ; - virtual bool putEvent(const MidiPlayEvent& ev); + virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const ; + virtual void setNativeGeometry(int, int, int, int); + virtual void preProcessAlways() { } + virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned nframes, float** buffer) ; virtual MidiPlayEvent receiveEvent(); virtual int eventsPending() const { return 0; } virtual int channels() const; @@ -211,13 +253,9 @@ virtual QString getPatchName(int chan, int prog, bool drum) const; virtual void populatePatchPopup(MusEGui::PopupMenu* menu, int chan, bool drum); virtual void write(int level, Xml& xml) const; - virtual float getParameter(unsigned long idx) const; - virtual void setParameter(unsigned long idx, float value); - virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) { return 0; } - - virtual void guiAutomationBegin(unsigned long param_idx); - virtual void guiAutomationEnd(unsigned long param_idx); - virtual int guiControlChanged(unsigned long param_idx, float value); + virtual double getParameter(unsigned long idx) const; + virtual void setParameter(unsigned long idx, double value); + virtual int getControllerInfo(int, QString*, int*, int*, int*, int*) { return 0; } //------------------------- // Methods for PluginIBase: @@ -230,19 +268,16 @@ QString fileName() const; 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 activate(); void deactivate(); unsigned long parameters() const; unsigned long parametersOut() const; - void setParam(unsigned long i, float val); - float param(unsigned long i) const; - float paramOut(unsigned long i) const; + void setParam(unsigned long i, double val); + double param(unsigned long i) const; + double paramOut(unsigned long i) const; const char* paramName(unsigned long i); const char* paramOutName(unsigned long i); LADSPA_PortRangeHint range(unsigned long i); @@ -251,6 +286,95 @@ CtrlList::Mode ctrlMode(unsigned long i) const; }; +class VstNativePluginWrapper_State : public QObject +{ + Q_OBJECT +public: + AEffect* plugin; + VstNativePluginWrapper *pluginWrapper; + PluginI *pluginI; + std::vector inPorts; + std::vector outPorts; + std::vector inControlPorts; + std::vector inControlLastValues; + MusEGui::VstNativeEditor* editor; + VstNativeSynthOrPlugin userData; + bool guiVisible; + bool inProcess; + bool active; + VstNativePluginWrapper_State() + { + plugin = 0; + pluginWrapper = 0; + pluginI = 0; + editor = 0; + guiVisible = false; + userData.sif = 0; + userData.pstate = this; + inProcess = false; + active = false; + } + virtual ~VstNativePluginWrapper_State() {} + void editorDeleted() + { + editor = 0; + } + void editorOpened() + { + guiVisible = true; + } + + void editorClosed() + { + guiVisible = false; + } +protected slots: + virtual void heartBeat(); +}; + +class VstNativePluginWrapper: public Plugin +{ + friend class MusEGui::VstNativeEditor; + friend class VstNativeSynth; +private: + VstNativeSynth *_synth; + LADSPA_Descriptor _fakeLd; + LADSPA_PortDescriptor *_fakePds; + std::vector inControlDefaults; + std::vector portNames; +public: + VstNativePluginWrapper ( VstNativeSynth *s, PluginFeatures reqFeatures = NoFeatures ); + VstNativeSynth *synth() { + return _synth; + } + virtual ~VstNativePluginWrapper(); + virtual LADSPA_Handle instantiate ( PluginI * ); + virtual int incReferences ( int ref ); + virtual void activate ( LADSPA_Handle handle ); + virtual void deactivate ( LADSPA_Handle handle ); + virtual void cleanup ( LADSPA_Handle handle ); + virtual void connectPort ( LADSPA_Handle handle, unsigned long port, float *value ); + virtual void apply ( LADSPA_Handle handle, unsigned long n ); + virtual LADSPA_PortDescriptor portd ( unsigned long k ) const; + + virtual LADSPA_PortRangeHint range ( unsigned long i ); + virtual void range (unsigned long, float *min, float *max ) const; + + virtual double defaultValue ( unsigned long port ) const; + virtual const char *portName (unsigned long port ); + virtual CtrlValueType ctrlValueType ( unsigned long ) const; + virtual CtrlList::Mode ctrlMode ( unsigned long ) const; + virtual bool hasNativeGui() const; + virtual void showNativeGui ( PluginI *p, bool bShow ); + virtual bool nativeGuiVisible (const PluginI *p ) const; + virtual void writeConfiguration(LADSPA_Handle handle, int level, Xml& xml); + virtual void setCustomData (LADSPA_Handle handle, const std::vector & customParams); + + VstIntPtr dispatch(VstNativePluginWrapper_State *state, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) const { + if(state->plugin) return state->plugin->dispatcher(state->plugin, opcode, index, value, ptr, opt); else return 0; } +}; + + #endif // VST_NATIVE_SUPPORT extern void initVST_Native(); diff -Nru muse-2.1.2/muse/wave.cpp muse-3.0.2+ds1/muse/wave.cpp --- muse-2.1.2/muse/wave.cpp 2013-03-28 15:17:36.000000000 +0000 +++ muse-3.0.2+ds1/muse/wave.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -40,10 +41,12 @@ #include "globals.h" #include "event.h" #include "audio.h" -///#include "sig.h" #include "al/sig.h" #include "part.h" #include "track.h" +#include "wavepreview.h" +#include "gconfig.h" +#include "type_defs.h" //#define WAVE_DEBUG //#define WAVE_DEBUG_PRC @@ -69,6 +72,8 @@ openFlag = false; sndFiles.push_back(this); refCount=0; + writeBuffer = 0; + writeSegSize = std::max((size_t)MusEGlobal::segmentSize, (size_t)cacheMag);// cache minimum segment size for write operations } SndFile::~SndFile() @@ -77,24 +82,28 @@ close(); for (iSndFile i = sndFiles.begin(); i != sndFiles.end(); ++i) { if (*i == this) { + //fprintf(stderr, "erasing from sndfiles:%s\n", finfo->canonicalFilePath().toLatin1().constData()); sndFiles.erase(i); break; } } delete finfo; if (cache) { - for (unsigned i = 0; i < channels(); ++i) - delete [] cache[i]; delete[] cache; cache = 0; } + if(writeBuffer) { + delete [] writeBuffer; + writeBuffer = 0; + } + } //--------------------------------------------------------- // openRead //--------------------------------------------------------- -bool SndFile::openRead() +bool SndFile::openRead(bool createCache, bool showProgress) { if (openFlag) { printf("SndFile:: already open\n"); @@ -102,16 +111,26 @@ } QString p = path(); sfinfo.format = 0; - sf = sf_open(p.toLatin1().constData(), SFM_READ, &sfinfo); - sfinfo.format = 0; - sfUI = sf_open(p.toLatin1().constData(), SFM_READ, &sfinfo); - if (sf == 0 || sfUI == 0) + sfUI = 0; + sf = sf_open(p.toLocal8Bit().constData(), SFM_READ, &sfinfo); + if (sf == 0) return true; - + if(createCache){ + sfinfo.format = 0; + sfUI = sf_open(p.toLocal8Bit().constData(), SFM_READ, &sfinfo); + if (sfUI == 0){ + sf_close(sf); + sf = 0; + return true; + } + } + writeFlag = false; openFlag = true; - QString cacheName = finfo->absolutePath() + QString("/") + finfo->completeBaseName() + QString(".wca"); - readCache(cacheName, true); + if (createCache) { + QString cacheName = finfo->absolutePath() + QString("/") + finfo->completeBaseName() + QString(".wca"); + readCache(cacheName, showProgress); + } return false; } @@ -120,96 +139,107 @@ // called after recording to file //--------------------------------------------------------- -void SndFile::update() +void SndFile::update(bool showProgress) { close(); // force recreation of wca data QString cacheName = finfo->absolutePath() + QString("/") + finfo->completeBaseName() + QString(".wca"); - ::remove(cacheName.toLatin1().constData()); - if (openRead()) { - printf("SndFile::update openRead(%s) failed: %s\n", path().toLatin1().constData(), strerror().toLatin1().constData()); + ::remove(cacheName.toLocal8Bit().constData()); + if (openRead(true, showProgress)) { + printf("SndFile::update openRead(%s) failed: %s\n", path().toLocal8Bit().constData(), strerror().toLocal8Bit().constData()); } } +//--------------------------------------------------- +// create cache +//--------------------------------------------------- + +void SndFile::createCache(const QString& path, bool showProgress, bool bWrite, sf_count_t cstart) +{ + if(cstart >= csize) + return; + QProgressDialog* progress = 0; + if (showProgress) { + QString label(QWidget::tr("create peakfile for ")); + label += basename(); + progress = new QProgressDialog(label, + QString::null, 0, csize, 0); + progress->setMinimumDuration(0); + progress->show(); + } + float data[channels()][cacheMag]; + float* fp[channels()]; + for (unsigned k = 0; k < channels(); ++k) + fp[k] = &data[k][0]; + int interval = (csize - cstart) / 10; + + if(!interval) + interval = 1; + for (int i = cstart; i < csize; i++) { + if (showProgress && ((i % interval) == 0)) + progress->setValue(i); + seek(i * cacheMag, 0); + read(channels(), fp, cacheMag); + for (unsigned ch = 0; ch < channels(); ++ch) { + float rms = 0.0; + cache[ch][i].peak = 0; + for (int n = 0; n < cacheMag; n++) { + float fd = data[ch][n]; + rms += fd * fd; + int idata = int(fd * 255.0); + if (idata < 0) + idata = -idata; + if (cache[ch][i].peak < idata) + cache[ch][i].peak = idata; + } + // amplify rms value +12dB + int rmsValue = int((sqrt(rms/cacheMag) * 255.0)); + if (rmsValue > 255) + rmsValue = 255; + cache[ch][i].rms = rmsValue; + } + } + if (showProgress) + progress->setValue(csize); + if(bWrite) + writeCache(path); + if (showProgress) + delete progress; + +} + //--------------------------------------------------------- // readCache //--------------------------------------------------------- void SndFile::readCache(const QString& path, bool showProgress) - { - if (cache) { - for (unsigned i = 0; i < channels(); ++i) - delete [] cache[i]; - delete[] cache; - } - if (samples() == 0) - return; - - csize = (samples() + cacheMag - 1)/cacheMag; - cache = new SampleV*[channels()]; +{ + if (cache) { + delete[] cache; + } + if (samples() == 0) + return; + + csize = (samples() + cacheMag - 1)/cacheMag; + cache = new SampleVtype[channels()]; + for (unsigned ch = 0; ch < channels(); ++ch) + { + cache [ch].resize(csize); + } + + FILE* cfile = fopen(path.toLocal8Bit().constData(), "r"); + if (cfile) { for (unsigned ch = 0; ch < channels(); ++ch) - cache[ch] = new SampleV[csize]; + fread(&cache[ch] [0], csize * sizeof(SampleV), 1, cfile); + fclose(cfile); + return; + } - FILE* cfile = fopen(path.toLocal8Bit().constData(), "r"); - if (cfile) { - for (unsigned ch = 0; ch < channels(); ++ch) - fread(cache[ch], csize * sizeof(SampleV), 1, cfile); - fclose(cfile); - return; - } - //--------------------------------------------------- - // create cache - //--------------------------------------------------- - QProgressDialog* progress = 0; - if (showProgress) { - QString label(QWidget::tr("create peakfile for ")); - label += basename(); - progress = new QProgressDialog(label, - QString::null, 0, csize, 0); - progress->setMinimumDuration(0); - progress->show(); - } - float data[channels()][cacheMag]; - float* fp[channels()]; - for (unsigned k = 0; k < channels(); ++k) - fp[k] = &data[k][0]; - int interval = csize / 10; - - if(!interval) - interval = 1; - for (int i = 0; i < csize; i++) { - if (showProgress && ((i % interval) == 0)) - progress->setValue(i); - seek(i * cacheMag, 0); - read(channels(), fp, cacheMag); - for (unsigned ch = 0; ch < channels(); ++ch) { - float rms = 0.0; - cache[ch][i].peak = 0; - for (int n = 0; n < cacheMag; n++) { - float fd = data[ch][n]; - rms += fd * fd; - int idata = int(fd * 255.0); - if (idata < 0) - idata = -idata; - if (cache[ch][i].peak < idata) - cache[ch][i].peak = idata; - } - // amplify rms value +12dB - int rmsValue = int((sqrt(rms/cacheMag) * 255.0)); - if (rmsValue > 255) - rmsValue = 255; - cache[ch][i].rms = rmsValue; - } - } - if (showProgress) - progress->setValue(csize); - writeCache(path); - if (showProgress) - delete progress; - } + createCache(path, showProgress, true); +} //--------------------------------------------------------- // writeCache @@ -217,11 +247,11 @@ void SndFile::writeCache(const QString& path) { - FILE* cfile = fopen(path.toLatin1().constData(), "w"); + FILE* cfile = fopen(path.toLocal8Bit().constData(), "w"); if (cfile == 0) return; for (unsigned ch = 0; ch < channels(); ++ch) - fwrite(cache[ch], csize * sizeof(SampleV), 1, cfile); + fwrite(&cache[ch] [0], csize * sizeof(SampleV), 1, cfile); fclose(cfile); } @@ -229,42 +259,44 @@ // read //--------------------------------------------------------- -void SndFile::read(SampleV* s, int mag, unsigned pos, bool overwrite) +void SndFile::read(SampleV* s, int mag, unsigned pos, bool overwrite, bool allowSeek) { if(overwrite) for (unsigned ch = 0; ch < channels(); ++ch) { s[ch].peak = 0; s[ch].rms = 0; } - - if (pos > samples()) + + // only check pos if seek is allowed + // for realtime drawing of the wave + // seek may cause writing errors + if (allowSeek && pos > samples()) return; - if (mag < cacheMag) { float data[channels()][mag]; float* fp[channels()]; for (unsigned i = 0; i < channels(); ++i) fp[i] = &data[i][0]; - + sf_count_t ret = 0; if(sfUI) - ret = sf_seek(sfUI, pos, SEEK_SET); + ret = sf_seek(sfUI, pos, SEEK_SET | SFM_READ); else - ret = sf_seek(sf, pos, SEEK_SET); + ret = sf_seek(sf, pos, SEEK_SET | SFM_READ); if(ret == -1) - return; + return; { int srcChannels = channels(); int dstChannels = sfinfo.channels; size_t n = mag; float** dst = fp; float buffer[n * dstChannels]; - + size_t rn = 0; if(sfUI) rn = sf_readf_float(sfUI, buffer, n); - else + else rn = sf_readf_float(sf, buffer, n); if(rn != n) return; @@ -292,10 +324,10 @@ } for (unsigned ch = 0; ch < channels(); ++ch) { - + if(overwrite) s[ch].peak = 0; - + float rms = 0.0; for (int i = 0; i < mag; i++) { float fd = data[ch][i]; @@ -306,7 +338,7 @@ if (s[ch].peak < idata) s[ch].peak = idata; } - + s[ch].rms = 0; // TODO rms / mag; } } @@ -315,7 +347,7 @@ int rest = csize - (pos/cacheMag); int end = mag; if (rest < mag) - end = rest; + end = rest; for (unsigned ch = 0; ch < channels(); ++ch) { int rms = 0; @@ -325,11 +357,11 @@ if (s[ch].peak < cache[ch][offset].peak) s[ch].peak = cache[ch][offset].peak; } - + if(overwrite) s[ch].rms = rms / mag; - - else + + else s[ch].rms += rms / mag; } } @@ -346,9 +378,13 @@ return false; } QString p = path(); + sf = sf_open(p.toLocal8Bit().constData(), SFM_RDWR, &sfinfo); sfUI = 0; if (sf) { + if(writeBuffer) + delete [] writeBuffer; + writeBuffer = new float [writeSegSize * std::max(2, sfinfo.channels)]; openFlag = true; writeFlag = true; QString cacheName = finfo->absolutePath() + @@ -368,9 +404,17 @@ printf("SndFile:: alread closed\n"); return; } - sf_close(sf); + if(int err = sf_close(sf)) + fprintf(stderr, "SndFile::close Error:%d on sf_close(sf:%p)\n", err, sf); + else + sf = 0; if (sfUI) - sf_close(sfUI); + { + if(int err = sf_close(sfUI)) + fprintf(stderr, "SndFile::close Error:%d on sf_close(sfUI:%p)\n", err, sfUI); + else + sfUI = 0; + } openFlag = false; } @@ -419,13 +463,16 @@ // samples //--------------------------------------------------------- -unsigned SndFile::samples() const +sf_count_t SndFile::samples() const { if (!writeFlag) // if file is read only sfinfo is reliable return sfinfo.frames; - sf_count_t curPos = sf_seek(sf, 0, SEEK_CUR); - int frames = sf_seek(sf, 0, SEEK_END); - sf_seek(sf, curPos, SEEK_SET); + SNDFILE* sfPtr = sf; + if (sfUI) + sfPtr = sfUI; + sf_count_t curPos = sf_seek(sfPtr, 0, SEEK_CUR | SFM_READ); + sf_count_t frames = sf_seek(sfPtr, 0, SEEK_END | SFM_READ); + sf_seek(sfPtr, curPos, SEEK_SET | SFM_READ); return frames; } @@ -541,57 +588,122 @@ //--------------------------------------------------------- size_t SndFile::write(int srcChannels, float** src, size_t n) - { - int dstChannels = sfinfo.channels; - //float buffer[n * dstChannels]; - float *buffer = new float[n * dstChannels]; - float *dst = buffer; +{ + size_t wrFrames = 0; - const float limitValue=0.9999; + if(n <= writeSegSize) + wrFrames = realWrite(srcChannels, src, n); + else + { + while(1) + { + size_t sz = (n - wrFrames) < writeSegSize ? (n - wrFrames) : writeSegSize; + size_t nrWrote = realWrite(srcChannels, src, sz, wrFrames); + if(nrWrote == 0) // Nothing written? + break; + wrFrames += nrWrote; + if(wrFrames >= n) + break; + } + } + return wrFrames; +} +size_t SndFile::realWrite(int srcChannels, float** src, size_t n, size_t offs) +{ + int dstChannels = sfinfo.channels; + float *dst = writeBuffer; - if (srcChannels == dstChannels) { - for (size_t i = 0; i < n; ++i) { - for (int ch = 0; ch < dstChannels; ++ch) - if (*(src[ch]+i) > 0) - *dst++ = *(src[ch]+i) < limitValue ? *(src[ch]+i) : limitValue; - else - *dst++ = *(src[ch]+i) > -limitValue ? *(src[ch]+i) : -limitValue; - } - } - else if ((srcChannels == 1) && (dstChannels == 2)) { - // mono to stereo - for (size_t i = 0; i < n; ++i) { - float data = *(src[0]+i); - if (data > 0) { - *dst++ = data < limitValue ? data : limitValue; - *dst++ = data < limitValue ? data : limitValue; - } - else { - *dst++ = data > -limitValue ? data : -limitValue; - *dst++ = data > -limitValue ? data : -limitValue; - } - } - } - else if ((srcChannels == 2) && (dstChannels == 1)) { - // stereo to mono - for (size_t i = 0; i < n; ++i) - if (*(src[0]+i) + *(src[1]+i) > 0) - *dst++ = (*(src[0]+i) + *(src[1]+i)) < limitValue ? (*(src[0]+i) + *(src[1]+i)) : limitValue; - else - *dst++ = (*(src[0]+i) + *(src[1]+i)) > -limitValue ? (*(src[0]+i) + *(src[1]+i)) : -limitValue; - } - else { - printf("SndFile:write channel mismatch %d -> %d\n", - srcChannels, dstChannels); - delete[] buffer; - return 0; - } - int nbr = sf_writef_float(sf, buffer, n) ; - delete[] buffer; - return nbr; + size_t iStart = offs; + size_t iEnd = offs + n; + + const float limitValue=0.9999; + + + if (srcChannels == dstChannels) { + for (size_t i = iStart; i < iEnd; ++i) { + for (int ch = 0; ch < dstChannels; ++ch) + if (*(src[ch]+i) > 0) + *dst++ = *(src[ch]+i) < limitValue ? *(src[ch]+i) : limitValue; + else + *dst++ = *(src[ch]+i) > -limitValue ? *(src[ch]+i) : -limitValue; + } + } + else if ((srcChannels == 1) && (dstChannels == 2)) { + // mono to stereo + for (size_t i = iStart; i < iEnd; ++i) { + float data = *(src[0]+i); + if (data > 0) { + *dst++ = data < limitValue ? data : limitValue; + *dst++ = data < limitValue ? data : limitValue; + } + else { + *dst++ = data > -limitValue ? data : -limitValue; + *dst++ = data > -limitValue ? data : -limitValue; + } + } + } + else if ((srcChannels == 2) && (dstChannels == 1)) { + // stereo to mono + for (size_t i = iStart; i < iEnd; ++i) { + if (*(src[0]+i) + *(src[1]+i) > 0) + *dst++ = (*(src[0]+i) + *(src[1]+i)) < limitValue ? (*(src[0]+i) + *(src[1]+i)) : limitValue; + else + *dst++ = (*(src[0]+i) + *(src[1]+i)) > -limitValue ? (*(src[0]+i) + *(src[1]+i)) : -limitValue; + } + } + else { + printf("SndFile:write channel mismatch %d -> %d\n", + srcChannels, dstChannels); + return 0; + } + int nbr = sf_writef_float(sf, writeBuffer, n) ; + + if(MusEGlobal::config.liveWaveUpdate) + { //update cache + if(!cache) + { + cache = new SampleVtype[sfinfo.channels]; + csize = 0; + } + sf_count_t cstart = (sfinfo.frames + cacheMag - 1) / cacheMag; + sfinfo.frames += n; + csize = (sfinfo.frames + cacheMag - 1) / cacheMag; + for (int ch = 0; ch < sfinfo.channels; ++ch) + { + cache [ch].resize(csize); + } + + for (int i = cstart; i < csize; i++) + { + for (int ch = 0; ch < sfinfo.channels; ++ch) + { + float rms = 0.0; + cache[ch][i].peak = 0; + for (int n = 0; n < cacheMag; n++) + { + //float fd = data[ch][n]; + float fd = writeBuffer [n * sfinfo.channels + ch]; + rms += fd * fd; + int idata = int(fd * 255.0); + if (idata < 0) + idata = -idata; + if (cache[ch][i].peak < idata) + cache[ch][i].peak = idata; + } + // amplify rms value +12dB + int rmsValue = int((sqrt(rms/cacheMag) * 255.0)); + if (rmsValue > 255) + rmsValue = 255; + cache[ch][i].rms = rmsValue; + } } + } + + return nbr; +} + //--------------------------------------------------------- // seek //--------------------------------------------------------- @@ -611,7 +723,7 @@ buffer[0] = 0; sf_error_str(sf, buffer, 128); return QString(buffer); - } +} //--------------------------------------------------------- // search @@ -630,7 +742,7 @@ // getWave //--------------------------------------------------------- -SndFileR getWave(const QString& inName, bool readOnlyFlag) +SndFileR getWave(const QString& inName, bool readOnlyFlag, bool openFlag, bool showErrorBox) { QString name = inName; @@ -646,69 +758,80 @@ } // only open one instance of wave file - SndFile* f = SndFile::sndFiles.search(name); - if (f == 0) { + // REMOVE Tim. Sharing. Changed. For testing only so far. + //SndFile* f = SndFile::sndFiles.search(name); + SndFile* f = 0; + //if (f == 0) + //{ if (!QFile::exists(name)) { fprintf(stderr, "wave file <%s> not found\n", - name.toLatin1().constData()); + name.toLocal8Bit().constData()); return NULL; } f = new SndFile(name); - bool error; - if (readOnlyFlag) - error = f->openRead(); - else { - error = f->openWrite(); - // if peak cache is older than wave file we reaquire the cache - QFileInfo wavinfo(name); - QString cacheName = wavinfo.absolutePath() + QString("/") + wavinfo.completeBaseName() + QString(".wca"); - QFileInfo wcainfo(cacheName); - if (!wcainfo.exists() || wcainfo.lastModified() < wavinfo.lastModified()) { - QFile(cacheName).remove(); - f->readCache(cacheName,true); - } - - } - if (error) { - fprintf(stderr, "open wave file(%s) for %s failed: %s\n", - name.toLatin1().constData(), - readOnlyFlag ? "writing" : "reading", - f->strerror().toLatin1().constData()); - QMessageBox::critical(NULL, "MusE import error.", - "MusE failed to import the file.\n" - "Possibly this wasn't a sound file?\n" - "If it was check the permissions, MusE\n" - "sometimes requires write access to the file."); - delete f; - f = 0; - } - } - else { - if (!readOnlyFlag && ! f->isWritable()) { - if (f->isOpen()) - f->close(); - f->openWrite(); - } - else { - // if peak cache is older than wave file we reaquire the cache - QFileInfo wavinfo(name); - QString cacheName = wavinfo.absolutePath() + QString("/") + wavinfo.completeBaseName() + QString(".wca"); - QFileInfo wcainfo(cacheName); - if (!wcainfo.exists() || wcainfo.lastModified() < wavinfo.lastModified()) { - QFile(cacheName).remove(); - f->readCache(cacheName,true); - } - - } - } + if(openFlag) + { + bool error; + if (readOnlyFlag) + error = f->openRead(); + else { + error = f->openWrite(); + // if peak cache is older than wave file we reaquire the cache + QFileInfo wavinfo(name); + QString cacheName = wavinfo.absolutePath() + QString("/") + wavinfo.completeBaseName() + QString(".wca"); + QFileInfo wcainfo(cacheName); + if (!wcainfo.exists() || wcainfo.lastModified() < wavinfo.lastModified()) { + QFile(cacheName).remove(); + f->readCache(cacheName,true); + } + + } + if (error) { + fprintf(stderr, "open wave file(%s) for %s failed: %s\n", + name.toLocal8Bit().constData(), + readOnlyFlag ? "writing" : "reading", + f->strerror().toLocal8Bit().constData()); + if(showErrorBox) + QMessageBox::critical(NULL, "MusE import error.", + "MusE failed to import the file.\n" + "Possibly this wasn't a sound file?\n" + "If it was check the permissions, MusE\n" + "sometimes requires write access to the file."); + + delete f; + f = 0; + } + } +// } +// else { +// if(openFlag) +// { +// if (!readOnlyFlag && ! f->isWritable()) { +// if (f->isOpen()) +// f->close(); +// f->openWrite(); +// } +// else { +// // if peak cache is older than wave file we reaquire the cache +// QFileInfo wavinfo(name); +// QString cacheName = wavinfo.absolutePath() + QString("/") + wavinfo.completeBaseName() + QString(".wca"); +// QFileInfo wcainfo(cacheName); +// if (!wcainfo.exists() || wcainfo.lastModified() < wavinfo.lastModified()) { +// QFile(cacheName).remove(); +// f->readCache(cacheName,true); +// } +// +// } +// } +// } return f; } //--------------------------------------------------------- // applyUndoFile //--------------------------------------------------------- -void SndFile::applyUndoFile(const QString& original, const QString& tmpfile, unsigned startframe, unsigned endframe) +void SndFile::applyUndoFile(const Event& original, const QString* tmpfile, unsigned startframe, unsigned endframe) { // This one is called on both undo and redo of a wavfile // For redo to be called, undo must have been called first, and we don't store both the original data and the modified data in separate @@ -720,42 +843,52 @@ // put in the tmpfile, and when redo is eventually called the data is switched again (causing the muted data to be written to the "original" // file. The data is merely switched. - SndFile* orig = sndFiles.search(original); - SndFile tmp = SndFile(tmpfile); - if (!orig) { - printf("Internal error: could not find original file: %s in filelist - Aborting\n", original.toLatin1().constData()); + if (original.empty()) { + printf("SndFile::applyUndoFile: Internal error: original event is empty - Aborting\n"); + return; + } + + SndFileR orig = original.sndFile(); + + if (orig.isNull()) { + printf("SndFile::applyUndoFile: Internal error: original sound file is NULL - Aborting\n"); + return; + } + if (orig.canonicalPath().isEmpty()) { + printf("SndFile::applyUndoFile: Error: Original sound file name is empty - Aborting\n"); return; } - if (!orig->isOpen()) { - if (orig->openRead()) { - printf("Cannot open original file %s for reading - cannot undo! Aborting\n", original.toLatin1().constData()); + if (!orig.isOpen()) { + if (orig.openRead()) { + printf("Cannot open original file %s for reading - cannot undo! Aborting\n", orig.canonicalPath().toLocal8Bit().constData()); return; } } + SndFile tmp = SndFile(*tmpfile); if (!tmp.isOpen()) { if (tmp.openRead()) { - printf("Could not open temporary file %s for writing - cannot undo! Aborting\n", tmpfile.toLatin1().constData()); + printf("Could not open temporary file %s for writing - cannot undo! Aborting\n", tmpfile->toLocal8Bit().constData()); return; } } MusEGlobal::audio->msgIdle(true); - tmp.setFormat(orig->format(), orig->channels(), orig->samplerate()); + tmp.setFormat(orig.format(), orig.channels(), orig.samplerate()); // Read data in original file to memory before applying tmpfile to original - unsigned file_channels = orig->channels(); + unsigned file_channels = orig.channels(); unsigned tmpdatalen = endframe - startframe; float* data2beoverwritten[file_channels]; for (unsigned i=0; iseek(startframe, 0); - orig->readWithHeap(file_channels, data2beoverwritten, tmpdatalen); + orig.seek(startframe, 0); + orig.readWithHeap(file_channels, data2beoverwritten, tmpdatalen); - orig->close(); + orig.close(); // Read data from temporary file to memory float* tmpfiledata[file_channels]; @@ -767,13 +900,13 @@ tmp.close(); // Write temporary data to original file: - if (orig->openWrite()) { + if (orig.openWrite()) { printf("Cannot open orig for write - aborting.\n"); return; } - orig->seek(startframe, 0); - orig->write(file_channels, tmpfiledata, tmpdatalen); + orig.seek(startframe, 0); + orig.write(file_channels, tmpfiledata, tmpdatalen); // Delete dataholder for temporary file for (unsigned i=0; iclose(); - orig->openRead(); - orig->update(); + orig.close(); + orig.openRead(); + orig.update(); MusEGlobal::audio->msgIdle(false); } @@ -809,41 +942,59 @@ { QString path_this = canonicalPath(); if(path_this.isEmpty()) - return false; + return false; bool fwrite = finfo->isWritable(); - + // No exceptions: Even if this wave event is a clone, if it ain't writeable we gotta copy the wave. if(!fwrite) return true; - - // Count the number of non-clone part wave events (including possibly this one) using this file. + + // Count the number of unique part wave events (including possibly this one) using this file. // Not much choice but to search all active wave events - the sndfile ref count is not the solution for this... int use_count = 0; + EventID_t id = MUSE_INVALID_EVENT_ID; + Part* part = 0; WaveTrackList* wtl = MusEGlobal::song->waves(); for(ciTrack it = wtl->begin(); it != wtl->end(); ++it) { PartList* pl = (*it)->parts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { - EventList* el = ip->second->events(); - // We are looking for active independent non-clone parts - if(el->arefCount() > 1) - continue; - for(ciEvent ie = el->begin(); ie != el->end(); ++ie) + Part* p = ip->second; + const EventList& el = p->events(); +// const EventList& el = ip->second->events(); +// // We are looking for active independent non-clone parts +// if(ip->second->hasClones()) +// continue; + for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { if(ie->second.type() != Wave) continue; const Event& ev = ie->second; - if(ev.empty()) + if(ev.empty() || ev.id() == MUSE_INVALID_EVENT_ID) continue; const SndFileR sf = ev.sndFile(); + if(sf.isNull()) + continue; QString path = sf.canonicalPath(); if(path.isEmpty()) continue; if(path == path_this) + { + // Ignore clones of an already found event. + if(ev.id() == id) + { + // Double check. + if(part && !p->isCloneOf(part)) + fprintf(stderr, "SndFile::checkCopyOnWrite() Error: Two event ids are the same:%d but their parts:%p, %p are not clones!\n", (int)id, p, part); + continue; + } + part = p; + id = ev.id(); ++use_count; - // If more than one non-clone part wave event is using the file, signify that the caller should make a copy of it. + } + // If more than one unique part wave event is using the file, signify that the caller should make a copy of it. if(use_count > 1) return true; } @@ -864,7 +1015,7 @@ { refCount = 0; for (int i = 1; true; ++i) { - _name.sprintf("%s.%d", f.basename().toLatin1().constData(), i); + _name.sprintf("%s.%d", f.basename().toLocal8Bit().constData(), i); ciClip ic = waveClips->begin(); for (; ic != waveClips->end(); ++ic) { if ((*ic)->name() == _name) @@ -922,7 +1073,7 @@ QString path = f.dirPath(); // - // waves in the project dirctory are stored + // waves in the project directory are stored // with relative path name, others with absolute path // if (path == MusEGlobal::museProject) @@ -990,7 +1141,7 @@ if ((*i)->name() == name) return Clip(*i); fprintf(stderr, "ClipList: clip <%s> not found\n", - name.toLatin1().constData()); + name.toLocal8Bit().constData()); return Clip(); } @@ -1028,22 +1179,47 @@ // 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, Undo& operations) { if (MusEGlobal::debugMsg) printf("cmdAddRecordedWave - loopCount = %d, punchin = %d", MusEGlobal::audio->loopCount(), punchin()); + // Driver should now be in transport 'stop' mode and no longer pummping the recording wave fifo, + // but the fifo may not be empty yet, it's in the prefetch thread. + // Wait a few seconds for the fifo to be empty, until it has been fully transferred to the + // track's recFile sndfile, which is done via Audio::process() sending periodic 'tick' messages + // to the prefetch thread to write its fifo to the sndfile, always UNLESS in stop or idle mode. + // It now sends one final tick message at stop, so we /should/ have all our buffers available here. + // This GUI thread is notified of the stop condition via the audio thread sending a message + // as soon as the state change is read from the driver. + // NOTE: The fifo scheme is used only if NOT in transport freewheel mode where the data is directly + // written to the sndfile and therefore stops immediately when the transport stops and thus is + // safe to read here regardless of waiting. + int tout = 100; // Ten seconds. Otherwise we gotta move on. + while(track->recordFifoCount() != 0) + { + usleep(100000); + --tout; + if(tout == 0) + { + fprintf(stderr, "Song::cmdAddRecordedWave: Error: Timeout waiting for _tempoFifo to empty! Count:%d\n", track->prefetchFifo()->getCount()); + break; + } + } + + // It should now be safe to work with the resultant sndfile here in the GUI thread. + // No other thread should be touching it right now. MusECore::SndFileR f = track->recFile(); if (f.isNull()) { printf("cmdAddRecordedWave: no snd file for track <%s>\n", - track->name().toLatin1().constData()); + track->name().toLocal8Bit().constData()); return; } - - // If externally clocking (and therefore master was forced off), + + // 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 + // 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 @@ -1054,13 +1230,13 @@ 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. @@ -1070,24 +1246,27 @@ // The function which calls this function already does this immediately after. But do it here anyway. track->setRecFile(NULL); // upon "return", f is removed from the stack, the WaveTrack::_recFile's // counter has dropped by 2 and _recFile will probably deleted then - remove(st.toLatin1().constData()); + remove(st.toLocal8Bit().constData()); if(MusEGlobal::debugMsg) - printf("Song::cmdAddRecordedWave: remove file %s - startframe=%d endframe=%d\n", st.toLatin1().constData(), s.frame(), e.frame()); - - // Restore master flag. + printf("Song::cmdAddRecordedWave: remove file %s - startframe=%d endframe=%d\n", st.toLocal8Bit().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. - 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 eframe = (a_rast == 1) ? e.frame() : Pos(AL::sigmap.raster2(e.tick(), MusEGlobal::song->arrangerRaster())).frame(); - unsigned etick = Pos(eframe, false).tick(); +// REMOVE Tim. Wave. Removed. Probably I should never have done this. It's more annoying than helpful. Look at it another way: Importing a wave DOES NOT do this. +// // Round the start down using the Arranger part snap raster value. +// 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 eframe = (a_rast == 1) ? e.frame() : Pos(AL::sigmap.raster2(e.tick(), MusEGlobal::song->arrangerRaster())).frame(); +// // unsigned etick = Pos(eframe, false).tick(); + unsigned sframe = s.frame(); + unsigned eframe = e.frame(); - // Done using master tempo map. Restore master flag. + // Done using master tempo map. Restore master flag. if(MusEGlobal::extSyncFlag.value() && !master_was_on) MusEGlobal::tempomap.setMasterFlag(0, false); @@ -1101,36 +1280,28 @@ // create Event MusECore::Event event(MusECore::Wave); event.setSndFile(f); - // We are done with the _recFile member. Set to zero. The function which - // calls this function already does this immediately after. But do it here anyway. + // We are done with the _recFile member. Set to zero. 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. - event.setFrame(s.frame() - sframe); + 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(f.samples()); event.setLenFrame(e.frame() - s.frame()); part->addEvent(event); - MusEGlobal::song->cmdAddPart(part); - - if (MusEGlobal::song->len() < etick) - MusEGlobal::song->setLen(etick); + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart, part)); } //--------------------------------------------------------- // cmdChangeWave // called from GUI context //--------------------------------------------------------- -void Song::cmdChangeWave(QString original, QString tmpfile, unsigned sx, unsigned ex) +void Song::cmdChangeWave(const Event& original, QString tmpfile, unsigned sx, unsigned ex) { - char* original_charstr = new char[original.length() + 1]; - char* tmpfile_charstr = new char[tmpfile.length() + 1]; - strcpy(original_charstr, original.toLatin1().constData()); - strcpy(tmpfile_charstr, tmpfile.toLatin1().constData()); - MusEGlobal::song->undoOp(UndoOp::ModifyClip, original_charstr, tmpfile_charstr, sx, ex); + MusEGlobal::song->undoOp(UndoOp::ModifyClip, original, tmpfile, sx, ex); } //--------------------------------------------------------- @@ -1193,72 +1364,317 @@ //--------------------------------------------------------- void MusE::importWave() +{ + MusECore::Track* track = _arranger->curTrack(); + if (track == 0 || track->type() != MusECore::Track::WAVE) { + + //just create new wave track and go on... + if(MusEGlobal::song) { - MusECore::Track* track = _arranger->curTrack(); - if (track == 0 || track->type() != MusECore::Track::WAVE) { - QMessageBox::critical(this, QString("MusE"), - tr("to import an audio file you have first to select" - "a wave track")); - return; - } - QString fn = MusEGui::getOpenFileName(MusEGlobal::lastWavePath, MusEGlobal::audio_file_pattern, this, - tr("Import Wave File"), 0); - if (!fn.isEmpty()) { - MusEGlobal::lastWavePath = fn; - importWaveToTrack(fn); - } + QAction act(MusEGlobal::song); + act.setData(MusECore::Track::WAVE); + track = MusEGlobal::song->addNewTrack(&act, NULL); + } + + if(track == 0) + { + QMessageBox::critical(this, QString("MusE"), + tr("to import an audio file you have first to select" + "a wave track")); + return; + } + } + MusECore::AudioPreviewDialog afd(this, MusEGlobal::sampleRate); + afd.setDirectory(MusEGlobal::lastWavePath); + afd.setWindowTitle(tr("Import Audio File")); + /*QString fn = afd.getOpenFileName(MusEGlobal::lastWavePath, MusEGlobal::audio_file_pattern, this, + tr("Import Audio File"), 0); +*/ + if(afd.exec() == QFileDialog::Rejected) + { + return; + } + + QStringList filenames = afd.selectedFiles(); + if(filenames.size() < 1) + { + return; + } + QString fn = filenames [0]; + + if (!fn.isEmpty()) { + MusEGlobal::lastWavePath = fn; + importWaveToTrack(fn); + } +} + //--------------------------------------------------------- // importWaveToTrack //--------------------------------------------------------- bool MusE::importWaveToTrack(QString& name, unsigned tick, MusECore::Track* track) +{ + if (track==NULL) + track = (MusECore::WaveTrack*)(_arranger->curTrack()); + + + + MusECore::SndFileR f = MusECore::getWave(name, true); + + if (f.isNull()) { + printf("import audio file failed\n"); + return true; + } + track->setChannels(f->channels()); + track->resetMeter(); + int samples = f->samples(); + if ((unsigned)MusEGlobal::sampleRate != f->samplerate()) { + if(QMessageBox::question(this, tr("Import Wavefile"), + tr("This wave file has a samplerate of %1,\n" + "as opposed to current setting %2.\n" + "File will be resampled from %1 to %2 Hz.\n" + "Do you still want to import it?").arg(f->samplerate()).arg(MusEGlobal::sampleRate), + tr("&Yes"), tr("&No"), + QString::null, 0, 1 )) { - if (track==NULL) - track = (MusECore::WaveTrack*)(_arranger->curTrack()); + return true; // this removed f from the stack, dropping refcount maybe to zero and maybe deleting the thing + } - MusECore::SndFileR f = MusECore::getWave(name, true); + //save project if necessary + //copy wave to project's folder, + //rename it if there is a duplicate, + //resample to project's rate - if (f.isNull()) { - printf("import audio file failed\n"); + if(MusEGlobal::museProject == MusEGlobal::museProjectInitPath) + { + if(!MusEGlobal::muse->saveAs()) return true; + } + + QFileInfo fi(f.name()); + QString projectPath = MusEGlobal::museProject + QDir::separator(); + QString fExt = "wav"; + QString fBaseName = fi.baseName(); + QString fNewPath = ""; + bool bNameIsNotUsed = false; + for(int i = 0; i < 1000; i++) + { + fNewPath = projectPath + fBaseName + ((i == 0) ? "" : QString::number(i)) + "." + fExt; + if(!QFile(fNewPath).exists()) + { + bNameIsNotUsed = true; + break; + } + } + + if(!bNameIsNotUsed) + { + QMessageBox::critical(MusEGlobal::muse, tr("Wave import error"), + tr("There are too many wave files\n" + "of the same base name as imported wave file\n" + "Can not continue.")); + return true; + } + + SF_INFO sfiNew; + sfiNew.channels = f.channels(); + sfiNew.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; + sfiNew.frames = 0; + sfiNew.samplerate = MusEGlobal::sampleRate; + sfiNew.seekable = 1; + sfiNew.sections = 0; + + SNDFILE *sfNew = sf_open(fNewPath.toUtf8().constData(), SFM_RDWR, &sfiNew); + if(sfNew == NULL) + { + QMessageBox::critical(MusEGlobal::muse, tr("Wave import error"), + tr("Can't create new wav file in project folder!\n") + sf_strerror(NULL)); + return true; + } + + int srErr = 0; + SRC_STATE *srState = src_new(SRC_SINC_BEST_QUALITY, sfiNew.channels, &srErr); + if(!srState) + { + QMessageBox::critical(MusEGlobal::muse, tr("Wave import error"), + tr("Failed to initialize sample rate converter!")); + sf_close(sfNew); + QFile(fNewPath).remove(); + return true; + } + + + + float fPeekMax = 1.0f; //if output save file will peek above this walue + //it should be normalized later + float fNormRatio = 1.0f / fPeekMax; + int nTriesMax = 5; + int nCurTry = 0; + do + { + QProgressDialog pDlg(MusEGlobal::muse); + pDlg.setMinimum(0); + pDlg.setMaximum(f.samples()); + pDlg.setCancelButtonText(tr("Cancel")); + if(nCurTry == 0) + { + pDlg.setLabelText(tr("Resampling wave file\n" + "\"%1\"\n" + "from %2 to %3 Hz...") + .arg(f.name()).arg(f.samplerate()).arg(sfiNew.samplerate)); + } + else + { + pDlg.setLabelText(tr("Output has clipped\n" + "Resampling again and normalizing wave file\n" + "\"%1\"\n" + "Try %2 of %3...") + .arg(QFileInfo(fNewPath).fileName()).arg(nCurTry).arg(nTriesMax)); + } + pDlg.setWindowModality(Qt::WindowModal); + src_reset(srState); + SRC_DATA sd; + sd.src_ratio = ((double)MusEGlobal::sampleRate) / (double)f.samplerate(); + sf_count_t szBuf = 8192; + float srcBuffer [szBuf]; + float dstBuffer [szBuf]; + unsigned sChannels = f.channels(); + sf_count_t szBufInFrames = szBuf / sChannels; + sf_count_t szFInFrames = f.samples(); + sf_count_t nFramesRead = 0; + sf_count_t nFramesWrote = 0; + sd.end_of_input = 0; + bool bEndOfInput = false; + pDlg.setValue(0); + + f.seek(0, SEEK_SET); + + while(sd.end_of_input == 0) + { + size_t nFramesBuf = 0; + if(bEndOfInput) + sd.end_of_input = 1; + else + { + nFramesBuf = f.readDirect(srcBuffer, szBufInFrames); + if(nFramesBuf == 0) + break; + nFramesRead += nFramesBuf; } - int samples = f->samples(); - if ((unsigned)MusEGlobal::sampleRate != f->samplerate()) { - if(QMessageBox::question(this, tr("Import Wavefile"), - tr("This wave file has a samplerate of %1,\n" - "as opposed to current setting %2.\n" - "Do you still want to import it?").arg(f->samplerate()).arg(MusEGlobal::sampleRate), - tr("&Yes"), tr("&No"), - QString::null, 0, 1 )) + + sd.data_in = srcBuffer; + sd.data_out = dstBuffer; + sd.input_frames = nFramesBuf; + sd.output_frames = szBufInFrames; + sd.input_frames_used = 0; + sd.output_frames_gen = 0; + do + { + if(src_process(srState, &sd) != 0) + break; + sd.data_in += sd.input_frames_used * sChannels; + sd.input_frames -= sd.input_frames_used; + + if(sd.output_frames_gen > 0) + { + nFramesWrote += sd.output_frames_gen; + //detect maximum peek value; + for(unsigned ch = 0; ch < sChannels; ch++) { - return true; // this removed f from the stack, dropping refcount maybe to zero and maybe deleting the thing + + for(long k = 0; k < sd.output_frames_gen; k++) + { + dstBuffer [k * sChannels + ch] *= fNormRatio; //normilize if needed + float fCurPeek = dstBuffer [k * sChannels + ch]; + if(fPeekMax < fCurPeek) + { + //update maximum peek value + fPeekMax = fCurPeek; + } + } } + sf_writef_float(sfNew, dstBuffer, sd.output_frames_gen); + } + else + break; + } - track->setChannels(f->channels()); + while(true); - MusECore::WavePart* part = new MusECore::WavePart((MusECore::WaveTrack *)track); - if (tick) - part->setTick(tick); - else - part->setTick(MusEGlobal::song->cpos()); - part->setLenFrame(samples); + pDlg.setValue(nFramesRead); - MusECore::Event event(MusECore::Wave); - MusECore::SndFileR sf(f); - event.setSndFile(sf); - event.setSpos(0); - event.setLenFrame(samples); - part->addEvent(event); + if(nFramesRead >= szFInFrames) + { + bEndOfInput = true; + } - part->setName(QFileInfo(name).completeBaseName()); - MusEGlobal::audio->msgAddPart(part); - unsigned endTick = part->tick() + part->lenTick(); - if (MusEGlobal::song->len() < endTick) - MusEGlobal::song->setLen(endTick); - return false; - } + if(pDlg.wasCanceled())//free all resources + { + src_delete(srState); + sf_close(sfNew); + f.close(); + f = NULL; + QFile(fNewPath).remove(); + return true; + } + } + + pDlg.setValue(szFInFrames); + + if(fPeekMax > 1.0f) //output has clipped. Normilize it + { + nCurTry++; + sf_seek(sfNew, 0, SEEK_SET); + f.seek(0, SEEK_SET); + pDlg.setValue(0); + fNormRatio = 1.0f / fPeekMax; + fPeekMax = 1.0f; + } + else + break; + } + while(nCurTry <= nTriesMax); + + src_delete(srState); + + sf_close(sfNew); + + f.close(); + f = NULL; + + //reopen resampled wave again + f = MusECore::getWave(fNewPath, true); + if(!f) + { + printf("import audio file failed\n"); + return true; + } + samples = f->samples(); + } + + MusECore::WavePart* part = new MusECore::WavePart((MusECore::WaveTrack *)track); + if (tick) + part->setTick(tick); + else + part->setTick(MusEGlobal::song->cpos()); + part->setLenFrame(samples); + + MusECore::Event event(MusECore::Wave); + MusECore::SndFileR sf(f); + event.setSndFile(sf); + event.setSpos(0); + event.setLenFrame(samples); + part->addEvent(event); + + part->setName(QFileInfo(f->name()).completeBaseName()); + MusEGlobal::audio->msgAddPart(part); + unsigned endTick = part->tick() + part->lenTick(); + if (MusEGlobal::song->len() < endTick) + MusEGlobal::song->setLen(endTick); + return false; +} } // namespace MusEGui diff -Nru muse-2.1.2/muse/waveedit/CMakeLists.txt muse-3.0.2+ds1/muse/waveedit/CMakeLists.txt --- muse-2.1.2/muse/waveedit/CMakeLists.txt 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/waveedit/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( waveedit_mocs +QT5_WRAP_CPP ( waveedit_mocs editgain.h waveedit.h wavecanvas.h @@ -36,7 +36,7 @@ file (GLOB waveedit_ui_files editgainbase.ui ) -QT4_WRAP_UI (waveedit_ui_headers ${waveedit_ui_files} ) +QT5_WRAP_UI (waveedit_ui_headers ${waveedit_ui_files} ) ## ## List of source files to compile diff -Nru muse-2.1.2/muse/waveedit/wavecanvas.cpp muse-3.0.2+ds1/muse/waveedit/wavecanvas.cpp --- muse-2.1.2/muse/waveedit/wavecanvas.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/waveedit/wavecanvas.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -79,7 +79,7 @@ // WEvent //--------------------------------------------------------- -WEvent::WEvent(MusECore::Event& e, MusECore::Part* p, int height) : MusEGui::CItem(e, p) +WEvent::WEvent(const MusECore::Event& e, MusECore::Part* p, int height) : MusEGui::CItem(e, p) { unsigned frame = e.frame() + p->frame(); setPos(QPoint(frame, 0)); @@ -93,7 +93,7 @@ // addItem //--------------------------------------------------------- -CItem* WaveCanvas::addItem(MusECore::Part* part, MusECore::Event& event) +CItem* WaveCanvas::addItem(MusECore::Part* part, const MusECore::Event& event) { if (signed(event.frame())<0) { printf("ERROR: trying to add event before current part!\n"); @@ -149,16 +149,12 @@ void WaveCanvas::songChanged(MusECore::SongChangedFlags_t flags) { - // Is it simply a midi controller value adjustment? Forget it. - if(flags == SC_MIDI_CONTROLLER) - return; - - if (flags & ~SC_SELECTION) { + if (flags & ~(SC_SELECTION | SC_PART_SELECTION | SC_TRACK_SELECTION)) { // TODO FIXME: don't we actually only want SC_PART_*, and maybe SC_TRACK_DELETED? // (same in waveview.cpp) bool curItemNeedsRestore=false; MusECore::Event storedEvent; - int partSn; + int partSn = 0; if (curItem) { curItemNeedsRestore=true; @@ -183,9 +179,8 @@ if (esample > endSample) endSample = esample; - MusECore::EventList* el = part->events(); - for (MusECore::iEvent i = el->begin(); i != el->end(); ++i) { - MusECore::Event e = i->second; + for (MusECore::ciEvent i = part->events().begin(); i != part->events().end(); ++i) { + const MusECore::Event& e = i->second; // Do not add events which are past the end of the part. if(e.frame() > len) break; @@ -212,14 +207,10 @@ int n = 0; // count selections for (iCItem k = items.begin(); k != items.end(); ++k) { - MusECore::Event ev = k->second->event(); - bool selected = ev.selected(); - if (selected) { - k->second->setSelected(true); + if (k->second->event().selected()) { ++n; if (!nevent) { nevent = k->second; - MusECore::Event mi = nevent->event(); } } } @@ -280,8 +271,8 @@ while (i != items.end()) { CItem* cur=i->second; - unsigned int curf=abs(cur->x() + cur->part()->frame() - frame); - unsigned int nearf=abs(nearest->x() + nearest->part()->frame() - frame); + unsigned int curf=abs(cur->x() + (int)cur->part()->frame() - (int)frame); + unsigned int nearf=abs(nearest->x() + (int)nearest->part()->frame() - (int)frame); if (curf < nearf) { nearest=cur; @@ -578,9 +569,9 @@ if(ci->isSelected()) list4.push_back(ci); // Draw clone parts, and parts with hidden events, in front of others all except selected. - //else if(ci->event().empty() && (ci->part()->events()->arefCount() > 1 || ci->part()->cachedHasHiddenEvents())) + //else if(ci->event().empty() && (ci->part()->hasClones() || ci->part()->cachedHasHiddenEvents())) // Draw clone parts in front of others all except selected. - //else if(ci->event().empty() && (ci->part()->events()->arefCount() > 1)) + //else if(ci->event().empty() && ci->part()->hasClones()) // list3.push_back(ci); else // Draw unselected parts. @@ -1327,9 +1318,9 @@ MusECore::Part* opart = ip2c->first; if (opart->hasHiddenEvents()) { - forbidden=true; - break; - } + forbidden=true; + break; + } } @@ -1468,6 +1459,7 @@ x=pframe; event.setFrame(x - pframe); event.setLenFrame(w); + event.setSelected(true); MusECore::Undo operations; int diff = event.endFrame() - part->lenFrame(); @@ -1478,7 +1470,6 @@ if (diff > 0)// part must be extended? { - //schedule_resize_all_same_len_clone_parts(part, event.endTick(), operations); schedule_resize_all_same_len_clone_parts(part, event.endFrame(), operations); printf("newItem: extending\n"); } @@ -1486,7 +1477,7 @@ MusEGlobal::song->applyOperationGroup(operations); } else // forbid action by not applying it - songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is neccessary + songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is necessary //to remove "forbidden" events from the list again } @@ -1533,7 +1524,7 @@ } //else forbid action by not performing it MusEGlobal::song->applyOperationGroup(operations); - songChanged(SC_EVENT_MODIFIED); //this forces an update of the itemlist, which is neccessary + songChanged(SC_EVENT_MODIFIED); //this forces an update of the itemlist, which is necessary //to remove "forbidden" events from the list again } @@ -1686,11 +1677,11 @@ if (part == 0) break; - MusECore::EventList* el = part->events(); + const MusECore::EventList& el = part->events(); MusECore::Undo operations; std::list elist; - for (MusECore::iEvent e = el->lower_bound(pos[0] - part->tick()); e != el->end(); ++e) + for (MusECore::ciEvent e = el.lower_bound(pos[0] - part->tick()); e != el.end(); ++e) elist.push_back((MusECore::Event)e->second); for (std::list::iterator i = elist.begin(); i != elist.end(); ++i) { MusECore::Event event = *i; @@ -1714,10 +1705,10 @@ break; MusECore::Undo operations; - MusECore::EventList* el = part->events(); + const MusECore::EventList& el = part->events(); std::list elist; - for (MusECore::iEvent e = el->lower_bound(pos[0]); e != el->end(); ++e) + for (MusECore::ciEvent e = el.lower_bound(pos[0]); e != el.end(); ++e) elist.push_back((MusECore::Event)e->second); for (std::list::iterator i = elist.begin(); i != elist.end(); ++i) { MusECore::Event event = *i; @@ -1927,17 +1918,17 @@ tempPart->setPos(MusEGlobal::song->lpos()); tempPart->setLenTick(MusEGlobal::song->rpos() - MusEGlobal::song->lpos()); // loop through the events and set them accordingly - for (MusECore::iEvent iWaveEvent = origPart->events()->begin(); iWaveEvent != origPart->events()->end(); iWaveEvent++) + for (MusECore::ciEvent iWaveEvent = origPart->events().begin(); iWaveEvent != origPart->events().end(); iWaveEvent++) { // TODO: handle multiple events correctly, // the math for subsequent events isn't correct - MusECore::Event &ev = iWaveEvent->second; + const MusECore::Event& ev = iWaveEvent->second; MusECore::Event *newEvent = new MusECore::Event(ev.clone()); newEvent->setSpos(ev.spos() + frameDistance); newEvent->setLenTick(MusEGlobal::song->rpos() - MusEGlobal::song->lpos()); tempPart->addEvent(*newEvent); } - std::set partList; + std::set partList; partList.insert(tempPart); QMimeData *mimeData = MusECore::parts_to_mime(partList); @@ -1993,10 +1984,9 @@ MusECore::WavePart* wp = (MusECore::WavePart*)(ip->second); unsigned part_offset = wp->frame(); - MusECore::EventList* el = wp->events(); - //printf("eventlist length=%d\n",el->size()); + const MusECore::EventList& el = wp->events(); - for (MusECore::iEvent e = el->begin(); e != el->end(); ++e) { + for (MusECore::ciEvent e = el.begin(); e != el.end(); ++e) { MusECore::Event event = e->second; if (event.empty()) continue; @@ -2071,7 +2061,11 @@ for(MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++) { MusECore::WaveEventSelection w = *i; + if(w.event.empty()) + continue; MusECore::SndFileR file = w.event.sndFile(); + if(file.isNull()) + continue; if(file.checkCopyOnWrite()) { std::vector::iterator i = copy_files_proj_dir.begin(); @@ -2116,8 +2110,10 @@ for(MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++) { MusECore::WaveEventSelection w = *i; + if(w.event.empty()) + continue; MusECore::SndFileR file = w.event.sndFile(); - if(!file.checkCopyOnWrite()) // Make sure to re-check + if(file.isNull() || !file.checkCopyOnWrite()) // Make sure to re-check continue; QString filePath = MusEGlobal::museProject + QString("/") + file.name(); QString newFilePath; @@ -2197,7 +2193,11 @@ MusEGlobal::song->startUndo(); for (MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++) { MusECore::WaveEventSelection w = *i; + if(w.event.empty()) + continue; MusECore::SndFileR file = w.event.sndFile(); + if(file.isNull()) + continue; unsigned sx = w.startframe; unsigned ex = w.endframe; unsigned file_channels = file.channels(); @@ -2294,7 +2294,7 @@ } // Undo handling - MusEGlobal::song->cmdChangeWave(file.dirPath() + "/" + file.name(), tmpWavFile, sx, ex); + MusEGlobal::song->cmdChangeWave(w.event, tmpWavFile, sx, ex); MusEGlobal::audio->msgIdle(false); // Not good with playback during operations } MusEGlobal::song->endUndo(SC_CLIP_MODIFIED); @@ -2498,23 +2498,23 @@ // startDrag //--------------------------------------------------------- -void WaveCanvas::startDrag(MusEGui::CItem* /* item*/, bool copymode) - { - QMimeData* md = MusECore::selected_events_to_mime(MusECore::partlist_to_set(editor->parts()), 1); - - if (md) { - // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. - // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can - // clean up after the drag and drop operation has been completed. " - QDrag* drag = new QDrag(this); - drag->setMimeData(md); - - if (copymode) - drag->exec(Qt::CopyAction); - else - drag->exec(Qt::MoveAction); - } - } +void WaveCanvas::startDrag(MusEGui::CItem* /* item*/, DragType t) +{ + QMimeData* md = MusECore::selected_events_to_mime(MusECore::partlist_to_set(editor->parts()), 1); + + if (md) { + // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. + // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can + // clean up after the drag and drop operation has been completed. " + QDrag* drag = new QDrag(this); + drag->setMimeData(md); + + if (t == MOVE_COPY || t == MOVE_CLONE) + drag->exec(Qt::CopyAction); + else + drag->exec(Qt::MoveAction); + } +} //--------------------------------------------------------- // dragEnterEvent @@ -2587,9 +2587,8 @@ void WaveCanvas::modifySelected(MusEGui::NoteInfo::ValType type, int val, bool delta_mode) { // TODO: New WaveCanvas: Convert this routine to frames and remove unneeded operations. - QList< QPair > already_done; - MusEGlobal::audio->msgIdle(true); - MusEGlobal::song->startUndo(); + QList< QPair > already_done; + MusECore::Undo operations; for (MusEGui::iCItem i = items.begin(); i != items.end(); ++i) { if (!(i->second->isSelected())) continue; @@ -2600,7 +2599,7 @@ MusECore::WavePart* part = (MusECore::WavePart*)(e->part()); - if (already_done.contains(QPair(part->events(), event))) + if (already_done.contains(QPair(part->clonemaster_sn(), event))) continue; MusECore::Event newEvent = event.clone(); @@ -2636,7 +2635,9 @@ if (velo > 127) velo = 127; else if (velo < 0) - velo = 0; + // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now. +// velo = 0; + velo = 1; newEvent.setVelo(velo); } break; @@ -2666,14 +2667,11 @@ break; } - MusEGlobal::song->changeEvent(event, newEvent, part); - // Indicate do not do port controller values and clone parts. - MusEGlobal::song->addUndo(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); - already_done.append(QPair(part->events(), event)); + already_done.append(QPair(part->clonemaster_sn(), event)); } - MusEGlobal::song->endUndo(SC_EVENT_MODIFIED); - MusEGlobal::audio->msgIdle(false); + MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/waveedit/wavecanvas.h muse-3.0.2+ds1/muse/waveedit/wavecanvas.h --- muse-2.1.2/muse/waveedit/wavecanvas.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/waveedit/wavecanvas.h 2018-01-26 21:59:38.000000000 +0000 @@ -64,7 +64,7 @@ class WEvent : public CItem { public: - WEvent(MusECore::Event& e, MusECore::Part* p, int height); + WEvent(const MusECore::Event& e, MusECore::Part* p, int height); }; //--------------------------------------------------------- @@ -83,7 +83,7 @@ unsigned endSample; int colorMode; int selectionStart, selectionStop, dragstartx; - int lastGainvalue; //!< Stores the last used gainvalue when specifiying gain value in the editgain dialog + int lastGainvalue; //!< Stores the last used gainvalue when specifying gain value in the editgain dialog QString copiedPart; //bool getUniqueTmpfileName(QString& newFilename); //!< Generates unique filename for temporary SndFile @@ -120,11 +120,11 @@ virtual void resizeItem(CItem*, bool noSnap, bool); virtual void newItem(CItem*, bool noSnap); virtual bool deleteItem(CItem*); - virtual void startDrag(CItem* item, bool copymode); + virtual void startDrag(CItem* item, DragType); virtual void dragEnterEvent(QDragEnterEvent* event); virtual void dragMoveEvent(QDragMoveEvent*); virtual void dragLeaveEvent(QDragLeaveEvent*); - virtual CItem* addItem(MusECore::Part*, MusECore::Event&); + virtual CItem* addItem(MusECore::Part*, const MusECore::Event&); int y2pitch(int) const; int pitch2y(int) const; diff -Nru muse-2.1.2/muse/waveedit/waveedit.cpp muse-3.0.2+ds1/muse/waveedit/waveedit.cpp --- muse-2.1.2/muse/waveedit/waveedit.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/waveedit/waveedit.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -59,6 +59,7 @@ #include "icons.h" #include "shortcuts.h" #include "cmd.h" +#include "operations.h" namespace MusECore { extern QColor readColor(MusECore::Xml& xml); @@ -240,14 +241,22 @@ //-------------------------------------------------- // ToolBar: Solo Cursor1 Cursor2 + // NOTICE: Please ensure that any tool bar object names here match the names assigned + // to identical or similar toolbars in class MusE or other TopWin classes. + // This allows MusE::setCurrentMenuSharingTopwin() to do some magic + // to retain the original toolbar layout. If it finds an existing + // toolbar with the same object name, it /replaces/ it using insertToolBar(), + // instead of /appending/ with addToolBar(). + + addToolBarBreak(); + + // Already has an object name. tools2 = new MusEGui::EditToolBar(this, waveEditTools); addToolBar(tools2); - addToolBarBreak(); tb1 = addToolBar(tr("WaveEdit tools")); - tb1->setObjectName("WaveEdit tools"); + tb1->setObjectName("Wave Pos/Snap/Solo-tools"); - //tb1->setLabel(tr("weTools")); solo = new QToolButton(); solo->setText(tr("Solo")); solo->setCheckable(true); @@ -468,6 +477,11 @@ void WaveEdit::cmd(int n) { + // Don't process if user is dragging or has clicked on the events. + // Causes crashes later in Canvas::viewMouseMoveEvent and viewMouseReleaseEvent. + if(canvas->getCurrentDrag()) + return; + ((WaveCanvas*)canvas)->cmd(n); } @@ -529,8 +543,8 @@ xml.tag(level++, "waveedit"); MidiEditor::writeStatus(level, xml); xml.intTag(level, "tool", int(canvas->tool())); - xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "xmag", hscroll->mag()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", ymag->value()); xml.tag(level, "/waveedit"); } @@ -604,8 +618,10 @@ void WaveEdit::soloChanged(bool flag) { MusECore::WavePart* part = (MusECore::WavePart*)(parts()->begin()->second); - MusEGlobal::audio->msgSetSolo(part->track(), flag); - MusEGlobal::song->update(SC_SOLO); + // This is a minor operation easily manually undoable. Let's not clog the undo list with it. + MusECore::PendingOperationList operations; + operations.add(MusECore::PendingOperationItem(part->track(), flag, MusECore::PendingOperationItem::SetTrackSolo)); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/waveedit/waveview.cpp muse-3.0.2+ds1/muse/waveedit/waveview.cpp --- muse-2.1.2/muse/waveedit/waveview.cpp 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/waveedit/waveview.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -119,13 +119,12 @@ int channels = wp->track()->channels(); int px = wp->frame(); - MusECore::EventList* el = wp->events(); - for (MusECore::iEvent e = el->begin(); e != el->end(); ++e) { - MusECore::Event event = e->second; + for (MusECore::ciEvent e = wp->events().begin(); e != wp->events().end(); ++e) { + const MusECore::Event event& = e->second; if (event.empty()) continue; MusECore::SndFileR f = event.sndFile(); - if (f.isNull()) + if(f.isNull()) continue; unsigned peoffset = px + event.frame() - event.spos(); @@ -256,11 +255,7 @@ void WaveView::songChanged(MusECore::SongChangedFlags_t flags) { - // Is it simply a midi controller value adjustment? Forget it. - if(flags == SC_MIDI_CONTROLLER) - return; - - if (flags & ~SC_SELECTION) { + if (flags & ~(SC_SELECTION | SC_PART_SELECTION | SC_TRACK_SELECTION)) { // TODO FIXME: don't we actually only want SC_PART_*, and maybe SC_TRACK_DELETED? // (same in ecanvas.cpp) startSample = INT_MAX; @@ -526,497 +521,6 @@ *e = MusEGlobal::tempomap.tick2frame(pe); } -//--------------------------------------------------------- -// cmd -//--------------------------------------------------------- -void WaveView::cmd(int n) - { - int modifyoperation = -1; - double paramA = 0.0; - - switch(n) { - case WaveEdit::CMD_SELECT_ALL: - if (!editor->parts()->empty()) { - MusECore::iPart iBeg = editor->parts()->begin(); - MusECore::iPart iEnd = editor->parts()->end(); - iEnd--; - MusECore::WavePart* beg = (MusECore::WavePart*) iBeg->second; - MusECore::WavePart* end = (MusECore::WavePart*) iEnd->second; - selectionStart = beg->frame(); - selectionStop = end->frame() + end->lenFrame(); - redraw(); - } - break; - - case WaveEdit::CMD_EDIT_EXTERNAL: - modifyoperation = EDIT_EXTERNAL; - break; - - case WaveEdit::CMD_SELECT_NONE: - selectionStart = selectionStop = 0; - redraw(); - break; - case WaveEdit::CMD_EDIT_COPY: - modifyoperation = COPY; - break; - case WaveEdit::CMD_EDIT_CUT: - modifyoperation = CUT; - break; - case WaveEdit::CMD_EDIT_PASTE: - modifyoperation = PASTE; - break; - - case WaveEdit::CMD_MUTE: - modifyoperation = MUTE; - break; - - case WaveEdit::CMD_NORMALIZE: - modifyoperation = NORMALIZE; - break; - - case WaveEdit::CMD_FADE_IN: - modifyoperation = FADE_IN; - break; - - case WaveEdit::CMD_FADE_OUT: - modifyoperation = FADE_OUT; - break; - - case WaveEdit::CMD_REVERSE: - modifyoperation = REVERSE; - break; - - case WaveEdit::CMD_GAIN_FREE: { - EditGain* editGain = new EditGain(this, lastGainvalue); - if (editGain->exec() == QDialog::Accepted) { - lastGainvalue = editGain->getGain(); - modifyoperation = GAIN; - paramA = (double)lastGainvalue / 100.0; - } - delete editGain; - } - break; - case WaveEdit::CMD_GAIN_200: - modifyoperation = GAIN; - paramA = 2.0; - break; - - case WaveEdit::CMD_GAIN_150: - modifyoperation = GAIN; - paramA = 1.5; - break; - - case WaveEdit::CMD_GAIN_75: - modifyoperation = GAIN; - paramA = 0.75; - break; - - case WaveEdit::CMD_GAIN_50: - modifyoperation = GAIN; - paramA = 0.5; - break; - - case WaveEdit::CMD_GAIN_25: - modifyoperation = GAIN; - paramA = 0.25; - break; - - default: - break; - } - - if (modifyoperation != -1) { - if (selectionStart == selectionStop && modifyoperation!=PASTE) { - printf("No selection. Ignoring\n"); //@!TODO: Disable menu options when no selection - QMessageBox::information(this, - QString("MusE"), - QWidget::tr("No selection. Ignoring")); - - return; - } - - //if(!modifyWarnedYet) - //{ - // modifyWarnedYet = true; - // if(QMessageBox::warning(this, QString("Muse"), - // tr("Warning! Muse currently operates directly on the sound file.\n" - // "Undo is supported, but NOT after exit, WITH OR WITHOUT A SAVE!\n" - // "If you are stuck, try deleting the associated .wca file and reloading."), tr("&Ok"), tr("&Cancel"), - // QString::null, 0, 1 ) != 0) - // return; - //} - modifySelection(modifyoperation, selectionStart, selectionStop, paramA); - } - } - - -//--------------------------------------------------------- -// getSelection -//--------------------------------------------------------- -MusECore::WaveSelectionList WaveView::getSelection(unsigned startpos, unsigned stoppos) - { - MusECore::WaveSelectionList selection; - - for (MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { - MusECore::WavePart* wp = (MusECore::WavePart*)(ip->second); - unsigned part_offset = wp->frame(); - - MusECore::EventList* el = wp->events(); - //printf("eventlist length=%d\n",el->size()); - - for (MusECore::iEvent e = el->begin(); e != el->end(); ++e) { - MusECore::Event event = e->second; - if (event.empty()) - continue; - MusECore::SndFileR file = event.sndFile(); - if (file.isNull()) - continue; - - unsigned event_offset = event.frame() + part_offset; - unsigned event_startpos = event.spos(); - unsigned event_length = event.lenFrame() + event.spos(); - unsigned event_end = event_offset + event_length; - //printf("startpos=%d stoppos=%d part_offset=%d event_offset=%d event_startpos=%d event_length=%d event_end=%d\n", startpos, stoppos, part_offset, event_offset, event_startpos, event_length, event_end); - - if (!(event_end <= startpos || event_offset > stoppos)) { - int tmp_sx = startpos - event_offset + event_startpos; - int tmp_ex = stoppos - event_offset + event_startpos; - unsigned sx; - unsigned ex; - - tmp_sx < (int)event_startpos ? sx = event_startpos : sx = tmp_sx; - tmp_ex > (int)event_length ? ex = event_length : ex = tmp_ex; - - //printf("Event data affected: %d->%d filename:%s\n", sx, ex, file.name().toLatin1().constData()); - MusECore::WaveEventSelection s; - s.file = file; - s.startframe = sx; - s.endframe = ex+1; - //printf("sx=%d ex=%d\n",sx,ex); - selection.push_back(s); - } - } - } - - return selection; - } - -//--------------------------------------------------------- -// modifySelection -//--------------------------------------------------------- -void WaveView::modifySelection(int operation, unsigned startpos, unsigned stoppos, double paramA) - { - MusEGlobal::song->startUndo(); - - if (operation == PASTE) { - // we need to redefine startpos and stoppos - if (copiedPart =="") - return; - MusECore::SndFile pasteFile(copiedPart); - pasteFile.openRead(); - startpos = pos[0]; - stoppos = startpos+ pasteFile.samples(); // possibly this is wrong if there are tempo changes - pasteFile.close(); - pos[0]=stoppos; - } - - MusECore::WaveSelectionList selection = getSelection(startpos, stoppos); - for (MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++) { - MusECore::WaveEventSelection w = *i; - MusECore::SndFileR& file = w.file; - unsigned sx = w.startframe; - unsigned ex = w.endframe; - unsigned file_channels = file.channels(); - - QString tmpWavFile = QString::null; - if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", tmpWavFile)) { - break; - } - - MusEGlobal::audio->msgIdle(true); // Not good with playback during operations - MusECore::SndFile tmpFile(tmpWavFile); - tmpFile.setFormat(file.format(), file_channels, file.samplerate()); - if (tmpFile.openWrite()) { - MusEGlobal::audio->msgIdle(false); - printf("Could not open temporary file...\n"); - break; - } - - // - // Write out data that will be changed to temp file - // - unsigned tmpdatalen = ex - sx; - off_t tmpdataoffset = sx; - float* tmpdata[file_channels]; - - for (unsigned i=0; icmdChangeWave(file.dirPath() + "/" + file.name(), tmpWavFile, sx, ex); - MusEGlobal::audio->msgIdle(false); // Not good with playback during operations - } - MusEGlobal::song->endUndo(SC_CLIP_MODIFIED); - redraw(); - } - -//--------------------------------------------------------- -// copySelection -//--------------------------------------------------------- -void WaveView::copySelection(unsigned file_channels, float** tmpdata, unsigned length, bool blankData, unsigned format, unsigned sampleRate) -{ - if (copiedPart!="") { - QFile::remove(copiedPart); - } - if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", copiedPart)) { - return; - } - - MusECore::SndFile tmpFile(copiedPart); - tmpFile.setFormat(format, file_channels, sampleRate); - tmpFile.openWrite(); - tmpFile.write(file_channels, tmpdata, length); - tmpFile.close(); - - if (blankData) { - // Set everything to 0! - for (unsigned i=0; i loudest) - loudest = data[i][j]; - } - } - - double scale = 0.99 / (double)loudest; - - for (unsigned i=0; iAudio:External Waveditor\nis set to a valid editor.")); - } - - if (exttmpFile.openRead()) { - printf("Could not reopen temporary file!\n"); - } - else { - // Re-read file again - exttmpFile.seek(0, 0); - size_t sz = exttmpFile.readWithHeap(file_channels, tmpdata, tmpdatalen); - if (sz != tmpdatalen) { - // File must have been shrunken - not good. Alert user. - QMessageBox::critical(this, tr("MusE - file size changed"), - tr("When editing in external editor - you should not change the filesize\nsince it must fit the selected region.\n\nMissing data is muted")); - for (unsigned i=0; i(&other_); + if (other==NULL) // dynamic cast hsa failed: "other_" is not of type WaveEventBase. + return false; + + return f.dirPath()==other->f.dirPath() && _spos==other->_spos && this->PosLen::operator==(*other); } //--------------------------------------------------------- // WaveEvent::mid //--------------------------------------------------------- -EventBase* WaveEventBase::mid(unsigned b, unsigned e) +EventBase* WaveEventBase::mid(unsigned b, unsigned e) const { WaveEventBase* ev = new WaveEventBase(*this); unsigned fr = frame(); @@ -144,7 +173,7 @@ xml.intTag(level, "frame", _spos); // offset in wave file // - // waves in the project dirctory are stored + // waves in the project directory are stored // with relative path name, others with absolute path // QString path = f.dirPath(); @@ -175,7 +204,7 @@ #ifdef USE_SAMPLERATE // TODO: - >>>>>>>>>>>+++++++++++++++++++++++++++++ + //>>>>>>>>>>>+++++++++++++++++++++++++++++ // If we have a valid audio converter then use it to do the processing. Otherwise just a normal seek + read. if(audConv) //sfCurFrame = audConv->process(f, sfCurFrame, offset + _spos, buffer, channel, n, doSeek, overwrite); DELETETHIS @@ -299,7 +328,7 @@ // Point the out buffer at our local buffers. // poutbuf = &outbuffer[0]; - // Converter channels are fixed at creation time! Can't change them on the fly. Can't use 'channel' paramter. + // Converter channels are fixed at creation time! Can't change them on the fly. Can't use 'channel' parameter. //rn = f.read(inbuffer, inFrames); rn = f.readDirect(inbuffer, inFrames); diff -Nru muse-2.1.2/muse/waveevent.h muse-3.0.2+ds1/muse/waveevent.h --- muse-2.1.2/muse/waveevent.h 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/waveevent.h 2017-12-04 21:01:18.000000000 +0000 @@ -42,17 +42,25 @@ QString _name; SndFileR f; int _spos; // start sample position in WaveFile - bool deleted; - virtual EventBase* clone(); + // Creates a non-shared clone (copies event base), including the same 'group' id. + virtual EventBase* clone() const { return new WaveEventBase(*this); } + // Creates a copy of the event base, excluding the 'group' _id. + virtual EventBase* duplicate() const { return new WaveEventBase(*this, true); } public: WaveEventBase(EventType t); + // Creates a non-shared clone with same id, or duplicate with unique id, and 0 ref count and invalid Pos sn. + WaveEventBase(const WaveEventBase& ev, bool duplicate_not_clone = false); virtual ~WaveEventBase() {} + + virtual void assign(const EventBase& ev); // Assigns to this event, excluding the _id. + + virtual bool isSimilarTo(const EventBase& other) const; virtual void read(Xml&); virtual void write(int, Xml&, const Pos& offset, bool forcePath = false) const; - virtual EventBase* mid(unsigned, unsigned); + virtual EventBase* mid(unsigned, unsigned) const; virtual void dump(int n = 0) const; diff -Nru muse-2.1.2/muse/wave.h muse-3.0.2+ds1/muse/wave.h --- muse-2.1.2/muse/wave.h 2013-03-28 15:17:37.000000000 +0000 +++ muse-3.0.2+ds1/muse/wave.h 2017-12-04 21:01:18.000000000 +0000 @@ -25,6 +25,7 @@ #define __WAVE_H__ #include +#include #include #include @@ -33,6 +34,8 @@ namespace MusECore { +class Event; + class Xml; //--------------------------------------------------------- @@ -45,6 +48,8 @@ unsigned char rms; }; +typedef std::vector SampleVtype; + class SndFileList; //--------------------------------------------------------- @@ -56,14 +61,18 @@ SNDFILE* sf; SNDFILE* sfUI; SF_INFO sfinfo; - SampleV** cache; - int csize; //!< frames in cache + SampleVtype* cache; + sf_count_t csize; //!< frames in cache + + float *writeBuffer; + size_t writeSegSize; void writeCache(const QString& path); bool openFlag; bool writeFlag; size_t readInternal(int srcChannels, float** dst, size_t n, bool overwrite, float *buffer); + size_t realWrite(int channel, float**, size_t n, size_t offs = 0); protected: int refCount; @@ -74,18 +83,19 @@ int getRefCount() { return refCount; } static SndFileList sndFiles; - static void applyUndoFile(const QString& original, const QString& tmpfile, unsigned sx, unsigned ex); + static void applyUndoFile(const Event& original, const QString* tmpfile, unsigned sx, unsigned ex); + void createCache(const QString& path, bool showProgress, bool bWrite, sf_count_t cstart = 0); void readCache(const QString& path, bool progress); - bool openRead(); //!< returns true on error + bool openRead(bool createCache=true, bool showProgress=true); //!< returns true on error bool openWrite(); //!< returns true on error void close(); void remove(); bool isOpen() const { return openFlag; } bool isWritable() const { return writeFlag; } - void update(); + void update(bool showProgress = true); bool checkCopyOnWrite(); //!< check if the file should be copied before writing to it QString basename() const; //!< filename without extension @@ -95,7 +105,7 @@ QString canonicalPath() const; //!< path with filename, resolved (no symlinks or . .. etc) QString name() const; //!< filename - unsigned samples() const; + sf_count_t samples() const; unsigned channels() const; unsigned samplerate() const; unsigned format() const; @@ -106,12 +116,14 @@ size_t readWithHeap(int channel, float**, size_t, bool overwrite = true); size_t readDirect(float* buf, size_t n) { return sf_readf_float(sf, buf, n); } size_t write(int channel, float**, size_t); + size_t writeDirect(float *buf, size_t n) { return sf_writef_float(sf, buf, n); } off_t seek(off_t frames, int whence); - void read(SampleV* s, int mag, unsigned pos, bool overwrite = true); + void read(SampleV* s, int mag, unsigned pos, bool overwrite = true, bool allowSeek = true); QString strerror() const; static SndFile* search(const QString& name); + friend class SndFileR; }; @@ -135,52 +147,52 @@ const SndFile* operator->() const { return sf; } operator bool() { return sf!=NULL; } ~SndFileR(); - int getRefCount() const { return sf->refCount; } + int getRefCount() const { return sf ? sf->refCount : 0; } bool isNull() const { return sf == 0; } - bool openRead() { return sf->openRead(); } - bool openWrite() { return sf->openWrite(); } - void close() { sf->close(); } - void remove() { sf->remove(); } - - bool isOpen() const { return sf->isOpen(); } - bool isWritable() const { return sf->isWritable(); } - void update() { sf->update(); } - bool checkCopyOnWrite() { return sf->checkCopyOnWrite(); }; - - QString basename() const { return sf->basename(); } - QString dirPath() const { return sf->dirPath(); } - QString canonicalDirPath() const { return sf->canonicalDirPath(); } - QString path() const { return sf->path(); } - QString canonicalPath() const { return sf->canonicalPath(); } - QString name() const { return sf->name(); } - - unsigned samples() const { return sf->samples(); } - unsigned channels() const { return sf->channels(); } - unsigned samplerate() const { return sf->samplerate(); } - unsigned format() const { return sf->format(); } - int sampleBits() const { return sf->sampleBits(); } + bool openRead(bool createCache=true) { return sf ? sf->openRead(createCache) : true; } + bool openWrite() { return sf ? sf->openWrite() : true; } + void close() { if(sf) sf->close(); } + void remove() { if(sf) sf->remove(); } + + bool isOpen() const { return sf ? sf->isOpen() : false; } + bool isWritable() const { return sf ? sf->isWritable() : false; } + void update() { if(sf) sf->update(); } + bool checkCopyOnWrite() { return sf ? sf->checkCopyOnWrite() : false; } + + QString basename() const { return sf ? sf->basename() : QString(); } + QString dirPath() const { return sf ? sf->dirPath() : QString(); } + QString canonicalDirPath() const { return sf ? sf->canonicalDirPath() : QString(); } + QString path() const { return sf ? sf->path() : QString(); } + QString canonicalPath() const { return sf ? sf->canonicalPath() : QString(); } + QString name() const { return sf ? sf->name() : QString(); } + + unsigned samples() const { return sf ? sf->samples() : 0; } + unsigned channels() const { return sf ? sf->channels() : 0; } + unsigned samplerate() const { return sf ? sf->samplerate() : 0; } + unsigned format() const { return sf ? sf->format() : 0; } + int sampleBits() const { return sf ? sf->sampleBits() : 0; } void setFormat(int fmt, int ch, int rate) { - sf->setFormat(fmt, ch, rate); + if(sf) sf->setFormat(fmt, ch, rate); } size_t readWithHeap(int channel, float** f, size_t n, bool overwrite = true) { - return sf->readWithHeap(channel, f, n, overwrite); + return sf ? sf->readWithHeap(channel, f, n, overwrite) : 0; } size_t read(int channel, float** f, size_t n, bool overwrite = true) { - return sf->read(channel, f, n, overwrite); + return sf ? sf->read(channel, f, n, overwrite) : 0; } - size_t readDirect(float* f, size_t n) { return sf->readDirect(f, n); } + size_t readDirect(float* f, size_t n) { return sf ? sf->readDirect(f, n) : 0; } size_t write(int channel, float** f, size_t n) { - return sf->write(channel, f, n); + return sf ? sf->write(channel, f, n) : 0; } off_t seek(off_t frames, int whence) { - return sf->seek(frames, whence); + return sf ? sf->seek(frames, whence) : 0; } - void read(SampleV* s, int mag, unsigned pos, bool overwrite = true) { - sf->read(s, mag, pos, overwrite); + void read(SampleV* s, int mag, unsigned pos, bool overwrite = true, bool allowSeek = true) { + if(sf) sf->read(s, mag, pos, overwrite, allowSeek); } - QString strerror() const { return sf->strerror(); } + QString strerror() const { return sf ? sf->strerror() : QString(); } }; @@ -289,7 +301,7 @@ extern ClipBase* readClip(Xml& xml); #endif -extern SndFileR getWave(const QString& name, bool readOnlyFlag); +extern SndFileR getWave(const QString& name, bool readOnlyFlag, bool openFlag = true, bool showErrorBox = true); } // namespace MusECore diff -Nru muse-2.1.2/muse/wavepreview.cpp muse-3.0.2+ds1/muse/wavepreview.cpp --- muse-2.1.2/muse/wavepreview.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/wavepreview.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,269 @@ +#include "wavepreview.h" +#include + + +namespace MusEGlobal +{ +MusECore::WavePreview *wavePreview; +} + +namespace MusECore +{ + +WavePreview::WavePreview(int segmentSize): + sf(0), + src(0), + isPlaying(false), + sem(1) +{ + segSize = segmentSize * 10; + tmpbuffer = new float [segSize]; + srcbuffer = new float [segSize]; +} + +WavePreview::~WavePreview() +{ + stop(); + delete[] tmpbuffer; + delete[] srcbuffer; +} + +long WavePreview::static_srcCallback (void *cb_data, float **data) +{ + MusECore::WavePreview *wp = (MusECore::WavePreview *)cb_data; + wp->nread = sf_readf_float(wp->sf, wp->tmpbuffer, wp->segSize / wp->sfi.channels); + *data = wp->tmpbuffer; + return wp->nread; +} + +void WavePreview::play(QString path, int systemSampleRate) +{ + stop(); + memset(&sfi, 0, sizeof(sfi)); + sf = sf_open(path.toUtf8().data(), SFM_READ, &sfi); + if(sf) + { + int err = 0; + //src = src_new(SRC_SINC_BEST_QUALITY, sfi.channels, &err); + src = src_callback_new(static_srcCallback, SRC_SINC_MEDIUM_QUALITY, sfi.channels, &err, this); + if(src) + { + p1 = tmpbuffer; + p2 = srcbuffer; + f1 = 0; + f2 = 0; + nread = 0; + sd.src_ratio = ((double)systemSampleRate) / (double)sfi.samplerate; + isPlaying = true; + } + else + { + sf_close(sf); + sf = 0; + } + + } + +} + +void WavePreview::stop() +{ + isPlaying = false; + sem.acquire(); + if(sf) + { + sf_close(sf); + sf = 0; + } + if(src) + { + src_delete(src); + src = 0; + } + sem.release(); +} + +void WavePreview::addData(int channels, int nframes, float *buffer[]) +{ + if(sf && isPlaying) + { + sem.acquire(); + + if(!isPlaying) + { + sem.release(); + return; + } + + memset(srcbuffer, 0, sizeof(segSize) * sizeof(float)); + /*p2 = srcbuffer; + f2 = 0; + + while(true) + { + if(nread <= 0) + { + f1 = 0; + p1 = tmpbuffer; + nread = sf_readf_float(sf, tmpbuffer, nframes); + + if(nread <= 0) + { + isPlaying = false; + return; + } + } + sd.data_in = p1; + sd.data_out = p2; + sd.end_of_input = (nread == nframes) ? false : true; + sd.input_frames = nread; + sd.output_frames = nframes - f2; + sd.input_frames_used = sd.output_frames_gen = 0; + int err = src_process(src, &sd); + if(err != 0) + { + break; + } + p1 += sd.input_frames_used * sfi.channels; + p2 += sd.output_frames_gen * sfi.channels; + f1 += sd.input_frames_used; + f2 += sd.output_frames_gen; + nread -= sd.input_frames_used; + if((f2 >= nframes) || sd.end_of_input) + { + break; + } + }*/ + + int rd = src_callback_read(src, sd.src_ratio, nframes, srcbuffer); + if(rd < nframes) + { + isPlaying = false; + } + + if(rd == 0) + { + sem.release(); + return; + } + + int chns = std::min(channels, sfi.channels); + for(int i = 0; i < chns; i++) + { + for(int k = 0; k < nframes; k++) + { + buffer [i] [k] += srcbuffer [k * sfi.channels + i]; + if((channels > 1) && (sfi.channels == 1)) + { + buffer [1] [k] += srcbuffer [k * sfi.channels + i]; + } + + } + } + sem.release(); + } +} + +void initWavePreview(int segmentSize) +{ + if(!MusEGlobal::wavePreview) + MusEGlobal::wavePreview = new WavePreview(segmentSize); +} + +void exitWavePreview() +{ + if(MusEGlobal::wavePreview) + { + delete MusEGlobal::wavePreview; + } +} + +void AudioPreviewDialog::urlChanged(const QString &str) +{ + QFileInfo fi(str); + if(fi.isDir()){ + return; + } + if(chAutoPlay->isChecked()) + { + MusEGlobal::wavePreview->play(str, _systemSampleRate); + } +} + +void AudioPreviewDialog::startStopWave() +{ + if(MusEGlobal::wavePreview->getIsPlaying()) + { + MusEGlobal::wavePreview->stop(); + } + else + { + QStringList files = selectedFiles(); + if(files.size() > 0) + { + QString file = files [0]; + QFileInfo fi(file); + if(fi.isFile()) + { + MusEGlobal::wavePreview->play(file, _systemSampleRate); + } + } + + } +} + +int AudioPreviewDialog::exec() +{ + int r = QFileDialog::exec(); + MusEGlobal::wavePreview->stop(); + return r; +} + +AudioPreviewDialog::AudioPreviewDialog(QWidget *parent, int systemSampleRate) + :QFileDialog(parent), + lastIsPlaying(false), + _systemSampleRate(systemSampleRate) +{ + setOption(QFileDialog::DontUseNativeDialog); + setNameFilter(QString("Samples *.wav *.ogg *.flac (*.wav *.WAV *.ogg *.flac);;All files (*)")); + //cb = new QComboBox; + //cb->setEditable(false); + //cb->addItems(list_ports()); + //cb->setCurrentIndex(cb->count() - 1); + + chAutoPlay = new QCheckBox(this); + chAutoPlay->setText(tr("Auto play")); + chAutoPlay->setChecked(true); + + + btnStop = new QPushButton(tr("Stop")); + connect(btnStop, SIGNAL(clicked()), this, SLOT(startStopWave())); + + + //QListView *v = this->findChild("listView", Qt::FindChildrenRecursively); + QObject::connect(this, SIGNAL(currentChanged(const QString&)), this, SLOT(urlChanged(const QString&))); + //this->layout()->addWidget(new QLabel("Midi device: ")); + //this->layout()->addWidget(cb); + this->layout()->addWidget(chAutoPlay); + this->layout()->addWidget(btnStop); + startTimer(30); + +} + +AudioPreviewDialog::~AudioPreviewDialog() +{ + MusEGlobal::wavePreview->stop(); +} + +void AudioPreviewDialog::timerEvent(QTimerEvent *) +{ + if(lastIsPlaying != MusEGlobal::wavePreview->getIsPlaying()) + { + lastIsPlaying = MusEGlobal::wavePreview->getIsPlaying(); + btnStop->setText(lastIsPlaying ? tr("Stop") : tr("Play")); + } + +} + + +} diff -Nru muse-2.1.2/muse/wavepreview.h muse-3.0.2+ds1/muse/wavepreview.h --- muse-2.1.2/muse/wavepreview.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/wavepreview.h 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,76 @@ +#ifndef WAVEPREVIEW_H +#define WAVEPREVIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace MusECore +{ + +class WavePreview +{ +private: + SNDFILE *sf; + SF_INFO sfi; + SRC_STATE *src; + bool isPlaying; + float *tmpbuffer; + float *srcbuffer; + int segSize; + float *p1; + float *p2; + int f1; + int f2; + SRC_DATA sd; + sf_count_t nread; + QSemaphore sem; + static long static_srcCallback (void *cb_data, float **data); +public: + WavePreview(int segmentSize); + virtual ~WavePreview(); + void play(QString path, int systemSampleRate); + void stop(); + void addData(int channels, int nframes, float *buffer []); + bool getIsPlaying() { return isPlaying; } + +}; + +class AudioPreviewDialog : public QFileDialog{ + Q_OBJECT +private: + QCheckBox *chAutoPlay; + QPushButton *btnStop; + bool lastIsPlaying; + int _systemSampleRate; +private slots: + void urlChanged(const QString &str); + void startStopWave(); +public slots: + virtual int exec(); +public: + AudioPreviewDialog(QWidget *parent, int systemSampleRate); + ~AudioPreviewDialog(); + void timerEvent(QTimerEvent *); +}; + + +extern void initWavePreview(int segmentSize); +extern void exitWavePreview(); + +} + + + +namespace MusEGlobal +{ +extern MusECore::WavePreview *wavePreview; +} + +#endif diff -Nru muse-2.1.2/muse/wavetrack.cpp muse-3.0.2+ds1/muse/wavetrack.cpp --- muse-2.1.2/muse/wavetrack.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/wavetrack.cpp 2018-01-11 17:41:21.000000000 +0000 @@ -21,6 +21,8 @@ // //========================================================= +#include + #include "track.h" #include "event.h" #include "audio.h" @@ -55,34 +57,26 @@ return; //const WaveTrack& wt = (const WaveTrack&)t; - if(flags & ASSIGN_PARTS) + const bool dup = flags & ASSIGN_DUPLICATE_PARTS; + const bool cpy = flags & ASSIGN_COPY_PARTS; + const bool cln = flags & ASSIGN_CLONE_PARTS; + if(dup || cpy || cln) { const PartList* pl = t.cparts(); for (ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* spart = ip->second; - bool clone = spart->events()->arefCount() > 1; - // This increments aref count if cloned, and chains clones. - // It also gives the new part a new serial number. - Part* dpart = newPart(spart, clone); - if(!clone) { - // Copy Events - MusECore::EventList* se = spart->events(); - MusECore::EventList* de = dpart->events(); - for (MusECore::iEvent i = se->begin(); i != se->end(); ++i) { - MusECore::Event oldEvent = i->second; - MusECore::Event ev = oldEvent.clone(); - de->add(ev); - } - } - - // TODO: Should we include the parts in the undo? - // dpart->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it - // // so we must decrement it first :/ - // // These will not increment ref count, and will not chain clones... - // // DELETETHIS: is the above comment still correct (by flo93)? i doubt it! - // operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart,dpart)); - - parts()->add(dpart); + Part* dpart = 0; + if(dup) + dpart = spart->hasClones() ? spart->createNewClone() : spart->duplicate(); + else if(cpy) + dpart = spart->duplicate(); + else if(cln) + dpart = spart->createNewClone(); + if(dpart) + { + dpart->setTrack(this); + parts()->add(dpart); + } } } @@ -99,48 +93,48 @@ // called from prefetch thread //--------------------------------------------------------- -void WaveTrack::fetchData(unsigned pos, unsigned samples, float** bp, bool doSeek) +void WaveTrack::fetchData(unsigned pos, unsigned samples, float** bp, bool doSeek, bool overwrite) { #ifdef WAVETRACK_DEBUG - printf("WaveTrack::fetchData %s samples:%lu pos:%u\n", name().toLatin1().constData(), samples, pos); + fprintf(stderr, "WaveTrack::fetchData %s samples:%u pos:%u overwrite:%d\n", name().toLatin1().constData(), samples, pos, overwrite); #endif - + // reset buffer to zero - for (int i = 0; i < channels(); ++i) + if(overwrite) + for (int i = 0; i < channels(); ++i) memset(bp[i], 0, samples * sizeof(float)); - + // Process only if track is not off. if(!off()) - { - + { + bool do_overwrite = overwrite; PartList* pl = parts(); unsigned n = samples; for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { WavePart* part = (WavePart*)(ip->second); if (part->mute()) continue; - + unsigned p_spos = part->frame(); unsigned p_epos = p_spos + part->lenFrame(); if (pos + n < p_spos) break; if (pos >= p_epos) continue; - - EventList* events = part->events(); - for (iEvent ie = events->begin(); ie != events->end(); ++ie) { + + for (iEvent ie = part->nonconst_events().begin(); ie != part->nonconst_events().end(); ++ie) { Event& event = ie->second; unsigned e_spos = event.frame() + p_spos; unsigned nn = event.lenFrame(); unsigned e_epos = e_spos + nn; - - if (pos + n < e_spos) + + if (pos + n < e_spos) break; - if (pos >= e_epos) + if (pos >= e_epos) continue; - + int offset = e_spos - pos; - + unsigned srcOffset, dstOffset; if (offset > 0) { nn = n - offset; @@ -150,7 +144,7 @@ else { srcOffset = -offset; dstOffset = 0; - + nn += offset; if (nn > n) nn = n; @@ -158,20 +152,20 @@ float* bpp[channels()]; for (int i = 0; i < channels(); ++i) bpp[i] = bp[i] + dstOffset; - - event.readAudio(part, srcOffset, bpp, channels(), nn, doSeek, false); - + + event.readAudio(part, srcOffset, bpp, channels(), nn, doSeek, do_overwrite); + do_overwrite = false; } } } - - if(MusEGlobal::config.useDenormalBias) { + + if(overwrite && MusEGlobal::config.useDenormalBias) { // add denormal bias to outdata for (int i = 0; i < channels(); ++i) for (unsigned int j = 0; j < samples; ++j) bp[i][j] +=MusEGlobal::denormalBias; } - + _prefetchFifo.add(); } @@ -201,11 +195,11 @@ switch (token) { case Xml::Error: case Xml::End: - return; + goto out_of_WaveTrackRead_forloop; case Xml::TagStart: if (tag == "part") { Part* p = 0; - p = readXmlPart(xml, this); + p = Part::readFromXml(xml, this); if(p) parts()->add(p); } @@ -217,12 +211,14 @@ case Xml::TagEnd: if (tag == "wavetrack") { mapRackPluginsToControllers(); - return; + goto out_of_WaveTrackRead_forloop; } default: break; } } +out_of_WaveTrackRead_forloop: + chainTrackParts(this); } //--------------------------------------------------------- @@ -231,105 +227,157 @@ Part* WaveTrack::newPart(Part*p, bool clone) { - WavePart* part = clone ? new WavePart(this, p->events()) : new WavePart(this); - if (p) { - part->setName(p->name()); - part->setColorIndex(p->colorIndex()); - - *(PosLen*)part = *(PosLen*)p; - part->setMute(p->mute()); - } - - if(clone) - chainClone(p, part); - + WavePart* part = clone ? (WavePart*)p->createNewClone() : (WavePart*)p->duplicate(); + part->setTrack(this); return part; } +bool WaveTrack::openAllParts() +{ + bool opened = false; + const PartList* pl = parts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + if(ip->second->openAllEvents()) + opened = true; + } + return opened; +} + +bool WaveTrack::closeAllParts() +{ + bool closed = false; + const PartList* pl = parts(); + for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) + { + if(ip->second->closeAllEvents()) + closed = true; + } + return closed; +} + //--------------------------------------------------------- // getData //--------------------------------------------------------- -bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float** bp) +bool WaveTrack::getData(unsigned framePos, int dstChannels, unsigned nframe, float** bp) +{ + bool have_data = false; + + const bool track_rec_flag = recordFlag(); + const bool track_rec_monitor = recMonitor(); // Separate monitor and record functions. + + //--------------------------------------------- + // Contributions to data from input sources: + //--------------------------------------------- + + // Gather input data from connected routes. + if((MusEGlobal::song->bounceTrack != this) && !noInRoute()) + { + have_data = AudioTrack::getData(framePos, dstChannels, nframe, bp); + // Do we want to record the incoming data? + if(have_data && track_rec_flag && MusEGlobal::audio->isRecording() && recFile()) + { + if(MusEGlobal::audio->freewheel()) { - if ((MusEGlobal::song->bounceTrack != this) && !noInRoute()) { - RouteList* irl = inRoutes(); - ciRoute i = irl->begin(); - if(i->track->isMidiTrack()) - return false; - - ((AudioTrack*)i->track)->copyData(framePos, channels, - i->channel, - i->channels, - nframe, bp); - - ++i; - for (; i != irl->end(); ++i) - { - if(i->track->isMidiTrack()) - continue; + } + else + { + if(fifo.put(dstChannels, nframe, bp, MusEGlobal::audio->pos().frame())) + printf("WaveTrack::getData(%d, %d, %d): fifo overrun\n", framePos, dstChannels, nframe); + } + } + } - ((AudioTrack*)i->track)->addData(framePos, channels, - i->channel, - i->channels, - nframe, bp); - - } - if (recordFlag()) { - if (MusEGlobal::audio->isRecording() && recFile()) { - if (MusEGlobal::audio->freewheel()) { - } - else { -#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); - } - } - return true; - } - } - if (!MusEGlobal::audio->isPlaying()) - return false; - - if (MusEGlobal::audio->freewheel()) { - - // when freewheeling, read data direct from file: - // Indicate do not seek file before each read. - fetchData(framePos, nframe, bp, false); - - } - else { - unsigned pos; - if (_prefetchFifo.get(channels, nframe, bp, &pos)) { - printf("WaveTrack::getData(%s) fifo underrun\n", - name().toLatin1().constData()); - return false; - } - if (pos != framePos) { - if (MusEGlobal::debugMsg) - printf("fifo get error expected %d, got %d\n", - framePos, pos); - while (pos < framePos) { - if (_prefetchFifo.get(channels, nframe, bp, &pos)) { - printf("WaveTrack::getData(%s) fifo underrun\n", - name().toLatin1().constData()); - return false; - } - } - } - } + //--------------------------------------------- + // Contributions to data from playback sources: + //--------------------------------------------- + + if(!MusEGlobal::audio->isPlaying()) + { + if(!have_data || (track_rec_monitor && have_data)) + return have_data; + return false; + } + + // If there is no input source data or we do not want to monitor it, + // overwrite the supplied buffers rather than mixing with them. + const bool do_overwrite = !have_data || !track_rec_monitor; + + // Set the return value. + have_data = !have_data || (track_rec_monitor && have_data); + + float* pf_buf[dstChannels]; + + if(MusEGlobal::audio->freewheel()) + { + // when freewheeling, read data direct from file: + if(isMute()) + { + // We are muted. We need to let the fetching progress, but discard the data. + for(int i = 0; i < dstChannels; ++i) + // Set to the audio dummy buffer. + pf_buf[i] = audioOutDummyBuf; + // Indicate do not seek file before each read. + fetchData(framePos, nframe, pf_buf, false, do_overwrite); + // Just return whether we have input sources data. + return have_data; + } + else + { + // Not muted. Fetch the data into the given buffers. + // Indicate do not seek file before each read. + fetchData(framePos, nframe, bp, false, do_overwrite); + // We have data. return true; + } + } + else + { + unsigned pos; + if(_prefetchFifo.get(dstChannels, nframe, pf_buf, &pos)) + { + fprintf(stderr, "WaveTrack::getData(%s) (A) fifo underrun\n", name().toLocal8Bit().constData()); + return have_data; + } + if(pos != framePos) + { + if(MusEGlobal::debugMsg) + fprintf(stderr, "fifo get error expected %d, got %d\n", framePos, pos); + while (pos < framePos) + { + if(_prefetchFifo.get(dstChannels, nframe, pf_buf, &pos)) + { + fprintf(stderr, "WaveTrack::getData(%s) (B) fifo underrun\n", + name().toLocal8Bit().constData()); + return have_data; + } } + } + + if(isMute()) + { + // We are muted. We need to let the fetching progress, but discard the data. + // Just return whether we have input sources data. + return have_data; + } + + if(do_overwrite) + { + for(int i = 0; i < dstChannels; ++i) + AL::dsp->cpy(bp[i], pf_buf[i], nframe, MusEGlobal::config.useDenormalBias); + } + else + { + for(int i = 0; i < dstChannels; ++i) + AL::dsp->mix(bp[i], pf_buf[i], nframe); + } + // We have data. + return true; + } + + return have_data; +} //--------------------------------------------------------- // setChannels diff -Nru muse-2.1.2/muse/widgets/aboutbox_impl.cpp muse-3.0.2+ds1/muse/widgets/aboutbox_impl.cpp --- muse-2.1.2/muse/widgets/aboutbox_impl.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/aboutbox_impl.cpp 2017-12-04 21:01:18.000000000 +0000 @@ -31,8 +31,24 @@ setupUi(this); imageLabel->setPixmap(*aboutMuseImage); QString version(VERSION); - QString svnrevision(SVNVERSION); - versionLabel->setText("Version: " + version + " (svn revision: "+ svnrevision +")"); + QString gitstring(GITSTRING); + versionLabel->setText("Version: " + version + (gitstring == QString() ? "" : "\n("+ gitstring + ")")); + QString systemInfo=""; + +#ifdef LV2_SUPPORT + systemInfo.append("LV2 support enabled.\n"); +#endif +#ifdef DSSI_SUPPORT + systemInfo.append("DSSI support enabled.\n"); +#endif +#ifdef VST_NATIVE_SUPPORT + #ifdef VST_VESTIGE_SUPPORT + systemInfo.append("Native VST support enabled using VESTIGE compatibility header.\n"); + #else + systemInfo.append("Native VST support enabled using Steinberg VSTSDK.\n"); + #endif +#endif + systemInformationLabel->setText(systemInfo); } } diff -Nru muse-2.1.2/muse/widgets/aboutbox.ui muse-3.0.2+ds1/muse/widgets/aboutbox.ui --- muse-2.1.2/muse/widgets/aboutbox.ui 2013-03-28 18:04:50.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/aboutbox.ui 2018-01-06 20:31:35.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 350 - 160 + 404 + 289
    @@ -26,21 +26,54 @@ QFrame::NoFrame - QFrame::Raised + QFrame::Plain + + + 0 - - - QFrame::StyledPanel - - - QFrame::Raised - - - 4 - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + 4 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + @@ -48,7 +81,7 @@ - Version 2.1.2 + Version info (replaced programmatically) false @@ -58,7 +91,7 @@ - (C) Copyright 1999-2012 Werner Schweer and others. + (C) Copyright 1999-2018 the MusE development team. See http://www.muse-sequencer.org for new versions and more information. @@ -70,6 +103,27 @@ + + + Qt::Horizontal + + + + + + + System information: + + + + + + + TextLabel + + + + &Keep On Rocking! @@ -84,6 +138,9 @@ textLabel1 doneHere imageFrame + line + label + systemInformationLabel diff -Nru muse-2.1.2/muse/widgets/appearancebase.ui muse-3.0.2+ds1/muse/widgets/appearancebase.ui --- muse-2.1.2/muse/widgets/appearancebase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/appearancebase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 519 - 517 + 568 + 596 @@ -23,19 +23,13 @@ - 2 + 0 Arranger - - - 11 - - - 6 - + @@ -45,9 +39,18 @@ 6 - + + 11 + + + 2 + + 11 + + 6 + @@ -58,14 +61,14 @@ - show events + show e&vents - show Cakewalk Style + show Ca&kewalk Style @@ -105,12 +108,12 @@ - + Events - + 11 @@ -169,7 +172,38 @@ - + + + + Wave Drawing in Parts + + + + 2 + + + 6 + + + + + Only Outline + + + + + + + RMS/PEAK(Traditional) + + + + + radioButtonDrawRmsPeak + radioButtonDrawOutline + + + Background picture @@ -224,7 +258,7 @@ - + show snap grid @@ -238,8 +272,66 @@ Colors + + + + + + + 0 + 0 + + + + Global opacity + + + Qt::AlignCenter + + + false + + + + + + + 255 + + + + + + + + 0 + 0 + + + + 0 + + + 255 + + + true + + + Qt::Horizontal + + + QSlider::NoTicks + + + + + + + QAbstractItemView::SelectItems + true @@ -786,6 +878,13 @@ + + + Choose color... + + + + add to palette @@ -795,7 +894,7 @@ - + @@ -1007,58 +1106,34 @@ - - + + - - - - 0 - 0 - - + - Global opacity - - - Qt::AlignCenter - - - false + Save all... - - - 255 + + + Load all... - - - - 0 - 0 - - - - 0 - - - 255 - - - true - + Qt::Horizontal - - QSlider::NoTicks + + + 40 + 20 + - + @@ -1081,6 +1156,12 @@ Themes + + 2 + + + 6 + @@ -1227,7 +1308,7 @@ 20 - 40 + 23 @@ -1240,88 +1321,97 @@ Fonts - - + + 11 + + 2 + + + 11 + + + 6 + 6 - - + + - Italic + Font 3 + + + false - - + + + + + 0 + 0 + + - Italic + ... - - + + - Italic + Bold - - - - - 0 - 0 - + + + + Font 1 + + + false - - + + - + 0 0 - - ... - - - + + - Font 2 - - - false + Bold - - + + - Family + Font 0 false - - + + - Font 5 - - - false + Italic - + @@ -1331,22 +1421,22 @@ - - + + - Bold + Italic - - + + Italic - - + + 0 @@ -1358,68 +1448,79 @@ - - + + - Font 3 + Italic + + + + + + + Italic + + + + + + + Font 5 false - - + + - + 0 0 - - + + - + 0 0 - - + + - Bold + Family + + + false - - - - - 0 - 0 - - + + - ... + Bold - - + + - + 0 0 - - + + 0 @@ -1431,10 +1532,10 @@ - - + + - Font 0 + Font 2 false @@ -1442,16 +1543,6 @@ - - - - 0 - 0 - - - - - @@ -1461,149 +1552,149 @@ - - + + - + 0 0 + + ... + - - + + - + 0 0 - - - - - Bold + ... - - - - - 0 - 0 - + + + + Bold - - + + - Font 1 + Font 6 false - - - - - 0 - 0 - + + + + Bold - - + + + + Bold + + + + + - + 0 0 - - + + - + 0 0 - - - - - Font 6 - - - false + ... - - - - Bold + + + + + 0 + 0 + - - + + - + 0 0 + + + + - ... + Italic - - + + - + 0 0 - - ... - - - + + Bold - - + + - Italic + Font 4 + + + false - - + + - Bold + Italic - - + + 0 @@ -1615,39 +1706,48 @@ - - + + - Italic + Size (pt) + + + false - - - - Bold + + + + + 0 + 0 + - - - - Font 4 - - - false + + + + + 0 + 0 + - - - - Italic + + + + + 0 + 0 + - - + + 0 @@ -1656,13 +1756,52 @@ - - + + + + + + + + - Size + Maximum aliased size, 0 = no alias: - - false + + + + + + At what point size to switch from aliased text + to non-aliased text. +Zero means never alias, always use anti-aliasing. +For certain controls such as compact sliders. +Aliased text is bright and sharp but may look poor + in some cases. +The font family is forced to 'Sans', which should + give reasonable results at low point sizes. + + + At what point size to switch from aliased text + to non-aliased text. +Zero means never alias, always use anti-aliasing. +For certain controls such as compact sliders. +Aliased text is bright and sharp but may look poor + in some cases. +The font family is forced to 'Sans', which should + give reasonable results at low point sizes. + + + + + + 0 + + + 100 + + + 0 diff -Nru muse-2.1.2/muse/widgets/background_painter.cpp muse-3.0.2+ds1/muse/widgets/background_painter.cpp --- muse-2.1.2/muse/widgets/background_painter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/background_painter.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,104 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// background_painter.cpp +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 +#include +#include +#include +#include +#include + +#include "gconfig.h" +#include "background_painter.h" + +// #include +// For debugging output: Uncomment the fprintf section. +#define DEBUG_BACKGROUND_PAINTER(dev, format, args...) // fprintf(dev, format, ##args); + +namespace MusEGui { + +ItemBackgroundPainter::ItemBackgroundPainter() +{ + +} + +void ItemBackgroundPainter::drawBackground(QPainter* painter, + const QRect& fullRect, + const QPalette& pal, + int xMargin, + int yMargin, + const QRect& onRect, + const QColor& activeColor) +{ + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + + bool onfull = false; + if(!onRect.isNull()) + onfull = (onRect == fullRect); + + QColor acolor = activeColor.isValid() ? activeColor : MusEGlobal::config.rackItemBackgroundColor; + + QRect cr = QRect(fullRect.x() + xMargin, fullRect.y() + yMargin, + fullRect.width() - 2 * xMargin, fullRect.height() - 2 * yMargin); + painter->fillRect(fullRect, pal.dark().color().darker(130)); + + const QColor mask_edge = QColor(110, 110, 110, 55); + const QColor mask_center = QColor(220, 220, 220, 55); + QLinearGradient mask; + mask.setColorAt(0, mask_edge); + mask.setColorAt(0.5, mask_center); + mask.setColorAt(1, mask_edge); + mask.setStart(QPointF(0, cr.y())); + mask.setFinalStop(QPointF(0, cr.y() + cr.height())); + + if(onRect.isNull() || !onfull) + { + int cw = fullRect.width(); + if(!onRect.isNull()) + cw -= onRect.width(); + const QRect knobclip(fullRect.x(), fullRect.y(), cw, fullRect.height()); + painter->setClipRect(knobclip); + painter->setBrush(pal.dark()); + painter->drawRoundedRect(cr, 2, 2); + painter->setClipRect(fullRect); + } + + if(!onRect.isNull()) + { + QRect labeldraw = QRect(onRect.x() + xMargin, + onRect.y() + yMargin, + onRect.width() - 2 * xMargin, + onRect.height() - 2 * yMargin); + painter->setBrush(acolor); + painter->drawRoundedRect(labeldraw, 2, 2); + } + + painter->setBrush(mask); + painter->drawRoundedRect(cr, 2, 2); + + painter->restore(); +} + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/background_painter.h muse-3.0.2+ds1/muse/widgets/background_painter.h --- muse-2.1.2/muse/widgets/background_painter.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/background_painter.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,58 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// background_painter.h +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __BACKGROUND_PAINTER_H__ +#define __BACKGROUND_PAINTER_H__ + +#include +#include + +class QPainter; +class QPalette; +class QColor; +class QRect; +class QString; + +namespace MusEGui { + +//--------------------------------------------------------- +// ItemBackgroundPainter +//--------------------------------------------------------- + +class ItemBackgroundPainter +{ + public: + ItemBackgroundPainter(); + + void drawBackground(QPainter* painter, + const QRect& rect, + const QPalette& pal, + int xMargin = 1, + int yMargin = 1, + const QRect& onRect = QRect(), + const QColor& activeColor = QColor()); +}; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/bigtime.cpp muse-3.0.2+ds1/muse/widgets/bigtime.cpp --- muse-2.1.2/muse/widgets/bigtime.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/bigtime.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -20,12 +20,14 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= -#include +//#include #include +#include #include #include #include +#include #include "globals.h" #include "bigtime.h" @@ -51,59 +53,56 @@ BigTime::BigTime(QWidget* parent) : QWidget(parent, Qt::Window | Qt::WindowStaysOnTopHint) // Possibly also Qt::X11BypassWindowManagerHint - { +{ - tickmode = true; - dwin = new QWidget(this, Qt::WindowStaysOnTopHint); // Possibly also Qt::X11BypassWindowManagerHint - dwin->setObjectName("bigtime-dwin"); - dwin->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); - fmtButton = new QCheckBox(QString(""), this); - fmtButton->resize(18,18); - fmtButton->setChecked(true); - fmtButton->setToolTip(tr("format display")); - fmtButton->setFocusPolicy(Qt::NoFocus); - barLabel = new QLabel(dwin); - beatLabel = new QLabel(dwin); - tickLabel = new QLabel(dwin); - //hourLabel = new QLabel(dwin); - minLabel = new QLabel(dwin); - secLabel = new QLabel(dwin); - frameLabel = new QLabel(dwin); - subFrameLabel = new QLabel(dwin); - sep1 = new QLabel(QString("."), dwin); - sep2 = new QLabel(QString("."), dwin); - sep3 = new QLabel(QString(":"), dwin); - sep4 = new QLabel(QString(":"), dwin); - sep5 = new QLabel(QString(":"), dwin); - absTickLabel = new QLabel(dwin); - absFrameLabel = new QLabel(dwin); - barLabel->setToolTip(tr("bar")); - beatLabel->setToolTip(tr("beat")); - tickLabel->setToolTip(tr("tick")); - //hourLabel->setToolTip(tr("hour")); - minLabel->setToolTip(tr("minute")); - secLabel->setToolTip(tr("second")); - frameLabel->setToolTip(tr("frame")); - subFrameLabel->setToolTip(tr("subframe")); - absTickLabel->setToolTip(tr("tick")); - absFrameLabel->setToolTip(tr("frame")); - fmtButtonToggled(true); - connect(fmtButton, SIGNAL(toggled(bool)), SLOT(fmtButtonToggled(bool))); - //oldbar = oldbeat = oldtick = oldhour = oldmin = oldsec = oldframe = -1; - oldbar = oldbeat = oldtick = oldmin = oldsec = oldframe = oldsubframe = -1; - oldAbsTick = oldAbsFrame = -1; - setString(INT_MAX); + tickmode = true; + dwin = new QWidget(this, Qt::WindowStaysOnTopHint); // Possibly also Qt::X11BypassWindowManagerHint + dwin->setObjectName("bigtime-dwin"); + dwin->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + dwin->setStyleSheet("font-size:10px; font-family:'Courier'; "); // Tim p4.0.8 + + fmtButton = new QCheckBox(QString(""), this); + fmtButton->resize(18,18); + fmtButton->setChecked(true); + fmtButton->setToolTip(tr("format display")); + fmtButton->setFocusPolicy(Qt::NoFocus); + barLabel = new QLabel(dwin); + beatLabel = new QLabel(dwin); + tickLabel = new QLabel(dwin); + //hourLabel = new QLabel(dwin); + minLabel = new QLabel(dwin); + secLabel = new QLabel(dwin); + frameLabel = new QLabel(dwin); + subFrameLabel = new QLabel(dwin); + sep1 = new QLabel(QString("."), dwin); + sep2 = new QLabel(QString("."), dwin); + sep3 = new QLabel(QString(":"), dwin); + sep4 = new QLabel(QString(":"), dwin); + sep5 = new QLabel(QString(":"), dwin); + absTickLabel = new QLabel(dwin); + absFrameLabel = new QLabel(dwin); + barLabel->setToolTip(tr("bar")); + beatLabel->setToolTip(tr("beat")); + tickLabel->setToolTip(tr("tick")); + //hourLabel->setToolTip(tr("hour")); + minLabel->setToolTip(tr("minute")); + secLabel->setToolTip(tr("second")); + frameLabel->setToolTip(tr("frame")); + subFrameLabel->setToolTip(tr("subframe")); + absTickLabel->setToolTip(tr("tick")); + absFrameLabel->setToolTip(tr("frame")); + fmtButtonToggled(true); + connect(fmtButton, SIGNAL(toggled(bool)), SLOT(fmtButtonToggled(bool))); + //oldbar = oldbeat = oldtick = oldhour = oldmin = oldsec = oldframe = -1; + oldbar = oldbeat = oldtick = oldmin = oldsec = oldframe = oldsubframe = -1; + oldAbsTick = oldAbsFrame = -1; + setString(INT_MAX); + metronome = new VerticalMetronomeWidget(dwin); - dwin->setStyleSheet("font-size:10px; font-family:'Courier'; "); // Tim p4.0.8 - - configChanged(); + configChanged(); - //QFont f(QString("Courier")); - //f.setPixelSize(10); - //dwin->setFont(f); - - setWindowTitle(tr("MusE: Bigtime")); - } + setWindowTitle(tr("MusE: Bigtime")); +} //--------------------------------------------------------- // fmtButtonToggled @@ -212,23 +211,23 @@ bool BigTime::setString(unsigned v) { if (v == INT_MAX) { - barLabel->setText(QString("----")); - beatLabel->setText(QString("--")); - tickLabel->setText(QString("---")); - //hourLabel->setText(QString("--")); - //minLabel->setText(QString("--")); - minLabel->setText(QString("---")); - secLabel->setText(QString("--")); - frameLabel->setText(QString("--")); - subFrameLabel->setText(QString("--")); - + barLabel->setText(QString("----")); + beatLabel->setText(QString("--")); + tickLabel->setText(QString("---")); + //hourLabel->setText(QString("--")); + //minLabel->setText(QString("--")); + minLabel->setText(QString("---")); + secLabel->setText(QString("--")); + frameLabel->setText(QString("--")); + subFrameLabel->setText(QString("--")); + absTickLabel->setText(QString("----------")); absFrameLabel->setText(QString("----------")); oldAbsTick = oldAbsFrame = -1; - //oldbar = oldbeat = oldtick = oldhour = oldmin = oldsec = oldframe = -1; + //oldbar = oldbeat = oldtick = oldhour = oldmin = oldsec = oldframe = -1; oldbar = oldbeat = oldtick = oldmin = oldsec = oldframe = oldsubframe = -1; - return true; - } + return true; + } // Quick fix: Not much to do but ignore the supplied tick: We need the exact frame here. unsigned absFrame = MusEGlobal::audio->pos().frame(); @@ -262,59 +261,58 @@ QString s; if(oldAbsTick != v) { - s.sprintf("%010d", v); + s = QString("%1").arg(v, 10, 10, QLatin1Char('0')); absTickLabel->setText(s); oldAbsTick = v; } if(oldAbsFrame != absFrame) { - s.sprintf("%010d", absFrame); + s = QString("%1").arg(absFrame, 10, 10, QLatin1Char('0')); absFrameLabel->setText(s); oldAbsFrame = absFrame; } if(oldbar != bar) { - s.sprintf("%04d", bar+1); + s = QString("%1").arg(bar + 1, 4, 10, QLatin1Char('0')); barLabel->setText(s); oldbar = bar; } if(oldbeat != beat) { - s.sprintf("%02d", beat+1); + s = QString("%1").arg(beat + 1, 2, 10, QLatin1Char('0')); beatLabel->setText(s); oldbeat = beat; } if(oldtick != tick) { - s.sprintf("%03d", tick); + s = QString("%1").arg(tick, 3, 10, QLatin1Char('0')); tickLabel->setText(s); oldtick = tick; } //if(oldhour != hour) { - // s.sprintf("%02d", hour); + // s = QString("%1").arg(hour, 2, 10, QLatin1Char('0')); // hourLabel->setText(s); // oldhour = hour; //} if(oldmin != min) { - //s.sprintf("%02d", min); - s.sprintf("%03d", min); + s = QString("%1").arg(min, 3, 10, QLatin1Char('0')); minLabel->setText(s); oldmin = min; } if(oldsec != sec) { - s.sprintf("%02d", sec); + s = QString("%1").arg(sec, 2, 10, QLatin1Char('0')); secLabel->setText(s); oldsec = sec; } if(oldframe != frame) { - s.sprintf("%02d", frame); + s = QString("%1").arg(frame, 2, 10, QLatin1Char('0')); frameLabel->setText(s); oldframe = frame; } if(oldsubframe != subframe) { - s.sprintf("%02u", subframe); + s = QString("%1").arg(subframe, 2, 10, QLatin1Char('0')); subFrameLabel->setText(s); oldsubframe = subframe; } @@ -325,86 +323,76 @@ //--------------------------------------------------------- // setPos //--------------------------------------------------------- - +#define PI 3.14159265 void BigTime::setPos(int idx, unsigned v, bool) - { - if (idx == 0) - setString(v); - } +{ + if (idx == 0) + { + int calcV = v%(MusEGlobal::config.midiDivision*2); + double rangeAdjuster = PI/double(MusEGlobal::config.midiDivision); + metronome->setMetronome(sin(double(calcV)*rangeAdjuster)); + //printf("calcV=%d rangeAdjuster %f metronomePosition=%f midiDivision=%d\n",v,rangeAdjuster, metronomePosition, MusEGlobal::config.midiDivision); + setString(v); + } + metronome->update(); +} //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void BigTime::resizeEvent(QResizeEvent *ev) - { +{ QWidget::resizeEvent(ev); dwin->resize(ev->size()); QFont f = dwin->font(); - QFontMetrics fm(f); - int fs = f.pixelSize(); - int hspace = 20; - //int tw = fm.width(QString("00:00:00:00")); - int tw = fm.width(QString("000:00:00:00")); + QFontMetrics fm(f); + int fs = f.pixelSize(); + int hspace = 20; + int tw = fm.width(QString("000:00:00:00")); - fs = ((ev->size().width() - hspace*2)*fs) / tw; + fs = ((ev->size().width() - hspace*2)*fs) / tw; - // set min/max - if (fs < 10) - fs = 10; - else if (fs > 256) - fs = 256; + // set min/max + if (fs < 10) + fs = 10; + else if (fs > 256) + fs = 256; - //if(debugMsg) - // printf("resize BigTime: Font name:%s CurSize:%d NewSize:%d, NewWidth:%d\n", - // f.family().toLatin1().constData(), fs, nfs, ev->size().width()); - - //f.setPixelSize(fs); - - //dwin->setFont(f); QString fstr = QString("font-size:%1px; font-family:'Courier'; ").arg(fs); // Tim p4.0.8 dwin->setStyleSheet(fstr); setBgColor(MusEGlobal::config.bigTimeBackgroundColor); setFgColor(MusEGlobal::config.bigTimeForegroundColor); int digitWidth = dwin->fontMetrics().width(QString("0")); - int vspace = (ev->size().height() - (fs*2)) / 3; + int vspace = (ev->size().height() - (fs*2)) / 3; int tickY = vspace; - int timeY = vspace*2 + fs; + int timeY = vspace*2 + fs; int absTickY = tickY; int absFrameY = timeY; - barLabel->resize(digitWidth*4, fs); - beatLabel->resize(digitWidth*2, fs); - tickLabel->resize(digitWidth*3, fs); - //hourLabel->resize(digitWidth*2, fs); - //minLabel->resize(digitWidth*2, fs); + barLabel->resize(digitWidth*4, fs); + beatLabel->resize(digitWidth*2, fs); + tickLabel->resize(digitWidth*3, fs); minLabel->resize(digitWidth*3, fs); - secLabel->resize(digitWidth*2, fs); - frameLabel->resize(digitWidth*2, fs); + secLabel->resize(digitWidth*2, fs); + frameLabel->resize(digitWidth*2, fs); subFrameLabel->resize(digitWidth*2, fs); absTickLabel->resize(digitWidth*10, fs); absFrameLabel->resize(digitWidth*10, fs); sep1->resize(digitWidth, fs); - sep2->resize(digitWidth, fs); - sep3->resize(digitWidth, fs); - sep4->resize(digitWidth, fs); - sep5->resize(digitWidth, fs); - - barLabel->move( hspace + (digitWidth*0), tickY); - sep1->move( hspace + (digitWidth*4), tickY); - beatLabel->move( hspace + (digitWidth*5), tickY); - sep2->move( hspace + (digitWidth*7), tickY); - tickLabel->move( hspace + (digitWidth*8), tickY); - - //hourLabel->move( hspace + (digitWidth*0), timeY); - //sep3->move( hspace + (digitWidth*2), timeY); - //minLabel->move( hspace + (digitWidth*3), timeY); - //sep4->move( hspace + (digitWidth*5), timeY); - //secLabel->move( hspace + (digitWidth*6), timeY); - //sep5->move( hspace + (digitWidth*8), timeY); - //frameLabel->move( hspace + (digitWidth*9), timeY); + sep2->resize(digitWidth, fs); + sep3->resize(digitWidth, fs); + sep4->resize(digitWidth, fs); + sep5->resize(digitWidth, fs); + + barLabel->move( hspace + (digitWidth*0), tickY); + sep1->move( hspace + (digitWidth*4), tickY); + beatLabel->move( hspace + (digitWidth*5), tickY); + sep2->move( hspace + (digitWidth*7), tickY); + tickLabel->move( hspace + (digitWidth*8), tickY); + minLabel->move( hspace + (digitWidth*0), timeY); sep3->move( hspace + (digitWidth*3), timeY); secLabel->move( hspace + (digitWidth*4), timeY); @@ -415,64 +403,68 @@ absTickLabel->move( hspace + (digitWidth*0), absTickY); absFrameLabel->move( hspace + (digitWidth*0), absFrameY); - } + + metronome->move(0,dwin->height() - dwin->height()/10); + metronome->resize(dwin->width(),dwin->height()/10); +} //--------------------------------------------------------- // setForegroundColor //--------------------------------------------------------- void BigTime::setFgColor(QColor c) - { - QPalette newpalette(palette()); - newpalette.setColor(QPalette::Foreground, c); - setPalette(newpalette); - - barLabel->setPalette(newpalette); - beatLabel->setPalette(newpalette); - tickLabel->setPalette(newpalette); - //hourLabel->setPalette(newpalette); - minLabel->setPalette(newpalette); - secLabel->setPalette(newpalette); - frameLabel->setPalette(newpalette); - subFrameLabel->setPalette(newpalette); - - absTickLabel->setPalette(newpalette); - absFrameLabel->setPalette(newpalette); - sep1->setPalette(newpalette); - sep2->setPalette(newpalette); - sep3->setPalette(newpalette); - sep4->setPalette(newpalette); - sep5->setPalette(newpalette); - } +{ + QPalette newpalette(palette()); + newpalette.setColor(QPalette::Foreground, c); + setPalette(newpalette); + + barLabel->setPalette(newpalette); + beatLabel->setPalette(newpalette); + tickLabel->setPalette(newpalette); + //hourLabel->setPalette(newpalette); + minLabel->setPalette(newpalette); + secLabel->setPalette(newpalette); + frameLabel->setPalette(newpalette); + subFrameLabel->setPalette(newpalette); + + absTickLabel->setPalette(newpalette); + absFrameLabel->setPalette(newpalette); + sep1->setPalette(newpalette); + sep2->setPalette(newpalette); + sep3->setPalette(newpalette); + sep4->setPalette(newpalette); + sep5->setPalette(newpalette); +} //--------------------------------------------------------- // setBackgroundColor //--------------------------------------------------------- void BigTime::setBgColor(QColor c) - { - QPalette newpalette(palette()); - newpalette.setColor(QPalette::Window, c); - setPalette(newpalette); - - barLabel->setPalette(newpalette); - beatLabel->setPalette(newpalette); - tickLabel->setPalette(newpalette); - //hourLabel->setPalette(newpalette); - minLabel->setPalette(newpalette); - secLabel->setPalette(newpalette); - frameLabel->setPalette(newpalette); - subFrameLabel->setPalette(newpalette); - - absTickLabel->setPalette(newpalette); - absFrameLabel->setPalette(newpalette); - sep1->setPalette(newpalette); - sep2->setPalette(newpalette); - sep3->setPalette(newpalette); - sep4->setPalette(newpalette); - sep5->setPalette(newpalette); +{ + QPalette newpalette(palette()); + newpalette.setColor(QPalette::Window, c); + setPalette(newpalette); + + barLabel->setPalette(newpalette); + beatLabel->setPalette(newpalette); + tickLabel->setPalette(newpalette); + //hourLabel->setPalette(newpalette); + minLabel->setPalette(newpalette); + secLabel->setPalette(newpalette); + frameLabel->setPalette(newpalette); + subFrameLabel->setPalette(newpalette); + + absTickLabel->setPalette(newpalette); + absFrameLabel->setPalette(newpalette); + sep1->setPalette(newpalette); + sep2->setPalette(newpalette); + sep3->setPalette(newpalette); + sep4->setPalette(newpalette); + sep5->setPalette(newpalette); + + setPalette(newpalette); +} - setPalette(newpalette); - } } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/bigtime.h muse-3.0.2+ds1/muse/widgets/bigtime.h --- muse-2.1.2/muse/widgets/bigtime.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/bigtime.h 2017-12-04 21:01:19.000000000 +0000 @@ -24,6 +24,9 @@ #define __BIGTIME_H__ #include +#include +#include +#include class QCheckBox; class QLabel; @@ -32,6 +35,41 @@ class MusE; +class VerticalMetronomeWidget : public QWidget +{ + float metronomePosition; + + +protected: + void paintEvent (QPaintEvent *ev ) + { + QRect rr(ev->rect()); + QPainter p(this); + + //p.fillRect(rr,Qt::yellow); + int y = (rr.height() - (rr.height()*fabs(metronomePosition)))-1; + if (metronomePosition > -0.05 and metronomePosition < 0.15) { + p.setPen(Qt::red); + p.drawLine(0,y-1,rr.width(),y-1); + } + else { + p.setPen(Qt::yellow); + } + p.drawLine(0,y,rr.width(),y); + } +public: + VerticalMetronomeWidget(QWidget* parent) : QWidget(parent) { + metronomePosition=0.0; + } + + void setMetronome(float v) + { + metronomePosition = v; + update(); + } +}; + + //--------------------------------------------------------- // BigTime //--------------------------------------------------------- @@ -42,9 +80,9 @@ bool tickmode; MusE* seq; - bool setString(unsigned); + VerticalMetronomeWidget *metronome; QWidget *dwin; QCheckBox *fmtButton; QLabel *absTickLabel; @@ -64,6 +102,7 @@ protected: virtual void resizeEvent(QResizeEvent*); virtual void closeEvent(QCloseEvent*); + //void paintEvent (QPaintEvent *event ); public slots: void setPos(int, unsigned, bool); diff -Nru muse-2.1.2/muse/widgets/canvas.cpp muse-3.0.2+ds1/muse/widgets/canvas.cpp --- muse-2.1.2/muse/widgets/canvas.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/canvas.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -61,11 +61,13 @@ Canvas::Canvas(QWidget* parent, int sx, int sy, const char* name) : View(parent, sx, sy, name) { + _cursorOverrideCount = 0; canvasTools = 0; itemPopupMenu = 0; button = Qt::NoButton; keyState = 0; + _mouseGrabbed = false; canScrollLeft = true; canScrollRight = true; @@ -75,6 +77,9 @@ vscrollDir = VSCROLL_NONE; scrollTimer=NULL; ignore_mouse_move = false; + resizeDirection= RESIZE_TO_THE_RIGHT; + + supportsResizeToTheLeft = false; scrollSpeed=30; // hardcoded scroll jump @@ -92,6 +97,10 @@ Canvas::~Canvas() { + // Just in case the ref count is not 0. This is our last chance to clear + // our contribution to QApplication::setOverrideCursor references. + showCursor(); + items.clearDelete(); if(newCItem) @@ -102,6 +111,40 @@ } } +void Canvas::showCursor(bool show) +{ + if(_cursorOverrideCount > 1) + fprintf(stderr, "MusE Warning: _cursorOverrideCount > 1 in Canvas::showCursor(%d)\n", show); + + if(show) + { + while(_cursorOverrideCount > 0) + { + QApplication::restoreOverrideCursor(); + _cursorOverrideCount--; + } + } + else + { + _cursorOverrideCount++; + QApplication::setOverrideCursor(Qt::BlankCursor); // CAUTION + } +} + +void Canvas::setMouseGrab(bool grabbed) +{ + if(grabbed && !_mouseGrabbed) + { + _mouseGrabbed = true; + grabMouse(); // CAUTION + } + else if(!grabbed && _mouseGrabbed) + { + releaseMouse(); + _mouseGrabbed = false; + } +} + //--------------------------------------------------------- // setPos // set one of three markers @@ -387,9 +430,12 @@ //--------------------------------------------------- if (drag == DRAG_LASSO) { + p.setWorldMatrixEnabled(false); p.setPen(Qt::blue); p.setBrush(Qt::NoBrush); - p.drawRect(lasso); + QRect _r(mapx(lasso.topLeft().x()), mapy(lasso.topLeft().y()), mapx(lasso.topRight().x()) - mapx(lasso.topLeft().x()), mapy(lasso.bottomLeft().y()) - mapy(lasso.topLeft().y())); + p.drawRect(_r); + p.setWorldMatrixEnabled(wmtxen); } //--------------------------------------------------- @@ -410,8 +456,8 @@ } } -#define WHEEL_STEPSIZE 40 -#define WHEEL_DELTA 120 +#define WHEEL_STEPSIZE 2 +//#define WHEEL_DELTA 120 //--------------------------------------------------------- // wheelEvent @@ -420,33 +466,55 @@ { int keyState = ev->modifiers(); + QPoint delta = ev->pixelDelta(); // WHEEL_DELTA; + bool shift = keyState & Qt::ShiftModifier; bool ctrl = keyState & Qt::ControlModifier; - if (shift) { // scroll horizontally - int delta = -ev->delta() / WHEEL_DELTA; + if (ctrl) { // zoom horizontally + + emit horizontalZoom(ev->delta()>0, ev->globalPos()); + return; + } + + if (shift || delta.x() != 0) { // scroll horizontally + + int scrolldelta = - delta.x() /2; + if (shift) { + scrolldelta = - delta.y() /2; + } + int xpixelscale = 5*MusECore::fast_log10(rmapxDev(1)); - if (xpixelscale <= 0) - xpixelscale = 1; - int scrollstep = WHEEL_STEPSIZE * (delta); + if (xpixelscale <= 0) { + xpixelscale = 1; + } + int scrollstep = WHEEL_STEPSIZE * (scrolldelta); scrollstep = scrollstep / 10; int newXpos = xpos + xpixelscale * scrollstep; - if (newXpos < 0) - newXpos = 0; + + if (newXpos < 0) { + newXpos = 0; + } + emit horizontalScroll((unsigned)newXpos); - } else if (ctrl) { // zoom horizontally - emit horizontalZoom(ev->delta()>0, ev->globalPos()); - } else { // scroll vertically - int delta = ev->delta() / WHEEL_DELTA; + } + + if (!shift && delta.y() != 0) { // scroll vertically + + int scrolldelta = delta.y() /2; int ypixelscale = rmapyDev(1); + if (ypixelscale <= 0) ypixelscale = 1; - int scrollstep = WHEEL_STEPSIZE * (-delta); + + int scrollstep = WHEEL_STEPSIZE * (-scrolldelta); scrollstep = scrollstep / 2; int newYpos = ypos + ypixelscale * scrollstep; + if (newYpos < 0) newYpos = 0; + emit verticalScroll((unsigned)newYpos); } } @@ -550,9 +618,13 @@ void Canvas::viewMousePressEvent(QMouseEvent* event) { + showCursor(); + if (!mousePress(event)) + { + setMouseGrab(false); return; - + } keyState = event->modifiers(); button = event->button(); //printf("viewMousePressEvent buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); @@ -561,6 +633,7 @@ // like moving or drawing lasso is performed. if (event->buttons() & Qt::RightButton & ~(button)) { //printf("viewMousePressEvent special buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); + setMouseGrab(false); switch (drag) { case DRAG_LASSO: drag = DRAG_OFF; @@ -576,8 +649,9 @@ } // ignore event if (another) button is already active: - if (event->buttons() & (Qt::LeftButton|Qt::RightButton|Qt::MidButton) & ~(button)) { + if (event->buttons() ^ button) { //printf("viewMousePressEvent ignoring buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); + setMouseGrab(false); return; } @@ -589,42 +663,7 @@ global_start = event->globalPos(); ev_global_pos = global_start; - //--------------------------------------------------- - // set curItem to item mouse is pointing - // (if any) - //--------------------------------------------------- - - if (virt()) - curItem = items.find(start); - else { - curItem = 0; - iCItem ius; - bool usfound = false; - for (iCItem i = items.begin(); i != items.end(); ++i) { - QRect box = i->second->bbox(); - int x = rmapxDev(box.x()); - int y = rmapyDev(box.y()); - int w = rmapxDev(box.width()); - int h = rmapyDev(box.height()); - QRect r(x, y, w, h); - r.translate(i->second->pos().x(), i->second->pos().y()); - if (r.contains(start)) { - if(i->second->isSelected()) - { - curItem = i->second; - break; - } - else - if(!usfound) - { - ius = i; - usfound = true; - } - } - } - if(!curItem && usfound) - curItem = ius->second; - } + curItem = findCurrentItem(start); if (curItem && (button == Qt::MidButton)) { deleteItem(start); // changed from "start drag" to "delete" by flo93 @@ -704,11 +743,26 @@ } else { drag = DRAG_RESIZE; + resizeDirection = RESIZE_TO_THE_RIGHT; + if(supportsResizeToTheLeft){ + if(curItem->x() + (curItem->width() / 2) > ev_pos.x()){ + resizeDirection = RESIZE_TO_THE_LEFT; + } + } setCursor(); - int dx = start.x() - curItem->x(); - curItem->setWidth(dx); - start.setX(curItem->x()); + if(resizeDirection == RESIZE_TO_THE_RIGHT){ + int dx = start.x() - curItem->x(); + curItem->setWidth(dx); + }else{ + int endX = curItem->x() + curItem->width(); + end = QPoint(endX, curItem->y()); + resizeToTheLeft(ev_pos); + } + start = curItem->pos(); } + deselectAll(); + if (curItem) + selectItem(curItem, true); } else { drag = DRAG_NEW; @@ -720,10 +774,9 @@ drag = DRAG_OFF; setCursor(); } + deselectAll(); + // selectItem() will be called in viewMouseReleaseEvent(). } - deselectAll(); - if (curItem) - selectItem(curItem, true); updateSelection(); redraw(); break; @@ -734,9 +787,24 @@ setCursor(); if(MusEGlobal::config.borderlessMouse) { + // "It is almost never necessary to grab the mouse when using Qt, as Qt grabs + // and releases it sensibly. In particular, Qt grabs the mouse when a mouse + // button is pressed and keeps it until the last button is released." + // + // Apparently not. For some reason this was necessary. When the cursor is dragged + // outside the window, holding left then pressing right mouse button COMPLETELY + // bypasses us, leaving the app's default right-click handler to popup, and leaving + // us in a really BAD state: mouse is grabbed (and hidden) and no way out ! + // + // That is likely just how QWidget works, but here using global cursor overrides + // it is disastrous. TESTED: Yes, that is how other controls work. Hitting another + // button while the mouse has been dragged outside causes it to bypass us ! + setMouseGrab(true); // CAUTION + QRect r = QApplication::desktop()->screenGeometry(); ignore_mouse_move = true; // Avoid recursion. QCursor::setPos( QPoint(r.width()/2, r.height()/2) ); + //ignore_mouse_move = false; } } break; @@ -747,9 +815,12 @@ setCursor(); if(MusEGlobal::config.borderlessMouse) { + setMouseGrab(true); // CAUTION + QRect r = QApplication::desktop()->screenGeometry(); ignore_mouse_move = true; // Avoid recursion. QCursor::setPos( QPoint(r.width()/2, r.height()/2) ); + //ignore_mouse_move = false; } // Update the small zoom drawing area QPoint pt = mapFromGlobal(global_start); @@ -938,7 +1009,7 @@ break; case DRAG_RESIZE: - if (doHMove) { + if (curItem && doHMove) { int w = ev_pos.x() - curItem->x(); if(w < 1) w = 1; @@ -992,7 +1063,7 @@ QPoint dist = ev_pos - start; int ax = ABS(rmapx(dist.x())); int ay = ABS(rmapy(dist.y())); - bool moving = (ax >= 2) || (ay > 2); + bool isMoving = (ax >= 2) || (ay > 2); int modifiers = event->modifiers(); bool ctrl = modifiers & Qt::ControlModifier; bool shift = modifiers & Qt::ShiftModifier; @@ -1070,11 +1141,13 @@ switch (drag) { case DRAG_LASSO_START: - if (!moving) + if (!isMoving) break; drag = DRAG_LASSO; setCursor(); // proceed with DRAG_LASSO: + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH case DRAG_LASSO: { lasso = QRect(start.x(), start.y(), dist.x(), dist.y()); @@ -1087,7 +1160,7 @@ case DRAG_MOVE_START: case DRAG_COPY_START: case DRAG_CLONE_START: - if (!moving) + if (!isMoving) break; if (keyState & Qt::ShiftModifier) { if (ax > ay) { @@ -1116,7 +1189,7 @@ drag = DRAG_CLONE; } setCursor(); - if (!curItem->isSelected()) { + if (curItem && !curItem->isSelected()) { if (drag == DRAG_MOVE) deselectAll(); selectItem(curItem, true); @@ -1194,11 +1267,15 @@ break; case DRAG_RESIZE: - if (last_dist.x()) { - int w = ev_pos.x() - curItem->x(); - if(w < 1) - w = 1; - curItem->setWidth(w); + if (curItem && last_dist.x()) { + if(resizeDirection == RESIZE_TO_THE_RIGHT){ + int w = ev_pos.x() - curItem->x(); + if(w < 1) + w = 1; + curItem->setWidth(w); + }else{ + resizeToTheLeft(ev_pos); + } redraw(); } break; @@ -1224,6 +1301,7 @@ { ignore_mouse_move = true; // Avoid recursion. QCursor::setPos(screen_center); + //ignore_mouse_move = false; } } break; @@ -1237,10 +1315,23 @@ { ignore_mouse_move = true; // Avoid recursion. QCursor::setPos(screen_center); + //ignore_mouse_move = false; } break; case DRAG_OFF: + if(_tool == PencilTool){ + if(findCurrentItem(ev_pos)){ + QWidget::setCursor(QCursor(Qt::SizeHorCursor)); + break; + } + } + else if(_tool == AutomationTool){ + // The PartCanvas mouseMove will take care of its own cursor. + // Break otherwise there is bad flickering as the 'pointing hand' competes with 'cross' etc. + break; + } + setCursor(); break; } @@ -1262,7 +1353,12 @@ canScrollUp = true; canScrollDown = true; if (event->buttons() & (Qt::LeftButton|Qt::RightButton|Qt::MidButton) & ~(event->button())) - return; + { + // Make sure this is done. See mousePressEvent. + showCursor(); + setMouseGrab(false); + return; + } QPoint pos = event->pos(); bool ctrl = event->modifiers() & Qt::ControlModifier; @@ -1273,26 +1369,30 @@ case DRAG_MOVE_START: case DRAG_COPY_START: case DRAG_CLONE_START: - if (curItem->part() != curPart) { + if (curItem && curItem->part() != curPart) { curPart = curItem->part(); curPartId = curPart->sn(); curPartChanged(); } if (!ctrl) deselectAll(); - if (!shift) { //Select or deselect only the clicked item - selectItem(curItem, !(ctrl && curItem->isSelected())); - } - else { //Select or deselect all on the same pitch (e.g. same y-value) - bool selectionFlag = !(ctrl && curItem->isSelected()); - for (iCItem i = items.begin(); i != items.end(); ++i) - if (i->second->y() == curItem->y() ) - selectItem(i->second, selectionFlag); - } + if(curItem) + { + if (!shift) { //Select or deselect only the clicked item + selectItem(curItem, !(ctrl && curItem->isSelected())); + } + else { //Select or deselect all on the same pitch (e.g. same y-value) + bool selectionFlag = !(ctrl && curItem->isSelected()); + for (iCItem i = items.begin(); i != items.end(); ++i) + if (i->second->y() == curItem->y() ) + selectItem(i->second, selectionFlag); + } + } updateSelection(); redrawFlag = true; - itemReleased(curItem, curItem->pos()); + if(curItem) + itemReleased(curItem, curItem->pos()); break; case DRAG_COPY: endMoveItems(pos, MOVE_COPY, 0, !shift); @@ -1324,7 +1424,17 @@ case DRAG_OFF: break; case DRAG_RESIZE: - resizeItem(curItem, shift, ctrl); + if(curItem){ + if(resizeDirection == RESIZE_TO_THE_LEFT){ + QPoint rpos = QPoint(raster(pos).x(), curItem->y()); + resizeToTheLeft(rpos); + curItem->move(start); + } + resizeItem(curItem, shift, ctrl); + updateSelection(); + redraw(); + resizeDirection = RESIZE_TO_THE_RIGHT; // reset to default state or ctrl+rightclick resize will cease to work + } break; case DRAG_NEW: if(newCItem) @@ -1363,6 +1473,7 @@ pos = global_start; ignore_mouse_move = true; // Avoid recursion. QCursor::setPos(global_start); + //ignore_mouse_move = false; } break; @@ -1372,6 +1483,7 @@ pos = global_start; ignore_mouse_move = true; // Avoid recursion. QCursor::setPos(global_start); + //ignore_mouse_move = false; } break; } @@ -1396,7 +1508,11 @@ drag = DRAG_OFF; if (redrawFlag) redraw(); - setCursor(); + + // Make sure this is done. See mousePressEvent. + setCursor(); // Calls showCursor(). + setMouseGrab(false); + mouseRelease(pos); } @@ -1502,8 +1618,55 @@ // setCursor //--------------------------------------------------------- +CItem *Canvas::findCurrentItem(const QPoint &cStart) +{ + //--------------------------------------------------- + // set curItem to item mouse is pointing + // (if any) + //--------------------------------------------------- + + CItem *item = 0; + if (virt()) + item = items.find(cStart); + else { + for (ciCItem i = items.begin(); i != items.end(); ++i) { + QRect box = i->second->bbox(); + int x = rmapxDev(box.x()); + int y = rmapyDev(box.y()); + int w = rmapxDev(box.width()); + int h = rmapyDev(box.height()); + QRect r(x, y, w, h); + r.translate(i->second->pos().x(), i->second->pos().y()); + if (r.contains(cStart)) { + if(i->second->isSelected()) + return i->second; + else + { + if(!item) + item = i->second; + } + } + } + } + return item; +} + +void Canvas::resizeToTheLeft(const QPoint &pos) +{ + int newX = pos.x(); + if(end.x() - newX < 1) + newX = end.x() - 1; + int dx = end.x() - newX; + curItem->setWidth(dx); + QPoint mp(newX, curItem->y()); + curItem->setMp(mp); + curItem->move(mp); + //fprintf(stderr, "newX=%d, dx=%d\n", newX, dx); +} + void Canvas::setCursor() { + showCursor(); switch (drag) { case DRAGX_MOVE: case DRAGX_COPY: @@ -1532,14 +1695,14 @@ case DRAG_PAN: if(MusEGlobal::config.borderlessMouse) - QWidget::setCursor(QCursor(Qt::BlankCursor)); // Hide it. + showCursor(false); // CAUTION else QWidget::setCursor(QCursor(Qt::ClosedHandCursor)); break; case DRAG_ZOOM: if(MusEGlobal::config.borderlessMouse) - QWidget::setCursor(QCursor(Qt::BlankCursor)); // Hide it. + showCursor(false); // CAUTION break; case DRAG_DELETE: @@ -1567,7 +1730,7 @@ QWidget::setCursor(QCursor(*editmuteIcon, 4, 15)); break; case AutomationTool: - QWidget::setCursor(QCursor(Qt::PointingHandCursor)); + QWidget::setCursor(QCursor(Qt::ArrowCursor)); break; case PanTool: QWidget::setCursor(QCursor(Qt::OpenHandCursor)); @@ -1605,19 +1768,32 @@ // isSingleSelection //--------------------------------------------------------- -bool Canvas::isSingleSelection() +bool Canvas::isSingleSelection() const { return selectionSize() == 1; } //--------------------------------------------------------- +// itemsAreSelected +//--------------------------------------------------------- + +bool Canvas::itemsAreSelected() const + { + for (ciCItem i = items.begin(); i != items.end(); ++i) { + if (i->second->isSelected()) + return true; + } + return false; + } + +//--------------------------------------------------------- // selectionSize //--------------------------------------------------------- -int Canvas::selectionSize() +int Canvas::selectionSize() const { int n = 0; - for (iCItem i = items.begin(); i != items.end(); ++i) { + for (ciCItem i = items.begin(); i != items.end(); ++i) { if (i->second->isSelected()) ++n; } diff -Nru muse-2.1.2/muse/widgets/canvas.h muse-3.0.2+ds1/muse/widgets/canvas.h --- muse-2.1.2/muse/widgets/canvas.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/canvas.h 2017-12-04 21:01:19.000000000 +0000 @@ -56,10 +56,25 @@ bool canScrollRight; bool canScrollUp; bool canScrollDown; + + CItem *findCurrentItem(const QPoint &cStart); + + // Whether we have grabbed the mouse. + bool _mouseGrabbed; + // The number of times we have called QApplication::setOverrideCursor(). + // This should always be one or zero, anything else is an error, but unforeseen + // events might cause us to miss a decrement with QApplication::restoreOverrideCursor(). + int _cursorOverrideCount; + + // If show is true, calls QApplication::restoreOverrideCursor() until _cursorOverrideCount-- is <= 0. + // If show is false, calls QApplication::setOverrideCursor with a blank cursor. + void showCursor(bool show = true); + // Sets or resets the _mouseGrabbed flag and grabs or releases the mouse. + void setMouseGrab(bool grabbed = false); protected: enum DragMode { - DRAG_OFF, DRAG_NEW, + DRAG_OFF=0, DRAG_NEW, DRAG_MOVE_START, DRAG_MOVE, DRAG_COPY_START, DRAG_COPY, DRAG_CLONE_START, DRAG_CLONE, @@ -85,21 +100,27 @@ enum MenuIdBase { TOOLS_ID_BASE=10000 }; + enum ResizeDirection { + RESIZE_TO_THE_LEFT, + RESIZE_TO_THE_RIGHT + }; CItemList items; CItemList moving; CItem* newCItem; CItem* curItem; MusECore::Part* curPart; - int curPartId; + int curPartId; int canvasTools; DragMode drag; QRect lasso; QPoint start; + QPoint end; QPoint global_start; Tool _tool; unsigned pos[3]; + ResizeDirection resizeDirection; HScrollDir hscrollDir; VScrollDir vscrollDir; @@ -108,6 +129,9 @@ QMenu* itemPopupMenu; QMenu* canvasPopupMenu; + bool supportsResizeToTheLeft; + + void resizeToTheLeft(const QPoint &pos); void setCursor(); virtual void viewKeyPressEvent(QKeyEvent* event); virtual void viewKeyReleaseEvent(QKeyEvent* event); @@ -138,7 +162,6 @@ virtual void resizeItem(CItem*, bool noSnap=false, bool ctrl=false) = 0; virtual void newItem(CItem*, bool noSnap=false) = 0; virtual bool deleteItem(CItem*) = 0; - int getCurrentDrag(); /*! \brief Virtual member @@ -168,7 +191,7 @@ virtual void itemPopup(CItem* /*item */, int /*n*/, const QPoint& /*pt*/) {} void canvasPopup(int); - virtual void startDrag(CItem*, bool) {} + virtual void startDrag(CItem*, DragType) = 0;// {} // selection virtual void deselectAll(); @@ -207,12 +230,16 @@ public: Canvas(QWidget* parent, int sx, int sy, const char* name = 0); virtual ~Canvas(); - bool isSingleSelection(); - int selectionSize(); + // Whether we have grabbed the mouse. + bool mouseGrabbed() const { return _mouseGrabbed; } + bool isSingleSelection() const; + int selectionSize() const; + bool itemsAreSelected() const; Tool tool() const { return _tool; } MusECore::Part* part() const { return curPart; } void setCurrentPart(MusECore::Part*); void setCanvasTools(int n) { canvasTools = n; } + int getCurrentDrag(); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/choose_sysex_base.ui muse-3.0.2+ds1/muse/widgets/choose_sysex_base.ui --- muse-2.1.2/muse/widgets/choose_sysex_base.ui 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/choose_sysex_base.ui 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,175 @@ + + + ChooseSysexBase + + + + 0 + 0 + 427 + 255 + + + + Dialog + + + + + + + + + Hex: + + + + + + + true + + + + + 0 + 0 + 411 + 43 + + + + + + + QFrame::Panel + + + QFrame::Sunken + + + TextLabel + + + + + + + + + + + Comment: + + + + + + + true + + + + + 0 + 0 + 411 + 43 + + + + + + + QFrame::Panel + + + QFrame::Sunken + + + TextLabel + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &OK + + + true + + + true + + + + + + + &Cancel + + + true + + + + + + + + + + + okButton + clicked() + ChooseSysexBase + accept() + + + 333 + 237 + + + 213 + 127 + + + + + cancelButton + clicked() + ChooseSysexBase + reject() + + + 390 + 237 + + + 213 + 127 + + + + + diff -Nru muse-2.1.2/muse/widgets/choose_sysex.cpp muse-3.0.2+ds1/muse/widgets/choose_sysex.cpp --- muse-2.1.2/muse/widgets/choose_sysex.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/choose_sysex.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,104 @@ +//========================================================= +// MusE +// Linux Music Editor +// choose_sysex.cpp +// (C) Copyright 2014 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 +#include +#include +#include +#include + +#include "choose_sysex.h" +#include "minstrument.h" + +namespace MusECore { +extern QString sysex2string(int len, unsigned char* data); +} + +namespace MusEGui { + +ChooseSysexDialog::ChooseSysexDialog(QWidget* parent, MusECore::MidiInstrument* instr) + : QDialog(parent) +{ + setupUi(this); + sysexList->clear(); + _sysex = NULL; + _instr = instr; + if(_instr) + { + foreach(const MusECore::SysEx* s, _instr->sysex()) + { + if(!s) + continue; + QListWidgetItem* item = new QListWidgetItem(s->name); + QVariant v = QVariant::fromValue((void*)s); + item->setData(Qt::UserRole, v); + sysexList->addItem(item); + } + } + + if(sysexList->item(0)) + sysexList->item(0)->setSelected(true); + + connect(sysexList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + SLOT(sysexChanged(QListWidgetItem*, QListWidgetItem*))); + + sysexChanged(sysexList->item(0), 0); +} + +//--------------------------------------------------------- +// sysexChanged +//--------------------------------------------------------- + +void ChooseSysexDialog::sysexChanged(QListWidgetItem* sel, QListWidgetItem*) +{ + if(!sel) + { + hexLabel->setText(""); + commentLabel->setText(""); + return; + } + MusECore::SysEx* sx = (MusECore::SysEx*)sel->data(Qt::UserRole).value(); + if(!sx) + { + hexLabel->setText(""); + commentLabel->setText(""); + return; + } + hexLabel->setText(MusECore::sysex2string(sx->dataLen, sx->data)); + commentLabel->setText(sx->comment); +} + +//--------------------------------------------------------- +// accept +//--------------------------------------------------------- + +void ChooseSysexDialog::accept() +{ + _sysex = NULL; + QListWidgetItem* item = sysexList->currentItem(); + if(item) + _sysex = (MusECore::SysEx*)item->data(Qt::UserRole).value(); + + QDialog::accept(); +} + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/choose_sysex.h muse-3.0.2+ds1/muse/widgets/choose_sysex.h --- muse-2.1.2/muse/widgets/choose_sysex.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/choose_sysex.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,61 @@ +//========================================================= +// MusE +// Linux Music Editor +// choose_sysex.h +// (C) Copyright 2014 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 __CHOOSE_SYSEX_H__ +#define __CHOOSE_SYSEX_H__ + +#include "ui_choose_sysex_base.h" + +class QWidget; +class QDialog; +class QListWidgetItem; + +namespace MusECore { + class MidiInstrument; + struct SysEx; +} + +namespace MusEGui { + +//--------------------------------------------------------- +// ChooseSysexDialog +//--------------------------------------------------------- + +class ChooseSysexDialog : public QDialog, public Ui::ChooseSysexBase { + Q_OBJECT + + MusECore::MidiInstrument* _instr; + MusECore::SysEx* _sysex; + + private slots: + virtual void accept(); + void sysexChanged(QListWidgetItem*, QListWidgetItem*); + + public: + ChooseSysexDialog(QWidget* parent = NULL, MusECore::MidiInstrument* instr = NULL); + MusECore::SysEx* sysex() { return _sysex; } + }; + +} // namespace MusEGui + +#endif + diff -Nru muse-2.1.2/muse/widgets/citem.cpp muse-3.0.2+ds1/muse/widgets/citem.cpp --- muse-2.1.2/muse/widgets/citem.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/citem.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -22,6 +22,8 @@ #include "part.h" #include "citem.h" +#include "undo.h" +#include "song.h" #include namespace MusEGui { @@ -65,7 +67,7 @@ void CItem::setSelected(bool f) { - _event.empty() ? _part->setSelected(f) : _event.setSelected(f); + _event.empty() ? _part->setSelected(f) : MusEGlobal::song->selectEvent(_event, _part, f); } //--------------------------------------------------------- @@ -74,8 +76,7 @@ CItem* CItemList::find(const QPoint& pos) const { - rciCItem ius; - bool usfound = false; + CItem* item = 0; for (rciCItem i = rbegin(); i != rend(); ++i) { if (i->second->contains(pos)) { @@ -84,18 +85,12 @@ else { - if(!usfound) - { - ius = i; - usfound = true; - } + if(!item) + item = i->second; } } } - if(usfound) - return ius->second; - else - return 0; + return item; } //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/widgets/cliplisteditorbase.ui muse-3.0.2+ds1/muse/widgets/cliplisteditorbase.ui --- muse-2.1.2/muse/widgets/cliplisteditorbase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/cliplisteditorbase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -1,167 +1,140 @@ - - - - - ClipListEditorBase - - - - 0 - 0 - 600 - 480 - - - - MusE: ClipList - - - - 11 + + ClipListEditorBase + + + + 0 + 0 + 600 + 480 + + + + MusE: ClipList + + + + 6 + + + 11 + + + + + true + + + true + + + + Name + + + + + Refs + + + + + Samplerate + + + + + Len + + + + Data + + + + + Status + + + + + + + + Clip Properties + + - 6 + 6 + + + 11 - - - true - - - true - - - - Name - - - true - - - true - - - - - Refs - - - true - - - true - - - - - Samplerate - - - true - - - true - - - - - Len - - - true - - - true - - - - - Data - - - true - - - true - - - + + + Pos: + + + false + + + + + + + true + + + + + + + Len: + + + false + + + + + + + true + + - - - Clip Properties - - - - 11 - - - 6 - - - - - Pos: - - - false - - - - - - - true - - - - - - - Len: - - - false - - - - - - - true - - - - - - - - 20 - 20 - - - - QSizePolicy::Expanding - - - Qt::Horizontal - - - - - + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 20 + 20 + + + - - - - - - Awl::PosEdit - QWidget -
    awl/posedit.h
    - 0 -
    -
    +
    +
    +
    +
    + + + + + Awl::PosEdit + QWidget +
    awl/posedit.h
    +
    +
    + + diff -Nru muse-2.1.2/muse/widgets/clipper_label.cpp muse-3.0.2+ds1/muse/widgets/clipper_label.cpp --- muse-2.1.2/muse/widgets/clipper_label.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/clipper_label.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,134 @@ +#include +#include +#include +#include + +#include "fastlog.h" +#include "clipper_label.h" + +namespace MusEGui +{ + +ClipperLabel::ClipperLabel(QWidget *parent): + QFrame(parent), + _isClipped(false), + _value(0.0) +{ + // Background is drawn by us. + setBackgroundRole(QPalette::NoRole); + setAttribute(Qt::WA_NoSystemBackground); + setAttribute(Qt::WA_StaticContents); + setAttribute(Qt::WA_OpaquePaintEvent); + + //setFrameStyle(QFrame::Box | QFrame::Sunken); + setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + + //setLineWidth(1); // Not really required - StyledPanel always has 1 width. + //setMidLineWidth(0); + + const int fw = frameWidth(); + setContentsMargins(fw, fw, fw, fw); +// setProperty("clipped", "false"); + + const QColor fc(255, 75, 75); + const QColor fcd = fc.darker(150); + _onGradient.setColorAt(0.0, fcd); + _onGradient.setColorAt(0.5, fc); + _onGradient.setColorAt(1.0, fcd); + + setVal(_value, true); +} + +QSize ClipperLabel::sizeHint() const +{ + const int fw = frameWidth(); + const QSize sz = fontMetrics().boundingRect("-88.8.").size(); + return QSize(sz.width() + 2 * fw, sz.height() + 2 * fw); +} + +void ClipperLabel::paintEvent(QPaintEvent *e) +{ + const QRect& r = frameRect(); + QPainter p; + p.begin(this); + if(_isClipped) + p.fillRect(r, _onGradient); + else + p.fillRect(r, palette().window()); + p.end(); + + QFrame::paintEvent(e); + + p.begin(this); + if(_isClipped) + p.setPen(Qt::white); + //p.drawText(e->rect(), Qt::AlignCenter, _text); // Drawing artifacts? + p.drawText(contentsRect(), Qt::AlignCenter, _text); + p.end(); +} + +void ClipperLabel::resizeEvent(QResizeEvent *) +{ + _onGradient.setStart(0, frameRect().y()); + _onGradient.setFinalStop(0, frameRect().y() + frameRect().height() - 1); +} + +void ClipperLabel::mousePressEvent(QMouseEvent *ev) +{ + ev->accept(); + emit clicked(); +} + +void ClipperLabel::mouseReleaseEvent(QMouseEvent *ev) +{ + ev->accept(); +} + +void ClipperLabel::mouseMoveEvent(QMouseEvent *ev) +{ + ev->accept(); +} + +void ClipperLabel::contextMenuEvent(QContextMenuEvent * ev) +{ + ev->accept(); +} + +void ClipperLabel::setClipped(bool b) +{ + if(b != _isClipped) + { + _isClipped = b; + setVal(_value, true); + update(); + } +} + +void ClipperLabel::setVal(double v, bool force) +{ + if((v == _value) && !force) + { + return; + } + + _value = v; + + v = MusECore::fast_log10(v) * 20.0; + + if(v >= -60.0f) + { + _text = locale().toString(v, 'f', 1); + } + else + { + _text = QString("-"); + _text += QChar(0x221e); // The infinty character + + } + + update(); + +} + +} + diff -Nru muse-2.1.2/muse/widgets/clipper_label.h muse-3.0.2+ds1/muse/widgets/clipper_label.h --- muse-2.1.2/muse/widgets/clipper_label.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/clipper_label.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,41 @@ +#ifndef _CLIPPER_LABEL_H_ +#define _CLIPPER_LABEL_H_ + +#include + +namespace MusEGui +{ +class ClipperLabel : public QFrame +{ + Q_OBJECT + Q_PROPERTY( bool clipped READ clipped WRITE setClipped ) + +private: + bool _isClipped; + double _value; + QString _text; + QLinearGradient _onGradient; + +public: + ClipperLabel(QWidget *parent = 0); + virtual QSize sizeHint() const; + bool clipped() const { return _isClipped; } + void setClipped(bool b); + void setVal(double v, bool force = false); + +protected: + virtual void paintEvent(QPaintEvent *); + virtual void resizeEvent(QResizeEvent *e); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void contextMenuEvent(QContextMenuEvent*); + +signals: + void clicked(); + +}; + +} + +#endif diff -Nru muse-2.1.2/muse/widgets/CMakeLists.txt muse-3.0.2+ds1/muse/widgets/CMakeLists.txt --- muse-2.1.2/muse/widgets/CMakeLists.txt 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/CMakeLists.txt 2017-12-04 21:01:18.000000000 +0000 @@ -30,24 +30,32 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP (widget_mocs +QT5_WRAP_CPP (widget_mocs aboutbox_impl.h arrangercolumns.h action.h - bigtime.h + bigtime.h canvas.h checkbox.h + choose_sysex.h + clipper_label.h colorframe.h comboQuant.h combobox.h comment.h + compact_knob.h + compact_patch_edit.h + compact_slider.h copy_on_write.h + cpu_toolbar.h ctrlcombo.h custom_widget_actions.h dentry.h didyouknow.h doublelabel.h doublespinbox.h + editevent.h + elided_label.h filedialog.h genset.h mdisettings.h @@ -55,10 +63,13 @@ hitscale.h intlabel.h knob.h - knob_and_meter.h + knob_and_meter.h + lcd_widgets.h lcombo.h + line_edit.h menutitleitem.h meter.h + meter_slider.h metronome.h midi_audio_control.h midisyncimpl.h @@ -67,7 +78,7 @@ mlabel.h mtscale.h mtscale_flo.h - mtrackinfo.h + # mtrackinfo.h nentry.h noteinfo.h pastedialog.h @@ -75,11 +86,15 @@ pitchedit.h pitchlabel.h pixmap_button.h + plugindialog.h + popup_double_spinbox.h popupmenu.h # posedit.h poslabel.h projectcreateimpl.h routepopup.h + scroll_area.h + scrollbar.h scrollscale.h shortcutcapturedialog.h shortcutconfig.h @@ -96,18 +111,20 @@ splitter.h swidget.h tb1.h - tempolabel.h - tools.h + tempolabel.h + text_edit.h + tools.h + trackinfo_layout.h tracks_duplicate.h # ttoolbar.h ttoolbutton.h unusedwavefiles.h - verticalmeter.h view.h vscale.h visibletracks.h vst_native_editor.h warn_bad_timing.h + widget_stack.h ) ## @@ -117,11 +134,13 @@ aboutbox.ui arrangercolumnsbase.ui appearancebase.ui + choose_sysex_base.ui cliplisteditorbase.ui commentbase.ui configmidifilebase.ui copy_on_write_base.ui didyouknow.ui + editctrlbase.ui editnotedialogbase.ui editsysexdialogbase.ui fdialogbuttons.ui @@ -134,9 +153,10 @@ midi_audio_control_base.ui mittransposebase.ui mixdowndialogbase.ui - mtrackinfobase.ui + # mtrackinfobase.ui pastedialogbase.ui pasteeventsdialogbase.ui + plugindialogbase.ui projectcreate.ui shortcutcapturedialogbase.ui shortcutconfigbase.ui @@ -147,7 +167,7 @@ unusedwavefiles.ui warn_bad_timing.ui ) -QT4_WRAP_UI (widget_ui_headers ${widgets_ui_files}) +QT5_WRAP_UI (widget_ui_headers ${widgets_ui_files}) ## ## List of source files to compile @@ -155,15 +175,22 @@ file (GLOB widgets_source_files aboutbox_impl.cpp arrangercolumns.cpp + background_painter.cpp bigtime.cpp canvas.cpp checkbox.cpp + choose_sysex.cpp citem.cpp + clipper_label.cpp colorframe.cpp comboQuant.cpp combobox.cpp comment.cpp + compact_knob.cpp + compact_patch_edit.cpp + compact_slider.cpp copy_on_write.cpp + cpu_toolbar.cpp ctrlcombo.cpp custom_widget_actions.cpp dentry.cpp @@ -171,6 +198,8 @@ doublelabel.cpp doublespinbox.cpp drange.cpp + editevent.cpp + elided_label.cpp filedialog.cpp genset.cpp mdisettings.cpp @@ -179,9 +208,12 @@ intlabel.cpp knob.cpp knob_and_meter.cpp + lcd_widgets.cpp lcombo.cpp + line_edit.cpp menutitleitem.cpp meter.cpp + meter_slider.cpp metronome.cpp midi_audio_control.cpp midisyncimpl.cpp @@ -189,7 +221,7 @@ mixdowndialog.cpp mlabel.cpp mmath.cpp - mtrackinfo.cpp + # mtrackinfo.cpp mtscale.cpp mtscale_flo.cpp nentry.cpp @@ -199,14 +231,18 @@ pitchedit.cpp pitchlabel.cpp pixmap_button.cpp + plugindialog.cpp + popup_double_spinbox.cpp popupmenu.cpp # posedit.cpp poslabel.cpp - projectcreateimpl.cpp + projectcreateimpl.cpp routepopup.cpp scldiv.cpp scldraw.cpp sclif.cpp + scroll_area.cpp + scrollbar.cpp scrollscale.cpp shortcutcapturedialog.cpp shortcutconfig.cpp @@ -223,19 +259,21 @@ swidget.cpp tb1.cpp tempolabel.cpp - tools.cpp + text_edit.cpp + tools.cpp + trackinfo_layout.cpp tracks_duplicate.cpp # ttoolbar.cpp ttoolbutton.cpp unusedwavefiles.cpp utils.cpp velocity.cpp - verticalmeter.cpp view.cpp vscale.cpp visibletracks.cpp vst_native_editor.cpp warn_bad_timing.cpp + widget_stack.cpp ) ## @@ -262,7 +300,7 @@ ## set_target_properties( widgets # PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h ${MUSECXXFLAGS} -I../ -I${PROJECT_SOURCE_DIR}/synti " - PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all.h ${MUSECXXFLAGS} -I../ -I${PROJECT_SOURCE_DIR}/synti" + PROPERTIES COMPILE_FLAGS "${VST_SDK_QUIRK} -include ${PROJECT_BINARY_DIR}/all.h ${MUSECXXFLAGS} -I../ -I${PROJECT_SOURCE_DIR}/synti" OUTPUT_NAME muse_widgets ) @@ -272,7 +310,6 @@ target_link_libraries ( widgets ${QT_LIBRARIES} icons - instruments ) ## diff -Nru muse-2.1.2/muse/widgets/colorframe.cpp muse-3.0.2+ds1/muse/widgets/colorframe.cpp --- muse-2.1.2/muse/widgets/colorframe.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/colorframe.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -12,6 +12,11 @@ { QRect r(e->rect()); QPainter p(this); - p.fillRect(r, color); + p.fillRect(r, _color); } +void ColorFrame::mousePressEvent(QMouseEvent* e) +{ + e->accept(); + emit clicked(); +} diff -Nru muse-2.1.2/muse/widgets/colorframe.h muse-3.0.2+ds1/muse/widgets/colorframe.h --- muse-2.1.2/muse/widgets/colorframe.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/colorframe.h 2017-12-04 21:01:19.000000000 +0000 @@ -6,18 +6,22 @@ class ColorFrame : public QWidget { Q_OBJECT +protected: virtual void paintEvent(QPaintEvent*); + virtual void mousePressEvent(QMouseEvent*); public: explicit ColorFrame(QWidget *parent = 0); - void setColor(QColor c) {color = c; update();} + QColor color() const { return _color; } + void setColor(QColor c) {_color = c; update();} signals: + void clicked(); public slots: private: - QColor color; + QColor _color; }; #endif // COLORFRAME_H diff -Nru muse-2.1.2/muse/widgets/combobox.cpp muse-3.0.2+ds1/muse/widgets/combobox.cpp --- muse-2.1.2/muse/widgets/combobox.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/combobox.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -105,4 +105,81 @@ itemlist << id; } + +//--------------------------------------------------------- +// CompactComboBox +//--------------------------------------------------------- + +CompactComboBox::CompactComboBox(QWidget* parent, const QIcon& icon, const char* name) + : CompactToolButton(parent, icon, name) + { + _currentItem = 0; + + menu = new QMenu(this); + + autoTypeSignalMapper = new QSignalMapper(this); + connect(autoTypeSignalMapper, SIGNAL(mapped(int)), this, SLOT(activatedIntern(int))); + } + +CompactComboBox::~CompactComboBox() + { + delete menu; + } + +//--------------------------------------------------------- +// mousePressEvent +//--------------------------------------------------------- + +void CompactComboBox::mousePressEvent(QMouseEvent* /*ev*/) + { + menu->exec(QCursor::pos()); + } + +//--------------------------------------------------------- +// wheelEvent +//--------------------------------------------------------- + +void CompactComboBox::wheelEvent(QWheelEvent* ev) + { + int i = itemlist.indexOf(_currentItem); + int len = itemlist.count(); + if (ev->delta() > 0 && i > 0) + activatedIntern(_currentItem-1); + else if (ev->delta() < 0 && -1 < i && i < len - 1) + activatedIntern(_currentItem+1); + } + +//--------------------------------------------------------- +// activated +//--------------------------------------------------------- + +void CompactComboBox::activatedIntern(int id) + { + setCurrentItem(id); + emit activated(id); + } + +//--------------------------------------------------------- +// setCurrentItem +//--------------------------------------------------------- + +void CompactComboBox::setCurrentItem(int id) + { + QAction* act = (QAction*) autoTypeSignalMapper->mapping(id); + _currentItem = id; + setText(act->text()); + } + +//--------------------------------------------------------- +// insertItem +//--------------------------------------------------------- + +void CompactComboBox::addAction(const QString& s, int id) + { + QAction *act = menu->addAction(s); + connect(act, SIGNAL(triggered()), autoTypeSignalMapper, SLOT(map())); + autoTypeSignalMapper->setMapping(act, id); + itemlist << id; + } + } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/combobox.h muse-3.0.2+ds1/muse/widgets/combobox.h --- muse-2.1.2/muse/widgets/combobox.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/combobox.h 2017-12-04 21:01:19.000000000 +0000 @@ -25,8 +25,11 @@ #include +#include "ttoolbutton.h" + class QMenu; class QSignalMapper; +class QIcon; namespace MusEGui { @@ -54,10 +57,40 @@ public: ComboBox(QWidget* parent = 0, const char* name = 0); - ~ComboBox(); + virtual ~ComboBox(); + void setCurrentItem(int); + void addAction(const QString& s, int id = -1); + }; + +//--------------------------------------------------------- +// CompactComboBox +//--------------------------------------------------------- + +class CompactComboBox : public CompactToolButton { + Q_OBJECT + + int _currentItem; + QList itemlist; + + QMenu* menu; + virtual void mousePressEvent(QMouseEvent*); + virtual void wheelEvent(QWheelEvent*); + + QSignalMapper* autoTypeSignalMapper; + + private slots: + void activatedIntern(int id); + + signals: + void activated(int id); + + public: + CompactComboBox(QWidget* parent = 0, const QIcon& icon = QIcon(), const char* name = 0); + virtual ~CompactComboBox(); void setCurrentItem(int); void addAction(const QString& s, int id = -1); }; + } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/compact_knob.cpp muse-3.0.2+ds1/muse/widgets/compact_knob.cpp --- muse-2.1.2/muse/widgets/compact_knob.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/compact_knob.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -0,0 +1,1385 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// compact_knob.cpp +// Adapted from Qwt Lib: +// Copyright (C) 1997 Josef Wilgen +// (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) completely redesigned. +// (C) Copyright 2016 - 2017 Tim E. Real (terminator356 on sourceforge). New CompactKnob based on Knob. +// +// 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 "background_painter.h" +#include "popup_double_spinbox.h" +#include "compact_knob.h" + +#include +#include +#include "mmath.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_KNOB(dev, format, args...) //fprintf(dev, format, ##args); + + +namespace MusEGui { + +//--------------------------------------------------------- +// The QwtKnob widget imitates look and behaviour of a volume knob on a radio. +// It contains +// a scale around the knob which is set up automatically or can +// be configured manually (see @^QwtScaleIf@). +// Automatic scrolling is enabled when the user presses a mouse +// button on the scale. For a description of signals, slots and other +// members, see QwtSliderBase@. +//--------------------------------------------------------- + + +//--------------------------------------------------------- +// CompactKnob +//--------------------------------------------------------- + +CompactKnob::CompactKnob(QWidget* parent, const char* name, + KnobLabelPos labelPos, + const QString& labelText, + const QString& valPrefix, + const QString& valSuffix, + const QString& specialValueText, + const QColor& faceColor) + : SliderBase(parent, name) + { + if(objectName().isEmpty()) + setObjectName(QStringLiteral("CompactKnob")); + + setMouseTracking(true); + setEnabled(true); + setFocusPolicy(Qt::WheelFocus); + + //setAutoFillBackground(false); + //setAttribute(Qt::WA_NoSystemBackground); + //setAttribute(Qt::WA_StaticContents); + // This is absolutely required for speed! Otherwise painfully slow because of full background + // filling, even when requesting small udpdates! Background is drawn by us. + //setAttribute(Qt::WA_OpaquePaintEvent); + + setBorderlessMouse(false); + setCursorHoming(false); + //setPagingButtons(Qt::NoButton); + + setEnableValueToolTips(true); + setShowValueToolTipsOnHover(true); + + _bkgPainter = new ItemBackgroundPainter(); + + _hovered = false; + _labelHovered = false; + _knobHovered = false; + + _editor = 0; + _editMode = false; + + hasScale = false; + + d_xMargin = 1; + d_yMargin = 1; + d_borderWidth = 4; + d_shineWidth = 1; + d_totalAngle = 270.0; + d_scaleDist = 1; + d_symbol = Line; + d_maxScaleTicks = 11; + d_knobWidth = 30; + _faceColSel = false; + //d_faceColor = palette().color(QPalette::Window); + //d_rimColor = rimColor; + //if(!d_rimColor.isValid()) + // d_rimColor = palette().mid().color(); + d_faceColor = faceColor; + if(!d_faceColor.isValid()) + d_faceColor = palette().color(QPalette::Window); + d_shinyColor = palette().mid().color(); + d_curFaceColor = d_faceColor; + d_altFaceColor = d_faceColor; + d_markerColor = palette().dark().color().darker(125); + d_dotWidth = 8; + + l_slope = 0; + l_const = 100; + + d_labelPos = labelPos; + d_labelText = labelText; + d_valPrefix = valPrefix; + d_valSuffix = valSuffix; + d_specialValueText = specialValueText; + + _hasOffMode = false; + _valueDecimals = 2; + _off = false; + d_offText = tr("off"); + _showLabel = true; + _showValue = true; + + setUpdateTime(50); + } + +CompactKnob::~CompactKnob() +{ + if(_bkgPainter) + delete _bkgPainter; +} + +// Static. +QSize CompactKnob::getMinimumSizeHint(const QFontMetrics& fm, + KnobLabelPos labelPos, + bool showValue, + bool showLabel, + int xMargin, + int yMargin) +{ + // Put some kind of limit so the knobs aren't tiny at small fonts. + const int minh = 17; + int label_h, label_h_2, fin_label_h; + label_h = (fm.height() - fm.leading() - fm.descent()) + 1; + label_h_2 = 2 * label_h - 1; + + if(showValue && showLabel) + fin_label_h = label_h_2; + else + fin_label_h = fm.height() + 5; + + switch(labelPos) { + case Left: + return QSize(label_h_2 + 2 * xMargin, label_h_2 + 2 * yMargin); + break; + case Right: + return QSize(label_h_2 + 2 * xMargin, label_h_2 + 2 * yMargin); + break; + case Top: + return QSize(label_h_2 + 2 * xMargin, label_h_2 + fin_label_h + 2 * yMargin); + break; + case Bottom: + return QSize(label_h_2 + 2 * xMargin, label_h_2 + fin_label_h + 2 * yMargin); + break; + case None: + break; + } + return QSize(minh + 2 * xMargin, minh + 2 * yMargin); +} + +void CompactKnob::processSliderPressed(int) +{ +// bPressed = true; + update(); +} + +void CompactKnob::processSliderReleased(int) +{ + update(); + + DEBUG_KNOB(stderr, + "CompactKnob::processSliderReleased trackingIsActive:%d val:%.20f valHasChanged:%d\n", + trackingIsActive(), value(), valueHasChangedAtRelease()); + +// Changed. BUG: Was causing problems with sending changed even though it hadn't. +// It should never signal a change on release UNLESS tracking is off, because otherwise the last movement +// already sent the last changed signal. FIXME Even this is still flawed. If no tracking, it would likely +// still signal a change upon simple press and release even though nothing changed. +// if((!tracking()) || valHasChanged()) + if(!trackingIsActive() && valueHasChangedAtRelease()) + emit valueStateChanged(value(), isOff(), id(), d_scrollMode); +} + +QString CompactKnob::toolTipValueText(bool inclLabel, bool inclVal) const +{ + const double minV = minValue(ConvertNone); + const double val = value(ConvertNone); + const QString comp_val_text = isOff() ? d_offText : + ((val <= minV && !d_specialValueText.isEmpty()) ? + d_specialValueText : (d_valPrefix + locale().toString(val, 'f', _valueDecimals) + d_valSuffix)); + QString txt; + if(inclLabel) + txt += d_labelText; + if(inclLabel && inclVal) + txt += QString(": "); + if(inclVal) + { + txt += QString(""); + txt += comp_val_text; + txt += QString(""); + } + return txt; +} + +void CompactKnob::showValueToolTip(QPoint /*p*/) +{ + const QString txt = toolTipValueText(true, true); + if(!txt.isEmpty()) + { + // Seems to be a small problem with ToolTip: Even if we force the font size, + // if a previous tooltip was showing from another control at another font size, + // it refuses to change font size. Also, if we supply the widget to showText(), + // it refuses to change font size and uses the widget's font size instead. + // Also, this craziness with ToolTip's self-offsetting is weird: In class CompactKnob + // it is best when we supply the parent's position, while in class CompactSlider + // it is best when we supply the widget's position - and it STILL isn't right! + // Supplying the widget's position to CompactKnob, or parent's position to CompactSlider + // actually makes the offsetting worse! + if(QToolTip::font().pointSize() != 10) + { + QFont fnt = font(); + fnt.setPointSize(10); + QToolTip::setFont(fnt); + QToolTip::hideText(); + } + QToolTip::showText(mapToGlobal(parentWidget() ? parentWidget()->pos() : pos()), txt, 0, QRect(), 3000); + //QToolTip::showText(mapToGlobal(parentWidget() ? parentWidget()->pos() : pos()), txt); + } +} + +//------------------------------------------------------------ +// CompactKnob::setTotalAngle +// Set the total angle by which the knob can be turned +// +// Syntax +// void CompactKnob::setTotalAngle(double angle) +// +// Parameters +// double angle -- angle in degrees. +// +// Description +// The default angle is 270 degrees. It is possible to specify +// an angle of more than 360 degrees so that the knob can be +// turned several times around its axis. +//------------------------------------------------------------ + +void CompactKnob::setTotalAngle (double angle) + { + if (angle < 10.0) + d_totalAngle = 10.0; + else + d_totalAngle = angle; + d_scale.setAngleRange( -0.5 * d_totalAngle, 0.5 * d_totalAngle); + } + +//------------------------------------------------------------ +// CompactKnob::setRange +// Set the range and step size of the knob +// +// Sets the parameters that define the shininess of the ring +// surrounding the knob and then proceeds by passing the +// parameters to the parent class' setRange() function. +//------------------------------------------------------------ + +void CompactKnob::setRange(double vmin, double vmax, double vstep, int pagesize, DoubleRange::ConversionMode mode) + { + // divide by zero protection. probably too cautious + if (! (vmin == vmax || qMax(-vmin, vmax) == 0)) + { + if (vmin * vmax < 0) + l_slope = 80.0 / qMax(-vmin, vmax); + else + { + l_slope = 80.0 / (vmax - vmin); + l_const = 100 - l_slope * vmin; + } + } + SliderBase::setRange(vmin, vmax, vstep, pagesize, mode); + } + +void CompactKnob::setShowValue(bool show) +{ + _showValue = show; + resize(size()); + updateGeometry(); // Required. + update(); +} + +void CompactKnob::setShowLabel(bool show) +{ + _showLabel = show; + resize(size()); + updateGeometry(); // Required. + update(); +} + +void CompactKnob::setOff(bool v) +{ + if(v && !_hasOffMode) + _hasOffMode = true; + if(_off == v) + return; + _off = v; + update(); + emit valueStateChanged(value(), isOff(), id(), d_scrollMode); +} + +void CompactKnob::setHasOffMode(bool v) +{ + _hasOffMode = v; + setOff(false); +} + +void CompactKnob::setValueState(double v, bool off, ConversionMode mode) +{ + // Do not allow setting value from the external while mouse is pressed. + if(_pressed) + return; + + bool do_off_upd = false; + bool do_val_upd = false; + // Both setOff and setValue emit valueStateChanged and setValue emits valueChanged. + // We will block them and emit our own here. Respect the current block state. + const bool blocked = signalsBlocked(); + if(!blocked) + blockSignals(true); + if(isOff() != off) + { + do_off_upd = true; + setOff(off); + } + if(value(mode) != v) + { + do_val_upd = true; + setValue(v, mode); + } + if(!blocked) + blockSignals(false); + + if(do_off_upd || do_val_upd) + update(); + if(do_val_upd) + emit valueChanged(value(), id()); + if(do_off_upd || do_val_upd) + emit valueStateChanged(value(), isOff(), id(), d_scrollMode); +} + +//------------------------------------------------------------ +//.F CompactKnob::valueChange +// Notify change of value +// +//.u Parameters +// double x -- new value +// +//.u Description +// Sets the slider's value to the nearest multiple +// of the step size. +//------------------------------------------------------------ + +void CompactKnob::valueChange() + { + // Turn the control back on with any value set. + // Wanted to make this flaggable, but actually we + // have to in order to see any value changing, + if(isOff()) + setOff(false); + + recalcAngle(); + d_newVal++; + + update(_knobRect); + if(_showValue) + update(_labelRect); + + // HACK + // In direct mode let the inherited classes (this) call these in their valueChange() methods, + // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. + // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. + // Hack: Since valueChange() is NOT called if nothing changed, in that case these are called for us by the SliderBase. + if(d_scrollMode == ScrDirect) + { + processSliderPressed(id()); + emit sliderPressed(value(), id()); + } + + // Emits valueChanged if tracking enabled. + SliderBase::valueChange(); + // Emit our own combined signal. + if(trackingIsActive()) + emit valueStateChanged(value(), isOff(), id(), d_scrollMode); + } + +//------------------------------------------------------------ +//.F CompactKnob::getValue +// Determine the value corresponding to a specified position +// +//.u Parameters: +// const QPoint &p -- point +// +//.u Description: +// Called by QwtSliderBase +//------------------------------------------------------------ + +double CompactKnob::getValue(const QPoint &p) + { + double newValue; + double oneTurn; + double eqValue; + double arc; + + double dx = double((_knobRect.x() + _knobRect.width() / 2) - p.x() ); + double dy = double((_knobRect.y() + _knobRect.height() / 2) - p.y() ); + + arc = atan2(-dx,dy) * 180.0 / M_PI; + + newValue = 0.5 * (minValue() + maxValue()) + + (arc + d_nTurns * 360.0) * (maxValue() - minValue()) + / d_totalAngle; + + oneTurn = fabs(maxValue() - minValue()) * 360.0 / d_totalAngle; + eqValue = value() + d_mouseOffset; + + if (fabs(newValue - eqValue) > 0.5 * oneTurn) + { + if (newValue < eqValue) + newValue += oneTurn; + else + newValue -= oneTurn; + } + + return newValue; + +} + +//------------------------------------------------------------ +// +//.F CompactKnob::moveValue +// Determine the value corresponding to a specified mouse movement. +// +//.u Syntax +//.f void CompactKnob::moveValue(const QPoint &deltaP, bool fineMode) +// +//.u Parameters +//.p const QPoint &deltaP -- Change in position +//.p bool fineMode -- Fine mode if true, coarse mode if false. +// +//.u Description +// Called by SliderBase +// Coarse mode (the normal mode) maps pixels to values depending on range and width, +// such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. +//------------------------------------------------------------ +double CompactKnob::moveValue(const QPoint &deltaP, bool /*fineMode*/) +{ + // FIXME: To make fine mode workable, we need a way to make the adjustments 'multi-turn'. + + double oneTurn; + double eqValue; + + const QPoint new_p = _lastMousePos + deltaP; + + const int cx = _knobRect.x() + _knobRect.width() / 2; + const int cy = _knobRect.y() + _knobRect.height() / 2; + + const double last_dx = double(cx - _lastMousePos.x()); + const double last_dy = double(cy - _lastMousePos.y()); + const double last_arc = atan2(-last_dx, last_dy) * 180.0 / M_PI; + + const double dx = double(cx - new_p.x()); + const double dy = double(cy - new_p.y()); + const double arc = atan2(-dx, dy) * 180.0 / M_PI; + + const double val = value(ConvertNone); + +// if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) +// { +// const double arc_diff = arc - last_arc; +// const double dval_diff = arc_diff * step(); +// const double new_val = val + dval_diff; +// d_valAccum = new_val; // Reset. +// return d_valAccum; +// } + + const double min = minValue(ConvertNone); + const double max = maxValue(ConvertNone); + const double drange = max - min; + + const double last_val = 0.5 * (min + max) + (last_arc + d_nTurns * 360.0) * drange / d_totalAngle; + const double new_val = 0.5 * (min + max) + (arc + d_nTurns * 360.0) * drange / d_totalAngle; + double dval_diff = new_val - last_val; + + //if(fineMode) + // dval_diff /= 10.0; + + d_valAccum += dval_diff; + + DEBUG_KNOB(stderr, "CompactKnob::moveValue value:%.20f last_val:%.20f new_val:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f d_valAccum:%.20f\n", + val, last_val, new_val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, d_valAccum); + + + oneTurn = fabs(drange) * 360.0 / d_totalAngle; + eqValue = val + d_mouseOffset; + + DEBUG_KNOB(stderr, " oneTurn:%.20f eqValue:%.20f\n", oneTurn, eqValue); + if(fabs(d_valAccum - eqValue) > 0.5 * oneTurn) + { + if (d_valAccum < eqValue) + { + d_valAccum += oneTurn; + DEBUG_KNOB(stderr, " added one turn, new d_valAccum:%.20f\n", d_valAccum); + } + else + { + d_valAccum -= oneTurn; + DEBUG_KNOB(stderr, " subtracted one turn, new d_valAccum:%.20f\n", d_valAccum); + } + } + + return d_valAccum; +} + + +//------------------------------------------------------------ +//.- +//.F CompactKnob::setScrollMode +// Determine the scrolling mode and direction +// corresponding to a specified position +// +//.u Parameters +// const QPoint &p -- point in question +// +//.u Description +// Called by QwtSliderBase +//------------------------------------------------------------ +void CompactKnob::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction) +{ + if(!_knobRect.contains(p)) + { + scrollMode = ScrNone; + direction = 0; + return; + } + // If modifier or button is held, jump directly to the position at first. + // After handling it, the caller can change to SrcMouse scroll mode. + else if(modifiers & Qt::ControlModifier || button == Qt::MidButton) + { + scrollMode = ScrDirect; + direction = 0; + return; + } + + int dx, dy, r; + double arc; + + r = _knobRect.width() / 2; + + dx = _knobRect.x() + r - p.x(); + dy = _knobRect.y() + r - p.y(); + + if((dx * dx) + (dy * dy) <= (r * r)) // point is inside the knob + { + scrollMode = ScrMouse; + direction = 0; + } + else // point lies outside + { + scrollMode = ScrTimer; + arc = atan2(double(-dx), double(dy)) * 180.0 / M_PI; + if ( arc < d_angle) + direction = -1; + else if (arc > d_angle) + direction = 1; + else + direction = 0; + } +} + + + +//------------------------------------------------------------ +//.F CompactKnob::rangeChange +// Notify a change of the range +// +//.u Description +// Called by QwtSliderBase +//------------------------------------------------------------ + +void CompactKnob::rangeChange() +{ + if (!hasUserScale()) + d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); + recalcAngle(); + SliderBase::rangeChange(); +// resize(size()); + +// repaint(); + update(); +} + +//--------------------------------------------------------- +// resizeEvent +//--------------------------------------------------------- + +void CompactKnob::resizeEvent(QResizeEvent* ev) + { + SliderBase::resizeEvent(ev); + + const QRect& r = rect(); + + if(_editor && _editor->isVisible()) + _editor->setGeometry(r); + + const int spacing = 0; + int x, y, sz = 1; + + QFontMetrics fm = fontMetrics(); + + int label_h, label_h_2, fin_label_h; + label_h = (fm.height() - fm.leading() - fm.descent()) + 1; + label_h_2 = 2 * label_h - 1; + + if(_showValue && _showLabel) + fin_label_h = label_h_2; + else + fin_label_h = fm.height() + 5; + + switch(d_labelPos) { + case Left: + sz = r.height(); + x = r.x() + r.width() - sz; + y = r.y(); + _knobRect.setRect(x, y, sz, sz); + _labelRect.setRect(r.x(), r.y(), r.width() - sz - spacing, sz); + break; + case Right: + sz = r.height(); + x = r.x(); + y = r.y(); + _knobRect.setRect(x, y, label_h_2 + 2 * d_xMargin, label_h_2 + 2 * d_yMargin); + _labelRect.setRect(r.x() + label_h_2 + 2 * d_xMargin + spacing, + r.y(), + r.width() - label_h_2 - 2 * d_xMargin - spacing, + label_h_2 + 2 * d_yMargin); + break; + case Top: // TODO + sz = r.width(); + x = r.x(); + y = r.y() + r.height() - sz; + _knobRect.setRect(x, y, sz, sz); + _labelRect.setRect(r.x(), r.y(), sz, r.height() - sz - spacing); + break; + case Bottom: + sz = r.height(); + x = r.x(); + y = r.y(); + _knobRect.setRect(r.width() / 2 - label_h_2 / 2, + y, + label_h_2 + 2 * d_xMargin, + label_h_2 + 2 * d_yMargin); + _labelRect.setRect(x, + y + label_h_2 + 2 * d_yMargin + spacing, + r.width(), + fin_label_h + 2 * spacing); + break; + case None: + sz = MusECore::qwtMin(r.height(), r.width()); + x = r.x(); + y = r.y(); + _knobRect.setRect(x, y, sz, sz); + break; + } + + x = _knobRect.x() - d_scaleDist; + y = _knobRect.y() - d_scaleDist; + int w = sz + 2 * d_scaleDist; + + d_scale.setGeometry(x, y, w, ScaleDraw::Round); + } + +void CompactKnob::showEditor() +{ + if(_editMode) + return; + + if(!_editor) + { + DEBUG_KNOB(stderr, " creating editor\n"); + _editor = new PopupDoubleSpinBox(this); + _editor->setFrame(false); + _editor->setContentsMargins(0, 0, 0, 0); + _editor->setFocusPolicy(Qt::WheelFocus); + connect(_editor, SIGNAL(returnPressed()), SLOT(editorReturnPressed())); + connect(_editor, SIGNAL(escapePressed()), SLOT(editorEscapePressed())); + } + int w = width(); + //int w = _labelRect.width(); + //if (w < _editor->sizeHint().width()) + // w = _editor->sizeHint().width(); + //_editor->setGeometry(0, 0, w, height()); + _editor->setGeometry(0, _labelRect.y(), w, _labelRect.height()); + DEBUG_KNOB(stderr, " x:%d y:%d w:%d h:%d\n", _editor->x(), _editor->y(), w, _editor->height()); + _editor->setDecimals(_valueDecimals); + _editor->setSingleStep(step()); + _editor->setPrefix(valPrefix()); + _editor->setSuffix(valSuffix()); + _editor->setMinimum(minValue()); + _editor->setMaximum(maxValue()); + _editor->setValue(value()); + _editor->selectAll(); + _editMode = true; + _editor->show(); + _editor->setFocus(); +} + +//------------------------------------------------------------ +// paintEvent +//------------------------------------------------------------ + +void CompactKnob::paintEvent(QPaintEvent*) + { + QPainter p(this); + + drawBackground(&p); + + p.setRenderHint(QPainter::Antialiasing, true); + if(hasScale) + d_scale.draw(&p, palette()); + drawKnob(&p, _knobRect); + + if(d_labelPos != None) + drawLabel(&p); + + d_newVal = 0; + } + +//------------------------------------------------------------ +// CompactKnob::drawBackground +//------------------------------------------------------------ + +void CompactKnob::drawBackground(QPainter* painter) +{ + switch(d_labelPos) + { + case None: + case Left: + case Right: + // Paint a background for the whole control. + _bkgPainter->drawBackground(painter, + rect(), + palette(), + d_xMargin, + d_yMargin, + hasOffMode() && ! isOff() ? _labelRect : QRect()); + break; + + case Top: + case Bottom: + { + // Paint a separate background for the knob. + // No, let's not paint a background for the knob. But if you want it, here it is: +/* + //QRect kr(rect().x(), _knobRect.y(), rect().width(), _knobRect.height()); + QRect kr(_knobRect.x() - 2, _knobRect.y(), _knobRect.width() + 4, _knobRect.height()); + _bkgPainter->drawBackground(painter, + kr, + palette(), + d_xMargin, + d_yMargin); +*/ + + // Paint a separate background for the label. + _bkgPainter->drawBackground(painter, + _labelRect, + palette(), + d_xMargin, + d_yMargin, + hasOffMode() && ! isOff() ? _labelRect : QRect()); + } + break; + } +} + +//------------------------------------------------------------ +// CompactKnob::drawKnob +// const QRect &r -- borders of the knob +//------------------------------------------------------------ + +void CompactKnob::drawKnob(QPainter* p, const QRect& r) + { + const QPalette& pal = palette(); + + QRect aRect; + aRect.setRect(r.x() + d_borderWidth, + r.y() + d_borderWidth, + r.width() - 2*d_borderWidth, + r.height() - 2*d_borderWidth); + + int width = r.width() - 2 * d_xMargin; + int height = r.height() - 2 * d_yMargin; + int size = qMin(width, height); + + p->setRenderHint(QPainter::Antialiasing, true); + + // + // draw the rim + // + + QLinearGradient linearg(QPoint(r.x() + d_xMargin,r.y() + d_yMargin), QPoint(size, size)); + linearg.setColorAt(1 - M_PI_4, d_faceColor.lighter(125)); + linearg.setColorAt(M_PI_4, d_faceColor.darker(175)); + p->setBrush(linearg); + p->setPen(Qt::NoPen); + p->drawEllipse(r.x() + d_xMargin,r.y() + d_yMargin,size,size); + + + // + // draw shiny surrounding + // + + QPen pn; + pn.setCapStyle(Qt::FlatCap); + + pn.setColor(d_shinyColor.lighter(l_const + fabs(value() * l_slope))); + pn.setWidth(d_shineWidth * 2); + p->setPen(pn); + p->drawArc(aRect, 0, 360 * 16); + + // + // draw button face + // + + QRadialGradient gradient(//aRect.x() + size/2, + //aRect.y() + size/2, + aRect.x(), + aRect.y(), + size-d_borderWidth, + aRect.x() + size/2-d_borderWidth, + aRect.y() + size/2-d_borderWidth); + gradient.setColorAt(0, d_curFaceColor.lighter(150)); + gradient.setColorAt(1, d_curFaceColor.darker(150)); + p->setBrush(gradient); + p->setPen(Qt::NoPen); + p->drawEllipse(aRect); + + // + // draw marker + // + drawMarker(p, d_angle, pal.currentColorGroup() == QPalette::Disabled ? + pal.color(QPalette::Disabled, QPalette::WindowText) : d_markerColor); + } + +//------------------------------------------------------------ +//.- +//.F CompactKnob::drawMarker +// Draw the marker at the knob's front +// +//.u Parameters +//.p QPainter *p -- painter +// double arc -- angle of the marker +// const QColor &c -- marker color +// +//.u Syntax +// void CompactKnob::drawMarker(QPainter *p) +// +//------------------------------------------------------------ +void CompactKnob::drawMarker(QPainter *p, double arc, const QColor &c) +{ + QPen pn; + int radius; + double rb,re; + double rarc; + + rarc = arc * M_PI / 180.0; + double ca = cos(rarc); + double sa = - sin(rarc); + + radius = _knobRect.width() / 2 - d_borderWidth + d_shineWidth; + if (radius < 3) radius = 3; + int ym = _knobRect.y() + radius + d_borderWidth - d_shineWidth; + int xm = _knobRect.x() + radius + d_borderWidth - d_shineWidth; + + switch (d_symbol) + { + case Dot: + p->setBrush(c); + p->setPen(Qt::NoPen); + rb = double(MusECore::qwtMax(radius - 4 - d_dotWidth / 2, 0)); + p->drawEllipse(xm - int(rint(sa * rb)) - d_dotWidth / 2, + ym - int(rint(ca * rb)) - d_dotWidth / 2, + d_dotWidth, d_dotWidth); + break; + + case Line: + pn.setColor(c); + pn.setWidth(2); + p->setPen(pn); + +// rb = MusECore::qwtMax(double((radius - 1) / 3.0), 0.0); +// re = MusECore::qwtMax(double(radius - 1), 0.0); + rb = MusECore::qwtMax(((double(radius) - 0.5) / 3.0), 0.0); + re = MusECore::qwtMax(double(radius) - 0.5, 0.0); + + p->setRenderHint(QPainter::Antialiasing, true); + p->drawLine( xm, + ym, + xm - int(rint(sa * re)), + ym - int(rint(ca * re))); + break; + } +} + +//------------------------------------------------------------ +// CompactKnob::drawLabel +// const QRect &r -- borders of the knob +//------------------------------------------------------------ + +void CompactKnob::drawLabel(QPainter* painter) +{ + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + + const bool has_focus = hasFocus(); + + if (has_focus) + { + if (_hovered) + painter->setPen(QPen(QColor(239,239,239))); + else + painter->setPen(QPen(Qt::white)); + } + else if (_hovered) + painter->setPen(QPen(QColor(48,48,48))); + else + painter->setPen(QPen(Qt::black)); + + + int label_flags = 0; + int value_flags = 0; + switch(d_labelPos) + { + case Left: + label_flags = Qt::AlignRight | ((_showValue && _showLabel) ? Qt::AlignTop : Qt::AlignVCenter); + value_flags = Qt::AlignRight | ((_showValue && _showLabel) ? Qt::AlignBottom : Qt::AlignVCenter); + break; + + case Right: + label_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignTop : Qt::AlignVCenter); + value_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignBottom : Qt::AlignVCenter); + break; + + case Top: + label_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignTop : Qt::AlignVCenter); + value_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignBottom : Qt::AlignVCenter); + break; + + case Bottom: + label_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignTop : Qt::AlignVCenter); + value_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignBottom : Qt::AlignVCenter); + break; + + case None: + return; + break; + } + + const QFontMetrics fm = fontMetrics(); + const int leading = fm.leading(); + const int descent = fm.descent(); + + const QRect label_br = fm.boundingRect(d_labelText); + const int label_bw = label_br.width(); + + if(_showLabel) + { + QRect label_r = _labelRect.adjusted(3, -descent + d_yMargin + 1, -2, 0); + + int label_xoff = (label_r.width() - label_bw) / 2; + if(label_xoff < 0) + label_xoff = 0; + + label_r.adjust(label_xoff, 0, 0, 0); + + painter->drawText(label_r, label_flags, d_labelText); + } + + if(_showValue) + { + const double minV = minValue(ConvertNone); + const double val = value(ConvertNone); + const QString val_txt = (val <= minV && !d_specialValueText.isEmpty()) ? + d_specialValueText : (d_valPrefix + locale().toString(val, 'f', _valueDecimals) + d_valSuffix); + const QRect val_br = fm.boundingRect(val_txt); + const int val_bw = val_br.width(); + QRect val_r = _labelRect.adjusted(3, -1, -2, descent + leading - d_yMargin - 2); + int val_xoff = (val_r.width() - val_bw) / 2; + if(val_xoff < 0) + val_xoff = 0; + val_r.adjust(val_xoff, 0, 0, 0); + painter->drawText(val_r, value_flags, val_txt); + } + + painter->restore(); +} + +void CompactKnob::mouseDoubleClickEvent(QMouseEvent* e) +{ + DEBUG_KNOB(stderr, "CompactKnob::mouseDoubleClickEvent\n"); + const Qt::MouseButtons buttons = e->buttons(); + const Qt::KeyboardModifiers keys = e->modifiers(); + + if(buttons == Qt::LeftButton && !_editMode) + { + DEBUG_KNOB(stderr, " left button\n"); + if(keys == Qt::ControlModifier) + { + if(_hasOffMode) + { + setOff(!isOff()); // Just toggle the off state. + emit valueChanged(value(), id()); + e->accept(); + return; + } + } + // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. + else if(keys == Qt::NoModifier && (!_editor || !_editor->hasFocus())) + { + showEditor(); + e->accept(); + return; + } + } + + e->ignore(); + SliderBase::mouseDoubleClickEvent(e); +} + +void CompactKnob::editorReturnPressed() +{ + DEBUG_KNOB(stderr, "CompactKnob::editorReturnPressed\n"); + _editMode = false; + if(_editor) + { + if(value() != _editor->value()) + setValue(_editor->value()); + _editor->deleteLater(); + _editor = 0; + setFocus(); + } +} + +void CompactKnob::editorEscapePressed() +{ + DEBUG_KNOB(stderr, "CompactKnob::editorEscapePressed\n"); + _editMode = false; + if(_editor) + { + _editor->deleteLater(); + _editor = 0; + setFocus(); + } +} + +void CompactKnob::keyPressEvent(QKeyEvent* e) +{ + if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) + { + // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. + if(!_editor || !_editor->hasFocus()) + showEditor(); + e->accept(); + return; + } + + e->ignore(); + SliderBase::keyPressEvent(e); +} + +void CompactKnob::leaveEvent(QEvent *e) +{ + if(_hovered) + { + _hovered = false; + update(); + } + if(_knobHovered) + { + _knobHovered = false; + update(_knobRect); + } + if(_labelHovered) + { + _labelHovered = false; + update(_labelRect); + } + e->ignore(); + SliderBase::leaveEvent(e); +} + +void CompactKnob::mouseMoveEvent(QMouseEvent *e) +{ + e->ignore(); + SliderBase::mouseMoveEvent(e); + if(!_hovered) + { + _hovered = true; + update(); + } + if(_knobRect.contains(e->pos()) != _knobHovered) + { + _knobHovered = !_knobHovered; + update(_knobRect); + } + if(_labelRect.contains(e->pos()) != _labelHovered) + { + _labelHovered = !_labelHovered; + update(_labelRect); + } +} + +void CompactKnob::mousePressEvent(QMouseEvent* e) +{ + const Qt::MouseButton button = e->button(); + const Qt::MouseButtons buttons = e->buttons(); + + // Only one mouse button at a time! Otherwise bad things happen. + if(buttons ^ button) + { + e->ignore(); + // Let ancestor handle the proper thing to do. + SliderBase::mousePressEvent(e); + return; + } + + if(button == Qt::RightButton) + { + e->accept(); + // Clear everything. + setMouseGrab(false); + d_scrollMode = ScrNone; + d_direction = 0; + _pressed = false; + emit sliderRightClicked(e->globalPos(), _id); + return; + } + + e->ignore(); + SliderBase::mousePressEvent(e); +} + +// bool CompactKnob::event(QEvent* e) +// { +// switch(e->type()) +// { +// // FIXME: Doesn't work. +// case QEvent::NonClientAreaMouseButtonPress: +// DEBUG_KNOB(stderr, "CompactKnob::event NonClientAreaMouseButtonPress\n"); +// e->accept(); +// _editMode = false; +// if(_editor) +// { +// _editor->deleteLater(); +// _editor = 0; +// } +// return true; +// break; +// +// default: +// break; +// } +// +// //e->ignore(); // No, this causes duplicates! For ex. skipping tab focus. +// return SliderBase::event(e); +// } + +//------------------------------------------------------------ +// +//.F CompactKnob::setKnobWidth +// Change the knob's width. +// +//.u Syntax +//.f void CompactKnob::setKnobWidth(int w) +// +//.u Parameters +//.p int w -- new width +// +//.u Description +// The specified width must be >= 5, or it will be clipped. +// +//------------------------------------------------------------ +void CompactKnob::setKnobWidth(int w) +{ + d_knobWidth = MusECore::qwtMax(w,5); + resize(size()); +// repaint(); + update(); +} + +//------------------------------------------------------------ +// +//.F CompactKnob::setBorderWidth +// Set the knob's border width +// +//.u Syntax +//.f void CompactKnob::setBorderWidth(int bw) +// +//.u Parameters +//.p int bw -- new border width +// +//------------------------------------------------------------ +void CompactKnob::setBorderWidth(int bw) +{ + d_borderWidth = MusECore::qwtMax(bw, 0); + resize(size()); +// repaint(); + update(); +} + +//------------------------------------------------------------ +//.- +//.F CompactKnob::recalcAngle +// Recalculate the marker angle corresponding to the +// current value +// +//.u Syntax +//.f void CompactKnob::recalcAngle() +// +//------------------------------------------------------------ +void CompactKnob::recalcAngle() +{ + d_oldAngle = d_angle; + + // + // calculate the angle corresponding to the value + // + if (maxValue() == minValue()) + { + d_angle = 0; + d_nTurns = 0; + } + else + { + d_angle = (value() - 0.5 * (minValue() + maxValue())) + / (maxValue() - minValue()) * d_totalAngle; + d_nTurns = floor((d_angle + 180.0) / 360.0); + d_angle = d_angle - d_nTurns * 360.0; + } +} + +//------------------------------------------------------------ +// setFaceColor +//------------------------------------------------------------ +void CompactKnob::setFaceColor(const QColor& c) +{ + d_faceColor = c; + +// if(!d_faceColor.isValid()) +// d_faceColor = palette().color(QPalette::Window); +// d_curFaceColor = d_faceColor; + if(!_faceColSel) + update(); +} + +//------------------------------------------------------------ +// setAltFaceColor +//------------------------------------------------------------ +void CompactKnob::setAltFaceColor(const QColor& c) +{ + d_altFaceColor = c; + if(_faceColSel) + update(); +} + +//------------------------------------------------------------ +// selectFaceColor +//------------------------------------------------------------ +void CompactKnob::selectFaceColor(bool alt) +{ + _faceColSel = alt; + if(alt) + d_curFaceColor = d_altFaceColor; + else + d_curFaceColor = d_faceColor; + update(); +} + +//------------------------------------------------------------ +// setShinyColor +//------------------------------------------------------------ +void CompactKnob::setShinyColor(const QColor& c) +{ + d_shinyColor = c; + update(); +} + +//------------------------------------------------------------ +// setMarkerColor +//------------------------------------------------------------ +void CompactKnob::setMarkerColor(const QColor& c) +{ + d_markerColor = c; + update(); +} + +//------------------------------------------------------------ +// +//.F CompactKnob::setMargins +// Set distances between the widget's border and +// internals. +// +//.u Syntax +//.f void CompactKnob::setMargins(int hor, int vert) +// +//.u Parameters +//.p int hor, int vert -- Margins +// +//------------------------------------------------------------ +void CompactKnob::setMargins(int hor, int vert) +{ + d_xMargin = MusECore::qwtMax(0, hor); + d_yMargin = MusECore::qwtMax(0, vert); + resize(this->size()); +} + +//------------------------------------------------------------ +// +//.F CompactKnob::sizeHint +// Return a recommended size +// +//.u Syntax +//.f QSize CompactKnob::sizeHint() const +// +//.u Note +// The return value of sizeHint() depends on the font and the +// scale. +//------------------------------------------------------------ + +QSize CompactKnob::sizeHint() const + { + QSize sz = getMinimumSizeHint(fontMetrics(), + d_labelPos, + _showValue, + _showLabel, + d_xMargin, + d_yMargin); + DEBUG_KNOB(stderr, "CompactKnob::sizeHint w:%d h:%d\n", sz.width(), sz.height()); + return sz; + } + + +} // namespace MusEGui + diff -Nru muse-2.1.2/muse/widgets/compact_knob.h muse-3.0.2+ds1/muse/widgets/compact_knob.h --- muse-2.1.2/muse/widgets/compact_knob.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/compact_knob.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,239 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// compact_knob.h +// Copyright (C) 1999-2011 by Werner Schweer and others +// (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) completely redesigned. +// (C) Copyright 2016 - 2017 Tim E. Real (terminator356 on sourceforge) +// +// 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 __COMPACT_KNOB_H__ +#define __COMPACT_KNOB_H__ + +#include "sliderbase.h" +#include "sclif.h" + +class QPoint; +class QRect; +class QSize; +class QColor; +class QFont; +class QFontMetrics; +class QResizeEvent; +class QPainter; +class QEvent; +class QPaintEvent; +class QMouseEvent; +class QKeyEvent; + +namespace MusEGui { + +class PopupDoubleSpinBox; +class ItemBackgroundPainter; + +//--------------------------------------------------------- +// CompactKnob +//--------------------------------------------------------- + +class CompactKnob : public SliderBase, public ScaleIf + { + Q_OBJECT + + public: + enum KnobLabelPos { None, Left, Right, Top, Bottom }; + enum Symbol { Line, Dot }; + + private: + KnobLabelPos d_labelPos; + + bool _hasOffMode; + + QString d_labelText; + QString d_valPrefix; + QString d_valSuffix; + QString d_specialValueText; + QString d_offText; + int _valueDecimals; + bool _off; + // Whether to display the label. + bool _showLabel; + // Whether to display the value. + bool _showValue; + + PopupDoubleSpinBox* _editor; + bool _editMode; + + ItemBackgroundPainter* _bkgPainter; + + private slots: + void editorReturnPressed(); + void editorEscapePressed(); + + protected: + bool hasScale; + // Whether the mouse is over the entire control. + bool _hovered; + // Whether the mouse is over the knob. + bool _knobHovered; + // Whether the mouse is over the label. + bool _labelHovered; + + int d_xMargin; + int d_yMargin; + int d_borderWidth; + int d_shineWidth; + int d_scaleDist; + int d_maxScaleTicks; + int d_newVal; + int d_knobWidth; + int d_dotWidth; + + Symbol d_symbol; + double d_angle; + double d_oldAngle; + double d_totalAngle; + double d_nTurns; + + double l_const; + double l_slope; + + QRect _labelRect; + QRect _knobRect; + bool _faceColSel; + QColor d_faceColor; + QColor d_altFaceColor; + QColor d_shinyColor; + QColor d_curFaceColor; + QColor d_markerColor; + + void recalcAngle(); + void valueChange(); + void rangeChange(); + void drawBackground(QPainter*); + void drawKnob(QPainter* p, const QRect &r); + void drawMarker(QPainter* p, double arc, const QColor &c); + void drawLabel(QPainter* p); + void showEditor(); + + virtual void resizeEvent(QResizeEvent*); + virtual void paintEvent(QPaintEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseDoubleClickEvent(QMouseEvent*); + virtual void keyPressEvent(QKeyEvent*); + virtual void leaveEvent(QEvent*); +// virtual bool event(QEvent*); + + double getValue(const QPoint &p); + // Determine the value corresponding to a specified mouse movement. + double moveValue(const QPoint& /*deltaP*/, bool /*fineMode*/ = false); + void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ); + void scaleChange() { repaint(); } + void fontChange(const QFont &) { repaint(); } + + virtual void processSliderPressed(int); + virtual void processSliderReleased(int); + // Show a handy tooltip value box. + virtual void showValueToolTip(QPoint); + + signals: + // Both value and off state changed combined into one signal. + // In typical automation use, this signal should be ignored in ScrDirect scroll mode. + // ScrDirect mode happens only once upon press with a modifier. + // In ScrDirect mode the slider sends both pressed AND changed signals + // since the position jumps to the pressed location. + // Note the SliderBase::valueChanged signal is also available. + void valueStateChanged(double value, bool off, int id, int scrollMode); + + public: + CompactKnob(QWidget* parent = 0, const char *name = 0, + KnobLabelPos labelPos = None, + const QString& labelText = QString(), + const QString& valPrefix = QString(), + const QString& valSuffix = QString(), + const QString& specialValueText = QString(), + const QColor& faceColor = QColor()); + ~CompactKnob(); + + static QSize getMinimumSizeHint(const QFontMetrics& fm, + //Qt::Orientation orient = Qt::Vertical, + KnobLabelPos labelPos = None, + bool showValue = true, + bool showLabel = true, + int xMargin = 0, + int yMargin = 0); + + void setRange(double vmin, double vmax, double vstep = 0.0, + int pagesize = 1, DoubleRange::ConversionMode mode = ConvertDefault); + + QString labelText() const { return d_labelText; }; + void setLabelText(const QString& t) { d_labelText = t; update(); } + QString valPrefix() const { return d_valPrefix; }; + void setValPrefix(const QString& t) { d_valPrefix = t; update(); } + QString valSuffix() const { return d_valSuffix; }; + void setValSuffix(const QString& t) { d_valSuffix = t; update(); } + QString specialValueText() const { return d_specialValueText; }; + void setSpecialValueText(const QString& t) { d_specialValueText = t; update(); } + QString offText() const { return d_offText; }; + void setOffText(const QString& t) { d_offText = t; update(); } + int valueDecimals() const { return _valueDecimals; } + void setValueDecimals(int d) { if(d < 0) return; _valueDecimals = d; update(); } + + void setKnobWidth(int w); + void setTotalAngle (double angle); + void setBorderWidth(int bw); + void selectFaceColor(bool alt); + bool selectedFaceColor() { return _faceColSel; } + QColor faceColor() { return d_faceColor; } + void setFaceColor(const QColor& c); + QColor altFaceColor() { return d_altFaceColor; } + void setAltFaceColor(const QColor& c); + QColor shinyColor() { return d_shinyColor; } + void setShinyColor(const QColor& c); + QColor markerColor() { return d_markerColor; } + void setMarkerColor(const QColor& c); + + QString toolTipValueText(bool inclLabel, bool inclVal) const; + + bool showLabel() const { return _showLabel; } + void setShowLabel(bool show); + + bool showValue() const { return _showValue; } + void setShowValue(bool show); + + bool hasOffMode() const { return _hasOffMode; } + void setHasOffMode(bool v); + bool isOff() const { return _off; } + // Sets the off state and emits valueStateChanged signal if required. + void setOff(bool v); + // Both value and off state changed combined into one setter. + // By default it is assumed that setting a value naturally implies resetting the 'off' state to false. + // Emits valueChanged and valueStateChanged signals if required. + // Note setOff and SliderBase::setValue are also available. + void setValueState(double v, bool off = false, ConversionMode mode = ConvertDefault); + + void setMargins(int x, int y); + + virtual QSize sizeHint() const; + }; + + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/compact_patch_edit.cpp muse-3.0.2+ds1/muse/widgets/compact_patch_edit.cpp --- muse-2.1.2/muse/widgets/compact_patch_edit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/compact_patch_edit.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,254 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// compact_patch_edit.cpp +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 +#include + +#include "compact_patch_edit.h" +#include "lcd_widgets.h" +#include "elided_label.h" + +namespace MusEGui { + +CompactPatchEdit::CompactPatchEdit(QWidget *parent, + const char *name, + QColor /*fillColor*/) + : QFrame(parent) + +{ + setObjectName(name); + + _orient = ReadoutHorizontal; + _showPatchLabel = true; + + _maxAliasedPointSize = -1; + _id = -1; + _currentPatch = 0; + + _patchNameLabel = new ElidedLabel(0, Qt::ElideNone); + _patchNameLabel->setObjectName("CompactPatchEditLabel"); + _patchNameLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); + _patchNameLabel->setHasOffMode(true); + + _patchEdit = new LCDPatchEdit(); + + _patchNameLabel->setToolTip(tr("Patch name")); + + _patchNameLabel->setContentsMargins(0, 0, 0, 0); + + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + + layout->addWidget(_patchNameLabel); + layout->addWidget(_patchEdit); + + connect(_patchNameLabel, SIGNAL(pressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)), + SLOT(patchNamePressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers))); + connect(_patchNameLabel, SIGNAL(returnPressed(QPoint,int,Qt::KeyboardModifiers)), + SLOT(patchNameReturnPressed(QPoint,int,Qt::KeyboardModifiers))); + + connect(_patchEdit, SIGNAL(valueChanged(int,int)), + SLOT(patchEditValueChanged(int,int))); + + connect(_patchEdit, SIGNAL(rightClicked(QPoint,int)), + SLOT(patchEditRightClicked(QPoint,int))); +} + +CompactPatchEdit::~CompactPatchEdit() +{ + if(_patchEdit) + delete _patchEdit; +} + +// Static. +QSize CompactPatchEdit::getMinimumSizeHint(const QFontMetrics& fm, + Qt::Orientation orient, + int xMargin, + int yMargin) +{ + const QSize ctrl_sz = LCDPatchEdit::getMinimumSizeHint( + fm, + xMargin, + yMargin, + orient == Qt::Horizontal ? LCDPatchEdit::PatchHorizontal : LCDPatchEdit::PatchVertical + ); + + // HACK Try to find the size of a label +// QLabel* dummyLabel = new QLabel("WWW", this); +// dummyLabel->setMargin(yMargin); +// const QSize lbl_sz = dummyLabel->sizeHint(); + const int lbl_h = fm.height() + 2 * yMargin; + + const int h = ctrl_sz.height() + lbl_h; + + switch(orient) { + case Qt::Horizontal: + return QSize(ctrl_sz.width(), h); // Patch edit is dominant. + break; + case Qt::Vertical: + return QSize(16, h); + break; + } + return QSize(10, 10); +} + +QWidget* CompactPatchEdit::setupComponentTabbing(QWidget* previousWidget) +{ + QWidget* prev = previousWidget; + if(_patchNameLabel) + { + if(prev) + QWidget::setTabOrder(prev, _patchNameLabel); + prev = _patchNameLabel; + } + if(_patchEdit) + { + if(prev) + QWidget::setTabOrder(prev, _patchEdit); + prev = _patchEdit; + } + return prev; +} + +// void CompactPatchEdit::keyPressEvent(QKeyEvent* e) +// { +// switch (e->key()) +// { +// case Qt::Key_Escape: +// break; +// +// default: +// break; +// } +// +// e->ignore(); +// return QFrame::keyPressEvent(e); +// } + +void CompactPatchEdit::setReadoutColor(const QColor& c) +{ + _patchEdit->setReadoutColor(c); + //update(); +} + +void CompactPatchEdit::setReadoutOrientation(ReadoutOrientation orient) +{ + _orient = orient; +} + +void CompactPatchEdit::setShowPatchLabel(bool v) +{ + _showPatchLabel = v; +} + +void CompactPatchEdit::setMaxAliasedPointSize(int sz) +{ + _maxAliasedPointSize = sz; + _patchEdit->setMaxAliasedPointSize(sz); +} + +// QSize CompactPatchEdit::sizeHint() const +// { +// return getMinimumSizeHint(fontMetrics(), +// _orient == ReadoutHorizontal ? Qt::Horizontal : Qt::Vertical, +// frameWidth(), //_xMargin, +// frameWidth() //_yMargin +// ); +// } + +int CompactPatchEdit::value() const +{ + return _currentPatch; +} + +void CompactPatchEdit::setValue(int v) +{ + if(_currentPatch != v) + { + _currentPatch = v; + //update(); + } + _patchEdit->setValue(v); +} + +void CompactPatchEdit::setLastValidValue(int v) +{ + _patchEdit->setLastValidPatch(v); +} + +void CompactPatchEdit::setLastValidBytes(int hbank, int lbank, int prog) +{ + _patchEdit->setLastValidBytes(hbank, lbank, prog); +} + +QString CompactPatchEdit::patchName() const +{ + return _patchNameLabel->text(); +} + +void CompactPatchEdit::setPatchName(const QString& patchName) +{ + _patchNameLabel->setText(patchName); +} + +void CompactPatchEdit::setPatchNameOff(bool v) +{ + _patchNameLabel->setOff(v); +} + +void CompactPatchEdit::patchEditValueChanged(int val, int /*id*/) +{ + _currentPatch = val; + emit valueChanged(_currentPatch, _id); +} + +void CompactPatchEdit::patchEditDoubleClicked(QPoint /*p*/, int /*id*/, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys) +{ + if(buttons == Qt::LeftButton && keys == (Qt::ControlModifier | Qt::ShiftModifier)) + { + + } +} + +void CompactPatchEdit::patchEditRightClicked(QPoint p, int /*id*/) +{ + emit patchValueRightClicked(p, _id); +} + +void CompactPatchEdit::patchNamePressed(QPoint p, int /*id*/, Qt::MouseButtons buttons, Qt::KeyboardModifiers /*keys*/) +{ + if(buttons == Qt::LeftButton) + emit patchNameClicked(p, _id); + else if(buttons == Qt::RightButton) + emit patchNameRightClicked(mapToGlobal(p), _id); +} + +void CompactPatchEdit::patchNameReturnPressed(QPoint p, int /*id*/, Qt::KeyboardModifiers /*keys*/) +{ + emit patchNameClicked(p, _id); +} + + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/compact_patch_edit.h muse-3.0.2+ds1/muse/widgets/compact_patch_edit.h --- muse-2.1.2/muse/widgets/compact_patch_edit.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/compact_patch_edit.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,121 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// compact_patch_edit.h +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __COMPACT_PATCH_EDIT_H__ +#define __COMPACT_PATCH_EDIT_H__ + +#include + +class QMouseEvent; + +namespace MusEGui { + +class ElidedLabel; +class LCDPatchEdit; + +//--------------------------------------------------------- +// CompactPatchEdit +//--------------------------------------------------------- + +class CompactPatchEdit : public QFrame +{ + Q_OBJECT + + public: + enum ReadoutOrientation { ReadoutHorizontal = 0, ReadoutVertical }; + + private: + ReadoutOrientation _orient; + bool _showPatchLabel; + int _maxAliasedPointSize; + int _id; + int _currentPatch; + LCDPatchEdit* _patchEdit; + ElidedLabel* _patchNameLabel; + + private slots: + void patchEditValueChanged(int val, int id); + void patchEditDoubleClicked(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); + void patchEditRightClicked(QPoint p, int id); + void patchNamePressed(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); + void patchNameReturnPressed(QPoint p, int id, Qt::KeyboardModifiers keys); + +// protected: +// virtual void keyPressEvent(QKeyEvent*); + + signals: + void patchValueRightClicked(QPoint p, int id); + void patchNameClicked(QPoint p, int id); + void patchNameRightClicked(QPoint p, int id); + void valueChanged(int value, int id); + + public: + CompactPatchEdit(QWidget *parent, const char *name = 0, + QColor fillColor = QColor()); + + virtual ~CompactPatchEdit(); + + static QSize getMinimumSizeHint(const QFontMetrics& fm, + Qt::Orientation orient = Qt::Vertical, + int xMargin = 0, + int yMargin = 0); + + int id() const { return _id; } + void setId(int i) { _id = i; } + + int value() const; + void setValue(int v); + void setLastValidValue(int v); + void setLastValidBytes(int hbank, int lbank, int prog); + + // Sets up tabbing for the entire patch edit. + // Accepts a previousWidget which can be null and returns the last widget in the control, + // which allows chaining other widgets. + virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); + + void setReadoutOrientation(ReadoutOrientation); + void setShowPatchLabel(bool); + + void setReadoutColor(const QColor&); + + // At what point size to switch from aliased text to non-aliased text. Zero means always use anti-aliasing. + // Here in CompactPatchEdit, this only affects the CompactSliders so far, not the patch label. + // If -1, no value has been set and default is each widget's setting. + int maxAliasedPointSize() const { return _maxAliasedPointSize; } + // Sets at what point size to switch from aliased text (brighter, crisper but may look too jagged and unreadable with some fonts) + // to non-aliased text (dimmer, fuzzier but looks better). Zero means always use anti-aliasing. Default is each widget's setting. + // Here in CompactPatchEdit, this only affects the CompactSliders so far, not the patch label. + void setMaxAliasedPointSize(int sz); + + QString patchName() const; + void setPatchName(const QString& patchName); + // Sets the off state. + void setPatchNameOff(bool v); + + //virtual QSize sizeHint() const; +}; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/compact_slider.cpp muse-3.0.2+ds1/muse/widgets/compact_slider.cpp --- muse-2.1.2/muse/widgets/compact_slider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/compact_slider.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,1540 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// compact_slider.cpp +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 +#include "mmath.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "popup_double_spinbox.h" +#include "compact_slider.h" +#include "slider.h" +//#include "icons.h" +//#include "lcd_widgets.h" + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_COMPACT_SLIDER(dev, format, args...) // fprintf(dev, format, ##args); + + +namespace MusEGui { + +//------------------------------------------------------------- +// Slider - The Slider Widget +// +// Slider is a slider widget which operates on an interval +// of type double. Slider supports different layouts as +// well as a scale. +//------------------------------------------------------------ + +//------------------------------------------------------------ +//.F CompactSlider::Slider +// +// Constructor +// +//.u Syntax: +//.f CompactSlider::Slider(QWidget *parent, const char *name, Orientation orient = Horizontal, ScalePos scalePos = None, int bgStyle = BgTrough) +// +//.u Parameters +//.p +// QWidget *parent -- parent widget +// const char *name -- The Widget's name. Default = 0. +// Orientation Orient -- Orientation of the slider. Can be CompactSlider::Horizontal +// or CompactSlider::Vertical. +// Defaults to Horizontal. +// ScalePos scalePos -- Position of the scale. Can be CompactSlider::None, +// CompactSlider::Left, CompactSlider::Right, CompactSlider::Top, +// or CompactSlider::Bottom. Defaults to CompactSlider::None. !!! CURRENTLY only CompactSlider::None supported - oget 20110913 +// QColor fillcolor -- the color used to fill in the full side +// of the Slider +//------------------------------------------------------------ + +CompactSlider::CompactSlider(QWidget *parent, const char *name, + Qt::Orientation orient, ScalePos scalePos, + const QString& labelText, + const QString& valPrefix, + const QString& valSuffix, + const QString& specialValueText, + const QColor& borderColor, + const QColor& barColor, + const QColor& slotColor, + const QColor& thumbColor) + : SliderBase(parent,name) + { + if(objectName().isEmpty()) + setObjectName(QStringLiteral("CompactSlider")); + + setMouseTracking(true); + setEnabled(true); + setFocusPolicy(Qt::WheelFocus); + + setAutoFillBackground(false); + setAttribute(Qt::WA_NoSystemBackground); + //setAttribute(Qt::WA_StaticContents); + // This is absolutely required for speed! Otherwise painfully slow because of full background + // filling, even when requesting small udpdates! Background is drawn by us. + setAttribute(Qt::WA_OpaquePaintEvent); + + setBorderlessMouse(false); + setCursorHoming(false); + setPagingButtons(Qt::NoButton); + + setEnableValueToolTips(true); + + //_LCDPainter = new LCDPainter(); + + //_onPath = 0; + //_offPath = 0; + _editor = 0; + _editMode = false; + + d_borderColor = borderColor; + d_barColor = barColor; + d_slotColor = slotColor; + d_thumbColor = thumbColor; + + _maxAliasedPointSize = 8; + + d_labelText = labelText; + d_valPrefix = valPrefix; + d_valSuffix = valSuffix; + d_specialValueText = specialValueText; + + _textHighlightMode = TextHighlightShadow | TextHighlightOn; + + _valueDecimals = 2; + _off = false; + d_offText = tr("off"); + _showValue = true; + + _detectThumb = false; + _autoHideThumb = true; + _hasOffMode = false; + d_thumbLength = 0; + d_thumbHitLength = 0; + d_thumbHalf = d_thumbLength / 2; + d_thumbWidth = 16; + d_thumbWidthMargin = 0; + _mouseOverThumb = false; + _hovered = false; + _activeBorders = AllBorders; + + d_scaleDist = 4; + d_scaleStep = 0.0; + d_scalePos = scalePos; + d_xMargin = 1; + d_yMargin = 1; + d_mMargin = 1; + + _entered = false; + + setOrientation(orient); + + d_valuePixel = 0; + d_valuePixelWidth = 0; + + //updatePixmaps(); + getActiveArea(); + getPixelValues(); + } + +//------------------------------------------------------------ +//.F CompactSlider::~Slider +// Destructor +//.u Syntax +//.f CompactSlider::~Slider() +//------------------------------------------------------------ + +CompactSlider::~CompactSlider() + { + //if(_onPath) + // delete _onPath; + //if(_offPath) + // delete _offPath; + + //if(_LCDPainter) + // delete _LCDPainter; + } + +// Static. +QSize CompactSlider::getMinimumSizeHint(const QFontMetrics& fm, + Qt::Orientation orient, + ScalePos /*scalePos*/, + int /*xMargin*/, + int yMargin) +{ + const int font_height = fm.height(); + switch(orient) { + case Qt::Vertical: + return QSize(16, font_height + 3 + 2 * yMargin); + break; + case Qt::Horizontal: + return QSize(16, font_height + 3 + 2 * yMargin); + break; + } + return QSize(10, 10); +} + +void CompactSlider::processSliderPressed(int) +{ +// bPressed = true; + update(); +} + +void CompactSlider::processSliderReleased(int) +{ + QPoint p = mapFromGlobal(QCursor::pos()); + getMouseOverThumb(p); + + update(); + + DEBUG_COMPACT_SLIDER(stderr, + "CompactSlider::processSliderReleased trackingIsActive:%d val:%.20f valHasChanged:%d\n", + trackingIsActive(), value(), valueHasChangedAtRelease()); + +// Changed. BUG: Was causing problems with sending changed even though it hadn't. +// It should never signal a change on release UNLESS tracking is off, because otherwise the last movement +// already sent the last changed signal. FIXME Even this is still flawed. If no tracking, it would likely +// still signal a change upon simple press and release even though nothing changed. +// if((!tracking()) || valHasChanged()) + if(!trackingIsActive() && valueHasChangedAtRelease()) + emit valueStateChanged(value(), isOff(), id(), d_scrollMode); +} + +QString CompactSlider::toolTipValueText(bool inclLabel, bool inclVal) const +{ + const double minV = minValue(ConvertNone); + const double val = value(ConvertNone); + const QString comp_val_text = isOff() ? d_offText : + ((val <= minV && !d_specialValueText.isEmpty()) ? + d_specialValueText : (d_valPrefix + locale().toString(val, 'f', _valueDecimals) + d_valSuffix)); + QString txt; + if(inclLabel) + txt += d_labelText; + if(inclLabel && inclVal) + txt += QString(": "); + if(inclVal) + { + txt += QString(""); + txt += comp_val_text; + txt += QString(""); + } + return txt; +} + +void CompactSlider::showValueToolTip(QPoint /*p*/) +{ + const QString txt = toolTipValueText(true, true); + if(!txt.isEmpty()) + { + // Seems to be a small problem with ToolTip: Even if we force the font size, + // if a previous tooltip was showing from another control at another font size, + // it refuses to change font size. Also, if we supply the widget to showText(), + // it refuses to change font size and uses the widget's font size instead. + // Also, this craziness with ToolTip's self-offsetting is weird: In class CompactKnob + // it is best when we supply the parent's position, while in class CompactSlider + // it is best when we supply the widget's position - and it STILL isn't right! + // Supplying the widget's position to CompactKnob, or parent's position to CompactSlider + // actually makes the offsetting worse! + if(QToolTip::font().pointSize() != 10) + { + QFont fnt = font(); + fnt.setPointSize(10); + QToolTip::setFont(fnt); + QToolTip::hideText(); + } + //QToolTip::showText(p, txt, this, QRect(), 1000); + //QToolTip::showText(p, txt, 0, QRect(), 1000); + QToolTip::showText(mapToGlobal(pos()), txt, 0, QRect(), 3000); + //QToolTip::showText(mapToGlobal(pos()), txt); + //QToolTip::showText(mapToGlobal(parentWidget() ? parentWidget()->pos() : pos()), txt, parentWidget() ? parentWidget() : this, QRect(), 3000); + } +} + +void CompactSlider::setActiveBorders(ActiveBorders_t borders) +{ + _activeBorders = borders; + resize(size()); + updateGeometry(); + update(); +} + +//---------------------------------------------------- +// +//.F CompactSlider::setThumbLength +// +// Set the slider's thumb length +// +//.u Syntax +// void CompactSlider::setThumbLength(int l) +// +//.u Parameters +//.p int l -- new length +// +//----------------------------------------------------- +void CompactSlider::setThumbLength(int l) +{ + d_thumbLength = MusECore::qwtMax(l,8); + d_thumbHalf = d_thumbLength / 2; + resize(this->size()); +} + +//------------------------------------------------------------ +// +//.F CompactSlider::setThumbWidth +// Change the width of the thumb +// +//.u Syntax +//.p void CompactSlider::setThumbWidth(int w) +// +//.u Parameters +//.p int w -- new width +// +//------------------------------------------------------------ +void CompactSlider::setThumbWidth(int w) +{ + d_thumbWidth = MusECore::qwtMax(w,4); + resize(this->size()); +} + + +//------------------------------------------------------------ +//.- +//.F CompactSlider::scaleChange +// Notify changed scale +// +//.u Syntax +//.f void CompactSlider::scaleChange() +// +//.u Description +// Called by QwtScaledWidget +// +//------------------------------------------------------------ +void CompactSlider::scaleChange() +{ + if (!hasUserScale()) + d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); + update(); +} + + +// //------------------------------------------------------------ +// //.- +// //.F CompactSlider::fontChange +// // Notify change in font +// // +// //.u Syntax +// //.f CompactSlider::fontChange(const QFont &oldFont) +// // +// //------------------------------------------------------------ +// void CompactSlider::fontChange(const QFont & /*oldFont*/) +// { +// repaint(); +// } + +//------------------------------------------------------------ +//.- +//.F CompactSlider::getValue +// Determine the value corresponding to a specified +// mouse location. +// +//.u Syntax +//.f double CompactSlider::getValue(const QPoint &p) +// +//.u Parameters +//.p const QPoint &p -- +// +//.u Description +// Called by SliderBase +// If borderless mouse is enabled p is a delta value not absolute, so can be negative. +//------------------------------------------------------------ +double CompactSlider::getValue( const QPoint &p) +{ + double rv; + const QRect r = d_sliderRect; + const double val = value(ConvertNone); + + if(borderlessMouse() && d_scrollMode != ScrDirect) + { + DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::getValue value:%.20f p x:%d y:%d step:%.20f x change:%.20f\n", + val, p.x(), p.y(), step(), p.x() * step()); + if(d_orient == Qt::Horizontal) + return val + p.x() * step(); + else + return val - p.y() * step(); + } + + const double min = minValue(ConvertNone); + const double max = maxValue(ConvertNone); + const double drange = max - min; + + if(d_orient == Qt::Horizontal) + { + if(r.width() <= d_thumbLength) + rv = 0.5 * (min + max); + else + { + const double dpos = double(p.x() - r.x() - d_thumbHalf); + const double dwidth = double(r.width() - d_thumbLength); + rv = min + rint(drange * dpos / dwidth / step()) * step(); + } + } + else + { + if(r.height() <= d_thumbLength) + rv = 0.5 * (min + max); + else + { + const double dpos = double(p.y() - r.y() - d_thumbHalf); + double dheight = double(r.height() - d_thumbLength); + rv = min + rint( (max - min) * (1.0 - dpos / dheight) / step() ) * step(); + } + } + return(rv); +} + + +//------------------------------------------------------------ +// +//.F CompactSlider::moveValue +// Determine the value corresponding to a specified mouse movement. +// +//.u Syntax +//.f void SliderBase::moveValue(const QPoint &deltaP, bool fineMode) +// +//.u Parameters +//.p const QPoint &deltaP -- Change in position +//.p bool fineMode -- Fine mode if true, coarse mode if false. +// +//.u Description +// Called by SliderBase +// Coarse mode (the normal mode) maps pixels to values depending on range and width, +// such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. +//------------------------------------------------------------ +double CompactSlider::moveValue(const QPoint &deltaP, bool fineMode) +{ + double rv; + const QRect r = d_sliderRect; + + const double val = value(ConvertNone); + + if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) + { + DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::moveValue value:%.20f p x:%d y:%d step:%.20f x change:%.20f\n", + val, deltaP.x(), deltaP.y(), step(), deltaP.x() * step()); + + double newval; + if(d_orient == Qt::Horizontal) + newval = val + deltaP.x() * step(); + else + newval = val - deltaP.y() * step(); + d_valAccum = newval; // Reset. + return newval; + } + + const double min = minValue(ConvertNone); + const double max = maxValue(ConvertNone); + const double drange = max - min; + + if(d_orient == Qt::Horizontal) + { + if(r.width() <= d_thumbLength) + rv = 0.5 * (min + max); + else + { + const double dpos = double(deltaP.x()); + const double dwidth = double(r.width() - d_thumbLength); + const double dval_diff = (drange * dpos) / dwidth; + d_valAccum += dval_diff; + rv = rint(d_valAccum / step()) * step(); + + DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::moveValue value:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f rv:%.20f\n", + val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, rv); + } + } + else + { + if(r.height() <= d_thumbLength) + rv = 0.5 * (min + max); + else + { + const double dpos = double(deltaP.y()); + const double dheight = double(r.height() - d_thumbLength); + const double dval_diff = (drange * dpos) / dheight; + d_valAccum += dval_diff; + rv = rint(d_valAccum / step()) * step(); + } + } + return(rv); +} + +//------------------------------------------------------------ +//.- +//.F CompactSlider::getScrollMode +// Determine scrolling mode and direction +// +//.u Syntax +//.f void CompactSlider::getScrollMode( const QPoint &p, int &scrollMode, int &direction ) +// +//.u Parameters +//.p const QPoint &p -- point +// +//.u Description +// Called by SliderBase +// +//------------------------------------------------------------ +void CompactSlider::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ) +{ + // If modifier or button is held, jump directly to the position at first. + // After handling it, the caller can change to SrcMouse scroll mode. + if(modifiers & Qt::ControlModifier || button == Qt::MidButton) + { + scrollMode = ScrDirect; + direction = 0; + return; + } + +// if((!detectThumb() || borderlessMouse())) + if(borderlessMouse() && button != Qt::NoButton && d_sliderRect.contains(p)) + { +// if(button != Qt::NoButton && d_sliderRect.contains(p)) +// { + scrollMode = ScrMouse; + direction = 0; + return; +// } + } + else + { + if(cursorHoming() && button == Qt::LeftButton) + { + if(d_sliderRect.contains(p)) + { + scrollMode = ScrMouse; + direction = 0; + + int mp = 0; + QRect cr; + QPoint cp; + int ipos,dist1; + double rpos; + + cr = d_sliderRect; + + rpos = (value(ConvertNone) - minValue(ConvertNone)) / (maxValue(ConvertNone) - minValue(ConvertNone)); + + if(d_orient == Qt::Horizontal) + { + dist1 = int(double(cr.width() - d_thumbLength) * rpos); + ipos = cr.x() + dist1; + mp = ipos + d_thumbHalf; + + p.setX(mp); + cp = mapToGlobal( QPoint(mp, p.y()) ); + } + else + { + dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); + ipos = cr.y() + dist1; + mp = ipos + d_thumbHalf; + p.setY(mp); + cp = mapToGlobal( QPoint(p.x(), mp) ); + } + cursor().setPos(cp.x(), cp.y()); + return; + } + } + else + { + int currentPos; + if (d_orient == Qt::Horizontal) + currentPos = p.x() - d_sliderRect.x(); + else + currentPos = p.y() - d_sliderRect.y(); + + if(d_sliderRect.contains(p)) + { + // if ((currentPos > d_valuePixel - d_thumbHalf) + // && (currentPos < d_valuePixel + d_thumbHalf)) + if (!detectThumb() || + ((currentPos >= d_valuePixel - d_thumbHitLength / 2) + && (currentPos <= d_valuePixel + d_thumbHitLength / 2))) + { + scrollMode = ScrMouse; + direction = 0; + return; + } + else if(pagingButtons().testFlag(button)) + { + scrollMode = ScrPage; + if(((currentPos > d_valuePixel) && (d_orient == Qt::Horizontal)) + || ((currentPos <= d_valuePixel) && (d_orient != Qt::Horizontal))) + direction = 1; + else + direction = -1; + return; + } + } + } + } + + scrollMode = ScrNone; + direction = 0; +} + +void CompactSlider::getActiveArea() +{ + const QRect& geo = rect(); +// const int req_thumb_margin = (d_thumbHalf - d_xMargin) > 1 ? (d_thumbHalf - d_xMargin) : 1; + const int req_thumb_margin = d_thumbLength == 0 ? 0 : ((d_thumbHalf - d_xMargin) > 1 ? (d_thumbHalf - d_xMargin) : 1); + +// QStyleOptionFrame option; +// option.initFrom(this); +/* + // FIXME: These aren't working for PE_Frame. We just get a 1px-wide square dark grey frame. + // For others like PE_FrameLineEdit which looks OK, these have no effect and items + // such as rounded frame are determined by the style. + option.lineWidth = d_xMargin; + option.features = QStyleOptionFrame::Rounded; + option.frameShape = QFrame::StyledPanel; + option.midLineWidth = 0; + option.state = QStyle::State_Sunken; + option.state |= QStyle::State_Active; + if(isEnabled()) + option.state |= QStyle::State_Enabled; + if(hasFocus()) + { + option.state |= QStyle::State_HasFocus; +// option.state |= QStyle::State_Selected; + } + if(underMouse()) + { + option.state |= QStyle::State_MouseOver; // FIXME: Not working. + } +// if(hasEditFocus()) +// option.state |= (QStyle::State_HasFocus | QStyle::State_HasEditFocus | QStyle::State_Selected); + + const QStyle* st = style(); + if(st) + st = st->proxy(); +// if(st) + if(0) + { + +// FIXME: Can't seem to get true inside area. All these methods return 1px frame width but the visible control has 2px width. +// Maybe ask for QStyle::PM_DefaultFrameWidth. +// return st->subElementRect(QStyle::SE_FrameContents, &option); +// return contentsRect(); + d_sliderRect = st->subElementRect(QStyle::SE_ShapedFrameContents, &option).adjusted(1, 1, -1, -1); + } + else*/ + { + d_sliderRect = QRect( +// geo.x() + d_xMargin + active_thumb_margin, + geo.x() + d_xMargin + req_thumb_margin, + geo.y() + d_yMargin, +// geo.width() - 2 * (d_xMargin + active_thumb_margin), + geo.width() - 2 * (d_xMargin + req_thumb_margin), + geo.height() - 2 * d_yMargin); + } +} + +void CompactSlider::getPixelValues() +{ + const int val_width_range = ((d_orient == Qt::Horizontal) ? d_sliderRect.width(): d_sliderRect.height()); + const int val_pix_range = val_width_range - 1; + const double minV = minValue(ConvertNone); + const double maxV = maxValue(ConvertNone); + const double range = maxV - minV; + const double val = value(ConvertNone); + + if(range == 0.0) + { + d_valuePixel = 0; + d_valuePixelWidth = 0; + return; + } + const double val_fact = (val - minV) / range; + + d_valuePixel = (val_fact * (double)val_pix_range); + d_valuePixelWidth = (val_fact * (double)val_width_range); +} + +//------------------------------------------------------------ +//.F CompactSlider::paintEvent +// Qt paint event +// +//.u Syntax +//.f void CompactSlider::paintEvent(QPaintEvent *e) +//------------------------------------------------------------ + +void CompactSlider::paintEvent(QPaintEvent* /*ev*/) +{ + const QRect& geo = rect(); + if(geo.width() <= 0 || geo.height() <= 0) + return; + + QPainter p(this); + + const QPalette& pal = palette(); + + const int req_thumb_margin = d_thumbLength == 0 ? 0 : ((d_thumbHalf - d_xMargin) > 1 ? (d_thumbHalf - d_xMargin) : 1); + + const int label_to_val_margin = 6; + + const QPen orig_pen = p.pen(); + + const QColor& margin_color = isEnabled() ? (d_borderColor.isValid() ? d_borderColor : pal.color(QPalette::Active, QPalette::Button)) : + pal.color(QPalette::Disabled, QPalette::Button); + QColor border_color; + + if(_textHighlightMode & TextHighlightFocus) + border_color = _hovered ? pal.color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Highlight).lighter() : margin_color; + else + //border_color = hasFocus() ? pal.color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Highlight).lighter() : margin_color; + border_color = isEnabled() ? (hasFocus() ? margin_color.lighter() : margin_color) : pal.color(QPalette::Disabled, QPalette::Highlight); + + const QColor c4 = isEnabled() ? (d_slotColor.isValid() ? d_slotColor : pal.color(QPalette::Active, QPalette::Dark)) : + pal.color(QPalette::Disabled, QPalette::Dark); + const QColor c3 = c4.darker(125); + + QColor inactive_border_color; + + if(_textHighlightMode & TextHighlightFocus) + //inactive_border_color = _hovered ? pal.color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Highlight).lighter() : margin_color; + inactive_border_color = isEnabled() ? (_hovered ? border_color : c3) : pal.color(QPalette::Disabled, QPalette::Highlight); + else + //border_color = hasFocus() ? pal.color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Highlight).lighter() : margin_color; + inactive_border_color = isEnabled() ? (hasFocus() ? border_color : c3) : pal.color(QPalette::Disabled, QPalette::Highlight); + + // Draw margins: + if(d_yMargin) + { + // Top + //if(_activeBorders & TopBorder) + p.fillRect(geo.x(), + geo.y(), + geo.width(), + d_yMargin, + _activeBorders & TopBorder ? border_color : inactive_border_color); + + // Bottom + //if(_activeBorders & BottomBorder) + p.fillRect(geo.x(), + geo.height() - d_yMargin, + geo.width(), + d_yMargin, + _activeBorders & BottomBorder ? border_color : inactive_border_color); + } + + if(d_xMargin) + { + // Left + //if(_activeBorders & LeftBorder) + p.fillRect(geo.x(), + geo.y(), + d_xMargin, + geo.height(), + _activeBorders & LeftBorder ? border_color : inactive_border_color); + + // Right + //if(_activeBorders & RightBorder) + p.fillRect(geo.width() - d_xMargin, + geo.y(), + d_xMargin, + geo.height(), + _activeBorders & RightBorder ? border_color : inactive_border_color); + } + + // Extra left margin + //if(_activeBorders & LeftBorder) + p.fillRect(d_xMargin, + d_yMargin, + req_thumb_margin, + geo.height() - 2 * d_yMargin, + _activeBorders & LeftBorder ? margin_color : inactive_border_color); + + // Extra right margin + //if(_activeBorders & RightBorder) + p.fillRect( + geo.width() - d_xMargin - req_thumb_margin, + d_yMargin, + req_thumb_margin, + geo.height() - 2 * d_yMargin, + _activeBorders & RightBorder ? margin_color : inactive_border_color); + +// const QPainterPath& onPath = *_onPath; +// +// // Draw corners as normal background colour. Path subtraction. +// QPainterPath cornerPath; +// cornerPath.addRect(d_sliderRect); +// cornerPath -= onPath; +// if(!cornerPath.isEmpty()) +// //p.fillPath(cornerPath, palette().window().color().darker(125)); +// //p.fillPath(cornerPath, palette().shadow().color().darker(125)); +// p.fillPath(cornerPath, isEnabled() ? (d_slotColor.isValid() ? d_slotColor : palette().color(QPalette::Active, QPalette::Dark)) : +// palette().color(QPalette::Disabled, QPalette::Dark)); + + int x = d_sliderRect.x(); + int y = d_sliderRect.y(); + int h = d_sliderRect.height(); + + const int x1 = x; + const int w1 = d_valuePixelWidth; + + const int y1 = y; + const int y2 = y1 + h - 1; + + QLinearGradient linearGrad_a(x, y1, x, y2); + + const QColor c2 = isEnabled() ? (d_barColor.isValid() ? d_barColor : pal.color(QPalette::Active, QPalette::Highlight)) : + pal.color(QPalette::Disabled, QPalette::Highlight); + const QColor c1 = c2.darker(125); + linearGrad_a.setColorAt(0, c1); + linearGrad_a.setColorAt(0.5, c2); + linearGrad_a.setColorAt(1, c1); + + QPainterPath onPath; + MusECore::addRoundedPath(&onPath, QRect (x1, y, w1, h), 4, 4, + (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight) ); + if(!onPath.isEmpty()) + p.fillPath(onPath, linearGrad_a); + + + linearGrad_a.setColorAt(0, c3); + linearGrad_a.setColorAt(0.5, c4); + linearGrad_a.setColorAt(1, c3); + + QPainterPath offPath; + offPath.addRect(d_sliderRect); + offPath -= onPath; + if(!offPath.isEmpty()) + p.fillPath(offPath, linearGrad_a); + + + const double minV = minValue(ConvertNone); + const double val = value(ConvertNone); + + const QRect bar_area((d_orient == Qt::Horizontal) ? + QRect(d_sliderRect.x(), + d_sliderRect.y(), + d_valuePixelWidth, + d_sliderRect.height()) : + QRect(d_sliderRect.x(), + d_sliderRect.y() + d_sliderRect.height() - d_valuePixelWidth, + d_sliderRect.width(), + d_sliderRect.height() - d_valuePixelWidth)); // FIXME + const QRect bkg_area((d_orient == Qt::Horizontal) ? + QRect(d_sliderRect.x() + d_valuePixelWidth, + d_sliderRect.y(), + //active_area.width() - (active_area.x() + val_pix), + d_sliderRect.width() - d_valuePixelWidth, + d_sliderRect.height()) : + QRect(d_sliderRect.x(), + d_sliderRect.y(), + d_sliderRect.width(), + d_sliderRect.height() - d_valuePixelWidth)); // FIXME + +// p.setClipPath(onPath); +// p.fillPath(onPath, _onPixmap); + +// p.setClipPath(offPath); +// p.fillPath(offPath, _offPixmap); + + // Restore the clipper to full size. +// p.setClipRect(geo); + + if((!_autoHideThumb || _mouseOverThumb) && d_thumbLength > 0) + { + DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::PaintEvent _mouseOverThumb:%d\n", _mouseOverThumb); + const QRect thumb_rect((d_orient == Qt::Horizontal) ? + QRect(d_sliderRect.x() + d_valuePixel - d_thumbHalf, + d_sliderRect.y() + d_thumbWidthMargin, + d_thumbLength, + d_sliderRect.height() - 2 * d_thumbWidthMargin) : + QRect(d_sliderRect.x() + d_thumbWidthMargin, + d_sliderRect.y() + d_valuePixel - d_thumbHalf, + d_sliderRect.width() - 2 * d_thumbWidthMargin, + d_thumbLength)); + p.fillRect(thumb_rect, isEnabled() ? (d_thumbColor.isValid() ? d_thumbColor : pal.color(QPalette::Active, QPalette::Mid)) : + pal.color(QPalette::Disabled, QPalette::Mid)); + } + + const QFont& fnt = font(); + QFont aliased_fnt(fnt); + // Turn off anti-aliasing for sharper text. if we want it: + if(fnt.pointSize() <= _maxAliasedPointSize) + { + aliased_fnt.setFamily("Sans"); + //aliased_fnt.setHintingPreference(QFont::PreferVerticalHinting); + //aliased_fnt.setStyleStrategy(QFont::PreferBitmap); + aliased_fnt.setStyleStrategy(QFont::NoAntialias); + //p.setFont(aliased_fnt); + } + const QFontMetrics aliased_fm(aliased_fnt); + + const bool show_val = _showValue; + + const QFontMetrics fm = p.fontMetrics(); + + const QRect text_area(d_sliderRect.adjusted(1, 1, -1, -1)); + + const QString comp_val_text = isOff() ? d_offText : + ((val <= minV && !d_specialValueText.isEmpty()) ? + d_specialValueText : (d_valPrefix + locale().toString(val, 'f', _valueDecimals) + d_valSuffix)); + //const int val_width = fm.width(comp_val_text); + const int val_width = aliased_fm.width(comp_val_text); + int vx = text_area.width() - val_width; + if(vx < 0) + vx = 0; + const QRect val_area(vx, text_area.y(), val_width, text_area.height()); + + int lw = text_area.width(); + if(show_val) + lw = lw - val_width - label_to_val_margin; + if(lw < 0) + lw = 0; + QRect label_area(text_area.x(), text_area.y(), lw, text_area.height()); + + //const QString elided_label_text = fm.elidedText(d_labelText, Qt::ElideMiddle, text_area.width()); +// const QString elided_label_text = fm.elidedText(d_labelText, Qt::ElideMiddle, label_area.width()); + const QString elided_label_text = d_labelText; + if(!show_val) + { + const QRect label_br = fm.boundingRect(d_labelText); + const int label_bw = label_br.width(); + int label_xoff = (label_area.width() - label_bw) / 2; + if(label_xoff < 0) + label_xoff = 0; + label_area.adjust(label_xoff, 0, 0, 0); + } + + //const int label_width = fm.width(elided_label_text); + + //const bool show_both = (text_area.width() - val_width) > (label_area.width() + 2); + //const bool show_both = label_area.width() > label_to_val_margin; + const bool show_label = show_val || label_area.width() > label_to_val_margin; + + const bool on = _textHighlightMode & TextHighlightOn; + const bool shd = _textHighlightMode & TextHighlightShadow; + const bool spl = _textHighlightMode & TextHighlightSplit; + const bool hov = _textHighlightMode & TextHighlightHover; + const bool foc = _textHighlightMode & TextHighlightFocus; + + const bool is_hov = hov && _hovered; + const bool is_foc = foc && hasFocus(); + + // Normal text: + p.setPen(Qt::black); + //QRect text_bkg = text_area; + QRect label_bkg = label_area; + QRect val_bkg = val_area; + if(shd) + { + //text_bkg.adjust(1, 1, 1, 1); + val_bkg.adjust(1, 1, 1, 1); + label_bkg.adjust(1, 1, 1, 1); + if(show_val) + { + p.setFont(aliased_fnt); + //p.drawText(text_bkg, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); + p.drawText(val_bkg, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); + //_LCDPainter->drawText(&p, val_bkg, comp_val_text, Qt::AlignRight | Qt::AlignVCenter); + p.setFont(fnt); + } + if(show_label) + //p.drawText(text_bkg, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); + p.drawText(label_bkg, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); + } + + if((!shd && ((!on || spl) && !is_hov && !is_foc))) + { + if(spl) + p.setClipRect(bkg_area); + else + // Restore the clipper to full size. + p.setClipRect(geo); + + if(show_val) + { + p.setFont(aliased_fnt); + //p.drawText(text_area, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); + p.drawText(val_area, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); + //_LCDPainter->drawText(&p, val_area, comp_val_text, Qt::AlignRight | Qt::AlignVCenter); + p.setFont(fnt); + } + if(show_label) + //p.drawText(text_area, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); + p.drawText(label_area, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); + } + + // Highlighted text: + if(on || is_hov || is_foc) + { + p.setPen(Qt::white); + if(spl && !is_hov && !is_foc) + p.setClipRect(bar_area); + else + // Restore the clipper to full size. + p.setClipRect(geo); + + if(show_val) + { + p.setFont(aliased_fnt); + //p.drawText(text_area, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); + p.drawText(val_area, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); + //_LCDPainter->drawText(&p, val_area, comp_val_text, Qt::AlignRight | Qt::AlignVCenter); + p.setFont(fnt); + } + if(show_label) + //p.drawText(text_area, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); + p.drawText(label_area, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); + } +} + +void CompactSlider::mouseMoveEvent(QMouseEvent *e) +{ + e->ignore(); + SliderBase::mouseMoveEvent(e); + + QPoint p = e->pos(); + + const bool oldv = _mouseOverThumb; + getMouseOverThumb(p); + if(_autoHideThumb &&_mouseOverThumb != oldv) + update(); +} + +void CompactSlider::mouseDoubleClickEvent(QMouseEvent* e) +{ + DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::mouseDoubleClickEvent\n"); + const Qt::MouseButtons buttons = e->buttons(); + const Qt::KeyboardModifiers keys = e->modifiers(); + + if(buttons == Qt::LeftButton && _mouseOverThumb && !_editMode) + { + DEBUG_COMPACT_SLIDER(stderr, " left button\n"); + //if(_mouseOverThumb && !_editMode) + //{ + //if(ctrl_key) + if(keys == Qt::ControlModifier) + { + if(_hasOffMode) + { + setOff(!isOff()); // Just toggle the off state. + emit valueChanged(value(), id()); + e->accept(); + return; + } + } + // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. + else if(keys == Qt::NoModifier && (!_editor || !_editor->hasFocus())) + { + showEditor(); + e->accept(); + return; + } + //} + //e->accept(); + //return; + } + + e->ignore(); + SliderBase::mouseDoubleClickEvent(e); +} + +void CompactSlider::editorReturnPressed() +{ + DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::editorReturnPressed\n"); + _editMode = false; + if(_editor) + { + if(value() != _editor->value()) + setValue(_editor->value()); + _editor->deleteLater(); + _editor = 0; + setFocus(); + } +} + +void CompactSlider::editorEscapePressed() +{ + DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::editorEscapePressed\n"); + _editMode = false; + if(_editor) + { + _editor->deleteLater(); + _editor = 0; + setFocus(); + } +} + +void CompactSlider::keyPressEvent(QKeyEvent* e) +{ + if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) + { + // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. + if(!_editor || !_editor->hasFocus()) + showEditor(); + e->accept(); + return; + } + + e->ignore(); + SliderBase::keyPressEvent(e); +} + +void CompactSlider::enterEvent(QEvent *e) +{ + _entered = true; + if(!_hovered) + { + _hovered = true; + if(_textHighlightMode & TextHighlightHover) + update(); + } + + e->ignore(); + SliderBase::enterEvent(e); +} + +void CompactSlider::leaveEvent(QEvent *e) +{ + _entered = false; + if(!_pressed) + { + if(_hovered) + { + _hovered = false; + } + if(_textHighlightMode & TextHighlightHover) + update(); + _mouseOverThumb = false; + if(_autoHideThumb) + update(); + } + e->ignore(); + SliderBase::leaveEvent(e); +} + +bool CompactSlider::event(QEvent* e) +{ + switch(e->type()) + { + // FIXME: Doesn't work. + case QEvent::NonClientAreaMouseButtonPress: + DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::event NonClientAreaMouseButtonPress\n"); + e->accept(); + _editMode = false; + if(_editor) + { + _editor->deleteLater(); + _editor = 0; + } + return true; + break; + +// case QEvent::PaletteChange: +// DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::event PaletteChange\n"); +// updatePixmaps(); +// break; +// +// case QEvent::EnabledChange: +// DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::event EnabledChange\n"); +// updatePixmaps(); +// break; + +// case QEvent::ActivationChange: +// case QEvent::WindowActivate: +// case QEvent::WindowDeactivate: +// DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::event ActivationChange, WindowActivate, or WindowDeactivate\n"); +// updatePixmaps(); +// break; + + default: + break; + } + + return SliderBase::event(e); +} + + +// void CompactSlider::updatePixmaps() +// { +// getActiveArea(); +// getPixelValues(); +// +// int x = d_sliderRect.x(); +// int y = d_sliderRect.y(); +// int w = d_sliderRect.width(); +// int h = d_sliderRect.height(); +// +// _onPixmap = QPixmap(w, h); +// _offPixmap = QPixmap(w, h); +// +// const int y1 = y; +// const int y2 = y1 + h - 1; +// +// +// const QPalette& pal = palette(); +// +// QLinearGradient linearGrad_a(x, y1, x, y2); +// +// +// const QColor c2 = isEnabled() ? (d_barColor.isValid() ? d_barColor : pal.color(QPalette::Active, QPalette::Highlight)) : +// pal.color(QPalette::Disabled, QPalette::Highlight); +// const QColor c1 = c2.darker(125); +// +// linearGrad_a.setColorAt(0, c1); +// linearGrad_a.setColorAt(0.5, c2); +// linearGrad_a.setColorAt(1, c1); +// +// if(w > 0 && h > 0) +// { +// QPainter p; +// p.begin(&_onPixmap); +// p.fillRect(0, 0, w, h, linearGrad_a); +// p.end(); +// p.begin(&_offPixmap); +// p.fillRect(0, 0, w, h, pal.window()); +// p.end(); +// } +// +// updatePainterPaths(); +// } + +//------------------------------------------------------------ +//.F CompactSlider::resizeEvent +// Qt resize event +// +//.u Parameters +//.p QResizeEvent *e +// +//.u Syntax +//.f void CompactSlider::resizeEvent(QResizeEvent *e) +//------------------------------------------------------------ + +void CompactSlider::resizeEvent(QResizeEvent *e) +{ + SliderBase::resizeEvent(e); + d_resized = true; + + //updatePixmaps(); + getActiveArea(); + getPixelValues(); + + if(_editor && _editor->isVisible()) + _editor->setGeometry(rect()); +} + +void CompactSlider::showEditor() +{ + if(_editMode) + return; + + if(!_editor) + { + DEBUG_COMPACT_SLIDER(stderr, " creating editor\n"); + _editor = new PopupDoubleSpinBox(this); + _editor->setFrame(false); + _editor->setFocusPolicy(Qt::WheelFocus); + connect(_editor, SIGNAL(returnPressed()), SLOT(editorReturnPressed())); + connect(_editor, SIGNAL(escapePressed()), SLOT(editorEscapePressed())); + } + int w = width(); + //if (w < _editor->sizeHint().width()) + // w = _editor->sizeHint().width(); + _editor->setGeometry(0, 0, w, height()); + DEBUG_COMPACT_SLIDER(stderr, " x:%d y:%d w:%d h:%d\n", _editor->x(), _editor->y(), w, _editor->height()); + _editor->setDecimals(_valueDecimals); + _editor->setSingleStep(step()); + _editor->setPrefix(valPrefix()); + _editor->setSuffix(valSuffix()); + _editor->setMinimum(minValue()); + _editor->setMaximum(maxValue()); + _editor->setValue(value()); + _editor->selectAll(); + _editMode = true; + _editor->show(); + _editor->setFocus(); +} + +void CompactSlider::getMouseOverThumb(QPoint &p) +{ + int scrollMode; + int direction; + //getScrollMode(p, left_btn ? Qt::LeftButton : 0, scrollMode, direction); + // Force left button in case getScrollMode depends on it. +// getScrollMode(p, Qt::LeftButton, Qt::KeyboardModifiers(), scrollMode, direction); + getScrollMode(p, Qt::NoButton, Qt::KeyboardModifiers(), scrollMode, direction); + const bool v = scrollMode == ScrMouse; + //if(_mouseOverThumb != v && !(left_btn && !v)) +// if(_mouseOverThumb != v && !(bPressed && !v)) + if(_mouseOverThumb != v && !(_pressed && !v)) + { + DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::getMouseOverThumb setting _mouseOverThumb:%d to v:%d\n", _mouseOverThumb, v); + _mouseOverThumb = v; + } + const bool hv = rect().contains(p); +// if(_hovered != hv && !bPressed) + if(_hovered != hv && !_pressed) + _hovered = hv; +} + +void CompactSlider::setShowValue(bool show) +{ + _showValue = show; + resize(size()); + updateGeometry(); // Required. + update(); +} + +void CompactSlider::setOff(bool v) +{ + if(v && !_hasOffMode) + _hasOffMode = true; + if(_off == v) + return; + _off = v; + update(); + emit valueStateChanged(value(), isOff(), id(), d_scrollMode); +} + +void CompactSlider::setHasOffMode(bool v) +{ + _hasOffMode = v; + setOff(false); +} + +void CompactSlider::setValueState(double v, bool off, ConversionMode mode) +{ + // Do not allow setting value from the external while mouse is pressed. + if(_pressed) + return; + + bool do_off_upd = false; + bool do_val_upd = false; + // Both setOff and setValue emit valueStateChanged and setValue emits valueChanged. + // We will block them and emit our own here. Respect the current block state. + const bool blocked = signalsBlocked(); + if(!blocked) + blockSignals(true); + if(isOff() != off) + { + do_off_upd = true; + setOff(off); + } +// if(value() != v) + if(value(mode) != v) + { + do_val_upd = true; +// setValue(v); + setValue(v, mode); + } + if(!blocked) + blockSignals(false); + + if(do_off_upd || do_val_upd) + update(); + if(do_val_upd) + emit valueChanged(value(), id()); + if(do_off_upd || do_val_upd) + emit valueStateChanged(value(), isOff(), id(), d_scrollMode); +} + + +//------------------------------------------------------------ +//.- +//.F CompactSlider::valueChange +// Notify change of value +// +//.u Syntax +//.f void CompactSlider::valueChange() +// +//------------------------------------------------------------ + +void CompactSlider::valueChange() + { + // Turn the control back on with any value set. + // Wanted to make this flaggable, but actually we + // have to in order to see any value changing, + if(isOff()) + setOff(false); + + getPixelValues(); + + //updatePainterPaths(); + + QPoint p = mapFromGlobal(QCursor::pos()); + + getMouseOverThumb(p); + update(); + + // HACK + // In direct mode let the inherited classes (this) call these in their valueChange() methods, + // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. + // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. + // Hack: Since valueChange() is NOT called if nothing changed, in that case these are called for us by the SliderBase. + if(d_scrollMode == ScrDirect) + { + processSliderPressed(id()); + emit sliderPressed(value(), id()); + } + + // Emits valueChanged if tracking enabled. + SliderBase::valueChange(); + // Emit our own combined signal. + if(trackingIsActive()) + emit valueStateChanged(value(), isOff(), id(), d_scrollMode); + } + +//------------------------------------------------------------ +//.- +//.F CompactSlider::rangeChange +// Notify change of range +// +//.u Description +// +//.u Syntax +//.f void CompactSlider::rangeChange() +// +//------------------------------------------------------------ +void CompactSlider::rangeChange() +{ + if (!hasUserScale()) + d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); + getPixelValues(); + SliderBase::rangeChange(); +// repaint(); + update(); +} + +//------------------------------------------------------------ +// +//.F CompactSlider::setMargins +// Set distances between the widget's border and +// internals. +// +//.u Syntax +//.f void CompactSlider::setMargins(int hor, int vert) +// +//.u Parameters +//.p int hor, int vert -- Margins +// +//------------------------------------------------------------ +void CompactSlider::setMargins(int hor, int vert) +{ + d_xMargin = MusECore::qwtMax(0, hor); + d_yMargin = MusECore::qwtMax(0, vert); + resize(this->size()); +} + +//------------------------------------------------------------ +// +//.F CompactSlider::sizeHint +// Return a recommended size +// +//.u Syntax +//.f QSize CompactSlider::sizeHint() const +// +//.u Note +// The return value of sizeHint() depends on the font and the +// scale. +//------------------------------------------------------------ + +QSize CompactSlider::sizeHint() const + { + return getMinimumSizeHint(fontMetrics(), d_orient, d_scalePos, d_xMargin, d_yMargin); + } + +//--------------------------------------------------------- +// setOrientation +//--------------------------------------------------------- + +void CompactSlider::setOrientation(Qt::Orientation o) + { + d_orient = o; + } + +Qt::Orientation CompactSlider::orientation() const + { + return d_orient; + } + +double CompactSlider::lineStep() const + { + return 1.0; + } + +double CompactSlider::pageStep() const + { + return 1.0; + } + +void CompactSlider::setLineStep(double) + { + } + +void CompactSlider::setPageStep(double) + { + } + +// void CompactSlider::updatePainterPaths() +// { +// if(_offPath) +// delete _offPath; +// if(_onPath) +// delete _onPath; +// +// _offPath = new QPainterPath; +// _onPath = new QPainterPath; +// +// int x = d_sliderRect.x(); +// int y = d_sliderRect.y(); +// int w = d_sliderRect.width(); +// int h = d_sliderRect.height(); +// +// const int x1 = x; +// const int w1 = d_valuePixelWidth; +// +// const int x2 = x + d_valuePixelWidth; +// const int w2 = w - d_valuePixelWidth; +// +// MusECore::addRoundedPath(_onPath, QRect (x1, y, w1, h), 4, 4, +// //(MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); +// //(MusECore::Corner) (MusECore::UpperLeft | MusECore::LowerLeft) ); +// (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight) ); +// +// MusECore::addRoundedPath(_offPath, QRect (x2, y, w2, h), 4, 4, +// //(MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); +// (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight) ); +// } + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/compact_slider.h muse-3.0.2+ds1/muse/widgets/compact_slider.h --- muse-2.1.2/muse/widgets/compact_slider.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/compact_slider.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,316 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// compact_slider.h +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __COMPACT_SLIDER_H__ +#define __COMPACT_SLIDER_H__ + +//#include + +#include "sclif.h" +#include "sliderbase.h" +#include "scldraw.h" + +class QPainterPath; +class QEvent; + +namespace MusEGui { + +class PopupDoubleSpinBox; +//class LCDPainter; + +//--------------------------------------------------------- +// CompactSlider +//--------------------------------------------------------- + +class CompactSlider : public SliderBase, public ScaleIf +{ + Q_OBJECT + + public: + enum ScalePos { None, Left, Right, Top, Bottom, Embedded }; + + // Can be OR'd together. + enum ActiveBorders { NoBorders = 0, + LeftBorder = 1, + RightBorder = 2, + TopBorder = 4, + BottomBorder = 8, + AllBorders = LeftBorder | RightBorder | TopBorder | BottomBorder, + AllBordersExceptTop = LeftBorder | RightBorder | BottomBorder, + AllBordersExceptBottom = LeftBorder | RightBorder | TopBorder, + AllBordersExceptLeft = RightBorder | TopBorder | BottomBorder, + AllBordersExceptRight = LeftBorder | TopBorder | BottomBorder, + }; + typedef int ActiveBorders_t; + + // Can be OR'd together. + enum TextHighlightModes { //TextHighlightNone, + //TextHighlightAlways, + TextHighlightOn = 1, + TextHighlightSplit = 2, + TextHighlightShadow = 4, + //TextHighlightSplitAndShadow, + TextHighlightHover = 8, + TextHighlightFocus = 16 + //TextHighlightHoverOrFocus + }; + typedef int TextHighlightMode_t; + + private: + Q_PROPERTY( double lineStep READ lineStep WRITE setLineStep ) + Q_PROPERTY( double pageStep READ pageStep WRITE setPageStep ) + Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation ) + + Q_PROPERTY( QColor barColor READ barColor WRITE setBarColor ) + Q_PROPERTY( QColor slotColor READ slotColor WRITE setSlotColor ) + Q_PROPERTY( QColor thumbColor READ thumbColor WRITE setThumbColor ) + + Q_PROPERTY( QString labelText READ labelText WRITE setLabelText ) + Q_PROPERTY( QString valPrefix READ valPrefix WRITE setValPrefix ) + Q_PROPERTY( QString valSuffix READ valSuffix WRITE setValSuffix ) + Q_PROPERTY( QString specialValueText READ specialValueText WRITE setSpecialValueText ) + + QRect d_sliderRect; + // Whether the mouse is currently over the thumb 'hit' space. + bool _mouseOverThumb; + // Whether the mouse is over the entire control. + bool _hovered; + // Cached pixmap values. + //QPixmap _onPixmap, _offPixmap; + //QPainterPath* _onPath; + //QPainterPath* _offPath; + + bool _detectThumb; + bool _autoHideThumb; + bool _hasOffMode; + int d_thumbLength; + int d_thumbHitLength; + int d_thumbHalf; + int d_thumbWidth; + int d_thumbWidthMargin; + int d_scaleDist; + int d_xMargin; + int d_yMargin; + int d_mMargin; + // Which of the coloured borders are active. + // This allows them to be stacked or placed side by side + // without the border colours being too bright or annoying + // since the joining edges are twice as thick. + ActiveBorders_t _activeBorders; + + QColor d_borderColor; + QColor d_barColor; + QColor d_slotColor; + QColor d_thumbColor; + + QString d_labelText; + QString d_valPrefix; + QString d_valSuffix; + QString d_specialValueText; + QString d_offText; + TextHighlightMode_t _textHighlightMode; + int _valueDecimals; + bool _off; + // Whether to display the value, along with the text. + bool _showValue; + + PopupDoubleSpinBox* _editor; + bool _editMode; + //LCDPainter* _LCDPainter; + + bool _entered; // Set on enter and leave. + bool d_resized; + bool d_autoResize; + double d_scaleStep; + + Qt::Orientation d_orient; + ScalePos d_scalePos; + int d_bgStyle; + + // Two slightly different versions of the same thing: valuePixel is the current value scaled to + // a range up to and including the LAST pixel, while valueWidth is the current value scaled to + // a range up to and including the last pixel PLUS ONE ie the 'pixel width'. Values start at zero. + int d_valuePixel; + int d_valuePixelWidth; + + //void updatePainterPaths(); + + private slots: + void editorReturnPressed(); + void editorEscapePressed(); + + protected: + // At what point size to switch from aliased text (brighter, crisper but may look too jagged and unreadable with some fonts) + // to non-aliased text (dimmer, fuzzier but looks better). Zero means always use anti-aliasing. + int _maxAliasedPointSize; + + // Determine the value corresponding to a specified mouse location. + // If borderless mouse is enabled p is a delta value not absolute, so can be negative. + double getValue(const QPoint &p); + // Determine the value corresponding to a specified mouse movement. + double moveValue(const QPoint &deltaP, bool fineMode = false); + // Determine scrolling mode and direction. + void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction); + + // Update internal values. + void getPixelValues(); + void getActiveArea(); + void getMouseOverThumb(QPoint &p); + + void showEditor(); + + virtual void resizeEvent(QResizeEvent*); + virtual void paintEvent (QPaintEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void mouseDoubleClickEvent(QMouseEvent*); + virtual void keyPressEvent(QKeyEvent*); + virtual void leaveEvent(QEvent*); + virtual void enterEvent(QEvent*); + virtual bool event(QEvent*); + + void valueChange(); + void rangeChange(); + void scaleChange(); + //void fontChange(const QFont &oldFont); + + //virtual void updatePixmaps(); + virtual void processSliderPressed(int); + virtual void processSliderReleased(int); + // Show a handy tooltip value box. + virtual void showValueToolTip(QPoint); + + signals: + // Both value and off state changed combined into one signal. + // In typical automation use, this signal should be ignored in ScrDirect scroll mode. + // ScrDirect mode happens only once upon press with a modifier. + // In ScrDirect mode the slider sends both pressed AND changed signals + // since the position jumps to the pressed location. + // Note the SliderBase::valueChanged signal is also available. + void valueStateChanged(double value, bool off, int id, int scrollMode); + + public: + CompactSlider(QWidget *parent = 0, const char *name = 0, + Qt::Orientation orient = Qt::Horizontal, + ScalePos scalePos = None, + const QString& labelText = QString(), + const QString& valPrefix = QString(), + const QString& valSuffix = QString(), + const QString& specialValueText = QString(), + const QColor& borderColor = QColor(), + const QColor& barColor = QColor(228,203,36), + const QColor& slotColor = QColor(), + const QColor& thumbColor = QColor()); + + virtual ~CompactSlider(); + + static QSize getMinimumSizeHint(const QFontMetrics& fm, + Qt::Orientation orient = Qt::Vertical, + ScalePos scalePos = None, + int xMargin = 0, + int yMargin = 0); + + // Which of the coloured borders are active. + // This allows them to be stacked or placed side by side + // without the border colours being too bright or annoying + // since the joining edges are twice as thick. + ActiveBorders_t activeBorders() const { return _activeBorders; } + void setActiveBorders(ActiveBorders_t borders); + + void setThumbLength(int l); + void setThumbWidth(int w); + + void setOrientation(Qt::Orientation o); + Qt::Orientation orientation() const; + + double lineStep() const; + double pageStep() const; + + void setLineStep(double); + void setPageStep(double); + + QColor borderColor() const { return d_borderColor; } + void setBorderColor(const QColor& c) { d_borderColor = c; update(); } + QColor barColor() const { return d_barColor; } + void setBarColor(const QColor& c) { d_barColor = c; update(); } + QColor slotColor() const { return d_slotColor; } + void setSlotColor(const QColor& c) { d_slotColor = c; update(); } + QColor thumbColor() const { return d_thumbColor; } + void setThumbColor(const QColor& c) { d_thumbColor = c; update(); } + // Whether the user must click on the thumb or else anywhere in the control to move the value. + bool detectThumb() const { return _detectThumb; } + // Set whether the user must click on the thumb or else anywhere in the control to move the value. + void setDetectThumb(bool v) { _detectThumb = v; update(); } + + // At what point size to switch from aliased text to non-aliased text. Zero means always use anti-aliasing. Default 8. + int maxAliasedPointSize() const { return _maxAliasedPointSize; } + // Sets at what point size to switch from aliased text (brighter, crisper but may look too jagged and unreadable with some fonts) + // to non-aliased text (dimmer, fuzzier but looks better). Zero means always use anti-aliasing. + void setMaxAliasedPointSize(int sz) { if(sz<0)sz=0;_maxAliasedPointSize = sz; update(); } + + QString labelText() const { return d_labelText; }; + void setLabelText(const QString& t) { d_labelText = t; update(); } + QString valPrefix() const { return d_valPrefix; }; + void setValPrefix(const QString& t) { d_valPrefix = t; update(); } + QString valSuffix() const { return d_valSuffix; }; + void setValSuffix(const QString& t) { d_valSuffix = t; update(); } + QString specialValueText() const { return d_specialValueText; }; + void setSpecialValueText(const QString& t) { d_specialValueText = t; update(); } + QString offText() const { return d_offText; }; + void setOffText(const QString& t) { d_offText = t; update(); } + TextHighlightMode_t textHighlightMode() const { return _textHighlightMode; } + void setTextHighlightMode(TextHighlightMode_t mode) { _textHighlightMode = mode; update(); } + int valueDecimals() const { return _valueDecimals; } + void setValueDecimals(int d) { if(d < 0) return; _valueDecimals = d; update(); } + + QString toolTipValueText(bool inclLabel, bool inclVal) const; + + bool showValue() const { return _showValue; } + void setShowValue(bool show); + + bool hasOffMode() const { return _hasOffMode; } + void setHasOffMode(bool v); + bool isOff() const { return _off; } + // Sets the off state and emits valueStateChanged signal if required. + void setOff(bool v); + // Both value and off state changed combined into one setter. + // By default it is assumed that setting a value naturally implies resetting the 'off' state to false. + // Emits valueChanged and valueStateChanged signals if required. + // Note setOff and SliderBase::setValue are also available. + void setValueState(double v, bool off = false, ConversionMode mode = ConvertDefault); + + void setMargins(int x, int y); + + int thumbWidthMargin() const { return d_thumbWidthMargin; } + void setThumbWidthMargin(int m) { d_thumbWidthMargin = m; update(); } + int thumbHitLength() const { return d_thumbHitLength; } + void setThumbHitLength(int l) { d_thumbHitLength = l; } + bool autoHideThumb() const { return _autoHideThumb; } + void setAutoHideThumb(bool v) {_autoHideThumb = v; } + + virtual QSize sizeHint() const; +}; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/configmidifilebase.ui muse-3.0.2+ds1/muse/widgets/configmidifilebase.ui --- muse-2.1.2/muse/widgets/configmidifilebase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/configmidifilebase.ui 2018-01-26 21:59:38.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 408 - 537 + 461 + 496
    @@ -16,457 +16,559 @@ true - - - - - - 0 - 0 - - - - Import: - - - - - - Default instrument: - - - - - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - - 164 - 20 - - - - - - - - false - - - Device Name metas trump Port metas if both exist - - - - - - - Instrument Name metas trump Mode sysexes if both exist - - - - - - - Split tracks into parts, or one single part - - - Split tracks into &parts - - - Alt+P - - - - - - - - - Use new-style drum tracks - - - true - - - - - - - - 0 - 0 - - - - Use old-style drum tracks - - - - - - - - - - - - 6 - - + + + + 0 - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - - - &OK - - - - - - true - - - true - - - - - - - &Cancel - - - - - - true - - - - - - - - - - 0 - 0 - - - - Export: - - - - - - - 0 - 0 - - - - Format: - - - false - - - - - - - - 0 - 0 - - - - - 0 (single track) - - - - - 1 (multiple tracks) - - - - - - - - Division: - - - false - - - - - - - - 0 - 0 - - - - - 96 - - - - - 192 - - - - - 384 - - - - - - - - Qt::Horizontal - - - - 59 - 20 - - - - - - - - - 0 - 0 - - - - - 8 - - - - Note: Format 0 uses the FIRST midi track's name/comment in the arranger - - - false - - - - - - - - 0 - 0 - - - - Copyright: - - - false - - - - - - - - - - false - - - Enable extended smf format (currently not implemented) - - - - - - - Use &2-byte time signatures instead of standard 4 - - - Alt+2 - - - - - - - Save space by replacing note-offs with &zero velocity note-ons - - - Alt+Z - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - 6 - - - 0 - + + + Import + + + + + + + + Default instrument: + + + - + 0 0 + + + + + + Qt::Horizontal + + + + 164 + 20 + + + + + + + + + + false + + + Device Name metas trump Port metas if both exist + + + + + + + Instrument Name metas trump Mode sysexes if both exist + + + + + + + Split tracks into parts, or one single part + + + Split tracks into &parts + + + Alt+P + + + + + + + - Mode sysexes + Use new-style drum tracks + + + true - + - + 0 0 - Instrument name metas + Use old-style drum tracks + + + + + + Qt::Vertical + + + + 20 + 245 + + + + + + + + + Export + + + + - + + + + 0 + 0 + + - Both + Format: + + + false - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - 0 - - + 0 0 + + + 0 (single track) + + + + + 1 (multiple tracks) + + + + + + - Port metas + Division: + + + false - + 0 0 + + + 96 + + + + + 192 + + + + + 384 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + + 8 + + + + Note: Format 0 uses the FIRST midi track's name/comment in the arranger + + + false + + + + + + + + + + 0 + 0 + + - Device name metas + Copyright: + + + false + + + + + + + + + + + + false + + + Enable extended smf format (currently not implemented) + + + + + + + Running Status saves space by not + repeating event status bytes. +If this is off every event will have a + status byte, increasing file size. + + + + Use Running &Status + + + Alt+S + + + + + + + To turn a note off some devices can use + note-offs or zero-velocity note-ons or + both. When used with Running Status + this setting saves space. It is safe to + leave this off. + + + Replace note-offs with &zero velocity note-ons + + + Alt+Z + + + + + + + Use &2-byte time signatures instead of standard 4 + + + Alt+2 + + + + + + + If enabled, and a drum track has any drum map + Port, Channel, and ANote overrides, they will be + applied to drum notes and drum note controllers. + +If disabled, drum maps are ignored during export. +All drum notes and drum controllers will be + exported using the track's Port and Channel and + the unmodified note. + +See "What's This ?" for more info. + + + If enabled, and a drum track has any drum map + Port, Channel, and ANote overrides, they will be + applied to drum notes and drum controllers. +Any Port overrides cause a separate track to be + created (requires format 1). Any Channel + overrides will still appear in the same track in + the exported file, but upon re-import into an + app such as MusE, MusE will treat that as a + new track and create one. + +If disabled, drum maps are ignored. +All drum notes and drum controllers will be + exported using the track's Port and Channel + and the unmodified note. Upon re-importing + into another app, it is up to the other app to + redirect the particular drum ports, channels + and notes to make the song sound like it + was intended. + +Drum map overrides are unique to MusE. +If re-importing into MusE, for faithful + reproduction disable this setting and apply + a drum map and some overrides later. +If re-importing or opening in another midi + player, enable this setting so that the player + can have a chance at faithful reproduction. +Some user adjustment may be required + (choice of instruments, ports, etc). + + + Apply drum map Port, Channel, ANote overrides + + + + + + + If enabled, any drum notes or drum controllers + on an applied drum map item in which Channel + is overridden will go to a separate track in the + midi file. + +If disabled, any drum notes or drum controllers + on an applied drum map item in which Channel + is overridden will stay on that track in the midi file. + +See "What's This ?" for more info. + + + If enabled, any drum notes or drum controllers + on an applied drum map item in which Channel + is overridden will go to a separate track in the + midi file. The extra track is automatically + created. In this mode all events on a track in + the midi file are on the same channel. + +If disabled, any drum notes or drum controllers + on an applied drum map item in which Channel + is overridden will stay on that track in the midi file. +In this mode the track events can be on different + channels. But if the file is re-imported into an + app such as MusE, MusE will automatically + create the extra track. + + + + Drum map Channel overrides go to a separate track + + + + + + + Export instrument or mode: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + Instrument name metas - + - Both + Mode sysexes + + + + Qt::Horizontal + + + + 40 + 20 + + + + - - - - - - Export a Port/Device meta for format 0 - - - - +
    + + + + Export port or device metas: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + Port metas + + + + + + + Device name metas + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Export a Port/Device meta for format 0 + + + + + + + Qt::Vertical + + + + 20 + 12 + + + + +
    +
    - - - - Qt::Vertical + + + + 6 - - - 20 - 40 - + + 0 - + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + + + &OK + + + + + + true + + + true + + + + + + + &Cancel + + + + + + true + + + +
    diff -Nru muse-2.1.2/muse/widgets/cpu_toolbar.cpp muse-3.0.2+ds1/muse/widgets/cpu_toolbar.cpp --- muse-2.1.2/muse/widgets/cpu_toolbar.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/cpu_toolbar.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,163 @@ +//========================================================= +// MusE +// Linux Music Editor +// cpu_toolbar.cpp +// (C) Copyright 2016 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 +// 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 +#include +#include +#include + +#include "cpu_toolbar.h" +#include "icons.h" + +namespace MusEGui +{ + +//--------------------------------- +// PaddedValueLabel +//--------------------------------- + +PaddedValueLabel::PaddedValueLabel(bool isFloat, QWidget* parent, Qt::WindowFlags f, + const QString& prefix, const QString& suffix) + :QLabel(parent, f), _isFloat(isFloat), _prefix(prefix), _suffix(suffix) +{ + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + _iVal = 0; + _dVal = 0.0; + _fieldWidth = 2; + _precision = 1; + updateText(); +} + +void PaddedValueLabel::setFieldWidth(int val) +{ + _fieldWidth = val; + if(_fieldWidth < 0) + _fieldWidth = 0; + updateText(); +} + +void PaddedValueLabel::setPrecision(int val) +{ + _precision = val; + if(_precision < 0) + _precision = 0; + updateText(); +} + +void PaddedValueLabel::setIntValue(int val) +{ + _iVal = val; + updateText(); +} + +void PaddedValueLabel::setFloatValue(double val) +{ + _dVal = val; + updateText(); +} + +void PaddedValueLabel::updateText() +{ + if(_isFloat) + setText(QString("%1%L2%3").arg(_prefix).arg(_dVal, 0, 'f', _precision).arg(_suffix)); + else + setText(QString("%1%2%3").arg(_prefix).arg(_iVal).arg(_suffix)); +} + +QSize PaddedValueLabel::sizeHint() const +{ + QString s; + if(_isFloat) + s = QString("%1%L2%3").arg(_prefix).arg(8.8888, _fieldWidth, 'f', _precision, QLatin1Char('8')).arg(_suffix); + else + s = QString("%1%2%3").arg(_prefix).arg(8, _fieldWidth, 10, QLatin1Char('8')).arg(_suffix); + const int w = fontMetrics().width(s); + const int h = QLabel::sizeHint().height(); + return QSize(w, h); +} + +//--------------------------------- +// CpuToolbar +//--------------------------------- + +CpuToolbar::CpuToolbar(QWidget* parent) + : QToolBar(parent) +{ + init(); +} + +CpuToolbar::CpuToolbar(const QString& title, QWidget* parent) + : QToolBar(title, parent) +{ + init(); +} + +void CpuToolbar::init() +{ + setObjectName("CpuLoadToolbar"); +// setToolTip(tr("CPU load averaged over each gui-update period, DSP load read from JACK and finally, number of xruns (reset by clicking)")); +// MusEGlobal::cpuLoadAction = new QWidgetAction(cpuLoadToolbar); +// MusEGlobal::cpuLoadAction->setWhatsThis(tr("Measured CPU load")); +// MusEGlobal::cpuLoadAction->setObjectName("CpuLoadToolbarAction"); +// QToolButton *cpuToolBtn = new QToolButton(this); +// cpuToolBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); +// cpuToolBtn->setText(tr("No CPU load data")); +// cpuToolBtn->setIcon(*cpuIcon); +// cpuToolBtn->setObjectName("CpuLoadToolbarButton"); +// ((QWidgetAction *)MusEGlobal::cpuLoadAction)->setDefaultWidget(cpuToolBtn); +// addAction(MusEGlobal::cpuLoadAction); +// connect(cpuToolBtn, SIGNAL(clicked(bool)), this, SLOT(resetXrunsCounter())); + + _resetButton = new QToolButton(this); + _resetButton->setToolButtonStyle(Qt::ToolButtonIconOnly); + _resetButton->setIcon(*MusEGui::cpuIcon); + _resetButton->setObjectName("CpuLoadToolbarButton"); + _resetButton->setToolTip(tr("CPU load averaged over each gui-update period, DSP load read from JACK and finally, number of xruns (reset by clicking)")); + + _cpuLabel = new PaddedValueLabel(true, this, 0, "CPU:", "%"); + _cpuLabel->setFieldWidth(5); + _cpuLabel->setPrecision(1); + _dspLabel = new PaddedValueLabel(true, this, 0, "DSP:", "%"); + _dspLabel->setFieldWidth(5); + _dspLabel->setPrecision(1); + _xrunsLabel = new PaddedValueLabel(false, this, 0, "XRUNS:"); + _xrunsLabel->setFieldWidth(3); + + setValues(0.0f, 0.0f, 0); + + addWidget(_resetButton); + addWidget(_cpuLabel); + addWidget(_dspLabel); + addWidget(_xrunsLabel); + + connect(_resetButton, SIGNAL(clicked(bool)), SIGNAL(resetClicked())); +} + +void CpuToolbar::setValues(float cpuLoad, float dspLoad, long xRunsCount) +{ + _cpuLabel->setFloatValue(cpuLoad); + _dspLabel->setFloatValue(dspLoad); + _xrunsLabel->setIntValue(xRunsCount); +} + + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/cpu_toolbar.h muse-3.0.2+ds1/muse/widgets/cpu_toolbar.h --- muse-2.1.2/muse/widgets/cpu_toolbar.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/cpu_toolbar.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,101 @@ +//========================================================= +// MusE +// Linux Music Editor +// cpu_toolbar.h +// (C) Copyright 2016 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 +// 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 __CPU_TOOLBAR_H__ +#define __CPU_TOOLBAR_H__ + +#include +#include + +class QWidget; +class QString; +class QToolButton; +class QSize; + +namespace MusEGui +{ + + //--------------------------------- + // PaddedValueLabel + //--------------------------------- + + class PaddedValueLabel : public QLabel + { + Q_OBJECT + + private: + + bool _isFloat; + QString _prefix; + QString _suffix; + + int _fieldWidth; + int _precision; + int _iVal; + double _dVal; + + void updateText(); + + public: + PaddedValueLabel(bool isFloat = false, QWidget* parent = 0, Qt::WindowFlags f = 0, + const QString& prefix = QString(), const QString& suffix = QString()); + + void setFieldWidth(int val); + void setPrecision(int val); + + void setIntValue(int val); + void setFloatValue(double val); + virtual QSize sizeHint() const; + }; + + //--------------------------------- + // CpuToolbar + //--------------------------------- + + class CpuToolbar : public QToolBar + { + Q_OBJECT + + private: + QToolButton* _resetButton; + PaddedValueLabel* _cpuLabel; + PaddedValueLabel* _dspLabel; + PaddedValueLabel* _xrunsLabel; + + void init(); + + public: + CpuToolbar(QWidget* parent = 0); + CpuToolbar(const QString& title, QWidget* parent = 0); + + void setCpuLabelText(const QString&); + void setDspLabelText(const QString&); + void setXrunsLabelText(const QString&); + void setValues(float cpuLoad, float dspLoad, long xRunsCount); + + signals: + void resetClicked(); + }; + +} + +#endif diff -Nru muse-2.1.2/muse/widgets/custom_widget_actions.cpp muse-3.0.2+ds1/muse/widgets/custom_widget_actions.cpp --- muse-2.1.2/muse/widgets/custom_widget_actions.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/custom_widget_actions.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -1,8 +1,9 @@ //============================================================================= // MusE // Linux Music Editor +// // custom_widget_actions.cpp -// (C) Copyright 2011 Tim E. Real (terminator356 on users.sourceforge.net) +// (C) Copyright 2011-2015 Tim E. Real (terminator356 on 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 @@ -23,12 +24,24 @@ #include #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include "icons.h" #include "pixmap_button.h" #include "custom_widget_actions.h" +// For debugging output: Uncomment the fprintf section. +#define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); + namespace MusEGui { //--------------------------------------------------------- @@ -56,22 +69,14 @@ lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); lbl->setAlignment(Qt::AlignCenter); lbl->setAutoFillBackground(true); - //QPalette palette; - //palette.setColor(label->backgroundRole(), c); - //lbl->setPalette(palette); lbl->setBackgroundRole(QPalette::Dark); layout->addWidget(lbl); - layout->addSpacing(8); - //layout->addStretch(); - QSignalMapper* mapper = new QSignalMapper(this); PixmapButton* pb = new PixmapButton(toggle_small_Icon, toggle_small_Icon, 2, lw, QString("T")); // Margin = 2 - //mapper->setMapping(pb, _channels); // Set to one past end. layout->addWidget(pb); layout->addSpacing(6); - //connect(pb, SIGNAL(clicked(bool)), mapper, SLOT(map())); for(int i = 0; i < _channels; ++i) { @@ -88,24 +93,15 @@ return lw; } -void PixmapButtonsHeaderWidgetAction::chanClickMap(int /*idx*/) -{ - // TODO: Toggle vertical columns... p4.0.42 - - trigger(); -} - - //--------------------------------------------------------- // PixmapButtonsWidgetAction //--------------------------------------------------------- -PixmapButtonsWidgetAction::PixmapButtonsWidgetAction(const QString& text, QPixmap* on_pixmap, QPixmap* off_pixmap, int channels, int initial, QWidget* parent) +PixmapButtonsWidgetAction::PixmapButtonsWidgetAction(const QString& text, QPixmap* on_pixmap, QPixmap* off_pixmap, const QBitArray& initial, QWidget* parent) : QWidgetAction(parent) { _onPixmap = on_pixmap; _offPixmap = off_pixmap; - _channels = channels; _current = initial; _text = text; // Just to be safe, set to -1 instead of default 0. @@ -114,6 +110,7 @@ QWidget* PixmapButtonsWidgetAction::createWidget(QWidget *parent) { + const int channels = _current.size(); QWidget* lw = new QWidget(parent); QHBoxLayout* layout = new QHBoxLayout(lw); @@ -121,11 +118,6 @@ QLabel* lbl = new QLabel(_text, lw); lbl->setAlignment(Qt::AlignCenter); - //lbl->setAutoFillBackground(true); - //QPalette palette; - //palette.setColor(label->backgroundRole(), c); - //lbl->setPalette(palette); - //lbl->setBackgroundRole(QPalette::Dark); layout->addWidget(lbl); layout->addSpacing(8); @@ -134,14 +126,14 @@ QSignalMapper* mapper = new QSignalMapper(this); PixmapButton* pb = new PixmapButton(toggle_small_Icon, toggle_small_Icon, 2, lw); // Margin = 2 - mapper->setMapping(pb, _channels); // Set to one past end. + mapper->setMapping(pb, channels); // Set to one past end. layout->addWidget(pb); layout->addSpacing(6); connect(pb, SIGNAL(pressed()), mapper, SLOT(map())); - for(int i = 0; i < _channels; ++i) + for(int i = 0; i < channels; ++i) { - bool set = _current & (1 << i); + bool set = _current.at(i); PixmapButton* b = new PixmapButton(_onPixmap, _offPixmap, 2, lw); // Margin = 2 _chan_buttons.append(b); b->setCheckable(true); @@ -160,37 +152,877 @@ void PixmapButtonsWidgetAction::chanClickMap(int idx) { - if(idx == _channels) // One past end = Toggle all button. + const int channels = _current.size(); + const int buttons_sz = _chan_buttons.size(); + if(idx == channels) // One past end = Toggle all button. { - int allch = (1 << _channels) - 1; - if((_current & allch) == allch) - _current = 0; - else - _current = allch; + int allch = 0; + for(; allch < channels; ++allch) + { + if(!_current.at(allch)) + break; + } + if(allch == channels) + _current.fill(false); + else + _current.fill(true); + // Set and redraw the buttons. - for(int i = 0; i < _channels; ++i) - _chan_buttons.at(i)->setDown(_current != 0); + for(int i = 0; i < buttons_sz; ++i) + _chan_buttons.at(i)->setDown(allch != channels); } else { - int c = 0; - for(int i = 0; i < _channels; ++i) + for(int i = 0; i < channels && i < buttons_sz; ++i) { if(_chan_buttons.at(i)->isChecked()) - c |= (1 << i); + _current.setBit(i); + else + _current.clearBit(i); } - _current = c; } - - trigger(); } -void PixmapButtonsWidgetAction::setCurrentState(int state) +void PixmapButtonsWidgetAction::setCurrentState(const QBitArray& state) { _current = state; + const int channels = _current.size(); + const int buttons_sz = _chan_buttons.size(); // Set and redraw the buttons. - for(int i = 0; i < _channels; ++i) - _chan_buttons.at(i)->setDown((_current & (1 << i)) != 0); + for(int i = 0; i < channels && i < buttons_sz; ++i) + _chan_buttons.at(i)->setDown(_current.at(i)); +} + + +//--------------------------------------------------------- +// RouteChannelArray +//--------------------------------------------------------- + +RouteChannelArray::RouteChannelArray(int cols) +{ + _array = 0; + _headerVisible = true; + _header = 0; + _cols = cols; + _colsExclusive = false; + _exclusiveToggle = false; + _activeCol = -1; + _pressedCol = -1; + init(); +} + +RouteChannelArray::~RouteChannelArray() +{ + if(_header) + { + delete[] _header; + _header = 0; + } + if(_array) + { + delete[] _array; + _array = 0; + } +} + +void RouteChannelArray::init() +{ + if(_header) + { + delete[] _header; + _header = 0; + } + if(_array) + { + delete[] _array; + _array = 0; + } + const int sz = columns(); + if(sz == 0) + return; + _array = new RouteChannelArrayItem[sz]; + _header = new RouteChannelArrayHeaderItem[sz]; +} + +RouteChannelArray& RouteChannelArray::operator=(const RouteChannelArray& a) +{ + if(a._cols != _cols) + { + _cols = a._cols; + init(); + } + _headerVisible = a._headerVisible; + _arrayTitleItem = a._arrayTitleItem; + _headerTitleItem = a._headerTitleItem; + _colsExclusive = a._colsExclusive; + _exclusiveToggle = a._exclusiveToggle; + + const int sz = columns(); + if(sz == 0) + return *this; + for(int i = 0; i < sz; ++i) + { + _array[i] = a._array[i]; + _header[i] = a._header[i]; + } + return *this; +} + +void RouteChannelArray::setColumns(int cols) +{ + if(cols == _cols) + return; + _cols = cols; + init(); +} + +void RouteChannelArray::setValues(int col, bool value, bool exclusive_cols, bool exclusive_toggle) +{ + if(invalidColumn(col)) + return; + + const bool v = (!exclusive_toggle || (exclusive_toggle && value)); + if(exclusive_cols) + { + for(int c = 0; c < _cols; ++c) + _array[c]._value = (c == col && v); + return; + } + + _array[col]._value = value; +} + +MenuItemControlWidget::MenuItemControlWidget(RoutingMatrixWidgetAction* action, QWidget* parent) + : QWidget(parent) +{ + _action = action; + setMouseTracking(true); +} + +void MenuItemControlWidget::elementRect(QRect* checkbox_rect, QRect* label_rect) const +{ + QSize checkbox_sz(0, 0); + + if(_action->hasCheckBox()) + { + QStyle* st = style() ? style() : QApplication::style(); + if(st) + { + QStyleOptionButton option; + option.state = QStyle::State_Active | QStyle::State_Enabled | QStyle::State_HasFocus | + (_action->checkBoxChecked() ? QStyle::State_On : QStyle::State_Off); + checkbox_sz = st->sizeFromContents(QStyle::CT_CheckBox, &option, QSize(0, 0)); //, q); + } + } + + const QFontMetrics txt_fm(_action->font()); + const QSize txt_sz = txt_fm.size(Qt::TextSingleLine, _action->actionText().isEmpty() ? "8" : _action->actionText()); + const int menu_item_h = txt_sz.height() > checkbox_sz.height() ? txt_sz.height() : checkbox_sz.height(); + if(checkbox_rect) + *checkbox_rect = QRect(0, 0, checkbox_sz.width(), menu_item_h); + if(label_rect) + *label_rect = QRect(0, 0, txt_sz.width(), menu_item_h); +} + +QSize MenuItemControlWidget::sizeHint() const +{ + QRect cb_ctrl_rect; + QRect lbl_ctrl_rect; + + elementRect(&cb_ctrl_rect, &lbl_ctrl_rect); + const int cb_w = _action->hasCheckBox() ? (_action->actionHMargin + cb_ctrl_rect.x() + cb_ctrl_rect.width()) : 0; + const int l_w = cb_w + _action->actionHMargin + lbl_ctrl_rect.x() + lbl_ctrl_rect.width(); + + const int cb_h = cb_ctrl_rect.y() + cb_ctrl_rect.height(); + const int l_h = lbl_ctrl_rect.y() + lbl_ctrl_rect.height(); + const int h = l_h > cb_h ? l_h : cb_h; + return QSize(cb_w + l_w, h); +} + +void MenuItemControlWidget::paintEvent(QPaintEvent*) +{ + QPainter p(this); + + QRect cb_ctrl_rect; + QRect lbl_ctrl_rect; + + elementRect(&cb_ctrl_rect, &lbl_ctrl_rect); + + if(_action->isSelected()) + p.fillRect(rect(), palette().highlight()); + + if(_action->hasCheckBox()) + { + QStyle* st = style() ? style() : QApplication::style(); + if(st) + { + QStyleOptionButton option; + option.state = QStyle::State_Active | QStyle::State_HasFocus | + (_action->isEnabled() ? QStyle::State_Enabled : QStyle::State_ReadOnly) | // Or State_None? + (_action->checkBoxChecked() ? QStyle::State_On : QStyle::State_Off) | + (_action->menuItemPressed() ? QStyle::State_Sunken : QStyle::State_None); + option.rect = QRect(_action->actionHMargin + cb_ctrl_rect.x(), + cb_ctrl_rect.y(), + cb_ctrl_rect.width(), + cb_ctrl_rect.height()); + option.palette = palette(); + st->drawControl(QStyle::CE_CheckBox, &option, &p); + } + } + + if(!_action->actionText().isEmpty()) + { + QPalette pal = palette(); + pal.setCurrentColorGroup(_action->isEnabled() ? QPalette::Active : QPalette::Disabled); + p.setPen(_action->isSelected() ? pal.highlightedText().color() : pal.text().color()); + p.setFont(_action->font()); + const int l_x = _action->actionHMargin + (_action->hasCheckBox() ? (_action->actionHMargin + cb_ctrl_rect.x() + cb_ctrl_rect.width()) : 0); + const QRect l_r(l_x, lbl_ctrl_rect.y(), + lbl_ctrl_rect.width(), lbl_ctrl_rect.height()); + p.drawText(l_r, Qt::AlignLeft | Qt::AlignVCenter, _action->actionText()); + } +} + + +//--------------------------------------------------------- +// SwitchBarActionWidget +//--------------------------------------------------------- + +SwitchBarActionWidget::SwitchBarActionWidget(RoutingMatrixWidgetAction* action, QWidget* parent) + : QWidget(parent) +{ + _action = action; + setMouseTracking(true); +} + +QSize SwitchBarActionWidget::sizeHint() const +{ + const int cols = _action->array()->columns(); + const QRect last_array_h_cell = _action->array()->rect(cols - 1); + // Take the height of any horizontal header column - they're all the same. + const int hdr_h = _action->array()->headerVisible() ? _action->array()->headerRect(0).height() : 0; + const int h = hdr_h + + last_array_h_cell.height() + + _action->itemVSpacing + + 2 * _action->margin; + // Take the width of any vertical header row - they're all the same. + const int w = last_array_h_cell.x() + last_array_h_cell.width() + + 2 * _action->margin; + return QSize(w, h); +} + +void SwitchBarActionWidget::paintEvent(QPaintEvent* /*event*/) +{ + QPainter p(this); + const int cols = _action->array()->columns(); + QString a_txt; + const QColor clr1(255, 160, 60); + const QColor clr2 = clr1.darker(150); + + p.setRenderHint(QPainter::Antialiasing, true); + for(int col = 0; col < cols; ++col) + { + const QRect r = _action->array()->rect(col); + if(col == _action->array()->pressedColumn()) + p.fillRect(r, palette().dark()); + else if(col == _action->array()->activeColumn()) + p.fillRect(r, palette().highlight()); + const QPixmap* pm = _action->array()->value(col) ? _action->onPixmap() : _action->offPixmap(); + const int pm_w = pm ? pm->width() : _action->maxPixmapGeometry().width(); + const int pm_h = pm ? pm->height() : _action->maxPixmapGeometry().height(); + + int x; + a_txt = _action->array()->text(col); + if(!a_txt.isEmpty()) + { + p.setFont(_action->font()); + //p.setPen(palette().text().color()); + p.setPen(col == _action->array()->activeColumn() ? palette().highlightedText().color() : palette().text().color()); + p.drawText(r, Qt::AlignLeft | Qt::AlignVCenter, a_txt); + x = r.x() + r.width() - pm_w - _action->groupSpacing; // Leave some space at the end. + } + else + { + x = r.x(); + if(r.width() > pm_w) + x += (r.width() - pm_w) / 2; + } + + int y = r.y(); + if(r.height() > pm_h) + y += (r.height() - pm_h) / 2; + + if(pm) + p.drawPixmap(x, y, *pm); + else + { + QPainterPath path; + path.addRoundedRect(x, y, pm_w, pm_h, 30, 30); + if(_action->array()->value(col)) + { + //QRadialGradient gradient(50, 50, 50, 50, 50); + QLinearGradient gradient(x + 1, y + 1, x + pm_w - 2, y + pm_h - 2); + gradient.setColorAt(0, clr1); + gradient.setColorAt(1, clr2); + QBrush brush(gradient); + + p.fillPath(path, brush); + } + p.setPen(col == _action->array()->activeColumn() ? palette().highlightedText().color() : palette().text().color()); + p.drawPath(path); + } + } + + if(_action->array()->headerVisible()) + { + p.setPen(palette().text().color()); + for(int col = 0; col < cols; ++col) + { + const QRect r = _action->array()->headerRect(col); + const QString str = _action->array()->headerText(col); + if(str.isEmpty()) + { + p.setFont(_action->smallFont()); + p.drawText(r, Qt::AlignCenter, QString::number(col + 1)); + } + else + { + p.setFont(_action->font()); + p.drawText(r, Qt::AlignCenter, str); + } + } + } +} + +//--------------------------------------------------------- +// RoutingMatrixActionWidget +//--------------------------------------------------------- + +RoutingMatrixActionWidget::RoutingMatrixActionWidget(RoutingMatrixWidgetAction* action, QWidget* parent) + : QWidget(parent) +{ + _action = action; + + setMouseTracking(true); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + const int layout_m_l = 0, layout_m_r = 0, layout_m_t = 1, layout_m_b = 1; + + QHBoxLayout* h_layout = new QHBoxLayout(this); + h_layout->setSpacing(0); + h_layout->setContentsMargins(layout_m_l, layout_m_t, layout_m_r, layout_m_b); + + QVBoxLayout* left_v_layout = new QVBoxLayout(); + QVBoxLayout* right_v_layout = new QVBoxLayout(); + left_v_layout->setSpacing(0); + right_v_layout->setSpacing(0); + left_v_layout->setContentsMargins(0, 0, 0, 0); + right_v_layout->setContentsMargins(0, 0, 0, 0); + + if(!_action->array()->headerTitle().isEmpty() || !_action->array()->checkBoxTitle().isEmpty()) + { + QHBoxLayout* left_title_layout = new QHBoxLayout(); + left_title_layout->setSpacing(0); + left_title_layout->setContentsMargins(0, 0, 0, 0); // Zero because we're already inside a layout. + if(!_action->array()->checkBoxTitle().isEmpty()) + { + QLabel* cb_lbl = new QLabel(_action->array()->checkBoxTitle(), parent); + cb_lbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + cb_lbl->setAlignment(Qt::AlignCenter); + cb_lbl->setAutoFillBackground(true); + cb_lbl->setBackgroundRole(QPalette::Dark); + left_title_layout->addWidget(cb_lbl); + left_title_layout->addSpacing(4); + } + if(!_action->array()->headerTitle().isEmpty()) + { + QLabel* hdr_lbl = new QLabel(_action->array()->headerTitle(), parent); + hdr_lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + hdr_lbl->setAlignment(Qt::AlignCenter); + hdr_lbl->setAutoFillBackground(true); + hdr_lbl->setBackgroundRole(QPalette::Dark); + left_title_layout->addWidget(hdr_lbl); + left_title_layout->addSpacing(4); + } + left_v_layout->addLayout(left_title_layout); + } + left_v_layout->addStretch(); + + _menuItemControlWidget = new MenuItemControlWidget(_action, parent); + _menuItemControlWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + left_v_layout->addWidget(_menuItemControlWidget); + + if(_action->array()->arrayTitle().isEmpty()) + { + right_v_layout->addStretch(); + } + else + { + QLabel* lbl = new QLabel(_action->array()->arrayTitle(), parent); + lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + lbl->setAlignment(Qt::AlignCenter); + lbl->setAutoFillBackground(true); + lbl->setBackgroundRole(QPalette::Dark); + right_v_layout->addWidget(lbl); + } + + QHBoxLayout* sw_h_layout = new QHBoxLayout(); + sw_h_layout->setSpacing(0); + sw_h_layout->setContentsMargins(0, 0, 0, 0); + sw_h_layout->addStretch(); + _switchWidget = new SwitchBarActionWidget(_action, parent); + _switchWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + sw_h_layout->addWidget(_switchWidget); + right_v_layout->addLayout(sw_h_layout); + + h_layout->addLayout(left_v_layout); + h_layout->addLayout(right_v_layout); +} + +RoutePopupHit RoutingMatrixActionWidget::hitTest(const QPoint& p, RoutePopupHit::HitTestType test_type) +{ + if(_action->isEnabled()) + { + // The point is relative to this widget. + // Check if we hit the left hand portion (the checkbox and text area). + if(_menuItemControlWidget->geometry().contains(p)) + { + switch(test_type) + { + case RoutePopupHit::HitTestClick: + case RoutePopupHit::HitTestHover: + if(_action->hasCheckBox()) + return RoutePopupHit(_action, RoutePopupHit::HitMenuItem); + break; + } + return RoutePopupHit(_action, RoutePopupHit::HitSpace); + } + + // Check if we hit one of the channel bar channels. + const QPoint cb_p(p.x() - _switchWidget->x(), p.y() - _switchWidget->y()); + const int cols = _action->array()->columns(); + for(int col = 0; col < cols; ++col) + { + const QRect rect = _action->array()->rect(col); + if(rect.contains(cb_p)) + return RoutePopupHit(_action, RoutePopupHit::HitChannel, col); + } + + // Check if we hit the channel bar itself. + if(_switchWidget->geometry().contains(p)) + return RoutePopupHit(_action, RoutePopupHit::HitChannelBar); + + // Check if we hit this widget. + if(rect().contains(p)) + return RoutePopupHit(_action, RoutePopupHit::HitSpace); + } + + return RoutePopupHit(_action, RoutePopupHit::HitNone); +} + +void RoutingMatrixActionWidget::actionEvent(QActionEvent* e) +{ + DEBUG_PRST_ROUTES(stderr, "RoutingMatrixActionWidget::actionEvent\n"); + if(e->type() == QEvent::ActionChanged && e->action() == _action) + { + _menuItemControlWidget->updateGeometry(); + if(layout()) + { + DEBUG_PRST_ROUTES(stderr, " layout...\n"); + //layout()->invalidate(); + //layout()->update(); + layout()->activate(); + } + } + e->ignore(); + QWidget::actionEvent(e); +} + + +//--------------------------------------------------------- +// RoutingMatrixWidgetAction +//--------------------------------------------------------- + +const int RoutingMatrixWidgetAction::margin = 1; +const int RoutingMatrixWidgetAction::itemHSpacing = 1; +const int RoutingMatrixWidgetAction::itemVSpacing = 3; +const int RoutingMatrixWidgetAction::groupSpacing = 4; +const int RoutingMatrixWidgetAction::itemsPerGroup = 4; +const int RoutingMatrixWidgetAction::actionHMargin = 8; + +RoutingMatrixWidgetAction::RoutingMatrixWidgetAction(int cols, + QPixmap* on_pixmap, QPixmap* off_pixmap, + QWidget* parent, const QString& action_text) + : QWidgetAction(parent) +{ + _actionText = action_text; + _hasCheckBox = false; + _checkBoxChecked = false; + _menuItemPressed = false; + _arrayStayOpen = false; + _isSelected = false; + _onPixmap = on_pixmap; + _offPixmap = off_pixmap; + _array.setColumns(cols); + _smallFont = font(); + _smallFont.setPointSize(_smallFont.pointSize() / 2 + 1); + + if(_onPixmap) + { + if(_maxPixmapGeometry.width() < _onPixmap->width()) + _maxPixmapGeometry.setWidth(_onPixmap->width()); + if(_maxPixmapGeometry.height() < _onPixmap->height()) + _maxPixmapGeometry.setHeight(_onPixmap->height()); + } + if(_offPixmap) + { + if(_maxPixmapGeometry.width() < _offPixmap->width()) + _maxPixmapGeometry.setWidth(_offPixmap->width()); + if(_maxPixmapGeometry.height() < _offPixmap->height()) + _maxPixmapGeometry.setHeight(_offPixmap->height()); + } + // No pixmaps? Set the size for manually painting. + if(!_onPixmap && !_offPixmap) + _maxPixmapGeometry = QSize(10, 10); + + updateChannelArray(); +} + +void RoutingMatrixWidgetAction::updateChannelArray() +{ + const int cols = _array.columns(); + const int cellW = _maxPixmapGeometry.width(); + const int cellH = _maxPixmapGeometry.height(); + + // Determine the maximum horizontal header height. + int max_h_hdr_h = 0; + if(_array.headerVisible()) + { + for(int col = 0; col < cols; ++col) + { + const QString str = _array.headerText(col); + const QFontMetrics fm(str.isEmpty() ? smallFont() : font()); + const QRect brect = fm.boundingRect(str.isEmpty() ? "8" : str); + const int r_h = brect.height(); + if(r_h > max_h_hdr_h) + max_h_hdr_h = r_h; + } + + // Set the horizontal header rectangles. + int x = margin + itemHSpacing; + int y = margin; + for(int col = 0; col < cols; ++col) + { + if(col != 0 && ((col % itemsPerGroup) == 0)) + x += groupSpacing; + const QString str = _array.headerText(col); + const QFontMetrics fm(str.isEmpty() ? smallFont() : font()); + const QRect brect = fm.boundingRect(str.isEmpty() ? "888" : str); + int r_w = brect.width(); + if(r_w < cellW) + r_w = cellW; + const int cell_w = r_w + itemHSpacing; + const QRect r(x, y, cell_w, max_h_hdr_h); + _array.headerSetRect(col, r); + x += cell_w; + } + } + + // Set the array rectangles. + int y = margin + max_h_hdr_h + itemVSpacing; + int x = margin + itemHSpacing; + const QFontMetrics a_fm(font()); + for(int col = 0; col < cols; ++col) + { + if(col != 0 && ((col % itemsPerGroup) == 0)) + x += groupSpacing; + const int hdr_w = _array.headerRect(col).width(); + int txt_w = 0; + int txt_h = 0; + if(!_array.text(col).isEmpty()) + { + const QRect b_rect = a_fm.boundingRect(_array.text(col)); + txt_w = b_rect.width() + margin + groupSpacing; // Add a wee bit of space. + txt_h = b_rect.height(); + } + int cell_w = txt_w + cellW; + if(hdr_w > cell_w) + cell_w = hdr_w; + const int cell_h = txt_h > cellH ? txt_h : cellH; + + const QRect r(x, y, cell_w, cell_h); + _array.setRect(col, r); + x += cell_w; + } +} + +void RoutingMatrixWidgetAction::updateCreatedWidgets() +{ + const int sz = createdWidgets().size(); + for(int i = 0; i < sz; ++i) + createdWidgets().at(i)->update(); +} + +QWidget* RoutingMatrixWidgetAction::createWidget(QWidget *parent) +{ + RoutingMatrixActionWidget* lw = new RoutingMatrixActionWidget(this, parent); + return lw; +} + +void RoutingMatrixWidgetAction::sendActionChanged() +{ + QActionEvent e(QEvent::ActionChanged, this); + + // This code copied and modified from QActionPrivate::sendDataChanged() source. + // Using QAction::setText() calls QActionPrivate::sendDataChanged() which doesn't + // send to the created widgets so fails to resize properly on text changes. + + // Update the created widgets first. + for (int i = 0; i < createdWidgets().size(); ++i) { + QWidget *w = createdWidgets().at(i); + //DEBUG_PRST_ROUTES(stderr, "RoutingMatrixWidgetAction::sendActionChanged created widget:%s\n", w->metaObject()->className()); + qApp->sendEvent(w, &e); + } + + // Now update the associated widgets and graphics widgets (popup menus, widgets etc. containing this action)... + +#ifndef QT_NO_GRAPHICSVIEW + for (int i = 0; i < associatedGraphicsWidgets().size(); ++i) { + //DEBUG_PRST_ROUTES(stderr, "RoutingMatrixWidgetAction::sendActionChanged associated graphics widget\n"); + QGraphicsWidget *w = associatedGraphicsWidgets().at(i); + qApp->sendEvent(w, &e); + } +#endif + + for (int i = 0; i < associatedWidgets().size(); ++i) { + QWidget *w = associatedWidgets().at(i); + //DEBUG_PRST_ROUTES(stderr, "RoutingMatrixWidgetAction::sendActionChanged associated widget:%s \n", w->metaObject()->className()); + qApp->sendEvent(w, &e); + } + emit changed(); +} + +void RoutingMatrixWidgetAction::setActionText(const QString& s) +{ + //DEBUG_PRST_ROUTES(stderr, "RoutingMatrixWidgetAction::setActionText\n"); + _actionText = s; + sendActionChanged(); +} + +RoutePopupHit RoutingMatrixWidgetAction::hitTest(const QPoint& p, RoutePopupHit::HitTestType test_type) +{ + for(int i = 0; i < createdWidgets().size(); ++i) + { + QWidget* w = createdWidgets().at(i); + if(RoutingMatrixActionWidget* maw = qobject_cast< RoutingMatrixActionWidget* >(w)) + { + // The point is relative to the menu. Translate the point to reference our created container widget. + RoutePopupHit hit = maw->hitTest(QPoint(p.x() - maw->x(), p.y() - maw->y()), test_type); + if(hit._type != RoutePopupHit::HitNone) + return hit; + } + } + return RoutePopupHit(this, RoutePopupHit::HitNone); +} + +RoutePopupHit RoutingMatrixWidgetAction::previousHit(const RoutePopupHit& fromHit) +{ + RoutePopupHit retHit = fromHit; + retHit._action = this; + const int cols = array()->columns(); + switch(fromHit._type) + { + case RoutePopupHit::HitChannel: + { + if(cols == 0) + { + if(hasCheckBox()) + { + retHit._type = RoutePopupHit::HitMenuItem; + retHit._value = 0; + } + } + else + { + int col = fromHit._value; // The column. + if(col > cols) // Greater than. Let the decrement work. + col = cols; + --col; + if(col == -1) + { + if(hasCheckBox()) + { + retHit._type = RoutePopupHit::HitMenuItem; + retHit._value = 0; + } + else + col = cols - 1; // Wrap around. + } + if(col != -1) + retHit._value = col; // Adjust the current 'last' column setting. + } + } + break; + + case RoutePopupHit::HitMenuItem: + { + if(cols != 0) + { + const int col = cols - 1; // Wrap around. + retHit._type = RoutePopupHit::HitChannel; + retHit._value = col; + } + } + break; + + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + case RoutePopupHit::HitNone: + // If it has a checkbox (or there is no channel bar) select the checkbox/text area. + if(hasCheckBox() || array()->columns() == 0) + { + retHit._type = RoutePopupHit::HitMenuItem; + retHit._value = 0; + } + // Otherwise select the first available channel bar column. + else + { + retHit._type = RoutePopupHit::HitChannel; + retHit._value = 0; + } + break; + } + + return retHit; +} + +RoutePopupHit RoutingMatrixWidgetAction::nextHit(const RoutePopupHit& fromHit) +{ + RoutePopupHit retHit = fromHit; + const int cols = array()->columns(); + switch(fromHit._type) + { + case RoutePopupHit::HitChannel: + { + if(cols == 0) + { + if(hasCheckBox()) + { + retHit._type = RoutePopupHit::HitMenuItem; + retHit._action = this; + retHit._value = 0; + } + } + else + { + int col = fromHit._value; // The column. + ++col; + if(col >= cols) + { + if(hasCheckBox()) + { + retHit._type = RoutePopupHit::HitMenuItem; + retHit._action = this; + retHit._value = 0; + col = -1; + } + else + col = 0; // Wrap around. + } + if(col != -1) + retHit._value = col; // Adjust the current 'last' column setting. + } + } + break; + + case RoutePopupHit::HitMenuItem: + { + if(cols != 0) + { + const int col = 0; // Select the first available channel. + retHit._type = RoutePopupHit::HitChannel; + retHit._action = this; + retHit._value = col; + } + } + break; + + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + case RoutePopupHit::HitNone: + // If it has a checkbox (or there is no channel bar) select the checkbox/text area. + if(hasCheckBox() || array()->columns() == 0) + { + retHit._type = RoutePopupHit::HitMenuItem; + retHit._action = this; + retHit._value = 0; + } + // Otherwise select the first available channel bar column. + else + { + retHit._type = RoutePopupHit::HitChannel; + retHit._action = this; + retHit._value = 0; + } + break; + } + + return retHit; +} + + +//--------------------------------------------------------- +// RoutingMatrixHeaderWidgetAction +//--------------------------------------------------------- + +RoutingMatrixHeaderWidgetAction::RoutingMatrixHeaderWidgetAction(const QString& checkbox_label, const QString& item_label, const QString& array_label, QWidget* parent) + : QWidgetAction(parent), _checkBoxLabel(checkbox_label), _itemLabel(item_label), _arrayLabel(array_label) +{ + setEnabled(false); +} + +QWidget* RoutingMatrixHeaderWidgetAction::createWidget(QWidget *parent) +{ + QWidget* lw = new QWidget(parent); + lw->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + QHBoxLayout* h_layout = new QHBoxLayout(lw); + h_layout->setSpacing(0); + h_layout->setContentsMargins(0, 0, 0, 0); + + if(!_checkBoxLabel.isEmpty()) + { + QLabel* lbl = new QLabel(_checkBoxLabel, parent); + lbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + lbl->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + lbl->setAutoFillBackground(true); + lbl->setBackgroundRole(QPalette::Dark); + h_layout->addWidget(lbl); + } + + if(!_itemLabel.isEmpty()) + { + QLabel* lbl = new QLabel(_itemLabel, parent); + lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + lbl->setAlignment(Qt::AlignCenter); + lbl->setAutoFillBackground(true); + lbl->setBackgroundRole(QPalette::Dark); + h_layout->addSpacing(4); + h_layout->addWidget(lbl); + } + + if(!_arrayLabel.isEmpty()) + { + QLabel* lbl = new QLabel(_arrayLabel, parent); + lbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + lbl->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + lbl->setAutoFillBackground(true); + lbl->setBackgroundRole(QPalette::Dark); + h_layout->addSpacing(4); + h_layout->addWidget(lbl); + } + + return lw; } } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/custom_widget_actions.h muse-3.0.2+ds1/muse/widgets/custom_widget_actions.h --- muse-2.1.2/muse/widgets/custom_widget_actions.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/custom_widget_actions.h 2017-12-04 21:01:19.000000000 +0000 @@ -1,8 +1,9 @@ //========================================================= // MusE // Linux Music Editor +// // custom_widget_actions.h -// (C) Copyright 2011 Tim E. Real (terminator356 on users.sourceforge.net) +// (C) Copyright 2011-2015 Tim E. Real (terminator356 on 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 @@ -23,12 +24,24 @@ #ifndef __CUSTOM_WIDGET_ACTIONS_H__ #define __CUSTOM_WIDGET_ACTIONS_H__ +#include #include +#include #include +#include + +#include "route.h" class QMouseEvent; +class QPainter; class QPaintEvent; class QPixmap; +class QString; +class QResizeEvent; +class QMouseEvent; +class QEnterEvent; +class QLeaveEvent; +class QSize; namespace MusEGui { @@ -46,13 +59,11 @@ QString _text; int _channels; - private slots: - void chanClickMap(int); - public: PixmapButtonsHeaderWidgetAction (const QString& text, QPixmap* ref_pixmap, int channels, QWidget* parent = 0); QWidget* createWidget(QWidget* parent); }; + //--------------------------------------------------------- // PixmapButtonsWidgetAction //--------------------------------------------------------- @@ -62,8 +73,7 @@ private: QString _text; - int _channels; - int _current; + QBitArray _current; QPixmap* _onPixmap; QPixmap* _offPixmap; QList _chan_buttons; @@ -74,13 +84,368 @@ public: PixmapButtonsWidgetAction(const QString& text, QPixmap* on_pixmap, QPixmap* off_pixmap, - int channels, int initial, + const QBitArray& initial, QWidget* parent = 0); QWidget* createWidget(QWidget* parent); - int currentState() const { return _current; } - void setCurrentState(int state); + QBitArray currentState() const { return _current; } + void setCurrentState(const QBitArray& state); }; + +//--------------------------------------------------------- +// RoutingMatrixWidgetAction +//--------------------------------------------------------- + +struct RouteChannelArrayItem +{ + // The switch's value. + bool _value; + // Accompanying text beside the switch. + QString _text; + // Rectangle of the swich portion. + QRect _rect; + RouteChannelArrayItem() { _value = false; } +}; + +struct RouteChannelArrayHeaderItem +{ + // The header item text. + QString _text; + // Rectangle of the header item text. + QRect _rect; +}; + +class RouteChannelArray +{ + private: + // The number of columns. + int _cols; + // Whether the columns are exclusive. + bool _colsExclusive; + // Whether toggling a column is allowed even when columns are exclusive. + bool _exclusiveToggle; + // Whether the header items above the array of switches are visible. + bool _headerVisible; + // Current column the mouse is over. -1 == none. + int _activeCol; + // Current column being pressed. -1 == none. + int _pressedCol; + // The array of switches. + RouteChannelArrayItem* _array; + // Header items above the array of switches. + RouteChannelArrayHeaderItem* _header; + // Title header item above the checkbox portion. + RouteChannelArrayHeaderItem _checkBoxTitleItem; + // Title header item above the item text portion. + RouteChannelArrayHeaderItem _headerTitleItem; + // Title header item above the switch array portion. + RouteChannelArrayHeaderItem _arrayTitleItem; + + void init(); + + public: + RouteChannelArray(int cols = 0); + virtual ~RouteChannelArray(); + RouteChannelArray& operator=(const RouteChannelArray&); + // Returns the number of columns in the array. + int columns() const { return _cols; } + // Sets the number of columns in the array. + void setColumns(int cols); + // Whether the given column number is out of range. + bool invalidColumn(int col) const { return col < 0 || col >= _cols; } + + // Whether the columns are exclusive. + bool columnsExclusive() const { return _colsExclusive; } + // Sets whether the columns are exclusive. + void setColumnsExclusive(bool v) { _colsExclusive = v; } + // Whether toggling a column is allowed even when columns are exclusive. + bool exclusiveToggle() const { return _exclusiveToggle; } + // Sets whether toggling a column is allowed even when columns are exclusive. + void setExclusiveToggle(bool v) { _exclusiveToggle = v; } + // Current column mouse is over. -1 == none. + int activeColumn() const { return _activeCol; } + // Set current column mouse is over. -1 == none. + void setActiveColumn(int col) + { if((col == -1 || !invalidColumn(col)) && _activeCol != col) _activeCol = col; } + // Current column being pressed. -1 == none. + int pressedColumn() const { return _pressedCol; } + // Set current column being pressed. -1 == none. + // Returns true if any channel was changed (for redrawing). + bool setPressedColumn(int col) + { if((col != -1 && invalidColumn(col)) || _pressedCol == col) return false; _pressedCol = col; return true; } + QString checkBoxTitle() const + { return _checkBoxTitleItem._text; } + void setCheckBoxTitle(const QString& str) + { _checkBoxTitleItem._text = str; } + QRect checkBoxTitleRect() const + { return _checkBoxTitleItem._rect; } + void setCheckBoxTitleRect(const QRect& r) + { _checkBoxTitleItem._rect = r; } + + void setValues(int col, bool value, bool exclusive_cols = false, bool exclusive_toggle = false); + bool value(int col) const + { if(invalidColumn(col)) return false; return _array[col]._value; } + void setValue(int col, bool value) + { setValues(col, value, _colsExclusive, _exclusiveToggle); } + QRect rect(int col) const + { if(invalidColumn(col)) return QRect(); return _array[col]._rect; } + void setRect(int col, const QRect& r) + { if(invalidColumn(col)) return; _array[col]._rect = r; } + QString text(int col) const + { if(invalidColumn(col)) return QString(); return _array[col]._text; } + void setText(int col, const QString& s) + { if(invalidColumn(col)) return; _array[col]._text = s; } + QString arrayTitle() const + { return _arrayTitleItem._text; } + void setArrayTitle(const QString& str) + { _arrayTitleItem._text = str; } + QRect arrayTitleRect() const + { return _arrayTitleItem._rect; } + void setArrayTitleRect(const QRect& r) + { _arrayTitleItem._rect = r; } + + bool headerVisible() const + { return _headerVisible; } + void headerSetVisible(bool v) + { _headerVisible = v; } + QRect headerRect(int col) const + { if(invalidColumn(col)) return QRect(); return _header[col]._rect; } + void headerSetRect(int col, const QRect& rect) + { if(invalidColumn(col)) return; _header[col]._rect = rect; } + QString headerText(int col) const + { if(invalidColumn(col)) return QString(); return _header[col]._text; } + void headerSetText(int col, const QString& str) + { if(invalidColumn(col)) return; _header[col]._text = str; } + QString headerTitle() const + { return _headerTitleItem._text; } + void headerSetTitle(const QString& str) + { _headerTitleItem._text = str; } + QRect headerTitleRect() const + { return _headerTitleItem._rect; } + void headerSetTitleRect(const QRect& r) + { _headerTitleItem._rect = r; } +}; + +//--------------------------------------------------------- +// RoutePopupHit +// Structure for action hit tests +//--------------------------------------------------------- + +struct RoutePopupHit +{ + enum HitTestType { HitTestHover, HitTestClick }; + enum HitType { HitNone, HitSpace, HitMenuItem, HitChannelBar, HitChannel }; + HitType _type; + QAction* _action; // Action where the hit occurred. + int _value; // Channel number for HitChannel. + + RoutePopupHit() { _action = 0; _type = HitNone; _value = 0; } + RoutePopupHit(QAction* action, HitType ht, int val = 0) { _action = action; _type = ht; _value = val; } + bool operator==(const RoutePopupHit& hit) { return _action == hit._action && _type == hit._type && _value == hit._value; } +}; + + +class RoutingMatrixActionWidget; +class RoutingMatrixWidgetAction; + +//--------------------------------------------------------- +// MenuItemControlWidget +// The checkable menu item-like portion of the custom widget action, with text portion as well +//--------------------------------------------------------- + +class MenuItemControlWidget : public QWidget +{ + Q_OBJECT + + private: + RoutingMatrixWidgetAction* _action; + + protected: + QSize sizeHint() const; + void paintEvent(QPaintEvent*); + + public: + MenuItemControlWidget(RoutingMatrixWidgetAction* action, QWidget* parent = 0); + // Returns, in the passed rectangle pointers, the rectangles of the checkbox, and/or item text label. + void elementRect(QRect* checkbox_rect = 0, QRect* label_rect = 0) const; +}; + +//--------------------------------------------------------- +// SwitchBarActionWidget +// The switch bar portion of the custom widget action +//--------------------------------------------------------- + +class SwitchBarActionWidget : public QWidget { + Q_OBJECT + + private: + RoutingMatrixWidgetAction* _action; + + protected: + QSize sizeHint() const; + void paintEvent(QPaintEvent*); + + public: + SwitchBarActionWidget(RoutingMatrixWidgetAction* action, QWidget* parent = 0); +}; + +//--------------------------------------------------------- +// RoutingMatrixActionWidget +// Container widget holds checkable menu item-like widget with text portion, +// and switch bar widget, for the custom widget action +//--------------------------------------------------------- + +class RoutingMatrixActionWidget : public QWidget +{ + Q_OBJECT + + private: + RoutingMatrixWidgetAction* _action; + // The part containing a checkbox, and the item text, together. + MenuItemControlWidget* _menuItemControlWidget; + // The array of switches. + SwitchBarActionWidget* _switchWidget; + + protected: + // For resizing properly on text changes. + void actionEvent(QActionEvent*); + + public: + RoutingMatrixActionWidget(RoutingMatrixWidgetAction* action, QWidget* parent = 0); + // Performs a hit test on the contents of the widget at the given point, according to the requested hit test type. + RoutePopupHit hitTest(const QPoint&, RoutePopupHit::HitTestType); +}; + +//--------------------------------------------------------- +// RoutingMatrixWidgetAction +// The custom widget action +//--------------------------------------------------------- + +class RoutingMatrixWidgetAction : public QWidgetAction +{ + Q_OBJECT + private: + // The switch (channel) array. + RouteChannelArray _array; + // Pixmap used for 'on' indicator. + QPixmap* _onPixmap; + // Pixmap used for 'off' indicator. + QPixmap* _offPixmap; + // A smaller font (about half size) than the action's font. + QFont _smallFont; + // Maximum dimensions of the on/off pixmaps. + QSize _maxPixmapGeometry; + // NOTE: _hasCheckBox is used instead of QAction::isCheckable()/setCheckable(). + bool _hasCheckBox; + // Whether the checkbox is currently checked or not. + bool _checkBoxChecked; + // Whether the label and checkbox area is currently pressed or not. + bool _menuItemPressed; + // Whether clicking the array closes the menu or not. + bool _arrayStayOpen; + // Whether the action is highlighted (hovered) or not. + bool _isSelected; + // NOTE: _actionText is used instead of QAction::text()/setText(). + QString _actionText; + + protected: + // Override + QWidget* createWidget(QWidget* parent); + // Sends ActionChanged events to created and associated widgets. Emits changed(). + // For resizing properly on text changes. + void sendActionChanged(); + + public: + static const int margin; + static const int itemHSpacing; + static const int itemVSpacing; + static const int groupSpacing; + static const int itemsPerGroup; + static const int actionHMargin; // Empty area left and right of checkbox and text label. + + RoutingMatrixWidgetAction(int cols, + QPixmap* on_pixmap, QPixmap* off_pixmap, + QWidget* parent = 0, const QString& action_text = QString()); + + // Access to the switch (channel) array. + RouteChannelArray* array() { return &_array; } + // Updates the structure and/or cached rectangles of the channel array. + // When array header text is changed this should also be called. + void updateChannelArray(); + // Updates (redraws) created widgets. + void updateCreatedWidgets(); + + // A smaller font (about half size) than the action's font. + QFont smallFont() const { return _smallFont; } + // Pixmap used for 'on' indicator. + QPixmap* onPixmap() const { return _onPixmap; } + // Pixmap used for 'off' indicator. + QPixmap* offPixmap() const { return _offPixmap; } + // Maximum dimensions of the on/off pixmaps. + QSize maxPixmapGeometry() const { return _maxPixmapGeometry; } + + // NOTE: Use hasCheckBox() instead of QAction::isCheckable(). + bool hasCheckBox() const { return _hasCheckBox; } + // NOTE: Use setHasCheckBox() instead of QAction::setCheckable(). + void setHasCheckBox(bool v) { _hasCheckBox = v; } + + // Whether the checkbox is currently checked or not. + bool checkBoxChecked() const { return _checkBoxChecked; } + // Sets whether the checkbox is currently checked or not. + void setCheckBoxChecked(bool v) { _checkBoxChecked = v; } + + // Whether the label and checkbox area is currently pressed or not. + bool menuItemPressed() const { return _menuItemPressed; } + // Sets whether the label and checkbox area is currently pressed or not. + // Returns true if the menu item section was changed (for redrawing). + bool setMenuItemPressed(bool v) { if(_menuItemPressed == v) return false; _menuItemPressed = v; return true; } + + // Whether clicking the array closes the menu or not. + bool arrayStayOpen() const { return _arrayStayOpen; } + // Sets whether clicking the array closes the menu or not. + void setArrayStayOpen(bool v) { _arrayStayOpen = v; } + + // Whether the action is highlighted (hovered) or not. + bool isSelected() const { return _isSelected; } + // Sets whether the action is highlighted (hovered) or not. + void setSelected(bool v) { _isSelected = v; } + + // NOTE: Use setActionText() instead of QAction::setText(). + void setActionText(const QString& s); + // NOTE: Use actionText() instead of QAction::text(). + QString actionText() const { return _actionText; } + //QString actionText() const { return text(); } + + // Does a hit test of type HitTestType, returning a RoutePopupHit structure describing what was hit. + RoutePopupHit hitTest(const QPoint&, RoutePopupHit::HitTestType); + // Returns the previous hittable item (ie. for left key movement). + RoutePopupHit previousHit(const RoutePopupHit& fromHit); + // Returns the next hittable item (ie. for right key movement). + RoutePopupHit nextHit(const RoutePopupHit& fromHit); +}; + +//--------------------------------------------------------- +// RoutingMatrixHeaderWidgetAction +// A header action suitable for non-RoutingMatrixWidgetAction items. +// NOTE: This is a separate action which can be used to head regular QAction +// items instead of the RoutingMatrixWidgetAction's own array headers. +//--------------------------------------------------------- + +class RoutingMatrixHeaderWidgetAction : public QWidgetAction { + Q_OBJECT + private: + QString _checkBoxLabel; + QString _itemLabel; + QString _arrayLabel; + + protected: + QWidget* createWidget(QWidget* parent); + + public: + RoutingMatrixHeaderWidgetAction(const QString& checkbox_label, const QString& item_label, const QString& array_label, QWidget* parent = 0); + }; + } // namespace MusEGui + #endif // __CUSTOM_WIDGET_ACTIONS_H__ diff -Nru muse-2.1.2/muse/widgets/dentry.cpp muse-3.0.2+ds1/muse/widgets/dentry.cpp --- muse-2.1.2/muse/widgets/dentry.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/dentry.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -43,19 +43,18 @@ // lineedit double values //--------------------------------------------------------- -Dentry::Dentry(QWidget* parent, const char* name) : QLineEdit(parent) +Dentry::Dentry(QWidget* parent, const char* name) : LineEdit(parent) { setObjectName(name); _slider = 0; _id = -1; - drawFrame = false; - QLineEdit::setFrame(drawFrame); + //setAutoFillBackground(true); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(repeat())); val = 0.01; connect(this, SIGNAL(returnPressed()), SLOT(endEdit())); setCursor(QCursor(Qt::ArrowCursor)); - evx = 1.0; + evx = 1; } //--------------------------------------------------------- @@ -68,17 +67,6 @@ } //--------------------------------------------------------- -// setFrame -//--------------------------------------------------------- - -void Dentry::setFrame(bool flag) - { - drawFrame = flag; - QLineEdit::setFrame(drawFrame); - update(); - } - -//--------------------------------------------------------- // endEdit //--------------------------------------------------------- @@ -91,9 +79,6 @@ } } setString(val); - clearFocus(); - if (!drawFrame) - QLineEdit::setFrame(false); } //--------------------------------------------------------- @@ -102,9 +87,25 @@ void Dentry::mousePressEvent(QMouseEvent* event) { - button = event->button(); + const Qt::MouseButton m_button = event->button(); + const Qt::MouseButtons m_buttons = event->buttons(); + + event->accept(); + + // Only one mouse button at a time! Otherwise bad things happen. + if(m_buttons ^ m_button) + { + button = Qt::NoButton; + timer->stop(); + return; + } + + if(m_button == Qt::LeftButton) + LineEdit::mousePressEvent(event); + + button = m_button; starty = event->y(); - evx = double(event->x()); + evx = event->x(); timecount = 0; repeat(); timer->start(TIMER1); @@ -116,19 +117,6 @@ void Dentry::wheelEvent(QWheelEvent* event) { - // Avoid unwanted wheel events from outside the control. - // Tested: No go, can't seem to determine where event came from. - /* - const QPoint gp = mapToGlobal(event->pos()); - const QRect gr = QRect(mapToGlobal(rect().topLeft()), mapToGlobal(rect().bottomRight())); - if(!gr.contains(gp)) - */ - //if(sender() != this) - //{ - // event->ignore(); - // return; - //} - event->accept(); int delta = event->delta(); @@ -138,14 +126,14 @@ if(_slider) _slider->stepPages(-1); else - decValue(-1.0); + decValue(1); } else if (delta > 0) { if(_slider) _slider->stepPages(1); else - incValue(1.0); + incValue(1); } } @@ -197,48 +185,100 @@ // mouseReleaseEvent //--------------------------------------------------------- -void Dentry::mouseReleaseEvent(QMouseEvent*) +void Dentry::mouseReleaseEvent(QMouseEvent* ev) { + ev->accept(); + // Don't call ancestor to avoid middle button pasting. + //LineEdit::mouseReleaseEvent(ev); + button = Qt::NoButton; timer->stop(); } //--------------------------------------------------------- -// mouseMoveEvent -//--------------------------------------------------------- - -void Dentry::mouseMoveEvent(QMouseEvent*) - { - switch (button) { - case Qt::LeftButton: - break; - case Qt::MidButton: - break; - case Qt::RightButton: - break; - default: - break; - } - } - -//--------------------------------------------------------- // mouseDoubleClickEvent //--------------------------------------------------------- void Dentry::mouseDoubleClickEvent(QMouseEvent* event) { + event->accept(); if (event->button() != Qt::LeftButton) { - mousePressEvent(event); + //mousePressEvent(event); + button = event->button(); + starty = event->y(); + evx = event->x(); + timecount = 0; + repeat(); + timer->start(TIMER1); return; } - setFocus(); - QLineEdit::setFrame(true); update(); emit doubleClicked(_id); if(event->modifiers() & Qt::ControlModifier) emit ctrlDoubleClicked(_id); + else + LineEdit::mouseDoubleClickEvent(event); } +void Dentry::keyPressEvent(QKeyEvent* e) +{ + bool inc = true; + switch (e->key()) + { + case Qt::Key_Up: + inc = true; + break; + + case Qt::Key_Down: + inc = false; + break; + + default: + // Let ancestor handle it. + e->ignore(); + LineEdit::keyPressEvent(e); + return; + break; + } + + if(e->modifiers() & (Qt::AltModifier | Qt::MetaModifier | Qt::ControlModifier)) + { + // Let ancestor handle it. + e->ignore(); + LineEdit::keyPressEvent(e); + return; + } + + e->accept(); + // Do not allow setting value from the external while mouse is pressed. + //if(_pressed) + // return; + + const bool shift = e->modifiers() == Qt::ShiftModifier; + int val = 1; + if(shift) + val *= 10; + + if(inc) + { + if(_slider) + _slider->stepPages(val); + else + incValue(val); + } + else + { + if(_slider) + _slider->stepPages(-val); + else + decValue(val); + } + + // Show a handy tooltip value box. + //if(d_enableValueToolTips) + // showValueToolTip(e->globalPos()); +} + //--------------------------------------------------------- // setValue //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/widgets/dentry.h muse-3.0.2+ds1/muse/widgets/dentry.h --- muse-2.1.2/muse/widgets/dentry.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/dentry.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,44 +23,51 @@ #ifndef __DENTRY_H__ #define __DENTRY_H__ -#include +#include "line_edit.h" #include "sliderbase.h" +class QWidget; +class QWheelEvent; +class QMouseEvent; +class QContextMenuEvent; +class QKeyEvent; +class QString; +class QTimer; + namespace MusEGui { //--------------------------------------------------------- // Dentry //--------------------------------------------------------- -class Dentry : public QLineEdit { +class Dentry : public LineEdit { Q_OBJECT Q_PROPERTY( int id READ id WRITE setId ) Q_PROPERTY( double value READ value WRITE setValue ) - Q_PROPERTY( bool frame READ frame WRITE setFrame ) SliderBase* _slider; int button; int starty; - bool drawFrame; QTimer* timer; - double evx; + int evx; int timecount; + protected: + int _id; + double val; + virtual void wheelEvent(QWheelEvent*); virtual void mousePressEvent(QMouseEvent*); - virtual void mouseMoveEvent(QMouseEvent*); virtual void mouseDoubleClickEvent(QMouseEvent*); virtual void mouseReleaseEvent(QMouseEvent*); - void contextMenuEvent(QContextMenuEvent*); + virtual void contextMenuEvent(QContextMenuEvent*); - protected: - int _id; - double val; + virtual void keyPressEvent(QKeyEvent*); - virtual void incValue(double x) = 0; - virtual void decValue(double x) = 0; + virtual void incValue(int steps = 1) = 0; + virtual void decValue(int steps = 1) = 0; virtual bool setString(double) = 0; virtual bool setSValue(const QString&) = 0; @@ -81,8 +88,6 @@ public: Dentry(QWidget*, const char* name=0); double value() const { return val; } - bool frame() const { return drawFrame; } - void setFrame(bool); int id() const { return _id; } void setId(int i) { _id = i; } SliderBase* slider() const { return _slider; } diff -Nru muse-2.1.2/muse/widgets/didyouknow.h muse-3.0.2+ds1/muse/widgets/didyouknow.h --- muse-2.1.2/muse/widgets/didyouknow.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/didyouknow.h 2017-12-04 21:01:19.000000000 +0000 @@ -35,6 +35,7 @@ Q_OBJECT int currTip; + bool showTeaser; public: QStringList tipList; @@ -45,6 +46,7 @@ tipText->setForegroundRole(QPalette::Foreground); tipText->setOpenExternalLinks(true); currTip=0; + showTeaser=false; connect(nextButton,SIGNAL(clicked()),SLOT(nextTip())); } @@ -54,8 +56,21 @@ if (currTip > tipList.size()-1){ currTip=0; } - tipText->setText(tipList[currTip]); - currTip++; + if (currTip==5 && !showTeaser) + { + tipText->setText("Still not started playing?"); + showTeaser=true; + } + else if (currTip==10 && !showTeaser) + { + tipText->setText("What are you waiting for? Make music! :)"); + showTeaser=true; + } + else { + tipText->setText(tipList[currTip]); + currTip++; + showTeaser=false; + } } void show() { diff -Nru muse-2.1.2/muse/widgets/didyouknow.ui muse-3.0.2+ds1/muse/widgets/didyouknow.ui --- muse-2.1.2/muse/widgets/didyouknow.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/didyouknow.ui 2017-12-04 21:01:19.000000000 +0000 @@ -16,421 +16,6 @@ - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 127 - 127 - 127 - - - - - - - 170 - 170 - 170 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 127 - 127 - 127 - - - - - - - 170 - 170 - 170 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - - - - 127 - 127 - 127 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 127 - 127 - 127 - - - - - - - 170 - 170 - 170 - - - - - - - 127 - 127 - 127 - - - - - - - 255 - 255 - 255 - - - - - - - 127 - 127 - 127 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 255 - - - - - - - 0 - 0 - 0 - - - - - - - 255 - 255 - 255 - - - - - - - 255 - 255 - 220 - - - - - - - 0 - 0 - 0 - - - - - - QFrame::Panel diff -Nru muse-2.1.2/muse/widgets/dimap.cpp muse-3.0.2+ds1/muse/widgets/dimap.cpp --- muse-2.1.2/muse/widgets/dimap.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/dimap.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -165,7 +165,7 @@ d_x2 = log(d2); } else { - d_log = FALSE; + d_log = false; d_x1 = d1; d_x2 = d2; } diff -Nru muse-2.1.2/muse/widgets/dimap.h muse-3.0.2+ds1/muse/widgets/dimap.h --- muse-2.1.2/muse/widgets/dimap.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/dimap.h 2017-12-04 21:01:19.000000000 +0000 @@ -43,7 +43,7 @@ static const double LogMax; DiMap(); - DiMap(int, int, double, double, bool lg = FALSE); + DiMap(int, int, double, double, bool lg = false); ~DiMap(); @@ -51,7 +51,7 @@ bool contains(int x) const; void setIntRange(int i1, int i2); - void setDblRange(double d1, double d2, bool lg = FALSE); + void setDblRange(double d1, double d2, bool lg = false); int transform(double x) const; double invTransform(int i) const; diff -Nru muse-2.1.2/muse/widgets/doublelabel.cpp muse-3.0.2+ds1/muse/widgets/doublelabel.cpp --- muse-2.1.2/muse/widgets/doublelabel.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/doublelabel.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -135,11 +135,11 @@ // incValue //--------------------------------------------------------- -void DoubleLabel::incValue(double) +void DoubleLabel::incValue(int steps) { if(val >= max) return; - double inc = calcIncrement(); + double inc = calcIncrement() * double(steps); if(val + inc >= max) setValue(max); else @@ -151,11 +151,11 @@ // decValue //--------------------------------------------------------- -void DoubleLabel::decValue(double) +void DoubleLabel::decValue(int steps) { if(val <= min) return; - double inc = calcIncrement(); + double inc = calcIncrement() * double(steps); if(val - inc <= min) setValue(min); else @@ -170,6 +170,7 @@ void DoubleLabel::setPrecision(int v) { _precision = v; + updateGeometry(); setString(val); } @@ -180,9 +181,9 @@ QSize DoubleLabel::sizeHint() const { QFontMetrics fm = fontMetrics(); - int h = fm.height() + 5; + int h = fm.height() + 9; int n = _precision; - + ++n; // For some reason I have to add one digit. Shouldn't have to. double aval = fmax(fabs(max), fabs(min)); if (aval >= 10.0) @@ -195,7 +196,7 @@ ++n; if (aval >= 100000.0) ++n; - + int w = fm.width(QString("-0.")) + fm.width('0') * n + 6; if(!_suffix.isEmpty()) { diff -Nru muse-2.1.2/muse/widgets/doublelabel.h muse-3.0.2+ds1/muse/widgets/doublelabel.h --- muse-2.1.2/muse/widgets/doublelabel.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/doublelabel.h 2017-12-04 21:01:19.000000000 +0000 @@ -50,8 +50,8 @@ virtual bool setSValue(const QString&); virtual bool setString(double val); - virtual void incValue(double); - virtual void decValue(double); + virtual void incValue(int steps = 1); + virtual void decValue(int steps = 1); public: DoubleLabel(QWidget* parent = 0, const char* name = 0); @@ -61,19 +61,19 @@ double minValue() const { return min; } double maxValue() const { return max; } double off() const { return _off; } - void setMinValue(double v) { min = v; } - void setMaxValue(double v) { max = v; } - void setRange(double a, double b) { _off = a - (min - _off); min = a; max = b; } + void setMinValue(double v) { min = v; updateGeometry(); } + void setMaxValue(double v) { max = v; updateGeometry(); } + void setRange(double a, double b) { _off = a - (min - _off); min = a; max = b; updateGeometry(); } void setOff(double v); int precision() const { return _precision; } void setPrecision(int val); QString specialText() const { return _specialText; } void setSpecialText(const QString& s) { _specialText = s; - update(); + updateGeometry(); } QString suffix() const { return _suffix; } - void setSuffix(const QString& s) { _suffix = s; } + void setSuffix(const QString& s) { _suffix = s; updateGeometry(); } }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/drange.cpp muse-3.0.2+ds1/muse/widgets/drange.cpp --- muse-2.1.2/muse/widgets/drange.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/drange.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -5,6 +5,7 @@ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -28,6 +29,12 @@ #include "mmath.h" #include "drange.h" +#include "fastlog.h" +#include "muse_math.h" + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_DRANGE(dev, format, args...) // fprintf(dev, format, ##args); + namespace MusEGui { @@ -57,24 +64,98 @@ { d_minValue = 0; d_maxValue = 100.0; - d_prevValue = 0.0; d_exactPrevValue = 0.0; d_exactValue = 0.0; d_value = 0.0; d_step = 0.1; - d_periodic = FALSE; + d_periodic = false; + d_log = false; + d_integer = false; } //--------------------------------------------------------- +// convertTo +//--------------------------------------------------------- + +double DoubleRange::convertTo(double x, ConversionMode mode) const +{ + switch(mode) + { + case ConvertNone: + return x; + + case ConvertDefault: + if(d_log) + return muse_db2val(x); + if(d_integer) + return rint(x); + return x; + break; + + case ConvertInt: + return rint(x); + break; + + case ConvertLog: + return muse_db2val(x); + break; + } + return x; +} + +//--------------------------------------------------------- +// convertTo +//--------------------------------------------------------- + +double DoubleRange::convertFrom(double x, ConversionMode mode) const +{ + switch(mode) + { + case ConvertNone: + return x; + + case ConvertDefault: + if(d_log) + { + if(x == 0.0f) + return d_minValue; + else + return MusECore::fast_log10(x) * 20.0f; + } + else if(d_integer) + return rint(x); + else + return x; + break; + + case ConvertInt: + return rint(x); + break; + + case ConvertLog: + if(x == 0.0f) + x = d_minValue; + else + x = MusECore::fast_log10(x) * 20.0f; + break; + } + return x; +} + +//--------------------------------------------------------- // setNewValue //--------------------------------------------------------- void DoubleRange::setNewValue(double x, bool align) { - d_prevValue = d_value; - - double vmin = MusECore::qwtMin(d_minValue, d_maxValue); - double vmax = MusECore::qwtMax(d_minValue, d_maxValue); + DEBUG_DRANGE(stderr, "DoubleRange::setNewValue TOP val:%.20f d_value:%.20f\n", x, d_value); + + if(x == d_value) + return; + + const double vmin = MusECore::qwtMin(d_minValue, d_maxValue); + const double vmax = MusECore::qwtMax(d_minValue, d_maxValue); + const double prevValue = d_value; // Range check @@ -113,9 +194,13 @@ if (fabs(d_value) < MinEps * MusECore::qwtAbs(d_step)) d_value = 0.0; } - if (d_prevValue != d_value) + DEBUG_DRANGE(stderr, " BOTTOM val:%.20f d_value:%.20f\n", x, d_value); + if (prevValue != d_value) + { + DEBUG_DRANGE(stderr, " not equal, calling valueChange\n"); valueChange(); } + } //--------------------------------------------------------- // fitValue @@ -126,9 +211,9 @@ // be mapped to a point in the interval such that //--------------------------------------------------------- -void DoubleRange::fitValue(double x) +void DoubleRange::fitValue(double x, ConversionMode mode) { - setNewValue(x, true); + setNewValue(convertFrom(x, mode), true); } //--------------------------------------------------------- @@ -143,9 +228,9 @@ // with an integer number n. //--------------------------------------------------------- -void DoubleRange::setValue(double x) +void DoubleRange::setValue(double x, ConversionMode mode) { - setNewValue(x, false); + setNewValue(convertFrom(x, mode), false); } //--------------------------------------------------------- @@ -161,8 +246,10 @@ // to a better one. //--------------------------------------------------------- -void DoubleRange::setRange(double vmin, double vmax, double vstep, int pageSize) +void DoubleRange::setRange(double vmin, double vmax, double vstep, int pageSize, ConversionMode mode) { + vmin = convertFrom(vmin, mode); + vmax = convertFrom(vmax, mode); bool rchg = ((d_maxValue != vmax) || (d_minValue != vmin)); if(!rchg && vstep == d_step && pageSize == d_pageSize) // p4.0.45 @@ -223,6 +310,7 @@ if (newStep != d_step) { d_step = newStep; + DEBUG_DRANGE(stderr, "DoubleRange::setStep vstep:%.20f d_step:%.20f\n", vstep, d_step); stepChange(); } } @@ -279,4 +367,79 @@ return MusECore::qwtAbs(d_step); } +//--------------------------------------------------------- +// value +//--------------------------------------------------------- + +double DoubleRange::value(ConversionMode mode) const + { + return convertTo(d_value, mode); + } + +//--------------------------------------------------------- +// minLogValue +//--------------------------------------------------------- + +//double AbstractSlider::minValue() const { +// return _log ? pow(10.0, _minValue*0.05f) : _minValue; +//} + +//--------------------------------------------------------- +// setMinLogValue +//--------------------------------------------------------- + +void DoubleRange::setMinLogValue(double val) { + if (d_log) { + if (val == 0.0f) d_minValue = -100; + else d_minValue = MusECore::fast_log10(val) * 20.0f; + } + else d_minValue = val; +} + +//--------------------------------------------------------- +// maxLogValue +//--------------------------------------------------------- + +//double AbstractSlider::maxValue() const { +// return _log ? pow(10.0, _maxValue*0.05f) : _maxValue; +//} + +//--------------------------------------------------------- +// setMaxLogValue +//--------------------------------------------------------- + +void DoubleRange::setMaxLogValue(double val) +{ + if (d_log) + { + d_maxValue = MusECore::fast_log10(val) * 20.0f; + } + else d_maxValue = val; +} + +void DoubleRange::setLogRange(double a, double b, double vstep, int pagesize) +{ +// setMinLogValue(a); +// setMaxLogValue(b); + + double mn = 0, mx = 0; + if(d_log) + { + if(a == 0.0f) + d_minValue = -100; + else + mn = MusECore::fast_log10(a) * 20.0f; + } + else + mn = a; + + if(d_log) + mx = MusECore::fast_log10(b) * 20.0f; + else + mx = b; + + setRange(mn, mx, vstep, pagesize); +} + + } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/drange.h muse-3.0.2+ds1/muse/widgets/drange.h --- muse-2.1.2/muse/widgets/drange.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/drange.h 2017-12-04 21:01:19.000000000 +0000 @@ -5,6 +5,7 @@ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -44,39 +45,51 @@ double d_value; double d_exactValue; double d_exactPrevValue; - double d_prevValue; bool d_periodic; + bool d_log; + bool d_integer; void setNewValue(double x, bool align = false); + public: + enum ConversionMode { ConvertNone, ConvertDefault, ConvertInt, ConvertLog }; + protected: - double exactValue() const { return d_exactValue; } - double exactPrevValue() const { return d_exactPrevValue; } - double prevValue() const { return d_prevValue; } + double convertFrom(double x, ConversionMode mode = ConvertDefault) const; + double convertTo(double x, ConversionMode mode = ConvertDefault) const; + double exactValue(ConversionMode mode = ConvertDefault) const { return convertTo(d_exactValue, mode); } + double exactPrevValue(ConversionMode mode = ConvertDefault) const { return convertTo(d_exactPrevValue, mode); } virtual void valueChange() {} virtual void stepChange() {} virtual void rangeChange() {} public: DoubleRange(); - virtual ~DoubleRange(){}; + virtual ~DoubleRange(){} - double value() const { return d_value; } - virtual void setValue(double); + double value(ConversionMode mode = ConvertDefault) const; + virtual void setValue(double x, ConversionMode mode = ConvertDefault); - virtual void fitValue(double); + virtual void fitValue(double x, ConversionMode mode = ConvertDefault); virtual void incValue(int); virtual void incPages(int); void setPeriodic(bool tf); - void setRange(double vmin, double vmax, double vstep = 0.0, - int pagesize = 1); + void setRange (double vmin, double vmax, double vstep = 0.0, int pagesize = 1, ConversionMode mode = ConvertDefault); + void setLogRange(double vmin, double vmax, double vstep = 0.0, int pagesize = 1); void setStep(double); - double maxValue() const { return d_maxValue; } - double minValue() const { return d_minValue; } + double maxValue(ConversionMode mode = ConvertDefault) const { return convertTo(d_maxValue, mode); } + void setMaxLogValue(double v); + double minValue(ConversionMode mode = ConvertDefault) const { return convertTo(d_minValue, mode); } + void setMinLogValue(double v); bool periodic() const { return d_periodic; } int pageSize() const { return d_pageSize; } double step() const; + + bool log() const { return d_log; } + void setLog(bool v) { d_log = v; } + bool integer() const { return d_integer; } + void setInteger(bool v) { d_integer = v; } }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/editctrlbase.ui muse-3.0.2+ds1/muse/widgets/editctrlbase.ui --- muse-2.1.2/muse/widgets/editctrlbase.ui 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/editctrlbase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,845 @@ + + + EditCtrlBase + + + + 0 + 0 + 601 + 413 + + + + MusE: Edit Controller Event + + + true + + + + + + Time Position + + + false + + + + + + + Available Controller: + + + false + + + + + + + Create New Controller + + + + + + true + + + true + + + + + + + + + + + + + + + 0 + 0 + 0 + + + + + + + 238 + 234 + 222 + + + + + + + 255 + 255 + 255 + + + + + + + 246 + 244 + 238 + + + + + + + 119 + 117 + 111 + + + + + + + 158 + 155 + 147 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 104 + 137 + 236 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 128 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + 238 + 234 + 222 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 119 + 117 + 111 + + + + + + + 158 + 155 + 147 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 104 + 137 + 236 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 128 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 192 + + + + + + + 128 + 0 + 128 + + + + + + + + + 128 + 128 + 128 + + + + + + + 238 + 234 + 222 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + 119 + 117 + 111 + + + + + + + 158 + 155 + 147 + + + + + + + 128 + 128 + 128 + + + + + + + 255 + 255 + 255 + + + + + + + 128 + 128 + 128 + + + + + + + 255 + 255 + 255 + + + + + + + 104 + 137 + 236 + + + + + + + 0 + 0 + 0 + + + + + + + 0 + 0 + 128 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 192 + + + + + + + 128 + 0 + 128 + + + + + + + + 0 + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 40 + + + + + + + + Controller + + + false + + + + + + + QFrame::Sunken + + + textLabel3 + + + false + + + + + + + Note + + + false + + + + + + + 127 + + + + + + + Value + + + false + + + + + + + 127 + + + + + + + 127 + + + Qt::Horizontal + + + + + + + + + + + H-Bank + + + false + + + + + + + L-Bank + + + false + + + + + + + Program + + + false + + + + + + + off + + + 0 + + + 128 + + + 0 + + + + + + + off + + + 0 + + + 128 + + + 0 + + + + + + + 1 + + + 128 + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 140 + + + + + + + + pushButton4 + + + + + + + + + + + 6 + + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + + + &OK + + + + + + true + + + true + + + + + + + &Cancel + + + + + + true + + + + + + + + + QFrame::HLine + + + QFrame::Raised + + + 3 + + + 2 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 60 + 20 + + + + + + + + QFrame::VLine + + + QFrame::Raised + + + 3 + + + 2 + + + + + + + + + Awl::PosEdit + QWidget +
    awl/posedit.h
    +
    +
    + + + + valSlider + valueChanged(int) + valSpinBox + setValue(int) + + + 20 + 20 + + + 20 + 20 + + + + + valSpinBox + valueChanged(int) + valSlider + setValue(int) + + + 20 + 20 + + + 20 + 20 + + + + +
    diff -Nru muse-2.1.2/muse/widgets/editevent.cpp muse-3.0.2+ds1/muse/widgets/editevent.cpp --- muse-2.1.2/muse/widgets/editevent.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/editevent.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,990 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: editevent.cpp,v 1.12.2.6 2009/02/02 21:38:00 terminator356 Exp $ +// (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "awl/posedit.h" + +#include "song.h" +#include "event.h" +#include "midictrl.h" +#include "editevent.h" +#include "pitchedit.h" +#include "intlabel.h" +#include "globals.h" +#include "gconfig.h" +#include "midiport.h" +#include "midiedit/drummap.h" +#include "instruments/minstrument.h" +#include "midi.h" +#include "popupmenu.h" +#include "choose_sysex.h" + +namespace MusEGui { + +//--------------------------------------------------------- +// string2qhex +//--------------------------------------------------------- + +QString string2hex(const unsigned char* data, int len) + { + QString d; + for (int i = 0; i < len; ++i) { + if ((i > 0) && ((i % 8)==0)) { + d += "\n"; + } + else if (i) + d += " "; + // Strip all f0 and f7 (whether accidental or on purpose enclosing etc). + if(data[i] == MusECore::ME_SYSEX || data[i] == MusECore::ME_SYSEX_END) + continue; + d += QString("%1").arg(data[i], 2, 16, QLatin1Char('0')); + } + return d; + } + +//--------------------------------------------------------- +// hex2string +//--------------------------------------------------------- + +char* hex2string(QWidget* parent, const char* src, int& len, bool warn = true) + { + char buffer[2048]; + char* dst = buffer; + + while (*src) { + while (*src == ' ' || *src == '\n') + ++src; + char* ep; + long val = strtol(src, &ep, 16); + if (ep == src) { + if(warn) + QMessageBox::information(parent, + QString("MusE"), + QWidget::tr("Cannot convert sysex string")); + return 0; + } + src = ep; + // Strip all f0 and f7 (whether accidental or on purpose enclosing etc). + if(val == MusECore::ME_SYSEX || val == MusECore::ME_SYSEX_END) + continue; + *dst++ = val; + if (dst - buffer >= 2048) { + if(warn) + QMessageBox::information(parent, + QString("MusE"), + QWidget::tr("Hex String too long (2048 bytes limit)")); + return 0; + } + } + len = dst - buffer; + if(len == 0) + return 0; + char* b = new char[len+1]; + memcpy(b, buffer, len); + b[len] = 0; + return b; + } + +//--------------------------------------------------------- +// getEvent +//--------------------------------------------------------- + +MusECore::Event EditNoteDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) + { + EditNoteDialog* dlg = new EditNoteDialog(tick, event, parent); + MusECore::Event nevent; + if (dlg->exec() == QDialog::Accepted) { + nevent = dlg->getEvent(); + } + delete dlg; + return nevent; + } + +MusECore::Event EditSysexDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent, MusECore::MidiInstrument* instr) + { + EditSysexDialog* dlg = new EditSysexDialog(tick, event, parent, instr); + MusECore::Event nevent; + if (dlg->exec() == QDialog::Accepted) { + nevent = dlg->getEvent(); + } + delete dlg; + return nevent; + } + +MusECore::Event EditMetaDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) + { + EditEventDialog* dlg = new EditMetaDialog(tick, event, parent); + MusECore::Event nevent; + if (dlg->exec() == QDialog::Accepted) { + nevent = dlg->getEvent(); + } + delete dlg; + return nevent; + } + +//--------------------------------------------------------- +// EditEventDialog +//--------------------------------------------------------- + +EditEventDialog::EditEventDialog(QWidget* parent) + : QDialog(parent) + { + QVBoxLayout* xlayout = new QVBoxLayout; + layout1 = new QGridLayout; // ddskrjo this + xlayout->addLayout(layout1); + + //--------------------------------------------------- + // Ok, Cancel + //--------------------------------------------------- + + QBoxLayout* w5 = new QHBoxLayout; // ddskrjo this + QPushButton* okB = new QPushButton(tr("Ok")); + okB->setDefault(true); + QPushButton* cancelB = new QPushButton(tr("Cancel")); + okB->setFixedWidth(80); + cancelB->setFixedWidth(80); + w5->addWidget(okB); + w5->addSpacing(12); + w5->addWidget(cancelB); + w5->addStretch(1); + xlayout->addLayout(w5); + setLayout(xlayout); + connect(cancelB, SIGNAL(clicked()), SLOT(reject())); + connect(okB, SIGNAL(clicked()), SLOT(accept())); + } + +//--------------------------------------------------------- +// EditNoteDialog +//--------------------------------------------------------- + +EditNoteDialog::EditNoteDialog(int tick, const MusECore::Event& event, + QWidget* parent) + : QDialog(parent) + { + setupUi(this); + if (!event.empty()) { + epos->setValue(tick); + il1->setValue(event.lenTick()); + pl->setValue(event.pitch()); + il2->setValue(event.velo()); + il3->setValue(event.veloOff()); + } + else { + epos->setValue(tick); + il1->setValue(96); + pl->setValue(64); + il2->setValue(100); + il3->setValue(0); + } + } + +//--------------------------------------------------------- +// EditNoteDialog::event +//--------------------------------------------------------- + +MusECore::Event EditNoteDialog::getEvent() + { + MusECore::Event event(MusECore::Note); + event.setTick(epos->pos().tick()); + event.setA(pl->value()); + event.setB(il2->value()); + event.setC(il3->value()); + event.setLenTick(il1->value()); + return event; + } + +//--------------------------------------------------------- +// EditSysExDialog +//--------------------------------------------------------- + +EditSysexDialog::EditSysexDialog(int tick, const MusECore::Event& event, + QWidget* parent, MusECore::MidiInstrument* instr) + : QDialog(parent) + { + setupUi(this); + sysex = 0; + _instr = instr; + if (!event.empty()) { + epos->setValue(tick); + edit->setText(string2hex(event.data(), event.dataLen())); + if(_instr) + { + typeLabel->setText(MusECore::nameSysex(event.dataLen(), event.data(), _instr)); + commentLabel->setText(MusECore::sysexComment(event.dataLen(), event.data(), _instr)); + } + } + else { + epos->setValue(tick); + } + connect(edit, SIGNAL(textChanged()), SLOT(editChanged())); + connect(buttonSelect, SIGNAL(clicked(bool)), SLOT(selectSysex())); + } + +//--------------------------------------------------------- +// ~EditSysexDialog +//--------------------------------------------------------- + +EditSysexDialog::~EditSysexDialog() + { + if (sysex) + delete sysex; + } + +//--------------------------------------------------------- +// EditSysExDialog::event +//--------------------------------------------------------- + +MusECore::Event EditSysexDialog::getEvent() + { + MusECore::Event event(MusECore::Sysex); + event.setTick(epos->pos().tick()); + event.setData(sysex, len); + return event; + } + +//--------------------------------------------------------- +// accept +//--------------------------------------------------------- + +void EditSysexDialog::accept() + { + QString qsrc = edit->toPlainText(); + QByteArray ba = qsrc.toLatin1(); + const char* src = ba.constData(); + + sysex = (unsigned char*)hex2string(this, src, len); + if (sysex) + QDialog::accept(); + } + +//--------------------------------------------------------- +// editChanged +//--------------------------------------------------------- + +void EditSysexDialog::editChanged() +{ + if(!_instr) + return; + + QString qsrc = edit->toPlainText(); + QByteArray ba = qsrc.toLatin1(); + const char* src = ba.constData(); + + int l; + unsigned char* data = (unsigned char*)hex2string(this, src, l, false); // false = Don't warn with popups + if(data && l > 0) + { + typeLabel->setText(MusECore::nameSysex(l, data, _instr)); + commentLabel->setText(MusECore::sysexComment(l, data, _instr)); + } + else + { + typeLabel->clear(); + commentLabel->clear(); + } +} + +//--------------------------------------------------------- +// selectSysex +//--------------------------------------------------------- + +void EditSysexDialog::selectSysex() +{ + ChooseSysexDialog* dlg = new ChooseSysexDialog(this, _instr); + if(dlg->exec() == QDialog::Accepted) + { + MusECore::SysEx* s = dlg->sysex(); + if(s) + { + edit->setText(string2hex(s->data, s->dataLen)); + typeLabel->setText(s->name); + commentLabel->setText(s->comment); + } + } + delete dlg; +} + +//--------------------------------------------------------- +// EditMetaDialog +//--------------------------------------------------------- + +EditMetaDialog::EditMetaDialog(int tick, const MusECore::Event& ev, + QWidget* parent) + : EditEventDialog(parent) + { + meta = 0; + setWindowTitle(tr("MusE: Enter Meta Event")); + + QLabel* l1 = new QLabel(tr("Time Position")); + ///epos = new PosEdit; + epos = new Awl::PosEdit; + + QLabel* l2 = new QLabel(tr("Meta Type")); + il2 = new MusEGui::IntLabel(-1, 0, 127, this, -1); + il2->setFixedWidth(100); + il2->setFrame(true); + il2->setDark(); + typeLabel = new QLabel; + typeLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + QHBoxLayout* typeLayout = new QHBoxLayout; + typeLayout->addWidget(il2); + typeLayout->addWidget(typeLabel); + typeLayout->addStretch(); + + hexButton = new QRadioButton(tr("Enter Hex")); + hexButton->setChecked(true); + connect(hexButton, SIGNAL(toggled(bool)), SLOT(toggled(bool))); + + edit = new QTextEdit; + edit->setFont(MusEGlobal::config.fonts[0]); + + if (!ev.empty()) { + epos->setValue(tick); + il2->setValue(ev.dataA()); + toggled(true); + edit->setText(string2hex(ev.data(), ev.dataLen())); + } + else { + epos->setValue(tick); + il2->setValue(0); + } + + typeChanged(il2->value()); + connect(il2, SIGNAL(valueChanged(int)), SLOT(typeChanged(int))); + + layout1->addWidget(l1, 0, 0); + layout1->addWidget(epos, 0, 1, Qt::AlignLeft); + layout1->addWidget(l2, 1, 0); + + //layout1->addWidget(il2, 1, 1, AlignLeft); + layout1->addLayout(typeLayout, 1, 1); + + //layout1->addMultiCellWidget(hexButton, 2, 2, 0, 1); + //layout1->addMultiCellWidget(edit, 3, 3, 0, 1); + layout1->addWidget(hexButton, 2, 0, 1, 2); + layout1->addWidget(edit, 3, 0, 1, 2); + } + +//--------------------------------------------------------- +// typeChanged +//--------------------------------------------------------- + +void EditMetaDialog::typeChanged(int val) +{ + typeLabel->setText(MusECore::midiMetaName(val)); +} + +//--------------------------------------------------------- +// toggled +//--------------------------------------------------------- + +void EditMetaDialog::toggled(bool flag) + { + QString qsrc = edit->toPlainText(); + QByteArray ba = qsrc.toLatin1(); + const char* src = ba.constData(); + edit->clear(); + + QString dst; + if (flag) { // convert to hex + dst = string2hex((unsigned char*)src, ba.length()); + } + else { // convert to string + int len; + dst = hex2string(this, src, len); + } + edit->setText(dst); + } + +//--------------------------------------------------------- +// ~EditMetaDialog +//--------------------------------------------------------- + +EditMetaDialog::~EditMetaDialog() + { + if (meta) + delete meta; + } + +//--------------------------------------------------------- +// EditMetaDialog::event +//--------------------------------------------------------- + +MusECore::Event EditMetaDialog::getEvent() + { + MusECore::Event event(MusECore::Meta); + event.setTick(epos->pos().tick()); + event.setA(il2->value()); + event.setData(meta, len); // TODO ?? + return event; + } + +//--------------------------------------------------------- +// accept +//--------------------------------------------------------- + +void EditMetaDialog::accept() + { + QString qsrc = edit->toPlainText(); + QByteArray ba = qsrc.toLatin1(); + const char* src = ba.constData(); + if (!hexButton->isChecked()) { + meta = (unsigned char*)strdup(src); + len = ba.length(); + QDialog::accept(); + return; + } + + meta = (unsigned char*)hex2string(this, src, len); + if (meta) + QDialog::accept(); + } + +//--------------------------------------------------------- +// getEvent +//--------------------------------------------------------- + +MusECore::Event EditCtrlDialog::getEvent(int tick, const MusECore::Event& event, + const MusECore::MidiPart* part, QWidget* parent) + { + EditCtrlDialog* dlg = new EditCtrlDialog(tick, event, part, parent); + MusECore::Event nevent; + if (dlg->exec() == QDialog::Accepted) { + nevent = dlg->getEvent(); + } + delete dlg; + return nevent; + } + +//--------------------------------------------------------- +// EditCtrlDialog::event +//--------------------------------------------------------- + +MusECore::Event EditCtrlDialog::getEvent() + { + MusECore::Event event(MusECore::Controller); + event.setTick(timePos->pos().tick()); + + int cnum = 0; + QListWidgetItem* item = ctrlList->currentItem(); + if(item != 0) + cnum = item->data(Qt::UserRole).toInt(); + + MusECore::MidiTrack* track = part->track(); + bool isDrum = track->type() == MusECore::Track::DRUM; + MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; + int channel = track->outChannel(); + bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; + + int evnum = cnum; + int num = cnum; + if((cnum & 0xff) == 0xff) + { + evnum = (cnum & ~0xff) | (noteSpinBox->value() & 0x7f); + num = evnum; + if(isDrum) + { + MusECore::DrumMap* dm = &MusEGlobal::drumMap[noteSpinBox->value() & 0x7f]; + num = (cnum & ~0xff) | dm->anote; + // Default to track port if -1 and track channel if -1. + if(dm->port != -1) + port = &MusEGlobal::midiPorts[dm->port]; + if(dm->channel != -1) + channel = dm->channel; + } + else if(isNewDrum) + { + MusECore::DrumMap* dm = &track->drummap()[noteSpinBox->value() & 0x7f]; + num = (cnum & ~0xff) | dm->anote; + // Default to track port if -1 and track channel if -1. + if(dm->port != -1) + port = &MusEGlobal::midiPorts[dm->port]; + if(dm->channel != -1) + channel = dm->channel; + } + } + + MusECore::MidiController* c = port->midiController(cnum); + MusECore::MidiCtrlValListList* cll = port->controller(); + + if(cll->find(channel, num) == cll->end()) + { + MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); + cll->add(channel, vl); + } + + event.setA(evnum); + if(cnum == MusECore::CTRL_PROGRAM) + { + int hb = hbank->value(); + int lb = lbank->value(); + int prog = program->value(); + if (hb > 0 && hb < 129) + hb -= 1; + else + hb = 0xff; + if (lb > 0 && lb < 129) + lb -= 1; + else + lb = 0xff; + if (prog > 0 && prog < 129) + prog -= 1; + else + prog = 0xff; + int val = (hb << 16) + (lb << 8) + prog; + event.setB(val); + } + else + event.setB(valSlider->value() + c->bias()); + return event; + } + +//--------------------------------------------------------- +// EditCtrlDialog +// PosEdit* timePos; +// QSlider* valSlider; +// QSpinBox* valSpinBox; +// QLabel* controllerName; +// QListWidget* ctrlList; +// QPushButton* buttonNewController; +//--------------------------------------------------------- + +struct CI { + int num; + QString s; + bool used; + bool off; + bool instrument; + CI(int n, const QString& ss, bool u, bool o, bool i) : num(n), s(ss), used(u), off(o), instrument(i) {} + }; + +EditCtrlDialog::EditCtrlDialog(int tick, const MusECore::Event& event, + const MusECore::MidiPart* p, QWidget* parent) + : QDialog(parent), part(p) + { + setupUi(this); + widgetStack->setAutoFillBackground(true); + + MusECore::MidiTrack* track = part->track(); + MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; + bool isDrum = track->type() == MusECore::Track::DRUM; + bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; + bool isMidi = track->type() == MusECore::Track::MIDI; + MusECore::MidiCtrlValListList* cll = port->controller(); + int channel = track->outChannel(); + MusECore::MidiInstrument* instr = port->instrument(); + MusECore::MidiControllerList* mcl = instr->controller(); + int val = 0; + int ev_num = 0; + int num = 0; + int ev_cnum = 0; + int ev_note = -1; + if (!event.empty()) { + ev_num = event.dataA(); + num = ev_num; + ev_cnum = ev_num; + val = event.dataB(); + if(port->drumController(ev_num)) + { + ev_cnum |= 0xff; + if(isDrum) + num = (ev_num & ~0xff) | MusEGlobal::drumMap[ev_num & 0xff].anote; + else if(isNewDrum) + num = (ev_num & ~0xff) | track->drummap()[ev_num & 0xff].anote; + + ev_note = ev_num & 0xff; + } + } + + MusECore::MidiController* mc = port->midiController(ev_num); + + ctrlList->clear(); + ctrlList->setSelectionMode(QAbstractItemView::SingleSelection); + + //--------------------------------------------------- + // build list of midi controllers for current + // MusECore::MidiPort/channel + //--------------------------------------------------- + + std::list sList; + typedef std::list::iterator isList; + std::set already_added_nums; + + for (MusECore::iMidiCtrlValList it = cll->begin(); it != cll->end(); ++it) { + MusECore::MidiCtrlValList* cl = it->second; + int ch = it->first >> 24; + if(ch != channel) + continue; + MusECore::MidiController* c = port->midiController(cl->num()); + bool isDrumCtrl = (c->isPerNoteController()); + int show = c->showInTracks(); + int cnum = c->num(); + int clnum = cl->num(); + isList i = sList.begin(); + for (; i != sList.end(); ++i) { + if (i->num == cnum) + break; + } + + if (i == sList.end()) { + bool used = (clnum == num); + bool off = cl->hwVal() == MusECore::CTRL_VAL_UNKNOWN; // Does it have a value or is it 'off'? + // Filter if not used and off. But if there's something there, we must show it. + //if(!used && off && + if(!used && //off && + (((isDrumCtrl || isNewDrum) && !(show & MusECore::MidiController::ShowInDrum)) || + (isMidi && !(show & MusECore::MidiController::ShowInMidi)))) + continue; + bool isinstr = mcl->find(cnum) != mcl->end(); + // Need to distinguish between global default controllers and + // instrument defined controllers. Instrument takes priority over global + // ie they 'overtake' definition of a global controller such that the + // global def is no longer available. + //sList.push_back(CI(num, + sList.push_back(CI(cnum, + isinstr ? MusECore::midiCtrlNumString(cnum, true) + c->name() : MusECore::midiCtrlName(cnum, true), + used, off, isinstr)); + already_added_nums.insert(num); + } + } + + // Add instrument-defined controllers: + QListWidgetItem* sel_item = 0; + for (isList i = sList.begin(); i != sList.end(); ++i) + { + // Filter if not used and off. But if there's something there, we must show it. + if(!i->instrument && !i->used && i->off) + continue; + QListWidgetItem* item = new QListWidgetItem(i->s, ctrlList); + item->setData(Qt::UserRole, i->num); + if(i->num == ev_cnum) + sel_item = item; + } + if(sel_item) + ctrlList->setCurrentItem(sel_item); + + valSlider->setRange(mc->minVal(), mc->maxVal()); + valSpinBox->setRange(mc->minVal(), mc->maxVal()); + + controllerName->setText(mc->name()); + + if(!event.empty()) + { + if(ev_num == MusECore::CTRL_PROGRAM) + { + widgetStack->setCurrentIndex(1); + updatePatch(val); + } + else + { + widgetStack->setCurrentIndex(0); + valSlider->setValue(val - mc->bias()); + + if(mc->isPerNoteController()) + { + noteSpinBox->setVisible(true); + noteSpinBox->setEnabled(true); + noteLabel->setVisible(true); + noteLabel->setEnabled(true); + if(ev_note != -1) + noteSpinBox->setValue(ev_note); + } + else + { + noteSpinBox->setEnabled(false); + noteSpinBox->setVisible(false); + noteLabel->setEnabled(false); + noteLabel->setVisible(false); + } + } + } + else + { + noteSpinBox->setEnabled(false); + noteSpinBox->setVisible(false); + noteLabel->setEnabled(false); + noteLabel->setVisible(false); + if(sel_item) + ctrlListClicked(sel_item); + } + connect(ctrlList, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(ctrlListClicked(QListWidgetItem*))); + connect(buttonNewController, SIGNAL(clicked()), SLOT(newController())); + connect(hbank, SIGNAL(valueChanged(int)), SLOT(programChanged())); + connect(lbank, SIGNAL(valueChanged(int)), SLOT(programChanged())); + connect(program, SIGNAL(valueChanged(int)), SLOT(programChanged())); + connect(patchName, SIGNAL(released()), SLOT(instrPopup())); + connect(buttonCancel, SIGNAL(clicked()), SLOT(reject())); + connect(buttonOk, SIGNAL(clicked()), SLOT(accept())); + timePos->setValue(tick); + + } +//--------------------------------------------------------- +// newController +//--------------------------------------------------------- + +void EditCtrlDialog::newController() + { + MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); + + // populate popup with all controllers available for + // current instrument + + MusECore::MidiTrack* track = part->track(); + int portn = track->outPort(); + MusECore::MidiPort* port = &MusEGlobal::midiPorts[portn]; + bool isDrum = track->type() == MusECore::Track::DRUM; + bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; + bool isMidi = track->type() == MusECore::Track::MIDI; + MusECore::MidiInstrument* instr = port->instrument(); + MusECore::MidiControllerList* mcl = instr->controller(); + + MusECore::MidiCtrlValListList* cll = port->controller(); + int channel = track->outChannel(); + for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) + { + MusECore::MidiController* c = ci->second; + int cnum = c->num(); + int show = c->showInTracks(); + if(((isDrum || isNewDrum) && !(show & MusECore::MidiController::ShowInDrum)) || + (isMidi && !(show & MusECore::MidiController::ShowInMidi))) + continue; + // If it's not already in the parent menu... + int idx = 0; + for(; idx < ctrlList->count(); ++idx) { + if(ctrlList->item(idx)->data(Qt::UserRole).toInt() == cnum) + break; + } + if(idx >= ctrlList->count()) { + QAction* act = pup->addAction(MusECore::midiCtrlNumString(cnum, true) + c->name()); + act->setData(cnum); + } + } + + QAction* act = pup->exec(buttonNewController->mapToGlobal(QPoint(0,0))); + if (act && act->data().toInt() != -1) { + int rv = act->data().toInt(); + int cnum = rv; + for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) { + MusECore::MidiController* mc = ci->second; + if (mc->num() == cnum) { + // Create a new controller list if it does not exist. + // FIXME: Sorry no per-pitch controller lists created here + // (meaning you should only create one 'new' one at a time) + // because the user has not had a chance to choose a pitch yet. + // They are handled in accept(), where there are more checks and creations. + if(!mc->isPerNoteController() && cll->find(channel, rv) == cll->end()) + { + MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(rv); + cll->add(channel, vl); + } + int idx = 0; + for (; idx < ctrlList->count() ;++idx) { + QListWidgetItem* item = ctrlList->item(idx); + int item_data = item->data(Qt::UserRole).toInt(); + if(item_data == cnum) + { + ctrlList->setCurrentItem(item); + ctrlListClicked(item); + break; + } + } + if (idx >= ctrlList->count()) { + QListWidgetItem* new_item = new QListWidgetItem(act->text(), ctrlList); + new_item->setData(Qt::UserRole, cnum); + ctrlList->setCurrentItem(new_item); + ctrlListClicked(new_item); + break; + } + break; + } + } + } + delete pup; + } +//--------------------------------------------------------- +// ctrlListClicked +//--------------------------------------------------------- + +void EditCtrlDialog::ctrlListClicked(QListWidgetItem* item) + { + if(item == 0) + return; + int cnum = item->data(Qt::UserRole).toInt(); + MusECore::MidiTrack* track = part->track(); + int portn = track->outPort(); + MusECore::MidiPort* port = &MusEGlobal::midiPorts[portn]; + MusECore::MidiController* c = port->midiController(cnum); + int val; + if (cnum == MusECore::CTRL_PROGRAM) { + widgetStack->setCurrentIndex(1); + + val = c->initVal(); + if(val == MusECore::CTRL_VAL_UNKNOWN) + val = 0; + updatePatch(val); + } + else { + widgetStack->setCurrentIndex(0); + if(c->isPerNoteController()) + { + noteSpinBox->setEnabled(true); + noteSpinBox->setVisible(true); + noteLabel->setEnabled(true); + noteLabel->setVisible(true); + } + else + { + noteSpinBox->setEnabled(false); + noteSpinBox->setVisible(false); + noteLabel->setEnabled(false); + noteLabel->setVisible(false); + } + valSlider->setRange(c->minVal(), c->maxVal()); + valSpinBox->setRange(c->minVal(), c->maxVal()); + controllerName->setText(c->name()); + val = c->initVal(); + + if(val == MusECore::CTRL_VAL_UNKNOWN || val == 0) + { + switch(cnum) + { + case MusECore::CTRL_PANPOT: + val = 64 - c->bias(); + break; + case MusECore::CTRL_VOLUME: + val = 100; + break; + default: + val = 0; + break; + } + } + valSlider->setValue(val); + } + } + +//--------------------------------------------------------- +// updatePatch +//--------------------------------------------------------- + +void EditCtrlDialog::updatePatch(int val) + { + MusECore::MidiTrack* track = part->track(); + int port = track->outPort(); + int channel = track->outChannel(); + MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); + patchName->setText(instr->getPatchName(channel, val, track->isDrumTrack(), true)); // Include default. + + int hb = ((val >> 16) & 0xff) + 1; + if (hb == 0x100) + hb = 0; + int lb = ((val >> 8) & 0xff) + 1; + if (lb == 0x100) + lb = 0; + int pr = (val & 0xff) + 1; + if (pr == 0x100) + pr = 0; + + hbank->blockSignals(true); + lbank->blockSignals(true); + program->blockSignals(true); + + hbank->setValue(hb); + lbank->setValue(lb); + program->setValue(pr); + + hbank->blockSignals(false); + lbank->blockSignals(false); + program->blockSignals(false); + } + +//--------------------------------------------------------- +// instrPopup +//--------------------------------------------------------- + +void EditCtrlDialog::instrPopup() + { + MusECore::MidiTrack* track = part->track(); + int channel = track->outChannel(); + int port = track->outPort(); + MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); + + MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); + instr->populatePatchPopup(pup, channel, track->isDrumTrack()); + + if(pup->actions().count() == 0) + { + delete pup; + return; + } + + QAction* rv = pup->exec(patchName->mapToGlobal(QPoint(10,5))); + if (rv) { + updatePatch(rv->data().toInt()); + } + + delete pup; + } + +//--------------------------------------------------------- +// programChanged +//--------------------------------------------------------- + +void EditCtrlDialog::programChanged() + { + int hb = hbank->value(); + int lb = lbank->value(); + int prog = program->value(); + + if (hb > 0 && hb < 129) + hb -= 1; + else + hb = 0xff; + if (lb > 0 && lb < 129) + lb -= 1; + else + lb = 0xff; + if (prog > 0 && prog < 129) + prog -= 1; + else + prog = 0xff; + + int val = (hb << 16) + (lb << 8) + prog; + updatePatch(val); + } + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/editevent.h muse-3.0.2+ds1/muse/widgets/editevent.h --- muse-2.1.2/muse/widgets/editevent.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/editevent.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,173 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: editevent.h,v 1.6.2.1 2008/05/21 00:28:53 terminator356 Exp $ +// (C) Copyright 1999 Werner Schweer (ws@seh.de) +// +// 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 __EDIT_EVENT_H__ +#define __EDIT_EVENT_H__ + +#include "ui_editnotedialogbase.h" +#include "ui_editsysexdialogbase.h" +#include "ui_editctrlbase.h" + +class QDialog; +class QLabel; +class QGridLayout; +class QTextEdit; +class QRadioButton; +class QListWidgetItem; +class QMenu; + +namespace Awl { + class PosEdit; + }; + +namespace MusECore { +class Event; +class MidiPart; +class MidiInstrument; +} + +namespace MusEGui { + +class IntLabel; +class PitchEdit; + +//--------------------------------------------------------- +// EditEventDialog +//--------------------------------------------------------- + +class EditEventDialog : public QDialog { + Q_OBJECT + + protected: + QGridLayout* layout1; + + public: + EditEventDialog(QWidget* parent=0); + virtual MusECore::Event getEvent() = 0; + }; + +//--------------------------------------------------------- +// EditNoteDialog +//--------------------------------------------------------- + +class EditNoteDialog : public QDialog, public Ui::EditNoteDialogBase { + Q_OBJECT + + public: + EditNoteDialog(int tick, const MusECore::Event&, + QWidget* parent=0); + static MusECore::Event getEvent(int tick, const MusECore::Event&, + QWidget* parent = 0); + virtual MusECore::Event getEvent(); + }; + +//--------------------------------------------------------- +// EditSysExDialog +//--------------------------------------------------------- + +class EditSysexDialog : public QDialog, public Ui::EditSysexDialogBase { + Q_OBJECT + + MusECore::MidiInstrument* _instr; + unsigned char* sysex; + int len; + + protected: + QGridLayout* layout; + + private slots: + virtual void accept(); + virtual void editChanged(); + virtual void selectSysex(); + + public: + EditSysexDialog(int tick, const MusECore::Event&, + QWidget* parent=0, MusECore::MidiInstrument* instr = 0); + ~EditSysexDialog(); + static MusECore::Event getEvent(int tick, const MusECore::Event&, + QWidget* parent = 0, MusECore::MidiInstrument* instr = 0); + virtual MusECore::Event getEvent(); + }; + +//--------------------------------------------------------- +// EditCtrlDialog +//--------------------------------------------------------- + +class EditCtrlDialog : public QDialog, public Ui::EditCtrlBase { + Q_OBJECT + + const MusECore::MidiPart* part; + void updatePatch(int val); + + private slots: + void ctrlListClicked(QListWidgetItem*); + void newController(); + void programChanged(); + void instrPopup(); + + protected: + QGridLayout* layout; + + public: + EditCtrlDialog(int tick, const MusECore::Event&, + const MusECore::MidiPart*, QWidget* parent=0); + static MusECore::Event getEvent(int tick, const MusECore::Event&, const MusECore::MidiPart*, + QWidget* parent = 0); + virtual MusECore::Event getEvent(); + }; + +//--------------------------------------------------------- +// EditMetaDialog +//--------------------------------------------------------- + +class EditMetaDialog : public EditEventDialog { + Q_OBJECT + + unsigned char* meta; + int len; + Awl::PosEdit* epos; + QTextEdit* edit; + MusEGui::IntLabel* il2; + QRadioButton* hexButton; + QLabel* typeLabel; + + protected: + QGridLayout* layout; + + private slots: + virtual void accept(); + void toggled(bool); + void typeChanged(int); + + public: + EditMetaDialog(int tick, const MusECore::Event&, + QWidget* parent=0); + ~EditMetaDialog(); + static MusECore::Event getEvent(int tick, const MusECore::Event&, + QWidget* parent = 0); + virtual MusECore::Event getEvent(); + }; + +} // namespace MusEGui + +#endif + diff -Nru muse-2.1.2/muse/widgets/editsysexdialogbase.ui muse-3.0.2+ds1/muse/widgets/editsysexdialogbase.ui --- muse-2.1.2/muse/widgets/editsysexdialogbase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/editsysexdialogbase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -7,7 +7,7 @@ 0 0 433 - 330 + 334 @@ -20,7 +20,16 @@ 6 - + + 11 + + + 11 + + + 11 + + 11 @@ -28,7 +37,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -42,7 +60,7 @@
    - + @@ -77,11 +95,38 @@
    + + + Hex: Note: The enclosing F0 ... F7 are not required + + + + + Name: + + + false + + + + + + + QFrame::Panel + + + QFrame::Sunken + + + + + + Comment: @@ -90,14 +135,49 @@ - + + + true + + + + + 0 + 0 + 407 + 59 + + + + + + + QFrame::Panel + + + QFrame::Sunken + + + + + + 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -117,9 +197,16 @@ + + + &Select... + + + + - OK + &OK 0 @@ -135,7 +222,7 @@ - Cancel + &Cancel 0 @@ -155,7 +242,6 @@ Awl::PosEdit QWidget
    awl/posedit.h
    - 0 diff -Nru muse-2.1.2/muse/widgets/elided_label.cpp muse-3.0.2+ds1/muse/widgets/elided_label.cpp --- muse-2.1.2/muse/widgets/elided_label.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/elided_label.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,315 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// elided_label.cpp +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 + +#include +#include +#include +#include + +#include "background_painter.h" +#include "elided_label.h" + +namespace MusEGui { + +ElidedLabel::ElidedLabel(QWidget* parent, + Qt::TextElideMode elideMode, + Qt::Alignment alignment, + //int maxFontPoint, + int minFontPoint, + bool ignoreHeight, bool ignoreWidth, + const QString& text, + Qt::WindowFlags flags) + : QFrame(parent, flags), + _elideMode(elideMode), + //_fontPointMax(maxFontPoint), + _fontPointMin(minFontPoint), + _fontIgnoreHeight(ignoreHeight), + _fontIgnoreWidth(ignoreWidth), + _text(text) +{ + setMouseTracking(true); + setEnabled(true); + setFocusPolicy(Qt::StrongFocus); + + //setAutoFillBackground(false); + //setAttribute(Qt::WA_NoSystemBackground); + //setAttribute(Qt::WA_StaticContents); + // This is absolutely required for speed! Otherwise painfully slow because of full background + // filling, even when requesting small udpdates! Background is drawn by us. + //setAttribute(Qt::WA_OpaquePaintEvent); + + _id = -1; + _hasOffMode = false; + _off = false; + _hovered = false; + + _alignment = alignment; + + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); +// updateSizeHint(); + _curFont = font(); + autoAdjustFontSize(); +} + +void ElidedLabel::keyPressEvent(QKeyEvent* e) +{ + switch (e->key()) + { + case Qt::Key_Escape: + // Don't let ancestor grab it, let it pass up the chain. + e->ignore(); + return; + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + e->accept(); + emit returnPressed(pos(), _id, e->modifiers()); + return; + break; + + default: + break; + } + + e->ignore(); + return QFrame::keyPressEvent(e); +} + +void ElidedLabel::setOff(bool v) +{ + if(v && !_hasOffMode) + _hasOffMode = true; + if(_off == v) + return; + _off = v; + update(); + //emit valueStateChanged(value(), isOff(), id(), d_scrollMode); +} + +void ElidedLabel::setHasOffMode(bool v) +{ + _hasOffMode = v; + setOff(false); +} + +void ElidedLabel::paintEvent(QPaintEvent* e) +{ + QFrame::paintEvent(e); + if(rect().width() <= 0 || rect().height() <= 0) + return; + QPainter painter(this); + + const QRect r = rect(); + const QRect ar = r.adjusted(1, 1, -1, -1); + + ItemBackgroundPainter ibp; + ibp.drawBackground(&painter, r, palette(), 1, 1, !hasOffMode() || !isOff() ? r : QRect()); + + if (hasFocus()) + { + if (_hovered) + painter.setPen(QPen(QColor(239,239,239))); + else + painter.setPen(QPen(Qt::white)); + } + else if (_hovered) + painter.setPen(QPen(QColor(48,48,48))); + else + painter.setPen(QPen(Qt::black)); + + painter.setRenderHint(QPainter::Antialiasing); + painter.setFont(_curFont); + QFontMetrics fm = painter.fontMetrics(); + QString elidedText = fm.elidedText(_text, _elideMode, r.width()); +// painter.drawText(QPoint(0, fm.ascent()), elidedText); + + painter.drawText(ar, _alignment, elidedText); +} + +//--------------------------------------------------------- +// autoAdjustFontSize +// w: Widget to auto adjust font size +// s: String to fit +// ignoreWidth: Set if dealing with a vertically constrained widget - one which is free to resize horizontally. +// ignoreHeight: Set if dealing with a horizontally constrained widget - one which is free to resize vertically. +//--------------------------------------------------------- + +bool ElidedLabel::autoAdjustFontSize() +{ +// FIXME: Disabled for now, the font modulates back and forth, not very good ATM. +// May have to revert to the font-checking iteration loop scheme. + +// QFont fnt = font(); // This is the maximum font. +// int max = fnt.pointSize(); +// int min = _fontPointMin; +// +// // In case the max or min was obtained from QFont::pointSize() which returns -1 +// // if the font is a pixel font, or if min is greater than max... +// // Limit the minimum and maximum sizes to something at least readable. +// if(max < 4) +// max = 4; +// if(min < 4) +// min = 4; +// if(max < min) +// max = min; +// +// //qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); +// //QRectF r = boundingRect(); +// QRectF r = rect(); +// //QFont f = painter->font(); +// +// +// //if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. +// if(_fontIgnoreWidth || _text.isEmpty()) // Also avoid divide by zero below. +// { +// if(fnt.pointSize() != max) +// { +// fnt.setPointSize(max); +// // setFont(fnt); +// _curFont = fnt; +// update(); +// } +// } +// else +// { +// //qreal aspectRatio = painter->fontMetrics().lineSpacing() / painter->fontMetrics().averageCharWidth(); +// qreal aspectRatio = fontMetrics().lineSpacing() / fontMetrics().averageCharWidth(); +// // int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (_text.length() * 3)) * aspectRatio; +// int pixelsize = sqrt(r.width() * r.height() / aspectRatio / _text.length()) * aspectRatio; +// fnt.setPixelSize(pixelsize); +// //int flags = Qt::AlignCenter|Qt::TextDontClip|Qt::TextWordWrap; +// int flags = Qt::AlignCenter; +// //if ((pixelsize * lod) < 13) +// // flags |= Qt::TextWrapAnywhere; +// QFontMetricsF fmf(fnt); +// QRectF tbr = fmf.boundingRect(r, flags, _text); +// pixelsize = fnt.pixelSize() * qMin(r.width() * 0.95 / tbr.width(), r.height() * 0.95 / tbr.height()); +// // if(pixelsize < min) +// // pixelsize = min; +// // else if(pixelsize > max) +// // pixelsize = max; +// fnt.setPixelSize(pixelsize); +// const QFontInfo fi(fnt); +// const int pointsize = fi.pointSize(); +// if(pointsize <= min) +// fnt.setPointSize(min); +// else if(pointsize >= max) +// fnt.setPointSize(max); +// // setFont(fnt); +// _curFont = fnt; +// //painter->drawText(r,flags,stitle); + update(); +// } + + // Force minimum height. Use the expected height for the highest given point size. + // This way the mixer strips aren't all different label heights, but can be larger if necessary. + // Only if ignoreHeight is set (therefore the height is adjustable). + if(_fontIgnoreHeight) + { +// FIXME Disabled for now, as per above. +// fnt.setPointSize(max); +// const QFontMetrics fm(fnt); + const QFontMetrics fm(font()); + + // Set the label's minimum height equal to the height of the font. + setMinimumHeight(fm.height() + 2 * frameWidth()); + } + + return true; +} + +void ElidedLabel::setText(const QString& txt) +{ + if(_text == txt) + return; + _text = txt; + autoAdjustFontSize(); +} + +void ElidedLabel::resizeEvent(QResizeEvent* e) +{ + e->ignore(); + QFrame::resizeEvent(e); + autoAdjustFontSize(); +} + +void ElidedLabel::mousePressEvent(QMouseEvent* e) +{ + e->accept(); + emit pressed(e->pos(), _id, e->buttons(), e->modifiers()); +} + +void ElidedLabel::mouseReleaseEvent(QMouseEvent* e) +{ + e->accept(); + emit released(e->pos(), _id, e->buttons(), e->modifiers()); +} + +void ElidedLabel::leaveEvent(QEvent *e) +{ + if(_hovered) + { + _hovered = false; + update(); + } + e->ignore(); + QFrame::leaveEvent(e); +} + +void ElidedLabel::mouseMoveEvent(QMouseEvent *e) +{ + //e->ignore(); + //QFrame::mouseMoveEvent(e); + e->accept(); + if(!_hovered) + { + _hovered = true; + update(); + } +} + +void ElidedLabel::setFontIgnoreDimensions(bool ignoreHeight, bool ignoreWidth) +{ + _fontIgnoreWidth = ignoreWidth; + _fontIgnoreHeight = ignoreHeight; + autoAdjustFontSize(); +} + +void ElidedLabel::setFontPointMin(int point) +{ + _fontPointMin = point; + autoAdjustFontSize(); +} + +QSize ElidedLabel::sizeHint() const +{ + QSize sz(fontMetrics().width(_text) + 8, fontMetrics().height() + 4); + return sz; +} + + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/elided_label.h muse-3.0.2+ds1/muse/widgets/elided_label.h --- muse-2.1.2/muse/widgets/elided_label.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/elided_label.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,123 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// elided_label.h +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __ELIDED_LABEL_H__ +#define __ELIDED_LABEL_H__ + +#include + +namespace MusEGui { + +//--------------------------------------------------------- +// ElidedLabel +//--------------------------------------------------------- + +class ElidedLabel : public QFrame +{ + Q_OBJECT + // FIXME: Found some problems here: If this was enabled, text() was + // intermittently including accelerator key characters, causing + // for example midi controller stream text rapid flickering + // between normal and accelerator key text. + // Maybe it should be const QString& ? + //Q_PROPERTY(QString text READ text WRITE setText) + + //Q_PROPERTY(Qt::TextElideMode elideMode READ elideMode WRITE setElideMode) + + private: + int _id; + bool _hasOffMode; + bool _off; + Qt::TextElideMode _elideMode; + Qt::Alignment _alignment; + int _fontPointMin; + bool _fontIgnoreHeight; + bool _fontIgnoreWidth; + QString _text; + QFont _curFont; + // Whether the mouse is over the entire control. + bool _hovered; + + bool autoAdjustFontSize(); + + protected: + virtual void paintEvent(QPaintEvent*); + virtual void resizeEvent(QResizeEvent*); + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseReleaseEvent(QMouseEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void leaveEvent(QEvent*); + virtual void keyPressEvent(QKeyEvent*); + + signals: + void pressed(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); + void released(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); + void returnPressed(QPoint p, int id, Qt::KeyboardModifiers keys); + + public: + explicit ElidedLabel(QWidget* parent = 0, + Qt::TextElideMode elideMode = Qt::ElideNone, + Qt::Alignment alignment = Qt::AlignLeft | Qt::AlignVCenter, + //int maxFontPoint = 10, + int minFontPoint = 5, + bool ignoreHeight = true, bool ignoreWidth = false, + const QString& text = QString(), + Qt::WindowFlags flags = 0); + + virtual QSize sizeHint() const; + + int id() const { return _id; } + void setId(int i) { _id = i; } + + Qt::TextElideMode elideMode() const { return _elideMode; } + void setElideMode(Qt::TextElideMode mode) { _elideMode = mode; update(); } + + Qt::Alignment alignment() const { return _alignment; } + void setAlignment(Qt::Alignment align) { _alignment = align; update(); } + + bool hasOffMode() const { return _hasOffMode; } + void setHasOffMode(bool v); + bool isOff() const { return _off; } + // Sets the off state and emits valueStateChanged signal if required. + void setOff(bool v); + // Both value and off state changed combined into one setter. + // By default it is assumed that setting a value naturally implies resetting the 'off' state to false. + // Emits valueChanged and valueStateChanged signals if required. + // Note setOff and SliderBase::setValue are also available. + //void setValueState(double v, bool off = false, ConversionMode mode = ConvertDefault); + + QString text() const { return _text; } + void setText(const QString& txt); + + int fontPointMin() const { return _fontPointMin; } + void setFontPointMin(int point); + + bool fontIgnoreWidth() const { return _fontIgnoreWidth; } + bool fontIgnoreHeight() const { return _fontIgnoreHeight; } + void setFontIgnoreDimensions(bool ignoreHeight, bool ignoreWidth = false); +}; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/fdialogbuttons.ui muse-3.0.2+ds1/muse/widgets/fdialogbuttons.ui --- muse-2.1.2/muse/widgets/fdialogbuttons.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/fdialogbuttons.ui 2017-12-04 21:01:19.000000000 +0000 @@ -6,15 +6,36 @@ 0 0 - 154 - 438 + 109 + 370
    + + + 0 + 0 + + fdialogbuttons - - + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + QFrame::StyledPanel @@ -25,86 +46,142 @@ 0 - - - - - - 80 - 80 - - - - Global - - - - 48 - 48 - - - - true - - - Qt::ToolButtonTextUnderIcon - - - - - - - - 80 - 80 - - - - User - - - - 48 - 48 - - - - true - - - Qt::ToolButtonTextUnderIcon - - - - - - - - 80 - 80 - - - - Project - - - - 48 - 48 - - - - true - - - Qt::ToolButtonTextUnderIcon - - + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + + 100 + 40 + + + + Home + + + + 24 + 24 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + false + + + Qt::NoArrow + + + + + + + + 100 + 40 + + + + Global + + + + 24 + 24 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 100 + 40 + + + + User + + + + 24 + 24 + + + + true + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 100 + 40 + + + + Project + + + + 24 + 24 + + + + + + + true + + + Qt::ToolButtonTextBesideIcon + + + + - + Qt::Vertical @@ -114,13 +191,13 @@ - 20 - 245 + 78 + 119 - + QFrame::StyledPanel @@ -129,8 +206,28 @@ QFrame::Raised + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + + 8 + + read Midi Port Configuration @@ -146,7 +243,7 @@ - + QFrame::StyledPanel @@ -155,8 +252,28 @@ QFrame::Raised + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + + 8 + + write window states diff -Nru muse-2.1.2/muse/widgets/filedialog.cpp muse-3.0.2+ds1/muse/widgets/filedialog.cpp --- muse-2.1.2/muse/widgets/filedialog.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/filedialog.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -75,15 +75,16 @@ static bool testDirCreate(QWidget* parent, const QString& path) { QDir dir(path); - if (!dir.exists()) + if (!dir.exists()) { if(QMessageBox::information(parent, QWidget::tr("MusE: get file name"), QWidget::tr("The directory\n%1\ndoes not exist.\nCreate it?").arg(path), - QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) { return true; - - if (createDir(path)) + } + + if (createDir(path)) { QMessageBox::critical(parent, QWidget::tr("MusE: create directory"), @@ -111,6 +112,20 @@ } //--------------------------------------------------------- +// homeToggled +//--------------------------------------------------------- + +void MFileDialog::homeToggled(bool flag) + { + if (flag) { + buttons.readMidiPortsButton->setChecked(true); + readMidiPortsSaved = true; + setDirectory(QDir::home()); + lastViewUsed = HOME_VIEW; + } + } + +//--------------------------------------------------------- // userToggled //--------------------------------------------------------- @@ -166,7 +181,7 @@ 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(); @@ -181,7 +196,7 @@ buttons.readMidiPortsButton->setChecked(readMidiPortsSaved); } } - + } @@ -193,6 +208,7 @@ const QString& filter, QWidget* parent, bool writeFlag) : QFileDialog(parent, QString(), QString("."), filter) { + setOption(QFileDialog::DontUseNativeDialog); readMidiPortsSaved = true; showButtons = false; lastUserDir = ""; @@ -213,24 +229,28 @@ spl->insertWidget(0,&buttons); - // Qt >= 4.6 allows us to select icons from the theme + // Qt >= 4.6 allows us to select icons from the theme #if QT_VERSION >= 0x040600 buttons.globalButton->setIcon(*globalIcon); buttons.userButton->setIcon(*userIcon); + buttons.homeButton->setIcon(*userIcon); buttons.projectButton->setIcon(*projectIcon); #else buttons.globalButton->setIcon(style()->standardIcon(QStyle::SP_DirIcon)); - buttons.userButton->setIcon(style()->standardIcon(QStyle::SP_DirHomeIcon)); + buttons.userButton->setIcon(style()->standardIcon(QStyle::SP_DesktopIcon)); + buttons.homeButton->setIcon(style()->standardIcon(QStyle::SP_DirHomeIcon)); buttons.projectButton->setIcon(style()->standardIcon(QStyle::SP_DirOpenIcon)); -#endif +#endif buttons.globalButton->setAutoExclusive(true); buttons.userButton->setAutoExclusive(true); buttons.projectButton->setAutoExclusive(true); + buttons.homeButton->setAutoExclusive(true); 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(buttons.homeButton, SIGNAL(toggled(bool)), this, SLOT(homeToggled(bool))); connect(this, SIGNAL(directoryEntered(const QString&)), SLOT(directoryChanged(const QString&))); connect(this, SIGNAL(currentChanged(const QString&)), SLOT(fileChanged(const QString&))); @@ -244,26 +264,33 @@ break; case USER_VIEW: - buttons.userButton->setChecked(true); + buttons.userButton->setChecked(true); + break; + + case HOME_VIEW: + buttons.homeButton->setChecked(true); break; } } else { switch (lastViewUsed) { case GLOBAL_VIEW: - buttons.globalButton->setChecked(true); + buttons.globalButton->setChecked(true); break; case PROJECT_VIEW: - buttons.projectButton->setChecked(true); + buttons.projectButton->setChecked(true); break; case USER_VIEW: - buttons.userButton->setChecked(true); + buttons.userButton->setChecked(true); + break; + case HOME_VIEW: + buttons.homeButton->setChecked(true); break; } - } + } buttons.readMidiPortsGroup->setVisible(false); buttons.writeWinStateGroup->setVisible(false); } @@ -305,7 +332,7 @@ QWidget* parent, const QString& name, bool* doReadMidiPorts, MFileDialog::ViewType viewType) { QStringList filters = localizedStringListFromCharArray(filters_chararray, "file_patterns"); - + MFileDialog *dlg = new MFileDialog(startWith, QString::null, parent, false); dlg->setNameFilters(filters); dlg->setWindowTitle(name); @@ -341,7 +368,7 @@ const char** filters_chararray, QWidget* parent, const QString& name, bool* writeWinState) { QStringList filters = localizedStringListFromCharArray(filters_chararray, "file_patterns"); - + MFileDialog *dlg = new MFileDialog(startWith, QString::null, parent, true); dlg->setNameFilters(filters); dlg->setWindowTitle(name); @@ -361,7 +388,7 @@ if (writeWinState) *writeWinState = dlg->buttons.writeWinStateButton->isChecked(); } - + // Added by T356. if(!result.isEmpty()) { @@ -382,15 +409,15 @@ // or any desired extension by commenting this section out, it's probably not a good idea to do so. // // NOTE: Most calls to this routine getSaveFileName() are followed by fileOpen(), - // which can tack on its own extension, but only if the *complete* extension is blank. - // So there is some overlap going on. Enabling this actually stops that action, + // which can tack on its own extension, but only if the *complete* extension is blank. + // So there is some overlap going on. Enabling this actually stops that action, // but only if there are no errors in the list of filters. fileOpen() will act as a 'catchall'. // // Force the filter list to the first one (the preferred one), and then get the filter. dlg->selectNameFilter(dlg->nameFilters().at(0)); filt = dlg->selectedNameFilter(); filt = getFilterExtension(filt); - + // Do we have a valid extension? if(!filt.isEmpty()) { @@ -402,7 +429,7 @@ } } } - + delete dlg; return result; } @@ -416,7 +443,7 @@ { QStringList filters = localizedStringListFromCharArray(filters_chararray, "file_patterns"); QString initialSelection; - QString* workingDirectory = new QString(QDir::currentPath()); + QString* workingDirectory = new QString(QDir::currentPath()); if (!startWith.isEmpty() ) { QFileInfo fi(startWith); if (fi.exists() && fi.isDir()) { @@ -445,8 +472,8 @@ if (!initialSelection.isEmpty()) dlg->selectFile( initialSelection); if (dlg->exec() == QDialog::Accepted) { - files = dlg->selectedFiles(); - if (!files.isEmpty()) + files = dlg->selectedFiles(); + if (!files.isEmpty()) result = files[0]; } delete dlg; @@ -502,13 +529,13 @@ case 1: // quit return 0; } - */ + */ if(QMessageBox::warning(parent, QWidget::tr("MusE: write"), s, QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Save) != QMessageBox::Save) return 0; - + } FILE* fp = 0; if (popenFlag) { diff -Nru muse-2.1.2/muse/widgets/filedialog.h muse-3.0.2+ds1/muse/widgets/filedialog.h --- muse-2.1.2/muse/widgets/filedialog.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/filedialog.h 2017-12-04 21:01:19.000000000 +0000 @@ -62,40 +62,19 @@ void globalToggled(bool); void userToggled(bool); void projectToggled(bool); + void homeToggled(bool); public: - enum ViewType { GLOBAL_VIEW, PROJECT_VIEW, USER_VIEW }; //!< The three different viewtypes + enum ViewType { GLOBAL_VIEW, PROJECT_VIEW, USER_VIEW, HOME_VIEW }; static ViewType lastViewUsed; FileDialogButtonsWidget buttons; MFileDialog(const QString& dir, const QString& filter = QString::null, QWidget* parent = 0, bool writeFlag = false); }; -/* ORCAN - Disable previeww for now. It is not available in qt4. We will - need to implement it ourselves. -//--------------------------------------------------------- -// ContentsPreview -//--------------------------------------------------------- - -class ContentsPreview : public QWidget, public Q3FilePreview { - Q_OBJECT - - virtual void previewUrl(const Q3Url &url); - QString path; - QPixmap* bg; - - public: - ContentsPreview(QWidget* parent, const char* name=0) - : QWidget(parent, name) { - bg = 0; - } - ~ContentsPreview(); - }; -*/ - -QString getSaveFileName(const QString& startWidth, const char** filters, +QString getSaveFileName(const QString& startWith, const char** filters, QWidget* parent, const QString& name, bool* writeWinState=NULL); -QString getOpenFileName(const QString& startWidth, const char** filters, +QString getOpenFileName(const QString& startWith, const char** filters, QWidget* parent, const QString& name, bool* doReadMidiPorts, MFileDialog::ViewType viewType = MFileDialog::PROJECT_VIEW); QString getImageFileName(const QString& startWith, const char** filters, QWidget* parent, const QString& name); diff -Nru muse-2.1.2/muse/widgets/function_dialogs/CMakeLists.txt muse-3.0.2+ds1/muse/widgets/function_dialogs/CMakeLists.txt --- muse-2.1.2/muse/widgets/function_dialogs/CMakeLists.txt 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/function_dialogs/CMakeLists.txt 2017-12-04 21:01:19.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP (widgets_functiondialogs_mocs +QT5_WRAP_CPP (widgets_functiondialogs_mocs crescendo.h deloverlaps.h gatetime.h @@ -53,7 +53,7 @@ velocitybase.ui ) -QT4_WRAP_UI (widgets_functiondialogs_ui_headers ${widgets_functiondialogs_ui_files}) +QT5_WRAP_UI (widgets_functiondialogs_ui_headers ${widgets_functiondialogs_ui_files}) ## ## List of source files to compile diff -Nru muse-2.1.2/muse/widgets/function_dialogs/deloverlaps.h muse-3.0.2+ds1/muse/widgets/function_dialogs/deloverlaps.h --- muse-2.1.2/muse/widgets/function_dialogs/deloverlaps.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/function_dialogs/deloverlaps.h 2017-12-04 21:01:19.000000000 +0000 @@ -21,7 +21,7 @@ //========================================================= #ifndef __DELOVERLAPS_H__ -#define __DELOVERLAPS__H__ +#define __DELOVERLAPS_H__ #include "ui_deloverlapsbase.h" diff -Nru muse-2.1.2/muse/widgets/gensetbase.ui muse-3.0.2+ds1/muse/widgets/gensetbase.ui --- muse-2.1.2/muse/widgets/gensetbase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/gensetbase.ui 2018-01-22 16:43:28.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 544 - 622 + 606 + 539 @@ -17,7 +17,7 @@ - + 0 0 @@ -30,6 +30,151 @@ Application + + + + Start Muse + + + + 2 + + + 6 + + + + + + + + + + + + Choose start song or template + + + ... + + + + + + + Reset to default + + + ... + + + + + + + + + Start song + + + + + + start with &last song + + + true + + + + + + + + 0 + 0 + + + + start with &template + + + false + + + + + + + sta&rt with song + + + + + + + + + + On Launch + + + + 2 + + + 6 + + + + + show splash screen + + + + + + + show "Did you know?" dialog + + + + + + + + + + Start template or song: + + + false + + + + + + + Read MIDI Ports configuration from file, + or else automatically configure + + + Read MIDI Ports configuration + + + + + + + + + Warn if opening file versions different than current version + + + + + + @@ -39,6 +184,12 @@ 6 + + 2 + + + 2 + @@ -62,7 +213,27 @@ - + + + + Auto save (every 5 minutes if not playing/recording) + + + + + + + + Views + + + + 2 + + + 6 + + @@ -73,7 +244,16 @@ Views - + + + 2 + + + 6 + + + 2 + @@ -445,139 +625,409 @@ - - - - Start Muse - - - - - - - - - - - - - Choose start song or template - - - ... - - - - - - - Reset to default - - - ... - - - - - - - - - Start song - - + + + + + Plugin Paths + + + + + + + + + + Ordered by priority. Changes take effect on restart. + + + + + + + 0 + + + + LADSPA + + - - - start with last song - - + + true + + QAbstractItemView::ExtendedSelection + + + + + + DSSI + + - - - - 0 - 0 - + + + true - - start with template + + QAbstractItemView::ExtendedSelection - - false + + + + + + + VST + + + + + + true + + + QAbstractItemView::ExtendedSelection + + + + + Linux VST + + - - - start with song + + + true + + + QAbstractItemView::ExtendedSelection - - - - - On Launch - - + + + LV2 + + - + - show splash screen + NOTE: Ensure the directory containing core LV2 bundles is included + (typically /usr/lib/lv2). Otherwise all plugins may not load properly. - - - show "Did you know?" dialog + + + true + + + QAbstractItemView::ExtendedSelection - - - + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Move up + + + + + + + + 0 + 0 + + + + Move down + + + + + + + + 0 + 0 + + + + Add... + + + + + + + + 0 + 0 + + + + Edit... + + + + + + + + 0 + 0 + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Audio + + + + + + External Waveditor + + + + 2 + + + 2 + + + 2 + + + + + + + + + + 1 + 0 + + + + External Waveditor command + + + false + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 60 + 23 + + + + + + + + + 2 + 0 + + + + + + + + + + + 8 + true + + - Start template or song: + Note: External editor opened from the internal editor. false - - - - Read MIDI Ports configuration from file, - or else automatically configure - - - Read MIDI Ports configuration - - - - - - - - Audio - - + + + + Audio Driver settings (require restart) + + + + + + Sample rate: + + + false + + + + + + + <html><head/><body><p><br/></p></body></html> + + + Audio backend: + + + false + + + + + + + Shorter periods give better midi playback resolution. + + + + 16 + + + + + 32 + + + + + 64 + + + + + 128 + + + + + 256 + + + + + 512 + + + + + 1024 + + + + + 2048 + + + + + + + + + + + Period size (Frames per period): + + + false + + + + + + + + + @@ -829,818 +1279,959 @@ - - - - External Waveditor + + + + + Midi + + + + + + true - - - 2 - - - 2 + + + + 0 + 0 + 550 + 452 + - - 2 - - - - - - - - - - 1 - 0 - - + + + 6 + + + 6 + + + 6 + + + 6 + + + + + Ticks + + + + 2 + + + 6 + + + 2 + + + - External Waveditor command + RTC Resolution +(Ticks/Sec) false - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 60 - 23 - + + + + + 0 + 0 + - + + + 1024 + + + + + 2048 + + + + + 4096 + + + + + 8192 + + + + + 16384 + + + + + 32768 + + + - - + + - - 2 + + 0 + 0 + + + + 3 + + + + 48 + + + + + 96 + + + + + 192 + + + + + 384 + + + + + 768 + + + + + 1536 + + + + + 3072 + + + + + 6144 + + + + + 12288 + + + + + + + + Midi Resolution +(Ticks/Quarternote) + + + false + + + + + + + Displayed Resolution +(Ticks/Quarternote) + + + false + + + + + + + + 0 0 + + 3 + + + + 48 + + + + + 96 + + + + + 192 + + + + + 384 + + + + + 768 + + + + + 1536 + + + + + 3072 + + + + + 6144 + + + + + 12288 + + + + + + + + Warn if timer frequency is inadequate + - - - - - - 8 - true - + + + + + + Instrument initialization + + + + 2 - - Note: External editor opened from the internal editor. + + + + Send instrument initialization sequences + + + + + + + Warn if instrument initialization sequences pending + + + + + + + Send instrument controller default values if none in song, at init or rewind + + + + + + + + + + Midi controller behaviour + + + + 2 - - false + + 6 - - - - - + + + + Send Null parameters after an (N)RPN value + + + If set, 'null' parameter numbers will be + sent after each RPN/NRPN event. +(A 'null' parameter number is 127.) +This prevents subsequent rogue or accidental + 'data' events (without parameters) from + corrupting the current RPN/NRPN controller. +Normally it is OK to leave this unset, but use it + if there is a possibility of such 'data' events. +If this is set, the 'Optimize controllers' setting + does not affect these controllers' parameters, + only the H/L values. + + + Send Null parameters + + + + + + + Don't send redundant parameters or values + + + If set, redundant H/L parameters or H/L values + are not sent. They are sent only if they changed. +This can save midi interface bandwidth. +But it is possible the receiving device might miss + the previous changes (for example if the device + is turned on after they were sent). +The 'Panic' button will force them to be resent. +Certain operations will also force them to be resent, + such as clicking a GUI midi control (but not while + adjusting it). + + + Optimize controllers + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Record new style drum tracks + + + + 2 + + + 2 + + + 6 + + + + + &Record all instruments + + + + + + + &Don't record hidden instruments + + + + + + + Don'&t record muted instruments + + + + + + + Don't record &hidden or muted instruments + + + + + + + + - - + +
    + + + GUI Behaviour + + + + 6 + + + 11 + + + 11 + + + 11 + + + 11 + + + - Dummy Audio Driver (settings require restart) + Behavior - + - 11 + 6 - 2 + 6 - 11 + 6 - 2 + 6 - 2 + 6 - - - - Sample rate - - - false - - - - - - - Hz - - - 3000 - - - 200000 - - - 10 - - - 44100 - - - - - - - Period size (Frames per period): - - - false - - - - - - - Shorter periods give better midi playback resolution. + + + + true - - - 16 - - - - - 32 + + + + 0 + -300 + 524 + 628 + - - - - 64 - - - - - 128 - - - - - 256 - - - - - 512 - - - - - 1024 - - - - - 2048 - - - - - - - - - - - - Midi - - - - - - Record new style drum tracks - - - - - - Record all instruments - - - - - - - Don't record hidden instruments - - - - - - - Don't record muted instruments - - - - - - - Don't record hidden or muted instruments - - - - - - - - - - Instrument initialization - - - - - - Send instrument initialization sequences - - - - - - - Warn if instrument initialization sequences pending - - - - - - - Send instrument controller default values if none in song, at init or rewind - - - - - - - - - - Qt::Vertical - - - - 191 - 166 - - - - - - - - Ticks - - - - - - RTC Resolution -(Ticks/Sec) - - - false - - - - - - - - 0 - 0 - - - - - 1024 - - - - - 2048 - - - - - 4096 - - - - - 8192 - - - - - 16384 - - - - - 32768 - - - - - - - - - 0 - 0 - - - - 3 - - - - 48 - - - - - 96 - - - - - 192 - - - - - 384 - - - - - 768 - - - - - 1536 - - - - - 3072 - - - - - 6144 - - - - - 12288 - - - - - - - - Midi Resolution -(Ticks/Quarternote) - - - false - - - - - - - Displayed Resolution -(Ticks/Quarternote) - - - false - - - - - - - - 0 - 0 - - - - 3 - - - - 48 - - - - - 96 - - - - - 192 - - - - - 384 - - - - - 768 - - - - - 1536 - - - - - 3072 - - - - - 6144 - - - - - 12288 - - - - - - - - Warn if timer frequency is inadequate - - - - - - - - - - - GUI Behaviour - - - - 6 - - - 11 - - - - - Behavior - - - - - - Track height - - - - - - - GUI Refresh Rate - - - false - - - - - - - /sec - - - 2 - - - 100 - - - 20 - - - - - - - Use old-style stop shortcut: - - - false - - - - - - - - 0 - 0 - - - - - - - - - - - Move single armed track with selection - - - false - - - - - - - - 0 - 0 - - - - - - - - - - - Use project save dialog - - - false - - - - - - - - 0 - 0 - - - - - - - - - - - - - - Some popup menus stay open (else hold Ctrl) - - - - - - - - 0 - 0 - - - - Allows some popup menus to stay open. + + + + + Move single armed track with selection + + + false + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Some popup menus stay open (else hold Ctrl) + + + + + + + Use project save dialog + + + false + + + + + + + + 0 + 0 + + + + + + + + + + + GUI Refresh Rate + + + false + + + + + + + /sec + + + 2 + + + 100 + + + 20 + + + + + + + Use old-style stop shortcut: + + + false + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. - - - - - - - - - - In some areas, the middle mouse button decreases -values, while the right button increases. Users without a -middle mouse button can select this option to make the -left button behave like the middle button in such areas. - - - Use left mouse button for decreasing values - - - - - - - - 0 - 0 - - - - - - - - - - - Shift + Right click sets left range marker - - - - - - - - 0 - 0 - - - - - - - - - - - Allow adding hidden tracks in track list menu - - - - - - - - - - - - - - Unhide tracks when adding hidden tracks - - - - - - - - - - - - - - Smart focus - - - false - - - - - - - - 0 - 0 - - - - Smart focus - - - After editing, controls will return + + + + + + + + + + In some areas, the middle mouse button decreases +values, while the right button increases. Users without a +middle mouse button can select this option to make the +left button behave like the middle button in such areas. + + + Use left mouse button for decreasing values + + + + + + + + 0 + 0 + + + + + + + + + + + Shift + Right click sets left range marker + + + + + + + + 0 + 0 + + + + + + + + + + + Allow adding hidden tracks in track list menu + + + + + + + + + + + + + + Unhide tracks when adding hidden tracks + + + + + + + + + + + + + + Smart focus + + + false + + + + + + + + 0 + 0 + + + + Smart focus + + + After editing, controls will return focus to their respective canvas - - - - - - - - - - Show newly created midi velocity graphs per-note - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - px - - - 20 - - - 2000 - - - 20 - - - - - - - Enable borderless mouse. + + + + + + + + + + Show newly created midi velocity graphs per-note + + + + + + + + + + + + + + + + + + + + Borderless zoom/pan mouse (else use alternate method) + + + + + + + Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. - - - Enable borderless mouse. + + + Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. - - - - - - - - - - - - - - - - Borderless zoom/pan mouse (else use alternate method) - + + + + + + + + + + Track height + + + + + + + px + + + 20 + + + 2000 + + + 20 + + + + + + + Scrollable submenus + + + + + + + + + + + + + + Live update wave parts while recording + + + + + + + + + + + + + + LV2 UI Open behavior + + + + + + + + Use first available + + + + + Ask once + + + + + Ask always + + + + + + + + Prefer knobs instead of sliders + + + + + + + Whether to show knobs or sliders in certain places, for example mixer strips + + + + + + + + + + Show knob and slider values + + + + + + + Whether to show knob and slider values in certain places, for example mixer strips. +Turn off to reduce clutter. + + + + + + + + + + Monitor on record-arm automatically + + + + + + + Whether record-arming a track + automatically activates monitoring. + + + + + + + + + + Style hack: Force line edit widgets to draw a frame + + + + + + + Force line edit widgets to draw a frame at + small sizes. Some styles refuse to draw + the frame. This hack forces a frame to be + drawn, but may interfere with other styles. + + + + + + + + + + Prefer midi volume as decibels instead of 0-127 + + + + + + + Prefer midi volume as decibels + instead of 0-127. + + + + + + + + + + Bug fix: Fix frozen MDI windows + + + false + + + + + + + + 0 + 0 + + + + Fix frozen MDI windows + + + With some themes like Breeze or Oxygen, + MDI windows may be frozen and + non-responsive to the mouse. +This option turns on a fix. +Benign XCB connection errors may still + occur, slowing the program somewhat. + + + + + + + + + + Show note names on notes in pianoroll + + + + + + + + + + + +
    @@ -1655,47 +2246,34 @@ - Only offer old-style drumtracks + &Only offer old-style drumtracks - Only offer new-style drumtracks + Only offer new-style &drumtracks - Prefer old-style drumtracks + Prefer old-style drumtrac&ks - Prefer new-style drumtracks + Prefer &new-style drumtracks
    - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -1754,8 +2332,8 @@ 0 0 - 96 - 26 + 554 + 362 @@ -1774,23 +2352,10 @@
    - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 51 - - - - - + + 6 + diff -Nru muse-2.1.2/muse/widgets/genset.cpp muse-3.0.2+ds1/muse/widgets/genset.cpp --- muse-2.1.2/muse/widgets/genset.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/genset.cpp 2018-01-22 16:43:28.000000000 +0000 @@ -47,9 +47,14 @@ static int divisions[] = { 48, 96, 192, 384, 768, 1536, 3072, 6144, 12288 }; -static int dummyAudioBufSizes[] = { +static int selectableAudioBufSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048 }; +static int selectableAudioSampleRates[] = { + 22050, 32000, 44100, 48000, 64000, 88200, 96000, 192000 + }; +int numAudioSampleRates = 8; + static unsigned long minControlProcessPeriods[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 }; @@ -60,7 +65,7 @@ GlobalSettingsConfig::GlobalSettingsConfig(QWidget* parent) : QDialog(parent) - { +{ setupUi(this); startSongGroup = new QButtonGroup(this); startSongGroup->addButton(startLastButton, 0); @@ -94,6 +99,12 @@ connect(buttonTraditionalPreset, SIGNAL(clicked()), SLOT(traditionalPreset())); connect(buttonMDIPreset, SIGNAL(clicked()), SLOT(mdiPreset())); connect(buttonBorlandPreset, SIGNAL(clicked()), SLOT(borlandPreset())); + + connect(pluginPathAdd, SIGNAL(clicked()), SLOT(addPluginPath())); + connect(pluginPathEdit, SIGNAL(clicked()), SLOT(editPluginPath())); + connect(pluginPathRemove, SIGNAL(clicked()), SLOT(removePluginPath())); + connect(pluginPathMoveUp, SIGNAL(clicked()), SLOT(movePluginPathUp())); + connect(pluginPathMoveDown, SIGNAL(clicked()), SLOT(movePluginPathDown())); addMdiSettings(TopWin::ARRANGER); addMdiSettings(TopWin::SCORE); @@ -105,8 +116,18 @@ addMdiSettings(TopWin::LMASTER); addMdiSettings(TopWin::CLIPLIST); addMdiSettings(TopWin::MARKER); - + + for (int i = 0; i < MusEGlobal::numRtAudioDevices; i++){ + deviceAudioBackendComboBox->addItem(MusEGlobal::selectableAudioBackendDevices[i],i); } +#ifndef HAVE_RTAUDIO + deviceAudioBackendComboBox->setDisabled(true); +#endif + + for (int i = 0; i < numAudioSampleRates; i++){ + deviceAudioRate->addItem(QString::number(selectableAudioSampleRates[i]),i); + } +} void GlobalSettingsConfig::addMdiSettings(TopWin::ToplevelType t) { @@ -139,13 +160,19 @@ break; } } - for (unsigned i = 0; i < sizeof(dummyAudioBufSizes)/sizeof(*dummyAudioBufSizes); ++i) { - if (dummyAudioBufSizes[i] == MusEGlobal::config.dummyAudioBufSize) { - dummyAudioSize->setCurrentIndex(i); + for (unsigned i = 0; i < sizeof(selectableAudioBufSizes)/sizeof(*selectableAudioBufSizes); ++i) { + if (selectableAudioBufSizes[i] == MusEGlobal::config.deviceAudioBufSize) { + deviceAudioSize->setCurrentIndex(i); break; } } + for (int i = 0; i < numAudioSampleRates; ++i) { + if (selectableAudioSampleRates[i] == MusEGlobal::config.deviceAudioSampleRate) { + deviceAudioRate->setCurrentIndex(i); + break; + } + } for (unsigned i = 0; i < sizeof(minControlProcessPeriods)/sizeof(*minControlProcessPeriods); ++i) { if (minControlProcessPeriods[i] == MusEGlobal::config.minControlProcessPeriod) { minControlProcessPeriodComboBox->setCurrentIndex(i); @@ -153,10 +180,22 @@ } } - warnIfBadTimingCheckBox->setChecked(MusEGlobal::config.warnIfBadTiming); + autoSaveCheckBox->setChecked(MusEGlobal::config.autoSave); + scrollableSubmenusCheckbox->setChecked(MusEGlobal::config.scrollableSubMenus); + liveWaveUpdateCheckBox->setChecked(MusEGlobal::config.liveWaveUpdate); + preferKnobsVsSlidersCheckBox->setChecked(MusEGlobal::config.preferKnobsVsSliders); + showControlValuesCheckBox->setChecked(MusEGlobal::config.showControlValues); + monitorOnRecordCheckBox->setChecked(MusEGlobal::config.monitorOnRecord); + lineEditStyleHackCheckBox->setChecked(MusEGlobal::config.lineEditStyleHack); + showNoteNamesCheckBox->setChecked(MusEGlobal::config.showNoteNamesInPianoRoll); + preferMidiVolumeDbCheckBox->setChecked(MusEGlobal::config.preferMidiVolumeDb); + warnIfBadTimingCheckBox->setChecked(MusEGlobal::config.warnIfBadTiming); + warnOnFileVersionsCheckBox->setChecked(MusEGlobal::config.warnOnFileVersions); midiSendInit->setChecked(MusEGlobal::config.midiSendInit); midiWarnInitPending->setChecked(MusEGlobal::config.warnInitPending); midiSendCtlDefaults->setChecked(MusEGlobal::config.midiSendCtlDefaults); + sendNullParamsCB->setChecked(MusEGlobal::config.midiSendNullParameters); + optimizeControllersCB->setChecked(MusEGlobal::config.midiOptimizeControllers); guiRefreshSelect->setValue(MusEGlobal::config.guiRefresh); minSliderSelect->setValue(int(MusEGlobal::config.minSlider)); minMeterSelect->setValue(MusEGlobal::config.minMeter); @@ -164,15 +203,12 @@ denormalCheckBox->setChecked(MusEGlobal::config.useDenormalBias); outputLimiterCheckBox->setChecked(MusEGlobal::config.useOutputLimiter); vstInPlaceCheckBox->setChecked(MusEGlobal::config.vstInPlace); - dummyAudioRate->setValue(MusEGlobal::config.dummyAudioSampleRate); - - //DummyAudioDevice* dad = dynamic_cast(audioDevice); - //dummyAudioRealRate->setText(dad ? QString().setNum(sampleRate) : "---"); - //dummyAudioRealRate->setText(QString().setNum(sampleRate)); // Not used any more. p4.0.20 DELETETHIS? - + + deviceAudioBackendComboBox->setCurrentIndex(MusEGlobal::config.deviceAudioBackend); + projDirEntry->setText(MusEGlobal::config.projectBaseFolder); - startSongEntry->setText(MusEGlobal::config.startSong); + startSongEntry->setText(MusEGlobal::config.startSong == "" ? "" : MusEGlobal::config.startSong); startSongGroup->button(MusEGlobal::config.startMode)->setChecked(true); readMidiConfigFromSongCheckBox->setChecked(MusEGlobal::config.startSongLoadConfig); @@ -211,6 +247,7 @@ setBigtimeCurrent->setEnabled(MusEGlobal::muse->bigtimeWindow()); setTransportCurrent->setEnabled(MusEGlobal::muse->transportWindow()); + fixFrozenMDISubWindowsCheckBox->setChecked(MusEGlobal::config.fixFrozenMDISubWindows); showSplash->setChecked(MusEGlobal::config.showSplashScreen); showDidYouKnow->setChecked(MusEGlobal::config.showDidYouKnow); externalWavEditorSelect->setText(MusEGlobal::config.externalWavEditor); @@ -237,6 +274,23 @@ trackHeight->setValue(MusEGlobal::config.trackHeight); + lv2UiBehaviorComboBox->setCurrentIndex(static_cast(MusEGlobal::config.lv2UiBehavior)); + + pluginLadspaPathList->clear(); + pluginLadspaPathList->addItems(MusEGlobal::config.pluginLadspaPathList); + + pluginDssiPathList->clear(); + pluginDssiPathList->addItems(MusEGlobal::config.pluginDssiPathList); + + pluginVstPathList->clear(); + pluginVstPathList->addItems(MusEGlobal::config.pluginVstPathList); + + pluginLinuxVstPathList->clear(); + pluginLinuxVstPathList->addItems(MusEGlobal::config.pluginLinuxVstPathList); + + pluginLv2PathList->clear(); + pluginLv2PathList->addItems(MusEGlobal::config.pluginLv2PathList); + updateMdiSettings(); } @@ -278,22 +332,28 @@ MusEGlobal::config.vstInPlace = vstInPlaceCheckBox->isChecked(); MusEGlobal::config.rtcTicks = rtcResolutions[rtcticks]; MusEGlobal::config.warnIfBadTiming = warnIfBadTimingCheckBox->isChecked(); + MusEGlobal::config.warnOnFileVersions = warnOnFileVersionsCheckBox->isChecked(); MusEGlobal::config.midiSendInit = midiSendInit->isChecked(); MusEGlobal::config.warnInitPending = midiWarnInitPending->isChecked(); MusEGlobal::config.midiSendCtlDefaults = midiSendCtlDefaults->isChecked(); + MusEGlobal::config.midiSendNullParameters = sendNullParamsCB->isChecked(); + MusEGlobal::config.midiOptimizeControllers = optimizeControllersCB->isChecked(); MusEGlobal::config.projectBaseFolder = projDirEntry->text(); - MusEGlobal::config.startSong = startSongEntry->text(); + MusEGlobal::config.startSong = startSongEntry->text() == "" ? "" : startSongEntry->text(); MusEGlobal::config.startMode = startSongGroup->checkedId(); MusEGlobal::config.startSongLoadConfig = readMidiConfigFromSongCheckBox->isChecked(); MusEGlobal::config.newDrumRecordCondition = MusECore::newDrumRecordCondition_t(recDrumGroup->checkedId()); - int das = dummyAudioSize->currentIndex(); - MusEGlobal::config.dummyAudioBufSize = dummyAudioBufSizes[das]; - MusEGlobal::config.dummyAudioSampleRate = dummyAudioRate->value(); + int das = deviceAudioSize->currentIndex(); + MusEGlobal::config.deviceAudioBufSize = selectableAudioBufSizes[das]; + MusEGlobal::config.deviceAudioSampleRate = selectableAudioSampleRates[deviceAudioRate->currentIndex()]; + + MusEGlobal::config.deviceAudioBackend = deviceAudioBackendComboBox->currentIndex(); + int mcp = minControlProcessPeriodComboBox->currentIndex(); MusEGlobal::config.minControlProcessPeriod = minControlProcessPeriods[mcp]; @@ -331,6 +391,16 @@ MusEGlobal::config.mixer2.geometry.setWidth(mixer2W->value()); MusEGlobal::config.mixer2.geometry.setHeight(mixer2H->value()); + MusEGlobal::config.fixFrozenMDISubWindows = fixFrozenMDISubWindowsCheckBox->isChecked(); + MusEGlobal::config.autoSave = autoSaveCheckBox->isChecked(); + MusEGlobal::config.scrollableSubMenus = scrollableSubmenusCheckbox->isChecked(); + MusEGlobal::config.liveWaveUpdate = liveWaveUpdateCheckBox->isChecked(); + MusEGlobal::config.preferKnobsVsSliders = preferKnobsVsSlidersCheckBox->isChecked(); + MusEGlobal::config.showControlValues = showControlValuesCheckBox->isChecked(); + MusEGlobal::config.monitorOnRecord = monitorOnRecordCheckBox->isChecked(); + MusEGlobal::config.lineEditStyleHack = lineEditStyleHackCheckBox->isChecked(); + MusEGlobal::config.showNoteNamesInPianoRoll = showNoteNamesCheckBox->isChecked(); + MusEGlobal::config.preferMidiVolumeDb = preferMidiVolumeDbCheckBox->isChecked(); MusEGlobal::config.showSplashScreen = showSplash->isChecked(); MusEGlobal::config.showDidYouKnow = showDidYouKnow->isChecked(); MusEGlobal::config.externalWavEditor = externalWavEditorSelect->text(); @@ -372,11 +442,15 @@ w->resize(MusEGlobal::config.geometryBigTime.size()); w->move(MusEGlobal::config.geometryBigTime.topLeft()); } - MusEGlobal::muse->resize(MusEGlobal::config.geometryMain.size()); - MusEGlobal::muse->move(MusEGlobal::config.geometryMain.topLeft()); + if(!MusEGlobal::muse->isMaximized() && !MusEGlobal::muse->isMinimized()) + { + MusEGlobal::muse->resize(MusEGlobal::config.geometryMain.size()); + MusEGlobal::muse->move(MusEGlobal::config.geometryMain.topLeft()); + } MusEGlobal::muse->setHeartBeat(); // set guiRefresh - MusEGlobal::midiSeq->msgSetRtc(); // set midi tick rate + if(MusEGlobal::midiSeq) + MusEGlobal::midiSeq->msgSetRtc(); // set midi tick rate if (onlyNewDrumBtn->isChecked()) MusEGlobal::config.drumTrackPreference=MusEGlobal::ONLY_NEW; @@ -389,9 +463,34 @@ MusEGlobal::config.trackHeight = trackHeight->value(); + MusEGlobal::config.lv2UiBehavior = static_cast(lv2UiBehaviorComboBox->currentIndex()); + + + MusEGlobal::config.pluginLadspaPathList.clear(); + for (int i = 0; i < pluginLadspaPathList->count(); ++i) + MusEGlobal::config.pluginLadspaPathList << pluginLadspaPathList->item(i)->text(); + + MusEGlobal::config.pluginDssiPathList.clear(); + for (int i = 0; i < pluginDssiPathList->count(); ++i) + MusEGlobal::config.pluginDssiPathList << pluginDssiPathList->item(i)->text(); + + MusEGlobal::config.pluginVstPathList.clear(); + for (int i = 0; i < pluginVstPathList->count(); ++i) + MusEGlobal::config.pluginVstPathList << pluginVstPathList->item(i)->text(); + + MusEGlobal::config.pluginLinuxVstPathList.clear(); + for (int i = 0; i < pluginLinuxVstPathList->count(); ++i) + MusEGlobal::config.pluginLinuxVstPathList << pluginLinuxVstPathList->item(i)->text(); + + MusEGlobal::config.pluginLv2PathList.clear(); + for (int i = 0; i < pluginLv2PathList->count(); ++i) + MusEGlobal::config.pluginLv2PathList << pluginLv2PathList->item(i)->text(); + applyMdiSettings(); - MusEGlobal::muse->changeConfig(true); // save settings + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + MusEGlobal::muse->changeConfig(true); + raise(); } //--------------------------------------------------------- @@ -525,6 +624,267 @@ updateMdiSettings(); } +void GlobalSettingsConfig::addPluginPath() +{ + QString path; + switch(pluginPathsTabs->currentIndex()) + { + case LadspaTab: + if(pluginLadspaPathList->currentItem()) + path = pluginLadspaPathList->currentItem()->text(); + break; + + case DssiTab: + if(pluginDssiPathList->currentItem()) + path = pluginDssiPathList->currentItem()->text(); + break; + + case VstTab: + if(pluginVstPathList->currentItem()) + path = pluginVstPathList->currentItem()->text(); + break; + + case LinuxVstTab: + if(pluginLinuxVstPathList->currentItem()) + path = pluginLinuxVstPathList->currentItem()->text(); + break; + + case Lv2Tab: + if(pluginLv2PathList->currentItem()) + path = pluginLv2PathList->currentItem()->text(); + break; + + default: + break; + } + + QString new_path = browsePluginPath(path); + + if(new_path.isEmpty()) + return; + + switch(pluginPathsTabs->currentIndex()) + { + case LadspaTab: + pluginLadspaPathList->addItem(new_path); + break; + + case DssiTab: + pluginDssiPathList->addItem(new_path); + break; + + case VstTab: + pluginVstPathList->addItem(new_path); + break; + + case LinuxVstTab: + pluginLinuxVstPathList->addItem(new_path); + break; + + case Lv2Tab: + pluginLv2PathList->addItem(new_path); + break; + + default: + break; + } +} + +void GlobalSettingsConfig::editPluginPath() +{ + QString path; + switch(pluginPathsTabs->currentIndex()) + { + case LadspaTab: + if(pluginLadspaPathList->currentItem()) + path = pluginLadspaPathList->currentItem()->text(); + break; + + case DssiTab: + if(pluginDssiPathList->currentItem()) + path = pluginDssiPathList->currentItem()->text(); + break; + + case VstTab: + if(pluginVstPathList->currentItem()) + path = pluginVstPathList->currentItem()->text(); + break; + + case LinuxVstTab: + if(pluginLinuxVstPathList->currentItem()) + path = pluginLinuxVstPathList->currentItem()->text(); + break; + + case Lv2Tab: + if(pluginLv2PathList->currentItem()) + path = pluginLv2PathList->currentItem()->text(); + break; + + default: + break; + } + + QString new_path = browsePluginPath(path); + + if(new_path.isEmpty()) + return; + + switch(pluginPathsTabs->currentIndex()) + { + case LadspaTab: + if(pluginLadspaPathList->currentItem()) + pluginLadspaPathList->currentItem()->setText(new_path); + break; + + case DssiTab: + if(pluginDssiPathList->currentItem()) + pluginDssiPathList->currentItem()->setText(new_path); + break; + + case VstTab: + if(pluginVstPathList->currentItem()) + pluginVstPathList->currentItem()->setText(new_path); + break; + + case LinuxVstTab: + if(pluginLinuxVstPathList->currentItem()) + pluginLinuxVstPathList->currentItem()->setText(new_path); + break; + + case Lv2Tab: + if(pluginLv2PathList->currentItem()) + pluginLv2PathList->currentItem()->setText(new_path); + break; + + default: + break; + } +} + +QString GlobalSettingsConfig::browsePluginPath(const QString& path) +{ + QString dir = QFileDialog::getExistingDirectory(this, + qApp->translate("@default", + QT_TRANSLATE_NOOP("@default", + "Select plugin directory")), + path); + return dir; +} + +void GlobalSettingsConfig::removePluginPath() +{ + switch(pluginPathsTabs->currentIndex()) + { + case LadspaTab: + foreach(QListWidgetItem* item, pluginLadspaPathList->selectedItems()) + delete item; + break; + + case DssiTab: + foreach(QListWidgetItem* item, pluginDssiPathList->selectedItems()) + delete item; + break; + + case VstTab: + foreach(QListWidgetItem* item, pluginVstPathList->selectedItems()) + delete item; + break; + + case LinuxVstTab: + foreach(QListWidgetItem* item, pluginLinuxVstPathList->selectedItems()) + delete item; + break; + + case Lv2Tab: + foreach(QListWidgetItem* item, pluginLv2PathList->selectedItems()) + delete item; + break; + + default: + return; + } +} + +void GlobalSettingsConfig::movePluginPathUp() +{ + QListWidget* list = 0; + switch(pluginPathsTabs->currentIndex()) + { + case LadspaTab: + list = pluginLadspaPathList; + break; + + case DssiTab: + list = pluginDssiPathList; + break; + + case VstTab: + list = pluginVstPathList; + break; + + case LinuxVstTab: + list = pluginLinuxVstPathList; + break; + + case Lv2Tab: + list = pluginLv2PathList; + break; + + default: + break; + } + + if(list) + { + int row = list->currentRow(); + if(row > 0) + { + list->insertItem(row - 1, list->takeItem(row)); + list->setCurrentRow(row - 1); + } + } +} + +void GlobalSettingsConfig::movePluginPathDown() +{ + QListWidget* list = 0; + switch(pluginPathsTabs->currentIndex()) + { + case LadspaTab: + list = pluginLadspaPathList; + break; + + case DssiTab: + list = pluginDssiPathList; + break; + + case VstTab: + list = pluginVstPathList; + break; + + case LinuxVstTab: + list = pluginLinuxVstPathList; + break; + + case Lv2Tab: + list = pluginLv2PathList; + break; + + default: + break; + } + + if(list) + { + int row = list->currentRow(); + if(row + 1 < list->count()) + { + list->insertItem(row + 1, list->takeItem(row)); + list->setCurrentRow(row + 1); + } + } +} + void GlobalSettingsConfig::browseProjDir() { QString dir = MusEGui::browseProjectFolder(this); @@ -547,7 +907,7 @@ void GlobalSettingsConfig::startSongReset() { - startSongEntry->setText(MusEGlobal::museGlobalShare + QString("/templates/default.med")); + startSongEntry->setText(""); readMidiConfigFromSongCheckBox->setChecked(false); } diff -Nru muse-2.1.2/muse/widgets/genset.h muse-3.0.2+ds1/muse/widgets/genset.h --- muse-2.1.2/muse/widgets/genset.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/genset.h 2017-12-04 21:01:19.000000000 +0000 @@ -41,6 +41,8 @@ class GlobalSettingsConfig : public QDialog, public Ui::GlobalSettingsDialogBase { Q_OBJECT + enum PathTab { LadspaTab, DssiTab, VstTab, LinuxVstTab, Lv2Tab }; + private slots: void updateSettings(); void updateMdiSettings(); @@ -54,6 +56,11 @@ void bigtimeCurrent(); void mainCurrent(); void transportCurrent(); + void editPluginPath(); + void addPluginPath(); + void removePluginPath(); + void movePluginPathUp(); + void movePluginPathDown(); void browseProjDir(); void browseStartSongFile(); void startSongReset(); @@ -67,6 +74,8 @@ QButtonGroup *recDrumGroup; std::list mdisettings; + QString browsePluginPath(const QString& path); + public: GlobalSettingsConfig(QWidget* parent=0); }; diff -Nru muse-2.1.2/muse/widgets/header.cpp muse-3.0.2+ds1/muse/widgets/header.cpp --- muse-2.1.2/muse/widgets/header.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/header.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -44,7 +44,7 @@ case MusECore::Xml::End: return; case MusECore::Xml::Text: - restoreState(QByteArray::fromHex(tag.toAscii())); + restoreState(QByteArray::fromHex(tag.toLatin1())); break; case MusECore::Xml::TagStart: xml.unknown("Header"); @@ -84,6 +84,15 @@ } //--------------------------------------------------------- +// columnLabel +//--------------------------------------------------------- + +QString Header::columnLabel(int col) +{ + return itemModel->horizontalHeaderItem(col)->text(); +} + +//--------------------------------------------------------- // setColumnLabel //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/widgets/header.h muse-3.0.2+ds1/muse/widgets/header.h --- muse-2.1.2/muse/widgets/header.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/header.h 2017-12-04 21:01:19.000000000 +0000 @@ -44,6 +44,7 @@ Header(QWidget* parent=0, const char* name=0); void writeStatus(int level, MusECore::Xml&) const; void readStatus(MusECore::Xml&); + QString columnLabel(int col); void setColumnLabel( const QString & s, int col, int width = -1 ); void setToolTip(int col, const QString &text); void setWhatsThis(int col, const QString &text); diff -Nru muse-2.1.2/muse/widgets/intlabel.cpp muse-3.0.2+ds1/muse/widgets/intlabel.cpp --- muse-2.1.2/muse/widgets/intlabel.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/intlabel.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -38,7 +38,7 @@ specialValue = "off"; min = _min; max = _max; - val = _val+1; // dont optimize away + val = _val+1; // don't optimize away off = _off; setValue(_val); int len = MusECore::num2cols(min, max); diff -Nru muse-2.1.2/muse/widgets/itransformbase.ui muse-3.0.2+ds1/muse/widgets/itransformbase.ui --- muse-2.1.2/muse/widgets/itransformbase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/itransformbase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -94,6 +94,11 @@ RPN + + + Program + + @@ -432,6 +437,11 @@ RPN + + + Program + +
    @@ -615,6 +625,11 @@ Random + + + Toggle + + diff -Nru muse-2.1.2/muse/widgets/knob_and_meter.cpp muse-3.0.2+ds1/muse/widgets/knob_and_meter.cpp --- muse-2.1.2/muse/widgets/knob_and_meter.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/knob_and_meter.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -105,7 +105,7 @@ // Knob::setRange // Set the range and step size of the knob // -// Sets the paramaters that define the shininess of the ring +// Sets the parameters that define the shininess of the ring // surrounding the knob and then proceeds by passing the // parameters to the parent class' setRange() function. //------------------------------------------------------------ @@ -171,7 +171,7 @@ QPen pn; pn.setCapStyle(Qt::FlatCap); - pn.setColor(d_shinyColor.lighter(l_const + abs(value() * l_slope))); + pn.setColor(d_shinyColor.lighter(l_const + fabs(value() * l_slope))); pn.setWidth(d_shineWidth * 2); p->setPen(pn); p->drawArc(aRect, 0, 360 * 16); @@ -273,7 +273,7 @@ //.u Description // Called by QwtSliderBase //------------------------------------------------------------ -// void Knob::getScrollMode( QPoint &p, const Qt::MouseButton &/*button*/, int &scrollMode, int &direction)// prevent compiler warning : unsused parameter +// void Knob::getScrollMode( QPoint &p, const Qt::MouseButton &/*button*/, const Qt::KeyboardModifiers& /*modifiers*/, int &scrollMode, int &direction)// prevent compiler warning : unsused parameter // { // int dx, dy, r; // double arc; @@ -409,7 +409,7 @@ QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); if(hasScale) - d_scale.draw(&p); + d_scale.draw(&p, palette()); ///drawKnob(&p, kRect); drawKnob(&p, r); diff -Nru muse-2.1.2/muse/widgets/knob_and_meter.h muse-3.0.2+ds1/muse/widgets/knob_and_meter.h --- muse-2.1.2/muse/widgets/knob_and_meter.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/knob_and_meter.h 2017-12-04 21:01:19.000000000 +0000 @@ -86,7 +86,7 @@ virtual void resizeEvent(QResizeEvent *e); virtual void mousePressEvent(QMouseEvent *e); // double getValue(const QPoint &p); -// void getScrollMode( QPoint &p, const Qt::MouseButton &button, int &scrollMode, int &direction ); +// void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ); // void scaleChange() { repaint(); } // void fontChange(const QFont &) { repaint(); } diff -Nru muse-2.1.2/muse/widgets/knob.cpp muse-3.0.2+ds1/muse/widgets/knob.cpp --- muse-2.1.2/muse/widgets/knob.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/knob.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -34,6 +34,10 @@ #include #include +// For debugging output: Uncomment the fprintf section. +#define DEBUG_KNOB(dev, format, args...) //fprintf(dev, format, ##args); + + namespace MusEGui { //--------------------------------------------------------- @@ -63,7 +67,7 @@ d_symbol = Line; d_maxScaleTicks = 11; d_knobWidth = 30; - _faceColSel = FALSE; + _faceColSel = false; d_faceColor = palette().color(QPalette::Window); d_rimColor = palette().mid().color(); d_shinyColor = palette().mid().color(); @@ -108,16 +112,13 @@ // Knob::setRange // Set the range and step size of the knob // -// Sets the paramaters that define the shininess of the ring +// Sets the parameters that define the shininess of the ring // surrounding the knob and then proceeds by passing the // parameters to the parent class' setRange() function. //------------------------------------------------------------ void Knob::setRange(double vmin, double vmax, double vstep, int pagesize) { - //if(vmin == d_minValue && vmax == d_maxValue && vstep == d_step && pageSize == d_pageSize) // p4.0.45 - // return; - // divide by zero protection. probably too cautious if (! (vmin == vmax || qMax(-vmin, vmax) == 0)) { @@ -172,7 +173,7 @@ QPen pn; pn.setCapStyle(Qt::FlatCap); - pn.setColor(d_shinyColor.lighter(l_const + abs(value() * l_slope))); + pn.setColor(d_shinyColor.lighter(l_const + fabs(value() * l_slope))); pn.setWidth(d_shineWidth * 2); p->setPen(pn); p->drawArc(aRect, 0, 360 * 16); @@ -213,6 +214,19 @@ recalcAngle(); d_newVal++; repaint(kRect); + + // HACK + // In direct mode let the inherited classes (this) call these in their valueChange() methods, + // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. + // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. + // Hack: Since valueChange() is NOT called if nothing changed, in that case these are called for us by the SliderBase. + if(d_scrollMode == ScrDirect) + { + processSliderPressed(id()); + emit sliderPressed(value(), id()); + } + + // Emits valueChanged if tracking enabled. SliderBase::valueChange(); } @@ -250,16 +264,102 @@ if (fabs(newValue - eqValue) > 0.5 * oneTurn) { - if (newValue < eqValue) - newValue += oneTurn; - else - newValue -= oneTurn; + if (newValue < eqValue) + newValue += oneTurn; + else + newValue -= oneTurn; } return newValue; } +//------------------------------------------------------------ +// +//.F Knob::moveValue +// Determine the value corresponding to a specified mouse movement. +// +//.u Syntax +//.f void SliderBase::moveValue(const QPoint &deltaP, bool fineMode) +// +//.u Parameters +//.p const QPoint &deltaP -- Change in position +//.p bool fineMode -- Fine mode if true, coarse mode if false. +// +//.u Description +// Called by SliderBase +// Coarse mode (the normal mode) maps pixels to values depending on range and width, +// such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. +//------------------------------------------------------------ +double Knob::moveValue(const QPoint &deltaP, bool /*fineMode*/) +{ + // FIXME: To make fine mode workable, we need a way to make the adjustments 'multi-turn'. + + double oneTurn; + double eqValue; + + const QRect& r = rect(); + const QPoint new_p = _lastMousePos + deltaP; + + const int cx = r.x() + r.width() / 2; + const int cy = r.y() + r.height() / 2; + + const double last_dx = double(cx - _lastMousePos.x()); + const double last_dy = double(cy - _lastMousePos.y()); + const double last_arc = atan2(-last_dx, last_dy) * 180.0 / M_PI; + + const double dx = double(cx - new_p.x()); + const double dy = double(cy - new_p.y()); + const double arc = atan2(-dx, dy) * 180.0 / M_PI; + + const double val = value(ConvertNone); + +// if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) +// { +// const double arc_diff = arc - last_arc; +// const double dval_diff = arc_diff * step(); +// const double new_val = val + dval_diff; +// d_valAccum = new_val; // Reset. +// return d_valAccum; +// } + + const double min = minValue(ConvertNone); + const double max = maxValue(ConvertNone); + const double drange = max - min; + + const double last_val = 0.5 * (min + max) + (last_arc + d_nTurns * 360.0) * drange / d_totalAngle; + const double new_val = 0.5 * (min + max) + (arc + d_nTurns * 360.0) * drange / d_totalAngle; + double dval_diff = new_val - last_val; + + //if(fineMode) + // dval_diff /= 10.0; + + d_valAccum += dval_diff; + + DEBUG_KNOB(stderr, "Knob::moveValue value:%.20f last_val:%.20f new_val:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f d_valAccum:%.20f\n", + val, last_val, new_val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, d_valAccum); + + + oneTurn = fabs(drange) * 360.0 / d_totalAngle; + eqValue = val + d_mouseOffset; + + DEBUG_KNOB(stderr, " oneTurn:%.20f eqValue:%.20f\n", oneTurn, eqValue); + if(fabs(d_valAccum - eqValue) > 0.5 * oneTurn) + { + if (d_valAccum < eqValue) + { + d_valAccum += oneTurn; + DEBUG_KNOB(stderr, " added one turn, new d_valAccum:%.20f\n", d_valAccum); + } + else + { + d_valAccum -= oneTurn; + DEBUG_KNOB(stderr, " subtracted one turn, new d_valAccum:%.20f\n", d_valAccum); + } + } + + return d_valAccum; +} //------------------------------------------------------------ @@ -274,8 +374,17 @@ //.u Description // Called by QwtSliderBase //------------------------------------------------------------ -void Knob::getScrollMode( QPoint &p, const Qt::MouseButton &/*button*/, int &scrollMode, int &direction)// prevent compiler warning : unsused parameter +void Knob::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction) { + // If modifier or button is held, jump directly to the position at first. + // After handling it, the caller can change to SrcMouse scroll mode. + if(modifiers & Qt::ControlModifier || button == Qt::MidButton) + { + scrollMode = ScrDirect; + direction = 0; + return; + } + int dx, dy, r; double arc; @@ -326,31 +435,6 @@ repaint(); } -void Knob::mousePressEvent(QMouseEvent *e) -{ - if (e->button() == Qt::MidButton || e->modifiers() & Qt::ControlModifier) { - int xpos = e->x() - width() /2; - double v = float(e->y()) / height() * 1.2; - - double halfRange = (maxValue() - minValue())/2; - double midValue = minValue() + halfRange; - // apply to range - if (xpos < 0) { // left values - v = -v; - } - setValue(v * halfRange + midValue); - SliderBase::valueChange(); - emit sliderMoved(value(),id()); // sliderMoved is used by auxChanged - - // fake a left-click to make the knob still "stick" to - // the mouse. - QMouseEvent temp(e->type(), e->pos(), Qt::LeftButton, e->buttons(), e->modifiers()); - SliderBase::mousePressEvent(&temp); - return; - } - SliderBase::mousePressEvent(e); -} - //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- @@ -409,7 +493,7 @@ QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); if(hasScale) - d_scale.draw(&p); + d_scale.draw(&p, palette()); drawKnob(&p, kRect); //drawMarker(&p, d_oldAngle, d_curFaceColor); //drawMarker(&p, d_angle, d_markerColor); diff -Nru muse-2.1.2/muse/widgets/knob.h muse-3.0.2+ds1/muse/widgets/knob.h --- muse-2.1.2/muse/widgets/knob.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/knob.h 2017-12-04 21:01:19.000000000 +0000 @@ -86,9 +86,10 @@ virtual void paintEvent(QPaintEvent *); virtual void resizeEvent(QResizeEvent *e); - virtual void mousePressEvent(QMouseEvent *e); double getValue(const QPoint &p); - void getScrollMode( QPoint &p, const Qt::MouseButton &button, int &scrollMode, int &direction ); + // Determine the value corresponding to a specified mouse movement. + double moveValue(const QPoint& /*deltaP*/, bool /*fineMode*/ = false); + void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ); void scaleChange() { repaint(); } void fontChange(const QFont &) { repaint(); } diff -Nru muse-2.1.2/muse/widgets/lcd_widgets.cpp muse-3.0.2+ds1/muse/widgets/lcd_widgets.cpp --- muse-2.1.2/muse/widgets/lcd_widgets.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/lcd_widgets.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,1469 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// lcd_widgets.cpp +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "popup_double_spinbox.h" +#include "midictrl.h" +#include "background_painter.h" +#include "lcd_widgets.h" + +// #include + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_LCD_WIDGETS(dev, format, args...) // fprintf(dev, format, ##args); + +namespace MusEGui { + +LCDPainter::LCDPainter() +{ + +} + +void LCDPainter::drawCharacter(QPainter* painter, const QRect& rect, char asciiChar) +{ + const int left = rect.x(); + const int right = rect.x() + rect.width() - 1; + const int top = rect.y(); + const int bottom = rect.y() + rect.height() - 1; + const int half = rect.y() + round(double(rect.height()) / 2.0) - 1; + + const int ileft = left + 1; + const int iright = right - 1; + const int itop = top + 1; + const int ibottom = bottom - 1; + const int ihalfp = half + 1; + const int ihalfm = half - 1; + + switch(asciiChar) + { + case 0x30: + painter->drawLine(left, top, iright, top); + painter->drawLine(right, top, right, ibottom); + painter->drawLine(ileft, bottom, right, bottom); + painter->drawLine(left, itop, left, bottom); + break; + + case 0x31: + painter->drawLine(right, top, right, bottom); + break; + + case 0x32: + painter->drawLine(left, top, iright, top); + painter->drawLine(right, top, right, ihalfm); + painter->drawLine(left, half, right, half); + painter->drawLine(left, ihalfp, left, bottom); + painter->drawLine(ileft, bottom, right, bottom); + break; + + case 0x33: + painter->drawLine(left, top, iright, top); + painter->drawLine(right, top, right, ibottom); + painter->drawLine(left, bottom, right, bottom); + painter->drawLine(ileft, half, iright, half); + break; + + case 0x34: + painter->drawLine(left, top, left, ihalfm); + painter->drawLine(left, half, iright, half); + painter->drawLine(right, top, right, bottom); + break; + + case 0x35: + painter->drawLine(ileft, top, right, top); + painter->drawLine(left, top, left, ihalfm); + painter->drawLine(left, half, right, half); + painter->drawLine(right, ihalfp, right, bottom); + painter->drawLine(left, bottom, iright, bottom); + break; + + case 0x36: + painter->drawLine(ileft, top, right, top); + painter->drawLine(left, top, left, bottom); + painter->drawLine(ileft, bottom, right, bottom); + painter->drawLine(right, half, right, ibottom); + painter->drawLine(ileft, half, iright, half); + break; + + case 0x37: + painter->drawLine(left, top, iright, top); + painter->drawLine(right, top, right, bottom); + break; + + case 0x38: + painter->drawLine(left, top, left, bottom); + painter->drawLine(right, top, right, bottom); + painter->drawLine(ileft, top, iright, top); + painter->drawLine(ileft, half, iright, half); + painter->drawLine(ileft, bottom, iright, bottom); + break; + + case 0x39: + painter->drawLine(left, top, iright, top); + painter->drawLine(right, top, right, bottom); + painter->drawLine(left, bottom, iright, bottom); + painter->drawLine(left, itop, left, half); + painter->drawLine(ileft, half, iright, half); + break; + + case 0x2d: // Minus (dash) '-' + painter->drawLine(left, half, right, half); + break; + } + +} + +void LCDPainter::drawText(QPainter* painter, const QRect& rect, const QString& text, int flags) +{ + if(text.isEmpty()) + return; + + int sz = rect.height(); + + if(sz < 7) + sz = 7; + const int sz3 = round(double(sz) / 2.8); + + const int margin = 1 + sz3 / 6; + + const int chars = text.size(); + + QRect r; + int invidx; + char c; + int curpos = 0; + + const int y = rect.y(); + + // Assume right alignment if unspecified. + if(flags & Qt::AlignLeft) + { + curpos = rect.x(); + for(int idx = 0; idx < chars; ++idx) + { + c = text.at(idx).toLatin1(); + r = QRect(curpos, y, sz3, sz); + drawCharacter(painter, r, c); + curpos += sz3 + margin; + } + } + else + { + curpos = rect.x() + rect.width(); + for(int idx = 0; idx < chars; ++idx) + { + invidx = chars - 1 - idx; + c = text.at(invidx).toLatin1(); + curpos -= sz3 + margin; + r = QRect(curpos, y, sz3, sz); + drawCharacter(painter, r, c); + } + } +} + +//------------------------------------------ +// LCDPatchEdit +//------------------------------------------ + +LCDPatchEdit::LCDPatchEdit(QWidget* parent, + int minFontPoint, + bool ignoreHeight, bool ignoreWidth, + const QString& text, + const QColor& readoutColor, + Qt::WindowFlags flags) + : QFrame(parent, flags), + _readoutColor(readoutColor), + _fontPointMin(minFontPoint), + _fontIgnoreHeight(ignoreHeight), + _fontIgnoreWidth(ignoreWidth), + _text(text) +{ + if(objectName().isEmpty()) + setObjectName(QStringLiteral("LCDPatchEdit")); + + setMouseTracking(true); + setEnabled(true); + setFocusPolicy(Qt::WheelFocus); + //setFocusPolicy(Qt::NoFocus); + +// setAutoFillBackground(false); +// setAttribute(Qt::WA_NoSystemBackground); +// //setAttribute(Qt::WA_StaticContents); +// // This is absolutely required for speed! Otherwise painfully slow because of full background +// // filling, even when requesting small udpdates! Background is drawn by us. +// setAttribute(Qt::WA_OpaquePaintEvent); + + _orient = PatchHorizontal; + + _enableValueToolTips = true; + _editor = 0; + _editMode = false; + _curEditSection = 0; + + _xMargin = 1; + _yMargin = 2; + _sectionSpacing = 4; + + _HBankHovered = false; + _LBankHovered = false; + _ProgHovered = false; + + _LCDPainter = new LCDPainter(); + + _maxAliasedPointSize = -1; + _lastValidPatch = _currentPatch = MusECore::CTRL_VAL_UNKNOWN; + _lastValidHB = _lastValidLB = _lastValidProg = MusECore::CTRL_VAL_UNKNOWN; + _id = -1; + + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + _curFont = font(); + autoAdjustFontSize(); + + setToolTip(tr("High bank: Low bank: Program\n(Ctrl-double-click on/off)")); +} + +LCDPatchEdit::~LCDPatchEdit() +{ + if(_LCDPainter) + delete _LCDPainter; +} + +// Static. +QSize LCDPatchEdit::getMinimumSizeHint(const QFontMetrics& fm, + int xMargin, + int yMargin, + PatchOrientation orient + ) +{ + const int font_height = fm.height(); + int fin_h = 1; + int fin_w = 1; + QRect aRect; + switch(orient) + { + case PatchHorizontal: + fin_h = font_height + 1 + 2 * yMargin; // +1 for extra anti-aliasing space. + aRect.setHeight(font_height); + break; + case PatchVertical: + fin_h = 3 * (font_height + 3) + 2 * yMargin; // +1 for extra anti-aliasing space. + aRect.setHeight(font_height); + break; + } + + const int sz2 = charWidth(aRect); + const int margin = readoutMargin(sz2); + + const int w = 2 * (sz2 + margin) + 1 + margin; // Three digit spaces: two full and one special slim consideration for leading '1'. + const int spacing = 4; + + switch(orient) + { + case PatchHorizontal: + fin_w = 2 * xMargin + 2 * spacing + 3 * w + 2; + break; + + case PatchVertical: + fin_w = w + spacing; + break; + } + + return QSize(fin_w, fin_h); +} + +void LCDPatchEdit::setReadoutOrientation(PatchOrientation orient) +{ + _orient = orient; + resize(this->size()); + update(); +} + +void LCDPatchEdit::setMaxAliasedPointSize(int sz) +{ + if(sz<0)sz=0; + _maxAliasedPointSize = sz; + update(); +} + +void LCDPatchEdit::setMargins(int hor, int vert) +{ + if(hor < 0) + hor = 0; + if(vert < 0) + vert = 0; + _xMargin = hor; + _yMargin = vert; + resize(this->size()); +} + +QSize LCDPatchEdit::sizeHint() const +{ + return getMinimumSizeHint(fontMetrics(), + _xMargin, + _yMargin, + _orient + ); +} + +void LCDPatchEdit::paintEvent(QPaintEvent* e) +{ + e->ignore(); + QFrame::paintEvent(e); + e->accept(); + if(rect().width() <= 0 || rect().height() <= 0) + return; + QPainter painter(this); +// painter.setFont(_curFont); + painter.setFont(font()); + + const int sec_spc2 = _sectionSpacing / 2; + + int hb = (_currentPatch >> 16) & 0xff; + int lb = (_currentPatch >> 8) & 0xff; + int pr = _currentPatch & 0xff; + const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; + const int hboff = is_unk || hb > 127; + const int lboff = is_unk || lb > 127; + const int proff = is_unk || pr > 127; + + const int colon1x = _HBankFieldRect.x() + _HBankFieldRect.width() + sec_spc2 - 1; + const int colon2x = _LBankFieldRect.x() + _LBankFieldRect.width() + sec_spc2 - 1; + // Arbitrary: Just use hbank. + const int colon_y1 = _HBankFieldRect.y() + _HBankFieldRect.height() / 3; + const int colon_y2 = _HBankFieldRect.y() + 2 * _HBankFieldRect.height() / 3; + + QString hbstr, lbstr, prgstr; + QColor hbcol, lbcol, prgcol, hbcolaa, lbcolaa, prgcolaa, oncolaa, offcolaa; + const QPalette& pal = palette(); + QColor offCol(pal.text().color()); + QColor onCol(_readoutColor); + oncolaa = onCol; + oncolaa.setAlpha(180); + offcolaa = offCol; + offcolaa.setAlpha(180); + if(!isEnabled()) + { + const qreal aafactor = 0.75; + onCol.setHsvF(onCol.hsvHueF(), + onCol.hsvSaturationF(), + onCol.valueF() * aafactor); + offCol.setHsv(offCol.hsvHueF(), + offCol.hsvSaturationF(), + offCol.valueF() * aafactor); + oncolaa.setHsvF(oncolaa.hsvHueF(), + oncolaa.hsvSaturationF(), + oncolaa.valueF() * aafactor, + oncolaa.alphaF()); + offcolaa.setHsv(offcolaa.hsvHueF(), + offcolaa.hsvSaturationF(), + offcolaa.valueF() * aafactor, + offcolaa.alphaF()); + } + + if(proff) + { + prgstr = QString("--"); + prgcol = offCol; + prgcolaa = offcolaa; + } + else + { + prgstr = QString::number(pr + 1); + prgcol = onCol; + prgcolaa = oncolaa; + } + + if(hboff) + { + hbstr = QString("--"); + hbcol = offCol; + hbcolaa = offcolaa; + } + else + { + hbstr = QString::number(hb + 1); + hbcol = onCol; + hbcolaa = oncolaa; + } + + if(lboff) + { + lbstr = QString("--"); + lbcol = offCol; + lbcolaa = offcolaa; + } + else + { + lbstr = QString::number(lb + 1); + lbcol = onCol; + lbcolaa = oncolaa; + } + + if(_HBankHovered) + { + hbcol = hbcol.lighter(135); + hbcolaa = hbcolaa.lighter(135); + } + if(_LBankHovered) + { + lbcol = lbcol.lighter(135); + lbcolaa = lbcolaa.lighter(135); + } + if(_ProgHovered) + { + prgcol = prgcol.lighter(135); + prgcolaa = prgcolaa.lighter(135); + } + + ItemBackgroundPainter ibp; + + //ibp.drawBackground(&painter, rect(), pal); + + switch(_orient) + { + case PatchHorizontal: + painter.setRenderHint(QPainter::Antialiasing, false); + ibp.drawBackground(&painter, rect(), pal); + + painter.setPen(offCol); + painter.drawPoint(colon1x, colon_y1); + painter.drawPoint(colon1x, colon_y2); + + painter.setPen(offCol); + painter.drawPoint(colon2x, colon_y1); + painter.drawPoint(colon2x, colon_y2); + break; + + case PatchVertical: + ibp.drawBackground(&painter, _HBankRect, pal); + ibp.drawBackground(&painter, _LBankRect, pal); + ibp.drawBackground(&painter, _ProgRect, pal); + break; + } + + painter.setPen(hbcolaa); + painter.setRenderHint(QPainter::Antialiasing, true); + _LCDPainter->drawText(&painter, _HBankFieldRect, hbstr); + painter.setPen(hbcol); + painter.setRenderHint(QPainter::Antialiasing, false); + _LCDPainter->drawText(&painter, _HBankFieldRect, hbstr); + + painter.setPen(lbcolaa); + painter.setRenderHint(QPainter::Antialiasing, true); + _LCDPainter->drawText(&painter, _LBankFieldRect, lbstr); + painter.setPen(lbcol); + painter.setRenderHint(QPainter::Antialiasing, false); + _LCDPainter->drawText(&painter, _LBankFieldRect, lbstr); + + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setPen(prgcolaa); + _LCDPainter->drawText(&painter, _ProgFieldRect, prgstr); + painter.setPen(prgcol); + painter.setRenderHint(QPainter::Antialiasing, false); + _LCDPainter->drawText(&painter, _ProgFieldRect, prgstr); +} + +int LCDPatchEdit::value() const +{ + return _currentPatch; +} + +void LCDPatchEdit::setValue(int v) +{ + if(_currentPatch == v) + return; + _currentPatch = v; + update(); +} + +void LCDPatchEdit::setLastValidPatch(int v) +{ + if(_lastValidPatch == v) + return; + _lastValidPatch = v; + //update(); // Not required. +} + +void LCDPatchEdit::setLastValidBytes(int hbank, int lbank, int prog) +{ + if(_lastValidHB != hbank) + { + _lastValidHB = hbank; + //update(); // Not required. + } + + if(_lastValidLB != lbank) + { + _lastValidLB = lbank; + //update(); // Not required. + } + + if(_lastValidProg != prog) + { + _lastValidProg = prog; + //update(); // Not required. + } +} + +//--------------------------------------------------------- +// autoAdjustFontSize +// w: Widget to auto adjust font size +// s: String to fit +// ignoreWidth: Set if dealing with a vertically constrained widget - one which is free to resize horizontally. +// ignoreHeight: Set if dealing with a horizontally constrained widget - one which is free to resize vertically. +//--------------------------------------------------------- + +bool LCDPatchEdit::autoAdjustFontSize() +{ +// FIXME: Disabled for now, the font modulates back and forth, not very good ATM. +// May have to revert to the font-checking iteration loop scheme. + +// QFont fnt = font(); // This is the maximum font. +// int max = fnt.pointSize(); +// int min = _fontPointMin; +// +// // In case the max or min was obtained from QFont::pointSize() which returns -1 +// // if the font is a pixel font, or if min is greater than max... +// // Limit the minimum and maximum sizes to something at least readable. +// if(max < 4) +// max = 4; +// if(min < 4) +// min = 4; +// if(max < min) +// max = min; +// +// //qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); +// //QRectF r = boundingRect(); +// QRectF r = rect(); +// //QFont f = painter->font(); +// +// +// //if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. +// if(_fontIgnoreWidth || _text.isEmpty()) // Also avoid divide by zero below. +// { +// if(fnt.pointSize() != max) +// { +// fnt.setPointSize(max); +// // setFont(fnt); +// _curFont = fnt; +// update(); +// } +// } +// else +// { +// //qreal aspectRatio = painter->fontMetrics().lineSpacing() / painter->fontMetrics().averageCharWidth(); +// qreal aspectRatio = fontMetrics().lineSpacing() / fontMetrics().averageCharWidth(); +// // int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (_text.length() * 3)) * aspectRatio; +// int pixelsize = sqrt(r.width() * r.height() / aspectRatio / _text.length()) * aspectRatio; +// fnt.setPixelSize(pixelsize); +// //int flags = Qt::AlignCenter|Qt::TextDontClip|Qt::TextWordWrap; +// int flags = Qt::AlignCenter; +// //if ((pixelsize * lod) < 13) +// // flags |= Qt::TextWrapAnywhere; +// QFontMetricsF fmf(fnt); +// QRectF tbr = fmf.boundingRect(r, flags, _text); +// pixelsize = fnt.pixelSize() * qMin(r.width() * 0.95 / tbr.width(), r.height() * 0.95 / tbr.height()); +// // if(pixelsize < min) +// // pixelsize = min; +// // else if(pixelsize > max) +// // pixelsize = max; +// fnt.setPixelSize(pixelsize); +// const QFontInfo fi(fnt); +// const int pointsize = fi.pointSize(); +// if(pointsize <= min) +// fnt.setPointSize(min); +// else if(pointsize >= max) +// fnt.setPointSize(max); +// // setFont(fnt); +// _curFont = fnt; +// //painter->drawText(r,flags,stitle); +// update(); +// } + + // Force minimum height. Use the expected height for the highest given point size. + // This way the mixer strips aren't all different label heights, but can be larger if necessary. + // Only if ignoreHeight is set (therefore the height is adjustable). +// if(_fontIgnoreHeight) +// { +// // FIXME Disabled for now, as per above. +// // fnt.setPointSize(max); +// // const QFontMetrics fm(fnt); +// const QFontMetrics fm(font()); +// +// // Set the label's minimum height equal to the height of the font. +// setMinimumHeight(fm.height() + 2 * frameWidth()); +// } +// + return true; +} + +void LCDPatchEdit::setText(const QString& txt) +{ + if(_text == txt) + return; + _text = txt; + autoAdjustFontSize(); + update(); +} + +QRect LCDPatchEdit::activeDrawingArea() const +{ + // +1 for extra anti-aliasing space. + return rect().adjusted(_xMargin, _yMargin + 1, -_xMargin, -_yMargin); +} + +// Static. +int LCDPatchEdit::charWidth(const QRect& aRect) +{ + int sz = aRect.height(); + if(sz < 7) + sz = 7; + return round(double(sz) / 2.8); +} + +// Static. +int LCDPatchEdit::readoutMargin(int charWidth) +{ + return 1 + charWidth / 6; +} + +void LCDPatchEdit::resizeEvent(QResizeEvent* e) +{ + e->ignore(); + QFrame::resizeEvent(e); + e->accept(); + autoAdjustFontSize(); + + const QFontMetrics fm = fontMetrics(); + const int font_height = fm.height(); + + QRect aRect = activeDrawingArea(); + switch(_orient) + { + case PatchHorizontal: + break; + + case PatchVertical: + aRect.setHeight(font_height); + break; + } + + const int sz2 = charWidth(aRect); + const int margin = readoutMargin(sz2); + + const int rectw3 = (aRect.width() - margin * 2) / 3; + const int w = 2 * (sz2 + margin) + 1 + margin; // Three digit spaces: two full and one special slim consideration for leading '1'. + int spacing = rectw3 - w; + if(spacing < 4) + spacing = 4; + if(spacing > 16) + spacing = 16; + _sectionSpacing = spacing; + const int indent = aRect.width() / 2 - w / 2; + + int x0, x1, x2; + int recty1 = 0, recty2 = 0, recty3 = 0, recth = 0; + int frecty1, frecty2, frecty3, frecth; + + switch(_orient) + { + case PatchHorizontal: + x0 = aRect.x() + _xMargin; + x1 = x0 + w + _sectionSpacing; + x2 = x1 + w + _sectionSpacing; + + recty1 = recty2 = recty3 = rect().y() + _yMargin; + recth = rect().height() - 2 * _yMargin; + + frecty1 = frecty2 = frecty3 = aRect.y(); + frecth = aRect.height(); + + + _HBankFieldRect.setRect(x0, frecty1, w, frecth); + _LBankFieldRect.setRect(x1, frecty2, w, frecth); + _ProgFieldRect.setRect (x2, frecty3, w, frecth); + break; + + case PatchVertical: + x0 = x1 = x2 = aRect.x() + indent; // + _xMargin; + + recth = font_height + 3; + recty1 = rect().y() + _yMargin; + recty2 = recty1 + recth; + recty3 = recty2 + recth; + + frecth = font_height; + frecty1 = aRect.y() + 1; + frecty2 = frecty1 + frecth + 3; + frecty3 = frecty2 + frecth + 3; + + _HBankFieldRect.setRect(x0 + _sectionSpacing / 2 + _xMargin, frecty1, w, frecth); + _LBankFieldRect.setRect(x1 + _sectionSpacing / 2 + _xMargin, frecty2, w, frecth); + _ProgFieldRect.setRect (x2 + _sectionSpacing / 2 + _xMargin, frecty3, w, frecth); + break; + } + + _HBankRect.setRect(x0, recty1, w + _sectionSpacing, recth); + _LBankRect.setRect(x1, recty2, w + _sectionSpacing, recth); + _ProgRect.setRect (x2, recty3, w + _sectionSpacing, recth); + + update(); +} + +void LCDPatchEdit::mouseMoveEvent(QMouseEvent *e) +{ + //fprintf(stderr, "LCDPatchEdit::mouseMoveEvent\n"); + e->ignore(); + QFrame::mouseMoveEvent(e); + e->accept(); + + QPoint p = e->pos(); + + bool doupd = false; + + if(_HBankRect.contains(p) != _HBankHovered) + { + _HBankHovered = !_HBankHovered; + doupd = true; + } + + if(_LBankRect.contains(p) != _LBankHovered) + { + _LBankHovered = !_LBankHovered; + doupd = true; + } + + if(_ProgRect.contains(p) != _ProgHovered) + { + _ProgHovered = !_ProgHovered; + doupd = true; + } + + if(doupd) + update(); +} + +void LCDPatchEdit::mousePressEvent(QMouseEvent* e) +{ + Qt::MouseButtons buttons = e->buttons(); + e->accept(); + emit pressed(e->pos(), _id, buttons, e->modifiers()); + + if(buttons == Qt::RightButton) + emit rightClicked(e->globalPos(), _id); +} + +void LCDPatchEdit::mouseReleaseEvent(QMouseEvent* e) +{ + e->accept(); + emit released(e->pos(), _id, e->buttons(), e->modifiers()); +} + +void LCDPatchEdit::enterEvent(QEvent *e) +{ + //fprintf(stderr, "LCDPatchEdit::enterEvent\n"); + QPoint p = mapFromGlobal(cursor().pos()); + + bool doupd = false; + + if(_HBankRect.contains(p) != _HBankHovered) + { + _HBankHovered = !_HBankHovered; + doupd = true; + } + + if(_LBankRect.contains(p) != _LBankHovered) + { + _LBankHovered = !_LBankHovered; + doupd = true; + } + + if(_ProgRect.contains(p) != _ProgHovered) + { + _ProgHovered = !_ProgHovered; + doupd = true; + } + + e->ignore(); + QFrame::enterEvent(e); + e->accept(); + if(doupd) + update(); +} + +void LCDPatchEdit::leaveEvent(QEvent *e) +{ + //fprintf(stderr, "LCDPatchEdit::leaveEvent\n"); + bool doupd = false; + + if(_HBankHovered) + { + _HBankHovered = false; + doupd = true; + } + if(_LBankHovered) + { + _LBankHovered = false; + doupd = true; + } + + if(_ProgHovered) + { + _ProgHovered = false; + doupd = true; + } + + e->ignore(); + QFrame::leaveEvent(e); + e->accept(); + if(doupd) + update(); +} + + + + +void LCDPatchEdit::setFontIgnoreDimensions(bool ignoreHeight, bool ignoreWidth) +{ + _fontIgnoreWidth = ignoreWidth; + _fontIgnoreHeight = ignoreHeight; + autoAdjustFontSize(); + update(); +} + +void LCDPatchEdit::setFontPointMin(int point) +{ + _fontPointMin = point; + autoAdjustFontSize(); + update(); +} + +void LCDPatchEdit::editorReturnPressed() +{ + DEBUG_LCD_WIDGETS(stderr, "LCDPatchEdit::editorReturnPressed\n"); + _editMode = false; + if(_editor) + { + int hb = (_currentPatch >> 16) & 0xff; + int lb = (_currentPatch >> 8) & 0xff; + int pr = _currentPatch & 0xff; + const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; + const int lasthb = (_lastValidPatch >> 16) & 0xff; + const int lastlb = (_lastValidPatch >> 8) & 0xff; + const int lastpr = _lastValidPatch & 0xff; + const bool last_is_unk = _lastValidPatch == MusECore::CTRL_VAL_UNKNOWN; + int new_val = _currentPatch; + + switch(_curEditSection) + { + case HBankSection: + hb = _editor->value(); + if(hb == 0) + hb = 0xff; + else + { + --hb; + if(is_unk) + { + lb = lastlb; + pr = lastpr; + if(last_is_unk) + { + lb = 0xff; + pr = 0; + } + } + } + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + break; + + case LBankSection: + lb = _editor->value(); + if(lb == 0) + lb = 0xff; + else + { + --lb; + if(is_unk) + { + hb = lasthb; + pr = lastpr; + if(last_is_unk) + { + hb = 0xff; + pr = 0; + } + } + } + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + break; + + case ProgSection: + pr = _editor->value(); + if(pr == 0) + new_val = MusECore::CTRL_VAL_UNKNOWN; + else + { + --pr; + if(is_unk) + { + hb = lasthb; + lb = lastlb; + if(last_is_unk) + { + hb = 0xff; + lb = 0xff; + } + } + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + } + break; + } + + if(value() != new_val) + { + setValue(new_val); + emit valueChanged(value(), _id); + } + + _editor->deleteLater(); + _editor = 0; + } + setFocus(); // FIXME There are three sections. Need to clear focus for now. +// clearFocus(); +} + +void LCDPatchEdit::editorEscapePressed() +{ + DEBUG_LCD_WIDGETS(stderr, "LCDPatchEdit::editorEscapePressed\n"); + _editMode = false; + if(_editor) + { + _editor->deleteLater(); + _editor = 0; + setFocus(); //FIXME There are three sections. Need to clear focus for now. +// clearFocus(); + } +} + +void LCDPatchEdit::keyPressEvent(QKeyEvent* e) +{ + switch (e->key()) + { + case Qt::Key_Return: + case Qt::Key_Enter: + { + // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. + if(!_editor || !_editor->hasFocus()) + showEditor(); + e->accept(); + return; + } + break; + + default: + break; + } + + e->ignore(); + QFrame::keyPressEvent(e); +} + +bool LCDPatchEdit::event(QEvent* e) +{ + switch(e->type()) + { + // FIXME: Doesn't work. + case QEvent::NonClientAreaMouseButtonPress: + DEBUG_LCD_WIDGETS(stderr, "LCDPatchEdit::event NonClientAreaMouseButtonPress\n"); + e->accept(); + _editMode = false; + if(_editor) + { + _editor->deleteLater(); + _editor = 0; + } + return true; + break; + + default: + break; + } + + return QFrame::event(e); +} + +void LCDPatchEdit::wheelEvent(QWheelEvent* e) +{ + QPoint p = e->pos(); + + bool doupd = false; + + if(_HBankRect.contains(p) != _HBankHovered) + { + _HBankHovered = !_HBankHovered; + doupd = true; + } + + if(_LBankRect.contains(p) != _LBankHovered) + { + _LBankHovered = !_LBankHovered; + doupd = true; + } + + if(_ProgRect.contains(p) != _ProgHovered) + { + _ProgHovered = !_ProgHovered; + doupd = true; + } + + if(doupd) + update(); + + int hb = (_currentPatch >> 16) & 0xff; + int lb = (_currentPatch >> 8) & 0xff; + int pr = _currentPatch & 0xff; + const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; + const int hboff = is_unk || hb > 127; + const int lboff = is_unk || lb > 127; + const int proff = is_unk || pr > 127; + + const int lasthb = (_lastValidPatch >> 16) & 0xff; + const int lastlb = (_lastValidPatch >> 8) & 0xff; + const int lastpr = _lastValidPatch & 0xff; + const bool last_is_unk = _lastValidPatch == MusECore::CTRL_VAL_UNKNOWN; + + const int lasthboff = last_is_unk || lasthb > 127; + const int lastlboff = last_is_unk || lastlb > 127; + const int lastproff = last_is_unk || lastpr > 127; + +// const QPoint pixelDelta = e->pixelDelta(); + const QPoint angleDegrees = e->angleDelta() / 8; + int delta = 0; +// if(!pixelDelta.isNull()) +// delta = pixelDelta.y(); +// else + if(!angleDegrees.isNull()) + delta = angleDegrees.y() / 15; + + int section = -1; + int new_val = _currentPatch; + + if(_HBankHovered) + { + section = HBankSection; + if(delta > 0 || !hboff) + { + if(hboff) + { + hb = _lastValidHB; + if(lasthboff) + hb = 0; + if(is_unk) + { + lb = lastlb; + pr = lastpr; + if(last_is_unk) + { + lb = 0xff; + pr = 0; + } + } + } + else + { + hb += delta; + if(hb < 0) + hb = 0xff; + else if(hb > 127) + hb = 127; + } + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + } + } + else if(_LBankHovered) + { + section = LBankSection; + if(delta > 0 || !lboff) + { + if(lboff) + { + lb = _lastValidLB; + if(lastlboff) + lb = 0; + if(is_unk) + { + hb = lasthb; + pr = lastpr; + if(last_is_unk) + { + hb = 0xff; + pr = 0; + } + } + } + else + { + lb += delta; + if(lb < 0) + lb = 0xff; + else if(lb > 127) + lb = 127; + } + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + } + } + else if(_ProgHovered) + { + section = ProgSection; + if(delta > 0 || !proff) + { + if(proff) + { + pr = _lastValidProg; + if(lastproff) + pr = 0; + if(is_unk) + { + hb = lasthb; + lb = lastlb; + if(last_is_unk) + { + hb = 0xff; + lb = 0xff; + } + } + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + } + else + { + pr += delta; + if(pr < 0) + new_val = MusECore::CTRL_VAL_UNKNOWN; + else + { + if(pr > 127) + pr = 127; + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + } + } + } + } + else + { + e->ignore(); + return QFrame::wheelEvent(e); + } + + e->accept(); + + if(value() != new_val) + { + setValue(new_val); + // Show a handy tooltip value box. + if(_enableValueToolTips) + showValueToolTip(e->globalPos(), section); + emit valueChanged(value(), _id); + } + //fprintf(stderr, "LCDPatchEdit::wheelEvent _HBankHovered:%d _LBankHovered:%d _ProgHovered:%d\n", _HBankHovered, _LBankHovered, _ProgHovered); +} + +void LCDPatchEdit::mouseDoubleClickEvent(QMouseEvent* e) +{ + const Qt::MouseButtons buttons = e->buttons(); + const Qt::KeyboardModifiers keys = e->modifiers(); + + if(buttons == Qt::LeftButton && !_editMode) + { + DEBUG_LCD_WIDGETS(stderr, " left button\n"); + if(keys == Qt::ControlModifier) + { + if(_HBankHovered || _LBankHovered || _ProgHovered) + { + int hb = (_currentPatch >> 16) & 0xff; + int lb = (_currentPatch >> 8) & 0xff; + int pr = _currentPatch & 0xff; + const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; + const int hboff = is_unk || hb > 127; + const int lboff = is_unk || lb > 127; + const int proff = is_unk || pr > 127; + const int lasthb = (_lastValidPatch >> 16) & 0xff; + const int lastlb = (_lastValidPatch >> 8) & 0xff; + const int lastpr = _lastValidPatch & 0xff; + const bool last_is_unk = _lastValidPatch == MusECore::CTRL_VAL_UNKNOWN; + const bool last_hb_is_unk = _lastValidHB == MusECore::CTRL_VAL_UNKNOWN; + const bool last_lb_is_unk = _lastValidLB == MusECore::CTRL_VAL_UNKNOWN; + const bool last_pr_is_unk = _lastValidProg == MusECore::CTRL_VAL_UNKNOWN; + int new_val = _currentPatch; + + if(_HBankHovered) + { + if(hboff) + { + hb = _lastValidHB; + if(last_hb_is_unk) + hb = 0; + if(is_unk) + { + lb = lastlb; + pr = lastpr; + if(last_is_unk) + { + lb = 0xff; + pr = 0; + } + } + } + else + hb = 0xff; + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + } + else if(_LBankHovered) + { + if(lboff) + { + lb = _lastValidLB; + if(last_lb_is_unk) + lb = 0; + if(is_unk) + { + hb = lasthb; + pr = lastpr; + if(last_is_unk) + { + hb = 0xff; + pr = 0; + } + } + } + else + lb = 0xff; + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + } + else if(_ProgHovered) + { + if(proff) + { + pr = _lastValidProg; + if(last_pr_is_unk) + pr = 0; + if(is_unk) + { + hb = lasthb; + lb = lastlb; + if(last_is_unk) + { + hb = 0xff; + lb = 0xff; + } + } + new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); + } + else + new_val = MusECore::CTRL_VAL_UNKNOWN; + } + + if(new_val != value()) + { + setValue(new_val); + emit valueChanged(value(), id()); + } + e->accept(); + return; + } + } + // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. + else if(keys == Qt::NoModifier && (!_editor || !_editor->hasFocus())) + { + int sec = -1; + if(_HBankHovered) + sec = HBankSection; + else if(_LBankHovered) + sec = LBankSection; + else if(_ProgHovered) + sec = ProgSection; + + if(sec != -1) + { + _curEditSection = sec; + showEditor(); + e->accept(); + return; + } + } + } + + e->ignore(); + QFrame::mouseDoubleClickEvent(e); +} + +void LCDPatchEdit::showEditor() +{ + if(_editMode) + return; + + if(!_editor) + { + DEBUG_LCD_WIDGETS(stderr, " creating editor\n"); + _editor = new PopupDoubleSpinBox(this); + _editor->setFrame(false); + _editor->setFocusPolicy(Qt::WheelFocus); + _editor->setDecimals(0); + _editor->setSpecialValueText(tr("off")); + _editor->setMinimum(0); + _editor->setMaximum(128); + connect(_editor, SIGNAL(returnPressed()), SLOT(editorReturnPressed())); + connect(_editor, SIGNAL(escapePressed()), SLOT(editorEscapePressed())); + } + int w = width(); + //if (w < _editor->sizeHint().width()) + // w = _editor->sizeHint().width(); + QRect r; + const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; + switch(_curEditSection) + { + case HBankSection: + { + r = _HBankRect; + int hb = (_currentPatch >> 16) & 0xff; + if(is_unk || hb < 0 || hb > 127) + hb = 0; + else + ++hb; + _editor->setValue(hb); + } + break; + + case LBankSection: + { + r = _LBankRect; + int lb = (_currentPatch >> 8) & 0xff; + if(is_unk || lb < 0 || lb > 127) + lb = 0; + else + ++lb; + _editor->setValue(lb); + } + break; + + case ProgSection: + { + r = _ProgRect; + int pr = _currentPatch & 0xff; + if(is_unk || pr < 0 || pr > 127) + pr = 0; + else + ++pr; + _editor->setValue(pr); + } + break; + } + + switch(_orient) + { + case PatchHorizontal: + _editor->setGeometry(0, 0, w, height()); + break; + + case PatchVertical: + _editor->setGeometry(0, r.y(), w, r.height()); + break; + } + + DEBUG_LCD_WIDGETS(stderr, " x:%d y:%d w:%d h:%d\n", _editor->x(), _editor->y(), w, _editor->height()); + _editor->selectAll(); + _editMode = true; + _editor->show(); + _editor->setFocus(); +} + +QString LCDPatchEdit::toolTipValueText(int section) const +{ + const int hb = (_currentPatch >> 16) & 0xff; + const int lb = (_currentPatch >> 8) & 0xff; + const int pr = _currentPatch & 0xff; + const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; + const int hboff = is_unk || hb > 127; + const int lboff = is_unk || lb > 127; + const int proff = is_unk || pr > 127; + + const QString off_text = tr("off"); + const QString hb_desc_text = tr("High bank"); + const QString lb_desc_text = tr("Low bank"); + const QString pr_desc_text = tr("Program"); + const QString hbtext = hboff ? off_text : QString::number(hb + 1); + const QString lbtext = lboff ? off_text : QString::number(lb + 1); + const QString prtext = proff ? off_text : QString::number(pr + 1); + + switch(section) + { + case HBankSection: + return QString("%1: %2") + .arg(hb_desc_text).arg(hbtext); + break; + + case LBankSection: + return QString("%1: %2") + .arg(lb_desc_text).arg(lbtext); + break; + + case ProgSection: + return QString("%1: %2") + .arg(pr_desc_text).arg(prtext); + break; + + default: + return QString("%1: %2\n%3: %4\n%5: %6") + .arg(hb_desc_text).arg(hbtext) + .arg(lb_desc_text).arg(lbtext) + .arg(pr_desc_text).arg(prtext); + break; + } +} + +void LCDPatchEdit::showValueToolTip(QPoint /*p*/, int section) +{ + const QString txt = toolTipValueText(section); + if(!txt.isEmpty()) + { + QToolTip::showText(mapToGlobal(pos()), txt, 0, QRect(), 3000); + } +} + + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/lcd_widgets.h muse-3.0.2+ds1/muse/widgets/lcd_widgets.h --- muse-2.1.2/muse/widgets/lcd_widgets.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/lcd_widgets.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,210 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// lcd_widgets.h +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __LCD_WIDGETS_H__ +#define __LCD_WIDGETS_H__ + +#include + +class QPainter; +class QColor; +class QRect; +class QString; +class QEvent; +class QMouseEvent; +class QWheelEvent; +class QKeyEvent; + +namespace MusEGui { + +class PopupDoubleSpinBox; + +//--------------------------------------------------------- +// LCDPainter +//--------------------------------------------------------- + +class LCDPainter +{ + public: + enum ScalePos { None, Left, Right, Top, Bottom, Embedded }; + enum TextHighlightMode { TextHighlightNone, TextHighlightAlways, TextHighlightSplit, TextHighlightShadow }; + + private: + + public: + LCDPainter(); + + void drawCharacter(QPainter* painter, const QRect& rect, char asciiChar); + void drawText(QPainter* painter, const QRect& rect, const QString& text, int flags = 0); +}; + +//--------------------------------------------------------- +// LCDPatchEdit +//--------------------------------------------------------- + +class LCDPatchEdit : public QFrame +{ + Q_OBJECT + //Q_PROPERTY(QString text READ text WRITE setText) + + public: + enum PatchSections { HBankSection, LBankSection, ProgSection }; + enum PatchOrientation { PatchHorizontal = 0, PatchVertical }; + + protected: + PatchOrientation _orient; + + int _maxAliasedPointSize; + int _xMargin; + int _yMargin; + int _sectionSpacing; + int _currentPatch; + int _lastValidPatch; + int _lastValidHB; + int _lastValidLB; + int _lastValidProg; + + QColor _readoutColor; + + LCDPainter* _LCDPainter; + + int _id; + int _fontPointMin; + bool _fontIgnoreHeight; + bool _fontIgnoreWidth; + QString _text; + QFont _curFont; + bool _enableValueToolTips; + + QRect _HBankRect; + QRect _LBankRect; + QRect _ProgRect; + QRect _HBankFieldRect; + QRect _LBankFieldRect; + QRect _ProgFieldRect; + + bool _HBankHovered; + bool _LBankHovered; + bool _ProgHovered; + + PopupDoubleSpinBox* _editor; + bool _editMode; + int _curEditSection; + + bool autoAdjustFontSize(); + // The total active drawing area, not including margins. + QRect activeDrawingArea() const; + + void showEditor(); + // Show a handy tooltip value box. + void showValueToolTip(QPoint, int section = -1); + + virtual void paintEvent(QPaintEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void wheelEvent(QWheelEvent*); + virtual void mouseDoubleClickEvent(QMouseEvent*); + virtual void resizeEvent(QResizeEvent*); + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseReleaseEvent(QMouseEvent*); + virtual void enterEvent(QEvent*); + virtual void leaveEvent(QEvent*); + virtual void keyPressEvent(QKeyEvent*); + virtual bool event(QEvent*); + + protected slots: + void editorReturnPressed(); + void editorEscapePressed(); + + signals: + void pressed(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); + void released(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); + void valueChanged(int value, int id); + void rightClicked(QPoint p, int id); + + public: + explicit LCDPatchEdit(QWidget* parent = 0, + int minFontPoint = 5, + bool ignoreHeight = true, bool ignoreWidth = false, + const QString& text = QString(), + const QColor& readoutColor = QColor(0,255,255), + Qt::WindowFlags flags = 0); + + virtual ~LCDPatchEdit(); + + // The width of a character, not including inter-character space, in a given active area. + static int charWidth(const QRect& aRect); + // The amount of space between the blocks of digits, for a given character width. + static int readoutMargin(int charWidth); + + static QSize getMinimumSizeHint(const QFontMetrics& fm, + int xMargin = 0, + int yMargin = 0, + PatchOrientation orient = PatchHorizontal + ); + + int id() const { return _id; } + void setId(int i) { _id = i; } + + void setReadoutOrientation(PatchOrientation); + + QColor readoutColor() const { return _readoutColor; } + void setReadoutColor(const QColor& c) { _readoutColor = c; update(); } + + int value() const; + void setValue(int v); + void setLastValidPatch(int v); + void setLastValidBytes(int hbank, int lbank, int prog); + + bool valueToolTipsEnabled() const { return _enableValueToolTips; } + void setEnableValueToolTips(bool v) { _enableValueToolTips = v; } + QString toolTipValueText(int section = -1) const; + + // At what point size to switch from aliased text to non-aliased text. Zero means always use anti-aliasing. + // Here in CompactPatchEdit, this only affects the CompactSliders so far, not the patch label. + // If -1, no value has been set and default is each widget's setting. + int maxAliasedPointSize() const { return _maxAliasedPointSize; } + // Sets at what point size to switch from aliased text (brighter, crisper but may look too jagged and unreadable with some fonts) + // to non-aliased text (dimmer, fuzzier but looks better). Zero means always use anti-aliasing. Default is each widget's setting. + // Here in CompactPatchEdit, this only affects the CompactSliders so far, not the patch label. + void setMaxAliasedPointSize(int sz); + + const QString& text() const { return _text; } + void setText(const QString& txt); + + int fontPointMin() const { return _fontPointMin; } + void setFontPointMin(int point); + + bool fontIgnoreWidth() const { return _fontIgnoreWidth; } + bool fontIgnoreHeight() const { return _fontIgnoreHeight; } + void setFontIgnoreDimensions(bool ignoreHeight, bool ignoreWidth = false); + + void setMargins(int hor, int vert); + + virtual QSize sizeHint() const; +}; + + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/lcombo.cpp muse-3.0.2+ds1/muse/widgets/lcombo.cpp --- muse-2.1.2/muse/widgets/lcombo.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/lcombo.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -48,6 +48,7 @@ layout->addSpacing(5); layout->addWidget(box); layout->addSpacing(5); + layout->setContentsMargins(0, 0, 0, 0); connect(box, SIGNAL(activated(int)), SIGNAL(activated(int))); } diff -Nru muse-2.1.2/muse/widgets/line_edit.cpp muse-3.0.2+ds1/muse/widgets/line_edit.cpp --- muse-2.1.2/muse/widgets/line_edit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/line_edit.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,295 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// line_edit.cpp +// (C) Copyright 2017 Tim E. Real (terminator356 on sourceforge) +// +// 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 + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include + +#include "line_edit.h" + +namespace MusEGui { + +//--------------------------------------------------------- +// LineEdit +//--------------------------------------------------------- + +LineEdit::LineEdit(QWidget* parent, const char* name) : QLineEdit(parent) +{ + setObjectName(name); + //setAutoFillBackground(true); + _enableStyleHack = true; +} + +LineEdit::LineEdit(const QString& contents, QWidget* parent, const char* name) + : QLineEdit(contents, parent) +{ + setObjectName(name); + //setAutoFillBackground(true); + _enableStyleHack = true; +} + +void LineEdit::setEnableStyleHack(bool v) +{ + _enableStyleHack = v; + update(); +} + +void LineEdit::paintEvent(QPaintEvent* ev) +{ + QLineEdit::paintEvent(ev); + + ev->accept(); + + if(!_enableStyleHack) + return; + + // -------------------------------------------- + // Special hack to force the frame to be drawn: + // -------------------------------------------- + + if(const QStyle* st = style()) + { + st = st->proxy(); + + QPainter p(this); + + QStyleOptionFrame o; + initStyleOption(&o); + + // KDE BUG: Breeze and Oxygen IGNORE the drawFrame setting if the text font is + // high enough such that it thinks the text is too big. + // It draws nothing but a flat, square panel. + // We end up with NO mouse over or focus rectangles at all ! + // Force a small font size to fool the line edit panel into drawing a frame. + QFont fnt(font()); + fnt.setPointSize(1); // Zero not allowed. + o.fontMetrics = QFontMetrics(fnt); + + QRect s_r(rect()); + s_r.adjust(4, 4, -4, -4); + + QPainterPath inner_rect_path; + inner_rect_path.addRect(s_r); + QPainterPath fin_rect_path; + fin_rect_path.addRect(rect()); + fin_rect_path -= inner_rect_path; + + p.setClipPath(fin_rect_path); + + p.fillRect(rect(), palette().window()); + st->drawPrimitive(QStyle::PE_PanelLineEdit, &o, &p); + } +} + + +//====================================================== +// TODO: Work in progress... +// Since KDE Breeze and Oxygen are unlikely to change +// the ignore-frame-if-too-small behaviour, muse really +// needs its own line edit widget once and for all. +// But it's a very complex and difficult control to make. +//====================================================== + +/* +//--------------------------------------------------------- +// LineEdit +//--------------------------------------------------------- + +LineEdit::LineEdit(QWidget* parent, const char* name) : QWidget(parent) +{ + setObjectName(name); + setAutoFillBackground(true); + + _drawFrame = true; + _readOnly = false; + + _textLayout = new QTextLayout(); +} + +LineEdit::~LineEdit() +{ + delete _textLayout; +} + +void LineEdit::initStyleOption(QStyleOptionFrame *option) const +{ + if(!option) + return; + option->initFrom(this); + option->rect = contentsRect(); + option->lineWidth = _drawFrame ? + style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this) : 0; + option->midLineWidth = 0; + option->state |= QStyle::State_Sunken; + if(isReadOnly()) + option->state |= QStyle::State_ReadOnly; +#ifdef QT_KEYPAD_NAVIGATION + if(hasEditFocus()) + option->state |= QStyle::State_HasEditFocus; +#endif + option->features = QStyleOptionFrame::None; +} + +void LineEdit::setFrame(bool v) +{ + if(v == _drawFrame) + return; + _drawFrame = v; + update(); +} + +void LineEdit::setReadOnly(bool v) +{ + if(v == _readOnly) + return; + _readOnly = v; + update(); +} + +void LineEdit::setText(const QString& s) +{ + if(s == _text) + return; + _text = s; + _textLayout->setText(_text); + redoTextLayout(); + update(); +} + +int LineEdit::redoTextLayout() const +{ + _textLayout->clearLayout(); + _textLayout->beginLayout(); + QTextLine l = _textLayout->createLine(); + _textLayout->endLayout(); + return qRound(l.ascent()); +} + +void LineEdit::draw(QPainter *painter, const QPoint &offset, const QRect &clip, int flags) +{ + QVector selections; + if (flags & DrawSelections) { + QTextLayout::FormatRange o; + if (m_selstart < m_selend) { + o.start = m_selstart; + o.length = m_selend - m_selstart; + o.format.setBackground(m_palette.brush(QPalette::Highlight)); + o.format.setForeground(m_palette.brush(QPalette::HighlightedText)); + } else { + // mask selection + if (m_blinkStatus){ + o.start = m_cursor; + o.length = 1; + o.format.setBackground(m_palette.brush(QPalette::Text)); + o.format.setForeground(m_palette.brush(QPalette::Window)); + } + } + selections.append(o); + } + + if (flags & DrawText) + textLayout()->draw(painter, offset, selections, clip); + + if (flags & DrawCursor){ + int cursor = m_cursor; + if (m_preeditCursor != -1) + cursor += m_preeditCursor; + if (!m_hideCursor && m_blinkStatus) + textLayout()->drawCursor(painter, offset, cursor, m_cursorWidth); + } +} + +void LineEdit::paintEvent(QPaintEvent* ev) +{ + ev->accept(); + + //fprintf(stderr, "LineEdit::paintEvent x:%d y:%d w:%d h:%d \n", ev->rect().x(), ev->rect().y(), ev->rect().width(), ev->rect().height()); + + QPainter p(this); + + if(const QStyle* st = style()) + { + st = st->proxy(); + + QStyleOptionFrame o; + initStyleOption(&o); + // Qt BUG: Breeze and Oxygen IGNORE the drawFrame setting if the text font is + // high enough such that it thinks the text is too big. + // It draws nothing but a flat, square panel. + // We end up with NO mouse over or focus rectangles at all ! + // Force a small font size to fool the line edit panel into drawing a frame. + QFont fnt(font()); + fnt.setPointSize(1); // Zero not allowed. + o.fontMetrics = QFontMetrics(fnt); + st->drawPrimitive(QStyle::PE_PanelLineEdit, &o, &p); + + QRect s_r = st->subElementRect(QStyle::SE_LineEditContents, &o, this); + //int ml, mt, mr, mb; + //getTextMargins(&ml, &mt, &mr, &mb); + //s_r.adjust(ml, mt, -mr, -mb); + + o.fontMetrics = fontMetrics(); + // Force no frame. + o.features = QStyleOptionFrame::None; + o.frameShape = QFrame::NoFrame; + o.lineWidth = 0; + o.midLineWidth = 0; + + QRect b_r = st->subElementRect(QStyle::SE_LineEditContents, &o, this); + //b_r.adjust(ml, mt, -mr, -mb); + + int x_offset = s_r.x() - b_r.x(); + int y_offset = s_r.y() - b_r.y(); + + QRect clip_r(b_r); + clip_r.adjust(-x_offset, -x_offset, -y_offset, -y_offset); + + QRect paint_r(o.rect); + //paint_r.adjust(-x_offset, -x_offset, -y_offset, -y_offset); + //paint_r.adjust(x_offset, -x_offset, y_offset, -y_offset); + paint_r.adjust(6, -6, 6, -6); + //QRect paint_r(s_r); + + //p.setClipRect(clip_r); + + // Done with this painter. + //~p(); + + QPaintEvent paint_ev(paint_r); + + QLineEdit::paintEvent(&paint_ev); + + } +} +*/ + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/line_edit.h muse-3.0.2+ds1/muse/widgets/line_edit.h --- muse-2.1.2/muse/widgets/line_edit.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/line_edit.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,113 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// line_edit.h +// (C) Copyright 2017 Tim E. Real (terminator356 on sourceforge) +// +// 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 __LINE_EDIT_H__ +#define __LINE_EDIT_H__ + +//#include +#include + +class QPaintEvent; +// class QStyleOptionFrame; +// class QString; +// class QTextLayout; + +namespace MusEGui { + +//--------------------------------------------------------- +// LineEdit +//--------------------------------------------------------- + +class LineEdit : public QLineEdit +{ + Q_OBJECT + bool _enableStyleHack; + + protected: + virtual void paintEvent(QPaintEvent*); +// virtual void keyPressEvent(QKeyEvent*); + + public: + LineEdit(QWidget* parent = 0, const char* name = 0); + LineEdit(const QString& contents, QWidget* parent = 0, const char* name = 0); + + bool enableStyleHack() const { return _enableStyleHack; } + void setEnableStyleHack(bool); +}; + + +//====================================================== +// TODO: Work in progress... +// Since KDE Breeze and Oxygen are unlikely to change +// the ignore-frame-if-too-small behaviour, muse really +// needs its own line edit widget once and for all. +// But it's a very complex and difficult control to make. +//====================================================== + +/* +//--------------------------------------------------------- +// LineEdit +//--------------------------------------------------------- + +class LineEdit : public QWidget +{ + Q_OBJECT + + public: + enum DrawFlags { DrawText = 0x01, DrawSelections = 0x02, + DrawCursor = 0x04, DrawAll = DrawText | DrawSelections | DrawCursor }; + + private: + bool _drawFrame; + bool _readOnly; + + QString _text; + QTextLayout* _textLayout; + + // Updates the internal text layout. Returns the ascent of the + // created QTextLine. + int redoTextLayout() const; + void draw(QPainter* painter, const QPoint& offset, const QRect &clip, int flags); + + virtual void paintEvent(QPaintEvent*); + + protected: + void initStyleOption(QStyleOptionFrame*) const; + + public: + LineEdit(QWidget*, const char* name=0); + virtual ~LineEdit(); + + bool hasFrame() const { return _drawFrame; } + void setFrame(bool); + bool isReadOnly() const { return _readOnly; } + void setReadOnly(bool); + QString text() const { return _text; } + QString setText(); +}; +*/ + + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/meter.cpp muse-3.0.2+ds1/muse/widgets/meter.cpp --- muse-2.1.2/muse/widgets/meter.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/meter.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -6,7 +6,7 @@ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) -// (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) +// (C) Copyright 2011-2016 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,11 +29,14 @@ #include #include #include +#include +#include +#include #include "meter.h" #include "utils.h" -#include "gconfig.h" #include "fastlog.h" +#include "muse_math.h" // Just an experiment. Some undesirable effects, see below... //#define _USE_CLIPPER 1 @@ -44,8 +47,18 @@ // Meter //--------------------------------------------------------- -Meter::Meter(QWidget* parent, MeterType type) - : QFrame(parent) //Qt::WNoAutoErase +Meter::Meter(QWidget* parent, + MeterType type, + Qt::Orientation orient, + double scaleMin, double scaleMax, + ScalePos scalePos, + const QColor& primaryColor, + ScaleDraw::TextHighlightMode textHighlightMode, + int refreshRate) + : QFrame(parent), + _primaryColor(primaryColor), + _scalePos(scalePos), + _refreshRate(refreshRate) //Qt::WNoAutoErase { setBackgroundRole(QPalette::NoRole); setAttribute(Qt::WA_NoSystemBackground); @@ -55,16 +68,33 @@ setAttribute(Qt::WA_OpaquePaintEvent); //setFrameStyle(QFrame::Raised | QFrame::StyledPanel); +// QFont fnt; +// fnt.setFamily("Sans"); +// fnt.setPixelSize(9); +// //fnt.setStyleStrategy(QFont::PreferBitmap); +// fnt.setStyleStrategy(QFont::NoAntialias); +// fnt.setHintingPreference(QFont::PreferVerticalHinting); +// setFont(fnt); +// // setStyleSheet("font: 9px \"Sans\"; "); +// setStyleSheet(MusECore::font2StyleSheet(fnt)); + mtype = type; + _orient = orient; + d_scale.setTextHighlightMode(textHighlightMode); + _scaleDist = 0; // Leftover from class Slider. Maybe use later? + _showText = false; overflow = false; - cur_yv = -1; // Flag as -1 to initialize in paint. - last_yv = 0; - cur_ymax = 0; - last_ymax = 0; + cur_pixv = -1; // Flag as -1 to initialize in paint. + last_pixv = 0; + cur_pixmax = 0; + last_pixmax = 0; val = 0.0; + targetVal = 0.0; + targetValStep = 0.0; maxVal = 0.0; - minScale = mtype == DBMeter ? MusEGlobal::config.minMeter : 0.0; // min value in dB or int - maxScale = mtype == DBMeter ? 10.0 : 127.0; + targetMaxVal = 0.0; + minScale = scaleMin; + maxScale = scaleMax; yellowScale = -10; redScale = 0; setLineWidth(0); @@ -81,8 +111,8 @@ dark_yellow_center = QColor(0x8e8e00); dark_yellow_begin = QColor(0x6a8400); - dark_green_end = QColor(0x467800); - dark_green_begin = QColor(0x007000); +// dark_green_end = QColor(0x467800); +// dark_green_begin = QColor(0x007000); light_red_end = QColor(0xff0000); light_red_begin = QColor(0xdd8800); @@ -91,8 +121,8 @@ light_yellow_center = QColor(0xffff00); light_yellow_begin = QColor(0xddff00); - light_green_end = QColor(0x88ff00); - light_green_begin = QColor(0x00ff00); +// light_green_end = QColor(0x88ff00); +// light_green_begin = QColor(0x00ff00); mask_center = QColor(225, 225, 225, 64); mask_edge = QColor(30, 30, 30, 64); @@ -100,8 +130,8 @@ separator_color = QColor(0x666666); peak_color = QColor(0xeeeeee); - darkGradGreen.setColorAt(1, dark_green_begin); - darkGradGreen.setColorAt(0, dark_green_end); +// darkGradGreen.setColorAt(1, dark_green_begin); +// darkGradGreen.setColorAt(0, dark_green_end); darkGradYellow.setColorAt(1, dark_yellow_begin); darkGradYellow.setColorAt(0.5, dark_yellow_center); @@ -110,8 +140,8 @@ darkGradRed.setColorAt(1, dark_red_begin); darkGradRed.setColorAt(0, dark_red_end); - lightGradGreen.setColorAt(1, light_green_begin); - lightGradGreen.setColorAt(0, light_green_end); +// lightGradGreen.setColorAt(1, light_green_begin); +// lightGradGreen.setColorAt(0, light_green_end); lightGradYellow.setColorAt(1, light_yellow_begin); lightGradYellow.setColorAt(0.5, light_yellow_center); @@ -124,8 +154,173 @@ maskGrad.setColorAt(0.5, mask_center); maskGrad.setColorAt(1, mask_edge); + connect(&fallingTimer, SIGNAL(timeout()), this, SLOT(updateTargetMeterValue())); + + setPrimaryColor(_primaryColor); + +// updateText(targetVal); } +//------------------------------------------------------------ +//.- +//.F Slider::scaleChange +// Notify changed scale +// +//.u Syntax +//.f void Slider::scaleChange() +// +//.u Description +// Called by QwtScaledWidget +// +//------------------------------------------------------------ +void Meter::scaleChange() +{ + if (!hasUserScale()) + d_scale.setScale(maxScale, minScale, d_maxMajor, d_maxMinor); + update(); +} + +QSize Meter::sizeHint() const +{ + int w = 40; + int h = 40; + const QFontMetrics fm = fontMetrics(); + int msWidth = 0, msHeight = 0; + + if(_scalePos != None) + { + msWidth = d_scale.maxWidth(fm, false); + msHeight = d_scale.maxHeight(fm); + + switch(_orient) + { + case Qt::Vertical: + { + const int smw = msWidth + _scaleDist; + switch(_scalePos) + { + case Left: + case Right: + //w = 2*d_xMargin + d_thumbWidth + smw + 2; + w = smw + 2; + break; + + case InsideVertical: + { + //const int aw = smw > d_thumbWidth ? smw : d_thumbWidth; + //w = 2*d_xMargin + aw + 2; + w = smw + 2; + } + break; + + case Top: + case Bottom: + case InsideHorizontal: + case None: + break; + } + } + break; + + case Qt::Horizontal: + { + const int smh = msHeight + _scaleDist; + switch(_scalePos) + { + case Top: + case Bottom: + //h = 2*d_yMargin + d_thumbWidth + smh; + h = smh; + break; + + case InsideHorizontal: + { + //const int ah = smh > d_thumbWidth ? smh : d_thumbWidth; + //h = 2*d_yMargin + ah; + h = smh; + } + break; + + case Left: + case Right: + case InsideVertical: + case None: + break; + } + } + break; + } + } + else + { // no scale + switch(_orient) + { + case Qt::Vertical: + w = 16; + break; + case Qt::Horizontal: + h = 16; + break; + } + } + //fprintf(stderr, "Meter::sizeHint w:%d, h:%d\n", w, h); + return QSize(w, h); +} + +//--------------------------------------------------------- +// updateText +//--------------------------------------------------------- + +void Meter::updateText(double val) +{ + if(val >= -60.0f) + _text = locale().toString(val, 'f', 1); + else + { + _text = QString("-"); + _text += QChar(0x221e); // The infinty character + } + + const QFontMetrics fm = fontMetrics(); + //const QFontMetrics fm(_textFont); + //// Rotate 90 deg. + //_textSize = fm.boundingRect(txt).size().transposed(); + const QSize sz = fm.boundingRect(_text).size(); + const int txtw = sz.width(); + const int txth = sz.height(); +// if(_textPM.isNull() || _textPM.size().width() < w || _textPM.size().height() < h) +// _textPM = QPixmap(w, h); +// QPainter p; +// p.begin(&_textPM); +// // p.eraseRect(0, 0, w, h); +// p.fillRect(0, 0, w, h, Qt::darkYellow); +// p.rotate(90); +// p.setPen(Qt::cyan); +// // Rotate 90 deg. +// //p.drawText(0, 0, h, w, Qt::AlignLeft | Qt::AlignTop, txt); +// p.drawText(0, 0, txt); +// p.end(); + + // Set the text size, incrementally expanding. Ensure that paint will erase the last largest size. + // Rotate 90 degrees. + const int fw = frameWidth(); + const int w = width() - 2*fw; + const int txtYOff = fw + (w > txth ? (w - txth) / 2 : 0); + + _textRect.setX(fw); + _textRect.setY(txtYOff); + + if(txtw > _textRect.width()) + _textRect.setWidth(txtw); + if(txth > _textRect.height()) + _textRect.setHeight(txth); + + const QRect rr(_textRect.y(), _textRect.x(), _textRect.height(), _textRect.width()); // Rotate -90 degrees. + update(rr); + //QRect ur(_textRect.y(), _textRect.x(), _textRect.height(), _textRect.width()); + //update(ur); +} + //--------------------------------------------------------- // setVal //--------------------------------------------------------- @@ -137,66 +332,152 @@ if(mtype == DBMeter) { - double minScaleLin = pow(10.0, minScale/20.0); - if((v >= minScaleLin && val != v) || val >= minScaleLin) + double minScaleLin = muse_db2val(minScale); + if((v >= minScaleLin && targetVal != v) || targetVal >= minScaleLin) { - val = v; + targetVal = v; ud = true; } } else { - if(val != v) + if(targetVal != v) { - val = v; + targetVal = v; ud = true; } - } - - double range = maxScale - minScale; - int fw = frameWidth(); - int w = width() - 2*fw; - int h = height() - 2*fw; - QRect udRect; - bool udPeak = false; - - if(maxVal != max) + } + + if(ud || (maxVal != max)) { - maxVal = max; - if(mtype == DBMeter) - cur_ymax = maxVal == 0 ? fw : int(((maxScale - (MusECore::fast_log10(maxVal) * 20.0)) * h)/range); - else - cur_ymax = maxVal == 0 ? fw : int(((maxScale - maxVal) * h)/range); - if(cur_ymax > h) cur_ymax = h; - // Not using regions. Just lump them together. - udRect = QRect(fw, last_ymax, w, 1) | QRect(fw, cur_ymax, w, 1); - //printf("Meter::setVal peak cur_ymax:%d last_ymax:%d\n", cur_ymax, last_ymax); - last_ymax = cur_ymax; - ud = true; - udPeak = true; + targetMaxVal = max; + if(!fallingTimer.isActive()) + { + fallingTimer.start(1000/std::max(30, _refreshRate)); + } } - if(ud) - { - if(mtype == DBMeter) - cur_yv = val == 0 ? h : int(((maxScale - (MusECore::fast_log10(val) * 20.0)) * h)/range); - else - cur_yv = val == 0 ? h : int(((maxScale - val) * h)/range); - if(cur_yv > h) cur_yv = h; - //printf("Meter::setVal cur_yv:%d last_yv:%d\n", cur_yv, last_yv); - int y1, y2; - if(last_yv < cur_yv) { y1 = last_yv; y2 = cur_yv; } else { y1 = cur_yv; y2 = last_yv; } - last_yv = cur_yv; - - if(udPeak) - update(udRect | QRect(fw, y1, w, y2 - y1 + 1)); - //repaint(udRect | QRect(fw, y1, w, y2 - y1 + 1)); - else - update(QRect(fw, y1, w, y2 - y1 + 1)); - //repaint(QRect(fw, y1, w, y2 - y1 + 1)); +} + +void Meter::updateTargetMeterValue() +{ + double range = maxScale - minScale; + int fw = frameWidth(); + int w = width() - 2*fw; + int h = height() - 2*fw; + QRect udRect; + bool udPeak = false; + bool ud = false; + + if(targetVal > val) + { + val = targetVal; + targetValStep = 0; + ud = true; + } + else if(targetVal < val) + { + targetValStep = (val - targetVal) / ((double)(1000 / std::max(30, _refreshRate + 1)) / 7.0f); + val -= targetValStep; + if(val < targetVal) + { + val = targetVal; } - } + ud = true; + } + + const double transl_val = val - minScale; + + if(maxVal != targetMaxVal) + { + maxVal = targetMaxVal; + const double v = (mtype == DBMeter) ? (MusECore::fast_log10(maxVal) * 20.0) : maxVal; + + if(_orient == Qt::Vertical) + { + cur_pixmax = maxVal == 0 ? fw : int(((maxScale - v) * h)/range); + if(_showText) + updateText(v); + if(cur_pixmax > h) + cur_pixmax = h; + // Not using regions. Just lump them together. + udRect = QRect(fw, last_pixmax, w, 1) | QRect(fw, cur_pixmax, w, 1); + } + else + { + //cur_pixmax = maxVal == 0 ? fw : int(((maxScale - v) * w)/range); + cur_pixmax = maxVal == 0 ? w - fw : int((v * w)/range); + if(_showText) + updateText(v); + if(cur_pixmax > w) + cur_pixmax = w; + // Not using regions. Just lump them together. + udRect = QRect(last_pixmax, fw, 1, h) | QRect(cur_pixmax, fw, 1, w); + } + + //printf("Meter::setVal peak cur_ymax:%d last_ymax:%d\n", cur_ymax, last_ymax); + last_pixmax = cur_pixmax; + ud = true; + udPeak = true; + } + + if(ud) + { + if(_orient == Qt::Vertical) + { + if(cur_pixv > h) + cur_pixv = h; + if(mtype == DBMeter) + cur_pixv = val == 0 ? h : int(((maxScale - (MusECore::fast_log10(val) * 20.0)) * h)/range); + //cur_pixv = transl_val <= 0.0 ? h : int(((maxScale - (MusECore::fast_log10(val) * 20.0)) * h)/range); // TODO + else + cur_pixv = val == 0 ? h : int(((maxScale - val) * h)/range); + //cur_pixv = int(((maxScale - transl_val) * h)/range); // TODO + + //printf("Meter::setVal cur_yv:%d last_yv:%d\n", cur_yv, last_yv); + int y1, y2; + if(last_pixv < cur_pixv) { y1 = last_pixv; y2 = cur_pixv; } else { y1 = cur_pixv; y2 = last_pixv; } + last_pixv = cur_pixv; + + if(udPeak) + update(udRect | QRect(fw, y1, w, y2 - y1 + 1)); + //repaint(udRect | QRect(fw, y1, w, y2 - y1 + 1)); + else + update(QRect(fw, y1, w, y2 - y1 + 1)); + //repaint(QRect(fw, y1, w, y2 - y1 + 1)); + } + else + { + if(cur_pixv > w) + cur_pixv = w; + if(mtype == DBMeter) + //cur_pixv = val == 0 ? 0 : int((MusECore::fast_log10(val) * 20.0 * w)/range); + cur_pixv = transl_val <= 0.0 ? 0 : int((MusECore::fast_log10(transl_val) * 20.0 * w)/range); // FIXME: Comparison correct? + else + //cur_pixv = int((val * w)/range); + cur_pixv = int((transl_val * w)/range); + + //printf("Meter::setVal cur_yv:%d last_yv:%d\n", cur_yv, last_yv); + int x1, x2; + if(last_pixv < cur_pixv) { x1 = last_pixv; x2 = cur_pixv; } else { x1 = cur_pixv; x2 = last_pixv; } + last_pixv = cur_pixv; + + if(udPeak) + update(udRect | QRect(x1, fw, x2 - x1 + 1, h)); + //repaint(udRect | QRect(x1, fw, x2 - x1 + 1, h)); + else + update(QRect(x1, fw, x2 - x1 + 1, h)); + //repaint(QRect(x1, fw, x2 - x1 + 1, h)); + } + } + if(!ud) + { + fallingTimer.stop(); + } + +} + //--------------------------------------------------------- // resetPeaks @@ -207,7 +488,7 @@ { maxVal = val; overflow = val > 0.0; - cur_yv = -1; // Force re-initialization. + cur_pixv = -1; // Force re-initialization. update(); } @@ -222,314 +503,644 @@ minScale = min; maxScale = max; - cur_yv = -1; // Force re-initialization. + cur_pixv = -1; // Force re-initialization. + + if (!hasUserScale()) + d_scale.setScale(minScale, maxScale, d_maxMajor, d_maxMinor); + update(); } //--------------------------------------------------------- -// paintEvent +// setRefreshRate //--------------------------------------------------------- -void Meter::paintEvent(QPaintEvent* ev) - { - // For some reason upon resizing we get double calls here and in resizeEvent. +void Meter::setRefreshRate(int rate) +{ + _refreshRate = rate; +} - QPainter p(this); - p.setRenderHint(QPainter::Antialiasing); +void Meter::setPrimaryColor(const QColor& color) +{ + _primaryColor = color; + int r = 0; + + dark_green_begin = _primaryColor.darker(200); + dark_green_end = dark_green_begin; + r = dark_green_end.red() + 0x46; + if(r > 255) + r = 255; + dark_green_end.setRed(r); + + light_green_begin = _primaryColor; + light_green_end = light_green_begin; + r = light_green_end.red() + 0x88; + if(r > 255) + r = 255; + light_green_end.setRed(r); + + darkGradGreen.setColorAt(1, dark_green_begin); + darkGradGreen.setColorAt(0, dark_green_end); + + lightGradGreen.setColorAt(1, light_green_begin); + lightGradGreen.setColorAt(0, light_green_end); + + update(); +} + +//--------------------------------------------------------- +// paintEvent +//--------------------------------------------------------- - double range = maxScale - minScale; - int fw = frameWidth(); - int w = width() - 2*fw; - int h = height() - 2*fw; - const QRect& rect = ev->rect(); - //printf("Meter::paintEvent rx:%d ry:%d rw:%d rh:%d w:%d h:%d\n", rect.x(), rect.y(), rect.width(), rect.height(), w, h); +void Meter::paintEvent(QPaintEvent* ev) +{ + // For some reason upon resizing we get double calls here and in resizeEvent. - QPainterPath drawingPath, updatePath, finalPath, cornerPath; - //bool updFull = false; - - // Initialize. Can't do in ctor, must be done after layouts have been done. Most reliable to do it here. - if(cur_yv == -1) + QPainter p(this); + const int fw = frameWidth(); + const int w = width() - 2*fw; + const int h = height() - 2*fw; + p.setRenderHint(QPainter::Antialiasing); + + //p.fillRect(0, 0, width(), height(), QColor(50, 50, 50)); + + const double range = maxScale - minScale; + const double transl_val = val - minScale; + + bool textDrawn = false; + const int rectCount = ev->region().rectCount(); + QVector rects = ev->region().rects(); + for(int ri = 0; ri < rectCount; ++ri) + { + const QRect& rect = rects.at(ri); + + // Tested OK! Small non-overlapping rectangles. + //fprintf(stderr, "Meter::paintEvent rcount:%d ridx:%d rx:%d ry:%d rw:%d rh:%d w:%d h:%d\n", + // rectCount, ri, rect.x(), rect.y(), rect.width(), rect.height(), w, h); + + QPainterPath drawingPath, updatePath, finalPath, cornerPath; + //bool updFull = false; + + // Initialize. Can't do in ctor, must be done after layouts have been done. Most reliable to do it here. + if(cur_pixv == -1) + { + if(_orient == Qt::Vertical) { if(mtype == DBMeter) { - cur_yv = val == 0 ? h : int(((maxScale - (MusECore::fast_log10(val) * 20.0)) * h)/range); - cur_ymax = maxVal == 0 ? fw : int(((maxScale - (MusECore::fast_log10(maxVal) * 20.0)) * h)/range); + cur_pixv = val == 0 ? h : int(((maxScale - (MusECore::fast_log10(val) * 20.0)) * h)/range); + cur_pixmax = maxVal == 0 ? fw : int(((maxScale - (MusECore::fast_log10(maxVal) * 20.0)) * h)/range); } else { - cur_yv = val == 0 ? h : int(((maxScale - val) * h)/range); - cur_ymax = maxVal == 0 ? fw : int(((maxScale - maxVal) * h)/range); + cur_pixv = val == 0 ? h : int(((maxScale - val) * h)/range); + cur_pixmax = maxVal == 0 ? fw : int(((maxScale - maxVal) * h)/range); } - if(cur_yv > h) cur_yv = h; - last_yv = cur_yv; - if(cur_ymax > h) cur_ymax = h; - last_ymax = cur_ymax; + if(cur_pixv > h) cur_pixv = h; + last_pixv = cur_pixv; + if(cur_pixmax > h) cur_pixmax = h; + last_pixmax = cur_pixmax; //updFull = true; updatePath.addRect(fw, fw, w, h); // Update the whole thing } else - updatePath.addRect(rect.x(), rect.y(), rect.width(), rect.height()); // Update only the requested rectangle - - drawingPath.addRoundedRect(fw, fw, w, h, xrad, yrad); // The actual desired shape of the meter - finalPath = drawingPath & updatePath; - - // Draw corners as normal background colour. - cornerPath = updatePath - finalPath; // Elegantly simple. Path subtraction! Wee... - if(!cornerPath.isEmpty()) - p.fillPath(cornerPath, palette().window()); - -#ifdef _USE_CLIPPER - p.setClipPath(finalPath); // Meh, nice but not so good. Clips at edge so antialising has no effect! Can it be done ? -#endif - - // Draw the red, green, and yellow sections. - drawVU(p, rect, finalPath, cur_yv); - - // Draw the peak white line. - //if(updFull || (cur_ymax >= rect.y() && cur_ymax < rect.height())) { - p.setRenderHint(QPainter::Antialiasing, false); // No antialiasing. Makes the line fuzzy, double height, or not visible at all. - - //p.setPen(peak_color); - //p.drawLine(fw, cur_ymax, w, cur_ymax); // Undesirable. Draws outside the top rounded corners. - // - //QPainterPath path; path.moveTo(fw, cur_ymax); path.lineTo(w, cur_ymax); // ? Didn't work. No line at all. - //p.drawPath(path & finalPath); - QPainterPath path; path.addRect(fw, cur_ymax, w, 1); path &= finalPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(peak_color)); + if(mtype == DBMeter) + { + cur_pixv = transl_val <= 0.0 ? 0 : int(((MusECore::fast_log10(transl_val) * 20.0) * w)/range); + cur_pixmax = maxVal <= 0.0 ? w - fw : int(((MusECore::fast_log10(maxVal) * 20.0) * w)/range); + } + else + { + cur_pixv = int((transl_val * w)/range); + cur_pixmax = maxVal <= 0.0 ? w - fw : int((maxVal * w)/range); + } + if(cur_pixv > w) cur_pixv = w; + last_pixv = cur_pixv; + if(cur_pixmax > w) cur_pixmax = w; + last_pixmax = cur_pixmax; + //updFull = true; + updatePath.addRect(fw, fw, w, h); // Update the whole thing } - - // Draw the transparent layer on top of everything to give a 3d look - p.setRenderHint(QPainter::Antialiasing); - maskGrad.setStart(QPointF(fw, fw)); + } + else + updatePath.addRect(rect.x(), rect.y(), rect.width(), rect.height()); // Update only the requested rectangle + + drawingPath.addRoundedRect(fw, fw, w, h, xrad, yrad); // The actual desired shape of the meter + finalPath = drawingPath & updatePath; + + // Draw corners as normal background colour. + cornerPath = updatePath - finalPath; // Elegantly simple. Path subtraction! Wee... + if(!cornerPath.isEmpty()) + p.fillPath(cornerPath, palette().window()); + +#ifdef _USE_CLIPPER + p.setClipPath(finalPath); // Meh, nice but not so good. Clips at edge so antialising has no effect! Can it be done ? +#endif + + // Draw the red, green, and yellow sections. + drawVU(p, rect, finalPath, cur_pixv); + + // Draw the peak white line. + //if(updFull || (cur_ymax >= rect.y() && cur_ymax < rect.height())) + { + p.setRenderHint(QPainter::Antialiasing, false); // No antialiasing. Makes the line fuzzy, double height, or not visible at all. + + //p.setPen(peak_color); + //p.drawLine(fw, cur_ymax, w, cur_ymax); // Undesirable. Draws outside the top rounded corners. + // + //QPainterPath path; path.moveTo(fw, cur_ymax); path.lineTo(w, cur_ymax); // ? Didn't work. No line at all. + //p.drawPath(path & finalPath); + QPainterPath path; + if(_orient == Qt::Vertical) + path.addRect(fw, cur_pixmax + cur_pixmax % 2 + 1, w, 1); + else + path.addRect(cur_pixmax + cur_pixmax % 2 + 1, fw, 1, h); + path &= finalPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(peak_color)); + } + + // Draw the transparent layer on top of everything to give a 3d look + p.setRenderHint(QPainter::Antialiasing); + maskGrad.setStart(QPointF(fw, fw)); + if(_orient == Qt::Vertical) maskGrad.setFinalStop(QPointF(w, fw)); + else + maskGrad.setFinalStop(QPointF(fw, h)); + #ifdef _USE_CLIPPER - p.fillRect(rect, QBrush(maskGrad)); + p.fillRect(rect, QBrush(maskGrad)); #else - //QPainterPath path; path.addRect(fw, fw, w); - //p.fillPath(finalPath & path, QBrush(maskGrad)); - p.fillPath(finalPath, QBrush(maskGrad)); + //QPainterPath path; path.addRect(fw, fw, w); + //p.fillPath(finalPath & path, QBrush(maskGrad)); + p.fillPath(finalPath, QBrush(maskGrad)); #endif - + + if(_showText) + { + const QRect rr(rect.y(), rect.x(), rect.height(), rect.width()); // Rotate 90 degrees. + if(!textDrawn && rr.intersects(_textRect)) + { + textDrawn = true; + //fprintf(stderr, " Drawing text:%s\n", _text.toLatin1().constData()); + //p.setFont(_textFont); + p.setPen(Qt::white); + + if(_orient == Qt::Vertical) + { + p.rotate(90); + p.translate(0, -frameGeometry().width()); + } + + //p.drawText(txtXOff, txtYOff, _textSize.width(), _textSize.height(), Qt::AlignLeft | Qt::AlignVCenter, _text); + p.drawText(_textRect, Qt::AlignLeft | Qt::AlignVCenter, _text); + //p.drawPixmap(fw, fw, _textPM); + + if(_orient == Qt::Vertical) + { + // Restore. + p.translate(0, frameGeometry().width()); + p.rotate(-90); + } } + } + } + + if(_scalePos != None) + { +// p.fillRect(rect(), palette().window()); + p.setRenderHint(QPainter::Antialiasing, false); + d_scale.draw(&p, palette()); + } +} //--------------------------------------------------------- // drawVU //--------------------------------------------------------- -void Meter::drawVU(QPainter& p, const QRect& rect, const QPainterPath& drawPath, int yv) +void Meter::drawVU(QPainter& p, const QRect& rect, const QPainterPath& drawPath, int pixv) { int fw = frameWidth(); int w = width() - 2*fw; int h = height() - 2*fw; - + // Test OK. We are passed small rectangles on small value changes. //printf("Meter::drawVU rx:%d ry:%d rw:%d rh:%d w:%d h:%d\n", rect.x(), rect.y(), rect.width(), rect.height(), w, h); - QRect pr(0, 0, w, 0); - if(mtype == DBMeter) // Meter type is dB... - { - double range = maxScale - minScale; - int y1 = int((maxScale - redScale) * h / range); - int y2 = int((maxScale - yellowScale) * h / range); - - darkGradGreen.setStart(QPointF(fw, y2)); - darkGradGreen.setFinalStop(QPointF(fw, h)); - darkGradYellow.setStart(QPointF(fw, y1)); - darkGradYellow.setFinalStop(QPointF(fw, y2)); - darkGradRed.setStart(QPointF(fw, fw)); - darkGradRed.setFinalStop(QPointF(fw, y1)); - - lightGradGreen.setStart(QPointF(fw, y2)); - lightGradGreen.setFinalStop(QPointF(fw, h)); - lightGradYellow.setStart(QPointF(fw, y1)); - lightGradYellow.setFinalStop(QPointF(fw, y2)); - lightGradRed.setStart(QPointF(fw, fw)); - lightGradRed.setFinalStop(QPointF(fw, y1)); + if(_orient == Qt::Vertical) + { + //QRect pr(0, 0, w, 0); + if(mtype == DBMeter) // Meter type is dB... + { + double range = maxScale - minScale; + int y1 = int((maxScale - redScale) * h / range); + int y2 = int((maxScale - yellowScale) * h / range); + + darkGradGreen.setStart(QPointF(fw, y2)); + darkGradGreen.setFinalStop(QPointF(fw, h)); + darkGradYellow.setStart(QPointF(fw, y1)); + darkGradYellow.setFinalStop(QPointF(fw, y2)); + darkGradRed.setStart(QPointF(fw, fw)); + darkGradRed.setFinalStop(QPointF(fw, y1)); + + lightGradGreen.setStart(QPointF(fw, y2)); + lightGradGreen.setFinalStop(QPointF(fw, h)); + lightGradYellow.setStart(QPointF(fw, y1)); + lightGradYellow.setFinalStop(QPointF(fw, y2)); + lightGradRed.setStart(QPointF(fw, fw)); + lightGradRed.setFinalStop(QPointF(fw, y1)); -#ifdef _USE_CLIPPER - if(yv < y1) + #ifdef _USE_CLIPPER + if(yv < y1) + { + // Red section: + pr.setTop(fw); pr.setHeight(yv); + p.fillRect(pr, QBrush(darkGradRed)); // dark red + pr.setTop(yv); pr.setHeight(y1-yv); + p.fillRect(pr & rect, QBrush(lightGradRed)); // light red + + // Yellow section: + pr.setTop(y1); pr.setHeight(y2-y1); + p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow + + // Green section: + pr.setTop(y2); pr.setHeight(h-y2); + p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green + } + else + if(yv < y2) + { + // Red section: + pr.setTop(fw); pr.setHeight(y1); + p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red + + // Yellow section: + pr.setTop(y1); pr.setHeight(yv-y1); + p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow + pr.setTop(yv); pr.setHeight(y2-yv); + p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow + + // Green section: + pr.setTop(y2); pr.setHeight(h-y2); + p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green + } + else + //if(yv <= y3) + { + // Red section: + pr.setTop(fw); pr.setHeight(y1); + p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red + + // Yellow section: + pr.setTop(y1); pr.setHeight(y2-y1); + p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow + + // Green section: + pr.setTop(y2); pr.setHeight(yv-y2); + p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green + pr.setTop(yv); pr.setHeight(h-yv); + p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green + } + } + else // Meter type is linear... { - // Red section: pr.setTop(fw); pr.setHeight(yv); - p.fillRect(pr, QBrush(darkGradRed)); // dark red - pr.setTop(yv); pr.setHeight(y1-yv); - p.fillRect(pr & rect, QBrush(lightGradRed)); // light red - - // Yellow section: - pr.setTop(y1); pr.setHeight(y2-y1); - p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow - - // Green section: - pr.setTop(y2); pr.setHeight(h-y2); - p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green - } - else - if(yv < y2) - { - // Red section: - pr.setTop(fw); pr.setHeight(y1); - p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red - - // Yellow section: - pr.setTop(y1); pr.setHeight(yv-y1); - p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow - pr.setTop(yv); pr.setHeight(y2-yv); - p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow - - // Green section: - pr.setTop(y2); pr.setHeight(h-y2); - p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green - } - else - //if(yv <= y3) - { - // Red section: - pr.setTop(fw); pr.setHeight(y1); - p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red - - // Yellow section: - pr.setTop(y1); pr.setHeight(y2-y1); - p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow - - // Green section: - pr.setTop(y2); pr.setHeight(yv-y2); - p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green + p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green pr.setTop(yv); pr.setHeight(h-yv); - p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green + p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } - } - else // Meter type is linear... - { - pr.setTop(fw); pr.setHeight(yv); - p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green - pr.setTop(yv); pr.setHeight(h-yv); - p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green - } -#else // NOT _USE_CLIPPER + #else // NOT _USE_CLIPPER - if(yv < y1) - { - // Red section: + if(pixv < y1) { - QPainterPath path; path.addRect(fw, fw, w, yv); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(darkGradRed)); // dark red + // Red section: + { + QPainterPath path; path.addRect(fw, fw, w, pixv); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradRed)); // dark red + } + { + QPainterPath path; path.addRect(fw, pixv, w, y1-pixv); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradRed)); // light red + } + + // Yellow section: + { + QPainterPath path; path.addRect(fw, y1, w, y2-y1); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradYellow)); // light yellow + } + + // Green section: + { + QPainterPath path; path.addRect(fw, y2, w, h-y2); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradGreen)); // light green + } } + else + if(pixv < y2) { - QPainterPath path; path.addRect(fw, yv, w, y1-yv); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(lightGradRed)); // light red + // Red section: + { + QPainterPath path; path.addRect(fw, fw, w, y1); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradRed)); // dark red + } + + // Yellow section: + { + QPainterPath path; path.addRect(fw, y1, w, pixv-y1); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradYellow)); // dark yellow + } + { + QPainterPath path; path.addRect(fw, pixv, w, y2-pixv); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradYellow)); // light yellow + } + + // Green section: + { + QPainterPath path; path.addRect(fw, y2, w, h-y2); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradGreen)); // light green + } } - - // Yellow section: + else + //if(yv <= y3) { - QPainterPath path; path.addRect(fw, y1, w, y2-y1); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(lightGradYellow)); // light yellow + // Red section: + { + QPainterPath path; path.addRect(fw, fw, w, y1); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradRed)); // dark red + } + + // Yellow section: + { + QPainterPath path; path.addRect(fw, y1, w, y2-y1); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradYellow)); // dark yellow + } + + // Green section: + { + QPainterPath path; path.addRect(fw, y2, w, pixv-y2); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradGreen)); // dark green + } + { + QPainterPath path; path.addRect(fw, pixv, w, h-pixv); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradGreen)); // light green + } } - - // Green section: + + // Separators: { - QPainterPath path; path.addRect(fw, y2, w, h-y2); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(lightGradGreen)); // light green - } - } - else - if(yv < y2) + QRect r(0, y1, w, 1); r &= rect; + if(!r.isNull()) + p.fillRect(r, separator_color); + } + { + QRect r(0, y2, w, 1); r &= rect; + if(!r.isNull()) + p.fillRect(r, separator_color); + } + } + else // Meter type is linear... { - // Red section: + darkGradGreen.setStart(QPointF(fw, fw)); + darkGradGreen.setFinalStop(QPointF(fw, h)); + + lightGradGreen.setStart(QPointF(fw, fw)); + lightGradGreen.setFinalStop(QPointF(fw, h)); + { - QPainterPath path; path.addRect(fw, fw, w, y1); path &= drawPath; + QPainterPath path; path.addRect(fw, fw, w, pixv); path &= drawPath; if(!path.isEmpty()) - p.fillPath(path, QBrush(darkGradRed)); // dark red + p.fillPath(path, QBrush(darkGradGreen)); // dark green } - - // Yellow section: { - QPainterPath path; path.addRect(fw, y1, w, yv-y1); path &= drawPath; + QPainterPath path; path.addRect(fw, pixv, w, h-pixv); path &= drawPath; if(!path.isEmpty()) - p.fillPath(path, QBrush(darkGradYellow)); // dark yellow + p.fillPath(path, QBrush(lightGradGreen)); // light green } + } + +#endif // NOT _USE_CLIPPER + + } + else // Horizontal meter + { + //QRect pr(0, 0, w, 0); + if(mtype == DBMeter) // Meter type is dB... + { + double range = maxScale - minScale; + int x1 = int(redScale * w / range); + int x2 = int(yellowScale * w / range); + + darkGradGreen.setStart(QPointF(x2, fw)); + darkGradGreen.setFinalStop(QPointF(w, fw)); + darkGradYellow.setStart(QPointF(x1, fw)); + darkGradYellow.setFinalStop(QPointF(x2, fw)); + darkGradRed.setStart(QPointF(fw, fw)); + darkGradRed.setFinalStop(QPointF(x1, fw)); + + lightGradGreen.setStart(QPointF(x2, fw)); + lightGradGreen.setFinalStop(QPointF(w, fw)); + lightGradYellow.setStart(QPointF(x1, fw)); + lightGradYellow.setFinalStop(QPointF(x2, fw)); + lightGradRed.setStart(QPointF(fw, fw)); + lightGradRed.setFinalStop(QPointF(x1, fw)); + + #ifdef _USE_CLIPPER + if(yv < y1) { - QPainterPath path; path.addRect(fw, yv, w, y2-yv); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(lightGradYellow)); // light yellow + // Red section: + pr.setTop(fw); pr.setHeight(yv); + p.fillRect(pr, QBrush(darkGradRed)); // dark red + pr.setTop(yv); pr.setHeight(y1-yv); + p.fillRect(pr & rect, QBrush(lightGradRed)); // light red + + // Yellow section: + pr.setTop(y1); pr.setHeight(y2-y1); + p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow + + // Green section: + pr.setTop(y2); pr.setHeight(h-y2); + p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } - - // Green section: + else + if(yv < y2) { - QPainterPath path; path.addRect(fw, y2, w, h-y2); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(lightGradGreen)); // light green + // Red section: + pr.setTop(fw); pr.setHeight(y1); + p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red + + // Yellow section: + pr.setTop(y1); pr.setHeight(yv-y1); + p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow + pr.setTop(yv); pr.setHeight(y2-yv); + p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow + + // Green section: + pr.setTop(y2); pr.setHeight(h-y2); + p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } - } - else - //if(yv <= y3) - { - // Red section: + else + //if(yv <= y3) { - QPainterPath path; path.addRect(fw, fw, w, y1); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(darkGradRed)); // dark red + // Red section: + pr.setTop(fw); pr.setHeight(y1); + p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red + + // Yellow section: + pr.setTop(y1); pr.setHeight(y2-y1); + p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow + + // Green section: + pr.setTop(y2); pr.setHeight(yv-y2); + p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green + pr.setTop(yv); pr.setHeight(h-yv); + p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } - - // Yellow section: + } + else // Meter type is linear... + { + pr.setTop(fw); pr.setHeight(yv); + p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green + pr.setTop(yv); pr.setHeight(h-yv); + p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green + } + + #else // NOT _USE_CLIPPER + + if(pixv < x1) { - QPainterPath path; path.addRect(fw, y1, w, y2-y1); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(darkGradYellow)); // dark yellow + // Red section: + { + QPainterPath path; path.addRect(fw, fw, pixv, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradRed)); // dark red + } + { + QPainterPath path; path.addRect(pixv, fw, x1-pixv, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradRed)); // light red + } + + // Yellow section: + { + QPainterPath path; path.addRect(x1, fw, x2-x1, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradYellow)); // light yellow + } + + // Green section: + { + QPainterPath path; path.addRect(x2, fw, w-x2, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradGreen)); // light green + } } - - // Green section: + else + if(pixv < x2) { - QPainterPath path; path.addRect(fw, y2, w, yv-y2); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(darkGradGreen)); // dark green + // Red section: + { + QPainterPath path; path.addRect(fw, fw, x1, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradRed)); // dark red + } + + // Yellow section: + { + QPainterPath path; path.addRect(x1, fw, pixv-x1, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradYellow)); // dark yellow + } + { + QPainterPath path; path.addRect(pixv, fw, x2-pixv, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradYellow)); // light yellow + } + + // Green section: + { + QPainterPath path; path.addRect(x2, fw, w-x2, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradGreen)); // light green + } } + else + //if(yv <= y3) { - QPainterPath path; path.addRect(fw, yv, w, h-yv); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(lightGradGreen)); // light green + // Red section: + { + QPainterPath path; path.addRect(fw, fw, x1, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradRed)); // dark red + } + + // Yellow section: + { + QPainterPath path; path.addRect(x1, fw, x2-x1, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradYellow)); // dark yellow + } + + // Green section: + { + QPainterPath path; path.addRect(x2, fw, pixv-x2, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradGreen)); // dark green + } + { + QPainterPath path; path.addRect(pixv, fw, w-pixv, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradGreen)); // light green + } } - } - // Separators: - { - QRect r(0, y1, w, 1); r &= rect; - if(!r.isNull()) - p.fillRect(r, separator_color); + // Separators: + { + QRect r(x1, 0, 1, h); r &= rect; + if(!r.isNull()) + p.fillRect(r, separator_color); + } + { + QRect r(x2, 0, 1, h); r &= rect; + if(!r.isNull()) + p.fillRect(r, separator_color); + } } + else // Meter type is linear... { - QRect r(0, y2, w, 1); r &= rect; - if(!r.isNull()) - p.fillRect(r, separator_color); - } - } - else // Meter type is linear... - { - darkGradGreen.setStart(QPointF(fw, fw)); - darkGradGreen.setFinalStop(QPointF(fw, h)); + darkGradGreen.setStart(QPointF(fw, fw)); + darkGradGreen.setFinalStop(QPointF(w, fw)); - lightGradGreen.setStart(QPointF(fw, fw)); - lightGradGreen.setFinalStop(QPointF(fw, h)); + lightGradGreen.setStart(QPointF(fw, fw)); + lightGradGreen.setFinalStop(QPointF(w, fw)); - { - QPainterPath path; path.addRect(fw, fw, w, yv); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(darkGradGreen)); // dark green - } - { - QPainterPath path; path.addRect(fw, yv, w, h-yv); path &= drawPath; - if(!path.isEmpty()) - p.fillPath(path, QBrush(lightGradGreen)); // light green + { + QPainterPath path; path.addRect(fw, fw, pixv, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(lightGradGreen)); // light green + } + { + QPainterPath path; path.addRect(pixv, fw, w, h); path &= drawPath; + if(!path.isEmpty()) + p.fillPath(path, QBrush(darkGradGreen)); // dark green + } } - } #endif // NOT _USE_CLIPPER + } } @@ -538,13 +1149,163 @@ //--------------------------------------------------------- void Meter::resizeEvent(QResizeEvent* ev) - { - // For some reason upon resizing we get double calls here and in paintEvent. - //printf("Meter::resizeEvent w:%d h:%d\n", ev->size().width(), ev->size().height()); - cur_yv = -1; // Force re-initialization. - QFrame::resizeEvent(ev); - update(); +{ + // For some reason upon resizing we get double calls here and in paintEvent. + //printf("Meter::resizeEvent w:%d h:%d\n", ev->size().width(), ev->size().height()); + + cur_pixv = -1; // Force re-initialization. + QFrame::resizeEvent(ev); + + //update(); //according to docs, update will be called automatically + + QSize s = ev->size(); + + const QFontMetrics fm = fontMetrics(); + // reposition slider + if(_orient == Qt::Horizontal) + { + switch(_scalePos) + { + case Top: + d_scale.setGeometry(this->rect().x(), + this->rect().y() + s.height() - 1 - _scaleDist, + s.width(), + ScaleDraw::Top); + break; + + case Bottom: + d_scale.setGeometry(this->rect().x(), + this->rect().y() + s.height() + _scaleDist, + s.width(), + ScaleDraw::Bottom); + break; + + case InsideHorizontal: + d_scale.setGeometry(this->rect().x(), + this->rect().y() + d_scale.maxHeight(fm) + _scaleDist, + s.width(), + ScaleDraw::InsideHorizontal); + break; + + default: + break; + } + } + else // d_orient == Qt::Vertical + { + switch(_scalePos) + { + case Left: + d_scale.setGeometry(this->rect().x() - _scaleDist, + this->rect().y(), + s.height(), + ScaleDraw::Left); + break; + + case Right: + d_scale.setGeometry(this->rect().x() + width() + _scaleDist, + this->rect().y(), + s.height(), + ScaleDraw::Right); + break; + + case InsideVertical: + { + const int mxlw = d_scale.maxLabelWidth(fm, false); + const int sclw = d_scale.scaleWidth(); + + d_scale.setGeometry(this->rect().x() + mxlw + sclw + _scaleDist, + this->rect().y(), + s.height(), + ScaleDraw::InsideVertical); + } + break; + + default: + break; + } } + + adjustScale(); +} + +void Meter::adjustScale() +{ +// d_maxMinor = maxMin; +// if(hasUserScale()) +// d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor, mstep, log()); +// else +// d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor, log()); +// update(); +// const double range = maxScale() - minScale(); +// if(range == 0.0) +// return; +// +// int maxMaj = 5; +// int maxMin = 3; +// double mstep = scaleStep(); +// +// QFontMetrics fm = fontMetrics(); +// if(_orient == Qt::Horizontal) +// { +// int unit_w = fm.width("888.8888"); +// if(unit_w == 0) +// unit_w = 20; +// +// if(hasUserScale()) +// { +// if(d_sliderRect.width() != 0) +// { +// const int fact = (int)(3.0 * range / (double)(d_sliderRect.width())) + 1; +// mstep *= fact; +// } +// } +// else +// { +// maxMaj = (int)((double)(d_sliderRect.width()) / (1.5 * ((double)unit_w))); +// if(maxMaj < 1) +// maxMaj = 1; +// if(maxMaj > 5) +// maxMaj = 5; +// } +// maxMin = (int)((double)(d_sliderRect.width()) / (1.5 * ((double)unit_w))); +// if(maxMin < 1) +// maxMin = 1; +// if(maxMin > 5) +// maxMin = 5; +// } +// else +// { +// int unit_h = fm.height(); +// if(unit_h == 0) +// unit_h = 20; +// +// if(hasUserScale()) +// { +// if(d_sliderRect.height() != 0) +// { +// const int fact = (int)(3.0 * range / (double)(d_sliderRect.height())) + 1; +// mstep *= fact; +// } +// } +// else +// { +// maxMaj = (int)((double)(d_sliderRect.height()) / (1.5 * ((double)unit_h))); +// if(maxMaj < 1) +// maxMaj = 1; +// if(maxMaj > 5) +// maxMaj = 5; +// } +// maxMin = (int)((double)(d_sliderRect.height()) / (1.5 * ((double)unit_h))); +// if(maxMin < 1) +// maxMin = 1; +// if(maxMin > 5) +// maxMin = 5; +// } +// +// //fprintf(stderr, "Slider::adjustScale: maxMaj:%d maxMin:%d scaleStep:%f\n", maxMaj, maxMin, mstep); +// d_maxMajor = maxMaj; +} //--------------------------------------------------------- // mousePressEvent diff -Nru muse-2.1.2/muse/widgets/meter.h muse-3.0.2+ds1/muse/widgets/meter.h --- muse-2.1.2/muse/widgets/meter.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/meter.h 2018-01-29 20:07:03.000000000 +0000 @@ -6,7 +6,7 @@ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) -// (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) +// (C) Copyright 2011-2016 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 @@ -28,19 +28,29 @@ #define __METER_H__ #include +#include class QResizeEvent; class QMouseEvent; class QPainter; class QPainterPath; +#include + +#include "sclif.h" +#include "scldraw.h" + namespace MusEGui { -class Meter : public QFrame { +class Meter : public QFrame, public ScaleIf { Q_OBJECT public: enum MeterType {DBMeter, LinMeter}; + enum ScalePos { None, Left, Right, Top, Bottom, InsideHorizontal, InsideVertical }; + private: + QColor _primaryColor; + protected: QLinearGradient darkGradRed; QColor dark_red_end; @@ -72,7 +82,7 @@ QColor mask_center; QColor mask_edge; - QColor separator_color;; + QColor separator_color; QColor peak_color; int xrad, yrad; @@ -80,27 +90,67 @@ virtual void paintEvent(QPaintEvent*); virtual void mousePressEvent(QMouseEvent*); + // Adjust scale so marks are not too close together. + void adjustScale(); + private: MeterType mtype; + Qt::Orientation _orient; + ScalePos _scalePos; + int _refreshRate; + int _scaleDist; bool overflow; double val; + double targetVal; + double targetValStep; double maxVal; + double targetMaxVal; double minScale, maxScale; int yellowScale, redScale; - int cur_yv, last_yv, cur_ymax, last_ymax; + int cur_pixv, last_pixv, cur_pixmax, last_pixmax; + bool _showText; + QString _text; + QRect _textRect; + void updateText(double val); void drawVU(QPainter& p, const QRect&, const QPainterPath&, int); + void scaleChange(); + + QTimer fallingTimer; + public slots: void resetPeaks(); void setVal(double, double, bool); + void updateTargetMeterValue(); signals: - void mousePress(); + void mousePress(); public: - Meter(QWidget* parent, MeterType type = DBMeter); + Meter(QWidget* parent, + MeterType type = DBMeter, + Qt::Orientation orient = Qt::Vertical, + double scaleMin = -60.0, double scaleMax = 10.0, + ScalePos scalePos = None, + const QColor& primaryColor = QColor(0, 255, 0), + ScaleDraw::TextHighlightMode textHighlightMode = ScaleDraw::TextHighlightNone, + int refreshRate = 20); + + QColor primaryColor() const { return _primaryColor; } + void setPrimaryColor(const QColor& color); + void setRange(double min, double max); + + void setRefreshRate(int rate); + + bool showText() const { return _showText; } + void setShowText(bool v) { _showText = v; update(); } + + Qt::Orientation orientation() const { return _orient; } + void setOrientation(Qt::Orientation o) { _orient = o; update(); } + + virtual QSize sizeHint() const; }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/meter_slider.cpp muse-3.0.2+ds1/muse/widgets/meter_slider.cpp --- muse-2.1.2/muse/widgets/meter_slider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/meter_slider.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,45 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// meter_slider.cpp +// (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) +// +// 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 + +#include "meter_slider.h" +#include "meter.h" + +namespace MusEGui { + +MeterSlider::MeterSlider(QWidget *parent, const char *name, + Qt::Orientation orient, ScalePos scalePos, int maxMeterChans, int grooveWidth, QColor fillColor) + : Slider(parent, name, orient, scalePos, grooveWidth, fillColor), _maxMeterChans(maxMeterChans) + +{ + +} + +void MeterSlider::paintEvent(QPaintEvent* /*e*/) +{ + +} + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/meter_slider.h muse-3.0.2+ds1/muse/widgets/meter_slider.h --- muse-2.1.2/muse/widgets/meter_slider.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/meter_slider.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,64 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// meter_slider.h +// (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) +// +// 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 __METER_SLIDER_H__ +#define __METER_SLIDER_H__ + +#include "globaldefs.h" +#include "slider.h" + +class QPaintEvent; + +namespace MusEGui { + +class Meter; + +//--------------------------------------------------------- +// MeterSlider +//--------------------------------------------------------- + +class MeterSlider : public Slider +{ + Q_OBJECT + + private: + int _maxMeterChans; + Meter** _meters; + + protected: + virtual void paintEvent (QPaintEvent*); + + public: + MeterSlider(QWidget *parent, const char *name = 0, + Qt::Orientation orient = Qt::Vertical, + ScalePos scalePos = None, + int maxMeterChans = MAX_CHANNELS, + int grooveWidth = 8, + QColor fillColor = QColor(100, 100, 255)); + +}; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/metronomebase.ui muse-3.0.2+ds1/muse/widgets/metronomebase.ui --- muse-2.1.2/muse/widgets/metronomebase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/metronomebase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 647 - 522 + 481 + 513 @@ -17,17 +17,41 @@ true + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + Metronome - - 11 + + 2 + + + 2 + + + 2 + + + 2 - 6 + 2 @@ -76,16 +100,23 @@ + + + Volume + + + + - Audio volume + Audio master false - + 0 @@ -107,7 +138,7 @@ - + 50 @@ -117,7 +148,7 @@ - + Qt::Vertical @@ -133,17 +164,27 @@ - + - Meas volume + Meas + + + false + + + + + + + Beat false - + 0 @@ -165,7 +206,7 @@ - + 50 @@ -175,17 +216,7 @@ - - - - Beat volume - - - false - - - - + 0 @@ -207,7 +238,7 @@ - + 50 @@ -217,17 +248,17 @@ - + - Accent1 volume + Accent1 false - + 0 @@ -249,7 +280,7 @@ - + 50 @@ -259,17 +290,17 @@ - + - Accent2 volume + Accent2 false - + 0 @@ -291,7 +322,7 @@ - + 50 @@ -301,6 +332,25 @@ + + + + Sample + + + + + + + + + + + + + + + @@ -331,12 +381,30 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -467,8 +535,86 @@ + + + + + + Hint: Enable metronome in Transportpanel + + + false + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 13 + 20 + + + + + + + + &Apply + + + Alt+A + + + true + + + + + + + &OK + + + Alt+O + + + true + + + true + + + + + + + &Cancel + + + Alt+C + + + true + + + + + + + false + + + Disabled since jack does not support it + Precount @@ -476,13 +622,22 @@ 6 - - 11 + + 2 + + + 2 + + + 2 + + + 2 - true + false enable @@ -497,13 +652,22 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 - true + false 1 @@ -518,6 +682,9 @@ + + false + Bars @@ -531,7 +698,7 @@ - true + false From Mastertrack @@ -546,13 +713,22 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 - true + false 1 @@ -567,6 +743,9 @@ + + false + / @@ -578,7 +757,7 @@ - true + false 2 @@ -593,6 +772,9 @@ + + false + Signature @@ -606,7 +788,7 @@ - true + false Prerecord @@ -616,91 +798,30 @@ - true + false Preroll + + + + Qt::Vertical + + + + 20 + 40 + + + + - - - - - - Hint: Enable metronome in Transportpanel - - - false - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 13 - 20 - - - - - - - - &Apply - - - Alt+A - - - true - - - - - - - &OK - - - Alt+O - - - true - - - true - - - - - - - &Cancel - - - Alt+C - - - true - - - - - - GroupBox1 - GroupBox2 diff -Nru muse-2.1.2/muse/widgets/metronome.cpp muse-3.0.2+ds1/muse/widgets/metronome.cpp --- muse-2.1.2/muse/widgets/metronome.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/metronome.cpp 2017-12-17 21:07:39.000000000 +0000 @@ -21,14 +21,21 @@ // //========================================================= +#include +#include +#include + #include #include "metronome.h" +#include "midi.h" +#include "ticksynth.h" -#include #include "globals.h" +#include "gconfig.h" #include "song.h" #include "track.h" #include "audio.h" +#include "operations.h" namespace MusEGui { @@ -36,27 +43,12 @@ // MetronomeConfig //--------------------------------------------------------- -MetronomeConfig::MetronomeConfig(QDialog* parent) +MetronomeConfig::MetronomeConfig(QWidget* parent) : QDialog(parent) - { +{ setupUi(this); - volumeSlider->setValue(MusEGlobal::audioClickVolume*100); - measVolumeSlider->setValue(MusEGlobal::measClickVolume*100); - beatVolumeSlider->setValue(MusEGlobal::beatClickVolume*100); - accent1VolumeSlider->setValue(MusEGlobal::accent1ClickVolume*100); - accent2VolumeSlider->setValue(MusEGlobal::accent2ClickVolume*100); - if (MusEGlobal::clickSamples == MusEGlobal::origSamples) - radioSamples2->setChecked(true); - else - radioSamples4->setChecked(true); - switchSamples(); // to disable gui elements - - volumeLabel->setText(QString::number(int(MusEGlobal::audioClickVolume*100))); - measVolumeLabel->setText(QString::number(int(MusEGlobal::measClickVolume*100))); - beatVolumeLabel->setText(QString::number(int(MusEGlobal::beatClickVolume*100))); - accent1VolumeLabel->setText(QString::number(int(MusEGlobal::accent1ClickVolume*100))); - accent2VolumeLabel->setText(QString::number(int(MusEGlobal::accent2ClickVolume*100))); + updateValues(); connect(buttonApply, SIGNAL(clicked()), SLOT(apply())); connect(midiClick, SIGNAL(toggled(bool)), SLOT(midiClickChanged(bool))); @@ -70,6 +62,82 @@ connect(accent1VolumeSlider, SIGNAL(valueChanged(int)), SLOT(accent1VolumeChanged(int))); connect(accent2VolumeSlider, SIGNAL(valueChanged(int)), SLOT(accent2VolumeChanged(int))); connect(radioSamples2, SIGNAL(toggled(bool)),SLOT(switchSamples())); +} + +//--------------------------------------------------------- +// fillSoundFiles +//--------------------------------------------------------- + +void MetronomeConfig::fillSoundFiles() +{ + QDir metroPath(MusEGlobal::museGlobalShare+"/metronome"); + QStringList filters; + filters.append("*.wav"); + QStringList klickfiles = metroPath.entryList(filters); + + measSampleCombo->clear(); + beatSampleCombo->clear(); + accent1SampleCombo->clear(); + accent2SampleCombo->clear(); + + measSampleCombo->addItems(klickfiles); + beatSampleCombo->addItems(klickfiles); + accent1SampleCombo->addItems(klickfiles); + accent2SampleCombo->addItems(klickfiles); + + measSampleCombo->setCurrentIndex(klickfiles.indexOf(MusEGlobal::config.measSample)); + beatSampleCombo->setCurrentIndex(klickfiles.indexOf(MusEGlobal::config.beatSample)); + accent1SampleCombo->setCurrentIndex(klickfiles.indexOf(MusEGlobal::config.accent1Sample)); + accent2SampleCombo->setCurrentIndex(klickfiles.indexOf(MusEGlobal::config.accent2Sample)); +} + +void MetronomeConfig::updateValues() +{ + volumeSlider->blockSignals(true); + measVolumeSlider->blockSignals(true); + beatVolumeSlider->blockSignals(true); + accent1VolumeSlider->blockSignals(true); + accent2VolumeSlider->blockSignals(true); + radioSamples2->blockSignals(true); + radioSamples4->blockSignals(true); + + measureNote->blockSignals(true); + measureVelocity->blockSignals(true); + beatNote->blockSignals(true); + beatVelocity->blockSignals(true); + midiChannel->blockSignals(true); + midiPort->blockSignals(true); + + precountBars->blockSignals(true); + precountEnable->blockSignals(true); + precountFromMastertrack->blockSignals(true); + precountSigZ->blockSignals(true); + precountSigN->blockSignals(true); + precountPrerecord->blockSignals(true); + precountPreroll->blockSignals(true); + + midiClick->blockSignals(true); + audioBeep->blockSignals(true); + + + volumeSlider->setValue(MusEGlobal::audioClickVolume*100); + measVolumeSlider->setValue(MusEGlobal::measClickVolume*100); + beatVolumeSlider->setValue(MusEGlobal::beatClickVolume*100); + accent1VolumeSlider->setValue(MusEGlobal::accent1ClickVolume*100); + accent2VolumeSlider->setValue(MusEGlobal::accent2ClickVolume*100); + if (MusEGlobal::clickSamples == MusEGlobal::origSamples) + radioSamples2->setChecked(true); + else + radioSamples4->setChecked(true); + switchSamples(); // to disable gui elements + + fillSoundFiles(); + + volumeLabel->setText(QString::number(int(MusEGlobal::audioClickVolume*99))); + measVolumeLabel->setText(QString::number(int(MusEGlobal::measClickVolume*99))); + beatVolumeLabel->setText(QString::number(int(MusEGlobal::beatClickVolume*99))); + accent1VolumeLabel->setText(QString::number(int(MusEGlobal::accent1ClickVolume*99))); + accent2VolumeLabel->setText(QString::number(int(MusEGlobal::accent2ClickVolume*99))); measureNote->setValue(MusEGlobal::measureClickNote); measureVelocity->setValue(MusEGlobal::measureClickVelo); @@ -90,7 +158,34 @@ midiClick->setChecked(MusEGlobal::midiClickFlag); audioBeep->setChecked(MusEGlobal::audioClickFlag); - } + + + volumeSlider->blockSignals(false); + measVolumeSlider->blockSignals(false); + beatVolumeSlider->blockSignals(false); + accent1VolumeSlider->blockSignals(false); + accent2VolumeSlider->blockSignals(false); + radioSamples2->blockSignals(false); + radioSamples4->blockSignals(false); + + measureNote->blockSignals(false); + measureVelocity->blockSignals(false); + beatNote->blockSignals(false); + beatVelocity->blockSignals(false); + midiChannel->blockSignals(false); + midiPort->blockSignals(false); + + precountBars->blockSignals(false); + precountEnable->blockSignals(false); + precountFromMastertrack->blockSignals(false); + precountSigZ->blockSignals(false); + precountSigN->blockSignals(false); + precountPrerecord->blockSignals(false); + precountPreroll->blockSignals(false); + + midiClick->blockSignals(false); + audioBeep->blockSignals(false); +} //--------------------------------------------------------- // audioBeepRoutesClicked @@ -119,16 +214,12 @@ QAction* clickaction = pup->exec(QCursor::pos()); if (clickaction) { - //QString s(pup->text(n)); nn = 0; for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) { - //if(((*iao)->name() == s) && (n == nn)) if (nn == clickaction->data()) { - //(*iao)->setSendMetronome(); - MusEGlobal::audio->msgSetSendMetronome(*iao, clickaction->isChecked()); - //MusEGlobal::song->update(SC_ROUTE); + MusEGlobal::audio->msgSetSendMetronome(*iao, clickaction->isChecked()); break; } ++nn; @@ -154,7 +245,7 @@ //--------------------------------------------------------- void MetronomeConfig::apply() - { +{ MusEGlobal::measureClickNote = measureNote->value(); MusEGlobal::measureClickVelo = measureVelocity->value(); MusEGlobal::beatClickNote = beatNote->value(); @@ -172,8 +263,16 @@ MusEGlobal::midiClickFlag = midiClick->isChecked(); MusEGlobal::audioClickFlag = audioBeep->isChecked(); - //audioVolumeChanged = volumeSlider->value(); - } + + MusEGlobal::config.measSample = measSampleCombo->currentText(); + MusEGlobal::config.beatSample = beatSampleCombo->currentText(); + MusEGlobal::config.accent1Sample = accent1SampleCombo->currentText(); + MusEGlobal::config.accent2Sample = accent2SampleCombo->currentText(); + + MusECore::PendingOperationList operations; + MusECore::metronome->initSamplesOperation(operations); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); +} //--------------------------------------------------------- // reject @@ -218,23 +317,23 @@ // these values are directly applied, not using th Apply button, it just seems more usable this way. void MetronomeConfig::volumeChanged(int volume) { MusEGlobal::audioClickVolume=volume/100.0; - volumeLabel->setText(QString::number(int(MusEGlobal::audioClickVolume*100))); + volumeLabel->setText(QString::number(int(MusEGlobal::audioClickVolume*99))); } void MetronomeConfig::measVolumeChanged(int volume) { MusEGlobal::measClickVolume=volume/100.0; - measVolumeLabel->setText(QString::number(int(MusEGlobal::measClickVolume*100))); + measVolumeLabel->setText(QString::number(int(MusEGlobal::measClickVolume*99))); } void MetronomeConfig::beatVolumeChanged(int volume) { MusEGlobal::beatClickVolume=volume/100.0; - beatVolumeLabel->setText(QString::number(int(MusEGlobal::beatClickVolume*100))); + beatVolumeLabel->setText(QString::number(int(MusEGlobal::beatClickVolume*99))); } void MetronomeConfig::accent1VolumeChanged(int volume) { MusEGlobal::accent1ClickVolume=volume/100.0; - accent1VolumeLabel->setText(QString::number(int(MusEGlobal::accent1ClickVolume*100))); + accent1VolumeLabel->setText(QString::number(int(MusEGlobal::accent1ClickVolume*99))); } void MetronomeConfig::accent2VolumeChanged(int volume) { MusEGlobal::accent2ClickVolume=volume/100.0; - accent2VolumeLabel->setText(QString::number(int(MusEGlobal::accent2ClickVolume*100))); + accent2VolumeLabel->setText(QString::number(int(MusEGlobal::accent2ClickVolume*99))); } void MetronomeConfig::switchSamples() { diff -Nru muse-2.1.2/muse/widgets/metronome.h muse-3.0.2+ds1/muse/widgets/metronome.h --- muse-2.1.2/muse/widgets/metronome.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/metronome.h 2017-12-17 21:07:39.000000000 +0000 @@ -37,6 +37,8 @@ class MetronomeConfig : public QDialog, public Ui::MetronomeConfigBase { Q_OBJECT + private: + void fillSoundFiles(); private slots: virtual void accept(); void apply(); @@ -53,7 +55,9 @@ void switchSamples(); public: - MetronomeConfig(QDialog* parent=0); + MetronomeConfig(QWidget* parent=0); + + void updateValues(); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/midi_audio_control.cpp muse-3.0.2+ds1/muse/widgets/midi_audio_control.cpp --- muse-2.1.2/muse/widgets/midi_audio_control.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/midi_audio_control.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -299,8 +299,7 @@ //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()); + QString name = QString("%1:%2").arg(i + 1).arg(MusEGlobal::midiPorts[i].portname()); portComboBox->insertItem(item_idx, name, i); if(_port == -1) _port = i; // Initialize diff -Nru muse-2.1.2/muse/widgets/midisyncimpl.cpp muse-3.0.2+ds1/muse/widgets/midisyncimpl.cpp --- muse-2.1.2/muse/widgets/midisyncimpl.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/midisyncimpl.cpp 2017-12-17 21:07:39.000000000 +0000 @@ -38,13 +38,14 @@ #include "icons.h" #include "sync.h" #include "globals.h" +#include "gconfig.h" #include "midisyncimpl.h" #include "driver/audiodev.h" #include "audio.h" namespace MusEGui { -enum { DEVCOL_NO = 0, DEVCOL_NAME, DEVCOL_IN, DEVCOL_TICKIN, DEVCOL_MRTIN, DEVCOL_MMCIN, DEVCOL_MTCIN, DEVCOL_MTCTYPE, +enum { DEVCOL_NO = 0, DEVCOL_NAME, DEVCOL_SYNC_TO, DEVCOL_IN, DEVCOL_TICKIN, DEVCOL_MRTIN, DEVCOL_MMCIN, DEVCOL_MTCIN, DEVCOL_MTCTYPE, DEVCOL_RID, DEVCOL_RCLK, DEVCOL_RMRT, DEVCOL_RMMC, DEVCOL_RMTC, DEVCOL_RREWSTART, DEVCOL_TID, DEVCOL_TCLK, DEVCOL_TMRT, DEVCOL_TMMC, DEVCOL_TMTC, /* DEVCOL_TREWSTART, */ }; @@ -59,6 +60,7 @@ item->setToolTip(DEVCOL_NO, tr("Port Number")); item->setToolTip(DEVCOL_NAME, tr("Name of the midi device associated with" " this port number")); + item->setToolTip(DEVCOL_SYNC_TO, tr("Sync to this device. Click to select.")); item->setToolTip(DEVCOL_IN, tr("Midi clock input detected")); item->setToolTip(DEVCOL_TICKIN, tr("Midi tick input detected")); item->setToolTip(DEVCOL_MRTIN, tr("Midi real time input detected")); @@ -87,33 +89,28 @@ { item->setWhatsThis(DEVCOL_NO, tr("Port Number")); item->setWhatsThis(DEVCOL_NAME, tr("Name of the midi device associated with this port number")); - item->setWhatsThis(DEVCOL_IN, tr("Midi clock input detected.\n" - "Current port actually used is red.\nClick to force a port to be used.")); + item->setWhatsThis(DEVCOL_SYNC_TO, tr("Sync to this device. Click to select.\n" + "MusE will synchronize to this device's\n clock or MTC quarter-frame,\n if 'slave to external sync' is on.")); + item->setWhatsThis(DEVCOL_IN, tr("Midi clock input detected")); item->setWhatsThis(DEVCOL_TICKIN, tr("Midi tick input detected")); item->setWhatsThis(DEVCOL_MRTIN, tr("Midi realtime input detected, including\n start/stop/continue, and song position.")); item->setWhatsThis(DEVCOL_MMCIN, tr("MMC input detected, including stop/play/deferred play, and locate.")); //"Current port actually used is red. Click to force a port to be current.")); - item->setWhatsThis(DEVCOL_MTCIN, tr("MTC input detected, including forward quarter-frame sync and full-frame locate.\n" - "Current port actually used is red. Click to force a port to be current.")); + item->setWhatsThis(DEVCOL_MTCIN, tr("MTC input detected, including forward quarter-frame sync and full-frame locate")); item->setWhatsThis(DEVCOL_MTCTYPE, tr("Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop\n" "Detects format of MTC quarter and full frame, and MMC locate.")); item->setWhatsThis(DEVCOL_RID, tr("Receive id number. 127 = global receive all, even if not global.")); - item->setWhatsThis(DEVCOL_RCLK, tr("Accept midi clock input. Only one input is used for clock.\n" - "Auto-acquire: If two or more port realtime inputs are enabled,\n" - " the first clock detected is used, until clock is lost,\n" - " then another can take over. Best if each turns off its clock\n" - " at stop, so MusE can re-acquire the clock from another port.\n" - "Click on detect indicator to force another.")); + item->setWhatsThis(DEVCOL_RCLK, tr("Accept midi clock input.\nOnly one port can be used for clock sync.")); item->setWhatsThis(DEVCOL_RMRT, tr("Accept midi realtime input, including\n start/stop/continue, and song position.\n" "Non-clock events (start,stop etc) are\n accepted by ALL enabled ports.\n" "This means you may have several master\n devices connected, and muse will accept\n" " input from them.")); item->setWhatsThis(DEVCOL_RMMC, tr("Accept MMC input, including stop/play/deferred play, and locate.")); item->setWhatsThis(DEVCOL_RMTC, tr("Accept MTC input, including forward quarter-frame sync and full-frame locate.\n" - "See 'rc' column for more help.")); + "Only one port can be used for sync.")); item->setWhatsThis(DEVCOL_RREWSTART, tr("When start is received, rewind before playing.\n" - "Note: It may be impossible to rewind fast\n" - " enough to synchronize with the external device.")); + "Note: In some cases, such as having many\n project audio tracks, it may be impossible\n to rewind fast" + " enough to synchronize\n with the external device.\nManually rewinding the device before\n playing is recommended.")); item->setWhatsThis(DEVCOL_TID, tr("Transmit id number. 127 = global transmit to all.")); item->setWhatsThis(DEVCOL_TCLK, tr("Send midi clock output. If 'Slave to External Sync' is chosen,\n" " muse can re-transmit clock to any other chosen ports.")); @@ -141,7 +138,7 @@ void MidiSyncLViewItem::setPort(int port) { _port = port; - if(_port < 0 || port > MIDI_PORTS) + if(_port < 0 || port >= MIDI_PORTS) return; copyFromSyncInfo(MusEGlobal::midiPorts[port].syncInfo()); @@ -199,13 +196,18 @@ { setupUi(this); + // Remember the current value, for cancelling. + _curMidiSyncInPort = MusEGlobal::config.curMidiSyncInPort; + _dirty = false; applyButton->setEnabled(false); + okButton->setEnabled(false); devicesListView->setAllColumnsShowFocus(true); QStringList columnnames; columnnames << tr("Port") << tr("Device Name") + << tr("s") << tr("c") << tr("k") << tr("r") @@ -260,6 +262,10 @@ connect(syncRecTempoValQuant, SIGNAL(valueChanged(double)), SLOT(syncChanged())); connect(&MusEGlobal::extSyncFlag, SIGNAL(valueChanged(bool)), SLOT(extSyncChanged(bool))); connect(syncDelaySpinBox, SIGNAL(valueChanged(int)), SLOT(syncChanged())); + + connect(extSyncCheckbox, SIGNAL(toggled(bool)), &MusEGlobal::extSyncFlag, SLOT(setValue(bool))); + connect(useJackTransportCheckbox, SIGNAL(toggled(bool)),&MusEGlobal::useJackTransport, SLOT(setValue(bool))); + connect(&MusEGlobal::useJackTransport, SIGNAL(valueChanged(bool)), SLOT(useJackTransportChanged(bool))); // Done in show(). //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); @@ -277,8 +283,7 @@ void MidiSyncConfig::songChanged(MusECore::SongChangedFlags_t flags) { // Is it simply a midi controller value adjustment? Forget it. Otherwise, it's mainly midi port/device changes we want. - if(flags == SC_MIDI_CONTROLLER || - !(flags & (SC_CONFIG | SC_MASTER | SC_TEMPO | SC_SIG | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | + if(!(flags & (SC_CONFIG | SC_MASTER | SC_TEMPO | SC_SIG | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED | SC_MIDI_CONTROLLER_ADD))) return; @@ -287,6 +292,8 @@ _dirty = false; if(applyButton->isEnabled()) applyButton->setEnabled(false); + if(okButton->isEnabled()) + okButton->setEnabled(false); //for(int i = 0; i < MIDI_PORTS; ++i) // tmpMidiSyncPorts[i] = midiSyncPorts[i]; @@ -355,16 +362,6 @@ bool sdet = MusEGlobal::midiPorts[port].syncInfo().MCSyncDetect(); if(sdet) { - if(port == MusEGlobal::curMidiSyncInPort) - { - if(!lvi->_curDet) - { - lvi->_curDet = true; - lvi->_inDet = false; - lvi->setIcon(DEVCOL_IN, QIcon( *record1_Icon)); - } - } - else if(!lvi->_inDet) { lvi->_inDet = true; @@ -382,6 +379,11 @@ } } + if(port == MusEGlobal::config.curMidiSyncInPort) + lvi->setIcon(DEVCOL_SYNC_TO, QIcon( *record1_Icon)); + else + lvi->setIcon(DEVCOL_SYNC_TO, QIcon( *dothIcon)); + sdet = MusEGlobal::midiPorts[port].syncInfo().tickDetect(); if(sdet) { @@ -463,16 +465,6 @@ if(mtcdet) { - if(port == MusEGlobal::curMidiSyncInPort) - { - if(!lvi->_curMTCDet) - { - lvi->_curMTCDet = true; - lvi->_MTCDet = false; - lvi->setIcon(DEVCOL_MTCIN, QIcon( *record1_Icon)); - } - } - else if(!lvi->_MTCDet) { lvi->_MTCDet = true; @@ -539,6 +531,19 @@ // extSyncChanged //--------------------------------------------------------- +void MidiSyncConfig::useJackTransportChanged(bool v) + { + useJackTransportCheckbox->blockSignals(true); + useJackTransportCheckbox->setChecked(v); +// if(v) +// MusEGlobal::song->setMasterFlag(false); + useJackTransportCheckbox->blockSignals(false); + } + +//--------------------------------------------------------- +// extSyncChanged +//--------------------------------------------------------- + void MidiSyncConfig::extSyncChanged(bool v) { extSyncCheckbox->blockSignals(true); @@ -555,7 +560,13 @@ void MidiSyncConfig::ok() { apply(); - cancel(); + _dirty = false; + if(applyButton->isEnabled()) + applyButton->setEnabled(false); + if(okButton->isEnabled()) + okButton->setEnabled(false); + + close(); } //--------------------------------------------------------- @@ -564,9 +575,14 @@ void MidiSyncConfig::cancel() { + // Restore the current sync port. + MusEGlobal::config.curMidiSyncInPort = _curMidiSyncInPort; + _dirty = false; if(applyButton->isEnabled()) applyButton->setEnabled(false); + if(okButton->isEnabled()) + okButton->setEnabled(false); close(); } @@ -641,14 +657,12 @@ 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::midiSyncContainer.setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); } } MusEGlobal::syncRecTempoValQuant = syncRecTempoValQuant->value(); - if(MusEGlobal::midiSeq) - MusEGlobal::midiSeq->setRecTempoValQuant(MusEGlobal::syncRecTempoValQuant); - + MusEGlobal::midiSyncContainer.setRecTempoValQuant(MusEGlobal::syncRecTempoValQuant); + MusEGlobal::mtcOffset.setH(mtcOffH->value()); MusEGlobal::mtcOffset.setM(mtcOffM->value()); MusEGlobal::mtcOffset.setS(mtcOffS->value()); @@ -664,14 +678,20 @@ } + // Update the current value. + _curMidiSyncInPort = MusEGlobal::config.curMidiSyncInPort; + if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) MusEGlobal::audio->msgIdle(false); - //muse->changeConfig(true); // save settings + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + //muse->changeConfig(true); _dirty = false; if(applyButton->isEnabled()) applyButton->setEnabled(false); + if(okButton->isEnabled()) + okButton->setEnabled(false); // Do not call this. Causes freeze sometimes. Only will be needed if extra pollfds are used by midi seq thread. //midiSeq->msgUpdatePollFd(); @@ -712,13 +732,6 @@ if(portsi.MCSyncDetect()) { - if(i == MusEGlobal::curMidiSyncInPort) - { - lvi->_curDet = true; - lvi->_inDet = false; - lvi->setIcon(DEVCOL_IN, QIcon( *record1_Icon)); - } - else { lvi->_curDet = false; lvi->_inDet = true; @@ -732,6 +745,11 @@ lvi->setIcon(DEVCOL_IN, QIcon( *dothIcon)); } + if(i == MusEGlobal::config.curMidiSyncInPort) + lvi->setIcon(DEVCOL_SYNC_TO, QIcon( *record1_Icon)); + else + lvi->setIcon(DEVCOL_SYNC_TO, QIcon( *dothIcon)); + if(portsi.tickDetect()) { lvi->_tickDet = true; @@ -789,13 +807,6 @@ if(portsi.MTCDetect()) { - if(i == MusEGlobal::curMidiSyncInPort) - { - lvi->_curMTCDet = true; - lvi->_MTCDet = false; - lvi->setIcon(DEVCOL_MTCIN, QIcon( *record1_Icon)); - } - else { lvi->_curMTCDet = false; lvi->_MTCDet = true; @@ -851,6 +862,7 @@ devicesListView->resizeColumnToContents(DEVCOL_NO); //devicesListView->resizeColumnToContents(DEVCOL_NAME); devicesListView->header()->resizeSection(DEVCOL_NAME, 120); + devicesListView->resizeColumnToContents(DEVCOL_SYNC_TO); devicesListView->resizeColumnToContents(DEVCOL_IN); devicesListView->resizeColumnToContents(DEVCOL_TICKIN); devicesListView->resizeColumnToContents(DEVCOL_MRTIN); @@ -869,21 +881,22 @@ devicesListView->resizeColumnToContents(DEVCOL_TMMC); devicesListView->resizeColumnToContents(DEVCOL_TMTC); - devicesListView->header()->setResizeMode(DEVCOL_NO, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_IN, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_TICKIN, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_MRTIN, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_MMCIN, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_MTCIN, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_RCLK, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_RMRT, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_RMMC, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_RMTC, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_RMTC, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_RREWSTART, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_TCLK, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_TMRT, QHeaderView::Fixed); - devicesListView->header()->setResizeMode(DEVCOL_TMMC, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_NO, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_SYNC_TO, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_IN, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_TICKIN, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_MRTIN, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_MMCIN, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_MTCIN, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_RCLK, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_RMRT, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_RMMC, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_RMTC, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_RMTC, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_RREWSTART, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_TCLK, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_TMRT, QHeaderView::Fixed); + devicesListView->header()->setSectionResizeMode(DEVCOL_TMMC, QHeaderView::Fixed); } @@ -912,45 +925,28 @@ break; case DEVCOL_NAME: break; - case DEVCOL_IN: - // 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) + case DEVCOL_SYNC_TO: + if(no != MusEGlobal::config.curMidiSyncInPort) { - if(lvi->_recMC && MusEGlobal::midiPorts[no].syncInfo().MCSyncDetect()) - { - MusEGlobal::curMidiSyncInPort = no; - lvi->setIcon(DEVCOL_IN, QIcon( *record1_Icon)); - } - if(lvi->_recMTC && MusEGlobal::midiPorts[no].syncInfo().MTCDetect()) - { - MusEGlobal::curMidiSyncInPort = no; - lvi->setIcon(DEVCOL_MTCIN, QIcon( *record1_Icon)); - } + // Turn off the current sync port's light. + MidiSyncLViewItem* prev_lvi = + (MidiSyncLViewItem*)devicesListView->topLevelItem(MusEGlobal::config.curMidiSyncInPort); + if(prev_lvi) + prev_lvi->setIcon(DEVCOL_SYNC_TO, QIcon( *dothIcon)); + + // Set the current sync port and turn on the port's light. + MusEGlobal::config.curMidiSyncInPort = no; + lvi->setIcon(DEVCOL_SYNC_TO, QIcon( *record1_Icon)); + setDirty(); } break; + case DEVCOL_IN: + break; case DEVCOL_TICKIN: break; case DEVCOL_MMCIN: break; case DEVCOL_MTCIN: - // 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) - { - if(lvi->_recMTC && MusEGlobal::midiPorts[no].syncInfo().MTCDetect()) - { - MusEGlobal::curMidiSyncInPort = no; - lvi->setIcon(DEVCOL_MTCIN, QIcon( *record1_Icon)); - } - if(lvi->_recMC && MusEGlobal::midiPorts[no].syncInfo().MCSyncDetect()) - { - MusEGlobal::curMidiSyncInPort = no; - lvi->setIcon(DEVCOL_IN, QIcon( *record1_Icon)); - } - } break; case DEVCOL_MTCTYPE: break; @@ -1027,7 +1023,7 @@ if(col == DEVCOL_RID) { int val = lvi->_idIn; - int newval = QInputDialog::getInteger(this, "Muse: Sync info" , "Enter new id number (127 = all):", val, 0, 127, 1, &ok); + int newval = QInputDialog::getInt(this, "Muse: Sync info" , "Enter new id number (127 = all):", val, 0, 127, 1, &ok); if(ok) { lvi->_idIn = newval; @@ -1038,7 +1034,7 @@ if(col == DEVCOL_TID) { int val = lvi->_idOut; - int newval = QInputDialog::getInteger(this, "Muse: Sync info" , "Enter new id number (127 = global):", val, 0, 127, 1, &ok); + int newval = QInputDialog::getInt(this, "Muse: Sync info" , "Enter new id number (127 = global):", val, 0, 127, 1, &ok); if(ok) { lvi->_idOut = newval; @@ -1059,6 +1055,8 @@ _dirty = true; if(!applyButton->isEnabled()) applyButton->setEnabled(true); + if(!okButton->isEnabled()) + okButton->setEnabled(true); } } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/midisyncimpl.h muse-3.0.2+ds1/muse/widgets/midisyncimpl.h --- muse-2.1.2/muse/widgets/midisyncimpl.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/midisyncimpl.h 2017-12-17 21:07:39.000000000 +0000 @@ -102,6 +102,7 @@ bool inHeartBeat; bool _dirty; + int _curMidiSyncInPort; // The original value, for restoring on Cancel. void updateSyncInfoLV(); void closeEvent(QCloseEvent*); @@ -112,6 +113,7 @@ private slots: void heartBeat(); void syncChanged(); + void useJackTransportChanged(bool v); void extSyncChanged(bool v); void ok(); void cancel(); diff -Nru muse-2.1.2/muse/widgets/midisync.ui muse-3.0.2+ds1/muse/widgets/midisync.ui --- muse-2.1.2/muse/widgets/midisync.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/midisync.ui 2017-12-17 21:07:39.000000000 +0000 @@ -10,7 +10,7 @@ 0 0 655 - 445 + 534 @@ -22,7 +22,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -79,8 +88,17 @@ 6 - - 11 + + 6 + + + 2 + + + 6 + + + 2 @@ -89,6 +107,9 @@ Type: + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + false @@ -125,30 +146,13 @@ - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 40 - 20 - - - - - - - - - Offset: + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + false @@ -280,7 +284,7 @@ including stop, start and position.
    - Use Jack transport + Use Jack Transport false @@ -304,7 +308,7 @@ You can always click here again for Master.
    - Jack transport Timebase Master + Make Muse the Jack Transport timebase master true diff -Nru muse-2.1.2/muse/widgets/mixdowndialog.cpp muse-3.0.2+ds1/muse/widgets/mixdowndialog.cpp --- muse-2.1.2/muse/widgets/mixdowndialog.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/mixdowndialog.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -23,6 +23,7 @@ #include #include "globals.h" +#include "gconfig.h" #include "mixdowndialog.h" #include "wave.h" @@ -51,7 +52,7 @@ //--------------------------------------------------------- MixdownFileDialog::MixdownFileDialog(const MusECore::SndFile* _sf, - QWidget* parent, Qt::WFlags fl) + QWidget* parent, Qt::WindowFlags fl) : QDialog(parent, fl) { setupUi(this); @@ -118,10 +119,13 @@ QString oldpath; if (sf) oldpath = sf->path(); - QString path = QFileDialog::getSaveFileName( - this, 0, oldpath, tr("Wave Files (*.wav);;All Files (*)")); + if (!MusEGlobal::config.mixdownPath.isEmpty()) { + printf("Setting oldpath to %s\n", MusEGlobal::config.mixdownPath.toLatin1().data()); + oldpath = MusEGlobal::config.mixdownPath; + } + QString path = QFileDialog::getSaveFileName(this, 0, oldpath, tr("Wave Files (*.wav);;All Files (*)")); if (!path.isEmpty()) editPath->setText(path); + MusEGlobal::config.mixdownPath = path; } - } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/mixdowndialog.h muse-3.0.2+ds1/muse/widgets/mixdowndialog.h --- muse-2.1.2/muse/widgets/mixdowndialog.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/mixdowndialog.h 2017-12-04 21:01:19.000000000 +0000 @@ -48,7 +48,7 @@ public: MixdownFileDialog(const MusECore::SndFile* f, QWidget* parent = 0, - Qt::WFlags fl = 0); + Qt::WindowFlags fl = 0); MusECore::SndFile* sndFile() { return sf; } }; diff -Nru muse-2.1.2/muse/widgets/mmath.cpp muse-3.0.2+ds1/muse/widgets/mmath.cpp --- muse-2.1.2/muse/widgets/mmath.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/mmath.cpp 2018-01-11 17:41:21.000000000 +0000 @@ -131,7 +131,7 @@ lx = log10(fabs(x)); p10 = floor(lx); - fr = pow(10.0,lx - p10); + fr = exp10(lx - p10); if (fr <=1.0) fr = 1.0; else if (fr <= 2.0) @@ -140,7 +140,7 @@ fr = 5.0; else fr = 10.0; - rv = fr * pow(10.0,p10); + rv = fr * exp10(p10); return sign * rv; } @@ -168,7 +168,7 @@ lx = log10(fabs(x)); p10 = floor(lx); - fr = pow(10.0,lx - p10); + fr = exp10(lx - p10); if (fr >= 10.0) fr = 10.0; else if (fr >= 5.0) @@ -177,7 +177,7 @@ fr = 2.0; else fr = 1.0; - rv = fr * pow(10.0,p10); + rv = fr * exp10(p10); return sign * rv; } diff -Nru muse-2.1.2/muse/widgets/mtrackinfobase.ui muse-3.0.2+ds1/muse/widgets/mtrackinfobase.ui --- muse-2.1.2/muse/widgets/mtrackinfobase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/mtrackinfobase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 94 - 417 + 93 + 437 @@ -59,16 +59,103 @@ 0 - - + + - + + 0 + 0 + + + + false + + + off + + + -127 + + + 127 + + + 1 + + + + + + + + 0 + 0 + + + + false + + + % + + + 25 + + + 200 + + + 100 + + + + + + + 0 0 + + Out ch + - true + false + + + 0 + + + 1 + + + + + + + + 0 + 0 + + + + + 8 + 5 + + + + false + + + -127 + + + 127 + + + 0 @@ -85,54 +172,42 @@ - - + + 0 0 - - output channel - - - all midi events are sent to this output channel - false + + off + - 1 + -1000 - 16 + 1000 - - + + - + 0 0 - - Out ch - - false - - - 0 - - - 1 + true - + 0 @@ -204,10 +279,10 @@ - Midi thru + Monitor - Pass input events through ('thru') to output. + Pass input through to output @@ -251,33 +326,8 @@ - - - - - 0 - 0 - - - - false - - - off - - - -127 - - - 127 - - - 1 - - - - - + + 0 @@ -285,10 +335,10 @@ - Transpose notes up or down + Change note length in percent of actual length - Transp. + Length false @@ -298,30 +348,30 @@ - - + + 0 0 - - false + + Offset playback of notes before or after actual note - - off + + Delay - - -1000 + + false - - 1000 + + 2 - - + + 0 @@ -329,10 +379,10 @@ - Offset playback of notes before or after actual note + Transpose notes up or down - Delay + Transp. false @@ -342,8 +392,8 @@ - - + + 0 @@ -356,6 +406,9 @@ % + + + 25 @@ -367,108 +420,92 @@ - - - - - 0 - 0 - - - - Change note length in percent of actual length - - - Length - - - false - - - 2 + + + + true - - - - 0 0 - - - 8 - 5 - + + Bank Select MSB. Ctrl-double-click on/off. false + + off + - -127 + 0 - 127 + 128 0 - - + + 0 0 - - <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + + 1 - Velocity + H-Bank false - + 0 + + 2 + - - + + - + 0 0 + + Bank Select LSB. Ctrl-double-click on/off. + false - - % - - - + + off - 25 + 0 - 200 + 128 - 100 + 0 - - + + 0 @@ -476,67 +513,45 @@ - Compress the notes velocity range, in percent of actual velocity + output channel - - Compr. + + all midi events are sent to this output channel - + false - - 2 + + 1 + + + 16 - - + + - + 0 0 - - QFrame::WinPanel - - - QFrame::Raised - - - 1 - - - 1 + + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> - Channel Info - - - Qt::AlignCenter + Velocity false - - - - - - - 0 - 0 - - - - Select instrument patch - - - <unknown> + + 0 - + @@ -555,7 +570,7 @@ - + 0 @@ -594,61 +609,55 @@ - - + + - + 0 0 - - Bank Select MSB. Ctrl-double-click on/off. + + QFrame::WinPanel - - false + + QFrame::Raised - - off + + 1 - - 0 + + 1 - - 128 + + Channel Info - - 0 + + Qt::AlignCenter + + + false - - + + - + 0 0 - - 1 + + Select instrument patch - H-Bank - - - false - - - 0 - - - 2 + <unknown> - - + + 0 @@ -656,26 +665,20 @@ - Bank Select LSB. Ctrl-double-click on/off. - - - false - - - off + Compress the notes velocity range, in percent of actual velocity - - 0 + + Compr. - - 128 + + false - - 0 + + 2 - + @@ -700,7 +703,7 @@ - + @@ -728,7 +731,7 @@ - + 0 @@ -767,7 +770,7 @@ - + @@ -795,7 +798,7 @@ - + 0 @@ -834,7 +837,7 @@ - + @@ -865,7 +868,7 @@ - + 0 @@ -904,7 +907,7 @@ - + Qt::Vertical @@ -920,6 +923,22 @@ + + + + + 0 + 0 + + + + Select instrument + + + <unknown> + + + diff -Nru muse-2.1.2/muse/widgets/mtrackinfo.cpp muse-3.0.2+ds1/muse/widgets/mtrackinfo.cpp --- muse-2.1.2/muse/widgets/mtrackinfo.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/mtrackinfo.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -2,7 +2,7 @@ // MusE // Linux Music Editor // (C) Copyright 2010 Werner Schweer and others (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -46,6 +46,7 @@ #include "route.h" #include "popupmenu.h" #include "routepopup.h" +#include "lv2host.h" namespace MusEGui { @@ -106,7 +107,7 @@ iChanDetectLabel->setPixmap(*darkRedLedIcon); recEchoButton->setFocusPolicy(Qt::NoFocus); - recEchoButton->setIcon((selected && ((MusECore::MidiTrack*)selected)->recEcho()) ? QIcon(*midiThruOnIcon) : QIcon(*midiThruOffIcon)); + recEchoButton->setIcon((selected && ((MusECore::MidiTrack*)selected)->recMonitor()) ? QIcon(*midiThruOnIcon) : QIcon(*midiThruOffIcon)); recEchoButton->setIconSize(midiThruOnIcon->size()); //recEchoButton->setOffPixmap(midiThruOffIcon); //recEchoButton->setOnPixmap(midiThruOnIcon); @@ -121,10 +122,13 @@ oRButton->setIconSize(routesMidiOutIcon->size()); //oRButton->setOffPixmap(routesMidiOutIcon); + updateRouteButtons(); + recordButton->setFocusPolicy(Qt::NoFocus); progRecButton->setFocusPolicy(Qt::NoFocus); volRecButton->setFocusPolicy(Qt::NoFocus); panRecButton->setFocusPolicy(Qt::NoFocus); + instrPushButton->setFocusPolicy(Qt::NoFocus); iPatch->setFocusPolicy(Qt::NoFocus); iOutput->setFocusPolicy(Qt::NoFocus); @@ -153,36 +157,10 @@ //trackNameLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum)); if(selected) - { trackNameLabel->setObjectName(selected->cname()); - - /*QPalette pal; - QColor c; - //pal.setColor(trackNameLabel->backgroundRole(), QColor(0, 160, 255)); // Med blue - if(selected->type() == MusECore::Track::DRUM) - c = MusEGlobal::config.drumTrackLabelBg; - else - c = MusEGlobal::config.midiTrackLabelBg; - - QLinearGradient gradient(trackNameLabel->geometry().topLeft(), trackNameLabel->geometry().bottomLeft()); - //gradient.setColorAt(0, c.darker()); - //gradient.setColorAt(0, c); - //gradient.setColorAt(1, c.darker()); - gradient.setColorAt(0, c.lighter()); - gradient.setColorAt(1, c); - //palette.setBrush(QPalette::Button, gradient); - //palette.setBrush(QPalette::Window, gradient); - pal.setBrush(trackNameLabel->backgroundRole(), gradient); - trackNameLabel->setPalette(pal); */ - } - //else - //{ - // pal.setColor(trackNameLabel->backgroundRole(), MusEGlobal::config.midiTrackLabelBg); - // trackNameLabel->setPalette(pal); - //} //trackNameLabel->setStyleSheet(QString("background-color: ") + QColor(0, 160, 255).name()); // Med blue - trackNameLabel->setWordWrap(true); +// trackNameLabel->setWordWrap(true); trackNameLabel->setAutoFillBackground(true); trackNameLabel->setTextFormat(Qt::PlainText); trackNameLabel->setLineWidth(2); @@ -192,18 +170,27 @@ setLabelText(); setLabelFont(); + MusECore::MidiInstrument* minstr = NULL; + MusECore::MidiTrack* track = static_cast(selected); + if(track) + minstr = MusEGlobal::midiPorts[track->outPort()].instrument(); + if(minstr) + { + instrPushButton->setText(minstr->iname()); + if(minstr->isSynti()) + instrPushButton->setEnabled(false); + else + instrPushButton->setEnabled(true); + } + else + instrPushButton->setText(tr("")); + //setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding)); - connect(iPatch, SIGNAL(released()), SLOT(instrPopup())); + connect(instrPushButton, SIGNAL(released()), SLOT(instrPopup())); + connect(iPatch, SIGNAL(released()), SLOT(patchPopup())); - //pop = new QMenu(iPatch); - //pop->setCheckable(false); // not needed in Qt4 - - // Removed by Tim. p3.3.9 - //connect(iName, SIGNAL(returnPressed()), SLOT(iNameChanged())); - connect(iOutputChannel, SIGNAL(valueChanged(int)), SLOT(iOutputChannelChanged(int))); - ///connect(iInputChannel, SIGNAL(textChanged(const QString&)), SLOT(iInputChannelChanged(const QString&))); connect(iHBank, SIGNAL(valueChanged(int)), SLOT(iProgHBankChanged())); connect(iLBank, SIGNAL(valueChanged(int)), SLOT(iProgLBankChanged())); connect(iProgram, SIGNAL(valueChanged(int)), SLOT(iProgramChanged())); @@ -220,7 +207,6 @@ connect(iPan, SIGNAL(valueChanged(int)), SLOT(iPanChanged(int))); connect(iPan, SIGNAL(ctrlDoubleClicked()), SLOT(iPanDoubleClicked())); connect(iOutput, SIGNAL(activated(int)), SLOT(iOutputPortChanged(int))); - ///connect(iInput, SIGNAL(textChanged(const QString&)), SLOT(iInputPortChanged(const QString&))); connect(recordButton, SIGNAL(clicked()), SLOT(recordClicked())); connect(progRecButton, SIGNAL(clicked()), SLOT(progRecClicked())); connect(volRecButton, SIGNAL(clicked()), SLOT(volRecClicked())); @@ -259,11 +245,6 @@ connect(iLen, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(iKompr, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(iPan, SIGNAL(escapePressed()), SIGNAL(escapePressed())); - - // TODO: Works OK, but disabled for now, until we figure out what to do about multiple out routes and display values... - // Enabled (for Midi Port to Audio Input routing). p4.0.14 Tim. - //oRButton->setEnabled(false); - //oRButton->setVisible(false); connect(oRButton, SIGNAL(pressed()), SLOT(outRoutesPressed())); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); @@ -278,7 +259,6 @@ void MidiTrackInfo::heartBeat() { - ///if(!showTrackinfoFlag || !selected) if(_blockHeartbeatCount < 0) // error { fprintf(stderr, "ERROR: MidiTrackInfo::heartBeat: _blockHeartbeatCount is < 0, resetting to 0\n"); @@ -297,81 +277,47 @@ int outChannel = track->outChannel(); int outPort = track->outPort(); - ///int ichMask = track->inChannelMask(); - //int iptMask = track->inPortMask(); - ///unsigned int iptMask = track->inPortMask(); - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outPort]; - // Set record echo. - //if(recEchoButton->isChecked() != track->recEcho()) - //{ - // recEchoButton->blockSignals(true); - // recEchoButton->setChecked(track->recEcho()); - // recEchoButton->blockSignals(false); - //} - // Check for detection of midi general activity on chosen channels... int mpt = 0; - //int mch = 0; MusECore::RouteList* rl = track->inRoutes(); MusECore::ciRoute r = rl->begin(); - //for( ; mpt < MIDI_PORTS; ++mpt) for( ; r != rl->end(); ++r) { - //if(!r->isValid() || ((r->type != Route::ALSA_MIDI_ROUTE) && (r->type != Route::JACK_MIDI_ROUTE))) - //if(!r->isValid() || (r->type != Route::MIDI_DEVICE_ROUTE)) - if(!r->isValid() || (r->type != MusECore::Route::MIDI_PORT_ROUTE)) // p3.3.49 + if(r->type != MusECore::Route::MIDI_PORT_ROUTE || !r->isValid()) continue; // NOTE: TODO: Code for channelless events like sysex, ** IF we end up using the 'special channel 17' method. //if(r->channel == -1) - if(r->channel == -1 || r->channel == 0) // p3.3.50 - continue; +// if(r->channel == -1 || r->channel == 0) +// continue; // No port assigned to the device? - //mpt = r->device->midiPort(); - mpt = r->midiPort; // p3.3.49 + mpt = r->midiPort; if(mpt < 0 || mpt >= MIDI_PORTS) continue; - - //for(; mch < MIDI_CHANNELS; ++mch) - //{ - //if(MusEGlobal::midiPorts[mpt].syncInfo().actDetect(mch) && (iptMask & (1 << mpt)) && (ichMask & (1 << mch)) ) - //if((iptMask & bitShiftLU[mpt]) && (MusEGlobal::midiPorts[mpt].syncInfo().actDetectBits() & ichMask) ) - //if(MusEGlobal::midiPorts[mpt].syncInfo().actDetectBits() & bitShiftLU[r->channel]) - if(MusEGlobal::midiPorts[mpt].syncInfo().actDetectBits() & r->channel) // p3.3.50 Use new channel mask. + + const int det_bits = MusEGlobal::midiPorts[mpt].syncInfo().actDetectBits(); + if(!det_bits) + continue; + if(r->channel == -1 || (det_bits & (1 << r->channel))) + { + if(!_midiDetect) { - //if(iChanTextLabel->paletteBackgroundColor() != green) - // iChanTextLabel->setPaletteBackgroundColor(green); - //if(iChanDetectLabel->pixmap() != greendotIcon) - if(!_midiDetect) - { - //printf("Arranger::midiTrackInfoHeartBeat setting green icon\n"); - - _midiDetect = true; - //iChanDetectLabel->setPixmap(*greendotIcon); - iChanDetectLabel->setPixmap(*redLedIcon); - } - break; - } - //} + _midiDetect = true; + iChanDetectLabel->setPixmap(*redLedIcon); + } + break; + } } // No activity detected? - //if(mch == MIDI_CHANNELS) - //if(mpt == MIDI_PORTS) if(r == rl->end()) { - //if(iChanTextLabel->paletteBackgroundColor() != darkGreen) - // iChanTextLabel->setPaletteBackgroundColor(darkGreen); - //if(iChanDetectLabel->pixmap() != darkgreendotIcon) if(_midiDetect) { - //printf("Arranger::midiTrackInfoHeartBeat setting darkgreen icon\n"); - _midiDetect = false; - //iChanDetectLabel->setPixmap(*darkgreendotIcon); iChanDetectLabel->setPixmap(*darkRedLedIcon); } } @@ -381,8 +327,6 @@ { if(program != MusECore::CTRL_VAL_UNKNOWN) { - //printf("Arranger::midiTrackInfoHeartBeat setting program to unknown\n"); - program = MusECore::CTRL_VAL_UNKNOWN; if(iHBank->value() != 0) { @@ -414,7 +358,7 @@ else { MusECore::MidiInstrument* instr = mp->instrument(); - const QString name = instr->getPatchName(outChannel, nprogram, track->isDrumTrack()); + const QString name = instr->getPatchName(outChannel, nprogram, track->isDrumTrack(), true); // Include default. if(name.isEmpty()) { const QString n("???"); @@ -427,7 +371,7 @@ } else { - // p4.0.27 The optimizing below, to avoid repeatedly calling getPatchName, generally worked OK. + // The optimizing below, to avoid repeatedly calling getPatchName, generally worked OK. // But Fluidsynth revealed a flaw. When loading a song, updateTrackInfo is called which correctly // sets program = nprogram. But a synth will not receive midistate sysexes until later. // With Fluidsynth, that messed up our optimizing because the soundfont has not loaded yet. @@ -440,37 +384,27 @@ // The alternative is to have a system where the synth can signal the host when a change has happened. // Or an 'isValidPatch' function, or make getPatchName (and several others) return 0, so that updateTrackInfo // can ignore it. Oops. No! Even if we make updateTrackInfo ignore it, then the same thing happens here. - // Thats is, program = nprogram but the text is still wrong. Not much choice but to do this for now... + // That is, program = nprogram but the text is still wrong. Not much choice but to do this for now... if(++heartBeatCounter >= 20) heartBeatCounter = 0; if(program != nprogram || heartBeatCounter == 0) { program = nprogram; - //int hb, lb, pr; - //if (program == MusECore::CTRL_VAL_UNKNOWN) { - // hb = lb = pr = 0; - // iPatch->setText("---"); - // } - //else - //{ - MusECore::MidiInstrument* instr = mp->instrument(); - const QString name = instr->getPatchName(outChannel, program, track->isDrumTrack()); - if(iPatch->text() != name) - iPatch->setText(name); - - int hb = ((program >> 16) & 0xff) + 1; - if (hb == 0x100) - hb = 0; - int lb = ((program >> 8) & 0xff) + 1; - if (lb == 0x100) - lb = 0; - int pr = (program & 0xff) + 1; - if (pr == 0x100) - pr = 0; - //} - - //printf("Arranger::midiTrackInfoHeartBeat setting program\n"); + MusECore::MidiInstrument* instr = mp->instrument(); + const QString name = instr->getPatchName(outChannel, program, track->isDrumTrack(), true); // Include default. + if(iPatch->text() != name) + iPatch->setText(name); + + int hb = ((program >> 16) & 0xff) + 1; + if (hb == 0x100) + hb = 0; + int lb = ((program >> 8) & 0xff) + 1; + if (lb == 0x100) + lb = 0; + int pr = (program & 0xff) + 1; + if (pr == 0x100) + pr = 0; if(iHBank->value() != hb) { @@ -498,12 +432,7 @@ int mn = mc->minVal(); int v = mp->hwCtrlState(outChannel, MusECore::CTRL_VOLUME); if(v == MusECore::CTRL_VAL_UNKNOWN) - //{ - //v = mc->initVal(); - //if(v == MusECore::CTRL_VAL_UNKNOWN) - // v = 0; v = mn - 1; - //} else // Auto bias... v -= mc->bias(); @@ -512,10 +441,7 @@ volume = v; if(iLautst->value() != v) { - //printf("Arranger::midiTrackInfoHeartBeat setting volume\n"); - iLautst->blockSignals(true); - //iLautst->setRange(mn - 1, mc->maxVal()); iLautst->setValue(v); iLautst->blockSignals(false); } @@ -525,12 +451,7 @@ mn = mc->minVal(); v = mp->hwCtrlState(outChannel, MusECore::CTRL_PANPOT); if(v == MusECore::CTRL_VAL_UNKNOWN) - //{ - //v = mc->initVal(); - //if(v == MusECore::CTRL_VAL_UNKNOWN) - // v = 0; v = mn - 1; - //} else // Auto bias... v -= mc->bias(); @@ -539,31 +460,46 @@ pan = v; if(iPan->value() != v) { - //printf("Arranger::midiTrackInfoHeartBeat setting pan\n"); - iPan->blockSignals(true); - //iPan->setRange(mn - 1, mc->maxVal()); iPan->setValue(v); iPan->blockSignals(false); } } - // Does it include a midi controller value adjustment? Then handle it... - //if(flags & SC_MIDI_CONTROLLER) - // seek(); - - /* if(iTransp->value() != track->transposition) + { + iTransp->blockSignals(true); iTransp->setValue(track->transposition); + iTransp->blockSignals(false); + } + if(iAnschl->value() != track->velocity) + { + iAnschl->blockSignals(true); iAnschl->setValue(track->velocity); + iAnschl->blockSignals(false); + } + if(iVerz->value() != track->delay) + { + iVerz->blockSignals(true); iVerz->setValue(track->delay); + iVerz->blockSignals(false); + } + if(iLen->value() != track->len) + { + iLen->blockSignals(true); iLen->setValue(track->len); + iLen->blockSignals(false); + } + if(iKompr->value() != track->compression) + { + iKompr->blockSignals(true); iKompr->setValue(track->compression); - */ + iKompr->blockSignals(false); + } } break; @@ -583,8 +519,6 @@ void MidiTrackInfo::configChanged() { - //printf("MidiTrackInfo::configChanged\n"); - //if (MusEGlobal::config.canvasBgPixmap.isEmpty()) { // canvas->setBg(MusEGlobal::config.partCanvasBg); // canvas->setBg(QPixmap()); @@ -603,12 +537,21 @@ void MidiTrackInfo::songChanged(MusECore::SongChangedFlags_t type) { - // Is it simply a midi controller value adjustment? Forget it. - if(type == SC_MIDI_CONTROLLER) - return; - if(type == SC_SELECTION) + if(type & SC_TRACK_SELECTION) + { + MusECore::Track* t = MusEGlobal::song->selectedTrack(); + if(!t) + selected = 0; + else if(t->isMidiTrack()) + { + selected = t; + trackNameLabel->setObjectName(selected->cname()); + } + } + + if(type == SC_SELECTION || type == SC_PART_SELECTION || type == SC_TRACK_SELECTION) return; - if(!isVisible()) + if(!isVisible()) return; updateTrackInfo(type); } @@ -629,7 +572,6 @@ { QPalette pal; QColor c; - //pal.setColor(trackNameLabel->backgroundRole(), QColor(0, 160, 255)); // Med blue if(track->type() == MusECore::Track::DRUM) c = MusEGlobal::config.drumTrackLabelBg; else if (track->type() == MusECore::Track::MIDI) @@ -640,14 +582,9 @@ printf("THIS SHOULD NEVER HAPPEN: track is not a MIDI track in MidiTrackInfo::setLabelText()!\n"); QLinearGradient gradient(trackNameLabel->geometry().topLeft(), trackNameLabel->geometry().bottomLeft()); - //gradient.setColorAt(0, c.darker()); - //gradient.setColorAt(0, c); - //gradient.setColorAt(1, c.darker()); gradient.setColorAt(0, c); gradient.setColorAt(0.5, c.lighter()); gradient.setColorAt(1, c); - //palette.setBrush(QPalette::Button, gradient); - //palette.setBrush(QPalette::Window, gradient); pal.setBrush(trackNameLabel->backgroundRole(), gradient); trackNameLabel->setPalette(pal); } @@ -659,15 +596,30 @@ void MidiTrackInfo::setLabelFont() { - //if(!selected) - // return; - //MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; - // Use the new font #6 I created just for these labels (so far). // Set the label's font. trackNameLabel->setFont(MusEGlobal::config.fonts[6]); + // Dealing with a horizontally constrained label. Ignore vertical. Use a minimum readable point size. - MusECore::autoAdjustFontSize(trackNameLabel, trackNameLabel->text(), false, true, MusEGlobal::config.fonts[6].pointSize(), 5); +// MusECore::autoAdjustFontSize(trackNameLabel, trackNameLabel->text(), false, true, MusEGlobal::config.fonts[6].pointSize(), 5); +// const bool need_word_wrap = +// !MusECore::autoAdjustFontSize(trackNameLabel, +// trackNameLabel->text(), false, true, +// MusEGlobal::config.fonts[6].pointSize(), 6); + + QFont fnt(MusEGlobal::config.fonts[6]); +// const bool need_word_wrap = +// !MusECore::autoAdjustFontSize(trackNameLabel, +// trackNameLabel->text(), false, true, +// fnt, 6); + const bool need_word_wrap = + !MusECore::autoAdjustFontSize(trackNameLabel, + trackNameLabel->text(), fnt, false, true, + fnt.pointSize(), 6); + // TODO: Add an updateStyleSheet() method. + + if(trackNameLabel->wordWrap() != need_word_wrap) + trackNameLabel->setWordWrap(need_word_wrap); } //--------------------------------------------------------- @@ -682,19 +634,12 @@ MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; if (channel != track->outChannel()) { ++_blockHeartbeatCount; - // Changed by T356. - //track->setOutChannel(channel); - MusEGlobal::audio->msgIdle(true); - //audio->msgSetTrackOutChannel(track, channel); - track->setOutChanAndUpdate(channel); + MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; + changed |= track->setOutChanAndUpdate(channel, false); MusEGlobal::audio->msgIdle(false); - // may result in adding/removing mixer strip: - //MusEGlobal::song->update(-1); - //MusEGlobal::song->update(SC_MIDI_TRACK_PROP); - MusEGlobal::audio->msgUpdateSoloStates(); // p4.0.14 - //MusEGlobal::song->update(SC_MIDI_TRACK_PROP | SC_ROUTE); // - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); // + MusEGlobal::audio->msgUpdateSoloStates(); + MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); --_blockHeartbeatCount; } } @@ -714,17 +659,13 @@ if (port_num == track->outPort()) return; ++_blockHeartbeatCount; - // Changed by T356. - //track->setOutPort(port_num); + MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; MusEGlobal::audio->msgIdle(true); - //audio->msgSetTrackOutPort(track, port_num); - track->setOutPortAndUpdate(port_num); + changed |= track->setOutPortAndUpdate(port_num, false); MusEGlobal::audio->msgIdle(false); - //MusEGlobal::song->update(SC_MIDI_TRACK_PROP); - MusEGlobal::audio->msgUpdateSoloStates(); // p4.0.14 - //MusEGlobal::song->update(SC_MIDI_TRACK_PROP | SC_ROUTE); // - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); // + MusEGlobal::audio->msgUpdateSoloStates(); + MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); --_blockHeartbeatCount; } @@ -739,9 +680,8 @@ if(!selected->isMidiTrack()) return; - //RoutePopupMenu* pup = MusEGlobal::muse->getRoutingPopupMenu(); RoutePopupMenu* pup = new RoutePopupMenu(); - pup->exec(QCursor::pos(), selected, false); + pup->exec(QCursor::pos(), MusECore::Route(selected), false); delete pup; iRButton->setDown(false); } @@ -757,9 +697,8 @@ if(!selected->isMidiTrack()) return; - //RoutePopupMenu* pup = MusEGlobal::muse->getRoutingPopupMenu(); RoutePopupMenu* pup = new RoutePopupMenu(); - pup->exec(QCursor::pos(), selected, true); + pup->exec(QCursor::pos(), MusECore::Route(selected), true); delete pup; oRButton->setDown(false); } @@ -841,7 +780,7 @@ MusEGlobal::audio->msgPlayMidiEvent(&ev); MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(channel, program, track->isDrumTrack())); + iPatch->setText(instr->getPatchName(channel, program, track->isDrumTrack(), true)); // Include default. // updateTrackInfo(); --_blockHeartbeatCount; @@ -924,7 +863,7 @@ MusEGlobal::audio->msgPlayMidiEvent(&ev); MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(channel, program, track->isDrumTrack())); + iPatch->setText(instr->getPatchName(channel, program, track->isDrumTrack(), true)); // Include default. // updateTrackInfo(); --_blockHeartbeatCount; @@ -1007,7 +946,7 @@ MusEGlobal::audio->msgPlayMidiEvent(&ev); MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(channel, program, track->isDrumTrack())); + iPatch->setText(instr->getPatchName(channel, program, track->isDrumTrack(), true)); // Include default. --_blockHeartbeatCount; } @@ -1041,7 +980,6 @@ MusECore::ME_CONTROLLER, MusECore::CTRL_VOLUME, val); MusEGlobal::audio->msgPlayMidiEvent(&ev); } - MusEGlobal::song->update(SC_MIDI_CONTROLLER); } //--------------------------------------------------------- @@ -1054,7 +992,6 @@ return; MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; track->transposition = val; - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); } //--------------------------------------------------------- @@ -1067,7 +1004,6 @@ return; MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; track->velocity = val; - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); } //--------------------------------------------------------- @@ -1080,7 +1016,6 @@ return; MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; track->delay = val; - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); } //--------------------------------------------------------- @@ -1093,7 +1028,6 @@ return; MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; track->len = val; - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); } //--------------------------------------------------------- @@ -1106,7 +1040,6 @@ return; MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; track->compression = val; - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); } //--------------------------------------------------------- @@ -1136,31 +1069,61 @@ MusECore::ME_CONTROLLER, MusECore::CTRL_PANPOT, val); MusEGlobal::audio->msgPlayMidiEvent(&ev); } - MusEGlobal::song->update(SC_MIDI_CONTROLLER); } //--------------------------------------------------------- -// instrPopupActivated +// patchPopupActivated //--------------------------------------------------------- -void MidiTrackInfo::instrPopupActivated(QAction* act) +void MidiTrackInfo::patchPopupActivated(QAction* act) { - //printf("MidiTrackInfo::instrPopupActivated\n"); - if(act && selected) { - int rv = act->data().toInt(); - if(rv != -1) + MusECore::MidiTrack* track = static_cast(selected); + const int channel = track->outChannel(); + const int port = track->outPort(); + MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); + if(act->data().type() == QVariant::Int || act->data().type() == QVariant::UInt) { - MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; - int channel = track->outChannel(); - int port = track->outPort(); - ++_blockHeartbeatCount; - MusECore::MidiPlayEvent ev(0, port, channel, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, rv); - MusEGlobal::audio->msgPlayMidiEvent(&ev); - updateTrackInfo(-1); - --_blockHeartbeatCount; - } + bool ok; + int rv = act->data().toInt(&ok); + if(ok && rv != -1) + { + ++_blockHeartbeatCount; + // If the chosen patch's number is don't care (0xffffff), + // then by golly since we "don't care" let's just set it to '-/-/1'. + // 0xffffff cannot be a valid patch number... yet... + if(rv == MusECore::CTRL_PROGRAM_VAL_DONT_CARE) + rv = 0xffff00; + MusECore::MidiPlayEvent ev(0, port, channel, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, rv); + MusEGlobal::audio->msgPlayMidiEvent(&ev); + updateTrackInfo(-1); + --_blockHeartbeatCount; + } + } + else if(instr->isSynti() && act->data().canConvert()) + { + MusECore::SynthI *si = static_cast(instr); + MusECore::Synth *s = si->synth(); +#ifdef LV2_SUPPORT + //only for lv2 synths call applyPreset function. + if(s && s->synthType() == MusECore::Synth::LV2_SYNTH) + { + MusECore::LV2SynthIF *sif = static_cast(si->sif()); + //be pedantic about checks + if(sif) + { + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + if(mp) + { + if(mp->hwCtrlState(channel, MusECore::CTRL_PROGRAM) != MusECore::CTRL_VAL_UNKNOWN) + MusEGlobal::audio->msgSetHwCtrlState(mp, channel, MusECore::CTRL_PROGRAM, MusECore::CTRL_VAL_UNKNOWN); + sif->applyPreset(act->data().value()); + } + } + } +#endif + } } } @@ -1172,13 +1135,12 @@ { if(!selected) return; - MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; - int channel = track->outChannel(); - int port = track->outPort(); + MusECore::MidiTrack* track = static_cast(selected); + int port = track->outPort(); MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); - PopupMenu* pup = new PopupMenu(true); + PopupMenu* pup = new PopupMenu(false); - instr->populatePatchPopup(pup, channel, track->isDrumTrack()); + MusECore::MidiInstrument::populateInstrPopup(pup, instr, false); if(pup->actions().count() == 0) { @@ -1186,27 +1148,56 @@ return; } - connect(pup, SIGNAL(triggered(QAction*)), SLOT(instrPopupActivated(QAction*))); - //connect(pup, SIGNAL(hovered(QAction*)), SLOT(instrPopupActivated(QAction*))); - - QAction *act = pup->exec(iPatch->mapToGlobal(QPoint(10,5))); + QAction *act = pup->exec(instrPushButton->mapToGlobal(QPoint(10,5))); if(act) { - int rv = act->data().toInt(); - if(rv != -1) + QString s = act->text(); + for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { - ++_blockHeartbeatCount; - MusECore::MidiPlayEvent ev(0, port, channel, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, rv); - MusEGlobal::audio->msgPlayMidiEvent(&ev); - updateTrackInfo(-1); - --_blockHeartbeatCount; - } + if((*i)->iname() == s) + { + MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures + MusEGlobal::midiPorts[port].changeInstrument(*i); + MusEGlobal::audio->msgIdle(false); + // Make sure device initializations are sent if necessary. + MusEGlobal::audio->msgInitMidiDevices(false); // false = Don't force + MusEGlobal::song->update(SC_MIDI_INSTRUMENT); + break; + } + } } - delete pup; } //--------------------------------------------------------- +// patchPopup +//--------------------------------------------------------- + +void MidiTrackInfo::patchPopup() + { + if(!selected) + return; + MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; + int channel = track->outChannel(); + int port = track->outPort(); + MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); + PopupMenu* pup = new PopupMenu(true); + + instr->populatePatchPopup(pup, channel, track->isDrumTrack()); + + if(pup->actions().count() == 0) + { + delete pup; + return; + } + + connect(pup, SIGNAL(triggered(QAction*)), SLOT(patchPopupActivated(QAction*))); + + pup->exec(iPatch->mapToGlobal(QPoint(10,5))); + delete pup; + } + +//--------------------------------------------------------- // recEchoToggled //--------------------------------------------------------- @@ -1214,9 +1205,8 @@ { if(!selected) return; - MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; - track->setRecEcho(v); - MusEGlobal::song->update(SC_MIDI_TRACK_PROP); + selected->setRecMonitor(v); + MusEGlobal::song->update(SC_TRACK_REC_MONITOR); } //--------------------------------------------------------- @@ -1246,20 +1236,10 @@ if(lastv == MusECore::CTRL_VAL_UNKNOWN) { int kiv = mctl->initVal(); - //int kiv = lrint(_knob->value()); if(kiv == MusECore::CTRL_VAL_UNKNOWN) kiv = 0; - //else - //{ - //if(kiv < mctrl->minVal()) - // kiv = mctrl->minVal(); - //if(kiv > mctrl->maxVal()) - // kiv = mctrl->maxVal(); - //kiv += mctrl->bias(); - //} ++_blockHeartbeatCount; - //MusECore::MidiPlayEvent ev(0, port, chan, MusECore::ME_CONTROLLER, num, kiv); MusECore::MidiPlayEvent ev(0, port, chan, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, kiv); MusEGlobal::audio->msgPlayMidiEvent(&ev); --_blockHeartbeatCount; @@ -1302,11 +1282,7 @@ MusEGlobal::audio->msgSetHwCtrlState(mp, chan, MusECore::CTRL_PROGRAM, MusECore::CTRL_VAL_UNKNOWN); --_blockHeartbeatCount; } -// MusECore::MidiPlayEvent ev(0, port, chan, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, curv); -// MusEGlobal::audio->msgPlayMidiEvent(&ev); } - - MusEGlobal::song->update(SC_MIDI_CONTROLLER); } //--------------------------------------------------------- @@ -1336,17 +1312,8 @@ if(lastv == MusECore::CTRL_VAL_UNKNOWN) { int kiv = mctl->initVal(); - //int kiv = lrint(_knob->value()); if(kiv == MusECore::CTRL_VAL_UNKNOWN) kiv = 0xff0000; - //else - //{ - //if(kiv < mctrl->minVal()) - // kiv = mctrl->minVal(); - //if(kiv > mctrl->maxVal()) - // kiv = mctrl->maxVal(); - //kiv += mctrl->bias(); - //} //MusECore::MidiPlayEvent ev(0, port, chan, MusECore::ME_CONTROLLER, num, kiv); MusECore::MidiPlayEvent ev(0, port, chan, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, kiv); @@ -1401,8 +1368,6 @@ // MusECore::MidiPlayEvent ev(0, port, chan, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, curv); // MusEGlobal::audio->msgPlayMidiEvent(&ev); } - - MusEGlobal::song->update(SC_MIDI_CONTROLLER); } //--------------------------------------------------------- @@ -1432,19 +1397,9 @@ if(lastv == MusECore::CTRL_VAL_UNKNOWN) { int kiv = mctl->initVal(); - //int kiv = lrint(_knob->value()); if(kiv == MusECore::CTRL_VAL_UNKNOWN) kiv = 0xffff00; - //else - //{ - //if(kiv < mctrl->minVal()) - // kiv = mctrl->minVal(); - //if(kiv > mctrl->maxVal()) - // kiv = mctrl->maxVal(); - //kiv += mctrl->bias(); - //} - //MusECore::MidiPlayEvent ev(0, port, chan, MusECore::ME_CONTROLLER, num, kiv); MusECore::MidiPlayEvent ev(0, port, chan, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, kiv); MusEGlobal::audio->msgPlayMidiEvent(&ev); } @@ -1459,8 +1414,6 @@ if(mp->hwCtrlState(chan, MusECore::CTRL_PROGRAM) != MusECore::CTRL_VAL_UNKNOWN) MusEGlobal::audio->msgSetHwCtrlState(mp, chan, MusECore::CTRL_PROGRAM, MusECore::CTRL_VAL_UNKNOWN); } - - MusEGlobal::song->update(SC_MIDI_CONTROLLER); } //--------------------------------------------------------- @@ -1490,7 +1443,6 @@ if(lastv == MusECore::CTRL_VAL_UNKNOWN) { int kiv = mctl->initVal(); - //int kiv = lrint(_knob->value()); if(kiv == MusECore::CTRL_VAL_UNKNOWN) // Set volume to 78% of range, so that if range is 0 - 127, then value is 100. kiv = lround(double(mctl->maxVal() - mctl->minVal()) * 0.7874); @@ -1517,8 +1469,6 @@ if(mp->hwCtrlState(chan, MusECore::CTRL_VOLUME) != MusECore::CTRL_VAL_UNKNOWN) MusEGlobal::audio->msgSetHwCtrlState(mp, chan, MusECore::CTRL_VOLUME, MusECore::CTRL_VAL_UNKNOWN); } - - MusEGlobal::song->update(SC_MIDI_CONTROLLER); } //--------------------------------------------------------- @@ -1548,7 +1498,6 @@ if(lastv == MusECore::CTRL_VAL_UNKNOWN) { int kiv = mctl->initVal(); - //int kiv = lrint(_knob->value()); if(kiv == MusECore::CTRL_VAL_UNKNOWN) // Set volume to 50% of range, so that if range is 0 - 127, then value is 64. kiv = lround(double(mctl->maxVal() - mctl->minVal()) * 0.5); @@ -1575,8 +1524,6 @@ if(mp->hwCtrlState(chan, MusECore::CTRL_PANPOT) != MusECore::CTRL_VAL_UNKNOWN) MusEGlobal::audio->msgSetHwCtrlState(mp, chan, MusECore::CTRL_PANPOT, MusECore::CTRL_VAL_UNKNOWN); } - - MusEGlobal::song->update(SC_MIDI_CONTROLLER); } @@ -1586,59 +1533,53 @@ void MidiTrackInfo::updateTrackInfo(MusECore::SongChangedFlags_t flags) { - // Is it simply a midi controller value adjustment? Forget it. - if(flags == SC_MIDI_CONTROLLER) - return; - if(flags == SC_SELECTION) + if(flags == SC_SELECTION || flags == SC_PART_SELECTION || flags == SC_TRACK_SELECTION) return; if(!selected) return; - MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; + MusECore::MidiTrack* track = static_cast(selected); ++_blockHeartbeatCount; setLabelText(); setLabelFont(); - - if(flags & (SC_MIDI_TRACK_PROP)) + + const int outChannel = track->outChannel(); + const int outPort = track->outPort(); + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outPort]; + MusECore::MidiInstrument* instr = mp->instrument(); + + // If a port's instrument changed. Also if a track's output port changed, the instrument changed. + if(flags & (SC_MIDI_INSTRUMENT | SC_ROUTE)) { - iTransp->blockSignals(true); - iAnschl->blockSignals(true); - iVerz->blockSignals(true); - iLen->blockSignals(true); - iKompr->blockSignals(true); - iTransp->setValue(track->transposition); - iAnschl->setValue(track->velocity); - iVerz->setValue(track->delay); - iLen->setValue(track->len); - iKompr->setValue(track->compression); - iTransp->blockSignals(false); - iAnschl->blockSignals(false); - iVerz->blockSignals(false); - iLen->blockSignals(false); - iKompr->blockSignals(false); + if(instr) + { + instrPushButton->setText(instr->iname()); + if(instr->isSynti()) + instrPushButton->setEnabled(false); + else + instrPushButton->setEnabled(true); + } + else + instrPushButton->setText(tr("")); + } - int outChannel = track->outChannel(); - ///int inChannel = track->inChannelMask(); - int outPort = track->outPort(); - //int inPort = track->inPortMask(); - ///unsigned int inPort = track->inPortMask(); - + if (flags & SC_ROUTE) { + updateRouteButtons(); + } + if (flags & SC_CONFIG) { iOutput->blockSignals(true); - //iInput->clear(); iOutput->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. p4.0.41 + 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() & 1) && (i != outPort)) // Only writeable ports, or current one. p4.0.41 + if(!(md->rwFlags() & 1) && (i != outPort)) // Only writeable ports, or current one. continue; - QString name; - name.sprintf("%d:%s", i+1, MusEGlobal::midiPorts[i].portname().toLatin1().constData()); + QString name = QString("%1:%2").arg(i + 1).arg(MusEGlobal::midiPorts[i].portname()); iOutput->insertItem(item_idx, name, i); if (i == outPort) iOutput->setCurrentIndex(item_idx); @@ -1646,136 +1587,102 @@ } iOutput->blockSignals(false); - //iInput->setText(bitmap2String(inPort)); - ///iInput->setText(u32bitmap2String(inPort)); - - //iInputChannel->setText(bitmap2String(inChannel)); - - // Removed by Tim. p3.3.9 - //if (iName->text() != selected->name()) { - // iName->setText(selected->name()); - // iName->home(false); - // } - iOutputChannel->blockSignals(true); iOutputChannel->setValue(outChannel+1); iOutputChannel->blockSignals(false); - ///iInputChannel->setText(bitmap2String(inChannel)); - + } + + if(flags & (SC_TRACK_REC_MONITOR)) + { // Set record echo. - if(recEchoButton->isChecked() != track->recEcho()) + if(recEchoButton->isChecked() != track->recMonitor()) { recEchoButton->blockSignals(true); - recEchoButton->setChecked(track->recEcho()); + recEchoButton->setChecked(track->recMonitor()); recEchoButton->blockSignals(false); } - recEchoButton->setIcon(track->recEcho() ? QIcon(*midiThruOnIcon) : QIcon(*midiThruOffIcon)); - //recEchoButton->setIconSize(midiThruOnIcon->size()); + recEchoButton->setIcon(track->recMonitor() ? QIcon(*midiThruOnIcon) : QIcon(*midiThruOffIcon)); } - int outChannel = track->outChannel(); - int outPort = track->outPort(); - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outPort]; - int nprogram = mp->hwCtrlState(outChannel, MusECore::CTRL_PROGRAM); + int nprogram = mp->hwCtrlState(outChannel, MusECore::CTRL_PROGRAM); + if(nprogram == MusECore::CTRL_VAL_UNKNOWN) + { + iHBank->blockSignals(true); + iLBank->blockSignals(true); + iProgram->blockSignals(true); + iHBank->setValue(0); + iLBank->setValue(0); + iProgram->setValue(0); + iHBank->blockSignals(false); + iLBank->blockSignals(false); + iProgram->blockSignals(false); + + program = MusECore::CTRL_VAL_UNKNOWN; + nprogram = mp->lastValidHWCtrlState(outChannel, MusECore::CTRL_PROGRAM); if(nprogram == MusECore::CTRL_VAL_UNKNOWN) - { - iHBank->blockSignals(true); - iLBank->blockSignals(true); - iProgram->blockSignals(true); - iHBank->setValue(0); - iLBank->setValue(0); - iProgram->setValue(0); - iHBank->blockSignals(false); - iLBank->blockSignals(false); - iProgram->blockSignals(false); - - program = MusECore::CTRL_VAL_UNKNOWN; - nprogram = mp->lastValidHWCtrlState(outChannel, MusECore::CTRL_PROGRAM); - if(nprogram == MusECore::CTRL_VAL_UNKNOWN) - //iPatch->setText(QString("")); - iPatch->setText(tr("")); - else - { - MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(outChannel, nprogram, track->isDrumTrack())); - } - } + iPatch->setText(tr("")); else - //if (program != nprogram) { - program = nprogram; + if(instr) + iPatch->setText(instr->getPatchName(outChannel, nprogram, track->isDrumTrack(), true)); // Include default. + } + } + else + { + program = nprogram; - //int hb, lb, pr; - //if (program == MusECore::CTRL_VAL_UNKNOWN) { - // hb = lb = pr = 0; - // iPatch->setText("---"); - // } - //else - //{ - MusECore::MidiInstrument* instr = mp->instrument(); - iPatch->setText(instr->getPatchName(outChannel, program, track->isDrumTrack())); - - int hb = ((program >> 16) & 0xff) + 1; - if (hb == 0x100) - hb = 0; - int lb = ((program >> 8) & 0xff) + 1; - if (lb == 0x100) - lb = 0; - int pr = (program & 0xff) + 1; - if (pr == 0x100) - pr = 0; - //} - iHBank->blockSignals(true); - iLBank->blockSignals(true); - iProgram->blockSignals(true); - - iHBank->setValue(hb); - iLBank->setValue(lb); - iProgram->setValue(pr); - - iHBank->blockSignals(false); - iLBank->blockSignals(false); - iProgram->blockSignals(false); - } - - MusECore::MidiController* mc = mp->midiController(MusECore::CTRL_VOLUME); - int mn = mc->minVal(); - int v = mp->hwCtrlState(outChannel, MusECore::CTRL_VOLUME); - volume = v; - if(v == MusECore::CTRL_VAL_UNKNOWN) - //{ - //v = mc->initVal(); - //if(v == MusECore::CTRL_VAL_UNKNOWN) - // v = 0; - v = mn - 1; - //} - else - // Auto bias... - v -= mc->bias(); - iLautst->blockSignals(true); - iLautst->setRange(mn - 1, mc->maxVal()); - iLautst->setValue(v); - iLautst->blockSignals(false); - - mc = mp->midiController(MusECore::CTRL_PANPOT); - mn = mc->minVal(); - v = mp->hwCtrlState(outChannel, MusECore::CTRL_PANPOT); - pan = v; - if(v == MusECore::CTRL_VAL_UNKNOWN) - //{ - //v = mc->initVal(); - //if(v == MusECore::CTRL_VAL_UNKNOWN) - // v = 0; - v = mn - 1; - //} - else - // Auto bias... - v -= mc->bias(); - iPan->blockSignals(true); - iPan->setRange(mn - 1, mc->maxVal()); - iPan->setValue(v); - iPan->blockSignals(false); - //} + iPatch->setText(instr->getPatchName(outChannel, program, track->isDrumTrack(), true)); // Include default. + + int hb = ((program >> 16) & 0xff) + 1; + if (hb == 0x100) + hb = 0; + int lb = ((program >> 8) & 0xff) + 1; + if (lb == 0x100) + lb = 0; + int pr = (program & 0xff) + 1; + if (pr == 0x100) + pr = 0; + + iHBank->blockSignals(true); + iLBank->blockSignals(true); + iProgram->blockSignals(true); + + iHBank->setValue(hb); + iLBank->setValue(lb); + iProgram->setValue(pr); + + iHBank->blockSignals(false); + iLBank->blockSignals(false); + iProgram->blockSignals(false); + } + + MusECore::MidiController* mc = mp->midiController(MusECore::CTRL_VOLUME); + int mn = mc->minVal(); + int v = mp->hwCtrlState(outChannel, MusECore::CTRL_VOLUME); + volume = v; + if(v == MusECore::CTRL_VAL_UNKNOWN) + v = mn - 1; + else + // Auto bias... + v -= mc->bias(); + iLautst->blockSignals(true); + iLautst->setRange(mn - 1, mc->maxVal()); + iLautst->setValue(v); + iLautst->blockSignals(false); + + mc = mp->midiController(MusECore::CTRL_PANPOT); + mn = mc->minVal(); + v = mp->hwCtrlState(outChannel, MusECore::CTRL_PANPOT); + pan = v; + if(v == MusECore::CTRL_VAL_UNKNOWN) + v = mn - 1; + else + // Auto bias... + v -= mc->bias(); + iPan->blockSignals(true); + iPan->setRange(mn - 1, mc->maxVal()); + iPan->setValue(v); + iPan->blockSignals(false); --_blockHeartbeatCount; } @@ -1796,9 +1703,8 @@ if(program == MusECore::CTRL_VAL_UNKNOWN || program == 0xffffff) return; - unsigned tick = MusEGlobal::song->cpos(); MusECore::Event a(MusECore::Controller); - a.setTick(tick); + a.setTick(0); a.setA(MusECore::CTRL_PROGRAM); a.setB(program); @@ -1867,13 +1773,12 @@ int portno = track->outPort(); int channel = track->outChannel(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[portno]; - unsigned tick = MusEGlobal::song->cpos(); int program = port->hwCtrlState(channel, MusECore::CTRL_PROGRAM); if(program != MusECore::CTRL_VAL_UNKNOWN && program != 0xffffff) { MusECore::Event a(MusECore::Controller); - a.setTick(tick); + a.setTick(0); a.setA(MusECore::CTRL_PROGRAM); a.setB(program); MusEGlobal::song->recordEvent(track, a); @@ -1882,7 +1787,7 @@ if(volume != MusECore::CTRL_VAL_UNKNOWN) { MusECore::Event a(MusECore::Controller); - a.setTick(tick); + a.setTick(0); a.setA(MusECore::CTRL_VOLUME); a.setB(volume); MusEGlobal::song->recordEvent(track, a); @@ -1891,7 +1796,7 @@ if(pan != MusECore::CTRL_VAL_UNKNOWN) { MusECore::Event a(MusECore::Controller); - a.setTick(tick); + a.setTick(0); a.setA(MusECore::CTRL_PANPOT); a.setB(pan); MusEGlobal::song->recordEvent(track, a); @@ -1900,10 +1805,43 @@ void MidiTrackInfo::resizeEvent(QResizeEvent* ev) { - //printf("MidiTrackInfo::resizeEvent\n"); QWidget::resizeEvent(ev); setLabelText(); setLabelFont(); } +void MidiTrackInfo::updateRouteButtons() +{ + if(!selected) + return; + + if (iRButton) + { + if (selected->noInRoute()) + { + iRButton->setStyleSheet("background-color:red;"); + iRButton->setToolTip(MusEGlobal::noInputRoutingToolTipWarn); + } + else + { + iRButton->setStyleSheet(""); + iRButton->setToolTip(MusEGlobal::inputRoutingToolTipBase); + } + } + + if (oRButton) + { + if (selected->noOutRoute()) + { + oRButton->setStyleSheet("background-color:red;"); + oRButton->setToolTip(MusEGlobal::noOutputRoutingToolTipWarn); + } + else + { + oRButton->setStyleSheet(""); + oRButton->setToolTip(MusEGlobal::outputRoutingToolTipBase); + } + } +} + } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/mtrackinfo.h muse-3.0.2+ds1/muse/widgets/mtrackinfo.h --- muse-2.1.2/muse/widgets/mtrackinfo.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/mtrackinfo.h 2017-12-04 21:01:19.000000000 +0000 @@ -2,7 +2,7 @@ // MusE // Linux Music Editor // (C) Copyright 2010 Werner Schweer and others (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -39,6 +39,8 @@ class MidiTrackInfo : public QWidget, public Ui::MidiTrackInfoBase { Q_OBJECT + + private: MusECore::Track* selected; bool _midiDetect; int program, pan, volume; @@ -47,6 +49,7 @@ protected: virtual void resizeEvent(QResizeEvent*); + virtual void updateRouteButtons(); private slots: void iOutputChannelChanged(int); @@ -66,6 +69,7 @@ void iKomprChanged(int); void iPanChanged(int); void iPanDoubleClicked(); + void patchPopup(); void instrPopup(); void recordClicked(); void progRecClicked(); @@ -74,7 +78,7 @@ void recEchoToggled(bool); void inRoutesPressed(); void outRoutesPressed(); - void instrPopupActivated(QAction*); + void patchPopupActivated(QAction*); protected slots: virtual void heartBeat(); diff -Nru muse-2.1.2/muse/widgets/mtscale.cpp muse-3.0.2+ds1/muse/widgets/mtscale.cpp --- muse-2.1.2/muse/widgets/mtscale.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/mtscale.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -275,7 +275,7 @@ //if (m->second.current()) // p.fillRect(tr, white); - QRect wr = r.intersect(tr); + QRect wr = r.intersected(tr); //if (r.intersects(tr)) if(!wr.isEmpty()) { diff -Nru muse-2.1.2/muse/widgets/mtscale_flo.cpp muse-3.0.2+ds1/muse/widgets/mtscale_flo.cpp --- muse-2.1.2/muse/widgets/mtscale_flo.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/mtscale_flo.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -220,7 +220,7 @@ QRect tr(xp, 0, xe-xp, 13); - QRect wr = r.intersect(tr); + QRect wr = r.intersected(tr); if(!wr.isEmpty()) { if (m->second.current()) diff -Nru muse-2.1.2/muse/widgets/musewidgetsplug.cpp muse-3.0.2+ds1/muse/widgets/musewidgetsplug.cpp --- muse-2.1.2/muse/widgets/musewidgetsplug.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/musewidgetsplug.cpp 2017-12-17 21:07:39.000000000 +0000 @@ -27,6 +27,7 @@ #include // p4.0.2 #include // #include +#include #include "poslabel.h" #include "pitchedit.h" @@ -54,6 +55,11 @@ }; MusEGlobal::GlobalConfigValues config = { + QStringList(), // pluginLadspaPathList + QStringList(), // pluginDssiPathList + QStringList(), // pluginVstPathList + QStringList(), // pluginLinuxVstPathList + QStringList(), // pluginLv2PathList 170, // globalAlphaBlend { QColor(0xff, 0xff, 0xff), // palette @@ -128,6 +134,7 @@ QColor(0xff, 0xff, 0xff), // trackBg; QColor(0x80, 0xff, 0x80), // selected track Bg; QColor(0x00, 0x00, 0x00), // selected track Fg; + Qt::gray, // trackSectionDividerColor; QColor(0, 160, 255), // midiTrackLabelBg; // Med blue QColor(150, 177, 189), // drumTrackLabelBg; // Pale pastel blue @@ -149,15 +156,62 @@ QColor(220, 220, 220), // auxTrackBg; QColor(220, 220, 220), // synthTrackBg; - QColor(98, 124, 168), // part canvas bg - QColor(255, 170, 0), // ctrlGraphFg; Medium orange - QColor(98, 124, 168), // mixerBg; + QColor(98, 124, 168), // part canvas bg + QColor(255, 170, 0), // ctrlGraphFg; Medium orange + QColor(0, 0, 0), // mixerBg; + QColor(0xe0, 0xe0, 0xe0), // Ruler background + QColor(0, 0, 0), // Ruler text + QColor(255, 255, 255), // Midi editor canvas + QColor(255, 255, 255), // midiControllerViewBg + QColor(255, 255, 255), // drumListBg + QColor(255, 255, 255), // rulerCurrent + Qt::gray, // midiCanvasBeatColor + Qt::black, // midiCanvasBarColor + Qt::lightGray, // waveNonselectedPart + Qt::darkGray, // wavePeakColor + Qt::black, // waveRmsColor + Qt::lightGray, // wavePeakColorSelected + Qt::white, // waveRmsColorSelected + + Qt::darkGray, // partWaveColorPeak + QColor(20,20,20), // partWaveColorRms + QColor(54,54,54), // partMidiDarkEventColor + QColor(200,200,200), // partMidiLightEventColor + + QColor(0,181,241 ), // sliderBarDefaultColor + QColor(228,203,36 ), // sliderDefaultColor + QColor(78,172,35 ), // panSliderColor + QColor(209,86,86 ), // gainSliderColor + QColor(190,190,39 ), // auxSliderColor + QColor(154,135,124), // audioVolumeSliderColor + QColor(153,156,124), // midiVolumeSliderColor + QColor(37,121,255 ), // audioControllerSliderDefaultColor + QColor(220,77,255 ), // audioPropertySliderDefaultColor + QColor(37,121,255 ), // midiControllerSliderDefaultColor + QColor(220,77,255 ), // midiPropertySliderDefaultColor + QColor(100,255,255), // midiPatchReadoutColor + QColor(0,221,255 ), // audioMeterPrimaryColor + QColor(0,221,255 ), // midiMeterPrimaryColor + QColor(208,145,49 ), // rackItemBackgroundColor + + MusEGlobal::WaveOutLine, // waveDrawing + + false, // fixFrozenMDISubWindows Turn on a fix for frozen MDIs in Breeze/Oxygen themes. + + // maxAliasedPointSize At what point size to switch from aliased text to non-aliased text. + // Zero means always use anti-aliasing. For certain widgets that use it. May be more later. + 8, + + false, // enableAlsaMidiDriver Whether to enable the ALSA midi driver 384, // division; 1024, // rtcTicks + 0, // curMidiSyncInPort The currently selected midi sync input port. true, // midiSendInit Send instrument initialization sequences true, // warnInitPending Warn instrument initialization sequences pending false, // midiSendCtlDefaults Send instrument controller defaults at position 0 if none in song + false, // midiSendNullParameters Send null parameters after each (N)RPN event + false, // midiOptimizeControllers Don't send redundant H/L parameters or H/L values true, // warnIfBadTiming Warn if timer res not good false, // velocityPerNote Whether to show per-note or all velocities -60, // int minMeter; @@ -172,14 +226,17 @@ 1, // smf export file format false, // midi export file 2 byte timesigs instead of 4 true, // optimize midi export file note offs + true, // expRunningStatus; Save space by using running status true, // Split imported tracks into multiple parts. true, // importMidiNewStyleDrum true, // importDevNameMetas Import Prefer Device Name metas over port number metas if both exist. true, // importInstrNameMetas Import Prefer Instrument Name metas over Mode sysexes if both exist. - MusEGlobal::EXPORT_PORTS_DEVICES_ALL, // exportPortsDevices Export port number metas and/or device name metas. + MusEGlobal::PORT_NUM_META | MusEGlobal::DEVICE_NAME_META, // exportPortsDevices. Or'd ExportPortsDevices_t flags. Export port number metas and/or device name metas. true, // exportPortDeviceSMF0 Export a port and/or device meta even for SMF0. - MusEGlobal::EXPORT_MODE_INSTR_ALL, // exportModeInstr Export mode sysexes and/or instrument name metas. + MusEGlobal::MODE_SYSEX | MusEGlobal::INSTRUMENT_NAME_META, // exportModeInstr. Or'd ExportModeInstr_t flags. Export mode sysexes and/or instrument name metas. QString("GM"), // importMidiDefaultInstr Default to this instrument not Generic, if no match found + true, // exportDrumMapOverrides Apply Port, Channel, and ANote drum map overrides to export + true, // exportChannelOverridesToNewTrack Drum map Channel overrides go to a separate track 1, // startMode QString(""), // start song path false, // startSongLoadConfig @@ -187,36 +244,41 @@ QRect(0, 0, 400, 300), // GeometryMain; QRect(0, 0, 200, 100), // GeometryTransport; QRect(0, 0, 600, 200), // GeometryBigTime; - //QRect(0, 0, 300, 500), // GeometryMixer; // Obsolete { QString("Mixer A"), + QStringList(), QRect(0, 0, 300, 500), // Mixer1 true, true, true, true, - true, true, true, true, true + true, true, true, true, true, + MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW, + QList() }, { QString("Mixer B"), + QStringList(), QRect(200, 200, 300, 500), // Mixer2 true, true, true, true, - true, true, true, true, true - }, + true, true, true, true, true, + MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW, + QList() + }, true, // TransportVisible; false, // BigTimeVisible; false, // mixer1Visible; false, // mixer2Visible; - false, // markerVisible; // This line was missing 2007-01-08 (willyfoobar) + false, // markerVisible; true, // arrangerVisible; true, // showSplashScreen 1, // canvasShowPartType 1 - names, 2 events 5, // canvasShowPartEvent - false, // canvasShowGrid; + true, // canvasShowGrid; QString(""), // canvasBgPixmap; QStringList(), // canvasCustomBgList QString(""), // default styleSheetFile - For built-in set to ":/style.qss" QString(""), // style - QString(""), // externalWavEditor //this line was missing 2007-01-08 (willyfoobar) + QString("sweep"), // externalWavEditor false, // useOldStyleStopShortCut - true, // moveArmedCheckBox + false, // moveArmedCheckBox true, // useDenormalBias false, // useOutputLimiter true, // showDidYouKnow @@ -236,7 +298,22 @@ MusEGlobal::PREFER_NEW, // drumTrackPreference true, // smartFocus 20, // trackHeight - true // borderlessMouse + true, // borderlessMouse + false, // autoSave + false, // scrollableSubMenus + true, // liveWaveUpdate + true, // warnOnFileVersions Warn if file version different than current + MusEGlobal::CONF_LV2_UI_USE_FIRST, //lv2UiBehavior + true, // preferKnobsVsSliders Whether to prefer the use of knobs over sliders, esp in mixer. + true, // showControlValues Whether to show the value along with label in small controls, esp in mixer. + true, // monitorOnRecord Whether to automatically monitor on record arm. + QString("klick1.wav"), // measSample + QString("klick2.wav"), // beatSample + QString("klick3.wav"), // accent1Sample + QString("klick4.wav"), // accent2Sample + MusEGlobal::RoutePreferCanonicalName, // preferredRouteNameOrAlias + true, // routerExpandVertically + 2 // routerGroupingChannels }; //--------------------------------------------------------- @@ -248,8 +325,7 @@ if (v < 0 || v > 127) return QString("----"); int octave = (v / 12) - 2; - QString o; - o.sprintf("%d", octave); + QString o = QString::number(octave); int i = v % 12; QString s(octave < 0 ? valu[i] : vall[i]); if (hIsB) { diff -Nru muse-2.1.2/muse/widgets/noteinfo.cpp muse-3.0.2+ds1/muse/widgets/noteinfo.cpp --- muse-2.1.2/muse/widgets/noteinfo.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/noteinfo.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -92,7 +92,9 @@ addWidget(label); selVelOn = new SpinBox(); selVelOn->setFocusPolicy(Qt::StrongFocus); - selVelOn->setRange(0, 127); + // REMOVE Tim. Noteoff. Changed. +// selVelOn->setRange(0, 127); + selVelOn->setRange(1, 127); selVelOn->setSingleStep(1); addWidget(selVelOn); @@ -156,7 +158,9 @@ } else { selLen->setRange(0, 100000); - selVelOn->setRange(0, 127); + // REMOVE Tim. Noteoff. Changed. +// selVelOn->setRange(0, 127); + selVelOn->setRange(1, 127); selVelOff->setRange(0, 127); } blockSignals(false); @@ -257,6 +261,10 @@ selLen->setValue(val); break; case VAL_VELON: + // REMOVE Tim. Noteoff. Added. + if(!deltaMode && val == 0) + fprintf(stderr, "NoteInfo::setValue: Warning: Value is zero note on velocity!\n"); + selVelOn->setValue(val); break; case VAL_VELOFF: @@ -284,7 +292,13 @@ if (selPitch->value() != val3) selPitch->setValue(val3); if (selVelOn->value() != val4) - selVelOn->setValue(val4); + { + // REMOVE Tim. Noteoff. Added. + if(!deltaMode && val4 == 0) + fprintf(stderr, "NoteInfo::setValues: Warning: Zero note on velocity!\n"); + + selVelOn->setValue(val4); + } if (selVelOff->value() != val5) selVelOff->setValue(val5); blockSignals(false); diff -Nru muse-2.1.2/muse/widgets/pitchlabel.cpp muse-3.0.2+ds1/muse/widgets/pitchlabel.cpp --- muse-2.1.2/muse/widgets/pitchlabel.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/pitchlabel.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -86,7 +86,7 @@ if (_pitchMode) s = MusECore::pitch2string(_value); else - s.sprintf("%d", _value); + s = QString::number(_value); setText(s); } diff -Nru muse-2.1.2/muse/widgets/pixmap_button.cpp muse-3.0.2+ds1/muse/widgets/pixmap_button.cpp --- muse-2.1.2/muse/widgets/pixmap_button.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/pixmap_button.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,10 @@ namespace MusEGui { +//------------------------------------------ +// PixmapButton +//------------------------------------------ + PixmapButton::PixmapButton(QWidget* parent) : QWidget(parent) { @@ -155,7 +160,7 @@ if(_checkable) emit toggled(_checked); - //e->setAccepted(true); // This makes menu not close when mouse is released. May be desireable with many small buttons... + //e->setAccepted(true); // This makes menu not close when mouse is released. May be desirable with many small buttons... QWidget::mousePressEvent(e); // Hm, need this so menus can close. } @@ -163,7 +168,7 @@ { emit clicked(_checked); - //e->setAccepted(true); // This makes menu not close when mouse is released. May be desireable with many small buttons... + //e->setAccepted(true); // This makes menu not close when mouse is released. May be desirable with many small buttons... QWidget::mouseReleaseEvent(e); // Hm, need this so menus can close. } @@ -172,4 +177,279 @@ e->accept(); } + +//------------------------------------------ +// IconButton +//------------------------------------------ + +IconButton::IconButton(QWidget* parent, const char* name) + : QWidget(parent) +{ + setObjectName(name); + _blinkPhase = false; + _iconSetB = false; + _iconSize = QSize(16, 16); + _onIcon = 0; + _offIcon = 0; + _onIconB = 0; + _offIconB = 0; + _margin = 0; + _checked = false; + _checkable = false; +} + +IconButton::IconButton(QIcon* on_icon, QIcon* off_icon, QIcon* on_iconB, QIcon* off_iconB, + bool hasFixedIconSize, bool drawFlat, + const QString& text, int margin, QWidget* parent, const char* name) + : QWidget(parent), + _onIcon(on_icon), _offIcon(off_icon), _onIconB(on_iconB), _offIconB(off_iconB), + _hasFixedIconSize(hasFixedIconSize), _drawFlat(drawFlat), _text(text), _margin(margin) +{ + setObjectName(name); + _blinkPhase = false; + _iconSetB = false; + _checked = false; + _checkable = false; + _iconSize = QSize(16, 16); +} + +QSize IconButton::sizeHint() const +{ + // TODO Ask style for margins. + const QSize isz = iconSize(); + const int fmh = fontMetrics().lineSpacing() + 5; + + const int iw = isz.width() + 2; + const int ih = isz.height() + 2; + + const int h = (_hasFixedIconSize && ih > fmh) ? ih : fmh; + const int w = (_hasFixedIconSize && iw > h) ? iw : h + 2; + + return QSize(w, h); +} + +QSize IconButton::minimumSizeHint () const +{ + return QSize(10, 10); +} + +void IconButton::setText(QString txt) +{ + _text = txt; + updateGeometry(); +} + +void IconButton::setIconSize(const QSize sz) +{ + _iconSize = sz; + updateGeometry(); +} + +void IconButton::setMargin(int v) +{ + _margin = v; + update(); +} + +void IconButton::setOffIcon(QIcon* pm) +{ + _offIcon = pm; + update(); +} + +void IconButton::setOnIcon(QIcon* pm) +{ + _onIcon = pm; + update(); +} + +void IconButton::setOffIconB(QIcon* pm) +{ + _offIconB = pm; + update(); +} + +void IconButton::setOnIconB(QIcon* pm) +{ + _onIconB = pm; + update(); +} + +void IconButton::setIconSetB(bool v) +{ + _iconSetB = v; + update(); +} + +void IconButton::setCheckable(bool v) +{ + _checkable = v; + if(!_checkable) + _checked = false; + update(); +} + +void IconButton::setChecked(bool v) +{ + if(!_checkable) + return; + if(_checked == v) + return; + _checked = v; + update(); + emit toggled(_checked); +} + +void IconButton::setDown(bool v) +{ + if(!_checkable) + return; + if(_checked == v) + return; + _checked = v; + update(); +} + +void IconButton::paintEvent(QPaintEvent* ev) +{ + ev->accept(); + +// if(!_drawFlat) +// QToolButton::paintEvent(ev); + + QIcon::Mode mode; + if(isEnabled()) + mode = hasFocus() ? QIcon::Selected : QIcon::Normal; + else + mode = QIcon::Disabled; + QIcon::State state = (isChecked() && (!_blinkPhase || !isEnabled())) ? QIcon::On : QIcon::Off; + + QIcon* ico = 0; + QPainter p(this); + if(!_text.isEmpty()) + //p.drawText(w2 - mw2, h2 - mh2, mw, mh, *pm); + p.drawText(_margin, height() - _margin, _text); + else + { + if(_iconSetB) + ico = _checked ? _onIconB : _offIconB; + else + ico = _checked ? _onIcon : _offIcon; + + if(ico) + ico->paint(&p, rect(), Qt::AlignCenter, mode, state); + } + +// TODO Bah! Just want a mouse-over rectangle for flat mode but some styles do this or that but not the other thing. +// if(const QStyle* st = style()) +// { +// st = st->proxy(); +// // QStyleOptionToolButton o; +// // initStyleOption(&o); +// // o.rect = rect(); +// // //o.state |= QStyle::State_MouseOver; +// // o.state = QStyle::State_Active | +// // QStyle::State_Enabled | +// // QStyle::State_AutoRaise | // This is required to get rid of the panel. +// // QStyle::State_MouseOver; +// // st->drawPrimitive(QStyle::PE_PanelButtonTool, &o, &p); +// +// // QStyleOptionFrame o; +// // //initStyleOption(&o); +// // o.rect = rect(); +// // o.features = QStyleOptionFrame::Rounded; +// // o.frameShape = QFrame::Box; +// // o.lineWidth = 2; +// // o.midLineWidth = 4; +// // o.state |= QStyle::State_MouseOver; +// // st->drawPrimitive(QStyle::PE_Frame, &o, &p); +// +// +// QStyleOptionFocusRect o; +// //o.QStyleOption::operator=(option); +// //o.rect = st->subElementRect(QStyle::SE_ItemViewItemFocusRect, &option); +// o.rect = rect(); +// o.state |= QStyle::State_KeyboardFocusChange; +// o.state |= QStyle::State_Item | +// QStyle::State_Active | +// QStyle::State_Enabled | +// QStyle::State_HasFocus | +// +// //QStyle::State_Raised | +// QStyle::State_Sunken | +// +// QStyle::State_Off | +// //QStyle::State_On | +// +// QStyle::State_Selected | +// +// //QStyle::State_AutoRaise | // This is required to get rid of the panel. +// +// QStyle::State_MouseOver; +// +// // QPalette::ColorGroup cg = +// // (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; +// // o.backgroundColor = option.palette.color(cg, +// // (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); +// st->drawPrimitive(QStyle::PE_FrameFocusRect, &o, &p); +// +// } +} + +void IconButton::mousePressEvent(QMouseEvent* e) +{ + e->accept(); + if(_checkable) + _checked = !_checked; + update(); + + emit pressed(); + if(_checkable) + emit toggled(_checked); + +// //e->setAccepted(true); // This makes menu not close when mouse is released. May be desirable with many small buttons... +// QWidget::mousePressEvent(e); // Hm, need this so menus can close. +} + +void IconButton::mouseReleaseEvent(QMouseEvent* e) +{ + e->accept(); + emit clicked(_checked); + +// //e->setAccepted(true); // This makes menu not close when mouse is released. May be desirable with many small buttons... +// QWidget::mouseReleaseEvent(e); // Hm, need this so menus can close. +} + +void IconButton::mouseMoveEvent(QMouseEvent* ev) +{ + ev->accept(); +} + +void IconButton::contextMenuEvent(QContextMenuEvent * e) +{ + e->accept(); +} + +void IconButton::setHasFixedIconSize(bool v) +{ + _hasFixedIconSize = v; + updateGeometry(); +} + +void IconButton::setDrawFlat(bool v) +{ + _drawFlat = v; + update(); +} + +void IconButton::setBlinkPhase(bool v) +{ + if(_blinkPhase == v) + return; + _blinkPhase = v; + if(isEnabled()) + update(); +} + + } // MusEGui \ No newline at end of file diff -Nru muse-2.1.2/muse/widgets/pixmap_button.h muse-3.0.2+ds1/muse/widgets/pixmap_button.h --- muse-2.1.2/muse/widgets/pixmap_button.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/pixmap_button.h 2017-12-04 21:01:19.000000000 +0000 @@ -28,6 +28,7 @@ class QMouseEvent; class QPaintEvent; class QPixmap; +class QIcon; class QString; namespace MusEGui { @@ -82,7 +83,94 @@ virtual QPixmap* onPixmap() const { return _onPixmap; } virtual void setOnPixmap(QPixmap*); }; - + + +//--------------------------------------------------------- +// IconButton +//--------------------------------------------------------- + +class IconButton : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable) + Q_PROPERTY(bool checked READ isChecked WRITE setChecked) + //Q_PROPERTY(QPixmap* offPixmap READ offPixmap WRITE setOffPixmap) + //Q_PROPERTY(QPixmap* onPixmap READ onPixmap WRITE setOnPixmap) + Q_PROPERTY(int margin READ margin WRITE setMargin) + + private: + QIcon* _onIcon; + QIcon* _offIcon; + QIcon* _onIconB; + QIcon* _offIconB; + bool _hasFixedIconSize; + bool _drawFlat; + QString _text; + int _margin; + + QSize _iconSize; + bool _checkable; + bool _checked; + bool _iconSetB; + bool _blinkPhase; + + protected: + virtual void paintEvent(QPaintEvent* ); + virtual void mousePressEvent(QMouseEvent* ); + virtual void mouseReleaseEvent(QMouseEvent* ); + virtual void mouseMoveEvent(QMouseEvent* ); + virtual void contextMenuEvent(QContextMenuEvent*); + + signals: + void clicked(bool checked = false); + void toggled(bool checked = false); + void pressed(); + + public: + IconButton(QWidget* parent = 0, const char* name = 0); + IconButton(QIcon* on_icon, QIcon* off_icon, QIcon* on_iconB, QIcon* off_iconB, + bool hasFixedIconSize = true, bool drawFlat = false, + const QString& text = QString(), int margin = 0, QWidget* parent = 0, const char* name = 0); + virtual QSize minimumSizeHint () const; + virtual bool margin() const { return _margin; } + virtual void setMargin(int v); + virtual bool isChecked() const { return _checked; } + virtual void setChecked(bool); + virtual bool isDown() const { return _checked; } + virtual void setDown(bool); + virtual bool isCheckable() const { return _checkable; } + virtual void setCheckable(bool); + virtual QIcon* offIcon() const { return _offIcon; } + virtual void setOffIcon(QIcon*); + virtual QIcon* onIcon() const { return _onIcon; } + virtual void setOnIcon(QIcon*); + virtual QIcon* offIconB() const { return _offIconB; } + virtual void setOffIconB(QIcon*); + virtual QIcon* onIconB() const { return _onIconB; } + virtual void setOnIconB(QIcon*); + virtual bool iconSetB() const { return _iconSetB; } + virtual void setIconSetB(bool v); + + QString text() const { return _text; } + void setText(QString txt); + + bool hasFixedIconSize() const { return _hasFixedIconSize; } + void setHasFixedIconSize(bool v); + + QSize iconSize() const { return _iconSize; } + void setIconSize(const QSize sz); + + bool drawFlat() const { return _drawFlat; } + void setDrawFlat(bool v); + + // If _hasFixedIconSize is true, this relies on iconSize(). Be sure to set iconSize to the desired value. + virtual QSize sizeHint() const; + + bool blinkPhase() const { return _blinkPhase; } + void setBlinkPhase(bool v); +}; + } // MusEGui #endif // __PIXMAP_BUTTON_H__ \ No newline at end of file diff -Nru muse-2.1.2/muse/widgets/plugindialogbase.ui muse-3.0.2+ds1/muse/widgets/plugindialogbase.ui --- muse-2.1.2/muse/widgets/plugindialogbase.ui 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/plugindialogbase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,280 @@ + + + PluginDialogBase + + + + 0 + 0 + 636 + 411 + + + + Dialog + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + 0 + + + + + + + + + 0 + 0 + + + + + 1 + + + + + + + + + 16777215 + 100 + + + + Ports: + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + 0 + + + + + + 8 + + + + Mono + + + portFilterGroup + + + + + + + + 8 + + + + Mono + Stereo + + + portFilterGroup + + + + + + + + + 1 + + + + + + 8 + + + + Stereo + + + portFilterGroup + + + + + + + + 8 + + + + All + + + portFilterGroup + + + + + + + + + + + + + + + + + 16777215 + 14 + + + + Plugin type: + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 10 + + + + + + + + + + + + + 16777215 + 14 + + + + Filter: + + + + + + + true + + + true + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 10 + + + + + + + + + + + + &OK + + + + + + + &Cancel + + + + + + + + + + + + QTabBar + QWidget +
    qtabbar.h
    + 1 +
    +
    + + + + + +
    diff -Nru muse-2.1.2/muse/widgets/plugindialog.cpp muse-3.0.2+ds1/muse/widgets/plugindialog.cpp --- muse-2.1.2/muse/widgets/plugindialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/plugindialog.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,498 @@ +#include +#include +#include + +#include "popupmenu.h" +#include "menutitleitem.h" + +#include "plugindialog.h" +#include "ui_plugindialogbase.h" +#include "plugin.h" + + +namespace MusEGui { + +int PluginDialog::selectedPlugType = SEL_TYPE_ALL; +int PluginDialog::selectedPlugPortType = SEL_PORT_ALL; +int PluginDialog::selectedGroup = 0; +QStringList PluginDialog::sortItems = QStringList(); +QRect PluginDialog::geometrySave = QRect(); +QByteArray PluginDialog::listSave = QByteArray(); + +//--------------------------------------------------------- +// PluginDialog +// select Plugin dialog +//--------------------------------------------------------- + +PluginDialog::PluginDialog(QWidget* parent) + : QDialog(parent) +{ + ui.setupUi(this); + + group_info=NULL; + setWindowTitle(tr("MusE: select plugin")); + + if(!geometrySave.isNull()) + setGeometry(geometrySave); + + //QVBoxLayout* layout = new QVBoxLayout(this); + + //tabBar = new QTabBar(this); + ui.tabBar->setToolTip(tr("Plugin categories.\nRight-click on tabs to manage.\nRight-click on plugins to add/remove from a category.")); + ui.tabBar->addTab("All"); + for (QList::iterator it=MusEGlobal::plugin_group_names.begin(); it!=MusEGlobal::plugin_group_names.end(); it++) + ui.tabBar->addTab(*it); + + + //pList = new QTreeWidget(this); + + ui.pList->setColumnCount(12); + // "Note: In order to avoid performance issues, it is recommended that sorting + // is enabled after inserting the items into the tree. Alternatively, you could + // also insert the items into a list before inserting the items into the tree. " + QStringList headerLabels; + headerLabels << tr("Type"); + headerLabels << tr("Lib"); + headerLabels << tr("Label"); + headerLabels << tr("Name"); + headerLabels << tr("AI"); + headerLabels << tr("AO"); + headerLabels << tr("CI"); + headerLabels << tr("CO"); + headerLabels << tr("IP"); + headerLabels << tr("FB"); + headerLabels << tr("2B"); + headerLabels << tr("id"); + headerLabels << tr("Maker"); + headerLabels << tr("Copyright"); + + ui.pList->setHeaderLabels(headerLabels); + + ui.pList->headerItem()->setToolTip(4, tr("Audio inputs")); + ui.pList->headerItem()->setToolTip(5, tr("Audio outputs")); + ui.pList->headerItem()->setToolTip(6, tr("Control inputs")); + ui.pList->headerItem()->setToolTip(7, tr("Control outputs")); + ui.pList->headerItem()->setToolTip(8, tr("In-place capable")); + ui.pList->headerItem()->setToolTip(9, tr("Requires fixed block size")); + ui.pList->headerItem()->setToolTip(10, tr("Requires power-of-2 block size")); + ui.pList->headerItem()->setToolTip(11, tr("ID number")); + + ui.pList->setRootIsDecorated(false); + ui.pList->setSelectionBehavior(QAbstractItemView::SelectRows); + ui.pList->setSelectionMode(QAbstractItemView::SingleSelection); + ui.pList->setAlternatingRowColors(true); + ui.pList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + ui.pList->setContextMenuPolicy(Qt::CustomContextMenu); + + + ui.okB->setDefault(true); + ui.okB->setFixedWidth(80); + ui.okB->setEnabled(false); + ui.cancelB->setFixedWidth(80); + + switch(selectedPlugPortType) { + case SEL_PORT_SM: ui.onlySM->setChecked(true); break; + case SEL_PORT_S: ui.onlyS->setChecked(true); break; + case SEL_PORT_M: ui.onlyM->setChecked(true); break; + case SEL_PORT_ALL: ui.allPlug->setChecked(true); break; + } + + ui.tabBar->setCurrentIndex(selectedGroup); + ui.tabBar->setContextMenuPolicy(Qt::ActionsContextMenu); + newGroupAction= new QAction(tr("&create new group"),ui.tabBar); + delGroupAction= new QAction(tr("&delete currently selected group"),ui.tabBar); + renGroupAction= new QAction(tr("re&name currently selected group"),ui.tabBar); + ui.tabBar->addAction(newGroupAction); + ui.tabBar->addAction(delGroupAction); + ui.tabBar->addAction(renGroupAction); + + if (selectedGroup==0) + { + delGroupAction->setEnabled(false); + renGroupAction->setEnabled(false); + } + //ui.tabBar->setMovable(true); //not yet. need to find a way to forbid moving the zeroth tab + + + ui.pluginfilterGroup->setToolTip(tr("Select which types of plugins should be visible in the list.
    " + "Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.
    " + "Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack.")); + + ui.pluginType->addItem("All", SEL_TYPE_ALL); + ui.pluginType->addItem("DSSI", SEL_TYPE_DSSI); + ui.pluginType->addItem("LADSPA", SEL_TYPE_LADSPA); + ui.pluginType->addItem("LV2", SEL_TYPE_LV2); + ui.pluginType->addItem("VST", SEL_TYPE_VST); + ui.pluginType->addItem("Wine VST", SEL_TYPE_WINE_VST); + connect (ui.pluginType,SIGNAL(currentIndexChanged(int)), SLOT(filterType(int))); + + for (int i=0; i < ui.pluginType->count(); i++) { + if (selectedPlugType == ui.pluginType->itemData(i).toInt()) { + ui.pluginType->setCurrentIndex(i); + //printf("set current index to %d\n",i); + break; + } + } + ui.sortBox->addItems(sortItems); + + fillPlugs(); + + ui.pList->setSortingEnabled(true); + + if(listSave.isEmpty()) + { + int sizes[] = { 80, 110, 110, 110, 30, 30, 30, 30, 30, 30, 30, 50, 110, 110 }; + for (int i = 0; i < 12; ++i) { + if (sizes[i] <= 50) // hack alert! + ui.pList->header()->setSectionResizeMode(i, QHeaderView::Fixed); + ui.pList->header()->resizeSection(i, sizes[i]); + } + ui.pList->sortByColumn(3, Qt::AscendingOrder); + } + else + ui.pList->header()->restoreState(listSave); + + connect(ui.pList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(accept())); + connect(ui.pList, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SLOT(enableOkB())); + connect(ui.pList, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(plistContextMenu(const QPoint&))); + connect(ui.cancelB, SIGNAL(clicked()), SLOT(reject())); + connect(ui.okB, SIGNAL(clicked()), SLOT(accept())); + connect(ui.portFilterGroup, SIGNAL(buttonClicked(QAbstractButton*)), SLOT(pluginTypeSelectionChanged(QAbstractButton*))); + connect(ui.tabBar, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); + //connect(tabBar, SIGNAL(tabMoved(int,int)), SLOT(tabMoved(int,int))); //not yet. need to find a way to forbid moving the zeroth tab + connect(ui.sortBox, SIGNAL(editTextChanged(const QString&)),SLOT(fillPlugs())); + connect(newGroupAction, SIGNAL(triggered()), SLOT(newGroup())); + connect(delGroupAction, SIGNAL(triggered()), SLOT(delGroup())); + connect(renGroupAction, SIGNAL(triggered()), SLOT(renameGroup())); + ui.sortBox->setFocus(); +} + +void PluginDialog::filterType(int i) +{ + selectedPlugType = ui.pluginType->itemData(i).toInt(); + + fillPlugs(); + +} + +void PluginDialog::plistContextMenu(const QPoint& point) +{ + QTreeWidgetItem* item = ui.pList->currentItem(); + if (item) + { + group_info = &MusEGlobal::plugin_groups.get(item->text(1), item->text(2)); + QMenu* menu = new MusEGui::PopupMenu(this, true); + QSignalMapper* mapper = new QSignalMapper(this); + menu->addAction(new MusEGui::MenuTitleItem(tr("Associated categories"), menu)); + + if (ui.tabBar->count()==1) + { + QAction* tmp=menu->addAction(tr("You need to define some categories first.")); + tmp->setEnabled(false); + } + else + { + for (int i=1; icount(); i++) // ignore the first tab ("All") + { + QAction* act=menu->addAction(ui.tabBar->tabText(i)); + act->setCheckable(true); + act->setChecked(group_info->contains(i)); + connect(act,SIGNAL(toggled(bool)), mapper, SLOT(map())); + mapper->setMapping(act, i); + } + connect(mapper, SIGNAL(mapped(int)), this, SLOT(groupMenuEntryToggled(int))); + } + + menu->exec(mapToGlobal(point)); + + delete mapper; + delete menu; + + if (selectedGroup!=0 && !group_info->contains(selectedGroup)) // we removed the entry from the currently visible group + fillPlugs(); + + group_info=NULL; + } +} + +void PluginDialog::groupMenuEntryToggled(int index) +{ + if (group_info) + { + if (group_info->contains(index)) + group_info->remove(index); + else + group_info->insert(index); + } + else + { + fprintf(stderr,"THIS SHOULD NEVER HAPPEN: groupMenuEntryToggled called but group_info is NULL!\n"); + } +} + + + +//--------------------------------------------------------- +// enableOkB +//--------------------------------------------------------- + +void PluginDialog::enableOkB() +{ + ui.okB->setEnabled(true); +} + + +void PluginDialog::newGroup() +{ + MusEGlobal::plugin_groups.shift_right(selectedGroup+1, ui.tabBar->count()); + ui.tabBar->insertTab(selectedGroup+1, tr("new group")); + MusEGlobal::plugin_group_names.insert(selectedGroup, tr("new group")); +} + +void PluginDialog::delGroup() +{ + if (selectedGroup!=0) + { + MusEGlobal::plugin_groups.erase(selectedGroup); + MusEGlobal::plugin_groups.shift_left(selectedGroup+1, ui.tabBar->count()); + ui.tabBar->removeTab(selectedGroup); + MusEGlobal::plugin_group_names.removeAt(selectedGroup-1); + } +} + +void PluginDialog::renameGroup() +{ + if (selectedGroup!=0) + { + bool ok; + QString newname = QInputDialog::getText(this, tr("Enter the new group name"), + tr("Enter the new group name"), QLineEdit::Normal, + ui.tabBar->tabText(selectedGroup), &ok); + if (ok) + { + ui.tabBar->setTabText(selectedGroup, newname); + MusEGlobal::plugin_group_names.replace(selectedGroup-1, newname); + } + } +} + +//--------------------------------------------------------- +// value +//--------------------------------------------------------- + +MusECore::Plugin* PluginDialog::value() +{ + QTreeWidgetItem* item = ui.pList->currentItem(); + if (item) + return MusEGlobal::plugins.find(item->text(1), item->text(2)); + printf("plugin not found\n"); + return 0; +} + +//--------------------------------------------------------- +// saveSettings +//--------------------------------------------------------- + +void PluginDialog::saveSettings() +{ + if (!ui.sortBox->currentText().isEmpty()) { + bool found = false; + foreach (QString item, sortItems) + if(item == ui.sortBox->currentText()) { + found = true; + break; + } + if(!found) + sortItems.push_front(ui.sortBox->currentText()); + } + + QHeaderView* hdr = ui.pList->header(); + if(hdr) + listSave = hdr->saveState(); + + geometrySave = geometry(); +} + +//--------------------------------------------------------- +// accept +//--------------------------------------------------------- + +void PluginDialog::accept() +{ + saveSettings(); + QDialog::accept(); +} + +//--------------------------------------------------------- +// reject +//--------------------------------------------------------- + +void PluginDialog::reject() +{ + saveSettings(); + QDialog::reject(); +} + +//--------------------------------------------------------- +// pluginTypeSelectionChanged +//--------------------------------------------------------- + +void PluginDialog::pluginTypeSelectionChanged(QAbstractButton* ab) +{ + if (ab == ui.allPlug) + selectedPlugPortType = SEL_PORT_ALL; + else if (ab == ui.onlyM) + selectedPlugPortType = SEL_PORT_M; + else if (ab == ui.onlyS) + selectedPlugPortType = SEL_PORT_S; + else if (ab == ui.onlySM) + selectedPlugPortType = SEL_PORT_SM; + fillPlugs(); +} + +void PluginDialog::tabChanged(int index) +{ + renGroupAction->setEnabled(index!=0); + delGroupAction->setEnabled(index!=0); + + selectedGroup=index; + fillPlugs(); +} + +void PluginDialog::tabMoved(int from, int to) +{ +//all the below doesn't work :/ +/* static bool recurse=false; + + if (!recurse) + { + if (from==0 && to!=0) {recurse=true; tabBar->moveTab(to, from);} + if (from!=0 && to==0) {recurse=true; tabBar->moveTab(from, to);} + } + recurse=false;*/ + + + //if ((from==0 && to!=0) || (from!=0 && to==0)) { tabBar->setMovable(false); tabBar->setMovable(true); } + printf("**** %i -> %i\n", from, to); + + //FINDMICH TODO +} + +void PluginDialog::fillPlugs() +{ + QString type_name; + ui.pList->clear(); + ui.okB->setEnabled(false); + for (MusECore::iPlugin i = MusEGlobal::plugins.begin(); i != MusEGlobal::plugins.end(); ++i) + if (selectedGroup==0 || MusEGlobal::plugin_groups.get(*i).contains(selectedGroup)) + { + unsigned long ai = (*i)->inports(); + unsigned long ao = (*i)->outports(); + unsigned long ci = (*i)->controlInPorts(); + unsigned long co = (*i)->controlOutPorts(); + bool found = false; + QString sb_txt = ui.sortBox->currentText().toLower(); + if(sb_txt.isEmpty() || (*i)->label().toLower().contains(sb_txt) || (*i)->name().toLower().contains(sb_txt)) + found = true; + + bool addFlag = false; + switch (selectedPlugPortType) { + case SEL_PORT_SM: // stereo & mono + if ((ai == 1 || ai == 2) && (ao == 1 || ao ==2)) { + addFlag = true; + } + break; + case SEL_PORT_S: // stereo + if ((ai == 1 || ai == 2) && ao ==2) { + addFlag = true; + } + break; + case SEL_PORT_M: // mono + if (ai == 1 && ao == 1) { + addFlag = true; + } + break; + case SEL_PORT_ALL: // all + addFlag = true; + break; + } + if (found && addFlag) { + int plugInstanceType; + if((*i)->isDssiSynth() || (*i)->isDssiPlugin()) { + if ((*i)->lib() == "dssi-vst") { + type_name = tr("Wine VST"); + plugInstanceType = SEL_TYPE_WINE_VST; + } else { + if ((*i)->isDssiSynth()) + type_name = tr("dssi synth"); + else + type_name = tr("dssi effect"); + plugInstanceType = SEL_TYPE_DSSI; + } + } + else if((*i)->isLV2Synth()) { + type_name = tr("LV2 synth"); + plugInstanceType = SEL_TYPE_LV2; + } + else if((*i)->isLV2Plugin()) { + type_name = tr("LV2 effect"); + plugInstanceType = SEL_TYPE_LV2; + } + else if((*i)->isVstNativeSynth()) { + type_name = tr("VST synth"); + plugInstanceType = SEL_TYPE_VST; + } + else if((*i)->isVstNativePlugin()) { + type_name = tr("VST effect"); + plugInstanceType = SEL_TYPE_VST; + } + else { + type_name = tr("ladspa"); + plugInstanceType = SEL_TYPE_LADSPA; + } + + // last check + if ( selectedPlugType != SEL_TYPE_ALL && plugInstanceType != selectedPlugType ) { + continue; + } + + QTreeWidgetItem* item = new QTreeWidgetItem; + item->setText(0, type_name); + item->setText(1, (*i)->lib()); + item->setText(2, (*i)->label()); + item->setText(3, (*i)->name()); + item->setText(4, QString().setNum(ai)); + item->setText(5, QString().setNum(ao)); + item->setText(6, QString().setNum(ci)); + item->setText(7, QString().setNum(co)); + if( !((*i)->requiredFeatures() & MusECore::Plugin::NoInPlaceProcessing) ) + item->setText(8, "*"); + if( (*i)->requiredFeatures() & MusECore::Plugin::FixedBlockSize ) + item->setText(9, "*"); + if( (*i)->requiredFeatures() & MusECore::Plugin::PowerOf2BlockSize ) + item->setText(10, "*"); + item->setText(11, QString().setNum((*i)->id())); + item->setText(12, (*i)->maker()); + item->setText(13, (*i)->copyright()); + ui.pList->addTopLevelItem(item); + } + } +} + +//--------------------------------------------------------- +// getPlugin +//--------------------------------------------------------- + +MusECore::Plugin* PluginDialog::getPlugin(QWidget* parent) +{ + PluginDialog* dialog = new PluginDialog(parent); + MusECore::Plugin* p = 0; + int rv = dialog->exec(); + if(rv) + p = dialog->value(); + delete dialog; + return p; +} + +} + diff -Nru muse-2.1.2/muse/widgets/plugindialog.h muse-3.0.2+ds1/muse/widgets/plugindialog.h --- muse-2.1.2/muse/widgets/plugindialog.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/plugindialog.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,93 @@ +#ifndef PLUGINDIALOG_H +#define PLUGINDIALOG_H + +#include +#include "globals.h" +#include "ui_plugindialogbase.h" + +class QAbstractButton; + +namespace Ui { +class PluginDialogBase; +} + +namespace MusECore { +class Plugin; +} + +namespace MusEGui { + + + +class PluginDialog : public QDialog { + Q_OBJECT + enum SelectedPluginPortTypes { SEL_PORT_SM, SEL_PORT_S, SEL_PORT_M, SEL_PORT_ALL }; + enum SelectedPluginTypes { SEL_TYPE_DSSI, SEL_TYPE_LADSPA, SEL_TYPE_LV2, SEL_TYPE_VST, SEL_TYPE_WINE_VST, SEL_TYPE_ALL}; + + public: + explicit PluginDialog(QWidget* parent=0); + static MusECore::Plugin* getPlugin(QWidget* parent); + MusECore::Plugin* value(); + + public slots: + void accept(); + void reject(); + + private slots: + void enableOkB(); + void pluginTypeSelectionChanged(QAbstractButton*); + void tabChanged(int); + void tabMoved(int,int); + void fillPlugs(); + void filterType(int); + + void newGroup(); + void delGroup(); + void renameGroup(); + void plistContextMenu(const QPoint&); + void groupMenuEntryToggled(int i); + + private: +// QComboBox* sortBox; +// QTabBar* tabBar; +// QTreeWidget* pList; +// QRadioButton* allPlug; +// QRadioButton* onlyM; +// QRadioButton* onlyS; +// QRadioButton* onlySM; +// QPushButton *okB; + + QAction* newGroupAction; + QAction* delGroupAction; + QAction* renGroupAction; + + + void saveSettings(); + + static int selectedPlugPortType; + static int selectedPlugType; + static int selectedGroup; // 0 means "show all" + static QStringList sortItems; + static QRect geometrySave; + static QByteArray listSave; + + QSet* group_info; //holds the group-set of the plugin which shall be affected by the plistContextMenu. + + Ui::PluginDialogBase ui; +}; + +} + + + + + + + + + + + + + +#endif // PLUGINDIALOGBASE_H diff -Nru muse-2.1.2/muse/widgets/popup_double_spinbox.cpp muse-3.0.2+ds1/muse/widgets/popup_double_spinbox.cpp --- muse-2.1.2/muse/widgets/popup_double_spinbox.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/popup_double_spinbox.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,169 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// popup_double_spinbox.cpp +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 "popup_double_spinbox.h" + +#include +#include + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_POPUP_DOUBLE_SPINBOX(dev, format, args...) // fprintf(dev, format, ##args); + +namespace MusEGui { + +//--------------------------------------------------------- +// PopupDoubleSpinBox +//--------------------------------------------------------- + +PopupDoubleSpinBox::PopupDoubleSpinBox(QWidget* parent) + : QDoubleSpinBox(parent) +{ + // Reset these since our parent will typically turn them on for speed. + setAutoFillBackground(true); + setAttribute(Qt::WA_NoSystemBackground, false); + setAttribute(Qt::WA_StaticContents, false); + setAttribute(Qt::WA_OpaquePaintEvent, false); + + setAlignment(Qt::AlignRight | Qt::AlignVCenter); + + setContentsMargins(0, 0, 0, 0); + setFrame(false); + + _closePending = false; + +// DoubleSpinBoxLineEdit* le = new DoubleSpinBoxLineEdit(this); +// setLineEdit(le); +// setKeyboardTracking(false); +// +// connect(le, SIGNAL(doubleClicked()), this, SIGNAL(doubleClicked())); +// connect(le, SIGNAL(ctrlDoubleClicked()), this, SIGNAL(ctrlDoubleClicked())); +// //connect(le, SIGNAL(ctrlClicked()), this, SIGNAL(ctrlClicked())); +} + +bool PopupDoubleSpinBox::event(QEvent* e) +{ + switch(e->type()) + { + case QEvent::KeyPress: + { + QKeyEvent* ke = static_cast(e); + switch(ke->key()) + { + // For return, we want to close the editor but don't want the + // parent to receive the event which will just open the box again. + case Qt::Key_Return: + case Qt::Key_Enter: + //e->ignore(); +// QDoubleSpinBox::event(e); + e->accept(); + //emit editingFinished(); + if(!_closePending) + { + _closePending = true; + emit returnPressed(); + } +// // Will emit editingFinished. +// deleteLater(); + return true; + break; + + case Qt::Key_Escape: + { + e->accept(); + //emit editingFinished(); + if(!_closePending) + { + _closePending = true; + emit escapePressed(); + } +// // Will emit editingFinished. +// deleteLater(); + return true; + } + break; + + default: + break; + } + } + break; + + case QEvent::NonClientAreaMouseButtonPress: + // FIXME: Doesn't work. + DEBUG_POPUP_DOUBLE_SPINBOX(stderr, "PopupDoubleSpinBox::event NonClientAreaMouseButtonPress\n"); + case QEvent::FocusOut: + e->accept(); + if(!_closePending) + { + _closePending = true; + emit returnPressed(); + } + return true; + break; + + default: + break; + } + + // Do not pass ANY events on to the parent. + QDoubleSpinBox::event(e); + e->accept(); + return true; +} + +// void PopupDoubleSpinBox::keyPressEvent(QKeyEvent* e) +// { +// // switch (e->key()) { +// // // For return, we want to close the editor but don't want the +// // // parent to receive the event which will just open the box again. +// // case Qt::Key_Return: +// // case Qt::Key_Escape: +// // e->accept(); +// // //emit editingFinished(); // Already emitted +// // return; +// // break; +// // default: +// // break; +// // } +// // e->ignore(); +// +// // Do not pass ANY events on to the parent. +// e->accept(); +// QDoubleSpinBox::keyPressEvent(e); +// } + +// void PopupDoubleSpinBox::wheelEvent(QWheelEvent* e) +// { +// QDoubleSpinBox::wheelEvent(e); +// // Need this because Qt doesn't deselect the text if not focused. +// if(!hasFocus() && lineEdit()) +// lineEdit()->deselect(); +// } + +// void PopupDoubleSpinBox::focusOutEvent(QFocusEvent*) +// { +// emit editingFinished(); +// } + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/popup_double_spinbox.h muse-3.0.2+ds1/muse/widgets/popup_double_spinbox.h --- muse-2.1.2/muse/widgets/popup_double_spinbox.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/popup_double_spinbox.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,56 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// popup_double_spinbox.h +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __POPUP_DOUBLE_SPINBOX_H__ +#define __POPUP_DOUBLE_SPINBOX_H__ + +#include + +class QEvent; + +namespace MusEGui { + +//--------------------------------------------------------- +// PopupDoubleSpinBox +//--------------------------------------------------------- + +class PopupDoubleSpinBox : public QDoubleSpinBox { + Q_OBJECT + + private: + bool _closePending; + + protected: + virtual bool event(QEvent*); + + signals: + void returnPressed(); + void escapePressed(); + + public: + PopupDoubleSpinBox(QWidget* parent=0); +}; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/popupmenu.cpp muse-3.0.2+ds1/muse/widgets/popupmenu.cpp --- muse-2.1.2/muse/widgets/popupmenu.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/popupmenu.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -6,7 +6,7 @@ // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) // // PopupMenu sub-class of QMenu created by Tim. -// (C) Copyright 2010-2011 Tim E. Real (terminator356 A T sourceforge D O T net) +// (C) Copyright 2010-2015 Tim E. Real (terminator356 A T sourceforge D O T net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -28,14 +28,20 @@ #include #include #include +#include +#include #include #include +#include #include "popupmenu.h" #include "gconfig.h" #include "route.h" - - + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); + + namespace MusEGui { //====================== @@ -43,293 +49,338 @@ //====================== PopupMenu::PopupMenu(bool stayOpen) - : _stayOpen(stayOpen) + : _stayOpen(stayOpen) { - init(); + init(); } PopupMenu::PopupMenu(QWidget* parent, bool stayOpen) - : QMenu(parent), _stayOpen(stayOpen) + : QMenu(parent), _stayOpen(stayOpen) { - init(); + init(); } PopupMenu::PopupMenu(const QString& title, QWidget* parent, bool stayOpen) - : QMenu(title, parent), _stayOpen(stayOpen) + : QMenu(title, parent), _stayOpen(stayOpen) { - init(); + init(); } void PopupMenu::init() { - // Menus will trigger! Set to make sure our trigger handlers ignore menus. - menuAction()->setData(-1); - - _cur_menu = this; - _cur_menu_count = 1; - _cur_item_width = 0; - _cur_col_count = 0; - - //_stayOpen = false; - moveDelta = 0; - - #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL - timer = new QTimer(this); - timer->setInterval(100); - timer->setSingleShot(false); - connect(this, SIGNAL(hovered(QAction*)), SLOT(popHovered(QAction*))); - connect(timer, SIGNAL(timeout()), SLOT(timerHandler())); - #endif // POPUP_MENU_DISABLE_AUTO_SCROLL -} - -// NOTE: Tested all RoutePopupMenu and PopupMenu dtors and a couple of action dtors from our -// PixmapButtonsHeaderWidgetAction and PixmapButtonsWidgetAction: -// This does not appear to be required any more. All submenus and actions are being deleted now. -/* -void PopupMenu::clear() -{ - //printf("PopupMenu::clear this:%p\n", this); + _contextMenu = 0; + _lastHoveredAction = 0; + _highlightedAction = 0; + // Menus will trigger! Set to make sure our trigger handlers ignore menus. + menuAction()->setData(-1); + _cur_menu = this; + _cur_menu_count = 1; + _max_items_in_breakup = 0; + moveDelta = 0; + timer = 0; + + connect(this, SIGNAL(hovered(QAction*)), SLOT(popHovered(QAction*))); + + if(MusEGlobal::config.scrollableSubMenus) + { + setStyleSheet("QMenu { menu-scrollable: 1; }"); + return; + } + +#ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + timer = new QTimer(this); + timer->setInterval(100); + timer->setSingleShot(false); + connect(timer, SIGNAL(timeout()), SLOT(timerHandler())); +#endif // POPUP_MENU_DISABLE_AUTO_SCROLL +} - QList list = actions(); - for(int i = 0; i < list.size(); ++i) - { - QAction* act = list[i]; - QMenu* menu = act->menu(); - if(menu) - { - menu->clear(); // Recursive. - act->setMenu(0); // CHECK: Is this OK? - //printf(" deleting menu:%p\n", menu); - delete menu; - } - } - - // Now let QT remove and delete this menu's actions. - QMenu::clear(); - - #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL - connect(this, SIGNAL(hovered(QAction*)), SLOT(popHovered(QAction*))); - connect(timer, SIGNAL(timeout()), SLOT(timerHandler())); - #endif // POPUP_MENU_DISABLE_AUTO_SCROLL +PopupMenu::~PopupMenu() +{ + if(_contextMenu) + delete _contextMenu; + _contextMenu = 0; } -*/ void PopupMenu::clearAllChecks() const { - QList list = actions(); - for(int i = 0; i < list.size(); ++i) - { - QAction* act = list[i]; - PopupMenu* menu = static_cast (act->menu()); - if(menu) - menu->clearAllChecks(); // Recursive. - if(act->isCheckable()) - { - act->blockSignals(true); - act->setChecked(false); - act->blockSignals(false); - } - } + QList list = actions(); + for(int i = 0; i < list.size(); ++i) + { + QAction* act = list[i]; + if(PopupMenu* menu = qobject_cast(act->menu())) + menu->clearAllChecks(); // Recursive. + if(act->isCheckable()) + { + act->blockSignals(true); + act->setChecked(false); + act->blockSignals(false); + } + } } QAction* PopupMenu::findActionFromData(const QVariant& v) const { - QList list = actions(); - for(int i = 0; i < list.size(); ++i) - { - QAction* act = list[i]; - PopupMenu* menu = (PopupMenu*)act->menu(); - if(menu) - { - if(QAction* actm = menu->findActionFromData(v)) // Recursive. - return actm; - } - - // "Operator == Compares this QVariant with v and returns true if they are equal, - // otherwise returns false. In the case of custom types, their equalness operators - // are not called. Instead the values' addresses are compared." - // - // Take care of struct Route first. Insert other future custom structures here too ! - if(act->data().canConvert() && v.canConvert()) - { - if(act->data().value() == v.value()) - return act; - } - else - if(act->data() == v) - return act; - } - return 0; + QList list = actions(); + for(int i = 0; i < list.size(); ++i) + { + QAction* act = list[i]; + if(PopupMenu* menu = qobject_cast(act->menu())) + { + if(QAction* actm = menu->findActionFromData(v)) // Recursive. + return actm; + } + + // "Operator == Compares this QVariant with v and returns true if they are equal, + // otherwise returns false. In the case of custom types, their equalness operators + // are not called. Instead the values' addresses are compared." + // + // Take care of struct Route first. Insert other future custom structures here too ! + if(act->data().canConvert() && v.canConvert()) + { + if(act->data().value() == v.value()) + return act; + } + else + if(act->data() == v) + return act; + } + return 0; } - + bool PopupMenu::event(QEvent* event) { - switch(event->type()) - { - #ifndef POPUP_MENU_DISABLE_STAY_OPEN - case QEvent::MouseButtonDblClick: - { + DEBUG_PRST_ROUTES(stderr, "PopupMenu::event:%p activePopupWidget:%p this:%p class:%s event type:%d\n", + event, QApplication::activePopupWidget(), this, metaObject()->className(), event->type()); + + switch(event->type()) + { +#ifndef POPUP_MENU_DISABLE_STAY_OPEN + case QEvent::MouseButtonDblClick: + { if(_stayOpen) - //if(_stayOpen && MusEGlobal::config.popupsDefaultStayOpen) { - QMouseEvent* e = static_cast(event); - if(e->modifiers() == Qt::NoModifier) + QMouseEvent* e = static_cast(event); + if(e->modifiers() == Qt::NoModifier) + { + event->accept(); + // Convert into a return press, which selects the item and closes the menu. + // Note that with double click, it's a press followed by release followed by double click. + // That would toggle our item twice eg on->off->on, which is hopefully OK. + QKeyEvent ke(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); + //ke.ignore(); // Pass it on + return QMenu::event(&ke); + } + } + } + break; + + case QEvent::KeyPress: + { + QKeyEvent* e = static_cast(event); + switch(e->key()) + { + case Qt::Key_Space: + if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this)) + break; + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH + case Qt::Key_Select: + case Qt::Key_Return: + case Qt::Key_Enter: { - event->accept(); - // Convert into a return press, which selects the item and closes the menu. - // Note that with double click, it's a press followed by release followed by double click. - // That would toggle our item twice eg on->off->on, which is hopefully OK. - QKeyEvent ke(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); - //ke.ignore(); // Pass it on - return QMenu::event(&ke); - } - } - } - break; - case QEvent::KeyPress: - { - if(_stayOpen) - //if(_stayOpen && MusEGlobal::config.popupsDefaultStayOpen) + if(QAction* act = activeAction()) + { + const bool stay_open = _stayOpen && (MusEGlobal::config.popupsDefaultStayOpen || (e->modifiers() & Qt::ControlModifier)); + // Stay open? Or does the action have a submenu, but also a checkbox of its own? + if(stay_open || (act->isEnabled() && act->menu() && act->isCheckable())) + { + act->trigger(); // Trigger the action. + event->accept(); + if(!stay_open) + closeUp(); + return true; // We handled it. + } + // Otherwise let ancestor QMenu handle it... + } + } + break; + + default: + break; + } + } + break; +#endif // POPUP_MENU_DISABLE_STAY_OPEN + +#ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + case QEvent::MouseMove: + { + if(!MusEGlobal::config.scrollableSubMenus) { - QKeyEvent* e = static_cast(event); - if(e->modifiers() == Qt::NoModifier && e->key() == Qt::Key_Space) + QMouseEvent* e = static_cast(event); + QPoint globPos = e->globalPos(); + int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. + if(x() < 0 && globPos.x() <= 0) // If on the very first pixel (or beyond) { - QAction* act = activeAction(); - if(act) + moveDelta = 32; + if(!timer->isActive()) + timer->start(); + event->accept(); + return true; + } + else + if(x() + width() >= dw && globPos.x() >= (dw -1)) // If on the very last pixel (or beyond) { - act->trigger(); - event->accept(); - return true; // We handled it. + moveDelta = -32; + if(!timer->isActive()) + timer->start(); + event->accept(); + return true; } - } + + if(timer->isActive()) + timer->stop(); } - } - break; - #endif // POPUP_MENU_DISABLE_STAY_OPEN - - #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL - case QEvent::MouseMove: + } + break; +#endif // POPUP_MENU_DISABLE_AUTO_SCROLL + + default: + break; + } + + return QMenu::event(event); +} + +void PopupMenu::closeUp() +{ + if(isVisible()) + { + DEBUG_PRST_ROUTES(stderr, "PopupMenu::closeUp() this:%p closing\n", this); + close(); + } + + QAction* act = menuAction(); + if(act) + { + DEBUG_PRST_ROUTES(stderr, "PopupMenu::closeUp() this:%p menuAction:%p\n", this, act); + const int sz = act->associatedWidgets().size(); + for(int i = 0; i < sz; ++i) { - QMouseEvent* e = static_cast(event); - QPoint globPos = e->globalPos(); - int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. - if(x() < 0 && globPos.x() <= 0) // If on the very first pixel (or beyond) - { - moveDelta = 32; - if(!timer->isActive()) - timer->start(); - event->accept(); - return true; - } - else - if(x() + width() >= dw && globPos.x() >= (dw -1)) // If on the very last pixel (or beyond) + DEBUG_PRST_ROUTES(stderr, " associated widget#:%d\n", i); + if(PopupMenu* pup = qobject_cast(act->associatedWidgets().at(i))) { - moveDelta = -32; - if(!timer->isActive()) - timer->start(); - event->accept(); - return true; + DEBUG_PRST_ROUTES(stderr, " associated popup:%p\n", pup); + DEBUG_PRST_ROUTES(stderr, " closing...\n"); + pup->closeUp(); } - - if(timer->isActive()) - timer->stop(); - - //event->accept(); - //return true; - - event->ignore(); // Pass it on - //return QMenu::event(event); - } - break; - #endif // POPUP_MENU_DISABLE_AUTO_SCROLL - - default: - break; + } } - - return QMenu::event(event); } #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL void PopupMenu::timerHandler() { - //if(!isVisible() || !hasFocus()) - if(!isVisible()) - { - timer->stop(); - return; - } + if(!isVisible()) + { + timer->stop(); + return; + } - int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. - int nx = x() + moveDelta; - if(moveDelta < 0 && nx + width() < dw) - { - timer->stop(); - nx = dw - width(); - } - else - if(moveDelta > 0 && nx > 0) - { - timer->stop(); - nx = 0; - } - - move(nx, y()); + int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. + int nx = x() + moveDelta; + if(moveDelta < 0 && nx + width() < dw) + { + timer->stop(); + nx = dw - width(); + } + else + if(moveDelta > 0 && nx > 0) + { + timer->stop(); + nx = 0; + } + + move(nx, y()); } +#endif // POPUP_MENU_DISABLE_AUTO_SCROLL void PopupMenu::popHovered(QAction* action) +{ + DEBUG_PRST_ROUTES(stderr, "PopupMenu::popHovered action text:%s\n", action->text().toLatin1().constData()); + _lastHoveredAction = action; + hideContextMenu(); +#ifndef POPUP_MENU_DISABLE_AUTO_SCROLL + if(action && !MusEGlobal::config.scrollableSubMenus) + { + int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. + QRect r = actionGeometry(action); + if(x() + r.x() < 0) + move(-r.x(), y()); + else + if(r.x() + r.width() + x() > dw) + move(dw - r.x() - r.width(), y()); + } +#endif // POPUP_MENU_DISABLE_AUTO_SCROLL +} + +void PopupMenu::mousePressEvent(QMouseEvent* e) { - if(action) - { - int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. - QRect r = actionGeometry(action); - if(x() + r.x() < 0) - move(-r.x(), y()); - else - if(r.x() + r.width() + x() > dw) - move(dw - r.x() - r.width(), y()); - } + DEBUG_PRST_ROUTES(stderr, "PopupMenu::mousePressEvent this:%p\n", this); + if (_contextMenu && _contextMenu->isVisible()) + _contextMenu->hide(); + e->ignore(); + QMenu::mousePressEvent(e); } -#endif // POPUP_MENU_DISABLE_AUTO_SCROLL void PopupMenu::mouseReleaseEvent(QMouseEvent *e) { - QAction* action = actionAt(e->pos()); - if (!(action && action == activeAction() && !action->isSeparator() && action->isEnabled())) + DEBUG_PRST_ROUTES(stderr, "PopupMenu::mouseReleaseEvent this:%p\n", this); + if(_contextMenu && _contextMenu->isVisible()) + return; + +// Removed by Tim. Why not stay-open scrollable menus? +// if(MusEGlobal::config.scrollableSubMenus) +// { +// QMenu::mouseReleaseEvent(e); +// return; +// } + + QAction* action = actionAt(e->pos()); + if (!(action && action == activeAction() && !action->isSeparator() && action->isEnabled())) action=NULL; - #ifdef POPUP_MENU_DISABLE_STAY_OPEN - if (action && action->menu() != NULL && action->isCheckable()) +#ifdef POPUP_MENU_DISABLE_STAY_OPEN + if (action && action->menu() != NULL && action->isCheckable()) action->activate(QAction::Trigger); - QMenu::mouseReleaseEvent(e); - - if (action && action->menu() != NULL && action->isCheckable()) + QMenu::mouseReleaseEvent(e); + + if (action && action->menu() != NULL && action->isCheckable()) close(); - - return; - - #else - // Check for Ctrl to stay open. - if(!_stayOpen || (!MusEGlobal::config.popupsDefaultStayOpen && (e->modifiers() & Qt::ControlModifier) == 0)) - { - if (action && action->menu() != NULL && action->isCheckable()) - action->activate(QAction::Trigger); - QMenu::mouseReleaseEvent(e); + return; - if (action && action->menu() != NULL && action->isCheckable()) - close(); +#else + + // Check for Ctrl to stay open. + const bool stay_open = _stayOpen && (MusEGlobal::config.popupsDefaultStayOpen || (e->modifiers() & Qt::ControlModifier)); + // Stay open? Or does the action have a submenu, but also a checkbox of its own? + if(action && (stay_open || (action->isEnabled() && action->menu() && action->isCheckable()))) + { + DEBUG_PRST_ROUTES(stderr, "PopupMenu::mouseReleaseEvent: stay open\n"); + action->trigger(); // Trigger the action. + e->accept(); + if(!stay_open) + closeUp(); + return; // We handled it. + } + // Otherwise let ancestor QMenu handle it... + e->ignore(); + QMenu::mouseReleaseEvent(e); - return; - } - - if (action) - action->activate(QAction::Trigger); - else - QMenu::mouseReleaseEvent(e); - - #endif // POPUP_MENU_DISABLE_STAY_OPEN +#endif // POPUP_MENU_DISABLE_STAY_OPEN } //----------------------------------------- @@ -340,28 +391,36 @@ // So try to avoid that. The overwhelming rule of thumb is that too-wide menus are bad design anyway. //------------------------------------------ PopupMenu* PopupMenu::getMenu() -{ - // We want the whole thing if multiple monitors. - // Resonable to assume if X can show this desktop, it can show a menu with the same width? - int dw = QApplication::desktop()->width(); - // If we're still only at one column, not much we can do - some item(s) must have had reeeeally long text. - // Not to worry. Hopefully the auto-scroll will handle it! - // Use columnCount() + 2 to catch well BEFORE it widens beyond the edge, and leave room for many - // TESTED: Not only does the system not appear to like too-wide menus, but also the column count was observed - // rolling over to zero repeatedly after it reached 15, simply when adding actions! The action width was 52 - // the number of items when it first rolled over was around 480 = 884, well below my desktop width of 1366. - // Apparently there is a limit on the number of columns - whatever, it made the col count limit necessary: - if((_cur_col_count > 1 && ((_cur_col_count + 2) * _cur_item_width) >= dw) || _cur_col_count >= 8) - { - // This menu is too wide. So make a new one... - _cur_item_width = 0; - _cur_col_count = 1; - QString s(tr(" %1").arg(_cur_menu_count)); - _cur_menu = new PopupMenu(s, this, _stayOpen); - ++_cur_menu_count; - addMenu(_cur_menu); - } - return _cur_menu; +{ + if(!_cur_menu) + return 0; + + // We want the whole thing if multiple monitors. + // Reasonable to assume if X can show this desktop, it can show a menu with the same width? + int dh = QApplication::desktop()->height(); + // [danvd] Due to slow actionGeometry method in qt5 QMenu, only limit PopupMenu to 2 columns without width checking... + // [Tim] It seems that sizeHint() is only slightly more costly, use it instead. + //int _act_height = _cur_menu->actionGeometry(_actions [0]).height(); + int mh = _cur_menu->sizeHint().height(); + + // If we're still only at one column, not much we can do - some item(s) must have had reeeeally long text. + // Not to worry. Hopefully the auto-scroll will handle it! + // Use columnCount() + 2 to catch well BEFORE it widens beyond the edge, and leave room for many + // TESTED: Not only does the system not appear to like too-wide menus, but also the column count was observed + // rolling over to zero repeatedly after it reached 15, simply when adding actions! The action width was 52 + // the number of items when it first rolled over was around 480 = 884, well below my desktop width of 1366. + // Apparently there is a limit on the number of columns - whatever, it made the col count limit necessary: + //if((_cur_col_count > 1 && ((_cur_col_count + 2) * _cur_item_width) >= dw) || _cur_col_count >= 8) + + if((mh + 100) >= dh) + { + // This menu is too wide. So make a new one... + QString s(tr(" %1").arg(_cur_menu_count)); + _cur_menu = cloneMenu(s, this, _stayOpen); + ++_cur_menu_count; + QMenu::addMenu(_cur_menu); + } + return _cur_menu; } //---------------------------------------------------- @@ -370,88 +429,179 @@ QAction* PopupMenu::addAction(const QString& text) { - QAction* act = static_cast(getMenu())->addAction(text); - int w = _cur_menu->actionGeometry(act).width(); - if(w > _cur_item_width) - _cur_item_width = w; - int c = _cur_menu->columnCount(); - if(c > _cur_col_count) - _cur_col_count = c; - return act; + if(MusEGlobal::config.scrollableSubMenus) + { + return QMenu::addAction(text); + } + QAction* act = static_cast(getMenu())->addAction(text); + return act; } QAction* PopupMenu::addAction(const QIcon& icon, const QString& text) { - QAction* act = static_cast(getMenu())->addAction(icon, text); - int w = _cur_menu->actionGeometry(act).width(); - if(w > _cur_item_width) - _cur_item_width = w; - int c = _cur_menu->columnCount(); - if(c > _cur_col_count) - _cur_col_count = c; - return act; + if(MusEGlobal::config.scrollableSubMenus) + { + return QMenu::addAction(icon, text); + } + QAction* act = static_cast(getMenu())->addAction(icon, text); + return act; } QAction* PopupMenu::addAction(const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut) { - QAction* act = static_cast(getMenu())->addAction(text, receiver, member, shortcut); - int w = _cur_menu->actionGeometry(act).width(); - if(w > _cur_item_width) - _cur_item_width = w; - int c = _cur_menu->columnCount(); - if(c > _cur_col_count) - _cur_col_count = c; - return act; + if(MusEGlobal::config.scrollableSubMenus) + { + return QMenu::addAction(text, receiver, member, shortcut); + } + QAction* act = static_cast(getMenu())->addAction(text, receiver, member, shortcut); + return act; } QAction* PopupMenu::addAction(const QIcon& icon, const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut) { - QAction* act = static_cast(getMenu())->addAction(icon, text, receiver, member, shortcut); - int w = _cur_menu->actionGeometry(act).width(); - if(w > _cur_item_width) - _cur_item_width = w; - int c = _cur_menu->columnCount(); - if(c > _cur_col_count) - _cur_col_count = c; - return act; + if(MusEGlobal::config.scrollableSubMenus) + { + return QMenu::addAction(icon, text, receiver, member, shortcut); + } + QAction* act = static_cast(getMenu())->addAction(icon, text, receiver, member, shortcut); + return act; } void PopupMenu::addAction(QAction* action) { - static_cast(getMenu())->addAction(action); - int w = _cur_menu->actionGeometry(action).width(); - if(w > _cur_item_width) - _cur_item_width = w; - int c = _cur_menu->columnCount(); - if(c > _cur_col_count) - _cur_col_count = c; + if(MusEGlobal::config.scrollableSubMenus) + { + return QMenu::addAction(action); + } + static_cast(getMenu())->addAction(action); } -/* -//====================== -// PopupView -//====================== +QAction* PopupMenu::addMenu(QMenu* menu) +{ + if(MusEGlobal::config.scrollableSubMenus) + { + return QMenu::addMenu(menu); + } + return static_cast(getMenu())->addMenu(menu); +} -PopupView::PopupView(QWidget* parent) - : QColumnView(parent) +QMenu* PopupMenu::addMenu(const QString &title) { - _model= new QStandardItemModel(this); - // FIXME: After clearing, then re-filling, no items seen. - // But if setModel is called FOR THE FIRST TIME after clearing the model, - // then it works. Calling setModel any time after that does not work. - setModel(_model); + if(MusEGlobal::config.scrollableSubMenus) + { + return QMenu::addMenu(title); + } + return static_cast(getMenu())->addMenu(title); } -PopupView::~PopupView() +QMenu* PopupMenu::addMenu(const QIcon &icon, const QString &title) { - // Make sure to clear the popup so that any child popups are also deleted ! - //popup->clear(); + if(MusEGlobal::config.scrollableSubMenus) + { + return QMenu::addMenu(icon, title); + } + return static_cast(getMenu())->addMenu(icon, title); } -void PopupView::clear() +//---------------- +// Context menu +//---------------- + +static void PopupMenuSetActionData(QMenu *context_menu, PopupMenu* menu, QAction* menuAction) { - _model->clear(); + const QListactions = context_menu->actions(); + for(int i = 0; i < actions.count(); i++) + { + QVariant e = actions[i]->data(); + // If it's already a PopupMenuContextData, just update the values. + if(e.canConvert()) + actions[i]->setData(QVariant::fromValue(PopupMenuContextData(menu, menuAction, e.value().varValue()))); + // Otherwise bring in the ORIGINAL supplied variant data. + else + actions[i]->setData(QVariant::fromValue(PopupMenuContextData(menu, menuAction, e))); + } } -*/ - + +QMenu* PopupMenu::contextMenu() +{ + if(!_contextMenu) + _contextMenu = new QMenu(this); + return _contextMenu; +} + +void PopupMenu::hideContextMenu() +{ + if(!_contextMenu || !_contextMenu->isVisible()) + return; + _contextMenu->hide(); +} + +void PopupMenu::showContextMenu(const QPoint &pos) +{ + _highlightedAction = activeAction(); + if(!_highlightedAction) + { + PopupMenuSetActionData(_contextMenu, 0, 0); + return; + } + emit aboutToShowContextMenu(this, _highlightedAction, _contextMenu); + PopupMenuSetActionData(_contextMenu, this, _highlightedAction); + if(QMenu* subMenu = _highlightedAction->menu()) + QTimer::singleShot(100, subMenu, SLOT(hide())); + _contextMenu->popup(mapToGlobal(pos)); +} + +PopupMenu* PopupMenu::contextMenuFocus() +{ + return qobject_cast(QApplication::activePopupWidget()); +} + +QAction* PopupMenu::contextMenuFocusAction() +{ + if(PopupMenu* menu = qobject_cast(QApplication::activePopupWidget())) + { + if(!menu->_lastHoveredAction) + return 0; + QVariant var = menu->_lastHoveredAction->data(); + PopupMenuContextData ctx = var.value(); + Q_ASSERT(ctx.menu() == menu); + return ctx.action(); + } + return 0; +} + +void PopupMenu::contextMenuEvent(QContextMenuEvent* e) +{ + if(_contextMenu) + { + if(e->reason() == QContextMenuEvent::Mouse) + showContextMenu(e->pos()); + else if(activeAction()) + showContextMenu(actionGeometry(activeAction()).center()); + + e->accept(); + return; + } + QMenu::contextMenuEvent(e); +} + +void PopupMenu::hideEvent(QHideEvent *e) +{ + if(_contextMenu && _contextMenu->isVisible()) + { + // "we need to block signals here when the ctxMenu is showing + // to prevent the QPopupMenu::activated(int) signal from emitting + // when hiding with a context menu, the user doesn't expect the + // menu to actually do anything. + // since hideEvent gets called very late in the process of hiding + // (deep within QWidget::hide) the activated(int) signal is the + // last signal to be emitted, even after things like aboutToHide() + // AJS" + bool blocked = blockSignals(true); + _contextMenu->hide(); + blockSignals(blocked); + } + QMenu::hideEvent(e); +} + } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/popupmenu.h muse-3.0.2+ds1/muse/widgets/popupmenu.h --- muse-2.1.2/muse/widgets/popupmenu.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/popupmenu.h 2017-12-04 21:01:19.000000000 +0000 @@ -6,7 +6,7 @@ // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) // // PopupMenu sub-class of QMenu created by Tim. -// (C) Copyright 2010-2011 Tim E. Real (terminator356 A T sourceforge D O T net) +// (C) Copyright 2010-2015 Tim E. Real (terminator356 A T sourceforge D O T net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -36,11 +36,12 @@ #include #endif -//#include -//#include +#include class QWidget; class QMouseEvent; +class QContextMenuEvent; +class QHideEvent; class QVariant; class QAction; class QEvent; @@ -60,31 +61,52 @@ int moveDelta; PopupMenu* _cur_menu; // For auto-breakup. int _cur_menu_count; - int _cur_item_width; - int _cur_col_count; + int _max_items_in_breakup; + QMenu* _contextMenu; + QAction* _lastHoveredAction; + QPointer _highlightedAction; + void init(); + void showContextMenu(const QPoint&); // Auto-breakup a too-wide menu. PopupMenu* getMenu(); private slots: - #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL void popHovered(QAction*); + + #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL void timerHandler(); #endif protected: - void mouseReleaseEvent(QMouseEvent *); - bool event(QEvent*); - + virtual void mouseReleaseEvent(QMouseEvent*); + virtual void mousePressEvent(QMouseEvent*); + virtual void contextMenuEvent(QContextMenuEvent*); + virtual void hideEvent(QHideEvent*); + virtual bool event(QEvent*); + virtual void closeUp(); + + // For auto-breakup of a too-wide menu. Virtual. + virtual PopupMenu* cloneMenu(const QString& title, QWidget* parent = 0, bool stayOpen = false) + { return new PopupMenu(title, parent, stayOpen); } + + public: signals: + void aboutToShowContextMenu(PopupMenu* menu, QAction* menuAction, QMenu* ctxMenu); + public: PopupMenu(bool stayOpen); PopupMenu(QWidget* parent=0, bool stayOpen = false); PopupMenu(const QString& title, QWidget* parent = 0, bool stayOpen = false); - //void clear(); + ~PopupMenu(); QAction* findActionFromData(const QVariant&) const; bool stayOpen() const { return _stayOpen; } void clearAllChecks() const; + + QMenu* contextMenu(); + void hideContextMenu(); + static PopupMenu* contextMenuFocus(); + static QAction* contextMenuFocusAction(); // Need to catch these to auto-breakup a too-big menu. QAction* addAction(const QString& text); @@ -92,28 +114,32 @@ QAction* addAction(const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); QAction* addAction(const QIcon& icon, const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); void addAction(QAction* action); + QAction* addMenu(QMenu* menu); + QMenu* addMenu(const QString &title); + QMenu* addMenu(const QIcon &icon, const QString &title); }; - -/* -class PopupView : public QColumnView -{ - Q_OBJECT - private: - QStandardItemModel* _model; +// A handy structure for use with PopupMenu context menu action data. +// The variant holds the ORIGINAL data as set by the programmer. +class PopupMenuContextData { + private: + PopupMenu* _menu; + QAction* _action; + QVariant _variant; - protected: - public: - PopupView(QWidget* parent=0); - ~PopupView(); - - void clear(); - QStandardItemModel* model() { return _model; } + PopupMenuContextData() : _menu(0), _action(0), _variant(0) { } + PopupMenuContextData(const PopupMenuContextData& o) : _menu(o._menu), _action(o._action), _variant(o._variant) { } + PopupMenuContextData(PopupMenu* menu, QAction* action, QVariant var) : _menu(menu), _action(action), _variant(var) { } + + inline PopupMenu* menu() const { return _menu; } + inline QAction* action() const { return _action; } + inline QVariant varValue() const { return _variant; } }; -*/ - + } // namespace MusEGui +Q_DECLARE_METATYPE(MusEGui::PopupMenuContextData) + #endif diff -Nru muse-2.1.2/muse/widgets/poslabel.cpp muse-3.0.2+ds1/muse/widgets/poslabel.cpp --- muse-2.1.2/muse/widgets/poslabel.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/poslabel.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -106,14 +106,20 @@ } int frame = int(rest); int subframe = int((rest-frame)*100); - s.sprintf("%03d:%02d:%02d:%02d", min, sec, frame, subframe); + s = QString("%1:%2:%3:%4") + .arg(min, 3, 10, QLatin1Char('0')) + .arg(sec, 2, 10, QLatin1Char('0')) + .arg(frame, 2, 10, QLatin1Char('0')) + .arg(subframe, 2, 10, QLatin1Char('0')); } else { int bar, beat; unsigned tick; AL::sigmap.tickValues(_tickValue, &bar, &beat, &tick); - //s.sprintf("%04d.%02d.%03ud", bar+1, beat+1, tick); - s.sprintf("%04d.%02d.%03u", bar+1, beat+1, tick); + s = QString("%1.%2.%3") + .arg(bar + 1, 4, 10, QLatin1Char('0')) + .arg(beat + 1, 2, 10, QLatin1Char('0')) + .arg(tick, 3, 10, QLatin1Char('0')); } setText(s); } diff -Nru muse-2.1.2/muse/widgets/projectcreateimpl.cpp muse-3.0.2+ds1/muse/widgets/projectcreateimpl.cpp --- muse-2.1.2/muse/widgets/projectcreateimpl.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/projectcreateimpl.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -93,6 +93,7 @@ //commentEdit->setPlaceholderText(""); #endif updateDirectoryPath(); + projectNameEdit->setFocus(); show(); } @@ -192,6 +193,7 @@ { MusEGlobal::config.projectStoreInFolder = createFolderCheckbox->isChecked(); //MusEGlobal::config.projectBaseFolder = directoryPath; + // Save to config file. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //MusEGlobal::muse->changeConfig(true); emit accept(); } @@ -199,7 +201,8 @@ void ProjectCreateImpl::createProjFolderChanged() { //MusEGlobal::config.projectStoreInFolder = createFolderCheckbox->isChecked(); - //MusEGlobal::muse->changeConfig(true); // Save to config file. + // Save to config file. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + //MusEGlobal::muse->changeConfig(true); updateDirectoryPath(); } @@ -210,7 +213,8 @@ { directoryPath = dir; MusEGlobal::config.projectBaseFolder = dir; - MusEGlobal::muse->changeConfig(true); // Save to config file. + // Save to config file. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + MusEGlobal::muse->changeConfig(true); updateDirectoryPath(); } } diff -Nru muse-2.1.2/muse/widgets/routepopup.cpp muse-3.0.2+ds1/muse/widgets/routepopup.cpp --- muse-2.1.2/muse/widgets/routepopup.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/routepopup.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // // RoutePopupMenu.cpp -// (C) Copyright 2011 Tim E. Real (terminator356 A T sourceforge D O T net) +// (C) Copyright 2011-2015 Tim E. Real (terminator356 A T sourceforge D O T net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -20,8 +20,18 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= +#include +#include +#include +#include +#include +#include +#include +#include + #include "app.h" #include "routepopup.h" +#include "gconfig.h" #include "midiport.h" #include "mididev.h" #include "audio.h" @@ -29,85 +39,225 @@ #include "song.h" #include "track.h" #include "synth.h" -#include "route.h" #include "icons.h" #include "menutitleitem.h" #include "popupmenu.h" +#include "operations.h" -#include "custom_widget_actions.h" #include "globaldefs.h" +#include "gconfig.h" + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); +#define DEBUG_PRST_ROUTES_2(dev, format, args...) // fprintf(dev, format, ##args); + #define _USE_CUSTOM_WIDGET_ACTIONS_ +// REMOVE Tim. Persistent routes. Added. Make this permanent later if it works OK and makes good sense. +#define _USE_SIMPLIFIED_SOLO_CHAIN_ + +// REMOVE Tim. Persistent routes. Added. Make this permanent later if it works OK and makes good sense. +#define _USE_MIDI_ROUTE_PER_CHANNEL_ + +// Undefine if and when multiple output routes are added to midi tracks. +#define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + +#define _SHOW_CANONICAL_NAMES_ 0x1000 +#define _SHOW_FIRST_ALIASES_ 0x1001 +#define _SHOW_SECOND_ALIASES_ 0x1002 + +#define _ALIASES_WIDGET_ACTION_ 0x2000 +#define _OPEN_MIDI_CONFIG_ 0x2001 +#define _OPEN_ROUTING_DIALOG_ 0x2002 +#define _GROUPING_CHANNELS_WIDGET_ACTION_ 0x2003 + namespace MusEGui { + +void RoutePopupMenu::addGroupingChannelsAction(PopupMenu* lb) +{ + RoutingMatrixWidgetAction* name_wa = new RoutingMatrixWidgetAction(2, 0, 0, this, tr("Channel grouping:")); + name_wa->setArrayStayOpen(true); + name_wa->setData(_GROUPING_CHANNELS_WIDGET_ACTION_); + name_wa->array()->setColumnsExclusive(true); + name_wa->array()->setExclusiveToggle(false); + name_wa->array()->headerSetVisible(false); + name_wa->array()->setText(0, tr("Mono ")); + name_wa->array()->setText(1, tr("Stereo")); + switch(MusEGlobal::config.routerGroupingChannels) + { + case 1: + name_wa->array()->setValue(0, true); + break; + case 2: + name_wa->array()->setValue(1, true); + break; + default: + break; + } + // Must rebuild array after text changes. + name_wa->updateChannelArray(); + lb->addAction(name_wa); + lb->addSeparator(); +} + //--------------------------------------------------------- // addMenuItem //--------------------------------------------------------- -int RoutePopupMenu::addMenuItem(MusECore::AudioTrack* track, MusECore::Track* route_track, PopupMenu* lb, int id, int channel, int channels, bool isOutput) +int RoutePopupMenu::addMenuItem(MusECore::AudioTrack* track, MusECore::Track* route_track, PopupMenu* lb, + int id, int channel, int /*channels*/, bool isOutput) { - // totalInChannels is only used by syntis. - //int toch = ((MusECore::AudioTrack*)track)->totalOutChannels(); - // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. - //if(track->channels() == 1) - // toch = 1; - - // Don't add the last stray mono route if the track is stereo. - //if(route_track->channels() > 1 && (channel+1 == chans)) - // return id; - - MusECore::RouteList* rl = isOutput ? track->outRoutes() : track->inRoutes(); - - QAction* act; - - QString s(route_track->name()); + if(route_track->isMidiTrack()) + return ++id; - act = lb->addAction(s); - act->setCheckable(true); - - int ach = channel; - int bch = -1; + MusECore::RouteList* rl = isOutput ? track->outRoutes() : track->inRoutes(); - MusECore::Route r(route_track, isOutput ? ach : bch, channels); + const bool circ_route = (isOutput ? track : route_track)->isCircularRoute(isOutput ? route_track : track); - r.remoteChannel = isOutput ? bch : ach; + MusECore::RouteCapabilitiesStruct t_caps = track->routeCapabilities(); + MusECore::RouteCapabilitiesStruct rt_caps = route_track->routeCapabilities(); + const int t_chans = isOutput ? t_caps._trackChannels._outChannels : t_caps._trackChannels._inChannels; + const int rt_chans = isOutput ? rt_caps._trackChannels._inChannels : rt_caps._trackChannels._outChannels; - act->setData(qVariantFromValue(r)); + // Support Audio Output Track to Audio Input Track 'Omni' routes. + if(isOutput && track->type() == MusECore::Track::AUDIO_OUTPUT && route_track->type() == MusECore::Track::AUDIO_INPUT) + { + if(channel != -1 || !t_caps._trackChannels._outRoutable || !rt_caps._trackChannels._inRoutable) + return ++id; + } + else + if(!isOutput && track->type() == MusECore::Track::AUDIO_INPUT && route_track->type() == MusECore::Track::AUDIO_OUTPUT) + { + if(channel != -1 || !t_caps._trackChannels._inRoutable || !rt_caps._trackChannels._outRoutable) + return ++id; + } + else + { + if(t_chans <= 0) + return ++id; + if(rt_chans <= 0) + return ++id; + } - for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) +#ifndef _USE_CUSTOM_WIDGET_ACTIONS_ + + // Is it an omnibus route? + if(channel == -1) { - if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == route_track && ir->remoteChannel == r.remoteChannel) + QAction* act = lb->addAction(route_track->name()); + act->setCheckable(true); + MusECore::Route r(route_track, -1); + act->setData(QVariant::fromValue(r)); + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { - int tcompch = r.channel; - if(tcompch == -1) - tcompch = 0; - int tcompchs = r.channels; - if(tcompchs == -1) - tcompchs = isOutput ? track->channels() : route_track->channels(); - - int compch = ir->channel; - if(compch == -1) - compch = 0; - int compchs = ir->channels; - if(compchs == -1) - compchs = isOutput ? track->channels() : ir->track->channels(); - - if(compch == tcompch && compchs == tcompchs) + if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == route_track && + ir->remoteChannel == r.channel && + ir->channel == r.remoteChannel && + ir->channels == r.channels) { act->setChecked(true); break; - } - } + } + } + if(!act->isChecked() && circ_route) // If circular route exists, allow user to break it, otherwise forbidden. + act->setEnabled(false); } - - if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. + else +#endif + { - if( (isOutput ? track : route_track)->isCircularRoute(isOutput ? route_track : track) ) + +#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ + + QAction* act = lb->addAction(route_track->name()); + act->setCheckable(true); + MusECore::Route r(route_track, -1); + act->setData(QVariant::fromValue(r)); + if(rl->contains(r)) + act->setChecked(true); + + if(rt_chans != 0 && t_chans != 0) + { + RoutePopupMenu* subp = new RoutePopupMenu(_route, this, isOutput, _broadcastChanges); + subp->addAction(new MenuTitleItem(tr("Channels"), this)); + act->setMenu(subp); + QActionGroup* act_group = new QActionGroup(this); + act_group->setExclusive(false); + for(int row = 0; row < rt_chans; ++row) + { + //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(t_chans, redLedIcon, darkRedLedIcon, this, QString::number(row + 1)); + RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(t_chans, 0, 0, this, QString::number(row + 1)); + wa->setFont(wa->smallFont()); + wa->array()->headerSetVisible(row == 0); + r.channel = row; + wa->setData(QVariant::fromValue(r)); // Ignore the routing channel and channels - our action holds the channels. + for(int col = 0; col < t_chans; ++col) + { + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == route_track && + ir->remoteChannel == row && + ir->channel == col && + ir->channels == 1) + { + wa->array()->setValue(col, true); + break; + } + } + } + // Must rebuild array after text changes. + wa->updateChannelArray(); +// subp->addAction(wa); + act_group->addAction(wa); + } + subp->addActions(act_group->actions()); + } + + if(!act->isChecked() && circ_route) // If circular route exists, allow user to break it, otherwise forbidden. act->setEnabled(false); + + lb->addAction(act); + +#else + + // It's not an omnibus route. Add the individual channels... + PopupMenu* subp = new PopupMenu(this, true); + subp->setTitle(route_track->name()); + subp->addAction(new MenuTitleItem(tr("Channels"), this)); + QActionGroup* act_group = new QActionGroup(this); + act_group->setExclusive(false); + for(int i = 0; i < rt_chans; ++i) + { +// QAction* act = subp->addAction(QString::number(i + 1)); + QAction* act = act_group->addAction(QString::number(i + 1)); + act->setCheckable(true); + MusECore::Route r(route_track, i, 1); + r.remoteChannel = channel; + act->setData(QVariant::fromValue(r)); + + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == route_track && + ir->remoteChannel == r.channel && + ir->channel == r.remoteChannel && + ir->channels == r.channels) + { + act->setChecked(true); + break; + } + } + if(!act->isChecked() && circ_route) // If circular route exists, allow user to break it, otherwise forbidden. + act->setEnabled(false); + } + subp->addActions(act_group->actions()); + lb->addMenu(subp); +#endif + } - - return ++id; -} + + return ++id; +} //--------------------------------------------------------- // addAuxPorts @@ -189,628 +339,2151 @@ return id; } -//--------------------------------------------------------- -// addSyntiPorts -//--------------------------------------------------------- - -int RoutePopupMenu::addSyntiPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, - int channel, int channels, bool isOutput) +void RoutePopupMenu::addMidiTracks(MusECore::Track* t, PopupMenu* pup, bool isOutput) { - MusECore::RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); - - QAction* act; - - MusECore::SynthIList* al = MusEGlobal::song->syntis(); - for (MusECore::iSynthI i = al->begin(); i != al->end(); ++i) - { - MusECore::Track* track = *i; - if (t == track) - continue; - int toch = ((MusECore::AudioTrack*)track)->totalOutChannels(); - // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. - if(track->channels() == 1) - toch = 1; - - // totalInChannels is only used by syntis. - int chans = (!isOutput || track->type() != MusECore::Track::AUDIO_SOFTSYNTH) ? toch : ((MusECore::AudioTrack*)track)->totalInChannels(); - - int tchans = (channels != -1) ? channels: t->channels(); - if(tchans == 2) - { - // Ignore odd numbered left-over mono channel. - //chans = chans & ~1; - //if(chans != 0) - chans -= 1; - } - - if(chans > 0) - { - PopupMenu* chpup = new PopupMenu(lb, true); - chpup->setTitle(track->name()); - for(int ch = 0; ch < chans; ++ch) - { - char buffer[128]; - if(tchans == 2) - snprintf(buffer, 128, "%s %d,%d", tr("Channel").toLatin1().constData(), ch+1, ch+2); - else - snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), ch+1); - act = chpup->addAction(QString(buffer)); - act->setCheckable(true); - - int ach = (channel == -1) ? ch : channel; - int bch = (channel == -1) ? -1 : ch; - - MusECore::Route rt(track, (t->type() != MusECore::Track::AUDIO_SOFTSYNTH || isOutput) ? ach : bch, tchans); - rt.remoteChannel = (t->type() != MusECore::Track::AUDIO_SOFTSYNTH || isOutput) ? bch : ach; - - act->setData(qVariantFromValue(rt)); - - for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel) - { - int tcompch = rt.channel; - if(tcompch == -1) - tcompch = 0; - int tcompchs = rt.channels; - if(tcompchs == -1) - tcompchs = isOutput ? t->channels() : track->channels(); - - int compch = ir->channel; - if(compch == -1) - compch = 0; - int compchs = ir->channels; - if(compchs == -1) - compchs = isOutput ? t->channels() : ir->track->channels(); - - if(compch == tcompch && compchs == tcompchs) - { - act->setChecked(true); - break; - } - } - } - - if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. - { - if( (isOutput ? t : track)->isCircularRoute(isOutput ? track : t) ) - act->setEnabled(false); - } - - ++id; - } - - lb->addMenu(chpup); - } - } - return id; + const MusECore::RouteList* const rl = isOutput ? t->outRoutes() : t->inRoutes(); + const MusECore::MidiTrackList* const mtracks = MusEGlobal::song->midis(); + for(MusECore::ciMidiTrack imt = mtracks->begin(); imt != mtracks->end(); ++imt) + { + MusECore::MidiTrack* const mt = *imt; + QAction* act = pup->addAction(mt->name()); + act->setCheckable(true); + const MusECore::Route r(mt, -1); + act->setData(QVariant::fromValue(r)); + if(rl->contains(r)) + act->setChecked(true); + } } -//--------------------------------------------------------- -// addMultiChannelOutPorts -//--------------------------------------------------------- - -int RoutePopupMenu::addMultiChannelPorts(MusECore::AudioTrack* t, PopupMenu* pup, int id, bool isOutput) +void RoutePopupMenu::addMidiPorts(MusECore::Track* t, PopupMenu* pup, bool isOutput, bool show_synths, bool want_writable) { - int toch = t->totalOutChannels(); - // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. - if(t->channels() == 1) - toch = 1; - - // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. - // totalInChannels is only used by syntis. - int chans = (isOutput || t->type() != MusECore::Track::AUDIO_SOFTSYNTH) ? toch : t->totalInChannels(); - if(chans > 1) - pup->addAction(new MenuTitleItem("", pup)); - - // - // If it's more than one channel, create a sub-menu. If it's just one channel, don't bother with a sub-menu... - // +#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ - PopupMenu* chpup = pup; + const MusECore::RouteList* const rl = isOutput ? t->outRoutes() : t->inRoutes(); + MusECore::MidiDevice* md; - for(int ch = 0; ch < chans; ++ch) + bool is_first_pass = true; + QActionGroup* act_group = 0; + // Order the entire listing by device type. + for(int dtype = 0; dtype <= MusECore::MidiDevice::SYNTH_MIDI; ++dtype) { - // If more than one channel, create the sub-menu. - if(chans > 1) - chpup = new PopupMenu(pup, true); - + // Currently only midi port output to midi track input supports 'Omni' routes. +#ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(isOutput) { - switch(t->type()) + // Count the number of required rows. + int rows = 0; + for(int i = 0; i < MIDI_PORTS; ++i) + { + md = MusEGlobal::midiPorts[i].device(); + // This is desirable, but could lead to 'hidden' routes unless we add more support such as removing the existing routes when user changes flags. + // So for now, just list all valid ports whether read or write. + //if(!md) + // continue; + if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an output click we are looking for midi writeable here. + continue; + // Do not list synth devices! + if(!show_synths && md->isSynti()) + continue; + // We only want the sorted device type. + if(md->deviceType() != dtype) + continue; + ++rows; + } + if(rows == 0) + continue; + + if(is_first_pass) + { + is_first_pass = false; + act_group = new QActionGroup(this); + act_group->setExclusive(true); + } + + int row = 0; + MusECore::Route r(-1); // Midi port route. + for(int i = 0; i < MIDI_PORTS; ++i) { + md = MusEGlobal::midiPorts[i].device(); + // This is desirable, but could lead to 'hidden' routes unless we add more support such as removing the existing routes when user changes flags. + // So for now, just list all valid ports whether read or write. + //if(!md) + // continue; + if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an output click we are looking for midi writeable here. + continue; + // Do not list synth devices! + if(!show_synths && md->isSynti()) + continue; + // We only want the sorted device type. + if(md->deviceType() != dtype) + continue; - case MusECore::Track::AUDIO_INPUT: - case MusECore::Track::WAVE: - case MusECore::Track::AUDIO_GROUP: - case MusECore::Track::AUDIO_SOFTSYNTH: - case MusECore::Track::AUDIO_AUX: - id = addWavePorts(t, chpup, id, ch, 1, isOutput); - id = addOutPorts(t, chpup, id, ch, 1, isOutput); - id = addGroupPorts(t, chpup, id, ch, 1, isOutput); - id = addSyntiPorts(t, chpup, id, ch, 1, isOutput); - break; - default: - break; + //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(MIDI_CHANNELS, redLedIcon, darkRedLedIcon, this, QString("%1:%2").arg(i + 1).arg(md->name())); + RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(MIDI_CHANNELS, 0, 0, this, QString("%1:%2").arg(i + 1).arg(md->name())); + if(row == 0) + { + switch(dtype) + { + case MusECore::MidiDevice::ALSA_MIDI: + wa->array()->headerSetTitle(tr("ALSA devices")); + break; + + case MusECore::MidiDevice::JACK_MIDI: + wa->array()->headerSetTitle(tr("JACK devices")); + break; + + case MusECore::MidiDevice::SYNTH_MIDI: + wa->array()->headerSetTitle(tr("Synth devices")); + break; + } + wa->array()->setArrayTitle(tr("Channels")); + wa->array()->headerSetVisible(true); + } + else + wa->array()->headerSetVisible(false); + + r.midiPort = i; + wa->setData(QVariant::fromValue(r)); + + wa->array()->setColumnsExclusive(true); + MusECore::MidiTrack* mt = static_cast(t); + if(i == mt->outPort()) + wa->array()->setValue(mt->outChannel(), true); + // Must rebuild array after text changes. + wa->updateChannelArray(); + + // Make it easy for the user: Show the device's jack ports as well. + // This is reasonable for midi devices since they are hidden away. + // (Midi devices were made tracks, and midi ports eliminated, in the old MusE-2 muse_evolution branch!) + switch(md->deviceType()) + { + case MusECore::MidiDevice::JACK_MIDI: + { + const MusECore::Route md_r(md, -1); + RoutePopupMenu* subp = new RoutePopupMenu(md_r, this, isOutput, _broadcastChanges); + addJackPorts(md_r, subp); + wa->setMenu(subp); + } + break; + + default: + break; + } + + act_group->addAction(wa); + ++row; } + pup->addActions(act_group->actions()); } - else + else + +#endif // _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + { - switch(t->type()) + // Count the number of required rows. + int rows = 0; + for(int i = 0; i < MIDI_PORTS; ++i) { - - case MusECore::Track::AUDIO_OUTPUT: - id = addWavePorts(t, chpup, id, ch, 1, isOutput); - id = addInPorts(t, chpup, id, ch, 1, isOutput); - id = addGroupPorts(t, chpup, id, ch, 1, isOutput); - id = addAuxPorts(t, chpup, id, ch, 1, isOutput); - id = addSyntiPorts(t, chpup, id, ch, 1, isOutput); - break; - case MusECore::Track::WAVE: - case MusECore::Track::AUDIO_SOFTSYNTH: - case MusECore::Track::AUDIO_GROUP: - id = addWavePorts(t, chpup, id, ch, 1, isOutput); - id = addInPorts(t, chpup, id, ch, 1, isOutput); - id = addGroupPorts(t, chpup, id, ch, 1, isOutput); - id = addAuxPorts(t, chpup, id, ch, 1, isOutput); - id = addSyntiPorts(t, chpup, id, ch, 1, isOutput); - break; - default: - break; + md = MusEGlobal::midiPorts[i].device(); + // This is desirable, but could lead to 'hidden' routes unless we add more support such as removing the existing routes when user changes flags. + // So for now, just list all valid ports whether read or write. + //if(!md) + // continue; + if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an input click we are looking for midi readable here. + continue; + // Do not list synth devices! + if(!show_synths && md->isSynti()) + continue; + // We only want the sorted device type. + if(md->deviceType() != dtype) + continue; + ++rows; } - } + if(rows == 0) + continue; - // If more than one channel, add the created sub-menu. - if(chans > 1) - { - char buffer[128]; - snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), ch+1); - chpup->setTitle(QString(buffer)); - pup->addMenu(chpup); - } - } - - // For stereo listing, ignore odd numbered left-over channels. - chans -= 1; - if(chans > 0) - { - // Ignore odd numbered left-over channels. - //int schans = (chans & ~1) - 1; - - pup->addSeparator(); - pup->addAction(new MenuTitleItem("", pup)); - - // - // If it's more than two channels, create a sub-menu. If it's just two channels, don't bother with a sub-menu... - // - - chpup = pup; - if(chans <= 2) - // Just do one iteration. - chans = 1; - - for(int ch = 0; ch < chans; ++ch) - { - // If more than two channels, create the sub-menu. - if(chans > 2) - chpup = new PopupMenu(pup, true); - - if(isOutput) - { - switch(t->type()) - { - case MusECore::Track::AUDIO_INPUT: - case MusECore::Track::WAVE: - case MusECore::Track::AUDIO_GROUP: - case MusECore::Track::AUDIO_SOFTSYNTH: - case MusECore::Track::AUDIO_AUX: - id = addWavePorts(t, chpup, id, ch, 2, isOutput); - id = addOutPorts(t, chpup, id, ch, 2, isOutput); - id = addGroupPorts(t, chpup, id, ch, 2, isOutput); - id = addSyntiPorts(t, chpup, id, ch, 2, isOutput); - break; + // It's an input. Allow 'Omni' routes'... + int row = 0; + for(int i = 0; i < MIDI_PORTS; ++i) + { + md = MusEGlobal::midiPorts[i].device(); + // This is desirable, but could lead to 'hidden' routes unless we add more support such as removing the existing routes when user changes flags. + // So for now, just list all valid ports whether read or write. + //if(!md) + // continue; + if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an input click we are looking for midi readable here. + continue; + // Do not list synth devices! + if(!show_synths && md->isSynti()) + continue; + // We only want the sorted device type. + if(md->deviceType() != dtype) + continue; + + MusECore::Route r(i, -1); + //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(MIDI_CHANNELS, redLedIcon, darkRedLedIcon, this, QString("%1:%2").arg(i + 1).arg(md->name())); + RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(MIDI_CHANNELS, 0, 0, this, QString("%1:%2").arg(i + 1).arg(md->name())); + if(row == 0) + { + wa->array()->setCheckBoxTitle(tr("Omni")); + switch(dtype) + { + case MusECore::MidiDevice::ALSA_MIDI: + wa->array()->headerSetTitle(tr("ALSA devices")); + break; + + case MusECore::MidiDevice::JACK_MIDI: + wa->array()->headerSetTitle(tr("JACK devices")); + break; + + case MusECore::MidiDevice::SYNTH_MIDI: + wa->array()->headerSetTitle(tr("Synth devices")); + break; + } + wa->array()->setArrayTitle(tr("Channels")); + wa->array()->headerSetVisible(true); + } + else + wa->array()->headerSetVisible(false); + + wa->setHasCheckBox(true); + if(rl->contains(r)) + wa->setCheckBoxChecked(true); + wa->setData(QVariant::fromValue(r)); // Ignore the routing channel and channels - our action holds the channels. + +#ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ + for(int col = 0; col < MIDI_CHANNELS; ++col) + { + r.channel = col; + if(rl->contains(r)) + wa->array()->setValue(col, true); + } +#else // _USE_MIDI_ROUTE_PER_CHANNEL_ + int chans = 0; + // Is there already a route? + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + switch(ir->type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + if(ir->midiPort == i) + chans = ir->channel; // Grab the channels. + break; + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; + } + if(chans != 0) + break; + } + if(chans != 0 && chans != -1) + { + for(int col = 0; col < MIDI_CHANNELS; ++col) + { + if(chans & (1 << col)) + wa->array()->setValue(col, true); + } + } +#endif // _USE_MIDI_ROUTE_PER_CHANNEL_ + + // Must rebuild array after text changes. + wa->updateChannelArray(); + + // Make it easy for the user: Show the device's jack ports as well. + // This is reasonable for midi devices since they are hidden away. + // (Midi devices were made tracks, and midi ports eliminated, in the old MusE-2 muse_evolution branch!) + switch(md->deviceType()) + { + case MusECore::MidiDevice::JACK_MIDI: + { + //PopupMenu* subp = new PopupMenu(this, true); + const MusECore::Route md_r(md, -1); + RoutePopupMenu* subp = new RoutePopupMenu(md_r, this, isOutput, _broadcastChanges); + addJackPorts(md_r, subp); + wa->setMenu(subp); + } + break; + default: - break; + break; } - } - else + + pup->addAction(wa); + ++row; + } + } + +#else // _USE_CUSTOM_WIDGET_ACTIONS_ + + pup->addAction(new MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Output port/device")), pup)); + for(int i = 0; i < MIDI_PORTS; ++i) + { + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; + MusECore::MidiDevice* md = mp->device(); + + // This is desirable, but could lead to 'hidden' routes unless we add more support + // such as removing the existing routes when user changes flags. + // So for now, just list all valid ports whether read or write. + //if(!md) + // continue; + if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an input click we are looking for midi outputs here. + continue; + + // Do not list synth devices! + if(!show_synths && md->isSynti()) + continue; + // We only want the sorted device type. + if(md->deviceType() != dtype) + continue; + + MusECore::RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); + + int chanmask = 0; + // To reduce number of routes required, from one per channel to just one containing a channel mask. + // Look for the first route to this midi port. There should always be only a single route for each midi port, now. + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { - switch(t->type()) + if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) { - case MusECore::Track::AUDIO_OUTPUT: - id = addWavePorts(t, chpup, id, ch, 2, isOutput); - id = addInPorts(t, chpup, id, ch, 2, isOutput); - id = addGroupPorts(t, chpup, id, ch, 2, isOutput); - id = addAuxPorts(t, chpup, id, ch, 2, isOutput); - id = addSyntiPorts(t, chpup, id, ch, 2, isOutput); - break; - case MusECore::Track::WAVE: - case MusECore::Track::AUDIO_SOFTSYNTH: - case MusECore::Track::AUDIO_GROUP: - id = addWavePorts(t, chpup, id, ch, 2, isOutput); - id = addInPorts(t, chpup, id, ch, 2, isOutput); - id = addGroupPorts(t, chpup, id, ch, 2, isOutput); - id = addAuxPorts(t, chpup, id, ch, 2, isOutput); - id = addSyntiPorts(t, chpup, id, ch, 2, isOutput); - break; - default: - break; + // We have a route to the midi port. Grab the channel mask. + chanmask = ir->channel; + break; } } - // If more than two channels, add the created sub-menu. - if(chans > 2) + PopupMenu* subp = new PopupMenu(pup, true); + subp->setTitle(md->name()); + QAction* act; + + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { - char buffer[128]; - snprintf(buffer, 128, "%s %d,%d", tr("Channel").toLatin1().constData(), ch+1, ch+2); - chpup->setTitle(QString(buffer)); - pup->addMenu(chpup); - } - } + act = subp->addAction(QString("Channel %1").arg(ch+1)); + act->setCheckable(true); + + int chbit = 1 << ch; + MusECore::Route srcRoute(i, chbit); // In accordance with channel mask, use the bit position. + + act->setData(QVariant::fromValue(srcRoute)); + + if(chanmask & chbit) // Is the channel already set? Show item check mark. + act->setChecked(true); + } + act = subp->addAction(QString("Toggle all")); + //act->setCheckable(true); + MusECore::Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. + act->setData(QVariant::fromValue(togRoute)); + pup->addMenu(subp); + } + +#endif // _USE_CUSTOM_WIDGET_ACTIONS_ + } - return id; + return; } //--------------------------------------------------------- -// nonSyntiTrackAddSyntis +// addSynthPorts //--------------------------------------------------------- -int RoutePopupMenu::nonSyntiTrackAddSyntis(MusECore::AudioTrack* t, PopupMenu* lb, int id, bool isOutput) +int RoutePopupMenu::addSynthPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { - MusECore::RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); - - QAction* act; MusECore::SynthIList* al = MusEGlobal::song->syntis(); - for (MusECore::iSynthI i = al->begin(); i != al->end(); ++i) - { + for (MusECore::iSynthI i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; - - int toch = ((MusECore::AudioTrack*)track)->totalOutChannels(); - // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. - if(track->channels() == 1) - toch = 1; - - // totalInChannels is only used by syntis. - int chans = (!isOutput || track->type() != MusECore::Track::AUDIO_SOFTSYNTH) ? toch : ((MusECore::AudioTrack*)track)->totalInChannels(); - - //int schans = synti->channels(); - //if(schans < chans) - // chans = schans; -// int tchans = (channels != -1) ? channels: t->channels(); -// if(tchans == 2) -// { - // Ignore odd numbered left-over mono channel. - //chans = chans & ~1; - //if(chans != 0) -// chans -= 1; -// } - //int tchans = (channels != -1) ? channels: t->channels(); - - if(chans > 0) - { - PopupMenu* chpup = new PopupMenu(lb, true); - chpup->setTitle(track->name()); - if(chans > 1) - chpup->addAction(new MenuTitleItem("", chpup)); - - for(int ch = 0; ch < chans; ++ch) - { - char buffer[128]; - snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), ch+1); - act = chpup->addAction(QString(buffer)); - act->setCheckable(true); - - int ach = ch; - int bch = -1; - - MusECore::Route rt(track, isOutput ? bch : ach, 1); - - rt.remoteChannel = isOutput ? ach : bch; - - act->setData(qVariantFromValue(rt)); - - for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel) - { - int tcompch = rt.channel; - if(tcompch == -1) - tcompch = 0; - int tcompchs = rt.channels; - if(tcompchs == -1) - tcompchs = isOutput ? t->channels() : track->channels(); - - int compch = ir->channel; - if(compch == -1) - compch = 0; - int compchs = ir->channels; - if(compchs == -1) - compchs = isOutput ? t->channels() : ir->track->channels(); - - if(compch == tcompch && compchs == tcompchs) - { - act->setChecked(true); - break; - } - } - } - - if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. - { - if( (isOutput ? t : track)->isCircularRoute(isOutput ? track : t) ) - act->setEnabled(false); - } - - ++id; - } - - chans -= 1; - if(chans > 0) - { - // Ignore odd numbered left-over channels. - //int schans = (chans & ~1) - 1; - - chpup->addSeparator(); - chpup->addAction(new MenuTitleItem("", chpup)); - - for(int ch = 0; ch < chans; ++ch) - { - char buffer[128]; - snprintf(buffer, 128, "%s %d,%d", tr("Channel").toLatin1().constData(), ch+1, ch+2); - act = chpup->addAction(QString(buffer)); - act->setCheckable(true); - - int ach = ch; - int bch = -1; - - MusECore::Route rt(track, isOutput ? bch : ach, 2); - - rt.remoteChannel = isOutput ? ach : bch; - - act->setData(qVariantFromValue(rt)); - - for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel) - { - int tcompch = rt.channel; - if(tcompch == -1) - tcompch = 0; - int tcompchs = rt.channels; - if(tcompchs == -1) - tcompchs = isOutput ? t->channels() : track->channels(); - - int compch = ir->channel; - if(compch == -1) - compch = 0; - int compchs = ir->channels; - if(compchs == -1) - compchs = isOutput ? t->channels() : ir->track->channels(); - - if(compch == tcompch && compchs == tcompchs) - { - act->setChecked(true); - break; - } - } - } - - if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. - { - if( (isOutput ? t : track)->isCircularRoute(isOutput ? track : t) ) - act->setEnabled(false); - } - - ++id; - } - } - - lb->addMenu(chpup); + id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } - } return id; } //--------------------------------------------------------- -// addMidiPorts +// addJackPorts //--------------------------------------------------------- -int RoutePopupMenu::addMidiPorts(MusECore::AudioTrack* t, PopupMenu* pup, int id, bool isOutput) +void RoutePopupMenu::addJackPorts(const MusECore::Route& route, PopupMenu* lb) { + if(!MusEGlobal::checkAudioDevice()) + return; -#ifndef _USE_CUSTOM_WIDGET_ACTIONS_ - - QAction* act; - -#endif + MusECore::RouteList* rl = 0; + int channels = -1; + std::list ol; + MusECore::RouteCapabilitiesStruct rcaps; + switch(route.type) + { + case MusECore::Route::TRACK_ROUTE: + ol = _isOutMenu ? MusEGlobal::audioDevice->inputPorts() : MusEGlobal::audioDevice->outputPorts(); + rl = _isOutMenu ? route.track->outRoutes() : route.track->inRoutes(); + rcaps = route.track->routeCapabilities(); + channels = _isOutMenu ? rcaps._jackChannels._outChannels : rcaps._jackChannels._inChannels; + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + ol = _isOutMenu ? MusEGlobal::audioDevice->inputPorts(true) : MusEGlobal::audioDevice->outputPorts(true); + rl = _isOutMenu ? route.device->outRoutes() : route.device->inRoutes(); + break; + + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + return; + break; + } -#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ - - PixmapButtonsHeaderWidgetAction* wa_hdr = new PixmapButtonsHeaderWidgetAction("Output port/device", darkRedLedIcon, MIDI_CHANNELS, pup); - pup->addAction(wa_hdr); - ++id; -#else - pup->addAction(new MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Output port/device")), pup)); -#endif - - for(int i = 0; i < MIDI_PORTS; ++i) + const int sz = ol.size(); + if(sz != 0) { - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; - MusECore::MidiDevice* md = mp->device(); - // This is desirable, but could lead to 'hidden' routes unless we add more support - // such as removing the existing routes when user changes flags. - // So for now, just list all valid ports whether read or write. - //if(!md) - // continue; - if(!md || !(md->rwFlags() & (isOutput ? 2 : 1))) // If this is an input click we are looking for midi outputs here. - continue; - - // Do not list synth devices! - if(md->isSynti()) - continue; - - MusECore::RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); +#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ - int chanmask = 0; - // To reduce number of routes required, from one per channel to just one containing a channel mask. - // Look for the first route to this midi port. There should always be only a single route for each midi port, now. - for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + //RoutingMatrixWidgetAction* name_wa = new RoutingMatrixWidgetAction(2, redLedIcon, darkRedLedIcon, this, tr("Show aliases:")); + RoutingMatrixWidgetAction* name_wa = new RoutingMatrixWidgetAction(2, 0, 0, this, tr("Show aliases:")); + name_wa->setArrayStayOpen(true); + name_wa->setData(_ALIASES_WIDGET_ACTION_); + name_wa->array()->setColumnsExclusive(true); + name_wa->array()->setExclusiveToggle(true); + name_wa->array()->headerSetVisible(false); + name_wa->array()->setText(0, tr("First ")); + name_wa->array()->setText(1, tr("Second")); + switch(MusEGlobal::config.preferredRouteNameOrAlias) { - if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) - { - // We have a route to the midi port. Grab the channel mask. - chanmask = ir->channel; - break; - } + case MusEGlobal::RoutePreferFirstAlias: + name_wa->array()->setValue(0, true); + break; + case MusEGlobal::RoutePreferSecondAlias: + name_wa->array()->setValue(1, true); + break; + case MusEGlobal::RoutePreferCanonicalName: + break; } + // Must rebuild array after text changes. + name_wa->updateChannelArray(); + lb->addAction(name_wa); + lb->addSeparator(); +#else + QActionGroup* act_grp = new QActionGroup(this); + act_grp->setExclusive(true); + act = act_grp->addAction(tr("Show names")); + act->setCheckable(true); + act->setData(_SHOW_CANONICAL_NAMES_); + if(MusEGlobal::config.preferredRouteNameOrAlias == MusEGlobal::RoutePreferCanonicalName) + act->setChecked(true); + act = act_grp->addAction(tr("Show first aliases")); + act->setCheckable(true); + act->setData(_SHOW_FIRST_ALIASES_); + if(MusEGlobal::config.preferredRouteNameOrAlias == MusEGlobal::RoutePreferFirstAlias) + act->setChecked(true); + act = act_grp->addAction(tr("Show second aliases")); + act->setCheckable(true); + act->setData(_SHOW_SECOND_ALIASES_); + if(MusEGlobal::config.preferredRouteNameOrAlias == MusEGlobal::RoutePreferSecondAlias) + act->setChecked(true); + lb->addActions(act_grp->actions()); + lb->addSeparator(); +#endif #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ + + QActionGroup* act_group = new QActionGroup(this); + act_group->setExclusive(false); + int row = 0; + for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) + { + QByteArray ba = (*ip).toLatin1(); + const char* port_name = ba.constData(); + void* const port = MusEGlobal::audioDevice->findPort(port_name); + if(port) + { + //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(channels == -1 ? 1 : channels, redLedIcon, darkRedLedIcon, this); + RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(channels == -1 ? 1 : channels, 0, 0, this); + if(row == 0) + { + wa->array()->headerSetTitle(tr("Jack ports")); + if(channels == -1) + { + wa->array()->setArrayTitle(tr("Connect")); + wa->array()->headerSetVisible(false); + } + else + { + wa->array()->setArrayTitle(tr("Channels")); + wa->array()->headerSetVisible(true); + } + } + else + wa->array()->headerSetVisible(false); - PixmapButtonsWidgetAction* wa = new PixmapButtonsWidgetAction(QString::number(i + 1) + ":" + (md ? md->name() : tr("")), - redLedIcon, darkRedLedIcon,MIDI_CHANNELS, chanmask, pup); - MusECore::Route srcRoute(i, 0); // Ignore the routing channels - our action holds the channels. - //wa->setData(id++); - wa->setData(qVariantFromValue(srcRoute)); - pup->addAction(wa); - ++id; - -#else + char good_name[ROUTE_PERSISTENT_NAME_SIZE]; + + // Get the preferred display name. + MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE, MusEGlobal::config.preferredRouteNameOrAlias); + wa->setActionText(good_name); + + // Get a good routing name. + MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); + MusECore::Route r(MusECore::Route::JACK_ROUTE, -1, port, -1, -1, -1, good_name); + + wa->setData(QVariant::fromValue(r)); + if(channels == -1) + { + if(rl->contains(r)) + wa->array()->setValue(0, true); + } + else + { + for(int i = 0; i < channels; ++i) + { + r.channel = i; + if(rl->contains(r)) + wa->array()->setValue(i, true); + } + } + // Must rebuild array after text changes. + wa->updateChannelArray(); +// lb->addAction(wa); + act_group->addAction(wa); + ++row; + } + } + lb->addActions(act_group->actions()); - PopupMenu* subp = new PopupMenu(pup, true); - subp->setTitle(md->name()); +#else - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + QAction* act = 0; + if(channels == -1) { - act = subp->addAction(QString("Channel %1").arg(ch+1)); - act->setCheckable(true); - - int chbit = 1 << ch; - MusECore::Route srcRoute(i, chbit); // In accordance with channel mask, use the bit position. - - act->setData(qVariantFromValue(srcRoute)); - - if(chanmask & chbit) // Is the channel already set? Show item check mark. - act->setChecked(true); + if(!MusEGlobal::checkAudioDevice()) + { + clear(); + return; + } + for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) + { + act = lb->addAction(*ip); + act->setCheckable(true); + + QByteArray ba = (*ip).toLatin1(); + const char* port_name = ba.constData(); + char good_name[ROUTE_PERSISTENT_NAME_SIZE]; + void* const port = MusEGlobal::audioDevice->findPort(port_name); + if(port) + { + MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); + port_name = good_name; + } + MusECore::Route dst(MusECore::Route::JACK_ROUTE, -1, NULL, -1, -1, -1, port_name); + + act->setData(QVariant::fromValue(dst)); + if(rl->exists(r)) + act->setChecked(true); + } + } + else + { + for(int i = 0; i < channels; ++i) + { + QString chBuffer = tr("Channel") + QString(" ") + QString::number(i + 1); + MenuTitleItem* titel = new MenuTitleItem(chBuffer, this); + lb->addAction(titel); + + if(!MusEGlobal::checkAudioDevice()) + { + clear(); + return; + } + for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) + { + act = lb->addAction(*ip); + act->setCheckable(true); + + QByteArray ba = (*ip).toLatin1(); + const char* port_name = ba.constData(); + char good_name[ROUTE_PERSISTENT_NAME_SIZE]; + void* const port = MusEGlobal::audioDevice->findPort(port_name); + if(port) + { + MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); + port_name = good_name; + } + MusECore::Route dst(MusECore::Route::JACK_ROUTE, -1, NULL, i, -1, -1, port_name); + + act->setData(QVariant::fromValue(dst)); + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(*ir == dst) + { + act->setChecked(true); + break; + } + } + } + if(i+1 != channels) + lb->addSeparator(); + } + } +#endif + + } + + QList act_list; + int row = 0; + for(MusECore::iRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + switch(ir->type) + { + case MusECore::Route::JACK_ROUTE: + if(ir->jackPort == 0 && MusEGlobal::audioDevice->findPort(ir->persistentJackPortName) == 0) + { + //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(channels == -1 ? 1 : channels, redLedIcon, darkRedLedIcon, + RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(channels == -1 ? 1 : channels, 0, 0, + this, ir->persistentJackPortName); + wa->setEnabled(false); + if(row == 0) + { + wa->array()->headerSetTitle(tr("Jack ports")); + if(channels == -1) + { + wa->array()->setArrayTitle(tr("Connect")); + wa->array()->headerSetVisible(false); + } + else + { + wa->array()->setArrayTitle(tr("Channels")); + wa->array()->headerSetVisible(true); + } + } + else + wa->array()->headerSetVisible(false); + + MusECore::Route r(MusECore::Route::JACK_ROUTE, -1, 0, -1, -1, -1, ir->persistentJackPortName); + wa->setData(QVariant::fromValue(r)); + + if(channels == -1) + wa->array()->setValue(0, true); + else + { + for(int i = 0; i < channels; ++i) + { + if(i == ir->channel) + wa->array()->setValue(i, true); + } + } + // Must rebuild array after text changes. + wa->updateChannelArray(); + act_list.append(wa); + ++row; + } + break; - ++id; + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; } - - //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. - act = subp->addAction(QString("Toggle all")); - //act->setCheckable(true); - MusECore::Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. - act->setData(qVariantFromValue(togRoute)); - ++id; - - pup->addMenu(subp); - -#endif // _USE_CUSTOM_WIDGET_ACTIONS_ - - } - return id; -} + } + if(!act_list.isEmpty()) + { + RoutePopupMenu* subp = new RoutePopupMenu(route, this, _isOutMenu, _broadcastChanges); + subp->setTitle(tr("Unavailable")); + const int sz = act_list.size(); + for(int i = 0; i < sz; ++i) + subp->addAction(act_list.at(i)); + lb->addMenu(subp); + } +} //====================== // RoutePopupMenu //====================== -RoutePopupMenu::RoutePopupMenu(QWidget* parent, MusECore::Track* track, bool isOutput) - //: _track(track), _isOutMenu(isOutput) - : PopupMenu(parent, true), _track(track), _isOutMenu(isOutput) +RoutePopupMenu::RoutePopupMenu(QWidget* parent, bool isOutput, bool broadcastChanges) + : PopupMenu(parent, true), _isOutMenu(isOutput), _broadcastChanges(broadcastChanges) +{ + init(); +} + +RoutePopupMenu::RoutePopupMenu(const MusECore::Route& route, QWidget* parent, bool isOutput, bool broadcastChanges) + : PopupMenu(parent, true), _route(route), _isOutMenu(isOutput), _broadcastChanges(broadcastChanges) { init(); } -RoutePopupMenu::RoutePopupMenu(const QString& title, QWidget* parent, MusECore::Track* track, bool isOutput) - //: _track(track), _isOutMenu(isOutput) - : PopupMenu(title, parent, true), _track(track), _isOutMenu(isOutput) +RoutePopupMenu::RoutePopupMenu(const MusECore::Route& route, const QString& title, QWidget* parent, bool isOutput, bool broadcastChanges) + //: PopupMenu(title, parent, true), _track(track), _isOutMenu(isOutput) + : PopupMenu(title, parent, true), _route(route), _isOutMenu(isOutput), _broadcastChanges(broadcastChanges) { init(); } void RoutePopupMenu::init() { - //printf("RoutePopupMenu::init this:%p\n", this); + _hoverIsFromMouse = false; + connect(this, SIGNAL(hovered(QAction*)), SLOT(routePopupHovered(QAction*))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); } -void RoutePopupMenu::songChanged(MusECore::SongChangedFlags_t val) +bool RoutePopupMenu::event(QEvent* event) +{ + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::event:%p activePopupWidget:%p this:%p class:%s event type:%d\n", + event, QApplication::activePopupWidget(), this, metaObject()->className(), event->type()); + + switch(event->type()) + { + // Technical difficulties: + // "mouseReleaseEvent() is called when a mouse button is released. A widget receives mouse release events + // when it has received the corresponding mouse press event. This means that if the user presses the mouse + // inside your widget, then drags the mouse somewhere else before releasing the mouse button, your widget + // receives the release event. There is one exception: if a popup menu appears while the mouse button is held down, + // this popup immediately steals the mouse events." + // Unfortunately that's exactly what we don't want. The mouse release events are not being passed to the higher-up menu + // if we hold the mouse down and move over another menu item which has a submenu - the (delayed) appearance of that + // submenu steals the release. Oddly, if the mouse is moved further - even just once - within the new item, + // the release event IS passed on. So to avoid dealing with that distinction, let's just pass on all release events. + // Should be OK under normal usage, since it makes some sense that no mouse events should be reaching a submenu anyway - + // they have no effect since the cursor position is outside of them ! + // NOTE: If a submenu OVERLAPS its higher-up menu, this could be a big problem. In general how to deal with overlapping popups + // when we wish to be able to click on items in both a menu and its submenu. Overlapping will only happen with too-wide + // menus which should be rare for routing, but we could also defer to the advanced router when the popup becomes too wide. + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonPress: +// Removed. Causes very high CPU usage spikes. +// I think I remember adding MouseMove simply for 'good luck' rather than any real usefulness. +// Tested OK /without/ this on KUBUNTU 15.10, we don't seem to need it. Retest on 14.04 LTS... +// case QEvent::MouseMove: + { + QMouseEvent* mev = static_cast(event); + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::event type:%d x:%d y:%d gx:%d gy:%d sx:%f sy:%f wx:%f wy:%f lx:%f ly:%f\n", + mev->type(), + mev->pos().x(), mev->pos().y(), + mev->globalPos().x(), mev->globalPos().y(), + mev->screenPos().x(), mev->screenPos().y(), + mev->windowPos().x(), mev->windowPos().y(), + mev->localPos().x(), mev->localPos().y()); + + QMenu* target_menu = 0; + const int sz = QApplication::topLevelWidgets().size(); + for(int i = 0; i < sz; ++i) + { + QWidget* w = QApplication::topLevelWidgets().at(i); + DEBUG_PRST_ROUTES(stderr, " checking top level widget:%p\n", w); + if(QMenu* menu = qobject_cast(w)) + { + if(menu->windowType() != Qt::Popup) + continue; + DEBUG_PRST_ROUTES(stderr, " checking hit in menu:%p visible:%d modal:%d\n", menu, menu->isVisible(), menu->isModal()); + if(!menu->isVisible() || !menu->geometry().contains(mev->globalPos())) + continue; + DEBUG_PRST_ROUTES(stderr, " hit\n"); + // If we hit the submenu it means the submenu is partially or wholly obscuring the other menu. + // We must honour the submenu in this case even if it is only slightly obscuring the other menu. + if(menu == this) + { + DEBUG_PRST_ROUTES(stderr, " menu is this\n"); + return PopupMenu::event(mev); + } + // The menu is a good target - the mouse is within it and it is not obscured. + // Regardless, afterward continue watching for THIS menu... + if(!target_menu) + target_menu = menu; + } + } + + if(target_menu) + { + DEBUG_PRST_ROUTES(stderr, " target_menu:%p\n", target_menu); + QMouseEvent new_mev(mev->type(), + //mev->windowPos(), // Relative to the widget the mouse is actually over (menu variable). + QPointF(target_menu->mapFromGlobal(mev->globalPos())), + mev->screenPos(), + mev->button(), + mev->buttons(), + mev->modifiers()); + new_mev.setAccepted(mev->isAccepted()); + new_mev.setTimestamp(mev->timestamp()); + QApplication::sendEvent(target_menu, &new_mev); + return true; + } + + DEBUG_PRST_ROUTES(stderr, " no target popup found\n"); + } + break; + + case QEvent::KeyPress: + { + QKeyEvent* e = static_cast(event); + switch(e->key()) + { + case Qt::Key_Space: + if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this)) + break; + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH + case Qt::Key_Select: + case Qt::Key_Return: + case Qt::Key_Enter: + { + if(activeAction() && (!contextMenu() || !contextMenu()->isVisible())) + { + if(RoutingMatrixWidgetAction* mwa = qobject_cast(activeAction())) + { + bool accept = false; + if(mwa->hasCheckBox() && mwa->isSelected()) + { + mwa->setCheckBoxChecked(!mwa->checkBoxChecked()); + } + else if(mwa->array()->columns() != 0 && mwa->array()->activeColumn() != -1) + { + mwa->array()->setValue(mwa->array()->activeColumn(), !mwa->array()->value(mwa->array()->activeColumn())); + // Reset any other switch bars besides this one which are part of a QActionGroup. + // Since they are all part of an action group, force them to be exclusive regardless of their exclusivity settings. + QActionGroup* act_group = mwa->actionGroup(); + if(act_group && act_group->isExclusive()) + { + const int sz = act_group->actions().size(); + for(int i = 0; i < sz; ++i) + { + if(RoutingMatrixWidgetAction* act = qobject_cast(act_group->actions().at(i))) + { + if(act != mwa) + { + // Set any column to false, and exclusiveColumns and exclusiveToggle to true which will reset all columns. + act->array()->setValues(0, false, true, true); + //update(); // Redraw the indicators. + act->updateCreatedWidgets(); // Redraw the indicators. + } + } + } + } + if(mwa->arrayStayOpen()) + accept = true; + } + else + { + // Nothing selected. Do nothing. TODO: Select the first available item, like QMenu does... + e->accept(); + return true; // We handled it. + } + + mwa->updateCreatedWidgets(); + e->accept(); + mwa->trigger(); // Trigger the action. + // Check for Ctrl to stay open. + if(!accept && (!stayOpen() || (!MusEGlobal::config.popupsDefaultStayOpen && (e->modifiers() & Qt::ControlModifier) == 0))) + closeUp(); // Close all the popups. + return true; // We handled it. + } + // Otherwise let ancestor PopupMenu handle it... + } + } + break; + + default: + break; + } + } + break; + + default: + break; + } + + return PopupMenu::event(event); +} + +void RoutePopupMenu::resizeEvent(QResizeEvent* e) { - if(val & (SC_ROUTE | SC_CHANNELS | SC_CONFIG)) - updateRouteMenus(); + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::resizeEvent\n"); + e->ignore(); + PopupMenu::resizeEvent(e); } -void RoutePopupMenu::updateRouteMenus() +void RoutePopupMenu::mouseReleaseEvent(QMouseEvent* e) { - // NOTE: The purpose of this routine is to make sure the items actually reflect - // the routing status. - // In case for some reason a route could not be added (or removed). - // Then the item will be properly un-checked (or checked) here. - - // TODO Fix this up a bit. It doesn't quite respond to complete removal, and other situations are a bit odd. - // Best to ignore it for now since it only half-works. p4.0.42 + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::mouseReleaseEvent this:%p x:%d y:%d\n", this, e->pos().x(), e->pos().y()); + if(contextMenu() && contextMenu()->isVisible()) + return; -/* - //printf("RoutePopupMenu::updateRouteMenus\n"); + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent begin: this:%p active action:%p\n", this, activeAction()); // REMOVE Tim. - if(!_track || actions().isEmpty() || !isVisible()) - return; - - MusECore::RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); + bool activate = false; + bool accept = false; - // Clear all the action check marks. - clearAllChecks(); - - // Take care of Midi Port to Audio Input routes first... - if(_isOutMenu && _track->isMidiTrack()) + QAction* action = actionAt(e->pos()); + RoutingMatrixWidgetAction* act_mwa = qobject_cast(action); + +// RoutingMatrixWidgetAction* mwa = 0; +// QAction* action = actionAt(e->pos()); +// if(action) +// { +// mwa = qobject_cast(action); +// if(mwa) +// { +// RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestClick); +// switch(hit._type) +// { +// case RoutePopupHit::HitChannel: +// { +// mwa->array()->setValue(hit._value, !mwa->array()->value(hit._value)); +// +// // Reset any other switch bars besides this one which are part of a QActionGroup. +// // Since they are all part of an action group, force them to be exclusive regardless of their exclusivity settings. +// QActionGroup* act_group = mwa->actionGroup(); +// if(act_group && act_group->isExclusive()) +// { +// const int sz = act_group->actions().size(); +// for(int i = 0; i < sz; ++i) +// { +// if(RoutingMatrixWidgetAction* act = qobject_cast(act_group->actions().at(i))) +// { +// if(act != mwa) +// { +// // Set any column to false, and exclusiveColumns and exclusiveToggle to true which will reset all columns. +// act->array()->setValues(0, false, true, true); +// act->updateCreatedWidgets(); // Redraw the indicators. +// } +// } +// } +// } +// +// if(mwa->arrayStayOpen()) +// accept = true; +// activate = true; +// } +// break; +// +// case RoutePopupHit::HitMenuItem: +// mwa->setCheckBoxChecked(!mwa->checkBoxChecked()); +// activate = true; +// break; +// +// case RoutePopupHit::HitChannelBar: +// case RoutePopupHit::HitSpace: +// accept = true; +// break; +// +// case RoutePopupHit::HitNone: +// break; +// } +// } +// } + + int ch_hit_clk_idx_min = -1; + int ch_hit_clk_idx_max = -1; + int ch_hit_clk_ch_start = -1; + bool ch_hit_clk_val = false; + QActionGroup* ch_hit_clk_act_group = 0; + + const int sz = actions().size(); + for(int i = 0; i < sz; ++i) { - int port = ((MusECore::MidiTrack*)_track)->outPort(); - if(port >= 0 && port < MIDI_PORTS) + if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) { - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; - MusECore::RouteList* mprl = mp->outRoutes(); - for (MusECore::ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) + bool do_upd = false; + // Sanity check: Only activate the item(s) if the action truly is the active one. + //if(mwa == activeAction()) + if(mwa == action) { - if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track && ir->track->type() == MusECore::Track::AUDIO_INPUT) + RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestClick); + switch(hit._type) { - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + case RoutePopupHit::HitChannel: { - int chbits = 1 << ch; - if(ir->channel & chbits) + // Support grouping together of channels. + ch_hit_clk_idx_min = i; + ch_hit_clk_idx_max = ch_hit_clk_idx_min + MusEGlobal::config.routerGroupingChannels; + if(ch_hit_clk_idx_max > sz) + ch_hit_clk_idx_min = sz - MusEGlobal::config.routerGroupingChannels; + ch_hit_clk_ch_start = hit._value - (i - ch_hit_clk_idx_min); + const int ch_diff = mwa->array()->columns() - (ch_hit_clk_ch_start + MusEGlobal::config.routerGroupingChannels); + if(ch_diff < 0) { - MusECore::Route r(ir->track, chbits); - //printf("RoutePopupMenu::updateRouteMenus MusECore::MidiPort to AudioInput chbits:%d\n", chbits); // - QAction* act = findActionFromData(qVariantFromValue(r)); - if(act) - { - //printf(" ... Found\n"); // - act->setChecked(true); - } - } - } - } - } - } - } - - // Now check the ones that are found in the route list. - for(MusECore::ciRoute irl = rl->begin(); irl != rl->end(); ++irl) - { - // Do MidiTrack to MidiPort routes... - if(irl->type == MusECore::Route::MIDI_PORT_ROUTE) - { - -#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ - + ch_hit_clk_idx_min += ch_diff; + ch_hit_clk_idx_max += ch_diff; + ch_hit_clk_ch_start += ch_diff; + } + + ch_hit_clk_act_group = mwa->actionGroup(); + ch_hit_clk_val = !mwa->array()->value(hit._value); + + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent i:%d hit._value:%d ch_hit_clk_idx_min:%d ch_hit_clk_idx_max:%d ch_hit_clk_ch_start:%d ch_hit_clk_val:%d\n", + i, hit._value, ch_hit_clk_idx_min, ch_hit_clk_idx_max, ch_hit_clk_ch_start, ch_hit_clk_val); // REMOVE Tim. + + if(mwa->array()->value(hit._value) != ch_hit_clk_val) + { + DEBUG_PRST_ROUTES_2(stderr, " calling mwa->array()->setValue\n"); // REMOVE Tim. + mwa->array()->setValue(hit._value, ch_hit_clk_val); + do_upd = true; + } + if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) + do_upd = true; + +// // Reset any other switch bars besides this one which are part of a QActionGroup. +// // Since they are all part of an action group, force them to be exclusive regardless of their exclusivity settings. +// QActionGroup* act_group = mwa->actionGroup(); +// if(act_group && act_group->isExclusive()) +// { +// const int sz = act_group->actions().size(); +// for(int i = 0; i < sz; ++i) +// { +// if(RoutingMatrixWidgetAction* act = qobject_cast(act_group->actions().at(i))) +// { +// if(act != mwa) +// { +// // Set any column to false, and exclusiveColumns and exclusiveToggle to true which will reset all columns. +// act->array()->setValues(0, false, true, true); +// act->updateCreatedWidgets(); // Redraw the indicators. +// } +// } +// } +// } + + if(mwa->arrayStayOpen()) + accept = true; + activate = true; + +// // Directly execute the trigger handler. +// e->accept(); +// routePopupActivated(mwa); + } + break; + + case RoutePopupHit::HitMenuItem: + { + const bool chk = !mwa->checkBoxChecked(); + if(mwa->checkBoxChecked() != chk) + { + mwa->setCheckBoxChecked(chk); + do_upd = true; + } + activate = true; + +// // Directly execute the trigger handler. +// routePopupActivated(mwa); + } + break; + + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + accept = true; + +// e->accept(); + break; + + case RoutePopupHit::HitNone: + break; + } + } + if(do_upd) + mwa->updateCreatedWidgets(); + } + } + + + + for(int i = 0; i < sz; ++i) + { + if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) + { + bool do_upd = false; + // Sanity check: Only activate the item(s) which are not the active one. + //if(mwa != activeAction()) + if(mwa != action) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent i:%d this:%p inactive mwa:%p\n", i, this, mwa); // REMOVE Tim. + + if(ch_hit_clk_act_group && ch_hit_clk_act_group == mwa->actionGroup()) + { + DEBUG_PRST_ROUTES_2(stderr, " ch_hit_clk_act_group && ch_hit_clk_act_group == mwa->actionGroup()\n"); // REMOVE Tim. + if(ch_hit_clk_act_group->isExclusive()) + { + // Reset any other switch bars besides this one which are part of a QActionGroup. + // Since they are all part of an action group, force them to be exclusive regardless of their exclusivity settings. + // Set any column to false, and exclusiveColumns and exclusiveToggle to true which will reset all columns. + DEBUG_PRST_ROUTES_2(stderr, " calling mwa->array()->setValues (reset)\n"); // REMOVE Tim. + mwa->array()->setValues(0, false, true, true); + do_upd = true; + } + else if(i >= ch_hit_clk_idx_min && i < ch_hit_clk_idx_max) + { + const int ch = ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min); +// mwa->array()->setValue(ch, !mwa->array()->value(ch)); + if(mwa->array()->value(ch) != ch_hit_clk_val) + { + DEBUG_PRST_ROUTES_2(stderr, " calling mwa->array()->setValue ch:%d\n", ch); // REMOVE Tim. + mwa->array()->setValue(ch, ch_hit_clk_val); + do_upd = true; + } + +// // Directly execute the trigger handler. +// e->accept(); +// routePopupActivated(mwa); + + DEBUG_PRST_ROUTES_2(stderr, " ch:%d active col:%d pressed col:%d\n", + ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min), mwa->array()->activeColumn(), mwa->array()->pressedColumn()); // REMOVE Tim. + } + } +// else + if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) + do_upd = true; + + } + if(do_upd) + mwa->updateCreatedWidgets(); + } + } + + if(!action || !act_mwa) + { + e->ignore(); + // Defer to PopupMenu, where we handle regular actions with checkboxes. + PopupMenu::mouseReleaseEvent(e); + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent defer end: this:%p active action:%p\n", this, activeAction()); // REMOVE Tim. + return; + } + + if(accept) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent accept\n"); // REMOVE Tim. + e->accept(); + if(activate) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent accept: directly executing trigger handler routePopupActivated(act_mwa)\n"); // REMOVE Tim. + // Directly execute the trigger handler. + routePopupActivated(act_mwa); + } + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent accept end: this:%p active action:%p\n", this, activeAction()); // REMOVE Tim. + return; + } + + // Check for Ctrl to stay open. + if(!stayOpen() || (!MusEGlobal::config.popupsDefaultStayOpen && (e->modifiers() & Qt::ControlModifier) == 0)) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent No stay-open\n"); // REMOVE Tim. + e->ignore(); + // If this is the active popup widget let the ancestor activate and close it, otherwise we must close this manually. +// // QMenu* m = qobject_cast(QApplication::activePopupWidget()); +// // if(m == this) +// // PopupMenu::mouseReleaseEvent(e); +// // else +// // { + + if(activate) + { + DEBUG_PRST_ROUTES_2(stderr, " activate true: directly executing trigger handler routePopupActivated(act_mwa)\n"); // REMOVE Tim. + // Directly execute the trigger handler. + routePopupActivated(act_mwa); + } + + // Close all the popups. + closeUp(); +// // } + return; + } + + e->accept(); + if(activate) + { + DEBUG_PRST_ROUTES_2(stderr, " activate true: directly executing trigger handler routePopupActivated(act_mwa)\n"); // REMOVE Tim. + routePopupActivated(act_mwa); + } + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent end: this:%p active action:%p\n", this, activeAction()); // REMOVE Tim. +} + +void RoutePopupMenu::mousePressEvent(QMouseEvent* e) +{ + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent begin: this:%p active action:%p\n", this, activeAction()); // REMOVE Tim. +// e->ignore(); +// PopupMenu::mousePressEvent(e); +// DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent after begin: this:%p active action:%p\n", this, activeAction()); // REMOVE Tim. + + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::mousePressEvent this:%p x:%d y:%d\n", this, e->pos().x(), e->pos().y()); + + RoutingMatrixWidgetAction* act_mwa = qobject_cast(actionAt(e->pos())); + + bool accept = false; + + int ch_hit_clk_idx_min = -1; + int ch_hit_clk_idx_max = -1; + int ch_hit_clk_ch_start = -1; + QActionGroup* ch_hit_clk_act_group = 0; + + const int sz = actions().size(); + for(int i = 0; i < sz; ++i) + { + if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) + { + bool do_upd = false; + // Sanity check: Only activate the item(s) if the action truly is the active one. + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent this:%p mwa:%p activeAction:%p\n", this, mwa, activeAction()); // REMOVE Tim. + //if(mwa == activeAction()) + if(mwa == act_mwa) + { + DEBUG_PRST_ROUTES_2(stderr, " is active\n"); // REMOVE Tim. + RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestClick); + switch(hit._type) + { + case RoutePopupHit::HitChannel: + { + // Support grouping together of channels. + ch_hit_clk_idx_min = i; + ch_hit_clk_idx_max = ch_hit_clk_idx_min + MusEGlobal::config.routerGroupingChannels; + if(ch_hit_clk_idx_max > sz) + ch_hit_clk_idx_min = sz - MusEGlobal::config.routerGroupingChannels; + ch_hit_clk_ch_start = hit._value - (i - ch_hit_clk_idx_min); + const int ch_diff = mwa->array()->columns() - (ch_hit_clk_ch_start + MusEGlobal::config.routerGroupingChannels); + if(ch_diff < 0) + { + ch_hit_clk_idx_min += ch_diff; + ch_hit_clk_idx_max += ch_diff; + ch_hit_clk_ch_start += ch_diff; + } + // Set the pressed value. If the column was already active, update again + // so that pressed colour overrides highlighted colour. + if(mwa->array()->setPressedColumn(hit._value) || mwa->array()->activeColumn() == hit._value) + do_upd = true; + ch_hit_clk_act_group = mwa->actionGroup(); + DEBUG_PRST_ROUTES_2(stderr, " HitChannel ch:%d active col:%d pressed col:%d\n", + hit._value, mwa->array()->activeColumn(), mwa->array()->pressedColumn()); // REMOVE Tim. + accept = true; + } + break; + + case RoutePopupHit::HitMenuItem: + if(mwa->setMenuItemPressed(true)) + do_upd = true; + accept = true; + break; + + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) + do_upd = true; + accept = true; + break; + + case RoutePopupHit::HitNone: + if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) + do_upd = true; // TODO Close the menu instead of letting QMenu do it (below)? + break; + } + } + if(do_upd) + mwa->updateCreatedWidgets(); + } + } + + for(int i = 0; i < sz; ++i) + { + if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) + { + bool do_upd = false; + // Sanity check: Only activate the item(s) which are not the active one. + //if(mwa != activeAction()) + if(mwa != act_mwa) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent this:%p inactive mwa:%p\n", this, mwa); // REMOVE Tim. + if(ch_hit_clk_act_group && + !ch_hit_clk_act_group->isExclusive() && + ch_hit_clk_act_group == mwa->actionGroup() && + i >= ch_hit_clk_idx_min && + i < ch_hit_clk_idx_max) + { + if(mwa->array()->setPressedColumn(ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min))) + do_upd = true; + DEBUG_PRST_ROUTES_2(stderr, " ch:%d active col:%d pressed col:%d\n", + ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min), mwa->array()->activeColumn(), mwa->array()->pressedColumn()); // REMOVE Tim. + } + else if(mwa->array()->setPressedColumn(-1)) + do_upd = true; + } + if(do_upd) + mwa->updateCreatedWidgets(); + } + } + + + if(accept) + { +// // e->accept(); +// // PopupMenu::mousePressEvent(e); +// // return; + } + + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent before end: this:%p active action:%p\n", this, activeAction()); // REMOVE Tim. + e->ignore(); + PopupMenu::mousePressEvent(e); + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent end: this:%p active action:%p\n", this, activeAction()); // REMOVE Tim. +} + +void RoutePopupMenu::mouseMoveEvent(QMouseEvent* e) +{ + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::mouseMoveEvent this:%p\n", this); + + RoutingMatrixWidgetAction* act_mwa = qobject_cast(actionAt(e->pos())); + + // Inform the hover handler that it was a mouse hover. + _hoverIsFromMouse = true; + // Ignore the event and pass it on. Let any new active action and hover signal be generated before the code below is run. + e->ignore(); + PopupMenu::mouseMoveEvent(e); + // Clear the flag. + _hoverIsFromMouse = false; + + int ch_hit_hvr_idx_min = -1; + int ch_hit_hvr_idx_max = -1; + int ch_hit_hvr_ch_start = -1; + QActionGroup* ch_hit_hvr_act_group = 0; + + int ch_hit_clk_idx_min = -1; + int ch_hit_clk_idx_max = -1; + int ch_hit_clk_ch_start = -1; + QActionGroup* ch_hit_clk_act_group = 0; + + const int sz = actions().size(); + for(int i = 0; i < sz; ++i) + { + if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) + { + bool do_upd = false; + // Sanity check: Only activate the item(s) if the action truly is the active one. + //if(mwa == activeAction()) + if(mwa == act_mwa) + { + RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestHover); + switch(hit._type) + { + case RoutePopupHit::HitChannel: + { + // Update the current 'last' hover info. + _lastHoveredHit = hit; + // Support grouping together of channels. + ch_hit_hvr_idx_min = i; + ch_hit_hvr_idx_max = ch_hit_hvr_idx_min + MusEGlobal::config.routerGroupingChannels; + if(ch_hit_hvr_idx_max > sz) + ch_hit_hvr_idx_min = sz - MusEGlobal::config.routerGroupingChannels; + ch_hit_hvr_ch_start = hit._value - (i - ch_hit_hvr_idx_min); + const int ch_diff = mwa->array()->columns() - (ch_hit_hvr_ch_start + MusEGlobal::config.routerGroupingChannels); + if(ch_diff < 0) + { + ch_hit_hvr_idx_min += ch_diff; + ch_hit_hvr_idx_max += ch_diff; + ch_hit_hvr_ch_start += ch_diff; + } + // Set the values. + if(mwa->isSelected()) + { + mwa->setSelected(false); + do_upd = true; + } + if(mwa->array()->activeColumn() != hit._value) + { + DEBUG_PRST_ROUTES(stderr, " Setting active column:%d\n", hit._value); + mwa->array()->setActiveColumn(hit._value); + do_upd = true; + } + ch_hit_hvr_act_group = mwa->actionGroup(); + } + break; + + case RoutePopupHit::HitMenuItem: + // Update the current 'last' hover info. + _lastHoveredHit = hit; + if(!mwa->isSelected()) + { + mwa->setSelected(true); + do_upd = true; + } + if(mwa->array()->activeColumn() != -1) + { + mwa->array()->setActiveColumn(-1); + do_upd = true; + } + break; + + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + case RoutePopupHit::HitNone: + if(mwa->isSelected()) + { + mwa->setSelected(false); + do_upd = true; + } + if(mwa->array()->activeColumn() != -1) + { + mwa->array()->setActiveColumn(-1); + do_upd = true; + } + break; + } +// } +// else +// { +// if(mwa->isSelected()) +// { +// mwa->setSelected(false); +// do_upd = true; +// } +// if(mwa->array()->activeColumn() != -1) +// { +// mwa->array()->setActiveColumn(-1); +// do_upd = true; +// } +// } + + if(e->buttons() != Qt::NoButton) + { + RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestClick); + switch(hit._type) + { + case RoutePopupHit::HitChannel: + { + // Support grouping together of channels. + ch_hit_clk_idx_min = i; + ch_hit_clk_idx_max = ch_hit_clk_idx_min + MusEGlobal::config.routerGroupingChannels; + if(ch_hit_clk_idx_max > sz) + ch_hit_clk_idx_min = sz - MusEGlobal::config.routerGroupingChannels; + ch_hit_clk_ch_start = hit._value - (i - ch_hit_clk_idx_min); + const int ch_diff = mwa->array()->columns() - (ch_hit_clk_ch_start + MusEGlobal::config.routerGroupingChannels); + if(ch_diff < 0) + { + ch_hit_clk_idx_min += ch_diff; + ch_hit_clk_idx_max += ch_diff; + ch_hit_clk_ch_start += ch_diff; + } + // Set the value. + if(mwa->array()->setPressedColumn(hit._value)) + do_upd = true; + ch_hit_clk_act_group = mwa->actionGroup(); + } + break; + + case RoutePopupHit::HitMenuItem: + if(mwa->setMenuItemPressed(true)) + do_upd = true; + break; + + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + case RoutePopupHit::HitNone: + if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) + do_upd = true; + break; + } + } + } + if(do_upd) + mwa->updateCreatedWidgets(); + } + } + + + for(int i = 0; i < sz; ++i) + { + if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) + { + bool do_upd = false; + // Sanity check: Only activate the item(s) which are not the active one. + //if(mwa != activeAction()) + if(mwa != act_mwa) + { + if(mwa->isSelected()) + { + mwa->setSelected(false); + do_upd = true; + } + + if(ch_hit_hvr_act_group && + !ch_hit_hvr_act_group->isExclusive() && + ch_hit_hvr_act_group == mwa->actionGroup() && + i >= ch_hit_hvr_idx_min && i < ch_hit_hvr_idx_max) + { + const int ch = ch_hit_hvr_ch_start + (i - ch_hit_hvr_idx_min); + if(mwa->array()->activeColumn() != ch) + { + DEBUG_PRST_ROUTES(stderr, " Setting inactive column:%d\n", ch); + mwa->array()->setActiveColumn(ch); + do_upd = true; + } + } + else if(mwa->array()->activeColumn() != -1) + { + mwa->array()->setActiveColumn(-1); + do_upd = true; + } +// } + + if(e->buttons() != Qt::NoButton && + ch_hit_clk_act_group && + !ch_hit_clk_act_group->isExclusive() && + ch_hit_clk_act_group == mwa->actionGroup() && + i >= ch_hit_clk_idx_min && + i < ch_hit_clk_idx_max) + { + if(mwa->array()->setPressedColumn(ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min))) + do_upd = true; + } + else if(mwa->array()->setPressedColumn(-1)) + do_upd = true; + } + + if(do_upd) + mwa->updateCreatedWidgets(); + } + } + + +} + +void RoutePopupMenu::routePopupHovered(QAction* action) +{ + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::popHovered this:%p action:%p _hoverIsFromMouse:%d text:%s\n", this, action, _hoverIsFromMouse, action->text().toLatin1().constData()); + + // Ignore if this hover was from mouse. + // Also, we get this hovered signal even if the hovered action is from another popup, so ignore it. + if(!_hoverIsFromMouse && actions().contains(action)) + { + const int sz = actions().size(); + for(int i = 0; i < sz; ++i) + { + if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) + { + bool do_upd = false; + if(mwa == action) + { + switch(_lastHoveredHit._type) + { + case RoutePopupHit::HitChannel: + { + if(mwa->isSelected()) + { + mwa->setSelected(false); + do_upd = true; + } + const int cols = mwa->array()->columns(); + if(cols != 0) + { + int col = _lastHoveredHit._value; // The column. + if(col >= cols) + { + col = cols - 1; // Clip it. + _lastHoveredHit._value = col; // Adjust the current 'last' column setting. + } + if(mwa->array()->activeColumn() != col) + { + mwa->array()->setActiveColumn(col); + do_upd = true; + } + } + } + break; + + case RoutePopupHit::HitMenuItem: + if(mwa->hasCheckBox() && !mwa->isSelected()) + { + mwa->setSelected(true); + do_upd = true; + } + if(mwa->array()->activeColumn() != -1) + { + mwa->array()->setActiveColumn(-1); + do_upd = true; + } + break; + + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + case RoutePopupHit::HitNone: + // If it has a checkbox (or there is no channel bar) select the checkbox/text area. + if(mwa->hasCheckBox() || mwa->array()->columns() == 0) + { + // Update the current 'last' hover info. + _lastHoveredHit._type = RoutePopupHit::HitMenuItem; + _lastHoveredHit._action = mwa; + _lastHoveredHit._value = 0; + if(!mwa->isSelected()) + { + mwa->setSelected(true); + do_upd = true; + } + } + // Otherwise select the first available channel bar column. + else + { + // Update the current 'last' hover info. + _lastHoveredHit._type = RoutePopupHit::HitChannel; + _lastHoveredHit._action = mwa; + _lastHoveredHit._value = 0; + if(mwa->array()->activeColumn() != 0) + { + mwa->array()->setActiveColumn(0); + do_upd = true; + } + } + break; + } + } + else + { + if(mwa->isSelected()) + { + mwa->setSelected(false); // Reset the checkbox/text active area. + do_upd = true; + } + if(mwa->array()->activeColumn() != -1) + { + mwa->array()->setActiveColumn(-1); // Reset any active column. + do_upd = true; + } + } + + if(do_upd) + mwa->updateCreatedWidgets(); + } + } + } + + // Clear the flag. + //_hoverIsFromMouse = false; +} + +void RoutePopupMenu::keyPressEvent(QKeyEvent* e) +{ + if(activeAction()) + { + if(RoutingMatrixWidgetAction* mwa = qobject_cast(activeAction())) + { + bool do_upd = false; + bool key_accepted = false; + const int key = e->key(); + switch(key) + { + case Qt::Key_Left: + { + switch(_lastHoveredHit._type) + { + case RoutePopupHit::HitMenuItem: + // Allow the menu to close. + break; + + case RoutePopupHit::HitChannel: + // If we're on the first available channel and there's no checkbox, allow the menu to close. + if(_lastHoveredHit._value == 0 && !mwa->hasCheckBox()) + break; + // Fall through. + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + case RoutePopupHit::HitNone: + { + // Get the next available item to the left. + RoutePopupHit hit = mwa->previousHit(_lastHoveredHit); + switch(hit._type) + { + case RoutePopupHit::HitChannel: + { + if(mwa->isSelected()) + { + mwa->setSelected(false); + do_upd = true; + } + if(mwa->array()->activeColumn() != hit._value) + { + mwa->array()->setActiveColumn(hit._value); + do_upd = true; + } + // Update the current 'last' hover info. + _lastHoveredHit = hit; + key_accepted = true; + } + break; + + case RoutePopupHit::HitMenuItem: + if(!mwa->isSelected()) + { + mwa->setSelected(true); + do_upd = true; + } + if(mwa->array()->activeColumn() != -1) + { + mwa->array()->setActiveColumn(-1); + do_upd = true; + } + // Update the current 'last' hover info. + _lastHoveredHit = hit; + key_accepted = true; + break; + + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + case RoutePopupHit::HitNone: + if(mwa->isSelected()) + { + mwa->setSelected(false); + do_upd = true; + } + if(mwa->array()->activeColumn() != -1) + { + mwa->array()->setActiveColumn(-1); + do_upd = true; + } + // Update the current 'last' hover info. + _lastHoveredHit = hit; + key_accepted = true; + break; + } + } + break; + } + } + break; + + case Qt::Key_Right: + { + switch(_lastHoveredHit._type) + { + case RoutePopupHit::HitChannel: + // If we're on the last available channel, allow any submenu to open. + if(mwa->array()->columns() != 0 && _lastHoveredHit._value == mwa->array()->columns() - 1) + break; + // Fall through. + case RoutePopupHit::HitMenuItem: + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + case RoutePopupHit::HitNone: + { + // Get the next available item to the right. + RoutePopupHit hit = mwa->nextHit(_lastHoveredHit); + switch(hit._type) + { + case RoutePopupHit::HitChannel: + { + if(mwa->isSelected()) + { + mwa->setSelected(false); + do_upd = true; + } + if(mwa->array()->activeColumn() != hit._value) + { + mwa->array()->setActiveColumn(hit._value); + do_upd = true; + } + // Update the current 'last' hover info. + _lastHoveredHit = hit; + key_accepted = true; + } + break; + + case RoutePopupHit::HitMenuItem: + if(!mwa->isSelected()) + { + mwa->setSelected(true); + do_upd = true; + } + if(mwa->array()->activeColumn() != -1) + { + mwa->array()->setActiveColumn(-1); + do_upd = true; + } + // Update the current 'last' hover info. + _lastHoveredHit = hit; + key_accepted = true; + break; + + case RoutePopupHit::HitChannelBar: + case RoutePopupHit::HitSpace: + case RoutePopupHit::HitNone: + if(mwa->isSelected()) + { + mwa->setSelected(false); + do_upd = true; + } + if(mwa->array()->activeColumn() != -1) + { + mwa->array()->setActiveColumn(-1); + do_upd = true; + } + // Update the current 'last' hover info. + _lastHoveredHit = hit; + key_accepted = true; + break; + } + } + } + } + break; + + default: + break; + } + + if(do_upd) + mwa->updateCreatedWidgets(); + if(key_accepted) + { + e->accept(); + return; + } + } + } + + e->ignore(); + PopupMenu::keyPressEvent(e); +} + +void RoutePopupMenu::songChanged(MusECore::SongChangedFlags_t val) +{ + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::songChanged flags:%ld", (long int)val); + if(val & (SC_ROUTE | SC_CHANNELS | SC_CONFIG)) + updateRouteMenus(); + if(val & SC_PORT_ALIAS_PREFERENCE) + preferredPortAliasChanged(); + if(val & SC_ROUTER_CHANNEL_GROUPING) + routerChannelGroupingChanged(); +} + +bool RoutePopupMenu::updateItemTexts(PopupMenu* menu) +{ + if(!menu) + menu = this; + QList list = menu->actions(); + const int sz = list.size(); + bool changed = false; + for(int i = 0; i < sz; ++i) + { + QAction* act = list.at(i); + if(RoutingMatrixWidgetAction* wa = qobject_cast(act)) + { + // Take care of struct Route first. Insert other future custom structures here too ! + if(act->data().canConvert()) + { + const MusECore::Route r = act->data().value(); + switch(r.type) + { + case MusECore::Route::JACK_ROUTE: + { + if(MusEGlobal::checkAudioDevice()) + { + char good_name[ROUTE_PERSISTENT_NAME_SIZE]; + const char* port_name = r.persistentJackPortName; + void* const port = MusEGlobal::audioDevice->findPort(port_name); + if(port) + { + MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE, MusEGlobal::config.preferredRouteNameOrAlias); + const QString str(good_name); + if(wa->actionText() != str) + { + wa->setActionText(str); + changed = true; + } + } + if(changed) + { +// wa->updateChannelArray(); + } + } + } + break; + + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + } + + + } + else + // Take care of struct Route first. Insert other future custom structures here too ! + if(act->data().canConvert()) + { + const MusECore::Route r = act->data().value(); + switch(r.type) + { + case MusECore::Route::JACK_ROUTE: + act->setText(r.name(MusEGlobal::config.preferredRouteNameOrAlias)); + break; + + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + } + } + + return changed; +} + +// Updates item texts and the 'preferred alias action'. Returns true if any action was changed. +bool RoutePopupMenu::preferredPortAliasChanged() +{ + QList list = actions(); + const int sz = list.size(); + bool changed = false; + for(int i = 0; i < sz; ++i) + { + QAction* act = list.at(i); + // Check for custom widget action. + if(RoutingMatrixWidgetAction* wa = qobject_cast(act)) + { + // Check for Route data type. + // Take care of struct Route first. Add other future custom structures here too. + if(act->data().canConvert()) + { + const MusECore::Route r = act->data().value(); + switch(r.type) + { + case MusECore::Route::JACK_ROUTE: + { + if(MusEGlobal::checkAudioDevice()) + { + char good_name[ROUTE_PERSISTENT_NAME_SIZE]; + const char* port_name = r.persistentJackPortName; + void* const port = MusEGlobal::audioDevice->findPort(port_name); + if(port) + { + MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE, MusEGlobal::config.preferredRouteNameOrAlias); + const QString str(good_name); + if(wa->actionText() != str) + { + wa->setActionText(str); + changed = true; + } + } + } + } + break; + + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + } + // No Route data type. Check for int data IDs. + // Handle future data types above, before this in case those types might be convertible to int. + else + { + bool ok = false; + const int n = act->data().toInt(&ok); + if(ok) + { + switch(n) + { + #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ + // Check for the 'preferred port alias' action. + case _ALIASES_WIDGET_ACTION_: + { + int v; + if(wa->array()->value(0)) + v = MusEGlobal::RoutePreferFirstAlias; + else if(wa->array()->value(1)) + v = MusEGlobal::RoutePreferSecondAlias; + else + v = MusEGlobal::RoutePreferCanonicalName; + + if(v != MusEGlobal::config.preferredRouteNameOrAlias) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::preferredPortAliasChanged setting alias array preferredRouteNameOrAlias:%d\n", + MusEGlobal::config.preferredRouteNameOrAlias); + switch(MusEGlobal::config.preferredRouteNameOrAlias) + { + case MusEGlobal::RoutePreferFirstAlias: + wa->array()->setValue(0, true); + break; + case MusEGlobal::RoutePreferSecondAlias: + wa->array()->setValue(1, true); + break; + case MusEGlobal::RoutePreferCanonicalName: + // Just set any column to false to clear this exclusive array. + wa->array()->setValue(0, false); + break; + } + changed = true; + } + } + break; + #endif + + default: + break; + } + } + } + } + // Not a custom widget action. Check for Route data type. + // Take care of struct Route first. Add other future custom structures here too. + else if(act->data().canConvert()) + { + const MusECore::Route r = act->data().value(); + switch(r.type) + { + case MusECore::Route::JACK_ROUTE: + { + const QString rname = r.name(MusEGlobal::config.preferredRouteNameOrAlias); + if(act->text() != rname) + { + act->setText(rname); + changed = true; + } + } + break; + + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + } + } + + return changed; +} + +bool RoutePopupMenu::routerChannelGroupingChanged() +{ + QList list = actions(); + const int sz = list.size(); + bool changed = false; + for(int i = 0; i < sz; ++i) + { + QAction* act = list.at(i); + // Check for custom widget action. + if(RoutingMatrixWidgetAction* wa = qobject_cast(act)) + { + // Check for Route data type. + // Take care of struct Route first. Add other future custom structures here too. + if(act->data().canConvert()) + { + // Nothing to do here yet. + } + // No Route data type. Check for int data IDs. + // Handle future data types above, before this in case those types might be convertible to int. + else + { + bool ok = false; + const int n = act->data().toInt(&ok); + if(ok) + { + switch(n) + { + #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ + // Check for the 'grouping channels' action. + case _GROUPING_CHANNELS_WIDGET_ACTION_: + { + int v; + if(wa->array()->value(0)) + v = 1; + else + v = 2; + + if(v != MusEGlobal::config.routerGroupingChannels) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::routerChannelGroupingChanged setting array routerGroupingChannels:%d\n", + MusEGlobal::config.routerGroupingChannels); + switch(MusEGlobal::config.routerGroupingChannels) + { + case 1: + wa->array()->setValue(0, true); + changed = true; + break; + case 2: + wa->array()->setValue(1, true); + changed = true; + break; + default: + break; + } + } + } + break; + #endif + + default: + break; + } + } + } + } + // Not a custom widget action. Check for Route data type. + // Take care of struct Route first. Add other future custom structures here too. + else if(act->data().canConvert()) + { + // Nothing to do here yet. + } + } + return changed; +} + +void RoutePopupMenu::updateRouteMenus() +{ + // NOTE: The purpose of this routine is to make sure the items actually reflect + // the routing status. + // In case for some reason a route could not be added (or removed). + // Then the item will be properly un-checked (or checked) here. + + // TODO Fix this up a bit. It doesn't quite respond to complete removal, and other situations are a bit odd. + // Best to ignore it for now since it only half-works. p4.0.42 + +/* + //printf("RoutePopupMenu::updateRouteMenus\n"); + + if(!_track || actions().isEmpty() || !isVisible()) + return; + + MusECore::RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); + + // Clear all the action check marks. + clearAllChecks(); + + // Take care of Midi Port to Audio Input routes first... + if(_isOutMenu && _track->isMidiTrack()) + { + int port = ((MusECore::MidiTrack*)_track)->outPort(); + if(port >= 0 && port < MIDI_PORTS) + { + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::RouteList* mprl = mp->outRoutes(); + for (MusECore::ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) + { + if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track && ir->track->type() == MusECore::Track::AUDIO_INPUT) + { + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + int chbits = 1 << ch; + if(ir->channel & chbits) + { + MusECore::Route r(ir->track, chbits); + //printf("RoutePopupMenu::updateRouteMenus MusECore::MidiPort to AudioInput chbits:%d\n", chbits); // + QAction* act = findActionFromData(QVariant::fromValue(r)); + if(act) + { + //printf(" ... Found\n"); // + act->setChecked(true); + } + } + } + } + } + } + } + + // Now check the ones that are found in the route list. + for(MusECore::ciRoute irl = rl->begin(); irl != rl->end(); ++irl) + { + // Do MidiTrack to MidiPort routes... + if(irl->type == MusECore::Route::MIDI_PORT_ROUTE) + { + +#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ + // Widget action handles channels. Look for route with channels ignored and set to zero. MusECore::Route r(irl->midiPort, 0); - QAction* act = findActionFromData(qVariantFromValue(r)); + QAction* act = findActionFromData(QVariant::fromValue(r)); if(act) { //printf("RoutePopupMenu::updateRouteMenus found MidiTrack to MidiPort irl type:%d\n", irl->type); // @@ -833,7 +2506,7 @@ MusECore::Route r(irl->midiPort, chbits); //printf("RoutePopupMenu::updateRouteMenus MidiTrack to MidiPort irl type:%d\n", irl->type); // // If act is a PixmapButtonsWidgetAction, route channel is ignored and is zero. - QAction* act = findActionFromData(qVariantFromValue(r)); + QAction* act = findActionFromData(QVariant::fromValue(r)); if(act) { //printf(" ... Found\n"); // @@ -855,7 +2528,7 @@ { // Widget action handles channels. Look for route with channels ignored and set to zero. MusECore::Route r(irl->track, 0); - QAction* act = findActionFromData(qVariantFromValue(r)); + QAction* act = findActionFromData(QVariant::fromValue(r)); if(act) { // Check for custom widget actions first. @@ -869,779 +2542,1384 @@ } } -#endif // _USE_CUSTOM_WIDGET_ACTIONS_ - - printf("RoutePopupMenu::updateRouteMenus other irl type:%d\n", irl->type); // REMOVE TIm. - if(act) - { - //printf("RoutePopupMenu::updateRouteMenus found other irl type:%d\n", irl->type); // - act->setChecked(true); +#endif // _USE_CUSTOM_WIDGET_ACTIONS_ + + printf("RoutePopupMenu::updateRouteMenus other irl type:%d\n", irl->type); // REMOVE TIm. + if(act) + { + //printf("RoutePopupMenu::updateRouteMenus found other irl type:%d\n", irl->type); // + act->setChecked(true); + } + } + } +*/ +} + +void RoutePopupMenu::trackRouteActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations) +{ + // Check for custom routing matrix action. + RoutingMatrixWidgetAction* matrix_wa = qobject_cast(action); + if(!matrix_wa) + return; + + switch(rem_route.type) + { + case MusECore::Route::TRACK_ROUTE: + { + // Make sure the track still exists. + if(MusEGlobal::song->tracks()->find(rem_route.track) == MusEGlobal::song->tracks()->end()) + return; + + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackRouteActivated:\n"); + + MusECore::Track* track = _route.track; + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Track types must be same. + if((track->isMidiTrack() && !t->isMidiTrack()) || (t->type() != track->type())) + continue; + // We are looking for the given track alone if unselected, or else all selected tracks. + // Ignore other tracks if broadcasting changes is disabled. + if(t != track && (!_broadcastChanges || !t->selected() || !track->selected())) + continue; + + + const int cols = matrix_wa->array()->columns(); + for(int col = 0; col < cols; ++col) + { + MusECore::Route this_route(t, col, 1); + rem_route.channels = 1; + + const MusECore::Route& src = _isOutMenu ? this_route : rem_route; + const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; + + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackRouteActivated: checking operations\n"); + const bool val = matrix_wa->array()->value(col); + // Connect if route does not exist. Allow it to reconnect a partial route. + if(val && MusECore::routeCanConnect(src, dst)) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackRouteActivated: adding AddRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); + } + // Disconnect if route exists. Allow it to reconnect a partial route. + else if(!val && MusECore::routeCanDisconnect(src, dst)) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackRouteActivated: adding DeleteRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); + } + } + } + } + break; + + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + return; + break; + } +} + +void RoutePopupMenu::jackRouteActivated(QAction* action, const MusECore::Route& route, const MusECore::Route& rem_route, MusECore::PendingOperationList& operations) +{ + // Check for custom routing matrix action. + RoutingMatrixWidgetAction* matrix_wa = qobject_cast(action); + if(!matrix_wa) + return; + + if(!MusEGlobal::checkAudioDevice()) + return; + + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::jackRouteActivated: Matrix\n"); + + const int cols = matrix_wa->array()->columns(); + const char* const port_name = rem_route.persistentJackPortName; + void* const port = MusEGlobal::audioDevice->findPort(port_name); + if(port) + { + for(int col = 0; col < cols; ++col) + { + MusECore::Route this_route(route); + switch(route.type) + { + case MusECore::Route::MIDI_DEVICE_ROUTE: + this_route.channel = -1; + break; + + case MusECore::Route::TRACK_ROUTE: + { + this_route.channel = col; + + MusECore::Track* track = route.track; + if(track) + { + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Track types must be same. + if((track->isMidiTrack() && !t->isMidiTrack()) || (t->type() != track->type())) + continue; + // We are looking for the given track alone if unselected, or else all selected tracks. + // Ignore other tracks if broadcasting changes is disabled. + if(t != track && (!_broadcastChanges || !t->selected() || !track->selected())) + continue; + + this_route.track = t; + // TODO Lazy identical copy of code below. Streamline somehow... + const MusECore::Route r_route(port); + const MusECore::Route& src = _isOutMenu ? this_route : r_route; + const MusECore::Route& dst = _isOutMenu ? r_route : this_route; + const bool val = matrix_wa->array()->value(col); + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: checking operations col:%d val:%d\n", col, val); + // Connect if route does not exist. Allow it to reconnect a partial route. + if(val && MusECore::routeCanConnect(src, dst)) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: adding AddRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); + } + // Disconnect if route exists. Allow it to reconnect a partial route. + else if(!val && MusECore::routeCanDisconnect(src, dst)) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: adding DeleteRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); + } + + } + } + // We took care of it. Continue with the next column. + continue; + + } + break; + + case MusECore::Route::MIDI_PORT_ROUTE: + if(route.midiPort == -1) + return; + if(MusECore::MidiDevice* md = MusEGlobal::midiPorts[route.midiPort].device()) + { + this_route.type = MusECore::Route::MIDI_DEVICE_ROUTE; + this_route.device = md; + this_route.channel = -1; + } + else + return; + break; + + case MusECore::Route::JACK_ROUTE: + break; + } + + const MusECore::Route r_route(port); + const MusECore::Route& src = _isOutMenu ? this_route : r_route; + const MusECore::Route& dst = _isOutMenu ? r_route : this_route; + + const bool val = matrix_wa->array()->value(col); + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: checking operations col:%d val:%d\n", col, val); + // Connect if route does not exist. Allow it to reconnect a partial route. + if(val && MusECore::routeCanConnect(src, dst)) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: adding AddRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); + } + // Disconnect if route exists. Allow it to reconnect a partial route. + else if(!val && MusECore::routeCanDisconnect(src, dst)) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: adding DeleteRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); + } + } + } +} + +void RoutePopupMenu::audioTrackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations) +{ + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::audioTrackPopupActivated: action text:%s checked:%d name:%s\n", + action->text().toLatin1().constData(), action->isChecked(), + action->objectName().toLatin1().constData()); + + MusECore::Track* track = _route.track; + // Check for custom routing matrix action. + RoutingMatrixWidgetAction* matrix_wa = qobject_cast(action); + if(matrix_wa) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::audioTrackPopupActivated: Matrix\n"); + + switch(rem_route.type) + { + case MusECore::Route::JACK_ROUTE: + jackRouteActivated(action, _route, rem_route, operations); + break; + + case MusECore::Route::TRACK_ROUTE: + trackRouteActivated(action, rem_route, operations); + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } + } +#ifndef _USE_SIMPLIFIED_SOLO_CHAIN_ + // Support Midi Port to Audio Input routes. + else if(!_isOutMenu && track->type() == MusECore::Track::AUDIO_INPUT && rem_route.type == MusECore::Route::MIDI_PORT_ROUTE) + { + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Track types must be same. + if((track->isMidiTrack() && !t->isMidiTrack()) || (t->type() != track->type())) + continue; + // We are looking for the given track alone if unselected, or else all selected tracks. + // Ignore other tracks if broadcasting changes is disabled. + if(t != track && (!_broadcastChanges || !t->selected() || !track->selected())) + continue; + + MusECore::RouteList* rl = _isOutMenu ? t->outRoutes() : t->inRoutes(); + // Check for custom midi channel select action. + PixmapButtonsWidgetAction* cs_wa = qobject_cast(action); + if(cs_wa) + { + const QBitArray ba = cs_wa->currentState(); + const int ba_sz = ba.size(); + int chbits = 0; + for(int mch = 0; mch < MIDI_CHANNELS && mch < ba_sz; ++mch) + { + if(ba.at(mch)) + chbits |= (1 << mch); + } + + rem_route.channel = chbits; // Restore the desired route channels from the custom widget action state. + int mdidx = rem_route.midiPort; + + int chmask = 0; + MusECore::ciRoute iir = rl->begin(); + for (; iir != rl->end(); ++iir) + if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) { // Is there already a route to this port? + chmask = iir->channel; // Grab the channel mask. + break; + } + + // Only if something changed... + if(chmask != chbits) + { + if(chmask != 0) + { + // Disconnect all existing channels. + MusECore::Route dstRoute(t, chmask); + operations.add(MusECore::PendingOperationItem(*iir, dstRoute, MusECore::PendingOperationItem::DeleteRoute)); + } + if(chbits != 0) + { + // Connect desired channels. + MusECore::Route dstRoute(t, chbits); + operations.add(MusECore::PendingOperationItem(rem_route, dstRoute, MusECore::PendingOperationItem::AddRoute)); + } + } + } + else + { + int chbit = rem_route.channel; + MusECore::Route dstRoute(t, chbit); + int mdidx = rem_route.midiPort; + int chmask = 0; + MusECore::ciRoute iir = rl->begin(); + for (; iir != rl->end(); ++iir) + { + if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port? + { + chmask = iir->channel; // Grab the channel mask. + break; + } + } + + if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? + operations.add(MusECore::PendingOperationItem(rem_route, dstRoute, MusECore::PendingOperationItem::DeleteRoute)); + else + operations.add(MusECore::PendingOperationItem(rem_route, dstRoute, MusECore::PendingOperationItem::AddRoute)); } } } -*/ -} +#endif // _USE_SIMPLIFIED_SOLO_CHAIN_ + else + { + // Make sure the track still exists. + if(MusEGlobal::song->tracks()->find(rem_route.track) == MusEGlobal::song->tracks()->end()) + return; + + MusECore::TrackList* tracks = MusEGlobal::song->tracks(); + for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::Track* t = *it; + // Track types must be same. + if((track->isMidiTrack() && !t->isMidiTrack()) || (t->type() != track->type())) + continue; + // We are looking for the given track alone if unselected, or else all selected tracks. + // Ignore other tracks if broadcasting changes is disabled. + if(t != track && (!_broadcastChanges || !t->selected() || !track->selected())) + continue; + + + MusECore::Route this_route(t, rem_route.channel, rem_route.channels); + this_route.remoteChannel = rem_route.remoteChannel; + + const MusECore::Route& src = _isOutMenu ? this_route : rem_route; + const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; + + // Connect if route does not exist. Allow it to reconnect a partial route. + if(action->isChecked() && MusECore::routeCanConnect(src, dst)) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::audioTrackPopupActivated: Route: adding AddRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); + } + // Disconnect if route exists. Allow it to reconnect a partial route. + else if(!action->isChecked() && MusECore::routeCanDisconnect(src, dst)) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::audioTrackPopupActivated: Route: adding DeleteRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); + } + } + } +} -void RoutePopupMenu::popupActivated(QAction* action) +void RoutePopupMenu::midiTrackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations) { - if(!action || !_track || actions().isEmpty()) + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: action text:%s checked:%d name:%s\n", + action->text().toLatin1().constData(), action->isChecked(), + action->objectName().toLatin1().constData()); + + MusECore::Track* track = _route.track; + if(rem_route.type == MusECore::Route::TRACK_ROUTE && rem_route.track && + // Make sure the track still exists. + MusEGlobal::song->tracks()->find(rem_route.track) != MusEGlobal::song->tracks()->end() && + rem_route.track->type() == MusECore::Track::AUDIO_INPUT) + { + + MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); + for(MusECore::iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::MidiTrack* mt = *it; + // We are looking for the given track alone if unselected, or else all selected tracks. + // Ignore other tracks if broadcasting changes is disabled. + if(mt != track && (!_broadcastChanges || !mt->selected() || !track->selected())) + continue; + + #ifdef _USE_SIMPLIFIED_SOLO_CHAIN_ + // Support Midi Track to Audio Input track soloing chain routes. + // Support omni routes only, because if channels are supported, the graphical router becomes more complicated. + if(_isOutMenu && rem_route.channel == -1) + { + const MusECore::Route this_route(mt, -1); + operations.add(MusECore::PendingOperationItem(this_route, rem_route, + action->isChecked() ? + MusECore::PendingOperationItem::AddRoute : + MusECore::PendingOperationItem::DeleteRoute)); + } + #else + // Support Midi Port to Audio Input track routes. + int chbit = rem_route.channel; + int port = mt->outPort(); + if(port < 0 || port >= MIDI_PORTS) return; - - if(_track->isMidiTrack()) + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + MusECore::Route bRoute(port, chbit); + + int chmask = 0; + MusECore::RouteList* mprl = _isOutMenu ? mp->outRoutes() : mp->inRoutes(); + MusECore::ciRoute ir = mprl->begin(); + for (; ir != mprl->end(); ++ir) + if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == rem_route.track) { // Is there already a route to this port? + chmask = ir->channel; // Grab the channel mask. + break; + } + if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? { - MusECore::RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); - - // Take care of Route data items first... - if(qVariantCanConvert(action->data())) + // disconnect + if(_isOutMenu) + operations.add(MusECore::PendingOperationItem(bRoute, rem_route, MusECore::PendingOperationItem::DeleteRoute)); + else + operations.add(MusECore::PendingOperationItem(rem_route, bRoute, MusECore::PendingOperationItem::DeleteRoute)); + } + else + { + // connect + if(_isOutMenu) + operations.add(MusECore::PendingOperationItem(bRoute, rem_route, MusECore::PendingOperationItem::AddRoute)); + else + operations.add(MusECore::PendingOperationItem(rem_route, bRoute, MusECore::PendingOperationItem::AddRoute)); + } + #endif + + } + } + // Midi track to Midi Port routes. + else if(rem_route.type == MusECore::Route::MIDI_PORT_ROUTE) + { + // Check for custom routing matrix action. + RoutingMatrixWidgetAction* matrix_wa = qobject_cast(action); + + MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; + + MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); + for(MusECore::iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) + { + MusECore::MidiTrack* mt = *it; + // We are looking for the given track alone if unselected, or else all selected tracks. + // Ignore other tracks if broadcasting changes is disabled. + if(mt != track && (!_broadcastChanges || !mt->selected() || !track->selected())) + continue; + + if(matrix_wa) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated midi port: matrix:\n"); + + #ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ + const int cols = matrix_wa->array()->columns(); + switch(rem_route.type) { - MusECore::Route aRoute = action->data().value(); - - // Support Midi Port to Audio Input track routes. - if(aRoute.type == MusECore::Route::TRACK_ROUTE && aRoute.track && aRoute.track->type() == MusECore::Track::AUDIO_INPUT) + case MusECore::Route::MIDI_PORT_ROUTE: { - //if(gIsOutRoutingPopupMenu) // Try to avoid splitting like this. + if(rem_route.isValid() && rem_route.midiPort != -1) { - int chbit = aRoute.channel; - int port = ((MusECore::MidiTrack*)_track)->outPort(); - if(port < 0 || port >= MIDI_PORTS) - return; - - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; - //MusECore::MidiDevice* md = mp->device(); - - // This is desirable, but could lead to 'hidden' routes unless we add more support - // such as removing the existing routes when user changes flags. - // So for now, just list all valid ports whether read or write. - //if(!md) - // return; - //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) - // return; - - MusECore::Route bRoute(port, chbit); - - int chmask = 0; - MusECore::RouteList* mprl = _isOutMenu ? mp->outRoutes() : mp->inRoutes(); - MusECore::ciRoute ir = mprl->begin(); - for (; ir != mprl->end(); ++ir) - if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == aRoute.track) { // Is there already a route to this port? - chmask = ir->channel; // Grab the channel mask. - break; - } - if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? + // Do channel routes... + for(int col = 0; col < cols && col < MIDI_CHANNELS; ++col) { - // disconnect - if(_isOutMenu) - MusEGlobal::audio->msgRemoveRoute(bRoute, aRoute); - else - MusEGlobal::audio->msgRemoveRoute(aRoute, bRoute); - } - else - { - // connect + const bool val = matrix_wa->array()->value(col); + + #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ + // In this case the channel bar (and any other channel bars in a QActionGroup) will be in exclusive mode... if(_isOutMenu) - MusEGlobal::audio->msgAddRoute(bRoute, aRoute); - else - MusEGlobal::audio->msgAddRoute(aRoute, bRoute); - } - - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); - - } - return; - } - // Support Audio Input track to Midi Port routes. - else if(aRoute.type == MusECore::Route::MIDI_PORT_ROUTE) - { - // Check for custom midi channel select action. - PixmapButtonsWidgetAction* cs_wa = dynamic_cast(action); - if(cs_wa) - { - MusECore::Route aRoute = action->data().value(); - int chbits = cs_wa->currentState(); - aRoute.channel = chbits; // Restore the desired route channels from the custom widget action state. - - int mdidx = aRoute.midiPort; - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mdidx]; - - MusECore::MidiDevice* md = mp->device(); - //if(!md) // Rem. Allow connections to ports with no device. - // return; - - //if(!(md->rwFlags() & 2)) - //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) - if(md && !(md->rwFlags() & (_isOutMenu ? 1 : 2))) - return; - - int chmask = 0; - MusECore::ciRoute iir = rl->begin(); - for (; iir != rl->end(); ++iir) - if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) { // Is there already a route to this port? - chmask = iir->channel; // Grab the channel mask. - break; - } - - // Only if something changed... - if(chmask != chbits) - { - if(chmask != 0) { - MusECore::Route bRoute(_track, chmask); - // Disconnect all existing channels. - if(_isOutMenu) - MusEGlobal::audio->msgRemoveRoute(bRoute, *iir); - else - MusEGlobal::audio->msgRemoveRoute(*iir, bRoute); - } - if(chbits != 0) + if(val) + { + const bool p_changed = rem_route.midiPort != mt->outPort(); + const bool c_changed = col != mt->outChannel(); + if(p_changed || c_changed) + { + // Avoid repeated idlings. And remember to un-idle outside of the loop! + if(!MusEGlobal::audio->isIdle()) + MusEGlobal::audio->msgIdle(true); + + if(p_changed && c_changed) + changed |= mt->setOutPortAndChannelAndUpdate(rem_route.midiPort, col, false); + else if(p_changed) + changed |= mt->setOutPortAndUpdate(rem_route.midiPort, false); + else if(c_changed) + changed |= mt->setOutChanAndUpdate(col, false); + } + break; + } + continue; + } + + #endif + MusECore::Route this_route(mt, col); + rem_route.channel = col; + const MusECore::Route& src = _isOutMenu ? this_route : rem_route; + const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: checking operations\n"); + // Connect if route does not exist. Allow it to reconnect a partial route. + if(val && MusECore::routeCanConnect(src, dst)) { - // Connect desired channels. - MusECore::Route bRoute(_track, chbits); - if(_isOutMenu) - MusEGlobal::audio->msgAddRoute(bRoute, aRoute); - else - MusEGlobal::audio->msgAddRoute(aRoute, bRoute); + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: adding AddRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); - } - //return; - } - else - { - int chbit = aRoute.channel; - MusECore::Route bRoute(_track, chbit); - int mdidx = aRoute.midiPort; - - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mdidx]; - MusECore::MidiDevice* md = mp->device(); - //if(!md) // Rem. Allow connections to ports with no device. - // return; - - //if(!(md->rwFlags() & 2)) - //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) - if(md && !(md->rwFlags() & (_isOutMenu ? 1 : 2))) - return; - - int chmask = 0; - MusECore::ciRoute iir = rl->begin(); - for (; iir != rl->end(); ++iir) - { - if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port? + // Disconnect if route exists. Allow it to reconnect a partial route. + else if(!val && MusECore::routeCanDisconnect(src, dst)) { - chmask = iir->channel; // Grab the channel mask. - break; - } + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: adding DeleteRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); + } } - if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? + } + + // Do Omni route... + if(matrix_wa->hasCheckBox()) + { + const bool cb_val = matrix_wa->checkBoxChecked(); + MusECore::Route this_route(mt); + rem_route.channel = -1; + const MusECore::Route& src = _isOutMenu ? this_route : rem_route; + const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Omni checking operations\n"); + // Connect if route does not exist. Allow it to reconnect a partial route. + if(cb_val && MusECore::routeCanConnect(src, dst)) { - // disconnect - if(_isOutMenu) - MusEGlobal::audio->msgRemoveRoute(bRoute, aRoute); - else - MusEGlobal::audio->msgRemoveRoute(aRoute, bRoute); + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Omni adding AddRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } - else + // Disconnect if route exists. Allow it to reconnect a partial route. + else if(!cb_val && MusECore::routeCanDisconnect(src, dst)) { - // connect - if(_isOutMenu) - MusEGlobal::audio->msgAddRoute(bRoute, aRoute); - else - MusEGlobal::audio->msgAddRoute(aRoute, bRoute); + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Omni adding DeleteRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); } - - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); } - } - } - // ... now take care of integer data items. - else if(qVariantCanConvert(action->data())) - { - int n = action->data().value(); - if(!_isOutMenu && n == 0) - MusEGlobal::muse->configMidiPorts(); - return; + } + break; + + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; } - } - else - { - MusECore::AudioTrack* t = (MusECore::AudioTrack*)_track; - MusECore::RouteList* rl = _isOutMenu ? t->outRoutes() : t->inRoutes(); - - if(!qVariantCanConvert(action->data())) - return; - - if(_isOutMenu) - { - MusECore::Route dstRoute = action->data().value(); - MusECore::Route srcRoute(t, dstRoute.channel, dstRoute.channels); - srcRoute.remoteChannel = dstRoute.remoteChannel; - - // check if route src->dst exists: - MusECore::ciRoute irl = rl->begin(); - for (; irl != rl->end(); ++irl) { - if (*irl == dstRoute) - break; - } - if (irl != rl->end()) { - // disconnect if route exists - MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); - } - else { - // connect if route does not exist - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); - } - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); - } - else + #else + MusECore::RouteList* rl = _isOutMenu ? mt->outRoutes() : mt->inRoutes(); + const int cols = matrix_wa->array()->columns(); + switch(rem_route.type) { - MusECore::Route srcRoute = action->data().value(); - - // Support Midi Port to Audio Input routes. - if(_track->type() == MusECore::Track::AUDIO_INPUT && srcRoute.type == MusECore::Route::MIDI_PORT_ROUTE) + case MusECore::Route::MIDI_PORT_ROUTE: { - // Check for custom midi channel select action. - PixmapButtonsWidgetAction* cs_wa = dynamic_cast(action); - if(cs_wa) - { - int chbits = cs_wa->currentState(); - srcRoute.channel = chbits; // Restore the desired route channels from the custom widget action state. - int mdidx = srcRoute.midiPort; - - int chmask = 0; - MusECore::ciRoute iir = rl->begin(); - for (; iir != rl->end(); ++iir) - if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) { // Is there already a route to this port? - chmask = iir->channel; // Grab the channel mask. + if(rem_route.isValid() && rem_route.midiPort != -1) + { + int chmask = 0; + // Is there already a route? + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + switch(ir->type) + { + case MusECore::Route::MIDI_PORT_ROUTE: + if(ir->midiPort == rem_route.midiPort) + chmask = ir->channel; // Grab the channels. break; - } - + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; + } + if(chmask != 0) + break; + } + + int chbits = 0; + for(int col = 0; col < cols; ++col) + { + if(matrix_wa->array()->value(col)) + chbits |= (1 << col); + } + // Only if something changed... if(chmask != chbits) { if(chmask != 0) { + MusECore::Route bRoute(mt, chmask); // Disconnect all existing channels. - MusECore::Route dstRoute(t, chmask); - MusEGlobal::audio->msgRemoveRoute(*iir, dstRoute); + if(_isOutMenu) + operations.add(MusECore::PendingOperationItem(bRoute, r, MusECore::PendingOperationItem::DeleteRoute)); + else + operations.add(MusECore::PendingOperationItem(r, bRoute, MusECore::PendingOperationItem::DeleteRoute)); } if(chbits != 0) { // Connect desired channels. - MusECore::Route dstRoute(t, chbits); - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); + MusECore::Route bRoute(mt, chbits); + if(_isOutMenu) + operations.add(MusECore::PendingOperationItem(bRoute, r, MusECore::PendingOperationItem::AddRoute)); + else + operations.add(MusECore::PendingOperationItem(r, bRoute, MusECore::PendingOperationItem::AddRoute)); } - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); - } - return; - } - else - { - int chbit = srcRoute.channel; - MusECore::Route dstRoute(t, chbit); - int mdidx = srcRoute.midiPort; - int chmask = 0; - MusECore::ciRoute iir = rl->begin(); - for (; iir != rl->end(); ++iir) - { - if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port? - { - chmask = iir->channel; // Grab the channel mask. - break; - } } - - if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? - { - //printf("routingPopupMenuActivated: removing src route ch:%d dst route ch:%d\n", srcRoute.channel, dstRoute.channel); - MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); - } - else - { - //printf("routingPopupMenuActivated: adding src route ch:%d dst route ch:%d\n", srcRoute.channel, dstRoute.channel); - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); - } - - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); - return; - } + } } - - MusECore::Route dstRoute(t, srcRoute.channel, srcRoute.channels); - dstRoute.remoteChannel = srcRoute.remoteChannel; - - MusECore::ciRoute irl = rl->begin(); - for (; irl != rl->end(); ++irl) { - if (*irl == srcRoute) - break; - } - if (irl != rl->end()) { - // disconnect - MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); - } - else { - // connect - MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); - } - MusEGlobal::audio->msgUpdateSoloStates(); - MusEGlobal::song->update(SC_ROUTE); + break; + + case MusECore::Route::TRACK_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; } - - - } - //else - //{ - //} -} + #endif -void RoutePopupMenu::prepare() -{ - ///disconnect(); - ///clear(); - - if(!_track) - return; - - connect(this, SIGNAL(triggered(QAction*)), SLOT(popupActivated(QAction*))); - - if(_track->isMidiTrack()) - { - MusECore::RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); - - int gid = 0; - QAction* act = 0; - - if(_isOutMenu) - { - // Support Midi Port to Audio Input track routes. - int port = ((MusECore::MidiTrack*)_track)->outPort(); - if(port >= 0 && port < MIDI_PORTS) + } + else { - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; - - // Do not list synth devices! Requiring valid device is desirable, - // but could lead to 'hidden' routes unless we add more support - // such as removing the existing routes when user changes flags. - // So for now, just list all valid ports whether read or write. - if(mp->device() && !mp->device()->isSynti()) + + #ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ + + MusECore::Route this_route(mt, rem_route.channel); + const MusECore::Route& src = _isOutMenu ? this_route : rem_route; + const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; + // Connect if route does not exist. Allow it to reconnect a partial route. + if(action->isChecked() && MusECore::routeCanConnect(src, dst)) { - MusECore::RouteList* mprl = mp->outRoutes(); - int chbits = 1 << ((MusECore::MidiTrack*)_track)->outChannel(); - //MusECore::MidiDevice* md = mp->device(); - //if(!md) - // continue; - - addSeparator(); - addAction(new MenuTitleItem(tr("Soloing chain"), this)); - PopupMenu* subp = new PopupMenu(this, true); - subp->setTitle(tr("Audio returns")); - addMenu(subp); - - MusECore::InputList* al = MusEGlobal::song->inputs(); - for (MusECore::ciAudioInput i = al->begin(); i != al->end(); ++i) + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Route: adding AddRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); + } + // Disconnect if route exists. Allow it to reconnect a partial route. + else if(!action->isChecked() && MusECore::routeCanDisconnect(src, dst)) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Route: adding DeleteRoute operation\n"); + operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); + } + + #else + int chbit = rem_route.channel; + MusECore::Route bRoute(mt, chbit); + int mdidx = rem_route.midiPort; + + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mdidx]; + MusECore::MidiDevice* md = mp->device(); + //if(!md) // Rem. Allow connections to ports with no device. + // return; + + if(md && !(md->rwFlags() & (_isOutMenu ? 1 : 2))) + return; + + int chmask = 0; + MusECore::ciRoute iir = rl->begin(); + for (; iir != rl->end(); ++iir) + { + if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port? { - MusECore::Track* t = *i; - QString s(t->name()); - act = subp->addAction(s); - act->setCheckable(true); - MusECore::Route r(t, chbits); - act->setData(qVariantFromValue(r)); - for(MusECore::ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) - { - if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == t && (ir->channel & chbits)) - { - act->setChecked(true); + chmask = iir->channel; // Grab the channel mask. break; - } - } - ++gid; } - } - } + } + if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? + { + // disconnect + if(_isOutMenu) + operations.add(MusECore::PendingOperationItem(bRoute, rem_route, MusECore::PendingOperationItem::DeleteRoute)); + else + operations.add(MusECore::PendingOperationItem(rem_route, bRoute, MusECore::PendingOperationItem::DeleteRoute)); + } + else + { + // connect + if(_isOutMenu) + operations.add(MusECore::PendingOperationItem(bRoute, rem_route, MusECore::PendingOperationItem::AddRoute)); + else + operations.add(MusECore::PendingOperationItem(rem_route, bRoute, MusECore::PendingOperationItem::AddRoute)); + } + #endif + + } } - else + + // If we are idling, we made some changes. Make sure to un-idle and update. + if(MusEGlobal::audio->isIdle()) { - // Warn if no devices available. Add an item to open midi config. - int pi = 0; - for( ; pi < MIDI_PORTS; ++pi) - { - MusECore::MidiDevice* md = MusEGlobal::midiPorts[pi].device(); - if(md && !md->isSynti() && (md->rwFlags() & 2)) - //if(md && (md->rwFlags() & 2 || md->isSynti()) ) // p4.0.27 Reverted p4.0.35 - break; - } - if(pi == MIDI_PORTS) + MusEGlobal::audio->msgIdle(false); + MusEGlobal::audio->msgUpdateSoloStates(); + MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); + } + } + // Midi device to Jack port routes - via Midi Track. + // NOTE: When a submenu's action is activated, its top-most menu always gets the activation call. + // Try this simple (ideal) method, otherwise it requires we abandon the '_route' member and store TWO + // routes in EACH action: In this case one is the midi device route and the other is the jack route. + else if(rem_route.type == MusECore::Route::JACK_ROUTE) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated rem_route is JACK_ROUTE\n"); + if(QAction* act = activeAction()) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated active action:%p\n", act); + if(act->data().canConvert()) { - act = addAction(tr("Warning: No input devices!")); - act->setCheckable(false); - act->setData(-1); - addSeparator(); + const MusECore::Route route = act->data().value(); + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated:Jack route: activePopupWidget:%p this:%p class:%s route type:%d\n", + QApplication::activePopupWidget(), this, metaObject()->className(), route.type); + + if(route.type == MusECore::Route::MIDI_PORT_ROUTE) + { + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated Midi port to Jack route\n"); + jackRouteActivated(action, route, rem_route, operations); + } } - act = addAction(QIcon(*settings_midiport_softsynthsIcon), tr("Open midi config...")); - act->setCheckable(false); - act->setData(gid); - addSeparator(); - ++gid; - -#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ + } + } +} + +void RoutePopupMenu::trackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations) +{ + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackPopupActivated: action text:%s checked:%d name:%s\n", + action->text().toLatin1().constData(), action->isChecked(), + action->objectName().toLatin1().constData()); + + MusECore::Track* track = _route.track; + // Make sure the track still exists. + if(MusEGlobal::song->tracks()->find(track) == MusEGlobal::song->tracks()->end()) + return; + + if(track->isMidiTrack()) + midiTrackPopupActivated(action, rem_route, operations); + else + audioTrackPopupActivated(action, rem_route, operations); +} - PixmapButtonsHeaderWidgetAction* wa_hdr = new PixmapButtonsHeaderWidgetAction("Input port/device", darkRedLedIcon, MIDI_CHANNELS, this); - addAction(wa_hdr); - ++gid; -#else - addAction(new MenuTitleItem("Input port/device", this)); -#endif - - for(int i = 0; i < MIDI_PORTS; ++i) +void RoutePopupMenu::routePopupActivated(QAction* action) +{ + if(!action || !_route.isValid() || actions().isEmpty()) + return; + + MusECore::PendingOperationList operations; + + // Handle any non-route items. + if(!action->data().canConvert()) + { + bool ok = false; + const int n = action->data().toInt(&ok); + if(ok) + { + switch(n) { - // NOTE: Could possibly list all devices, bypassing ports, but no, let's stick with ports. - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; - MusECore::MidiDevice* md = mp->device(); - //if(!md) - // continue; + case _OPEN_MIDI_CONFIG_: + MusEGlobal::muse->configMidiPorts(); + break; - // Do not list synth devices! - if( md && (!(md->rwFlags() & 2) || md->isSynti()) ) - // p4.0.27 Go ahead. Synths esp MESS send out stuff. Reverted p4.0.35 - //if( md && !(md->rwFlags() & 2) && !md->isSynti() ) - continue; - - //printf("MusE::prepareRoutingPopupMenu adding submenu portnum:%d\n", i); +#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ + case _ALIASES_WIDGET_ACTION_: + { + // Check for custom widget action. + RoutingMatrixWidgetAction* wa = qobject_cast(action); + if(wa) + { + if(wa->array()->value(0)) + MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RoutePreferFirstAlias; + else if(wa->array()->value(1)) + MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RoutePreferSecondAlias; + else + MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RoutePreferCanonicalName; + + MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); + } + } + break; - int chanmask = 0; - // To reduce number of routes required, from one per channel to just one containing a channel mask. - // Look for the first route to this midi port. There should always be only a single route for each midi port, now. - MusECore::ciRoute ir = rl->begin(); - for( ; ir != rl->end(); ++ir) + case _GROUPING_CHANNELS_WIDGET_ACTION_: { - if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) + // Check for custom widget action. + RoutingMatrixWidgetAction* wa = qobject_cast(action); + if(wa) { - // We have a route to the midi port. Grab the channel mask. - chanmask = ir->channel; - break; + if(wa->array()->value(0)) + MusEGlobal::config.routerGroupingChannels = 1; + else + MusEGlobal::config.routerGroupingChannels = 2; + + MusEGlobal::song->update(SC_ROUTER_CHANNEL_GROUPING); } } - // List ports with no device, but with routes to this track, in the main popup. - if(!md && ir == rl->end()) - continue; + break; +#endif -#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ - - PixmapButtonsWidgetAction* wa = new PixmapButtonsWidgetAction(QString::number(i + 1) + ":" + (md ? md->name() : tr("")), - redLedIcon, darkRedLedIcon, MIDI_CHANNELS, chanmask, this); - MusECore::Route srcRoute(i, 0); // Ignore the routing channels - our action holds the channels. - //wa->setData(id++); - wa->setData(qVariantFromValue(srcRoute)); - addAction(wa); - ++gid; - -#else - - PopupMenu* subp = new PopupMenu(this, true); - subp->setTitle(QString("%1:").arg(i+1) + (md ? md->name() : tr(""))); + case _SHOW_CANONICAL_NAMES_: + { + MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RoutePreferCanonicalName; + MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); + } + break; - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + case _SHOW_FIRST_ALIASES_: { - act = subp->addAction(QString("Channel %1").arg(ch+1)); - act->setCheckable(true); - int chbit = 1 << ch; - MusECore::Route srcRoute(i, chbit); // In accordance with channel mask, use the bit position. - act->setData(qVariantFromValue(srcRoute)); - if(chanmask & chbit) // Is the channel already set? Show item check mark. - act->setChecked(true); - ++gid; + MusEGlobal::config.preferredRouteNameOrAlias = action->isChecked() ? + MusEGlobal::RoutePreferFirstAlias : MusEGlobal::RoutePreferCanonicalName; + MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); } - //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. - act = subp->addAction(tr("Toggle all")); - //act->setCheckable(true); - MusECore::Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. - act->setData(qVariantFromValue(togRoute)); - ++gid; - addMenu(subp); + break; -#endif // _USE_CUSTOM_WIDGET_ACTIONS_ + case _SHOW_SECOND_ALIASES_: + { + MusEGlobal::config.preferredRouteNameOrAlias = action->isChecked() ? + MusEGlobal::RoutePreferSecondAlias : MusEGlobal::RoutePreferCanonicalName; + MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); + } + break; + + case _OPEN_ROUTING_DIALOG_: + MusEGlobal::muse->startRouteDialog(); + break; + default: + break; } + } + + return; + } + + DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::popupActivated: action data is a Route\n"); +// MusECore::Route rem_route = action->data().value(); + + // Support grouping together of channels. + int act_group_sz = 0; + int act_idx = 0; + int act_start = 0; +// int act_count = 0; + bool use_act_list = false; + QList < QAction* > act_list; + const QActionGroup* act_group = action->actionGroup(); + if(act_group && !act_group->isExclusive()) + { + act_list = act_group->actions(); + act_idx = act_list.indexOf(action); + if(act_idx != -1) + { + use_act_list = true; + act_group_sz = act_list.size(); - #if 0 - // p4.0.17 List ports with no device and no in routes, in a separate popup. - PopupMenu* morep = new PopupMenu(pup, true); - morep->setTitle(tr("More...")); - for(int i = 0; i < MIDI_PORTS; ++i) +// Attempt to optimize by only doing the actions whose channels changed. Flawed. Just do the whole list. +// act_start = act_idx; +// if((act_start + MusEGlobal::config.routerGroupingChannels) > act_group_sz) +// act_start = act_group_sz - MusEGlobal::config.routerGroupingChannels; +// if(act_start < 0 ) +// act_start = 0; +// act_count = MusEGlobal::config.routerGroupingChannels; +// if((act_start + act_count) > act_group_sz) +// act_count = act_group_sz - act_start; + +// FIXME TODO: If an external event causes a connection 'behind our back' while the menu is open, we need to update the menu by +// detecting the song changed SC_ROUTE flag. Otherwise when the menu checks all the actions against current connections, it finds +// a connection and tries to turn it off because we've not updated the menu and that channel is 'off' in the menu right now. + } + } + + while(1) + { + QAction* act = use_act_list ? act_list.at(act_start) : action; + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::popupActivated this:%p act:%p active action:%p act_group_sz:%d act_start:%d\n", // act_count:%d\n", + this, act, activeAction(), act_group_sz, act_start); //, act_count); // REMOVE Tim. + if(!act) + break; + MusECore::Route rem_route = act->data().value(); + switch(_route.type) + { + case MusECore::Route::TRACK_ROUTE: + trackPopupActivated(act, rem_route, operations); + break; + + case MusECore::Route::JACK_ROUTE: + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + jackRouteActivated(act, _route, rem_route, operations); + break; + + case MusECore::Route::MIDI_PORT_ROUTE: + break; + + } + if(use_act_list) + { + ++act_start; +// if(--act_count == 0) + if(--act_group_sz == 0) + break; + } + else + break; + } + + if(!operations.empty()) + { + DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::popupActivated: executing operations\n"); + MusEGlobal::audio->msgExecutePendingOperations(operations, true); +// MusEGlobal::song->update(SC_ROUTE); + } +} + +void RoutePopupMenu::prepare() +{ + if(!_route.isValid()) + return; + + connect(this, SIGNAL(triggered(QAction*)), SLOT(routePopupActivated(QAction*))); + + QAction* route_act = addAction(tr("Open advanced router...")); + route_act->setCheckable(false); + route_act->setData(_OPEN_ROUTING_DIALOG_); + + switch(_route.type) + { + case MusECore::Route::TRACK_ROUTE: + { + MusECore::Track* const track = _route.track; + if(track->isMidiTrack()) { - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; - if(mp->device()) - continue; - - PopupMenu* subp = new PopupMenu(morep, true); - subp->setTitle(QString("%1:%2").arg(i).arg(tr(""))); - - // MusE-2: Check this - needed with QMenu? Help says no. No - verified, it actually causes double triggers! - //connect(subp, SIGNAL(triggered(QAction*)), pup, SIGNAL(triggered(QAction*))); - //connect(subp, SIGNAL(aboutToHide()), pup, SIGNAL(aboutToHide())); - - iRoute ir = rl->begin(); - for( ; ir != rl->end(); ++ir) + QAction* act = 0; + // Warn if no devices available. Add an item to open midi config. + int pi = 0; + for( ; pi < MIDI_PORTS; ++pi) { - if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) + MusECore::MidiDevice* md = MusEGlobal::midiPorts[pi].device(); + //if(md && !md->isSynti() && (md->rwFlags() & 2)) + //if(md && (md->rwFlags() & 2 || md->isSynti()) ) // p4.0.27 Reverted p4.0.35 + if(md && (md->rwFlags() & (_isOutMenu ? 1 : 2))) // Allow synth as input. break; } - if(ir != rl->end()) - continue; - - for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + if(pi == MIDI_PORTS) { - act = subp->addAction(QString("Channel %1").arg(ch+1)); - act->setCheckable(true); - act->setData(gid); - - int chbit = 1 << ch; - MusECore::Route srcRoute(i, chbit); // In accordance with new channel mask, use the bit position. - - gRoutingMenuMap.insert( pRouteMenuMap(gid, srcRoute) ); - - //if(chanmask & chbit) // Is the channel already set? Show item check mark. - // act->setChecked(true); - - ++gid; + if(_isOutMenu) + act = addAction(tr("Warning: No output devices!")); + else + act = addAction(tr("Warning: No input devices!")); + act->setCheckable(false); + act->setData(-1); } - //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. - act = subp->addAction(QString("Toggle all")); - //act->setCheckable(true); - act->setData(gid); - MusECore::Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. - gRoutingMenuMap.insert( pRouteMenuMap(gid, togRoute) ); - ++gid; - morep->addMenu(subp); - } - pup->addMenu(morep); - #endif - + act = addAction(QIcon(*settings_midiport_softsynthsIcon), tr("Open midi config...")); + act->setCheckable(false); + act->setData(_OPEN_MIDI_CONFIG_); + } } - return; + break; + + default: + break; } + + addSeparator(); + + if(_isOutMenu) + addAction(new MenuTitleItem(tr("Output routes:"), this)); else + addAction(new MenuTitleItem(tr("Input routes:"), this)); + //addSeparator(); + + switch(_route.type) { - MusECore::AudioTrack* t = (MusECore::AudioTrack*)_track; - int channel = t->channels(); - if(_isOutMenu) + case MusECore::Route::TRACK_ROUTE: { - MusECore::RouteList* orl = t->outRoutes(); - - QAction* act = 0; - int gid = 0; - gid = 0; - - switch(_track->type()) + MusECore::Track* const track = _route.track; + if(track->isMidiTrack()) { - case MusECore::Track::AUDIO_OUTPUT: + QAction* act = 0; + addMidiPorts(track, this, _isOutMenu, true, _isOutMenu); + if(_isOutMenu) { - for(int i = 0; i < channel; ++i) + +#ifdef _USE_SIMPLIFIED_SOLO_CHAIN_ + // Support Midi Track to Audio Input track soloing chain routes. + // Support omni routes only, because if channels are supported, the graphical router becomes more complicated. + const MusECore::InputList* const il = MusEGlobal::song->inputs(); + if(!il->empty()) { - char buffer[128]; - snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), i+1); - MenuTitleItem* titel = new MenuTitleItem(QString(buffer), this); - addAction(titel); - - if(!MusEGlobal::checkAudioDevice()) - { - clear(); - return; - } - std::list ol = MusEGlobal::audioDevice->inputPorts(); - for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) + addSeparator(); + addAction(new MenuTitleItem(tr("Soloing chain"), this)); + RoutePopupMenu* subp = new RoutePopupMenu(_route, this, _isOutMenu, _broadcastChanges); + subp->setTitle(tr("Audio returns")); + for(MusECore::ciAudioInput ai = il->begin(); ai != il->end(); ++ai) { - act = addAction(*ip); + // Add omni route: + MusECore::Track* t = *ai; + act = subp->addAction(t->name()); act->setCheckable(true); + const MusECore::Route r(t, -1); + act->setData(QVariant::fromValue(r)); + if(track->outRoutes()->contains(r)) + act->setChecked(true); + } + addMenu(subp); + } +#else // _USE_SIMPLIFIED_SOLO_CHAIN_ + // Support Midi Port to Audio Input track routes. + int port = ((MusECore::MidiTrack*)track)->outPort(); + if(port >= 0 && port < MIDI_PORTS) + { + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; + + // Do not list synth devices! Requiring valid device is desirable, + // but could lead to 'hidden' routes unless we add more support + // such as removing the existing routes when user changes flags. + // So for now, just list all valid ports whether read or write. + if(mp->device() && !mp->device()->isSynti()) + { + MusECore::RouteList* rl = mp->outRoutes(); + //int chbits = 1 << ((MusECore::MidiTrack*)track)->outChannel(); + //MusECore::MidiDevice* md = mp->device(); + //if(!md) + // continue; - MusECore::Route dst(*ip, true, i, MusECore::Route::JACK_ROUTE); - act->setData(qVariantFromValue(dst)); - ++gid; - for(MusECore::ciRoute ir = orl->begin(); ir != orl->end(); ++ir) + addSeparator(); + addAction(new MenuTitleItem(tr("Soloing chain"), this)); + PopupMenu* subp = new PopupMenu(this, true); + subp->setTitle(tr("Audio returns")); + addMenu(subp); + + MusECore::InputList* al = MusEGlobal::song->inputs(); + +#ifdef _USE_CUSTOM_WIDGET_ACTIONS_ + for (MusECore::ciAudioInput ai = al->begin(); ai != al->end(); ++ai) { - if(*ir == dst) - { + // Add omni route: + MusECore::Track* t = *ai; + act = subp->addAction(t->name()); + act->setCheckable(true); + const MusECore::Route r(t, -1); + act->setData(QVariant::fromValue(r)); + if(rl->exists(r)) act->setChecked(true); - break; + + // Add channel routes: + RoutePopupMenu* subp = new RoutePopupMenu(_route, this, _isOutMenu, _broadcastChanges); + wa_subp->addAction(new MenuTitleItem(tr("Channels"), this)); + act->setMenu(wa_subp); + //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(1, MIDI_CHANNELS, redLedIcon, darkRedLedIcon, this); + RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(1, MIDI_CHANNELS, 0, 0, this); + wa->setData(QVariant::fromValue(r)); // Ignore the routing channel and channels - our action holds the channels. + int chans = 0; + // Is there already a route? + for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + switch(ir->type) + { + case MusECore::Route::TRACK_ROUTE: + if(ir->track == t) + chans = ir->channel; // Grab the channels. + break; + case MusECore::Route::MIDI_PORT_ROUTE: + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_DEVICE_ROUTE: + break; + } + if(chans != 0) + break; + } + if(chans != 0 && chans != -1) + { + for(int col = 0; col < MIDI_CHANNELS; ++col) + { + if(chans & (1 << col)) + wa->array()->setValue(0, col, true); + } + } + + // Must rebuild array after text changes. + wa->updateChannelArray(); + wa_subp->addAction(wa); + } +#else + int chbits = 1 << ((MusECore::MidiTrack*)track)->outChannel(); + for (MusECore::ciAudioInput i = al->begin(); i != al->end(); ++i) + { + MusECore::Track* t = *i; + QString s(t->name()); + act = subp->addAction(s); + act->setCheckable(true); + MusECore::Route r(t, chbits); + act->setData(QVariant::fromValue(r)); + for(MusECore::ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) + { + if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == t && (ir->channel & chbits)) + { + act->setChecked(true); + break; + } } } +#endif // _USE_CUSTOM_WIDGET_ACTIONS_ + + } + } +#endif // _USE_SIMPLIFIED_SOLO_CHAIN_ + + } + else + { + #if 0 + // p4.0.17 List ports with no device and no in routes, in a separate popup. + PopupMenu* morep = new PopupMenu(pup, true); + morep->setTitle(tr("More...")); + for(int i = 0; i < MIDI_PORTS; ++i) + { + MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; + if(mp->device()) + continue; + + PopupMenu* subp = new PopupMenu(morep, true); + subp->setTitle(QString("%1:%2").arg(i).arg(tr(""))); + + // MusE-2: Check this - needed with QMenu? Help says no. No - verified, it actually causes double triggers! + //connect(subp, SIGNAL(triggered(QAction*)), pup, SIGNAL(triggered(QAction*))); + //connect(subp, SIGNAL(aboutToHide()), pup, SIGNAL(aboutToHide())); + + iRoute ir = rl->begin(); + for( ; ir != rl->end(); ++ir) + { + if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) + break; } - if(i+1 != channel) - addSeparator(); + if(ir != rl->end()) + continue; + + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + act = subp->addAction(QString("Channel %1").arg(ch+1)); + act->setCheckable(true); + act->setData(gid); + + int chbit = 1 << ch; + MusECore::Route srcRoute(i, chbit); // In accordance with new channel mask, use the bit position. + + gRoutingMenuMap.insert( pRouteMenuMap(gid, srcRoute) ); + + //if(chanmask & chbit) // Is the channel already set? Show item check mark. + // act->setChecked(true); + + ++gid; + } + //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. + act = subp->addAction(QString("Toggle all")); + //act->setCheckable(true); + act->setData(gid); + MusECore::Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. + gRoutingMenuMap.insert( pRouteMenuMap(gid, togRoute) ); + ++gid; + morep->addMenu(subp); } + pup->addMenu(morep); + #endif - // - // Display using separate menu for audio inputs: - // - addSeparator(); - addAction(new MenuTitleItem(tr("Soloing chain"), this)); - PopupMenu* subp = new PopupMenu(this, true); - subp->setTitle(tr("Audio returns")); - addMenu(subp); - gid = addInPorts(t, subp, gid, -1, -1, true); - // - // Display all in the same menu: - // - //addSeparator(); - //MenuTitleItem* title = new MenuTitleItem(tr("Audio returns"), this); - //addAction(title); - //gid = addInPorts(t, this, gid, -1, -1, true); } - break; - case MusECore::Track::AUDIO_SOFTSYNTH: - gid = addMultiChannelPorts(t, this, gid, true); - break; - - case MusECore::Track::AUDIO_INPUT: - case MusECore::Track::WAVE: - case MusECore::Track::AUDIO_GROUP: - case MusECore::Track::AUDIO_AUX: - gid = addWavePorts( t, this, gid, -1, -1, true); - gid = addOutPorts( t, this, gid, -1, -1, true); - gid = addGroupPorts( t, this, gid, -1, -1, true); - gid = nonSyntiTrackAddSyntis(t, this, gid, true); - break; - default: - clear(); - return; - } - } - else - { - if(_track->type() == MusECore::Track::AUDIO_AUX) return; - - MusECore::RouteList* irl = t->inRoutes(); - - QAction* act = 0; - int gid = 0; - gid = 0; - - switch(_track->type()) + } + else { - case MusECore::Track::AUDIO_INPUT: + MusECore::AudioTrack* t = (MusECore::AudioTrack*)track; + MusECore::RouteCapabilitiesStruct rcaps = t->routeCapabilities(); + + + if(_isOutMenu) { - for(int i = 0; i < channel; ++i) + const int t_ochs = rcaps._trackChannels._outChannels; + int gid = 0; + + switch(track->type()) { - char buffer[128]; - snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), i+1); - MenuTitleItem* titel = new MenuTitleItem(QString(buffer), this); - addAction(titel); - - if(!MusEGlobal::checkAudioDevice()) - { + case MusECore::Track::AUDIO_OUTPUT: + { + addGroupingChannelsAction(this); + addJackPorts(_route, this); + + if(!MusEGlobal::song->inputs()->empty()) + { + // + // Display using separate menu for audio inputs: + // + addSeparator(); + addAction(new MenuTitleItem(tr("Soloing chain"), this)); + RoutePopupMenu* subp = new RoutePopupMenu(_route, this, _isOutMenu, _broadcastChanges); + subp->setTitle(tr("Audio returns")); + addMenu(subp); + gid = addInPorts(t, subp, gid, -1, -1, true); + // + // Display all in the same menu: + // + //addSeparator(); + //MenuTitleItem* title = new MenuTitleItem(tr("Audio returns"), this); + //addAction(title); + //gid = addInPorts(t, this, gid, -1, -1, true); + } + } + break; + + case MusECore::Track::AUDIO_INPUT: + case MusECore::Track::WAVE: + case MusECore::Track::AUDIO_GROUP: + case MusECore::Track::AUDIO_AUX: + case MusECore::Track::AUDIO_SOFTSYNTH: + if(t_ochs > 0) + { + addGroupingChannelsAction(this); + addAction(new RoutingMatrixHeaderWidgetAction(tr("Omni"), tr("Tracks"), QString(), this)); + gid = addWavePorts( t, this, gid, -1, -1, true); + gid = addOutPorts( t, this, gid, -1, -1, true); + gid = addGroupPorts( t, this, gid, -1, -1, true); + gid = addSynthPorts( t, this, gid, -1, -1, true); + } + break; + + default: clear(); return; - } - std::list ol = MusEGlobal::audioDevice->outputPorts(); - for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) + break; + } + + #ifndef _USE_CUSTOM_WIDGET_ACTIONS_ + switch(_track->type()) + { + case MusECore::Track::AUDIO_INPUT: + case MusECore::Track::WAVE: + case MusECore::Track::AUDIO_GROUP: + case MusECore::Track::AUDIO_AUX: + case MusECore::Track::AUDIO_SOFTSYNTH: + if(t_ochs > 0) + { + addSeparator(); + addAction(new MenuTitleItem(tr("Channels"), this)); + for(int i = 0; i < t_ochs; ++i) + { + PopupMenu* subp = new PopupMenu(this, true); + subp->setTitle(QString::number(i + 1)); + subp->addAction(new MenuTitleItem(tr("Destinations:"), this)); + addMenu(subp); + gid = addWavePorts( t, subp, gid, i, 1, true); + gid = addOutPorts( t, subp, gid, i, 1, true); + gid = addGroupPorts(t, subp, gid, i, 1, true); + gid = addSynthPorts(t, subp, gid, i, 1, true); + } + } + break; + + default: + break; + } + #endif + + } + else + { + if(track->type() == MusECore::Track::AUDIO_AUX) + return; + + const int t_ichs = rcaps._trackChannels._inChannels; + int gid = 0; + + switch(track->type()) + { + case MusECore::Track::AUDIO_INPUT: { - act = addAction(*ip); - act->setCheckable(true); + addGroupingChannelsAction(this); + addJackPorts(_route, this); - MusECore::Route dst(*ip, true, i, MusECore::Route::JACK_ROUTE); - act->setData(qVariantFromValue(dst)); - ++gid; - for(MusECore::ciRoute ir = irl->begin(); ir != irl->end(); ++ir) + if(!MusEGlobal::song->outputs()->empty() || !MusEGlobal::song->midis()->empty()) { - if(*ir == dst) + RoutePopupMenu* subp; + // + // Display using separate menus for midi ports and audio outputs: + // + addSeparator(); + addAction(new MenuTitleItem(tr("Soloing chain"), this)); + if(!MusEGlobal::song->outputs()->empty()) { - act->setChecked(true); - break; + subp = new RoutePopupMenu(_route, this, _isOutMenu, _broadcastChanges); + subp->setTitle(tr("Audio sends")); + addMenu(subp); + gid = addOutPorts(t, subp, gid, -1, -1, false); + } + if(!MusEGlobal::song->midis()->empty()) + { + subp = new RoutePopupMenu(_route, this, true, _broadcastChanges); + subp->setTitle(tr("Midi sends")); + addMenu(subp); + addMidiTracks(t, subp, false); + // + // Display all in the same menu: + // + //addAction(new MenuTitleItem(tr("Audio sends"), this)); + //gid = addOutPorts(t, this, gid, -1, -1, false); + //addSeparator(); + //addAction(new MenuTitleItem(tr("Midi sends"), this)); + //addMidiPorts(t, this, gid, false); } } } - if(i+1 != channel) - addSeparator(); - } - - // - // Display using separate menus for midi ports and audio outputs: - // - addSeparator(); - addAction(new MenuTitleItem(tr("Soloing chain"), this)); - PopupMenu* subp = new PopupMenu(this, true); - subp->setTitle(tr("Audio sends")); - addMenu(subp); - gid = addOutPorts(t, subp, gid, -1, -1, false); - subp = new PopupMenu(this, true); - subp->setTitle(tr("Midi port sends")); - addMenu(subp); - addMidiPorts(t, subp, gid, false); - // - // Display all in the same menu: - // - //addAction(new MenuTitleItem(tr("Audio sends"), this)); - //gid = addOutPorts(t, this, gid, -1, -1, false); - //addSeparator(); - //addAction(new MenuTitleItem(tr("Midi sends"), this)); - //addMidiPorts(t, this, gid, false); - } - break; - case MusECore::Track::AUDIO_OUTPUT: - gid = addWavePorts( t, this, gid, -1, -1, false); - gid = addInPorts( t, this, gid, -1, -1, false); - gid = addGroupPorts(t, this, gid, -1, -1, false); - gid = addAuxPorts( t, this, gid, -1, -1, false); - gid = nonSyntiTrackAddSyntis(t, this, gid, false); - break; - case MusECore::Track::WAVE: - gid = addWavePorts( t, this, gid, -1, -1, false); - gid = addInPorts( t, this, gid, -1, -1, false); - gid = addGroupPorts(t, this, gid, -1, -1, false); - gid = addAuxPorts( t, this, gid, -1, -1, false); - gid = nonSyntiTrackAddSyntis(t, this, gid, false); - break; - case MusECore::Track::AUDIO_GROUP: - gid = addWavePorts( t, this, gid, -1, -1, false); - gid = addInPorts( t, this, gid, -1, -1, false); - gid = addGroupPorts(t, this, gid, -1, -1, false); - gid = addAuxPorts( t, this, gid, -1, -1, false); - gid = nonSyntiTrackAddSyntis(t, this, gid, false); - break; - - case MusECore::Track::AUDIO_SOFTSYNTH: - gid = addMultiChannelPorts(t, this, gid, false); - break; - default: + break; + + case MusECore::Track::AUDIO_OUTPUT: + case MusECore::Track::WAVE: + case MusECore::Track::AUDIO_GROUP: + case MusECore::Track::AUDIO_SOFTSYNTH: + if(t_ichs > 0) + { + addGroupingChannelsAction(this); + addAction(new RoutingMatrixHeaderWidgetAction(tr("Omni"), tr("Tracks"), QString(), this)); + gid = addWavePorts( t, this, gid, -1, -1, false); + gid = addInPorts( t, this, gid, -1, -1, false); + gid = addGroupPorts(t, this, gid, -1, -1, false); + gid = addAuxPorts( t, this, gid, -1, -1, false); + gid = addSynthPorts(t, this, gid, -1, -1, false); + } + break; + + default: clear(); return; - } - } + break; + } + + #ifndef _USE_CUSTOM_WIDGET_ACTIONS_ + switch(_track->type()) + { + case MusECore::Track::AUDIO_OUTPUT: + case MusECore::Track::WAVE: + case MusECore::Track::AUDIO_GROUP: + case MusECore::Track::AUDIO_SOFTSYNTH: + if(t_ichs > 0) + { + addSeparator(); + addAction(new MenuTitleItem(tr("Channels"), this)); + for(int i = 0; i < t_ichs; ++i) + { + PopupMenu* subp = new PopupMenu(this, true); + subp->setTitle(QString::number(i + 1)); + subp->addAction(new MenuTitleItem(tr("Sources:"), this)); + addMenu(subp); + gid = addWavePorts( t, subp, gid, i, 1, false); + gid = addInPorts( t, subp, gid, i, 1, false); + gid = addGroupPorts(t, subp, gid, i, 1, false); + gid = addAuxPorts( t, subp, gid, i, 1, false); + gid = addSynthPorts(t, subp, gid, i, 1, false); + } + } + break; + + default: + break; + } + #endif + + } + } + + + } + break; + + case MusECore::Route::MIDI_DEVICE_ROUTE: + addJackPorts(_route, this); + break; + + case MusECore::Route::JACK_ROUTE: + case MusECore::Route::MIDI_PORT_ROUTE: + break; + } } -void RoutePopupMenu::exec(MusECore::Track* track, bool isOutput) +void RoutePopupMenu::exec(const MusECore::Route& route, bool isOutput) { - if(track) + if(route.isValid()) { - _track = track; + _route = route; _isOutMenu = isOutput; } prepare(); PopupMenu::exec(); } -void RoutePopupMenu::exec(const QPoint& p, MusECore::Track* track, bool isOutput) +void RoutePopupMenu::exec(const QPoint& p, const MusECore::Route& route, bool isOutput) { - if(track) + if(route.isValid()) { - _track = track; + _route = route; _isOutMenu = isOutput; } prepare(); PopupMenu::exec(p); } -void RoutePopupMenu::popup(const QPoint& p, MusECore::Track* track, bool isOutput) +void RoutePopupMenu::popup(const QPoint& p, const MusECore::Route& route, bool isOutput) { - if(track) + if(route.isValid()) { - _track = track; + _route = route; _isOutMenu = isOutput; } prepare(); diff -Nru muse-2.1.2/muse/widgets/routepopup.h muse-3.0.2+ds1/muse/widgets/routepopup.h --- muse-2.1.2/muse/widgets/routepopup.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/routepopup.h 2017-12-04 21:01:19.000000000 +0000 @@ -3,7 +3,7 @@ // Linux Music Editor // // RoutePopupMenu.h -// (C) Copyright 2011 Tim E. Real (terminator356 A T sourceforge D O T net) +// (C) Copyright 2011-2015 Tim E. Real (terminator356 A T sourceforge D O T net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -23,37 +23,50 @@ #ifndef __ROUTEPOPUPMENU_H__ #define __ROUTEPOPUPMENU_H__ -//#include #include "type_defs.h" +#include "route.h" +#include "custom_widget_actions.h" #include "popupmenu.h" namespace MusECore { class AudioTrack; class Track; +class PendingOperationList; } class QWidget; class QString; class QAction; class QPoint; +class QResizeEvent; namespace MusEGui { -//class PopupMenu; +class RoutingMatrixWidgetAction; -//class RoutePopupMenu : public QObject class RoutePopupMenu : public PopupMenu { Q_OBJECT - //PopupMenu* _pup; - MusECore::Track* _track; + // Route describing the caller (a track, device, jack port etc). + MusECore::Route _route; // Whether the route popup was shown by clicking the output routes button, or input routes button. bool _isOutMenu; - + // For keyboard navigation. + RoutePopupHit _lastHoveredHit; + // To inform a pending hover slot whether the hover was from mouse or not. + bool _hoverIsFromMouse; + // Whether to propagate changes to other selected tracks. + // This includes operating a control or using the universal up/down volume/ pan keys etc. + bool _broadcastChanges; + void init(); + // Prepares (fills) the popup before display. void prepare(); + // Recursive. Returns true if any text was changed. + bool updateItemTexts(PopupMenu* menu = 0); + void addGroupingChannelsAction(PopupMenu* lb); int addMenuItem(MusECore::AudioTrack* track, MusECore::Track* route_track, PopupMenu* lb, int id, int channel, int channels, bool isOutput); int addAuxPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); @@ -61,25 +74,53 @@ int addOutPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); int addGroupPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); int addWavePorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); - int addSyntiPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); - int addMultiChannelPorts(MusECore::AudioTrack* t, PopupMenu* pup, int id, bool isOutput); - int nonSyntiTrackAddSyntis(MusECore::AudioTrack* t, PopupMenu* lb, int id, bool isOutput); - int addMidiPorts(MusECore::AudioTrack* t, PopupMenu* pup, int id, bool isOutput); + void addMidiPorts(MusECore::Track* t, PopupMenu* pup, bool isOutput, bool show_synths, bool want_readable); + void addMidiTracks(MusECore::Track* t, PopupMenu* pup, bool isOutput); + int addSynthPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); + void addJackPorts(const MusECore::Route& route, PopupMenu* lb); + + void jackRouteActivated(QAction* action, const MusECore::Route& route, const MusECore::Route& rem_route, MusECore::PendingOperationList& operations); + void trackRouteActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations); + void audioTrackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations); + void midiTrackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations); + void trackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations); private slots: - void popupActivated(QAction*); + void routePopupHovered(QAction*); + void routePopupActivated(QAction*); void songChanged(MusECore::SongChangedFlags_t); + protected: + virtual bool event(QEvent*); + virtual void resizeEvent(QResizeEvent* e); + virtual void mouseReleaseEvent(QMouseEvent*); + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void keyPressEvent(QKeyEvent*); + // Updates item texts and the 'preferred alias action'. Returns true if any action was changed. + virtual bool preferredPortAliasChanged(); + // Updates items and the 'channel grouping'. Returns true if any action was changed. + virtual bool routerChannelGroupingChanged(); + + // For auto-breakup of a too-wide menu. Virtual. + virtual PopupMenu* cloneMenu(const QString& title, QWidget* parent = 0, bool /*stayOpen*/ = false) + { return new RoutePopupMenu(_route, title, parent, _isOutMenu, _broadcastChanges); } + public: - RoutePopupMenu(QWidget* parent = 0, MusECore::Track* track = 0, bool isOutput = false); - RoutePopupMenu(const QString& title, QWidget* parent = 0, MusECore::Track* track = 0, bool isOutput = false); + RoutePopupMenu(QWidget* parent = 0, bool isOutput = false, bool broadcastChanges = false); + RoutePopupMenu(const MusECore::Route& route, QWidget* parent = 0, bool isOutput = false, bool broadcastChanges = false); + RoutePopupMenu(const MusECore::Route& route, const QString& title, QWidget* parent = 0, bool isOutput = false, bool broadcastChanges = false); + bool broadcastChanges() const { return _broadcastChanges; } + void setBroadcastChanges(bool v) { _broadcastChanges = v; } + void updateRouteMenus(); - void exec(MusECore::Track* track = 0, bool isOutput = false); - void exec(const QPoint& p, MusECore::Track* track = 0, bool isOutput = false); - void popup(const QPoint& p, MusECore::Track* track = 0, bool isOutput = false); + + void exec(const MusECore::Route& route, bool isOutput = false); + void exec(const QPoint& p, const MusECore::Route& route, bool isOutput = false); + void popup(const QPoint& p, const MusECore::Route& route, bool isOutput = false); }; -} +} // namespace MusEGui #endif diff -Nru muse-2.1.2/muse/widgets/scldiv.cpp muse-3.0.2+ds1/muse/widgets/scldiv.cpp --- muse-2.1.2/muse/widgets/scldiv.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/scldiv.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -28,7 +28,7 @@ namespace MusEGui { -// ScaleDiv - A class for building scale divisions +// ScaleDiv - A class for building scale divisions // // The ScaleDiv class can build // linear and logarithmic scale divisions for specified @@ -36,7 +36,7 @@ // generate the major and minor step widths automatically. // A scale division has a minimum value, a maximum value, // a vector of major marks, and a vector of minor marks. -// +// // ScaleDiv uses implicit sharing for the mark vectors. // // Build a logarithmic scale division from 0.01 to 1000 @@ -80,26 +80,26 @@ static const double border_eps = 1.0e-10; static bool limRange(double &val, double v1, double v2, double eps_rel = 0.0, - double eps_abs = 0.0) - { + double eps_abs = 0.0) +{ - bool rv = TRUE; - double vmin = MusECore::qwtMin(v1, v2); - double vmax = MusECore::qwtMax(v1, v2); - double delta_min = MusECore::qwtMax(MusECore::qwtAbs(eps_rel * vmin), MusECore::qwtAbs(eps_abs)); - double delta_max = MusECore::qwtMax(MusECore::qwtAbs(eps_rel * vmax), MusECore::qwtAbs(eps_abs)); - - if (val < vmin) - { - if (val < vmin - delta_min) rv = FALSE; - val = vmin; - } - else if (val > vmax) - { - if (val > vmax + delta_max) rv = FALSE; - val = vmax; - } - return rv; + bool rv = true; + double vmin = MusECore::qwtMin(v1, v2); + double vmax = MusECore::qwtMax(v1, v2); + double delta_min = MusECore::qwtMax(MusECore::qwtAbs(eps_rel * vmin), MusECore::qwtAbs(eps_abs)); + double delta_max = MusECore::qwtMax(MusECore::qwtAbs(eps_rel * vmax), MusECore::qwtAbs(eps_abs)); + + if (val < vmin) + { + if (val < vmin - delta_min) rv = false; + val = vmin; + } + else if (val > vmax) + { + if (val > vmax + delta_max) rv = false; + val = vmax; + } + return rv; } @@ -112,12 +112,12 @@ //------------------------------------------------------------ ScaleDiv::ScaleDiv() - { - d_lBound = 0.0; - d_hBound = 0.0; - d_majStep = 0.0; - d_log = FALSE; - } +{ + d_lBound = 0.0; + d_hBound = 0.0; + d_majStep = 0.0; + d_log = false; +} //------------------------------------------------------------ //.F ScaleDiv::~ScaleDiv @@ -128,8 +128,8 @@ //------------------------------------------------------------ ScaleDiv::~ScaleDiv() - { - } +{ +} //------------------------------------------------------------ //.F ScaleDiv::ScaleDiv @@ -143,9 +143,9 @@ //------------------------------------------------------------ ScaleDiv::ScaleDiv(const ScaleDiv &s) - { - copy(s); - } +{ + copy(s); +} //------------------------------------------------------------ //.F ScaleDiv::operator= @@ -155,14 +155,14 @@ //.f ScaleDiv & ScaleDiv::operator=(const ScaleDiv &s) // //.u Parameters -//.p const ScaleDiv &s -- scale divison to be assigned +//.p const ScaleDiv &s -- scale division to be assigned //------------------------------------------------------------ ScaleDiv& ScaleDiv::operator=(const ScaleDiv &s) - { - copy(s); - return *this; - } +{ + copy(s); + return *this; +} //------------------------------------------------------------ //.F ScaleDiv::copy @@ -176,14 +176,14 @@ //------------------------------------------------------------ void ScaleDiv::copy(const ScaleDiv &s) - { - d_lBound = s.d_lBound; - d_hBound = s.d_hBound; - d_log = s.d_log; - d_majStep = s.d_majStep; - d_minMarks = s.d_minMarks; - d_majMarks = s.d_majMarks; - } +{ + d_lBound = s.d_lBound; + d_hBound = s.d_hBound; + d_log = s.d_log; + d_majStep = s.d_majStep; + d_minMarks = s.d_minMarks; + d_majMarks = s.d_majMarks; +} //------------------------------------------------------------ //.F ScaleDiv::rebuild @@ -222,35 +222,35 @@ // with a natural number n. // -- If the whole range is less than one decade, a linear scale // division will be built -// +// //.u Note // For logarithmic scales, the step width is measured in decades. //------------------------------------------------------------ bool ScaleDiv::rebuild(double x1, double x2, int maxMajSteps, int maxMinSteps, - bool log, double step, bool ascend) + bool log, double step, bool ascend) { - int rv; + int rv; - d_lBound = MusECore::qwtMin(x1, x2); - d_hBound = MusECore::qwtMax(x1, x2); - d_log = log; + d_lBound = MusECore::qwtMin(x1, x2); + d_hBound = MusECore::qwtMax(x1, x2); + d_log = log; - if (d_log) + if (d_log) rv = buildLogDiv(maxMajSteps,maxMinSteps,step); - else + else rv = buildLinDiv(maxMajSteps, maxMinSteps, step); - if ((!ascend) && (x2 < x1)) - { + if ((!ascend) && (x2 < x1)) + { d_lBound = x1; d_hBound = x2; MusECore::qwtTwistArray(d_majMarks.data(), d_majMarks.size()); MusECore::qwtTwistArray(d_minMarks.data(), d_minMarks.size()); - } + } - return rv; + return rv; } @@ -283,101 +283,101 @@ //------------------------------------------------------------ bool ScaleDiv::buildLinDiv(int maxMajSteps, int maxMinSteps, double step) - { - - int nMaj, nMin, minSize, i0,i,k; - double val, mval; - double firstTick, lastTick; - double minStep; - QVector buffer; - bool rv = TRUE; - - // parameter range check - maxMajSteps = MusECore::qwtMax(1, maxMajSteps); - maxMinSteps = MusECore::qwtMax(0, maxMinSteps); - step = MusECore::qwtAbs(step); - - // reset vectors - d_minMarks.resize(0); - d_majMarks.resize(0); - - if (d_lBound == d_hBound) return TRUE; - - // - // Set up major divisions - // - if (step == 0.0) - d_majStep = MusECore::qwtCeil125(MusECore::qwtAbs(d_hBound - d_lBound) * 0.999999 - / double(maxMajSteps)); - else - d_majStep = step; - - if (d_majStep == 0.0) return TRUE; - - firstTick = ceil( (d_lBound - step_eps * d_majStep) / d_majStep) * d_majStep; - lastTick = floor( (d_hBound + step_eps * d_majStep) / d_majStep) * d_majStep; - - nMaj = MusECore::qwtMin(10000, int(rint((lastTick - firstTick) / d_majStep)) + 1); - - d_majMarks.resize(nMaj); - MusECore::qwtLinSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick); - - // - // Set up minor divisions - // - if (maxMinSteps < 1) // no minor divs - return TRUE; - - minStep = MusECore::qwtCeil125( d_majStep / double(maxMinSteps) ); - - if (minStep == 0.0) return TRUE; - - nMin = MusECore::qwtAbs(int(rint(d_majStep / minStep))) - 1; // # minor steps per interval - - // Do the minor steps fit into the interval? - if ( MusECore::qwtAbs(double(nMin + 1) * minStep - d_majStep) > step_eps * d_majStep) - { - nMin = 1; - minStep = d_majStep * 0.5; - } - - // Are there minor ticks below the first major tick? - if (d_majMarks[0] > d_lBound ) - i0 = -1; - else - i0 = 0; - - // resize buffer to the maximum possible number of minor ticks - buffer.resize(nMin * (nMaj + 1)); - - // calculate minor ticks - if (rv) - { - minSize = 0; - for (i = i0; i < (int)d_majMarks.size(); i++) - { - if (i >= 0) - val = d_majMarks[i]; - else - val = d_majMarks[0] - d_majStep; - - for (k=0; k< nMin; k++) - { - mval = (val += minStep); - if (limRange(mval, d_lBound, d_hBound, border_eps)) - { - buffer[minSize] = mval; - minSize++; - } - } - } - //d_minMarks.duplicate(buffer.data(), minSize); - d_minMarks.resize(minSize); - qCopy(buffer.data(), buffer.data() + minSize, d_minMarks.begin()); - } +{ - return rv; + int nMaj, nMin, minSize, i0,i,k; + double val, mval; + double firstTick, lastTick; + double minStep; + QVector buffer; + bool rv = true; + + // parameter range check + maxMajSteps = MusECore::qwtMax(1, maxMajSteps); + maxMinSteps = MusECore::qwtMax(0, maxMinSteps); + step = MusECore::qwtAbs(step); + + // reset vectors + d_minMarks.resize(0); + d_majMarks.resize(0); + + if (d_lBound == d_hBound) return true; + + // + // Set up major divisions + // + if (step == 0.0) + d_majStep = MusECore::qwtCeil125(MusECore::qwtAbs(d_hBound - d_lBound) * 0.999999 + / double(maxMajSteps)); + else + d_majStep = step; + + if (d_majStep == 0.0) return true; + + firstTick = ceil( (d_lBound - step_eps * d_majStep) / d_majStep) * d_majStep; + lastTick = floor( (d_hBound + step_eps * d_majStep) / d_majStep) * d_majStep; + + nMaj = MusECore::qwtMin(10000, int(rint((lastTick - firstTick) / d_majStep)) + 1); + + d_majMarks.resize(nMaj); + MusECore::qwtLinSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick); + + // + // Set up minor divisions + // + if (maxMinSteps < 1) // no minor divs + return true; + + minStep = MusECore::qwtCeil125( d_majStep / double(maxMinSteps) ); + + if (minStep == 0.0) return true; + + nMin = MusECore::qwtAbs(int(rint(d_majStep / minStep))) - 1; // # minor steps per interval + + // Do the minor steps fit into the interval? + if ( MusECore::qwtAbs(double(nMin + 1) * minStep - d_majStep) > step_eps * d_majStep) + { + nMin = 1; + minStep = d_majStep * 0.5; + } + + // Are there minor ticks below the first major tick? + if (!d_majMarks.empty() && d_majMarks[0] > d_lBound ) + i0 = -1; + else + i0 = 0; + + // resize buffer to the maximum possible number of minor ticks + buffer.resize(nMin * (nMaj + 1)); + + // calculate minor ticks + if (rv) + { + minSize = 0; + for (i = i0; i < (int)d_majMarks.size(); i++) + { + if (i >= 0) + val = d_majMarks[i]; + else + val = d_majMarks[0] - d_majStep; + + for (k=0; k< nMin; k++) + { + mval = (val += minStep); + if (limRange(mval, d_lBound, d_hBound, border_eps)) + { + buffer[minSize] = mval; + minSize++; + } + } } + //d_minMarks.duplicate(buffer.data(), minSize); + d_minMarks.resize(minSize); + qCopy(buffer.data(), buffer.data() + minSize, d_minMarks.begin()); + } + + return rv; +} //------------------------------------------------------------ //.F ScaleDiv::buildLogDiv @@ -398,196 +398,196 @@ //------------------------------------------------------------ bool ScaleDiv::buildLogDiv(int maxMajSteps, int maxMinSteps, double majStep) +{ + double firstTick, lastTick; + double lFirst, lLast; + double val, sval, minStep, minFactor; + int nMaj, nMin, minSize, i, k, k0, kstep, kmax, i0; + bool rv = true; + double width; + + QVector buffer; + + + // Parameter range check + maxMajSteps = MusECore::qwtMax(1, MusECore::qwtAbs(maxMajSteps)); + maxMinSteps = MusECore::qwtMax(0, MusECore::qwtAbs(maxMinSteps)); + majStep = MusECore::qwtAbs(majStep); + + // boundary check + limRange(d_hBound, LOG_MIN, LOG_MAX); + limRange(d_lBound, LOG_MIN, LOG_MAX); + + // reset vectors + d_minMarks.resize(0); + d_majMarks.resize(0); + + if (d_lBound == d_hBound) return true; + + // scale width in decades + width = log10(d_hBound) - log10(d_lBound); + + // scale width is less than one decade -> build linear scale + if (width < 1.0) + { + rv = buildLinDiv(maxMajSteps, maxMinSteps, 0.0); + // convert step width to decades + if (d_majStep > 0) + d_majStep = log10(d_majStep); + + return rv; + } + + // + // Set up major scale divisions + // + if (majStep == 0.0) + d_majStep = MusECore::qwtCeil125( width * 0.999999 / double(maxMajSteps)); + else + d_majStep = majStep; + + // major step must be >= 1 decade + d_majStep = MusECore::qwtMax(d_majStep, 1.0); + + + lFirst = ceil((log10(d_lBound) - step_eps * d_majStep) / d_majStep) * d_majStep; + lLast = floor((log10(d_hBound) + step_eps * d_majStep) / d_majStep) * d_majStep; + + firstTick = exp10(lFirst); + lastTick = exp10(lLast); + + nMaj = MusECore::qwtMin(10000, int(rint(MusECore::qwtAbs(lLast - lFirst) / d_majStep)) + 1); + + d_majMarks.resize(nMaj); + MusECore::qwtLogSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick); + + + // + // Set up minor scale divisions + // + + if ((d_majMarks.size() < 1) || (maxMinSteps < 1)) return true; // no minor marks + + if (d_majStep < 1.1) // major step width is one decade + { + if (maxMinSteps >= 8) + { + k0 = 2; + kmax = 9; + kstep = 1; + minSize = (d_majMarks.size() + 1) * 8; + } + else if (maxMinSteps >= 4) + { + k0 = 2; + kmax = 8; + kstep = 2; + minSize = (d_majMarks.size() + 1) * 4; + } + else if (maxMinSteps >= 2) + { + k0 = 2; + kmax = 5; + kstep = 3; + minSize = (d_majMarks.size() + 1) * 2; + } + else { - double firstTick, lastTick; - double lFirst, lLast; - double val, sval, minStep, minFactor; - int nMaj, nMin, minSize, i, k, k0, kstep, kmax, i0; - int rv = TRUE; - double width; - - QVector buffer; - - - // Parameter range check - maxMajSteps = MusECore::qwtMax(1, MusECore::qwtAbs(maxMajSteps)); - maxMinSteps = MusECore::qwtMax(0, MusECore::qwtAbs(maxMinSteps)); - majStep = MusECore::qwtAbs(majStep); - - // boundary check - limRange(d_hBound, LOG_MIN, LOG_MAX); - limRange(d_lBound, LOG_MIN, LOG_MAX); - - // reset vectors - d_minMarks.resize(0); - d_majMarks.resize(0); - - if (d_lBound == d_hBound) return TRUE; - - // scale width in decades - width = log10(d_hBound) - log10(d_lBound); - - // scale width is less than one decade -> build linear scale - if (width < 1.0) - { - rv = buildLinDiv(maxMajSteps, maxMinSteps, 0.0); - // convert step width to decades - if (d_majStep > 0) - d_majStep = log10(d_majStep); - - return rv; - } - - // - // Set up major scale divisions - // - if (majStep == 0.0) - d_majStep = MusECore::qwtCeil125( width * 0.999999 / double(maxMajSteps)); - else - d_majStep = majStep; - - // major step must be >= 1 decade - d_majStep = MusECore::qwtMax(d_majStep, 1.0); - - - lFirst = ceil((log10(d_lBound) - step_eps * d_majStep) / d_majStep) * d_majStep; - lLast = floor((log10(d_hBound) + step_eps * d_majStep) / d_majStep) * d_majStep; - - firstTick = pow(10.0, lFirst); - lastTick = pow(10.0, lLast); - - nMaj = MusECore::qwtMin(10000, int(rint(MusECore::qwtAbs(lLast - lFirst) / d_majStep)) + 1); - - d_majMarks.resize(nMaj); - MusECore::qwtLogSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick); - - - // - // Set up minor scale divisions - // - - if ((d_majMarks.size() < 1) || (maxMinSteps < 1)) return TRUE; // no minor marks - - if (d_majStep < 1.1) // major step width is one decade - { - if (maxMinSteps >= 8) - { - k0 = 2; - kmax = 9; - kstep = 1; - minSize = (d_majMarks.size() + 1) * 8; - } - else if (maxMinSteps >= 4) - { - k0 = 2; - kmax = 8; - kstep = 2; - minSize = (d_majMarks.size() + 1) * 4; - } - else if (maxMinSteps >= 2) - { - k0 = 2; - kmax = 5; - kstep = 3; - minSize = (d_majMarks.size() + 1) * 2; - } - else - { - k0 = 5; - kmax = 5; - kstep = 1; - minSize = (d_majMarks.size() + 1); - } - - // resize buffer to the max. possible number of minor marks - buffer.resize(minSize); - - // Are there minor ticks below the first major tick? - if ( d_lBound < firstTick ) - i0 = -1; - else - i0 = 0; - - minSize = 0; - for (i = i0; i< (int)d_majMarks.size(); i++) - { - if (i >= 0) - val = d_majMarks[i]; - else - val = d_majMarks[0] / pow(10.0, d_majStep); - - for (k=k0; k<= kmax; k+=kstep) - { - sval = val * double(k); - if (limRange(sval, d_lBound, d_hBound, border_eps)) - { - buffer[minSize] = sval; - minSize++; - } - } - } - - // copy values into the minMarks array - //d_minMarks.duplicate(buffer.data(), minSize); - d_minMarks.resize(minSize); - qCopy(buffer.data(), buffer.data() + minSize, d_minMarks.begin()); - - - } - else // major step > one decade - { - - // substep width in decades, at least one decade - minStep = MusECore::qwtCeil125( (d_majStep - step_eps * (d_majStep / double(maxMinSteps))) - / double(maxMinSteps) ); - minStep = MusECore::qwtMax(1.0, minStep); - - // # subticks per interval - nMin = int(rint(d_majStep / minStep)) - 1; - - // Do the minor steps fit into the interval? - if ( MusECore::qwtAbs( double(nMin + 1) * minStep - d_majStep) > step_eps * d_majStep) - nMin = 0; - - if (nMin < 1) return TRUE; // no subticks - - // resize buffer to max. possible number of subticks - buffer.resize((d_majMarks.size() + 1) * nMin ); - - // substep factor = 10^substeps - minFactor = MusECore::qwtMax(pow(10,minStep), 10.0); - - // Are there minor ticks below the first major tick? - if ( d_lBound < firstTick ) - i0 = -1; - else - i0 = 0; - - minSize = 0; - for (i = i0; i< (int)d_majMarks.size(); i++) - { - if (i >= 0) - val = d_majMarks[i]; - else - val = firstTick / pow(10.0, d_majStep); - - for (k=0; k< nMin; k++) - { - sval = (val *= minFactor); - if (limRange(sval, d_lBound, d_hBound, border_eps)) - { - buffer[minSize] = sval; - minSize++; - } - } - } - //d_minMarks.duplicate(buffer.data(), minSize); - d_minMarks.resize(minSize); - qCopy(buffer.data(), buffer.data() + minSize, d_minMarks.begin()); + k0 = 5; + kmax = 5; + kstep = 1; + minSize = (d_majMarks.size() + 1); + } + + // resize buffer to the max. possible number of minor marks + buffer.resize(minSize); - } + // Are there minor ticks below the first major tick? + if ( d_lBound < firstTick ) + i0 = -1; + else + i0 = 0; + + minSize = 0; + for (i = i0; i< (int)d_majMarks.size(); i++) + { + if (i >= 0) + val = d_majMarks[i]; + else + val = d_majMarks[0] / exp10(d_majStep); + + for (k=k0; k<= kmax; k+=kstep) + { + sval = val * double(k); + if (limRange(sval, d_lBound, d_hBound, border_eps)) + { + buffer[minSize] = sval; + minSize++; + } + } + } + + // copy values into the minMarks array + //d_minMarks.duplicate(buffer.data(), minSize); + d_minMarks.resize(minSize); + qCopy(buffer.data(), buffer.data() + minSize, d_minMarks.begin()); + + + } + else // major step > one decade + { + + // substep width in decades, at least one decade + minStep = MusECore::qwtCeil125( (d_majStep - step_eps * (d_majStep / double(maxMinSteps))) + / double(maxMinSteps) ); + minStep = MusECore::qwtMax(1.0, minStep); + + // # subticks per interval + nMin = int(rint(d_majStep / minStep)) - 1; + + // Do the minor steps fit into the interval? + if ( MusECore::qwtAbs( double(nMin + 1) * minStep - d_majStep) > step_eps * d_majStep) + nMin = 0; + + if (nMin < 1) return true; // no subticks + + // resize buffer to max. possible number of subticks + buffer.resize((d_majMarks.size() + 1) * nMin ); + + // substep factor = 10^substeps + minFactor = MusECore::qwtMax(exp10(minStep), 10.0); + + // Are there minor ticks below the first major tick? + if ( d_lBound < firstTick ) + i0 = -1; + else + i0 = 0; + + minSize = 0; + for (i = i0; i< (int)d_majMarks.size(); i++) + { + if (i >= 0) + val = d_majMarks[i]; + else + val = firstTick / exp10(d_majStep); + + for (k=0; k< nMin; k++) + { + sval = (val *= minFactor); + if (limRange(sval, d_lBound, d_hBound, border_eps)) + { + buffer[minSize] = sval; + minSize++; + } + } + } + //d_minMarks.duplicate(buffer.data(), minSize); + d_minMarks.resize(minSize); + qCopy(buffer.data(), buffer.data() + minSize, d_minMarks.begin()); - return rv; + } + + return rv; } //------------------------------------------------------------ @@ -605,19 +605,19 @@ //------------------------------------------------------------ int ScaleDiv::operator==(const ScaleDiv &s) const - { - if (d_lBound != s.d_lBound) - return 0; - if (d_hBound != s.d_hBound) - return 0; - if (d_log != s.d_log) - return 0; - if (d_majStep != s.d_majStep) - return 0; - if (d_majMarks != s.d_majMarks) - return 0; - return (d_minMarks == s.d_minMarks); - } +{ + if (d_lBound != s.d_lBound) + return 0; + if (d_hBound != s.d_hBound) + return 0; + if (d_log != s.d_log) + return 0; + if (d_majStep != s.d_majStep) + return 0; + if (d_majMarks != s.d_majMarks) + return 0; + return (d_minMarks == s.d_minMarks); +} //------------------------------------------------------------ //.F ScaleDiv::operator!= @@ -634,9 +634,9 @@ //------------------------------------------------------------ int ScaleDiv::operator!=(const ScaleDiv &s) const - { - return (!(*this == s)); - } +{ + return (!(*this == s)); +} //------------------------------------------------------------ //.F ScaleDiv::reset @@ -647,17 +647,17 @@ //------------------------------------------------------------ void ScaleDiv::reset() - { - // reset vectors - d_minMarks.resize(0); - d_majMarks.resize(0); +{ + // reset vectors + d_minMarks.resize(0); + d_majMarks.resize(0); - d_lBound = 0.0; - d_hBound = 0.0; - d_majStep = 0.0; - d_log = FALSE; - } + d_lBound = 0.0; + d_hBound = 0.0; + d_majStep = 0.0; + d_log = false; +} } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/scldiv.h muse-3.0.2+ds1/muse/widgets/scldiv.h --- muse-2.1.2/muse/widgets/scldiv.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/scldiv.h 2017-12-04 21:01:19.000000000 +0000 @@ -63,7 +63,7 @@ double majStep() const { return d_majStep; } void reset(); bool rebuild(double lBound, double hBound, int maxMaj, int maxMin, - bool log, double step = 0.0, bool ascend = TRUE); + bool log, double step = 0.0, bool ascend = true); }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/scldraw.cpp muse-3.0.2+ds1/muse/widgets/scldraw.cpp --- muse-2.1.2/muse/widgets/scldraw.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/scldraw.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -5,6 +5,7 @@ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,11 +26,15 @@ #include #include +#include +#include +#include +#include +#include #include "mmath.h" #include "scldraw.h" - namespace MusEGui { int const ScaleDraw::minLen = 10; @@ -99,13 +104,6 @@ //------------------------------------------------------------ ScaleDraw::ScaleDraw() { -/* d_hpad = 6; - d_vpad = 3; - d_majLen = 8; - d_medLen = 6; - d_minLen = 4; - */ - d_hpad = 3; d_vpad = 1; d_majLen = 4; @@ -114,9 +112,12 @@ d_minAngle = -135 * 16; d_maxAngle = 135 * 16; - d_fmt = 'g'; + d_fmt = 'M'; // Metric suffix G M K. d_prec = 4; + d_drawBackBone = true; + d_textHighlightMode = TextHighlightNone; + // initialize scale and geometry setGeometry(0,0,100,Bottom); setScale(0,100,0,0,10); @@ -145,7 +146,7 @@ void ScaleDraw::setScale(double x1, double x2, int maxMajIntv, int maxMinIntv, double step, int logscale) { - d_scldiv.rebuild( x1, x2, maxMajIntv, maxMinIntv, logscale, step, FALSE ); + d_scldiv.rebuild( x1, x2, maxMajIntv, maxMinIntv, logscale, step, false ); setDblRange( d_scldiv.lBound(), d_scldiv.hBound(), d_scldiv.logScale()); } @@ -169,7 +170,21 @@ setDblRange(d_scldiv.lBound(),d_scldiv.hBound(),d_scldiv.logScale()); } - +QString ScaleDraw::composeLabelText(double val, char fmt, int prec) const +{ + if(fmt == 'M') + { + if(val > 1000000000.0) + return QString("%L1").arg(val / 1000000000.0, 0, 'g', prec) + "G"; + else if(val > 1000000.0) + return QString("%L1").arg(val / 1000000.0, 0, 'g', prec) + "M"; + else if(val > 1000.0) + return QString("%L1").arg(val / 1000.0, 0, 'g', prec) + "K"; + else + return QString("%L1").arg(val, 0, 'g', prec); + } + return QString("%L1").arg(val, 0, fmt, prec); +} //------------------------------------------------------------ //.F ScaleDraw::draw @@ -178,65 +193,75 @@ //.p QPainter *p -- the painter //------------------------------------------------------------ -void ScaleDraw::draw(QPainter *p) const +void ScaleDraw::draw(QPainter *p, const QPalette& palette, double curValue)// const { double val,hval,majTick; int i,k,kmax; - for (i=0; i< d_scldiv.majCnt(); i++) + p->setPen(palette.text().color()); + const int majCnt = d_scldiv.majCnt(); + const int minCnt = d_scldiv.minCnt(); + + for (i=0; i< majCnt; i++) { - val = d_scldiv.majMark(i); - drawTick(p, val, d_majLen); - drawLabel(p, val); + val = d_scldiv.majMark(i); + drawTick(p, palette, curValue, val, d_majLen); } + for (i=0; i< majCnt; i++) + { + val = d_scldiv.majMark(i); + drawLabel(p, palette, curValue, val, i == 0); + } + p->setPen(palette.text().color()); + if (d_scldiv.logScale()) { - for (i=0; i< d_scldiv.minCnt(); i++) - { - drawTick(p,d_scldiv.minMark(i),d_minLen); - } + for (i=0; i< minCnt; i++) + { + drawTick(p,palette,curValue,d_scldiv.minMark(i),d_minLen); + } } else { - k = 0; - kmax = d_scldiv.majCnt() - 1; - if (kmax > 0) - { - majTick = d_scldiv.majMark(0); - hval = majTick - 0.5 * d_scldiv.majStep(); - - for (i=0; i< d_scldiv.minCnt(); i++) - { - val = d_scldiv.minMark(i); - if (val > majTick) - { - if (k < kmax) - { - k++; - majTick = d_scldiv.majMark(k); - } - else - { - majTick += d_scldiv.majMark(kmax) + d_scldiv.majStep(); - } - hval = majTick - 0.5 * d_scldiv.majStep(); - - } - if (MusECore::qwtAbs(val-hval) < step_eps * d_scldiv.majStep()) - drawTick(p, val, d_medLen); - else - drawTick(p, val, d_minLen); - } - } + k = 0; + kmax = majCnt - 1; + if (kmax > 0) + { + majTick = d_scldiv.majMark(0); + hval = majTick - 0.5 * d_scldiv.majStep(); + + for (i=0; i< minCnt; i++) + { + val = d_scldiv.minMark(i); + if (val > majTick) + { + if (k < kmax) + { + k++; + majTick = d_scldiv.majMark(k); + } + else + { + majTick += d_scldiv.majMark(kmax) + d_scldiv.majStep(); + } + hval = majTick - 0.5 * d_scldiv.majStep(); + + } + if (MusECore::qwtAbs(val-hval) < step_eps * d_scldiv.majStep()) + drawTick(p, palette, curValue, val, d_medLen); + else + drawTick(p, palette, curValue, val, d_minLen); + } + } } // // draw backbone // - //if (d_baseEnabled) - drawBackbone(p); + if (d_drawBackBone) + drawBackbone(p, palette, curValue); } @@ -249,7 +274,7 @@ //.p QPainter *p, double val, int len //------------------------------------------------------------ -void ScaleDraw::drawTick(QPainter *p, double val, int len) const +void ScaleDraw::drawTick(QPainter *p, const QPalette& /*palette*/, double /*curValue*/, double val, int len) const { int tval = transform(val); double arc; @@ -263,11 +288,15 @@ break; case Bottom: - p->drawLine(tval, d_yorg, tval, d_yorg + len); break; + + case InsideHorizontal: + p->drawLine(tval, d_vpad + d_majLen - len, tval, d_vpad + d_majLen); + break; case Left: + case InsideVertical: p->drawLine(d_xorg, tval, d_xorg - len, tval); break; @@ -296,9 +325,6 @@ } - - - //------------------------------------------------------------ //.- //.F ScaleDraw::drawLabel @@ -308,7 +334,7 @@ //.p QPainter *p, double val // //------------------------------------------------------------ -void ScaleDraw::drawLabel(QPainter *p, double val) const +void ScaleDraw::drawLabel(QPainter *p, const QPalette& palette, double curValue, double val, bool isSpecialText) const { static QString label; @@ -316,7 +342,7 @@ static double pi_75 = M_PI * 0.75; double arc; - int xpos, ypos; + int xpos, ypos, x0, y0; int tval; QFontMetrics fm = p->fontMetrics(); @@ -327,27 +353,38 @@ if ((!d_scldiv.logScale()) && (MusECore::qwtAbs(val) < MusECore::qwtAbs(step_eps * d_scldiv.majStep()))) val = 0.0; - label.setNum(val, d_fmt, d_prec); + if(isSpecialText && !_specialText.isEmpty()) + label = _specialText; + else + label = composeLabelText(val, d_fmt, d_prec); switch(d_orient) { case Right: - p->drawText(d_xorg + d_majLen + d_hpad, - tval + (fm.ascent()-1) / 2, - label); + x0 = d_xorg + d_majLen + d_hpad; + y0 = tval + (fm.ascent()-1) / 2; break; case Left: - p->drawText(d_xorg - d_majLen - d_hpad - fm.width(label), - tval + (fm.ascent() -1) / 2, - label); + case InsideVertical: + x0 = d_xorg - d_majLen - d_hpad - fm.width(label); + y0 = tval + (fm.ascent() -1) / 2; break; case Bottom: - p->drawText(tval - (fm.width(label)-1) / 2, d_yorg + d_majLen + d_vpad + fm.ascent(), label); + x0 = tval - (fm.width(label)-1) / 2; + y0 = d_yorg + d_majLen + d_vpad + fm.ascent(); break; + + case InsideHorizontal: + x0 = tval - (fm.width(label)-1) / 2; + //y0 = d_yorg + d_majLen + d_vpad + fm.ascent(); + y0 = d_majLen + d_vpad + fm.ascent(); + break; + case Round: if ((tval > d_minAngle + 359 * 16) || (tval < d_minAngle - 359 * 16)) - break; +// break; + return; arc = double(tval) / 16.0 * M_PI / 180.0; @@ -360,49 +397,96 @@ if (arc < -pi_75) { - p->drawText(xpos - MusECore::qwtInt(double(fm.width(label)) - * (1.0 + (arc + pi_75) * M_2_PI) ), - ypos + fm.ascent() - 1, - label); + x0 = xpos - MusECore::qwtInt(double(fm.width(label)) + * (1.0 + (arc + pi_75) * M_2_PI)); + y0 = ypos + fm.ascent() - 1; } else if (arc < -M_PI_4) { - p->drawText(xpos - fm.width(label), - - - ypos - MusECore::qwtInt(double(fm.ascent() - 1) - * (arc + M_PI_4) * M_2_PI), - label); + x0 = xpos - fm.width(label); + y0 = ypos - MusECore::qwtInt(double(fm.ascent() - 1) + * (arc + M_PI_4) * M_2_PI); } else if (arc < pi_4) { - p->drawText(xpos + MusECore::qwtInt(double(fm.width(label)) - * ( arc - M_PI_4 ) * M_2_PI ), - ypos, - label); + x0 = xpos + MusECore::qwtInt(double(fm.width(label)) + * ( arc - M_PI_4 ) * M_2_PI ); + y0 = ypos; } else if (arc < pi_75) { - p->drawText(xpos, - ypos + MusECore::qwtInt(double(fm.ascent() - 1) - * (arc - M_PI_4) * M_2_PI), - label); + x0 = xpos; + y0 = ypos + MusECore::qwtInt(double(fm.ascent() - 1) + * (arc - M_PI_4) * M_2_PI); } else { - p->drawText(xpos - MusECore::qwtInt(double(fm.width(label)) - * ( arc - pi_75) * M_2_PI ), - ypos + fm.ascent() - 1, - label); + x0 = xpos - MusECore::qwtInt(double(fm.width(label)) + * ( arc - pi_75) * M_2_PI ); + y0 = ypos + fm.ascent() - 1; } break; case Top: default: - p->drawText(tval - (fm.width(label)-1) / 2, d_yorg - d_majLen - d_vpad, label); + x0 = tval - (fm.width(label)-1) / 2; + y0 = d_yorg - d_majLen - d_vpad; break; } + switch(d_textHighlightMode) + { + case TextHighlightNone: + p->setPen(palette.text().color()); + p->drawText(x0, y0, label); + break; + + case TextHighlightAlways: + p->setPen(palette.brightText().color()); + p->drawText(x0, y0, label); + break; + case TextHighlightSplit: + if(val > curValue) + { + p->setPen(palette.text().color()); + p->drawText(x0, y0, label); + } + else + { + p->setPen(palette.brightText().color()); + p->drawText(x0, y0, label); + } + break; + + case TextHighlightShadow: + // Text shadow: + p->setPen(Qt::black); + p->drawText(x0 + 1, y0 + 1, label); + // Text: + p->setPen(QColor(Qt::white).darker(120)); // Meh, not quite so bright, + p->drawText(x0, y0, label); + break; + + case TextHighlightSplitAndShadow: + //fprintf(stderr, "ScaleDraw::drawLabel val:%.20f curValue:%.20f\n", val, curValue); + if(val > curValue) + { + //fprintf(stderr, " drawing normal\n"); + p->setPen(palette.text().color()); + p->drawText(x0, y0, label); + } + else + { + // Text shadow: + p->setPen(Qt::black); + p->drawText(x0 + 1, y0 + 1, label); + // Text: + //p->setPen(palette.brightText().color().darker(120)); // Meh, not quite so bright, + p->setPen(QColor(Qt::white).darker(120)); // Meh, not quite so bright, + p->drawText(x0, y0, label); + } + break; + } } @@ -416,7 +500,7 @@ //.p QPainter *p // //------------------------------------------------------------ -void ScaleDraw::drawBackbone(QPainter *p) const +void ScaleDraw::drawBackbone(QPainter *p, const QPalette& /*palette*/, double /*curValue*/) const { int bw2; int a1, a2; @@ -426,6 +510,7 @@ switch(d_orient) { case Left: + case InsideVertical: p->drawLine(d_xorg - bw2, d_yorg, d_xorg - bw2, d_yorg + d_len - 1); break; case Right: @@ -448,6 +533,9 @@ case Bottom: p->drawLine(d_xorg, d_yorg+bw2, d_xorg + d_len - 1, d_yorg+bw2); break; + case InsideHorizontal: + p->drawLine(d_xorg, d_majLen + d_vpad, d_xorg + d_len - 1, d_majLen + d_vpad); + break; default: p->drawLine(d_xorg, d_yorg, d_xorg + d_len - 1, d_yorg); break; @@ -521,6 +609,7 @@ { case Left: case Right: + case InsideVertical: setIntRange(d_yorg + d_len - 1, d_yorg); break; case Round: @@ -528,92 +617,65 @@ break; case Top: case Bottom: - default: + case InsideHorizontal: setIntRange(d_xorg, d_xorg + d_len - 1); break; } } - - //------------------------------------------------------------ // -//.F ScaleDraw::maxWidth -// Return the maximum width of the scale for a specified QPainter +//.F ScaleDraw::maxWidth +// Return the maximum width of the scale for a specified QPainter // -//.u Syntax -//.f int ScaleDraw::maxWidth(QPainter *p) +//.u Syntax +//.f int ScaleDraw::maxWidth(QPainter *p, int penWidth) // -//.u Parameters -//.p QPainter *p -- painter -// bool worst -- if TRUE, assume the worst possible case. If FALSE, -// calculate the real maximum width, which is more -// CPU intensive. +//.u Parameters +//.p const QFontMetrics& fm -- font metrics used for calculations +// bool worst -- if TRUE, assume the worst possible case. If FALSE, +// calculate the real maximum width, which is more +// CPU intensive. +// int penWidth -- the width of the pen that will be used to draw the scale // //------------------------------------------------------------ -int ScaleDraw::maxWidth(QPainter *p, bool worst) const +int ScaleDraw::maxWidth(const QFontMetrics& fm, bool worst, int penWidth) const { - int rv = 0; - int bw = p->pen().width(); - - QString s; - - QFontMetrics fm = p->fontMetrics(); - - rv = maxLabelWidth(p,worst); - - switch (d_orient) - { - case Left: - case Right: - rv += (bw + d_hpad + d_majLen); - break; - case Round: - rv += (bw + d_vpad + d_majLen); - break; - case Top: - case Bottom: - default: - rv += d_len; - } - - return rv; - + return maxLabelWidth(fm,worst) + scaleWidth(penWidth); } //------------------------------------------------------------ // -//.F ScaleDraw::maxHeight -// Return the maximum height of the scale for the -// specified painter +//.F ScaleDraw::maxHeight +// Return the maximum height of the scale for the +// specified painter // -//.u Syntax -//.f int ScaleDraw::maxHeight(QPainter *p) +//.u Syntax +//.f int ScaleDraw::maxHeight(QPainter *p) // -//.u Parameters -//.p QPainter *p +//.u Parameters +//.p const QFontMetrics& fm -- font metrics used for calculations +// int penWidth -- the width of the pen that will be used to draw the scale // //------------------------------------------------------------ -int ScaleDraw::maxHeight(QPainter *p) const +int ScaleDraw::maxHeight(const QFontMetrics& fm, int /*penWidth*/) const { - int rv = 0; - int bw = p->pen().width(); - - p->save(); - QFontMetrics fm = p->fontMetrics(); - switch (d_orient) { case Top: case Bottom: case Round: - rv = bw + d_vpad + d_majLen + fm.height(); - break; + case InsideHorizontal: + //rv = penWidth + d_vpad + d_majLen + fm.height(); + rv = 2 * d_vpad + d_majLen + fm.ascent(); + break; case Left: case Right: - default: - rv = d_len + ((fm.height() + 1) / 2); + case InsideVertical: + //rv = d_len + ((fm.height() + 1) / 2); + rv = d_len + ((fm.ascent() + 1) / 2); + break; } return rv; @@ -623,18 +685,18 @@ //------------------------------------------------------------ // //.F ScaleDraw:maxBoundingRect -// Return the maximum bounding rectangle of the scale -// for a specified painter +// Return the maximum bounding rectangle of the scale +// for a specified painter // //.u Parameters -//.p QPainter *p -- painter +//.p const QFontMetrics& fm -- font metrics used for calculations // //.u Description -// The bounding rectangle is not very exact for round scales -// with strange angle ranges. +// The bounding rectangle is not very exact for round scales +// with strange angle ranges. // //------------------------------------------------------------ -QRect ScaleDraw::maxBoundingRect(QPainter *p) const +QRect ScaleDraw::maxBoundingRect(const QFontMetrics& fm) const { int i, wl; //,wmax; int a, ar, amin, amax; @@ -642,90 +704,93 @@ QRect r; - QFontMetrics fm = p->fontMetrics(); - - wl = maxLabelWidth(p, TRUE); + wl = maxLabelWidth(fm, true); switch(d_orient) { case Left: - - r = QRect( d_xorg - d_hpad - d_majLen - wl, - d_yorg - fm.ascent(), - d_majLen + d_hpad + wl, - d_len + fm.height()); - break; - + + r = QRect( d_xorg - d_hpad - d_majLen - wl, + d_yorg - fm.ascent(), + d_majLen + d_hpad + wl, + d_len + fm.height()); + break; + case Right: - - r = QRect( d_xorg, - d_yorg - fm.ascent(), - d_majLen + d_hpad + wl, - d_len + fm.height()); - break; - + + r = QRect( d_xorg, + d_yorg - fm.ascent(), + d_majLen + d_hpad + wl, + d_len + fm.height()); + break; + case Top: - - r = QRect ( d_xorg - wl / 2, - d_yorg - d_majLen - fm.ascent(), - d_len + wl, - d_majLen + d_vpad + fm.ascent()); - break; - + + r = QRect ( d_xorg - wl / 2, + d_yorg - d_majLen - fm.ascent(), + d_len + wl, + d_majLen + d_vpad + fm.ascent()); + break; + case Bottom: - - r = QRect ( d_xorg - wl / 2, - d_yorg, - d_len + wl, - d_majLen + d_vpad + fm.height()); - break; - + + r = QRect ( d_xorg - wl / 2, + d_yorg, + d_len + wl, + d_majLen + d_vpad + fm.height()); + break; + case Round: - amin = 2880; - amax = 0; - ar = 0; - - for (i=0; i< d_scldiv.majCnt(); i++) - { - a = transform(d_scldiv.majMark(i)); - - while (a > 2880) a -= 5760; - while (a < - 2880) a += 5760; - - ar = MusECore::qwtAbs(a); - - if (ar < amin) amin = ar; - if (ar > amax) amax = ar; - - } - - for (i=0; i< d_scldiv.minCnt(); i++) - { - a = transform(d_scldiv.majMark(i)); - - while (a > 2880) a -= 5760; - while (a < - 2880) a += 5760; - - ar = MusECore::qwtAbs(a); - - if (ar < amin) amin = ar; - if (ar > amax) amax = ar; - } - - arc = double(amin) / 16.0 * M_PI / 180.0; - r.setTop(MusECore::qwtInt(d_yCenter - (d_radius + double(d_majLen + d_vpad)) * cos(arc)) - + fm.ascent() ); - - arc = double(amax) / 16.0 * M_PI / 180.0; - 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; DELETETHIS - - r.setLeft(d_xorg - d_majLen - d_hpad - wl); - r.setWidth(d_len + 2*(d_majLen + d_hpad + wl)); - break; + amin = 2880; + amax = 0; + ar = 0; + + for (i=0; i< d_scldiv.majCnt(); i++) + { + a = transform(d_scldiv.majMark(i)); + + while (a > 2880) a -= 5760; + while (a < - 2880) a += 5760; + + ar = MusECore::qwtAbs(a); + + if (ar < amin) amin = ar; + if (ar > amax) amax = ar; + + } + + for (i=0; i< d_scldiv.minCnt(); i++) + { + a = transform(d_scldiv.majMark(i)); + + while (a > 2880) a -= 5760; + while (a < - 2880) a += 5760; + + ar = MusECore::qwtAbs(a); + + if (ar < amin) amin = ar; + if (ar > amax) amax = ar; + } + + arc = double(amin) / 16.0 * M_PI / 180.0; + r.setTop(MusECore::qwtInt(d_yCenter - (d_radius + double(d_majLen + d_vpad)) * cos(arc)) + + fm.ascent() ); + + arc = double(amax) / 16.0 * M_PI / 180.0; + 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; DELETETHIS + + r.setLeft(d_xorg - d_majLen - d_hpad - wl); + r.setWidth(d_len + 2*(d_majLen + d_hpad + wl)); + break; + + case InsideHorizontal: + case InsideVertical: + return r; + break; } return r; @@ -778,7 +843,6 @@ d_minAngle = amin; d_maxAngle = amax; setIntRange(d_minAngle, d_maxAngle); - } @@ -796,7 +860,8 @@ // //.u Description // Format character and precision have the same meaning as for the -// QString class. +// QString class, with one exception: Special format 'M' (Metric suffix G M K) +// // //.u See also // QString::setNum in the Qt manual @@ -810,51 +875,75 @@ //------------------------------------------------------------ // -//.F ScaleDraw::maxLabelWidth -// Return the maximum width of a label +//.F ScaleDraw::maxLabelWidth +// Return the maximum width of a label // -//.u Syntax -//.f int ScaleDraw::maxLabelWidth(QPainter *p, int worst) +//.u Syntax +//.f int ScaleDraw::maxLabelWidth(QPainter *p, int worst) // -//.u Parameters -//.p QPainter *p -- painter -// int worst -- If TRUE, take the worst case. If FALSE, take -// the actual width of the largest label. +//.u Parameters +//.p const QFontMetrics& fm -- font metrics used for calculations +// int worst -- If TRUE, take the worst case. If FALSE, take +// the actual width of the largest label. // //------------------------------------------------------------ -int ScaleDraw::maxLabelWidth(QPainter *p, int worst) const +int ScaleDraw::maxLabelWidth(const QFontMetrics& fm, bool worst) const { - int i,rv = 0; double val; QString s; - - QFontMetrics fm = p->fontMetrics(); - - if (worst) // worst case + if (worst) // worst case { - s.setNum(WorstCase, d_fmt, d_prec); - rv = fm.width(s); + s = composeLabelText(WorstCase, d_fmt, d_prec); + rv = fm.width(s); } - else // actual width + else // actual width { - for (i=0;i #include "dimap.h" #include "scldiv.h" +class QPalette; +class QFontMetrics; class QPainter; class QRect; +class QString; namespace MusEGui { class ScaleDraw : public DiMap { public: - enum OrientationX { Bottom, Top, Left, Right, Round }; + enum OrientationX { Bottom, Top, Left, Right, InsideHorizontal, InsideVertical, Round }; + enum TextHighlightMode { TextHighlightNone, + TextHighlightAlways, + TextHighlightSplit, + TextHighlightShadow, + TextHighlightSplitAndShadow }; private: ScaleDiv d_scldiv; static const int minLen; OrientationX d_orient; - + TextHighlightMode d_textHighlightMode; + QString _specialText; // Text to show if value = min + int d_xorg; int d_yorg; int d_len; @@ -62,10 +74,15 @@ char d_fmt; int d_prec; - - void drawTick(QPainter *p, double val, int len) const; - void drawBackbone(QPainter *p) const; - void drawLabel(QPainter *p, double val) const; + + bool d_drawBackBone; + + // Like QString::number except it allows special 'M' format (Metric suffix G, M, K). + QString composeLabelText(double val, char fmt, int prec) const; + + void drawTick(QPainter *p, const QPalette& palette, double curValue, double val, int len) const; + void drawBackbone(QPainter *p, const QPalette& palette, double curValue) const; + void drawLabel(QPainter *p, const QPalette& palette, double curValue, double val, bool isSpecialText = false) const; public: @@ -76,24 +93,27 @@ double step = 0.0, int logarithmic = 0); void setGeometry(int xorigin, int yorigin, int length, OrientationX o); void setAngleRange(double angle1, double angle2); + // Special 'M' format (Metric suffix G, M, K) supported. void setLabelFormat(char f, int prec); - + void setBackBone(bool v) { d_drawBackBone = v; } + const ScaleDiv& scaleDiv() const { return d_scldiv; } OrientationX orientation() const { return d_orient; } - QRect maxBoundingRect(QPainter *p) const; - int maxWidth(QPainter *p, bool worst = TRUE) const; - int maxHeight(QPainter *p) const; - int maxLabelWidth(QPainter *p, int worst = TRUE) const; - void draw(QPainter *p) const; + TextHighlightMode textHighlightMode() const { return d_textHighlightMode; } + void setTextHighlightMode(TextHighlightMode mode) { d_textHighlightMode = mode; } + QString specialText() const { return _specialText; } + void setSpecialText(const QString& s) { _specialText = s; } + + QRect maxBoundingRect(const QFontMetrics& fm) const; + int maxWidth(const QFontMetrics& fm, bool worst = true, int penWidth = 1) const; + int maxHeight(const QFontMetrics& fm, int penWidth = 1) const; + int maxLabelWidth(const QFontMetrics& fm, bool worst = true) const; + int scaleWidth(int penWidth = 1) const; + + void draw(QPainter *p, const QPalette& palette, double curValue = 0.0); // const; }; } // namespace MusEGui #endif - - - - - - diff -Nru muse-2.1.2/muse/widgets/sclif.cpp muse-3.0.2+ds1/muse/widgets/sclif.cpp --- muse-2.1.2/muse/widgets/sclif.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/sclif.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -61,9 +61,10 @@ ScaleIf::ScaleIf() { - d_userScale = FALSE; + d_userScale = false; d_maxMajor = 5; d_maxMinor = 3; + d_scaleStep = 0.0; d_scale.setScale(0.0,100.0,d_maxMajor, d_maxMinor); } @@ -120,10 +121,11 @@ void ScaleIf::setScale(double vmin, double vmax, double step, int logarithmic) { + d_scaleStep = step; ScaleDiv oldscl(d_scale.scaleDiv()); d_scale.setScale(vmin, vmax, d_maxMajor, d_maxMinor, step, logarithmic); - d_userScale = TRUE; + d_userScale = true; if (oldscl != d_scale.scaleDiv()) scaleChange(); } @@ -159,7 +161,7 @@ void ScaleIf::autoScale() { if (!d_userScale) { - d_userScale = FALSE; + d_userScale = false; scaleChange(); } } diff -Nru muse-2.1.2/muse/widgets/sclif.h muse-3.0.2+ds1/muse/widgets/sclif.h --- muse-2.1.2/muse/widgets/sclif.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/sclif.h 2017-12-04 21:01:19.000000000 +0000 @@ -41,6 +41,7 @@ ScaleDraw d_scale; int d_maxMajor; int d_maxMinor; + double d_scaleStep; bool hasUserScale() {return d_userScale;} virtual void scaleChange() = 0; @@ -53,10 +54,14 @@ void setScale(const ScaleDiv &s); void setScaleMaxMajor( int ticks); void setScaleMaxMinor( int ticks); + void setScaleBackBone(bool v) { d_scale.setBackBone(v); } + QString specialText() const { return d_scale.specialText(); } + void setSpecialText(const QString& s) { d_scale.setSpecialText(s); } void autoScale(); int scaleMaxMinor() const {return d_maxMinor;} int scaleMaxMajor() const {return d_maxMinor;} + double scaleStep() const { return d_scaleStep; } }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/scroll_area.cpp muse-3.0.2+ds1/muse/widgets/scroll_area.cpp --- muse-2.1.2/muse/widgets/scroll_area.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/scroll_area.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,120 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// scroll_area.cpp +// (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) +// +// 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 + +#include "scroll_area.h" +#include "compact_slider.h" + +namespace MusEGui { + +//--------------------------------------------------------- +// CompactScrollArea +//--------------------------------------------------------- + +CompactScrollArea::CompactScrollArea(QWidget *parent) + : QScrollArea(parent) +{ +} + +QSize CompactScrollArea::minimumSizeHint() const +{ + if(widget()) + return widget()->sizeHint(); + return QSize(16, 16); +} + +//--------------------------------------------------------- +// CompactControllerRack +//--------------------------------------------------------- + +CompactControllerRack::CompactControllerRack(QWidget *parent, int minItems) + : QScrollArea(parent), _minItems(minItems) +{ + _xItemMargin = 0; + _yItemMargin = 0; + _minItemSize = defaultItemSizeHint(); + _minSize = QSize(_minItemSize.width(), _minItems * _minItemSize.height()); // + 1; // TODO Instead of just +1, may need margins etc? +} + +QSize CompactControllerRack::minimumSizeHint() const +{ + //return _minSize; + const QSize sz = CompactSlider::getMinimumSizeHint(fontMetrics(), + Qt::Horizontal, + CompactSlider::None, + _xItemMargin, + _yItemMargin); + return QSize(sz.width() + 2 * frameWidth(), _minItems * sz.height() + 2 * frameWidth()); +} + +QSize CompactControllerRack::defaultItemSizeHint() +{ +// // HACK: Get some idea of the height of each item - even with an empty scroll area. +// CompactSlider* dummy_slider = new CompactSlider(this, 0, Qt::Horizontal, CompactSlider::None, "WWW"); +// dummy_slider->setValueDecimals(0); +// dummy_slider->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum)); +// dummy_slider->setEnabled(true); +// +// const QSize sz = dummy_slider->sizeHint(); +// +// delete dummy_slider; +// +// return sz; + + // HACK: Get some idea of the height of each item - even with an empty scroll area. + return CompactSlider::getMinimumSizeHint(fontMetrics(), Qt::Horizontal, CompactSlider::None, _xItemMargin, _yItemMargin); +} + +void CompactControllerRack::updateDefaultItemSizeHint() +{ + _minItemSize = defaultItemSizeHint(); + update(); +} + +void CompactControllerRack::updateMinimumSize() +{ + _minSize = QSize(_minItemSize.width(), _minItems * _minItemSize.height()); // + 1; // TODO Instead of just +1, may need margins etc? + update(); +} + +void CompactControllerRack::setMinItems(int n) +{ + _minItems = n; + _minItemSize = defaultItemSizeHint(); + _minSize = QSize(_minItemSize.width(), _minItems * _minItemSize.height()); // + 1; // TODO Instead of just +1, may need margins etc? + update(); +} + +void CompactControllerRack::setItemMargins(int hor, int vert) +{ + _xItemMargin = hor; + _yItemMargin = vert; + _minItemSize = defaultItemSizeHint(); + _minSize = QSize(_minItemSize.width(), _minItems * _minItemSize.height()); // + 1; // TODO Instead of just +1, may need margins etc? + //resize(this->size()); // FIXME Call something else like updateGeometry etc. + updateGeometry(); +} + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/scroll_area.h muse-3.0.2+ds1/muse/widgets/scroll_area.h --- muse-2.1.2/muse/widgets/scroll_area.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/scroll_area.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,76 @@ +//========================================================= +// MusE +// Linux Music Editor +// Copyright (C) 1999-2011 by Werner Schweer and others +// +// scroll_area.h +// (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) +// +// 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 __SCROLL_AREA_H__ +#define __SCROLL_AREA_H__ + +#include + +class QSize; + +namespace MusEGui { + +//--------------------------------------------------------- +// CompactScrollArea +//--------------------------------------------------------- + +class CompactScrollArea : public QScrollArea +{ + Q_OBJECT + + public: + CompactScrollArea(QWidget* parent); + virtual QSize minimumSizeHint() const; +}; + +//--------------------------------------------------------- +// CompactControllerRack +//--------------------------------------------------------- + +class CompactControllerRack : public QScrollArea +{ + Q_OBJECT + + private: + int _minItems; + QSize _minItemSize; + QSize _minSize; + int _xItemMargin; + int _yItemMargin; + + public: + CompactControllerRack(QWidget* parent, int minItems = 0); + virtual QSize minimumSizeHint() const; + int minItems() const { return _minItems; } + void setMinItems(int n); + QSize defaultItemSizeHint(); + void updateDefaultItemSizeHint(); + void updateMinimumSize(); + + void setItemMargins(int x, int y); +}; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/scrollbar.cpp muse-3.0.2+ds1/muse/widgets/scrollbar.cpp --- muse-2.1.2/muse/widgets/scrollbar.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/scrollbar.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,49 @@ +//========================================================= +// MusE +// Linux Music Editor +// scrollbar.cpp +// (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 "scrollbar.h" + +#include +#include + +namespace MusEGui { + +ScrollBar::ScrollBar(Qt::Orientation orientation, bool autoPageStep, QWidget* parent) : + QScrollBar(orientation, parent), _autoPageStep(autoPageStep) +{ + +}; + +void ScrollBar::redirectedWheelEvent(QWheelEvent* e) +{ + if(isVisible()) + wheelEvent(e); +} + +void ScrollBar::resizeEvent(QResizeEvent* e) +{ + if(_autoPageStep) + setPageStep(e->size().height()); +} + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/scrollbar.h muse-3.0.2+ds1/muse/widgets/scrollbar.h --- muse-2.1.2/muse/widgets/scrollbar.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/scrollbar.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,59 @@ +//========================================================= +// MusE +// Linux Music Editor +// scrollbar.h +// (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __SCROLLBAR_H__ +#define __SCROLLBAR_H__ + +#include + +class QWheelEvent; +class QResizeEvent; + +namespace MusEGui { + +//--------------------------------------------------------- +// ScrollBar +//--------------------------------------------------------- + +class ScrollBar : public QScrollBar { + Q_OBJECT + + private: + bool _autoPageStep; + + public slots: + void redirectedWheelEvent(QWheelEvent*); + + protected: + virtual void resizeEvent(QResizeEvent*); + + public: + ScrollBar(Qt::Orientation orientation, bool autoPageStep = false, QWidget* parent = 0); + + bool autoPageStep() const { return _autoPageStep; } + void setAutoPageStep(bool v) { _autoPageStep = v; update(); } +}; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/scrollscale.cpp muse-3.0.2+ds1/muse/widgets/scrollscale.cpp --- muse-2.1.2/muse/widgets/scrollscale.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/scrollscale.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -198,7 +198,6 @@ void ScrollScale::setPos ( unsigned pos ) { - scroll->setValue ( pos ); } diff -Nru muse-2.1.2/muse/widgets/shortcutcapturedialog.cpp muse-3.0.2+ds1/muse/widgets/shortcutcapturedialog.cpp --- muse-2.1.2/muse/widgets/shortcutcapturedialog.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/shortcutcapturedialog.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -39,6 +39,7 @@ #include #include #include +#include namespace MusEGui { @@ -47,7 +48,7 @@ { setupUi(this); QKeySequence q = QKeySequence(shortcuts[index].key); - oshrtLabel->setText(q); + oshrtLabel->setText(q.toString()); connect(okButton, SIGNAL( clicked() ), this, SLOT( apply() ) ); connect(cancelButton, SIGNAL(pressed()), this, SLOT(cancel())); shortcutindex = index; @@ -95,9 +96,9 @@ realkey = true; QKeySequence q = QKeySequence(key); //QKeySequence q = QKeySequence(k, mods); - QString keyString = q; + QString keyString = q.toString(); if (keyString != QString::null) - nshrtLabel->setText(q); + nshrtLabel->setText(keyString); // Check against conflicting shortcuts for (int i=0; i < SHRT_NUM_OF_ELEMENTS; i++) { diff -Nru muse-2.1.2/muse/widgets/shortcutconfig.cpp muse-3.0.2+ds1/muse/widgets/shortcutconfig.cpp --- muse-2.1.2/muse/widgets/shortcutconfig.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/shortcutconfig.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -103,7 +103,7 @@ // catpre.clear(); //newItem->setText(SHRT_DESCR_COL, catpre + tr(qApp->translate("shortcuts", shortcuts[i].descr)); // Tim QKeySequence key = QKeySequence(shortcuts[i].key); - newItem->setText(SHRT_SHRTCUT_COL, key); + newItem->setText(SHRT_SHRTCUT_COL, key.toString()); } } } @@ -118,7 +118,7 @@ if (key != Rejected) { shortcuts[shortcutindex].key = key; QKeySequence keySequence = QKeySequence(key); - active->setText(SHRT_SHRTCUT_COL, keySequence); + active->setText(SHRT_SHRTCUT_COL, keySequence.toString()); _config_changed = true; clearButton->setEnabled(true); } diff -Nru muse-2.1.2/muse/widgets/sigscale.cpp muse-3.0.2+ds1/muse/widgets/sigscale.cpp --- muse-2.1.2/muse/widgets/sigscale.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/sigscale.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -147,8 +147,7 @@ continue; p.drawLine(xp, 0, xp, h/2); p.drawLine(xp, h/2, xp+5, h/2); - QString s; - s.sprintf("%d/%d", e->sig.z, e->sig.n); + QString s = QString("%1/%2").arg(e->sig.z).arg(e->sig.n); p.drawText(xp+8, h-6, s); } diff -Nru muse-2.1.2/muse/widgets/sig_tempo_toolbar.cpp muse-3.0.2+ds1/muse/widgets/sig_tempo_toolbar.cpp --- muse-2.1.2/muse/widgets/sig_tempo_toolbar.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/sig_tempo_toolbar.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -26,101 +26,166 @@ #include "awl/sigedit.h" #include "song.h" -#include #include - +#include namespace MusEGui { - TempoToolbarWidget::TempoToolbarWidget(QWidget* p) : QWidget(p) - { - tempo_edit=new MusEGui::TempoEdit(this); - tempo_edit->setToolTip(tr("tempo at current position")); - tempo_edit->setFocusPolicy(Qt::StrongFocus); - - label=new QLabel(tr("Tempo: "),this); - - layout = new QHBoxLayout(this); - layout->setContentsMargins(0,0,0,0); - layout->setSpacing(1); - layout->addWidget(label); - layout->addWidget(tempo_edit); - - connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), this, SLOT(song_changed(MusECore::SongChangedFlags_t))); - connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(pos_changed(int,unsigned,bool))); - - connect(tempo_edit, SIGNAL(tempoChanged(double)), MusEGlobal::song, SLOT(setTempo(double))); - connect(tempo_edit, SIGNAL(returnPressed()), SIGNAL(returnPressed())); - connect(tempo_edit, SIGNAL(escapePressed()), SIGNAL(escapePressed())); - song_changed(-1); - } - - void TempoToolbarWidget::pos_changed(int,unsigned,bool) - { - song_changed(SC_TEMPO); - } - - void TempoToolbarWidget::song_changed(MusECore::SongChangedFlags_t type) - { - if (type & SC_TEMPO) - { - int tempo = MusEGlobal::tempomap.tempo(MusEGlobal::song->cpos()); - tempo_edit->blockSignals(true); - tempo_edit->setValue(double(60000000.0/tempo)); - tempo_edit->blockSignals(false); - } - if (type & SC_MASTER) - { - tempo_edit->setEnabled(MusEGlobal::song->masterFlag()); - label->setEnabled(MusEGlobal::song->masterFlag()); - } - } - - SigToolbarWidget::SigToolbarWidget(QWidget* p) : QWidget(p) - { - sig_edit=new Awl::SigEdit(this); - sig_edit->setFocusPolicy(Qt::StrongFocus); - sig_edit->setValue(AL::TimeSignature(4, 4)); - sig_edit->setToolTip(tr("time signature at current position")); - - label=new QLabel(tr("Signature: "),this); - - layout = new QHBoxLayout(this); - layout->setContentsMargins(0,0,0,0); - layout->setSpacing(1); - layout->addWidget(label); - layout->addWidget(sig_edit); - - connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), this, SLOT(song_changed(MusECore::SongChangedFlags_t))); - connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(pos_changed(int,unsigned,bool))); - - connect(sig_edit, SIGNAL(valueChanged(const AL::TimeSignature&)), MusEGlobal::song, SLOT(setSig(const AL::TimeSignature&))); - connect(sig_edit, SIGNAL(returnPressed()), SIGNAL(returnPressed())); - connect(sig_edit, SIGNAL(escapePressed()), SIGNAL(escapePressed())); +//--------------------------------- +// TempoToolbar +//--------------------------------- - song_changed(-1); - } - - void SigToolbarWidget::pos_changed(int,unsigned,bool) - { - song_changed(SC_SIG); - } +TempoToolbar::TempoToolbar(QWidget* parent) + : QToolBar(parent) +{ + init(); +} + +TempoToolbar::TempoToolbar(const QString& title, QWidget* parent) + : QToolBar(title, parent) +{ + init(); +} + +void TempoToolbar::init() +{ + setObjectName("Tempo toolbar"); + tempo_edit=new TempoEdit(this); + tempo_edit->setToolTip(tr("tempo at current position")); + tempo_edit->setFocusPolicy(Qt::StrongFocus); + + label=new QLabel(tr("Tempo: "),this); + + tap_button = new QToolButton(this); + tap_button->setText(tr("TAP")); + + addWidget(label); + addWidget(tempo_edit); + addWidget(tap_button); + + connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), this, SLOT(song_changed(MusECore::SongChangedFlags_t))); + connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(pos_changed(int,unsigned,bool))); + + connect(tempo_edit, SIGNAL(tempoChanged(double)), MusEGlobal::song, SLOT(setTempo(double))); + connect(tempo_edit, SIGNAL(returnPressed()), SIGNAL(returnPressed())); + connect(tempo_edit, SIGNAL(escapePressed()), SIGNAL(escapePressed())); + + connect(tap_button, SIGNAL(clicked(bool)), SLOT(tap_tempo())); + connect(&tap_timer, SIGNAL(timeout()), SLOT(tap_timer_signal())); + tap_timer.stop(); + + song_changed(-1); +} + +void TempoToolbar::pos_changed(int,unsigned,bool) +{ + song_changed(SC_TEMPO); +} + +void TempoToolbar::song_changed(MusECore::SongChangedFlags_t type) +{ + if(type & SC_TEMPO) + { + int tempo = MusEGlobal::tempomap.tempo(MusEGlobal::song->cpos()); + tempo_edit->blockSignals(true); + tempo_edit->setValue(double(60000000.0/tempo)); + tempo_edit->blockSignals(false); + } + if(type & SC_MASTER) + { + tempo_edit->setEnabled(MusEGlobal::song->masterFlag()); + label->setEnabled(MusEGlobal::song->masterFlag()); + tap_button->setEnabled(MusEGlobal::song->masterFlag()); + } +} + +void TempoToolbar::tap_tempo() +{ + QDateTime local(QDateTime::currentDateTime()); + + if(tap_timer.isActive()) + { + qint64 msecs_tap = last_tap_time.msecsTo(local); + double t_tap = (double)60000.0f / (double)msecs_tap; + tempo_edit->setValue(t_tap); + emit tempo_edit->tempoChanged(t_tap); + + } + else + { + tap_timer.start(2000); + } + last_tap_time = local; +} + +void TempoToolbar::tap_timer_signal() +{ + tap_timer.stop(); +} + + +//--------------------------------- +// SigToolbar +//--------------------------------- - void SigToolbarWidget::song_changed(MusECore::SongChangedFlags_t type) - { - if (type & SC_SIG) - { - int z, n; - AL::sigmap.timesig(MusEGlobal::song->cpos(), z, n); - sig_edit->blockSignals(true); - sig_edit->setValue(AL::TimeSignature(z, n)); - sig_edit->blockSignals(false); - } - if (type & SC_MASTER) - { - sig_edit->setEnabled(MusEGlobal::song->masterFlag()); - label->setEnabled(MusEGlobal::song->masterFlag()); - } - } +SigToolbar::SigToolbar(QWidget* parent) + : QToolBar(parent) +{ + init(); +} + +SigToolbar::SigToolbar(const QString& title, QWidget* parent) + : QToolBar(title, parent) +{ + init(); } + +void SigToolbar::init() +{ + setObjectName("Signature toolbar"); + sig_edit=new Awl::SigEdit(this); + sig_edit->setFocusPolicy(Qt::StrongFocus); + sig_edit->setValue(AL::TimeSignature(4, 4)); + sig_edit->setToolTip(tr("time signature at current position")); + + label=new QLabel(tr("Signature: "),this); + + addWidget(label); + addWidget(sig_edit); + + connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), this, SLOT(song_changed(MusECore::SongChangedFlags_t))); + connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(pos_changed(int,unsigned,bool))); + + connect(sig_edit, SIGNAL(valueChanged(const AL::TimeSignature&)), MusEGlobal::song, SLOT(setSig(const AL::TimeSignature&))); + connect(sig_edit, SIGNAL(returnPressed()), SIGNAL(returnPressed())); + connect(sig_edit, SIGNAL(escapePressed()), SIGNAL(escapePressed())); + + song_changed(-1); +} + + +void SigToolbar::pos_changed(int,unsigned,bool) +{ + song_changed(SC_SIG); +} + +void SigToolbar::song_changed(MusECore::SongChangedFlags_t type) +{ + if(type & SC_SIG) + { + int z, n; + AL::sigmap.timesig(MusEGlobal::song->cpos(), z, n); + sig_edit->blockSignals(true); + sig_edit->setValue(AL::TimeSignature(z, n)); + sig_edit->blockSignals(false); + } + if(type & SC_MASTER) + { + sig_edit->setEnabled(MusEGlobal::song->masterFlag()); + label->setEnabled(MusEGlobal::song->masterFlag()); + } +} + + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/sig_tempo_toolbar.h muse-3.0.2+ds1/muse/widgets/sig_tempo_toolbar.h --- muse-2.1.2/muse/widgets/sig_tempo_toolbar.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/sig_tempo_toolbar.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,62 +23,79 @@ #ifndef __SIG_TEMPO_TOOLBAR_H__ #define __SIG_TEMPO_TOOLBAR_H__ -#include +#include +#include +#include + #include "type_defs.h" +class QWidget; +class QLabel; +class QString; +class QToolButton; + namespace Awl { - class SigEdit; + class SigEdit; } -class QHBoxLayout; -class QLabel; - namespace MusEGui { - class TempoEdit; - - class SigToolbarWidget : public QWidget - { - Q_OBJECT - - private: - QHBoxLayout* layout; - QLabel* label; - Awl::SigEdit* sig_edit; - - public: - SigToolbarWidget(QWidget* parent); - - signals: - void returnPressed(); - void escapePressed(); - - private slots: - void pos_changed(int,unsigned,bool); - void song_changed(MusECore::SongChangedFlags_t); + class TempoEdit; + + class TempoToolbar : public QToolBar + { + Q_OBJECT + + private: + QLabel* label; + TempoEdit* tempo_edit; + QToolButton *tap_button; + + QTimer tap_timer; + QDateTime last_tap_time; + + void init(); + + public: + TempoToolbar(QWidget* parent = 0); + TempoToolbar(const QString& title, QWidget* parent = 0); + + signals: + void returnPressed(); + void escapePressed(); + + private slots: + void pos_changed(int,unsigned,bool); + void song_changed(MusECore::SongChangedFlags_t); + void tap_tempo(); + void tap_timer_signal(); }; - class TempoToolbarWidget : public QWidget - { - Q_OBJECT - - private: - QHBoxLayout* layout; - QLabel* label; - MusEGui::TempoEdit* tempo_edit; - - public: - TempoToolbarWidget(QWidget* parent); - - signals: - void returnPressed(); - void escapePressed(); - - private slots: - void pos_changed(int,unsigned,bool); - void song_changed(MusECore::SongChangedFlags_t); + + class SigToolbar : public QToolBar + { + Q_OBJECT + + private: + QLabel* label; + Awl::SigEdit* sig_edit; + + void init(); + + public: + SigToolbar(QWidget* parent = 0); + SigToolbar(const QString& title, QWidget* parent = 0); + + signals: + void returnPressed(); + void escapePressed(); + + private slots: + void pos_changed(int,unsigned,bool); + void song_changed(MusECore::SongChangedFlags_t); }; + } #endif diff -Nru muse-2.1.2/muse/widgets/sliderbase.cpp muse-3.0.2+ds1/muse/widgets/sliderbase.cpp --- muse-2.1.2/muse/widgets/sliderbase.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/sliderbase.cpp 2018-01-26 21:59:38.000000000 +0000 @@ -5,6 +5,7 @@ // Copyright (C) 1997 Josef Wilgen // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -22,12 +23,21 @@ // //========================================================= +#include #include #include "sliderbase.h" #include "mmath.h" #include #include #include +#include +#include +#include +#include + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_SLIDER_BASE(dev, format, args...) //fprintf(dev, format, ##args); + namespace MusEGui { @@ -57,13 +67,74 @@ setObjectName(name); _id = -1; _cursorHoming = false; + +// NOTE: Avoid using borderlessMouse for now because it may jump when clicking from another active window: + _borderlessMouse = false; + +// REMOVE Tim Trackinfo. +// // [ Audio strip: Press and move: When switching from another active window... ] +// // --------------------------------------------------------------------------- +// // +// // AudioComponentRack::controllerPressed id:1 +// // val:0.03000000000000002665 calling enableController(false) +// // +// // SliderBase::mouseMoveEvent firstMouseMoveAfterPress +// // e->globalPos() x:1042 y:602 _lastGlobalMousePos x:1041 y:602 calling setPosition(delta x:1, y:0) +// // SliderBase::setPosition calling fitValue(delta x:1, y:0) d_mouseOffset:0.00000000000000000000 +// // CompactSlider::getValue value:0.03000000000000002665 p x:1 y:0 step:0.01000000000000000021 x change:0.01000000000000000021 +// // DoubleRange::setNewValue TOP val:0.04000000000000002859 d_prevValue:0.02000000000000001776 d_value:0.03000000000000002665 +// // BOTTOM val:0.04000000000000002859 d_prevValue:0.03000000000000002665 d_value:0.04000000000000003553 +// // not equal, calling valueChange +// // AudioComponentRack::controllerChanged id:1 val:0.04000000000000003553 +// // AudioComponentRack::controllerMoved id:1 val:0.04000000000000003553 +// // +// // -------------------------------------------------------------------------------------- +// // [[ ignoreMouseMove is now set to true, and the cursor is now told to move to screen centre ... ]] +// // -------------------------------------------------------------------------------------- +// // +// // SliderBase::mouseMoveEvent ignoring mouse move +// // +// // -------------------------------------------------------------------------------------- +// // [[ Here's the trouble: The cursor has not yet moved to screen centre !!! It's fine if MusE is already active, see below... ]] +// // -------------------------------------------------------------------------------------- +// // +// // SliderBase::mouseMoveEvent not firstMouseMoveAfterPress +// // e->globalPos() x:1044 y:601 scrn_cntr x:683 y:384 calling setPosition(delta x:361, y:217) +// // SliderBase::setPosition calling fitValue(delta x:361, y:217) d_mouseOffset:0.00000000000000000000 +// // CompactSlider::getValue value:0.04000000000000003553 p x:361 y:217 step:0.01000000000000000021 x change:3.60999999999999987566 +// // DoubleRange::setNewValue TOP val:3.64999999999999991118 d_prevValue:0.03000000000000002665 d_value:0.04000000000000003553 +// // BOTTOM val:3.64999999999999991118 d_prevValue:0.04000000000000003553 d_value:1.00000000000000000000 +// // not equal, calling valueChange +// // AudioComponentRack::controllerChanged id:1 val:1.00000000000000000000 +// // AudioComponentRack::controllerMoved id:1 val:1.00000000000000000000 +// // +// // SliderBase::mouseMoveEvent ignoring mouse move +// // +// // SliderBase::mouseMoveEvent not firstMouseMoveAfterPress +// // e->globalPos() x:683 y:384 scrn_cntr x:683 y:384 calling setPosition(delta x:0, y:0) +// // SliderBase::setPosition calling fitValue(delta x:0, y:0) d_mouseOffset:0.00000000000000000000 +// // CompactSlider::getValue value:1.00000000000000000000 p x:0 y:0 step:0.01000000000000000021 x change:0.00000000000000000000 +// // DoubleRange::setNewValue TOP val:1.00000000000000000000 d_prevValue:0.04000000000000003553 d_value:1.00000000000000000000 +// // BOTTOM val:1.00000000000000000000 d_prevValue:1.00000000000000000000 d_value:1.00000000000000000000 + + _ignoreMouseMove = false; + _mouseGrabbed = false; + _pressed = false; + _pagingButtons = Qt::RightButton; + _firstMouseMoveAfterPress = false; + _cursorOverrideCount = 0; d_tmrID = 0; d_updTime = 150; d_mass = 0.0; d_tracking = true; + d_trackingTempDisable = false; d_mouseOffset = 0.0; - d_scrollMode = ScrNone; + d_valAccum = 0.0; + d_enableValueToolTips = false; + d_showValueToolTipsOnHover = false; + d_valueAtPress = 0.0; + d_scrollMode = ScrNone; setRange(0.0, 1.0, 0.1); } @@ -77,10 +148,46 @@ SliderBase::~SliderBase() { + // Just in case the ref count is not 0. This is our last chance to clear + // our contribution to QApplication::setOverrideCursor references. + showCursor(); + if (d_tmrID) killTimer(d_tmrID); } +void SliderBase::showCursor(bool show) +{ + if(_cursorOverrideCount > 1) + fprintf(stderr, "MusE Warning: _cursorOverrideCount > 1 in SliderBase::showCursor(%d)\n", show); + if(show) + { + while(_cursorOverrideCount > 0) + { + QApplication::restoreOverrideCursor(); + _cursorOverrideCount--; + } + } + else + { + _cursorOverrideCount++; + QApplication::setOverrideCursor(Qt::BlankCursor); + } +} + +void SliderBase::setMouseGrab(bool grabbed) +{ + if(grabbed && !_mouseGrabbed) + { + _mouseGrabbed = true; + grabMouse(); // CAUTION + } + else if(!grabbed && _mouseGrabbed) + { + releaseMouse(); + _mouseGrabbed = false; + } +} //------------------------------------------------------------ //.F void SliderBase::wheelEvent(QWheelEvent *e) @@ -91,21 +198,12 @@ //------------------------------------------------------------ void SliderBase::wheelEvent(QWheelEvent *e) { - // Avoid unwanted wheel events from outside the control. - // Just in case it grabs focus somehow. - // Tested: No go, can't seem to determine where event came from. - /* - const QPoint gp = mapToGlobal(e->pos()); - const QRect gr = QRect(mapToGlobal(rect().topLeft()), mapToGlobal(rect().bottomRight())); - if(!gr.contains(gp)) - { - e->ignore(); + e->accept(); + // Do not allow setting value from the external while mouse is pressed. + if(_pressed) return; - } */ - e->accept(); - - float inc = (maxValue() - minValue()) / 40; + float inc = (maxValue(ConvertNone) - minValue(ConvertNone)) / 40; if (e->modifiers() == Qt::ShiftModifier) inc = inc / 10; @@ -113,10 +211,14 @@ inc = step(); if(e->delta() > 0) - setValue(value()+inc); + setValue(value(ConvertNone)+inc, ConvertNone); else - setValue(value()-inc); + setValue(value(ConvertNone)-inc, ConvertNone); + // Show a handy tooltip value box. + if(d_enableValueToolTips) + showValueToolTip(e->globalPos()); + emit sliderMoved(value(), _id); emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); } @@ -172,40 +274,148 @@ void SliderBase::mousePressEvent(QMouseEvent *e) { + e->accept(); QPoint p = e->pos(); + const Qt::MouseButton button = e->button(); + const Qt::MouseButtons buttons = e->buttons(); + const bool shift = e->modifiers() & Qt::ShiftModifier; + //const bool ctrl = e->modifiers() & Qt::ControlModifier; + const bool meta = e->modifiers() & Qt::MetaModifier; + + const bool clicked = (button == Qt::LeftButton || button == Qt::MidButton); + d_timerTick = 0; + _pressed = true; - getScrollMode(p, button, d_scrollMode, d_direction); + _mouseDeltaAccum = QPoint(); // Reset. + _lastGlobalMousePos = e->globalPos(); + d_valueAtPress = value(ConvertNone); + d_valAccum = d_valueAtPress; // Reset. + + d_trackingTempDisable = meta; // Generate automation graph recording straight lines if modifier held. + + getScrollMode(p, button, e->modifiers(), d_scrollMode, d_direction); + _lastMousePos = p; stopMoving(); + showCursor(); + + DEBUG_SLIDER_BASE(stderr, "SliderBase::mousePressEvent x:%d, y:%d d_mouseOffset:%.20f d_valueAtPress:%.20f d_scrollMode:%d\n", p.x(), p.y(), d_mouseOffset, d_valueAtPress, d_scrollMode); + + // Only one mouse button at a time! Otherwise bad things happen. + if(buttons ^ button) + { + // Clear everything. + setMouseGrab(false); + d_scrollMode = ScrNone; + d_direction = 0; + _pressed = false; + return; + } switch(d_scrollMode) { case ScrPage: case ScrTimer: d_mouseOffset = 0; DoubleRange::incPages(d_direction); + + // Show a handy tooltip value box. + if(d_enableValueToolTips) + showValueToolTip(e->globalPos()); + emit sliderMoved(value(), _id); - emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); + emit sliderMoved(value(), _id, shift); d_tmrID = startTimer(MusECore::qwtMax(250, 2 * d_updTime)); break; case ScrMouse: + case ScrDirect: d_speed = 0; + //if(!pagingButtons().testFlag(Qt::RightButton) && button == Qt::RightButton) if(button == Qt::RightButton) { + // Clear everything. + setMouseGrab(false); + d_scrollMode = ScrNone; + d_direction = 0; + _pressed = false; + DEBUG_SLIDER_BASE(stderr, "SliderBase::mousePressEvent _pressed:%d\n", _pressed); emit sliderRightClicked(e->globalPos(), _id); break; } - d_time.start(); - if(_cursorHoming && button == Qt::LeftButton) + + if(d_scrollMode != ScrDirect) + d_time.start(); + + if(_cursorHoming && clicked && d_scrollMode != ScrDirect) + { + _ignoreMouseMove = true; // Avoid recursion. + d_mouseOffset = 0.0; + } + else if(_borderlessMouse && clicked) { - _ignoreMouseMove = true; d_mouseOffset = 0.0; +// _lastGlobalMousePos = e->globalPos(); + + // "It is almost never necessary to grab the mouse when using Qt, as Qt grabs + // and releases it sensibly. In particular, Qt grabs the mouse when a mouse + // button is pressed and keeps it until the last button is released." + // + // Apparently not. For some reason this was necessary. When the cursor is dragged + // outside the window, holding left then pressing right mouse button COMPLETELY + // bypasses us, leaving the app's default right-click handler to popup, and leaving + // us in a really BAD state: mouse is grabbed (and hidden) and no way out ! + // + // That is likely just how QWidget works, but here using global cursor overrides + // it is disastrous. TESTED: Yes, that is how other controls work. Hitting another + // button while the mouse has been dragged outside causes it to bypass us ! + setMouseGrab(true); // CAUTION + + showCursor(false); // CAUTION + + _firstMouseMoveAfterPress = true; // Prepare for the first mouse move event after this press. + + // The problem with this approach of moving the cursor on click is that + // we can't get a double click signal (ex. to open an editor). + //_ignoreMouseMove = true; // Avoid recursion. + //const QRect r = QApplication::desktop()->screenGeometry(); + //const QPoint pt(r.width()/2, r.height()/2); + //QCursor::setPos(pt); + } else - d_mouseOffset = getValue(p) - value(); + d_mouseOffset = getValue(p) - d_valueAtPress; + + // If direct mode, jump to the pressed location, which also calls valueChange() if anything changed. + if(d_scrollMode == ScrDirect) + { + d_mouseOffset = 0.0; + //setPosition(p); // No, it subtracts d_mouseOffset which leaves net zero in case of last line above. + DoubleRange::fitValue(getValue(p)); + // Must set this so that mouseReleaseEvent reads the right value. + d_valAccum = value(ConvertNone); + } + + // HACK + // In direct mode let the inherited classes call these in their valueChange() methods, + // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. + // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. + // Hack: Since valueChange() is NOT called if nothing changed, call these in that case. + if(d_scrollMode != ScrDirect || + value(ConvertNone) == d_valueAtPress) + { + processSliderPressed(_id); + emit sliderPressed(value(), _id); + } - emit sliderPressed(_id); + // Show a handy tooltip value box. + if(d_enableValueToolTips) + showValueToolTip(e->globalPos()); + + // If direct mode, now set the mode to a regular mouse mode. + if(d_scrollMode == ScrDirect) + d_scrollMode = ScrMouse; + break; default: @@ -227,8 +437,11 @@ //------------------------------------------------------------ void SliderBase::buttonReleased() { - if ((!d_tracking) || (value() != prevValue())) + if (!trackingIsActive() && valueHasChangedAtRelease()) + { emit valueChanged(value(), _id); + emit valueChanged(value(), _id, d_scrollMode); + } } @@ -246,71 +459,96 @@ //------------------------------------------------------------ void SliderBase::mouseReleaseEvent(QMouseEvent *e) { - int ms = 0; - /*double inc = step(); */ // prevent compiler warning: unused variable - _ignoreMouseMove = false; - const Qt::MouseButton button = e->button(); - - switch(d_scrollMode) - { + int ms = 0; + _ignoreMouseMove = false; + const Qt::MouseButton button = e->button(); + const bool clicked = (button == Qt::LeftButton || button == Qt::MidButton); + const bool shift = e->modifiers() & Qt::ShiftModifier; + + _pressed = e->buttons() != Qt::NoButton; + DEBUG_SLIDER_BASE(stderr, "SliderBase::mouseReleaseEvent pos x:%d y:%d last x:%d y:%d e->buttons():%d button:%d _pressed:%d val:%.20f d_valueAtPress:%.20f\n", + e->pos().x(), e->pos().y(), _lastMousePos.x(), _lastMousePos.y(), int(e->buttons()), button, _pressed, value(), d_valueAtPress); + e->accept(); + switch(d_scrollMode) + { case ScrMouse: - if(button == Qt::RightButton) - { - d_scrollMode = ScrNone; - break; - } - if(_cursorHoming && button == Qt::LeftButton) - d_scrollMode = ScrNone; - else - { - setPosition(e->pos()); - d_direction = 0; - d_mouseOffset = 0; - if (d_mass > 0.0) + if(button == Qt::RightButton) { - ms = d_time.elapsed(); - if ((fabs(d_speed) > 0.0) && (ms < 50)) - d_tmrID = startTimer(d_updTime); + d_scrollMode = ScrNone; + break; + } + + if(_borderlessMouse && clicked) + { + d_scrollMode = ScrNone; + if(!_firstMouseMoveAfterPress) + { + _ignoreMouseMove = true; // Avoid recursion. + QCursor::setPos(_lastGlobalMousePos); + //_ignoreMouseMove = false; + } +// showCursor(); } else { +// setPosition(e->pos()); + movePosition(e->pos() - _lastMousePos, shift); + d_direction = 0; + d_mouseOffset = 0; + if (d_mass > 0.0) + { + ms = d_time.elapsed(); + if ((fabs(d_speed) > 0.0) && (ms < 50)) + d_tmrID = startTimer(d_updTime); + } + else + { d_scrollMode = ScrNone; buttonReleased(); + } } - } - emit sliderReleased(_id); + + DEBUG_SLIDER_BASE(stderr, " Calling processSliderReleased val:%.20f d_valueAtPress:%.20f\n", value(), d_valueAtPress); + processSliderReleased(_id); + emit sliderReleased(value(), _id); - break; + break; case ScrDirect: - - setPosition(e->pos()); - d_direction = 0; - d_mouseOffset = 0; - d_scrollMode = ScrNone; - buttonReleased(); - break; +// setPosition(e->pos()); + movePosition(e->pos() - _lastMousePos, shift); + d_direction = 0; + d_mouseOffset = 0; + d_scrollMode = ScrNone; + buttonReleased(); + break; case ScrPage: - stopMoving(); - d_timerTick = 0; - buttonReleased(); - d_scrollMode = ScrNone; - break; + stopMoving(); + d_timerTick = 0; + buttonReleased(); + d_scrollMode = ScrNone; + break; case ScrTimer: - stopMoving(); - d_timerTick = 0; - buttonReleased(); - d_scrollMode = ScrNone; - break; + stopMoving(); + d_timerTick = 0; + buttonReleased(); + d_scrollMode = ScrNone; + break; default: - d_scrollMode = ScrNone; - buttonReleased(); - } + d_scrollMode = ScrNone; + buttonReleased(); + } + + // Make sure this is done. See mousePressEvent. + showCursor(); + setMouseGrab(false); + + d_trackingTempDisable = false; // Reset generating automation graph recording straight lines if ctrl held. } @@ -329,9 +567,32 @@ //------------------------------------------------------------ void SliderBase::setPosition(const QPoint &p) { + DEBUG_SLIDER_BASE(stderr, "SliderBase::setPosition calling fitValue(x:%d, y:%d) d_mouseOffset:%.20f\n", p.x(), p.y(), d_mouseOffset); DoubleRange::fitValue(getValue(p) - d_mouseOffset); } +//------------------------------------------------------------ +// +//.F SliderBase::movePosition +// Move the slider to a specified point, adjust the value +// and emit signals if necessary +// +//.u Syntax +//.f void SliderBase::movePosition(const QPoint &deltaP, bool fineMode) +// +//.u Parameters +//.p const QPoint &deltaP -- Change in position +//.p bool fineMode -- Fine mode if true, coarse mode if false. +// +//.u Description +// Coarse mode (the normal mode) maps pixels to values depending on range and width, +// such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. +//------------------------------------------------------------ +void SliderBase::movePosition(const QPoint &deltaP, bool fineMode) +{ + DEBUG_SLIDER_BASE(stderr, "SliderBase::movePosition calling fitValue(delta x:%d, y:%d) fineMode:%d\n", deltaP.x(), deltaP.y(), fineMode); + DoubleRange::fitValue(moveValue(deltaP, fineMode)); +} //------------------------------------------------------------ // @@ -377,31 +638,175 @@ //------------------------------------------------------------ void SliderBase::mouseMoveEvent(QMouseEvent *e) { - if(_ignoreMouseMove) + DEBUG_SLIDER_BASE(stderr, "SliderBase::mouseMoveEvent _ignoreMouseMove:%d\n", _ignoreMouseMove); + + e->accept(); + + const bool shift = e->modifiers() & Qt::ShiftModifier; + //const bool ctrl = e->modifiers() & Qt::ControlModifier; + const bool meta = e->modifiers() & Qt::MetaModifier; + + //d_trackingTempDisable = ctrl; // Generate automation graph recording straight lines if modifier held. + + if(_ignoreMouseMove) + { + DEBUG_SLIDER_BASE(stderr, "SliderBase::mouseMoveEvent ignoring mouse move\n"); + _ignoreMouseMove = false; + return; + } + + const double prevValue = value(ConvertNone); + + if (d_scrollMode == ScrMouse ) + { + d_trackingTempDisable = meta; // Generate automation graph recording straight lines if modifier held. + if(borderlessMouse()) { - _ignoreMouseMove = false; - return; + const QRect r = QApplication::desktop()->screenGeometry(); + const QPoint scrn_cntr(r.width()/2, r.height()/2); + QPoint delta; + if(_firstMouseMoveAfterPress) + { + _firstMouseMoveAfterPress = false; + delta = e->globalPos() - _lastGlobalMousePos; + DEBUG_SLIDER_BASE(stderr, + "SliderBase::mouseMoveEvent firstMouseMoveAfterPress\n e->globalPos() x:%d y:%d _lastGlobalMousePos x:%d y:%d calling setPosition(delta x:%d, y:%d)\n", + e->globalPos().x(), e->globalPos().y(), _lastGlobalMousePos.x(), _lastGlobalMousePos.y(), delta.x(), delta.y()); + } + else + { + delta = e->globalPos() - scrn_cntr; + DEBUG_SLIDER_BASE(stderr, + "SliderBase::mouseMoveEvent not firstMouseMoveAfterPress\n e->globalPos() x:%d y:%d scrn_cntr x:%d y:%d calling setPosition(delta x:%d, y:%d)\n", + e->globalPos().x(), e->globalPos().y(), scrn_cntr.x(), scrn_cntr.y(), delta.x(), delta.y()); + } + setPosition(delta); + _ignoreMouseMove = true; + QCursor::setPos(scrn_cntr); + //_ignoreMouseMove = false; + } + else + { + //if(shift) + // movePosition(e->pos() - _lastMousePos, true); + //else + // setPosition(e->pos()); + movePosition(e->pos() - _lastMousePos, shift); } + + _mouseDeltaAccum += (e->pos() - _lastMousePos); + _lastMousePos = e->pos(); + _lastGlobalMousePos = e->globalPos(); - double ms = 0.0; - if (d_scrollMode == ScrMouse ) + if (d_mass > 0.0) + { + double ms = double(d_time.elapsed()); + if (ms < 1.0) ms = 1.0; + d_speed = (exactValue(ConvertNone) - exactPrevValue(ConvertNone)) / ms; + d_time.start(); + } + + //const bool valch = (valueHasChangedAtRelease()); + const bool valch = value(ConvertNone) != prevValue; + + // Show a handy tooltip value box. + if(d_enableValueToolTips && valch) + //showValueToolTip(mapToGlobal(pos())); + showValueToolTip(e->globalPos()); + + if(valch) { - setPosition(e->pos()); - if (d_mass > 0.0) + emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, shift); + } + } + else if(d_scrollMode == ScrNone) { - ms = double(d_time.elapsed()); - if (ms < 1.0) ms = 1.0; - d_speed = (exactValue() - exactPrevValue()) / ms; - d_time.start(); + // Show a handy tooltip value box. + if(d_enableValueToolTips && d_showValueToolTipsOnHover) + //showValueToolTip(mapToGlobal(pos())); + showValueToolTip(e->globalPos()); } - if (value() != prevValue()) - emit sliderMoved(value(), _id); - emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); - } +} +void SliderBase::mouseDoubleClickEvent(QMouseEvent* e) +{ + DEBUG_SLIDER_BASE(stderr, "mouseDoubleClickEvent::mouseDoubleClickEvent\n"); + emit sliderDoubleClicked(e->pos(), _id, e->buttons(), e->modifiers()); + e->ignore(); + QWidget::mouseDoubleClickEvent(e); } +void SliderBase::keyPressEvent(QKeyEvent* e) +{ + int val = 0; + switch (e->key()) + { + case Qt::Key_Up: + val = 1; + break; + + case Qt::Key_Down: + val = -1; + break; + + default: + // Let ancestor handle it. + e->ignore(); + QWidget::keyPressEvent(e); + return; + break; + } + + if(e->modifiers() & (Qt::AltModifier | Qt::MetaModifier | Qt::ControlModifier)) + { + // Let ancestor handle it. + e->ignore(); + QWidget::keyPressEvent(e); + return; + } + + e->accept(); + // Do not allow setting value from the external while mouse is pressed. + if(_pressed) + return; + + if(e->modifiers() == Qt::ShiftModifier) + //incPages(val); + incValue(val * 5); + else + incValue(val); + + // Show a handy tooltip value box. + //if(d_enableValueToolTips) + // showValueToolTip(e->globalPos()); + emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); +} + +void SliderBase::focusOutEvent(QFocusEvent* e) +{ + DEBUG_SLIDER_BASE(stderr, "SliderBase::focusOutEvent _pressed:%d\n", _pressed); + e->ignore(); + QWidget::focusOutEvent(e); + + // Was a mouse button already pressed before focus was lost? + // We will NOT get a mouseReleaseEvent! Take care of it here. + // Typically this happens when popping up a menu in response to a click. + if(_pressed) + { + // Clear everything. + _ignoreMouseMove = false; + d_scrollMode = ScrNone; + d_direction = 0; + _pressed = false; + + // Make sure this is done. See mousePressEvent. + showCursor(); + setMouseGrab(false); + } +} //------------------------------------------------------------ // @@ -418,67 +823,76 @@ void SliderBase::timerEvent(QTimerEvent*) { - double newval; - double inc = step(); + const double prevValue = value(ConvertNone); + double newval; + double inc = step(); - switch (d_scrollMode) - { - case ScrMouse: - if (d_mass > 0.0) + switch (d_scrollMode) { - d_speed *= exp( - double(d_updTime) * 0.001 / d_mass ); - newval = exactValue() + d_speed * double(d_updTime); - DoubleRange::fitValue(newval); - // stop if d_speed < one step per second - if (fabs(d_speed) < 0.001 * fabs(step())) - { - d_speed = 0; - stopMoving(); - buttonReleased(); + case ScrMouse: + if (d_mass > 0.0) + { + d_speed *= exp( - double(d_updTime) * 0.001 / d_mass ); + newval = exactValue(ConvertNone) + d_speed * double(d_updTime); + DoubleRange::fitValue(newval); + // stop if d_speed < one step per second + if (fabs(d_speed) < 0.001 * fabs(step())) + { + d_speed = 0; + stopMoving(); + buttonReleased(); + } } - - } - else - stopMoving(); - - break; + else + stopMoving(); + break; case ScrPage: - DoubleRange::incPages(d_direction); + DoubleRange::incPages(d_direction); - if (value() != prevValue()) - { - emit sliderMoved(value(), _id); - emit sliderMoved(value(), _id, false); - } + if(value(ConvertNone) != prevValue) + { + // Show a handy tooltip value box. + if(d_enableValueToolTips) + showValueToolTip(cursor().pos()); + + emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, false); + } - if (!d_timerTick) - { - killTimer(d_tmrID); - d_tmrID = startTimer(d_updTime); - } - break; + if (!d_timerTick) + { + killTimer(d_tmrID); + d_tmrID = startTimer(d_updTime); + } + break; + case ScrTimer: - DoubleRange::fitValue(value() + double(d_direction) * inc); + DoubleRange::fitValue(value(ConvertNone) + double(d_direction) * inc); - if (value() != prevValue()) - { - emit sliderMoved(value(), _id); - emit sliderMoved(value(), _id, false); - } - - if (!d_timerTick) - { - killTimer(d_tmrID); - d_tmrID = startTimer(d_updTime); - } - break; + if(value(ConvertNone) != prevValue) + { + // Show a handy tooltip value box. + if(d_enableValueToolTips) + showValueToolTip(cursor().pos()); + + emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, false); + } + + if (!d_timerTick) + { + killTimer(d_tmrID); + d_tmrID = startTimer(d_updTime); + } + break; + default: - stopMoving(); - break; - } + stopMoving(); + break; + } - d_timerTick = 1; + d_timerTick = 1; } @@ -502,8 +916,11 @@ //------------------------------------------------------------ void SliderBase::valueChange() { - if (d_tracking) + if (trackingIsActive()) + { emit valueChanged(value(), _id); + emit valueChanged(value(), _id, d_scrollMode); + } } //------------------------------------------------------------ @@ -561,11 +978,14 @@ // @SliderBase::fitValue@ //------------------------------------------------------------ -void SliderBase::setValue(double val) +void SliderBase::setValue(double val, ConversionMode mode) { + // Do not allow setting value from the external while mouse is pressed. + if(_pressed) + return; if (d_scrollMode == ScrMouse) stopMoving(); - DoubleRange::setValue(val); + DoubleRange::setValue(val, mode); } @@ -581,10 +1001,13 @@ //.u See also: // @SliderBase::setValue@ //------------------------------------------------------------ -void SliderBase::fitValue(double val) +void SliderBase::fitValue(double val, ConversionMode mode) { + // Do not allow setting value from the external while mouse is pressed. + if(_pressed) + return; if (d_scrollMode == ScrMouse) stopMoving(); - DoubleRange::fitValue(val); + DoubleRange::fitValue(val, mode); } @@ -602,6 +1025,9 @@ //------------------------------------------------------------ void SliderBase::incValue(int steps) { + // Do not allow setting value from the external while mouse is pressed. + if(_pressed) + return; if (d_scrollMode == ScrMouse) stopMoving(); DoubleRange::incValue(steps); } @@ -627,6 +1053,9 @@ //------------------------------------------------------------ void SliderBase::stepPages(int pages) { + // Do not allow setting value from the external while mouse is pressed. + if(_pressed) + return; DoubleRange::incPages(pages); emit sliderMoved(value(), _id); emit sliderMoved(value(), _id, false); diff -Nru muse-2.1.2/muse/widgets/sliderbase.h muse-3.0.2+ds1/muse/widgets/sliderbase.h --- muse-2.1.2/muse/widgets/sliderbase.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/sliderbase.h 2017-12-04 21:01:19.000000000 +0000 @@ -5,6 +5,7 @@ // Copyright (C) 1997 Josef Wilgen // (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -30,6 +31,8 @@ #include #include +class QFocusEvent; + namespace MusEGui { //--------------------------------------------------------- @@ -44,7 +47,10 @@ Q_PROPERTY( double maxValue READ maxValue WRITE setMaxValue ) Q_PROPERTY( double value READ value WRITE setValue ) - int _id; + public: + enum SliderBaseScrollMode { ScrNone, ScrMouse, ScrTimer, ScrDirect, ScrPage }; + + int _id; int d_tmrID; int d_updTime; int d_timerTick; @@ -52,65 +58,129 @@ double d_speed; double d_mass; bool _cursorHoming; - bool _ignoreMouseMove; + bool _borderlessMouse; + double d_valAccum; + QPoint _mouseDeltaAccum; + QPoint _lastMousePos; + QPoint _lastGlobalMousePos; + Qt::MouseButtons _pagingButtons; + // Internal flags. + bool _ignoreMouseMove; + bool _firstMouseMoveAfterPress; + // Whether we have grabbed the mouse. + bool _mouseGrabbed; + // The number of times we have called QApplication::setOverrideCursor(). + // This should always be one or zero, anything else is an error, but unforeseen + // events might cause us to miss a decrement with QApplication::restoreOverrideCursor(). + int _cursorOverrideCount; + + // If show is true, calls QApplication::restoreOverrideCursor() until _cursorOverrideCount-- is <= 0. + // If show is false, calls QApplication::setOverrideCursor with a blank cursor. + void showCursor(bool show = true); + // Sets or resets the _mouseGrabbed flag and grabs or releases the mouse. + void setMouseGrab(bool grabbed = false); + void buttonReleased(); protected: + bool d_enableValueToolTips; + bool d_showValueToolTipsOnHover; int d_scrollMode; double d_mouseOffset; int d_direction; int d_tracking; + bool _pressed; + bool d_trackingTempDisable; + double d_valueAtPress; + + bool valueHasChangedAtRelease() const { return value(ConvertNone) != d_valueAtPress; } virtual void setMass(double val); void setPosition(const QPoint &p); + void movePosition(const QPoint &deltaP, bool fineMode); virtual void valueChange(); virtual double mass() const { return d_mass; } - void wheelEvent(QWheelEvent *e); + void wheelEvent(QWheelEvent *e); void timerEvent(QTimerEvent *e); void mousePressEvent(QMouseEvent *e); void mouseReleaseEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void keyPressEvent(QKeyEvent*); + // Required because if focus is lost while a mouse button is pressed, we do not get the mouseReleaseEvent. + virtual void focusOutEvent(QFocusEvent*); + + // Determine the value corresponding to a specified mouse location. + // If borderless mouse is enabled p is a delta value not absolute, so can be negative. virtual double getValue(const QPoint & p) = 0; - virtual void getScrollMode( QPoint &p, const Qt::MouseButton &button, + // Determine the value corresponding to a specified mouse movement. + virtual double moveValue(const QPoint &deltaP, bool fineMode = false) = 0; + // Determine scrolling mode and direction. + virtual void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction) = 0; + // Show a handy tooltip value box. + virtual void showValueToolTip(QPoint) { } + // Same as sliderPressed signal, except it's not a signal and is called before sliderPressed is emitted. + virtual void processSliderPressed(int) { } + // Same as sliderReleased signal, except it's not a signal and is called before sliderReleased is emitted. + virtual void processSliderReleased(int) { } + public slots: - void setValue(double val); - void fitValue(double val); + void setValue(double val, ConversionMode mode = ConvertDefault); + void fitValue(double val, ConversionMode mode = ConvertDefault); void incValue(int steps); signals: void valueChanged(double value, int id); - void sliderPressed(int id); - void sliderReleased(int id); + void valueChanged(double value, int id, int scrollMode); + void sliderPressed(double value, int id); + void sliderReleased(double value, int id); void sliderMoved(double value, int id); void sliderMoved(double value, int id, bool shift); - void sliderRightClicked(const QPoint &p, int id); + void sliderRightClicked(QPoint p, int id); + void sliderDoubleClicked(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); public: - enum { ScrNone, ScrMouse, ScrTimer, ScrDirect, ScrPage }; SliderBase( QWidget *parent = 0, const char *name = 0 ); - ~SliderBase(); + virtual ~SliderBase(); + + // Useful for signal mappers where we can't pass scroll mode. + int scrollMode() const { return d_scrollMode; } + bool mouseGrabbed() const { return _mouseGrabbed; } + bool isPressed() const { return _pressed; } bool cursorHoming() const { return _cursorHoming; } void setCursorHoming(bool b) { _cursorHoming = b; } + bool borderlessMouse() const { return _borderlessMouse; } + void setBorderlessMouse(bool v) { _borderlessMouse = v; update(); } + // The allowed mouse buttons which will cause a page step. + Qt::MouseButtons pagingButtons() const { return _pagingButtons; } + // Set the allowed mouse buttons which will cause a page step. + void setPagingButtons(Qt::MouseButtons buttons) { _pagingButtons = buttons; } + + bool enableValueToolTips() const { return d_enableValueToolTips; } + void setEnableValueToolTips(bool enable) { d_enableValueToolTips = enable; } + bool showValueToolTipsOnHover() const { return d_showValueToolTipsOnHover; } + void setShowValueToolTipsOnHover(bool enable) { d_showValueToolTipsOnHover = enable; } + void setUpdateTime(int t); - // void incValue(double nSteps); void stopMoving(); + bool tracking() const { return d_tracking; } void setTracking(bool enable); + bool trackingIsActive() const { return d_tracking && !d_trackingTempDisable; } - double value() const { return DoubleRange::value(); } - void stepPages(int pages); - double minValue() const { return DoubleRange::minValue(); } - double maxValue() const { return DoubleRange::maxValue(); } - void setMinValue(double v) { DoubleRange::setRange(v, maxValue(), 0.0, 1); } - void setMaxValue(double v) { DoubleRange::setRange(minValue(), v, 0.0, 1); } - int id() const { return _id; } - void setId(int i) { _id = i; } - }; + void stepPages(int pages); + void setMinValue(double v, ConversionMode mode = ConvertDefault) + { DoubleRange::setRange(v, maxValue(mode), 0.0, 1, mode); } + void setMaxValue(double v, ConversionMode mode = ConvertDefault) + { DoubleRange::setRange(minValue(mode), v, 0.0, 1, mode); } + int id() const { return _id; } + void setId(int i) { _id = i; } + }; } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/slider.cpp muse-3.0.2+ds1/muse/widgets/slider.cpp --- muse-2.1.2/muse/widgets/slider.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/slider.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -5,6 +5,7 @@ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -30,6 +31,10 @@ #include "utils.h" #include "slider.h" +// For debugging output: Uncomment the fprintf section. +#define DEBUG_SLIDER(dev, format, args...) // fprintf(dev, format, ##args); + + namespace MusEGui { //------------------------------------------------------------- @@ -59,29 +64,37 @@ // Slider::Left, Slider::Right, Slider::Top, // or Slider::Bottom. Defaults to Slider::None. !!! CURRENTLY only Slider::None supported - oget 20110913 // QColor fillcolor -- the color used to fill in the full side -// of the Slider +// of the Slider. If fillColor is invalid (default) it will draw with the palette highlight colour. //------------------------------------------------------------ Slider::Slider(QWidget *parent, const char *name, - Qt::Orientation orient, ScalePos scalePos, QColor fillColor) - : SliderBase(parent,name) + Qt::Orientation orient, + ScalePos scalePos, + int grooveWidth, + QColor fillColor, + ScaleDraw::TextHighlightMode textHighlightMode) + : SliderBase(parent,name), d_scalePos(scalePos), d_grooveWidth(grooveWidth), d_fillColor(fillColor) { + setPagingButtons(Qt::RightButton); + d_thumbLength = 16; d_thumbHalf = 8; d_thumbWidth = 16; - + d_fillThumb = true; + d_fillEmptySide = true; d_scaleDist = 4; d_scaleStep = 0.0; - d_scalePos = scalePos; d_xMargin = 0; d_yMargin = 0; d_mMargin = 1; - d_fillColor = fillColor; - + horizontal_hint = 40; + vertical_hint = 40; + d_sliderRect.setRect(0, 0, 8, 8); setOrientation(orient); + d_scale.setTextHighlightMode(textHighlightMode); } //------------------------------------------------------------ @@ -120,7 +133,8 @@ //----------------------------------------------------- void Slider::setThumbLength(int l) { - d_thumbLength = MusECore::qwtMax(l,8); +// d_thumbLength = MusECore::qwtMax(l,8); + d_thumbLength = l; d_thumbHalf = d_thumbLength / 2; resize(size()); } @@ -175,7 +189,128 @@ //------------------------------------------------------------ void Slider::fontChange(const QFont & /*oldFont*/) { - repaint(); +// repaint(); + update(); +} + +void Slider::drawThumb(QPainter *p, const QRect &r) +{ + p->setRenderHint(QPainter::Antialiasing); + const QPalette& pal = palette(); + + QColor thumb_edge = pal.dark().color(); +// QColor thumb_center = pal.midlight().color(); + QColor thumb_center = pal.mid().color(); + //thumb_edge.setAlpha(60); + //thumb_center.setAlpha(60); + QLinearGradient thumbGrad; + thumbGrad.setColorAt(0, thumb_edge); + thumbGrad.setColorAt(0.5, thumb_center); + thumbGrad.setColorAt(1, thumb_edge); + + const double rpos = (value(ConvertNone) - minValue(ConvertNone)) / (maxValue(ConvertNone) - minValue(ConvertNone)); + + if(d_orient == Qt::Horizontal) + { + int crh, thh; +// if(d_scalePos == InsideHorizontal) +// { +// crh = height() - r.y() - d_yMargin - 2 * d_mMargin; +// thh = height() - r.y() - d_yMargin - d_mMargin; +// } +// else + { + crh = r.height() - 2 * d_mMargin; + thh = r.height(); + } + + const QRect cr(r.x(), + r.y() + d_mMargin, + r.width(), + //r.height() - 2*d_mMargin); + crh); + + const int dist1 = int(double(cr.width() - d_thumbLength) * rpos); + const int ipos = cr.x() + dist1; + markerPos = ipos + d_thumbHalf; + + // + // Draw thumb + // + + QPainterPath thumb_rect = MusECore::roundedPath(ipos, r.y(), + //d_thumbLength, r.height(), + d_thumbLength, thh, + 2, 2, + (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); + +// thumbGrad.setStart(QPointF(0, cr.y())); +// thumbGrad.setFinalStop(QPointF(0, cr.y() + cr.height())); + thumbGrad.setStart(QPointF(ipos, 0)); + thumbGrad.setFinalStop(QPointF(ipos + d_thumbLength, 0)); + + if(d_fillThumb) + p->fillPath(thumb_rect, QBrush(thumbGrad)); + else + { + p->setPen(pal.shadow().color()); + p->drawPath(thumb_rect); + } + + // center line + p->fillRect(ipos + d_thumbHalf, cr.y(), 1, cr.height(), pal.dark().color()); + } + else + { + int crw, thw; +// if(d_scalePos == InsideVertical) +// { +// crw = width() - r.x() - d_xMargin - 2 * d_mMargin; +// thw = width() - r.x() - d_xMargin - d_mMargin; +// } +// else + { + crw = r.width() - 2 * d_mMargin; + thw = r.width(); + } + + const QRect cr(r.x() + d_mMargin, + r.y(), + //r.width() - 2*d_mMargin, + crw, + r.height()); + + const int dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); + const int ipos = cr.y() + dist1; + markerPos = ipos + d_thumbHalf; + + // + // Draw thumb + // + + QPainterPath thumb_rect = MusECore::roundedPath(r.x(), ipos, + //r.width(), d_thumbLength, + thw, d_thumbLength, + 2, 2, + (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); + +// thumbGrad.setStart(QPointF(cr.x(), 0)); +// thumbGrad.setFinalStop(QPointF(cr.x() + cr.width(), 0)); + thumbGrad.setStart(QPointF(0, ipos)); + thumbGrad.setFinalStop(QPointF(0, ipos + d_thumbLength)); + + if(d_fillThumb) + p->fillPath(thumb_rect, QBrush(thumbGrad)); + else + { + p->setPen(pal.shadow().color()); + p->drawPath(thumb_rect); + } + + // center line + p->fillRect(cr.x(), ipos + d_thumbHalf, cr.width(), 1, pal.dark().color()); + } + } //------------------------------------------------------------ @@ -188,207 +323,110 @@ p->setRenderHint(QPainter::Antialiasing); const QPalette& pal = palette(); - QBrush brBack(pal.window()); - QBrush brMid(pal.mid()); - QBrush brDark(pal.dark()); - - QRect cr; - - int ipos,dist1; - double rpos; int xrad = 4; int yrad = 4; - // for the empty side - QColor e_mask_edge = pal.mid().color(); - QColor e_mask_center = pal.midlight().color(); - int e_alpha = 215; - e_mask_edge.setAlpha(e_alpha); - e_mask_center.setAlpha(e_alpha); - - QLinearGradient e_mask; - e_mask.setColorAt(0, e_mask_edge); - e_mask.setColorAt(0.5, e_mask_center); - e_mask.setColorAt(1, e_mask_edge); - // for the full side - rpos = (value() - minValue()) / (maxValue() - minValue()); + const double rpos = (value(ConvertNone) - minValue(ConvertNone)) / (maxValue(ConvertNone) - minValue(ConvertNone)); - int f_brightness = 155 * rpos + 100; - int f_alpha; - int f_edge; - if (pal.currentColorGroup() == QPalette::Disabled) - { - f_alpha = 185; - f_edge = 100; - } - else - { - f_alpha = 127; - f_edge = 0; - } - - QColor f_mask_center = QColor(f_brightness, f_brightness, f_brightness, f_alpha); - QColor f_mask_edge = QColor(f_edge, f_edge, f_edge, f_alpha); + QColor f_mask_min(d_fillColor.isValid() ? d_fillColor : pal.highlight().color()); + QColor f_mask_max(f_mask_min); + f_mask_min.setAlpha(40); + //f_mask_max.setAlpha(200); + f_mask_max.setAlpha(255); QLinearGradient f_mask; - f_mask.setColorAt(0, f_mask_edge); - f_mask.setColorAt(0.5, f_mask_center); - f_mask.setColorAt(1, f_mask_edge); - - // for the thumb - QLinearGradient thumbGrad; - QColor thumb_edge = pal.dark().color(); - QColor thumb_center = pal.midlight().color(); - - thumbGrad.setColorAt(0, thumb_edge); - thumbGrad.setColorAt(0.5, thumb_center); - thumbGrad.setColorAt(1, thumb_edge); - - if (d_orient == Qt::Horizontal) { - cr.setRect(r.x(), - r.y() + d_mMargin, - r.width(), - r.height() - 2*d_mMargin); + const QRect cr(r.x(), + r.y() + r.height() / 2 - d_grooveWidth / 2, + r.width(), + d_grooveWidth); // // Draw background // - QPainterPath bg_rect = MusECore::roundedPath(cr, - xrad, yrad, - (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); - - p->fillPath(bg_rect, d_fillColor); - - dist1 = int(double(cr.width() - d_thumbLength) * rpos); - ipos = cr.x() + dist1; + + const int dist1 = int(double(cr.width() - (d_fillThumb ? d_thumbLength : d_thumbHalf)) * rpos); + const int ipos = cr.x() + dist1; markerPos = ipos + d_thumbHalf; - // - // Draw empty right side + // Draw groove empty right side // - e_mask.setStart(QPointF(0, cr.y())); - e_mask.setFinalStop(QPointF(0, cr.y() + cr.height())); - - QPainterPath e_rect = MusECore::roundedPath(ipos + d_thumbLength, cr.y(), - cr.width() - d_thumbLength - dist1, cr.height(), - xrad, yrad, (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight) ); - - p->fillPath(e_rect, QBrush(e_mask)); + if(d_fillEmptySide) + { + QPainterPath e_rect = MusECore::roundedPath(ipos + (d_fillThumb ? d_thumbLength : d_thumbHalf), cr.y(), + cr.width() - (d_fillThumb ? d_thumbLength : d_thumbHalf) - dist1, cr.height(), + xrad, yrad, (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight) ); + + p->fillPath(e_rect, f_mask_min); + } // - // Draw full left side + // Draw groove full left side // - f_mask.setStart(QPointF(0, cr.y())); - f_mask.setFinalStop(QPointF(0, cr.y() + cr.height())); + f_mask.setColorAt(0, f_mask_min); + f_mask.setColorAt(1, f_mask_max); + f_mask.setStart(QPointF(cr.x(), cr.y())); + f_mask.setFinalStop(QPointF(cr.x() + ipos + (d_fillThumb ? 0 : d_thumbHalf), cr.y())); QPainterPath f_rect = MusECore::roundedPath(cr.x(), cr.y(), - ipos + 1, cr.height(), + ipos + (d_fillThumb ? 0 : d_thumbHalf), cr.height(), xrad, yrad, (MusECore::Corner) (MusECore::LowerLeft | MusECore::UpperLeft) ); p->fillPath(f_rect, QBrush(f_mask)); - - - // - // Draw thumb - // - - QPainterPath thumb_rect = MusECore::roundedPath(ipos, r.y(), - d_thumbLength, r.height(), - 2, 2, - (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); - - thumbGrad.setStart(QPointF(0, cr.y())); - thumbGrad.setFinalStop(QPointF(0, cr.y() + cr.height())); - - - p->fillPath(thumb_rect, QBrush(thumbGrad)); - - // center line - p->fillRect(ipos + d_thumbHalf, cr.y(), 1, cr.height(), pal.dark().color()); - - } else // (d_orient == Qt::Vertical) { - - cr.setRect(r.x() + d_mMargin, - r.y(), - r.width() - 2*d_mMargin, - r.height()); - + const QRect cr(r.x() + r.width() / 2 - d_grooveWidth / 2, + r.y(), + d_grooveWidth, + r.height()); // // Draw background // - QPainterPath bg_rect = MusECore::roundedPath(cr, - xrad, yrad, - (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); - - p->fillPath(bg_rect, d_fillColor); - - dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); - ipos = cr.y() + dist1; + + const int dist1 = int(double(cr.height() - (d_fillThumb ? d_thumbLength : d_thumbHalf)) * (1.0 - rpos)); + const int ipos = cr.y() + dist1; markerPos = ipos + d_thumbHalf; - // - // Draw empty upper filling + // Draw groove empty upper filling // - e_mask.setStart(QPointF(cr.x(), 0)); - e_mask.setFinalStop(QPointF(cr.x() + cr.width(), 0)); - - QPainterPath e_rect = MusECore::roundedPath(cr.x(), cr.y(), - cr.width(), ipos + 1, - xrad, yrad, - (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight) ); - - p->fillPath(e_rect, QBrush(e_mask)); - + if(d_fillEmptySide) + { + QPainterPath e_rect = MusECore::roundedPath(cr.x(), cr.y(), + cr.width(), ipos + (d_fillThumb ? 0 : d_thumbHalf), + xrad, yrad, + (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight) ); + + p->fillPath(e_rect, QBrush(f_mask_min)); + } // - // Draw lower filling mask + // Draw groove lower filling mask // - f_mask.setStart(QPointF(cr.x(), 0)); - f_mask.setFinalStop(QPointF(cr.x() + cr.width(), 0)); + f_mask.setColorAt(0, f_mask_max); + f_mask.setColorAt(1, f_mask_min); + f_mask.setStart(QPointF(cr.x(), markerPos)); + f_mask.setFinalStop(QPointF(cr.x(), cr.y() + cr.height())); - QPainterPath f_rect = MusECore::roundedPath(cr.x(), ipos + d_thumbLength, - cr.width(), cr.height() - d_thumbLength - dist1, + QPainterPath f_rect = MusECore::roundedPath(cr.x(), ipos + (d_fillThumb ? d_thumbLength : d_thumbHalf), + cr.width(), cr.height() - (d_fillThumb ? d_thumbLength : d_thumbHalf) - dist1, xrad, yrad, (MusECore::Corner) (MusECore::LowerLeft | MusECore::LowerRight) ); p->fillPath(f_rect, QBrush(f_mask)); - - - // - // Draw thumb - // - - QPainterPath thumb_rect = MusECore::roundedPath(r.x(), ipos, - r.width(), d_thumbLength, - 2, 2, - (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); - - thumbGrad.setStart(QPointF(cr.x(), 0)); - thumbGrad.setFinalStop(QPointF(cr.x() + cr.width(), 0)); - - - p->fillPath(thumb_rect, QBrush(thumbGrad)); - - // center line - p->fillRect(cr.x(), ipos + d_thumbHalf, cr.width(), 1, pal.dark().color()); - } } @@ -410,49 +448,126 @@ //------------------------------------------------------------ double Slider::getValue( const QPoint &p) { - double rv; - int pos; - QRect r = d_sliderRect; + double rv; + const QRect r = d_sliderRect; + const double val = value(ConvertNone); - if (d_orient == Qt::Horizontal) - { + if(borderlessMouse() && d_scrollMode != ScrDirect) + { + DEBUG_SLIDER(stderr, "Slider::getValue value:%.20f p x:%d y:%d step:%.20f x change:%.20f\n", + val, p.x(), p.y(), step(), p.x() * step()); + if(d_orient == Qt::Horizontal) + return val + p.x() * step(); + else + return val - p.y() * step(); + } - if (r.width() <= d_thumbLength) + const double min = minValue(ConvertNone); + const double max = maxValue(ConvertNone); + const double drange = max - min; + + if(d_orient == Qt::Horizontal) { - rv = 0.5 * (minValue() + maxValue()); + if(r.width() <= d_thumbLength) + rv = 0.5 * (min + max); + else + { + const double dpos = double(p.x() - r.x() - d_thumbHalf); + const double dwidth = double(r.width() - d_thumbLength); + rv = min + rint(drange * dpos / dwidth / step()) * step(); + } } else { - pos = p.x() - r.x() - d_thumbHalf; - rv = minValue() + - rint( (maxValue() - minValue()) * double(pos) - / double(r.width() - d_thumbLength) - / step() ) * step(); + if(r.height() <= d_thumbLength) + rv = 0.5 * (min + max); + else + { + const double dpos = double(p.y() - r.y() - d_thumbHalf); + double dheight = double(r.height() - d_thumbLength); + rv = min + rint(drange * (1.0 - dpos / dheight) / step()) * step(); + } + } + return(rv); +} + +//------------------------------------------------------------ +// +//.F Slider::moveValue +// Determine the value corresponding to a specified mouse movement. +// +//.u Syntax +//.f void Slider::moveValue(const QPoint &deltaP, bool fineMode) +// +//.u Parameters +//.p const QPoint &deltaP -- Change in position +//.p bool fineMode -- Fine mode if true, coarse mode if false. +// +//.u Description +// Called by SliderBase +// Coarse mode (the normal mode) maps pixels to values depending on range and width, +// such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. +//------------------------------------------------------------ +double Slider::moveValue(const QPoint &deltaP, bool fineMode) +{ + double rv; + const QRect r = d_sliderRect; + + const double val = value(ConvertNone); + + if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) + { + DEBUG_SLIDER(stderr, "Slider::moveValue value:%.20f p x:%d y:%d step:%.20f x change:%.20f\n", + val, deltaP.x(), deltaP.y(), step(), deltaP.x() * step()); + + double newval; + if(d_orient == Qt::Horizontal) + newval = val + deltaP.x() * step(); + else + newval = val - deltaP.y() * step(); + d_valAccum = newval; // Reset. + return newval; } - } + const double min = minValue(ConvertNone); + const double max = maxValue(ConvertNone); + const double drange = max - min; + + if(d_orient == Qt::Horizontal) + { + if(r.width() <= d_thumbLength) + rv = 0.5 * (min + max); else { - if (r.height() <= d_thumbLength) - { - rv = 0.5 * (minValue() + maxValue()); + const double dpos = double(deltaP.x()); + const double dwidth = double(r.width() - d_thumbLength); + const double dval_diff = (drange * dpos) / dwidth; + d_valAccum += dval_diff; + rv = rint(d_valAccum / step()) * step(); + + DEBUG_SLIDER(stderr, "Slider::moveValue Horizontal value:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f d_valAccum:%.20f rv:%.20f\n", + val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, d_valAccum, rv); + } } else { - pos = p.y() - r.y() - d_thumbHalf; - rv = minValue() + - rint( (maxValue() - minValue()) * - (1.0 - double(pos) - / double(r.height() - d_thumbLength)) - / step() ) * step(); - } - + if(r.height() <= d_thumbLength) + rv = 0.5 * (min + max); + else + { + const double dpos = double(-deltaP.y()); + const double dheight = double(r.height() - d_thumbLength); + const double dval_diff = (drange * dpos) / dheight; + d_valAccum += dval_diff; + rv = rint(d_valAccum / step()) * step(); + + DEBUG_SLIDER(stderr, "Slider::moveValue Vertical value:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f d_valAccum:%.20f rv:%.20f\n", + val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, d_valAccum, rv); } - - return(rv); + } + return(rv); } - //------------------------------------------------------------ //.- //.F Slider::getScrollMode @@ -468,12 +583,33 @@ // Called by SliderBase // //------------------------------------------------------------ -void Slider::getScrollMode( QPoint &p, const Qt::MouseButton &button, int &scrollMode, int &direction ) +void Slider::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ) { + // If modifier or button is held, jump directly to the position at first. + // After handling it, the caller can change to SrcMouse scroll mode. + if(modifiers & Qt::ControlModifier || button == Qt::MidButton) + { + scrollMode = ScrDirect; + direction = 0; + return; + } + + if(borderlessMouse()) + { + if(button != Qt::NoButton && d_sliderRect.contains(p)) + { + scrollMode = ScrMouse; + direction = 0; + return; + } + } + else + { if(cursorHoming() && button == Qt::LeftButton) { if(d_sliderRect.contains(p)) { + DEBUG_SLIDER(stderr, "Slider::getScrollMode cursor homing + left button: ScrMouse\n"); scrollMode = ScrMouse; direction = 0; @@ -485,7 +621,7 @@ cr = d_sliderRect; - rpos = (value() - minValue()) / (maxValue() - minValue()); + rpos = (value(ConvertNone) - minValue(ConvertNone)) / (maxValue(ConvertNone) - minValue(ConvertNone)); if(d_orient == Qt::Horizontal) { @@ -505,41 +641,44 @@ cp = mapToGlobal( QPoint(p.x(), mp) ); } cursor().setPos(cp.x(), cp.y()); + return; } } else { int currentPos; - if (d_orient == Qt::Horizontal) + if(d_orient == Qt::Horizontal) currentPos = p.x(); else currentPos = p.y(); - if (d_sliderRect.contains(p)) + if(d_sliderRect.contains(p)) { - if ((currentPos > markerPos - d_thumbHalf) + if((currentPos > markerPos - d_thumbHalf) && (currentPos < markerPos + d_thumbHalf)) { + DEBUG_SLIDER(stderr, "Slider::getScrollMode ScrMouse\n"); scrollMode = ScrMouse; direction = 0; + return; } - else + else if(pagingButtons().testFlag(button)) { + DEBUG_SLIDER(stderr, "Slider::getScrollMode ScrPage\n"); scrollMode = ScrPage; if (((currentPos > markerPos) && (d_orient == Qt::Horizontal)) || ((currentPos <= markerPos) && (d_orient != Qt::Horizontal))) direction = 1; else direction = -1; + return; } } - else - { - scrollMode = ScrNone; - direction = 0; - } - } + } + + scrollMode = ScrNone; + direction = 0; } //------------------------------------------------------------ @@ -551,21 +690,228 @@ //------------------------------------------------------------ void Slider::paintEvent(QPaintEvent* /*ev*/) +{ + QPainter p(this); + if(d_grooveWidth != 0) + drawSlider(&p, d_sliderRect); + + if(d_thumbLength != 0) + drawThumb(&p, d_sliderRect); + if(d_scalePos != None) + { +// p.fillRect(rect(), palette().window()); + p.setRenderHint(QPainter::Antialiasing, false); + d_scale.draw(&p, palette(), value()); + } +} + +void Slider::adjustSize(const QSize& s) +{ + const QFontMetrics fm = fontMetrics(); + const int sliderWidth = d_thumbWidth; + // reposition slider + if(d_orient == Qt::Horizontal) + { + switch(d_scalePos) { - QPainter p(this); + case Top: + d_sliderRect.setRect(this->rect().x() + d_xMargin, + this->rect().y() + s.height() - 1 + - d_yMargin - sliderWidth, + s.width() - 2 * d_xMargin, + sliderWidth); + d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, + d_sliderRect.y() - d_scaleDist, + d_sliderRect.width() - d_thumbLength, + ScaleDraw::Top); + break; + + case Bottom: + d_sliderRect.setRect(this->rect().x() + d_xMargin, + this->rect().y() + d_yMargin, + s.width() - 2*d_xMargin, + sliderWidth); + d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, + d_sliderRect.y() + d_sliderRect.height() + d_scaleDist, + d_sliderRect.width() - d_thumbLength, + ScaleDraw::Bottom); + break; + + case InsideHorizontal: + d_sliderRect.setRect(this->rect().x() + d_xMargin, + this->rect().y() + s.height() - 1 + - d_yMargin - sliderWidth, + s.width() - 2 * d_xMargin, + sliderWidth); + d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, + //d_sliderRect.y() - d_scaleDist, + this->rect().y() + d_yMargin + d_scale.maxHeight(fm) + d_scaleDist, + d_sliderRect.width() - d_thumbLength, + ScaleDraw::InsideHorizontal); + break; + + default: + d_sliderRect.setRect(this->rect().x(), this->rect().x(), + s.width(), s.height()); + break; + } + } + else // d_orient == Qt::Vertical + { + switch(d_scalePos) + { + case Left: + d_sliderRect.setRect(this->rect().x() + s.width() + - sliderWidth - 1 - d_xMargin, + this->rect().y() + d_yMargin, + sliderWidth, + s.height() - 2 * d_yMargin); + d_scale.setGeometry(d_sliderRect.x() - d_scaleDist, + d_sliderRect.y() + d_thumbHalf, + s.height() - d_thumbLength, + ScaleDraw::Left); + break; + + case Right: + d_sliderRect.setRect(this->rect().x() + d_xMargin, + this->rect().y() + d_yMargin, + sliderWidth, + s.height() - 2* d_yMargin); + d_scale.setGeometry(this->rect().x() + d_sliderRect.width() + + d_scaleDist, + d_sliderRect.y() + d_thumbHalf, + s.height() - d_thumbLength, + ScaleDraw::Right); + break; - /* Scale is not supported - if (p.begin(this)) { - if (d_scalePos != None) { - p.fillRect(rect(), palette().window()); - d_scale.draw(&p); - } - drawSlider(&p, d_sliderRect); - } - p.end(); - */ - drawSlider(&p, d_sliderRect); + case InsideVertical: + { +// d_sliderRect.setRect(this->rect().x() + s.width() +// - sliderWidth - 1 - d_xMargin, +// d_sliderRect.setRect(this->rect().x() + d_xMargin, +// d_sliderRect.setRect(this->rect().x() + d_xMargin + d_scale.maxLabelWidth(fm, false) - sliderWidth, + const int mxlw = d_scale.maxLabelWidth(fm, false); + const int sclw = d_scale.scaleWidth(); + const int sldw = mxlw > sliderWidth ? sliderWidth : mxlw; + const int sldoffs = mxlw > sliderWidth ? ((mxlw - sldw) / 2) : 0; + const int fh = fm.ascent() + 2; + const int fh2 = fh / 2; + const int margin = d_thumbLength > fh ? d_thumbLength : fh; + const int margin2 = d_thumbHalf > fh2 ? d_thumbHalf : fh2; + const int sldymargin = fh > d_thumbLength ? fh - d_thumbLength : 0; + const int sldymargin2 = fh2 > d_thumbHalf ? fh2 - d_thumbHalf : 0; + +// d_sliderRect.setRect(this->rect().x() + (s.width() - 1) - sliderWidth - sclw + sldoffs, // - d_xMargin, + d_sliderRect.setRect(this->rect().x() + s.width() - sliderWidth - sclw + sldoffs, // - d_xMargin, +// this->rect().y() + d_yMargin, + this->rect().y() + d_yMargin + sldymargin2, + sliderWidth, +// s.height() - 2 * d_yMargin); +// s.height() - margin - 2 * d_yMargin); + s.height() - sldymargin - 2 * d_yMargin); + + //d_scale.setGeometry(d_sliderRect.x() - d_scaleDist, +// d_scale.setGeometry(this->rect().x() + d_xMargin + d_scale.maxWidth(fm, false) + d_scaleDist, + d_scale.setGeometry(this->rect().x() + d_xMargin + mxlw + sclw + d_scaleDist, +// d_sliderRect.y() + d_thumbHalf, +// d_sliderRect.y(), + this->rect().y() + d_yMargin + margin2, +// s.height() - d_thumbLength, +// s.height() - margin, +// d_sliderRect.height(), + s.height() - margin - 2 * d_yMargin, + ScaleDraw::InsideVertical); + } + break; + + default: + d_sliderRect.setRect(this->rect().x(), this->rect().x(), + s.width(), s.height()); + break; + } + } + + adjustScale(); +} + +void Slider::adjustScale() +{ + const double range = maxValue() - minValue(); + if(range == 0.0) + return; + + int maxMaj = 5; + int maxMin = 3; + double mstep = scaleStep(); + + QFontMetrics fm = fontMetrics(); + if(d_orient == Qt::Horizontal) + { + int unit_w = fm.width("888.8888"); + if(unit_w == 0) + unit_w = 20; + + if(hasUserScale()) + { + if(d_sliderRect.width() != 0) + { + const int fact = (int)(3.0 * range / (double)(d_sliderRect.width())) + 1; + mstep *= fact; } + } + else + { + maxMaj = (int)((double)(d_sliderRect.width()) / (1.5 * ((double)unit_w))); + if(maxMaj < 1) + maxMaj = 1; + if(maxMaj > 5) + maxMaj = 5; + } + maxMin = (int)((double)(d_sliderRect.width()) / (1.5 * ((double)unit_w))); + if(maxMin < 1) + maxMin = 1; + if(maxMin > 5) + maxMin = 5; + } + else + { + int unit_h = fm.height(); + if(unit_h == 0) + unit_h = 20; + + if(hasUserScale()) + { + if(d_sliderRect.height() != 0) + { + const int fact = (int)(3.0 * range / (double)(d_sliderRect.height())) + 1; + mstep *= fact; + } + } + else + { + maxMaj = (int)((double)(d_sliderRect.height()) / (1.5 * ((double)unit_h))); + if(maxMaj < 1) + maxMaj = 1; + if(maxMaj > 5) + maxMaj = 5; + } + maxMin = (int)((double)(d_sliderRect.height()) / (1.5 * ((double)unit_h))); + if(maxMin < 1) + maxMin = 1; + if(maxMin > 5) + maxMin = 5; + } + + DEBUG_SLIDER(stderr, "Slider::adjustScale: maxMaj:%d maxMin:%d scaleStep:%f\n", maxMaj, maxMin, mstep); + d_maxMajor = maxMaj; + d_maxMinor = maxMin; + if(hasUserScale()) + d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor, mstep, log()); + else + d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor, log()); +// update(); + updateGeometry(); +} //------------------------------------------------------------ //.F Slider::resizeEvent @@ -581,85 +927,49 @@ void Slider::resizeEvent(QResizeEvent *e) { SliderBase::resizeEvent(e); - d_resized = TRUE; - QSize s = e->size(); - /* Scale is not supported - int sliderWidth = d_thumbWidth; + adjustSize(e->size()); +} - // reposition slider - if(d_orient == Qt::Horizontal) - { - switch(d_scalePos) - { - case Top: - - d_sliderRect.setRect(this->rect().x() + d_xMargin, - this->rect().y() + s.height() - 1 - - d_yMargin - sliderWidth, - s.width() - 2 * d_xMargin, - sliderWidth); - d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, - d_sliderRect.y() - d_scaleDist, - d_sliderRect.width() - d_thumbLength, - ScaleDraw::Top); - - break; - - case Bottom: - - d_sliderRect.setRect(this->rect().x() + d_xMargin, - this->rect().y() + d_yMargin, - s.width() - 2*d_xMargin, - sliderWidth); - d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, - d_sliderRect.y() + d_sliderRect.height() + d_scaleDist, - d_sliderRect.width() - d_thumbLength, - ScaleDraw::Bottom); - - break; - - default: - d_sliderRect.setRect(this->rect().x(), this->rect().x(), - s.width(), s.height()); - break; - } - } - else // d_orient == Qt::Vertical - { - switch(d_scalePos) - { - case Left: - d_sliderRect.setRect(this->rect().x() + s.width() - - sliderWidth - 1 - d_xMargin, - this->rect().y() + d_yMargin, - sliderWidth, - s.height() - 2 * d_yMargin); - d_scale.setGeometry(d_sliderRect.x() - d_scaleDist, - d_sliderRect.y() + d_thumbHalf, - s.height() - d_thumbLength, - ScaleDraw::Left); - - break; - case Right: - d_sliderRect.setRect(this->rect().x() + d_xMargin, - this->rect().y() + d_yMargin, - sliderWidth, - s.height() - 2* d_yMargin); - d_scale.setGeometry(this->rect().x() + d_sliderRect.width() - + d_scaleDist, - d_sliderRect.y() + d_thumbHalf, - s.height() - d_thumbLength, - ScaleDraw::Right); - break; - default: - d_sliderRect.setRect(this->rect().x(), this->rect().x(), - s.width(), s.height()); - break; - } - } - */ - d_sliderRect.setRect(this->rect().x(), this->rect().y(), - s.width(), s.height()); +void Slider::setScale(double vmin, double vmax, int logarithmic) +{ + ScaleIf::setScale(vmin, vmax, logarithmic); + // Must adjust the scale. + adjustScale(); +} + +void Slider::setScale(double vmin, double vmax, double step, int logarithmic) +{ + ScaleIf::setScale(vmin, vmax, step, logarithmic); + // Must adjust the scale. + adjustScale(); +} + +void Slider::setScale(const ScaleDiv &s) +{ + ScaleIf::setScale(s); + // Must adjust the scale. + adjustScale(); +} + +void Slider::setScaleMaxMajor(int ticks) +{ + ScaleIf::setScaleMaxMajor(ticks); + // Must adjust the scale. + adjustScale(); +} + +void Slider::setScaleMaxMinor(int ticks) +{ + ScaleIf::setScaleMaxMinor(ticks); + // Must adjust the scale. + adjustScale(); +} + +void Slider::setScaleBackBone(bool v) +{ + ScaleIf::setScaleBackBone(v); + // Must adjust the scale. + adjustScale(); } //------------------------------------------------------------ @@ -675,6 +985,19 @@ void Slider::valueChange() { update(); + + // HACK + // In direct mode let the inherited classes (this) call these in their valueChange() methods, + // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. + // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. + // Hack: Since valueChange() is NOT called if nothing changed, in that case these are called for us by the SliderBase. + if(d_scrollMode == ScrDirect) + { + processSliderPressed(id()); + emit sliderPressed(value(), id()); + } + + // Emits valueChanged if tracking enabled. SliderBase::valueChange(); } @@ -694,7 +1017,8 @@ if (!hasUserScale()) d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); SliderBase::rangeChange(); - repaint(); +// repaint(); + update(); } //------------------------------------------------------------ @@ -713,7 +1037,7 @@ void Slider::setMargins(int hor, int vert) { d_xMargin = MusECore::qwtMax(0, hor); - d_yMargin = MusECore::qwtMin(0, vert); + d_yMargin = MusECore::qwtMax(0, vert); resize(this->size()); } @@ -732,40 +1056,88 @@ QSize Slider::sizeHint() const { - /* Scale is not supported int w = 40; int h = 40; - QPainter p; + const QFontMetrics fm = fontMetrics(); int msWidth = 0, msHeight = 0; - if (d_scalePos != None) { - if (p.begin(this)) { - msWidth = d_scale.maxWidth(&p, FALSE); - msHeight = d_scale.maxHeight(&p); - } - p.end(); + if (d_scalePos != None) + { + msWidth = d_scale.maxWidth(fm, false); + msHeight = d_scale.maxHeight(fm); - switch(d_orient) { - case Qt::Vertical: - w = 2*d_xMargin + d_thumbWidth + msWidth + d_scaleDist + 2; - break; - case Qt::Horizontal: - h = 2*d_yMargin + d_thumbWidth + msHeight + d_scaleDist; - break; - } + switch(d_orient) + { + case Qt::Vertical: + { + h = vertical_hint; + const int smw = msWidth + d_scaleDist; + switch(d_scalePos) + { + case Left: + case Right: + w = 2*d_xMargin + d_thumbWidth + smw + 2; + break; + + case InsideVertical: + { + const int aw = smw > d_thumbWidth ? smw : d_thumbWidth; + w = 2*d_xMargin + aw + 2; + } + break; + + case Top: + case Bottom: + case InsideHorizontal: + case None: + break; } - else { // no scale - switch(d_orient) { - case Qt::Vertical: - w = 16; - break; - case Qt::Horizontal: - h = 16; - break; - } + } + break; + + case Qt::Horizontal: + { + w = horizontal_hint; + const int smh = msHeight + d_scaleDist; + switch(d_scalePos) + { + case Top: + case Bottom: + h = 2*d_yMargin + d_thumbWidth + smh; + break; + + case InsideHorizontal: + { + const int ah = smh > d_thumbWidth ? smh : d_thumbWidth; + h = 2*d_yMargin + ah; + } + break; + + case Left: + case Right: + case InsideVertical: + case None: + break; } - */ - return QSize(horizontal_hint, vertical_hint); + } + break; + } + } + else + { // no scale + switch(d_orient) + { + case Qt::Vertical: + w = 16; + h = vertical_hint; + break; + case Qt::Horizontal: + h = 16; + w = horizontal_hint; + break; + } + } + return QSize(w, h); } //--------------------------------------------------------- @@ -775,21 +1147,44 @@ void Slider::setOrientation(Qt::Orientation o) { d_orient = o; - /* Scale is not supported ScaleDraw::OrientationX so = ScaleDraw::Bottom; switch(d_orient) { case Qt::Vertical: - if (d_scalePos == Right) - so = ScaleDraw::Right; - else - so = ScaleDraw::Left; - break; + switch(d_scalePos) + { + case Right: + so = ScaleDraw::Right; + break; + case Left: + so = ScaleDraw::Left; + break; + case InsideVertical: + so = ScaleDraw::InsideVertical; + break; + case Bottom: + case Top: + case InsideHorizontal: + case None: + break; + } case Qt::Horizontal: - if (d_scalePos == Bottom) - so = ScaleDraw::Bottom; - else - so = ScaleDraw::Top; - break; + switch(d_scalePos) + { + case Bottom: + so = ScaleDraw::Bottom; + break; + case Top: + so = ScaleDraw::Top; + break; + case InsideHorizontal: + so = ScaleDraw::InsideHorizontal; + break; + case Right: + case Left: + case InsideVertical: + case None: + break; + } } d_scale.setGeometry(0, 0, 40, so); @@ -800,18 +1195,6 @@ QRect r = geometry(); setGeometry(r.x(), r.y(), r.height(), r.width()); update(); - */ - - switch(d_orient) { - case Qt::Vertical: - horizontal_hint = 16; - vertical_hint = 64; - break; - case Qt::Horizontal: - horizontal_hint = 64; - vertical_hint = 16; - break; - } } Qt::Orientation Slider::orientation() const diff -Nru muse-2.1.2/muse/widgets/slider.h muse-3.0.2+ds1/muse/widgets/slider.h --- muse-2.1.2/muse/widgets/slider.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/slider.h 2017-12-04 21:01:19.000000000 +0000 @@ -6,6 +6,7 @@ // Copyright (C) 1997 Josef Wilgen // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) +// (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -39,14 +40,21 @@ class Slider : public SliderBase, public ScaleIf { Q_OBJECT + Q_PROPERTY( double lineStep READ lineStep WRITE setLineStep ) + Q_PROPERTY( double pageStep READ pageStep WRITE setPageStep ) + Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation ) + public: - enum ScalePos { None, Left, Right, Top, Bottom }; + enum ScalePos { None, Left, Right, Top, Bottom, InsideHorizontal, InsideVertical }; - private: - Q_PROPERTY( double lineStep READ lineStep WRITE setLineStep ) - Q_PROPERTY( double pageStep READ pageStep WRITE setPageStep ) - Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation ) + private: + Qt::Orientation d_orient; + ScalePos d_scalePos; + int d_grooveWidth; + QColor d_fillColor; + bool d_fillThumb; + bool d_fillEmptySide; QRect d_sliderRect; @@ -58,14 +66,9 @@ int d_yMargin; int d_mMargin; - QColor d_fillColor; - - int d_resized; bool d_autoResize; double d_scaleStep; - Qt::Orientation d_orient; - ScalePos d_scalePos; int d_bgStyle; int markerPos; @@ -76,9 +79,22 @@ void drawVsBgSlot(QPainter *, const QRect&, const QRect&,const QBrush&); protected: + virtual void drawThumb (QPainter *p, const QRect &r); virtual void drawSlider (QPainter *p, const QRect &r); + + // Determine the value corresponding to a specified mouse location. + // If borderless mouse is enabled p is a delta value not absolute, so can be negative. double getValue(const QPoint &p); - void getScrollMode( QPoint &p, const Qt::MouseButton &button, int &scrollMode, int &direction); + // Determine the value corresponding to a specified mouse movement. + double moveValue(const QPoint& /*deltaP*/, bool /*fineMode*/ = false); + // Determine scrolling mode and direction. + void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction); + + // Setup all slider and scale rectangles. + void adjustSize(const QSize& s); + // Adjust scale so marks are not too close together. + void adjustScale(); + virtual void resizeEvent(QResizeEvent *e); virtual void paintEvent (QPaintEvent *e); void valueChange(); @@ -87,10 +103,13 @@ void fontChange(const QFont &oldFont); public: + Slider(QWidget *parent, const char *name = 0, Qt::Orientation orient = Qt::Vertical, ScalePos scalePos = None, - QColor fillColor = QColor(100, 100, 255)); + int grooveWidth = 8, + QColor fillColor = QColor(), + ScaleDraw::TextHighlightMode textHighlightMode = ScaleDraw::TextHighlightNone); ~Slider(); void setThumbLength(int l); @@ -99,6 +118,13 @@ void setOrientation(Qt::Orientation o); Qt::Orientation orientation() const; + void setScale (double vmin, double vmax, int logarithmic = 0); + void setScale (double vmin, double vmax, double step, int logarithmic = 0); + void setScale(const ScaleDiv &s); + void setScaleMaxMajor( int ticks); + void setScaleMaxMinor( int ticks); + void setScaleBackBone(bool v); + double lineStep() const; double pageStep() const; @@ -106,6 +132,18 @@ void setPageStep(double); void setMargins(int x, int y); + int grooveWidth() const { return d_grooveWidth; } + void setGrooveWidth(int w) { d_grooveWidth = w; update(); } + + QColor fillColor() const { return d_fillColor; } + void setFillColor(const QColor& color) { d_fillColor = color; update(); } + + bool fillThumb() const { return d_fillThumb; } + void setFillThumb(bool v) { d_fillThumb = v; update(); } + + bool fillEmptySide() const { return d_fillEmptySide; } + void setFillEmptySide(bool v) { d_fillEmptySide = v; update(); } + virtual QSize sizeHint() const; void setSizeHint(uint w, uint h); }; diff -Nru muse-2.1.2/muse/widgets/songpos_toolbar.cpp muse-3.0.2+ds1/muse/widgets/songpos_toolbar.cpp --- muse-2.1.2/muse/widgets/songpos_toolbar.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/songpos_toolbar.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -37,10 +37,10 @@ song_changed(0); } - void SongPosToolbarWidget::song_changed(MusECore::SongChangedFlags_t flags) + void SongPosToolbarWidget::song_changed(MusECore::SongChangedFlags_t /*flags*/) { - if(flags & SC_MIDI_CONTROLLER) // TODO: Filter more stuff out - return; +// if(flags == ) // TODO: Filter more stuff out +// return; if (width()!=0) setXMag(-(MusEGlobal::song->len()/width())); } diff -Nru muse-2.1.2/muse/widgets/spinboxFP.cpp muse-3.0.2+ds1/muse/widgets/spinboxFP.cpp --- muse-2.1.2/muse/widgets/spinboxFP.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/spinboxFP.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -23,7 +23,7 @@ #include #include -//#include +//#include //#include //#include @@ -156,7 +156,7 @@ //QString qs = cleanText(); if (_decimals) { //const char* s = qs.toLatin1(); - //const char* s = cleanText().toAscii().data(); + //const char* s = cleanText().toLatin1().data(); //int a, b; bool ok; diff -Nru muse-2.1.2/muse/widgets/splitter.cpp muse-3.0.2+ds1/muse/widgets/splitter.cpp --- muse-2.1.2/muse/widgets/splitter.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/splitter.cpp 2018-01-06 20:31:35.000000000 +0000 @@ -32,10 +32,19 @@ // Splitter //--------------------------------------------------------- +Splitter::Splitter(QWidget* parent, const char* name) + : QSplitter(parent) + { + if(name) + setObjectName(name); + setOpaqueResize(true); + } + Splitter::Splitter(Qt::Orientation o, QWidget* parent, const char* name) : QSplitter(o, parent) { - setObjectName(name); + if(name) + setObjectName(name); setOpaqueResize(true); } @@ -82,6 +91,17 @@ int val = (*it).toInt(); vl.append(val); } + + // fix for allowing the arranger split to work nicely with old songs + // that have only two splitters, we add a first splitter with the + // standard strip width 53 pixels + if (objectName() == "split") { + if (vl.size() < 3) { + vl.prepend(53); + } + } + // + // } break; case MusECore::Xml::TagEnd: diff -Nru muse-2.1.2/muse/widgets/splitter.h muse-3.0.2+ds1/muse/widgets/splitter.h --- muse-2.1.2/muse/widgets/splitter.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/splitter.h 2017-12-04 21:01:19.000000000 +0000 @@ -39,9 +39,12 @@ Q_OBJECT public: - Splitter(Qt::Orientation o, QWidget* parent, const char* name); + Splitter(QWidget* parent, const char* name = 0); + Splitter(Qt::Orientation o, QWidget* parent, const char* name = 0); void writeStatus(int level, MusECore::Xml&); void readStatus(MusECore::Xml&); + + void setPosition(int idx, int pos) { moveSplitter(pos, idx); } }; } diff -Nru muse-2.1.2/muse/widgets/synthconfigbase.ui muse-3.0.2+ds1/muse/widgets/synthconfigbase.ui --- muse-2.1.2/muse/widgets/synthconfigbase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/synthconfigbase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -6,204 +6,265 @@ 0 0 - 820 - 475 + 773 + 448
    - Midi Port and Soft Synth Configuration + Configure midi devices, midi ports, and synthesizers - - - - Midi connections + + + + Qt::Vertical - - - - - 18 - - - - - - - - - - Soft Synthesizer - - - - - - 6 - - - 0 - - - + + + Available soft synthesizers + + + Qt::AlignCenter + + + + + + list of available software synthesizers + + + true + + - Add Instance + File + + + + Type + + + + + Inst + + + + + Name + + + + + Version + + + + + Description + + + + + + + + + Qt::Horizontal + + + + + 0 + 0 + + + + Useable devices + + + Qt::AlignCenter + + + + + + + 0 + 0 + + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + + 18 + + + false + - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - + + + + + Add: + + + + + + + Synth + + + + + + + true + + + ALSA + + + true + + + false + + + + + + + JACK + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Rename + + + + + + + Remove + + + + - - - - - list of available software synthesizers - - - true - - - - File - - - - - Type - - - - - Inst - - - - - Name - - - - - Version - - - - - Description - - - - - - - - - - - Instances - - - - - - true - - - - Name - - - - - Type - - - - - Midi Port - - - - - - + + + + + 0 + 0 + + + + Device port assignments + + + Qt::AlignCenter + + - - - Remove Instance + + + + 0 + 0 + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + 18 + - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 113 - 20 - - - + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Apply + + + + + + + &OK + + + + - - -
    -
    - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - &Apply - - - - - - &OK - - - - +
    +
    + + + MusEGui::Splitter + QSplitter +
    splitter.h
    + 1 +
    +
    diff -Nru muse-2.1.2/muse/widgets/text_edit.cpp muse-3.0.2+ds1/muse/widgets/text_edit.cpp --- muse-2.1.2/muse/widgets/text_edit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/text_edit.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,72 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// text_edit.cpp +// (C) Copyright 2017 Tim E. Real (terminator356 on sourceforge) +// +// 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 +#include +#include +#include + +#include "text_edit.h" + +// For debugging output: Uncomment the fprintf section. +#define DEBUG_TEXT_EDIT(dev, format, args...) // fprintf(dev, format, ##args); + +namespace MusEGui { + +TextEdit::TextEdit(QWidget* parent, const char* name) + : QPlainTextEdit(parent) +{ + setObjectName(name); +} + +TextEdit::TextEdit(const QString& txt, QWidget* parent, const char* name) + : QPlainTextEdit(txt, parent) +{ + setObjectName(name); +} + +void TextEdit::mouseDoubleClickEvent(QMouseEvent* ev) +{ + if(!hasFocus()) + { +// ev->accept(); + setFocus(); +// return; + } + ev->ignore(); + QPlainTextEdit::mouseDoubleClickEvent(ev); +} + +QSize TextEdit::sizeHint() const +{ + const int w = QPlainTextEdit::sizeHint().width(); + const int lines = document()->lineCount(); + const int fh = fontMetrics().lineSpacing(); + const int marg = contentsMargins().bottom() + contentsMargins().top() + + //viewportMargins().bottom() + viewportMargins().top() + // Only in Qt 5.5 + viewport()->contentsMargins().bottom() + viewport()->contentsMargins().top(); + const int h = fh * lines + marg + 6; // Extra for some kind of voodoo added deep down, can't find it. + return QSize(w, h); +} + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/text_edit.h muse-3.0.2+ds1/muse/widgets/text_edit.h --- muse-2.1.2/muse/widgets/text_edit.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/text_edit.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,65 @@ +//========================================================= +// MusE +// Linux Music Editor +// +// text_edit.h +// (C) Copyright 2017 Tim E. Real (terminator356 on sourceforge) +// +// 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 __TEXT_EDIT_H__ +#define __TEXT_EDIT_H__ + +#include + +class QMouseEvent; + +namespace MusEGui { + +//--------------------------------------------------------- +// TextEdit +//--------------------------------------------------------- + +class TextEdit : public QPlainTextEdit +{ + Q_OBJECT + + private: + bool _doubleClickFocus; + + protected: + + virtual void mouseDoubleClickEvent(QMouseEvent*); + + public: + TextEdit(QWidget* parent = 0, const char* name = 0); + TextEdit(const QString& txt, QWidget* parent = 0, const char* name = 0); + + bool doubleClickFocus() const { return _doubleClickFocus; } + void setDoubleClickFocus(bool v) { _doubleClickFocus = v; } + + QString text() const { return toPlainText(); } + virtual void setText(const QString& s) { setPlainText(s); } + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const { return sizeHint(); } +}; + + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/thinslider.cpp muse-3.0.2+ds1/muse/widgets/thinslider.cpp --- muse-2.1.2/muse/widgets/thinslider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/thinslider.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,910 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: ./muse/widgets/slider.cpp $ +// +// Copyright (C) 1999-2011 by Werner Schweer and others +// (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.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 +#include "mmath.h" + +#include +#include + +#include "utils.h" +#include "thinslider.h" + +namespace MusEGui { + +//------------------------------------------------------------- +// ThinSlider - The ThinSlider Widget +// +// Slider is a slider widget which operates on an interval +// of type double. Slider supports different layouts as +// well as a scale. +//------------------------------------------------------------ + +//------------------------------------------------------------ +//.F ThinSlider::Slider +// +// Constructor +// +//.u Syntax: +//.f ThinSlider::Slider(QWidget *parent, const char *name, Orientation orient = Horizontal, ScalePos scalePos = None, int bgStyle = BgTrough) +// +//.u Parameters +//.p +// QWidget *parent -- parent widget +// const char *name -- The Widget's name. Default = 0. +// Orientation Orient -- Orientation of the slider. Can be ThinSlider::Horizontal +// or ThinSlider::Vertical. +// Defaults to Horizontal. +// ScalePos scalePos -- Position of the scale. Can be ThinSlider::None, +// ThinSlider::Left, ThinSlider::Right, ThinSlider::Top, +// or ThinSlider::Bottom. Defaults to ThinSlider::None. !!! CURRENTLY only ThinSlider::None supported - oget 20110913 +// QColor fillcolor -- the color used to fill in the full side +// of the Slider +//------------------------------------------------------------ + +ThinSlider::ThinSlider(QWidget *parent, const char *name, + Qt::Orientation orient, ScalePos scalePos, QColor fillColor) + : SliderBase(parent,name) + { + d_thumbLength = 16; + d_thumbHalf = 8; + d_thumbWidth = 16; + + + d_scaleDist = 4; + d_scaleStep = 0.0; + d_scalePos = scalePos; + d_xMargin = 0; + d_yMargin = 0; + d_mMargin = 1; + + d_fillColor = fillColor; + + d_sliderRect.setRect(0, 0, 8, 8); + setOrientation(orient); + } + +//------------------------------------------------------------ +//.F ThinSlider::setSizeHint +//------------------------------------------------------------ + +void ThinSlider::setSizeHint(uint w, uint h) + { + horizontal_hint = w; + vertical_hint = h; + } + +//------------------------------------------------------------ +//.F ThinSlider::~Slider +// Destructor +//.u Syntax +//.f ThinSlider::~Slider() +//------------------------------------------------------------ + +ThinSlider::~ThinSlider() + { + } + +//---------------------------------------------------- +// +//.F ThinSlider::setThumbLength +// +// Set the slider's thumb length +// +//.u Syntax +// void ThinSlider::setThumbLength(int l) +// +//.u Parameters +//.p int l -- new length +// +//----------------------------------------------------- +void ThinSlider::setThumbLength(int l) +{ + d_thumbLength = MusECore::qwtMax(l,8); + d_thumbHalf = d_thumbLength / 2; + resize(size()); +} + +//------------------------------------------------------------ +// +//.F ThinSlider::setThumbWidth +// Change the width of the thumb +// +//.u Syntax +//.p void ThinSlider::setThumbWidth(int w) +// +//.u Parameters +//.p int w -- new width +// +//------------------------------------------------------------ +void ThinSlider::setThumbWidth(int w) +{ + d_thumbWidth = MusECore::qwtMax(w,4); + resize(size()); +} + + +//------------------------------------------------------------ +//.- +//.F ThinSlider::scaleChange +// Notify changed scale +// +//.u Syntax +//.f void ThinSlider::scaleChange() +// +//.u Description +// Called by QwtScaledWidget +// +//------------------------------------------------------------ +void ThinSlider::scaleChange() +{ + if (!hasUserScale()) + d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); + update(); +} + + +//------------------------------------------------------------ +//.- +//.F ThinSlider::fontChange +// Notify change in font +// +//.u Syntax +//.f ThinSlider::fontChange(const QFont &oldFont) +// +//------------------------------------------------------------ +void ThinSlider::fontChange(const QFont & /*oldFont*/) +{ + repaint(); +} + +//------------------------------------------------------------ +// drawSlider +// Draw the slider into the specified rectangle. +//------------------------------------------------------------ + +void ThinSlider::drawSlider(QPainter *p, const QRect &r) +{ + p->setRenderHint(QPainter::Antialiasing); + + const QPalette& pal = palette(); + QBrush brBack(pal.window()); + QBrush brMid(pal.mid()); + QBrush brDark(pal.dark()); + + QRect cr; + + int ipos,dist1; + double rpos; + + int xrad = 4; + int yrad = 4; + + // for the empty side + QColor e_mask_edge = pal.mid().color(); + QColor e_mask_center = pal.midlight().color(); + int e_alpha = 215; + e_mask_edge.setAlpha(e_alpha); + e_mask_center.setAlpha(e_alpha); + + QLinearGradient e_mask; + e_mask.setColorAt(0, e_mask_edge); + e_mask.setColorAt(0.5, e_mask_center); + e_mask.setColorAt(1, e_mask_edge); + + // for the full side + rpos = (value() - minValue()) / (maxValue() - minValue()); + + int f_brightness = 155 * rpos + 100; + int f_alpha; + int f_edge; + if (pal.currentColorGroup() == QPalette::Disabled) + { + f_alpha = 185; + f_edge = 100; + } + else + { + f_alpha = 127; + f_edge = 0; + } + + QColor f_mask_center = QColor(f_brightness, f_brightness, f_brightness, f_alpha); + QColor f_mask_edge = QColor(f_edge, f_edge, f_edge, f_alpha); + QLinearGradient f_mask; + + f_mask.setColorAt(0, f_mask_edge); + f_mask.setColorAt(0.5, f_mask_center); + f_mask.setColorAt(1, f_mask_edge); + + // for the thumb + QLinearGradient thumbGrad; + QColor thumb_edge = pal.dark().color(); + QColor thumb_center = pal.midlight().color(); + + thumbGrad.setColorAt(0, thumb_edge); + thumbGrad.setColorAt(0.5, thumb_center); + thumbGrad.setColorAt(1, thumb_edge); + + + if (d_orient == Qt::Horizontal) + { + + cr.setRect(r.x(), + r.y() + d_mMargin, + r.width(), + r.height() - 2*d_mMargin); + + + // + // Draw background + // + QPainterPath bg_rect = MusECore::roundedPath(cr, + xrad, yrad, + (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); + + p->fillPath(bg_rect, d_fillColor); + + dist1 = int(double(cr.width() - d_thumbLength) * rpos); + ipos = cr.x() + dist1; + markerPos = ipos + d_thumbHalf; + + + // + // Draw empty right side + // + + e_mask.setStart(QPointF(0, cr.y())); + e_mask.setFinalStop(QPointF(0, cr.y() + cr.height())); + + QPainterPath e_rect = MusECore::roundedPath(ipos + d_thumbLength, cr.y(), + cr.width() - d_thumbLength - dist1, cr.height(), + xrad, yrad, (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight) ); + + p->fillPath(e_rect, QBrush(e_mask)); + + + // + // Draw full left side + // + + f_mask.setStart(QPointF(0, cr.y())); + f_mask.setFinalStop(QPointF(0, cr.y() + cr.height())); + + QPainterPath f_rect = MusECore::roundedPath(cr.x(), cr.y(), + ipos + 1, cr.height(), + xrad, yrad, + (MusECore::Corner) (MusECore::LowerLeft | MusECore::UpperLeft) ); + + p->fillPath(f_rect, QBrush(f_mask)); + + + // + // Draw thumb + // + + QPainterPath thumb_rect = MusECore::roundedPath(ipos, r.y(), + d_thumbLength, r.height(), + 2, 2, + (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); + + thumbGrad.setStart(QPointF(0, cr.y())); + thumbGrad.setFinalStop(QPointF(0, cr.y() + cr.height())); + + + p->fillPath(thumb_rect, QBrush(thumbGrad)); + + // center line + p->fillRect(ipos + d_thumbHalf, cr.y(), 1, cr.height(), pal.dark().color()); + + + } + else // (d_orient == Qt::Vertical) + { + + cr.setRect(r.x() + d_mMargin, + r.y(), + r.width() - 2*d_mMargin, + r.height()); + + + // + // Draw background + // + QPainterPath bg_rect = MusECore::roundedPath(cr, + xrad, yrad, + (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); + + p->fillPath(bg_rect, d_fillColor); + + dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); + ipos = cr.y() + dist1; + markerPos = ipos + d_thumbHalf; + + + // + // Draw empty upper filling + // + + e_mask.setStart(QPointF(cr.x(), 0)); + e_mask.setFinalStop(QPointF(cr.x() + cr.width(), 0)); + + QPainterPath e_rect = MusECore::roundedPath(cr.x(), cr.y(), + cr.width(), ipos + 1, + xrad, yrad, + (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight) ); + + p->fillPath(e_rect, QBrush(e_mask)); + + + // + // Draw lower filling mask + // + + f_mask.setStart(QPointF(cr.x(), 0)); + f_mask.setFinalStop(QPointF(cr.x() + cr.width(), 0)); + + QPainterPath f_rect = MusECore::roundedPath(cr.x(), ipos + d_thumbLength, + cr.width(), cr.height() - d_thumbLength - dist1, + xrad, yrad, (MusECore::Corner) (MusECore::LowerLeft | MusECore::LowerRight) ); + + p->fillPath(f_rect, QBrush(f_mask)); + + + // + // Draw thumb + // + + QPainterPath thumb_rect = MusECore::roundedPath(r.x(), ipos, + r.width(), d_thumbLength, + 2, 2, + (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); + + thumbGrad.setStart(QPointF(cr.x(), 0)); + thumbGrad.setFinalStop(QPointF(cr.x() + cr.width(), 0)); + + + p->fillPath(thumb_rect, QBrush(thumbGrad)); + + // center line + p->fillRect(cr.x(), ipos + d_thumbHalf, cr.width(), 1, pal.dark().color()); + + } + +} + +//------------------------------------------------------------ +//.- +//.F ThinSlider::getValue +// Determine the value corresponding to a specified +// mouse location. +// +//.u Syntax +//.f double ThinSlider::getValue(const QPoint &p) +// +//.u Parameters +//.p const QPoint &p -- +// +//.u Description +// Called by SliderBase +//------------------------------------------------------------ +double ThinSlider::getValue( const QPoint &p) +{ + double rv; + int pos; + QRect r = d_sliderRect; + + if (d_orient == Qt::Horizontal) + { + + if (r.width() <= d_thumbLength) + { + rv = 0.5 * (minValue() + maxValue()); + } + else + { + pos = p.x() - r.x() - d_thumbHalf; + rv = minValue() + + rint( (maxValue() - minValue()) * double(pos) + / double(r.width() - d_thumbLength) + / step() ) * step(); + } + + } + else + { + if (r.height() <= d_thumbLength) + { + rv = 0.5 * (minValue() + maxValue()); + } + else + { + pos = p.y() - r.y() - d_thumbHalf; + rv = minValue() + + rint( (maxValue() - minValue()) * + (1.0 - double(pos) + / double(r.height() - d_thumbLength)) + / step() ) * step(); + } + + } + + return(rv); +} + + +//------------------------------------------------------------ +// +//.F ThinSlider::moveValue +// Determine the value corresponding to a specified mouse movement. +// +//.u Syntax +//.f void Slider::moveValue(const QPoint &deltaP, bool fineMode) +// +//.u Parameters +//.p const QPoint &deltaP -- Change in position +//.p bool fineMode -- Fine mode if true, coarse mode if false. +// +//.u Description +// Called by SliderBase +// Coarse mode (the normal mode) maps pixels to values depending on range and width, +// such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. +//------------------------------------------------------------ +double ThinSlider::moveValue(const QPoint &deltaP, bool fineMode) +{ + double rv; + const QRect r = d_sliderRect; + + const double val = value(ConvertNone); + + if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) + { + double newval; + if(d_orient == Qt::Horizontal) + newval = val + deltaP.x() * step(); + else + newval = val - deltaP.y() * step(); + d_valAccum = newval; // Reset. + return newval; + } + + const double min = minValue(ConvertNone); + const double max = maxValue(ConvertNone); + const double drange = max - min; + + if(d_orient == Qt::Horizontal) + { + if(r.width() <= d_thumbLength) + rv = 0.5 * (min + max); + else + { + const double dpos = double(deltaP.x()); + const double dwidth = double(r.width() - d_thumbLength); + const double dval_diff = (drange * dpos) / dwidth; + d_valAccum += dval_diff; + rv = rint(d_valAccum / step()) * step(); + } + } + else + { + if(r.height() <= d_thumbLength) + rv = 0.5 * (min + max); + else + { + const double dpos = double(-deltaP.y()); + const double dheight = double(r.height() - d_thumbLength); + const double dval_diff = (drange * dpos) / dheight; + d_valAccum += dval_diff; + rv = rint(d_valAccum / step()) * step(); + } + } + return(rv); +} + +//------------------------------------------------------------ +//.- +//.F ThinSlider::getScrollMode +// Determine scrolling mode and direction +// +//.u Syntax +//.f void ThinSlider::getScrollMode( const QPoint &p, int &scrollMode, int &direction ) +// +//.u Parameters +//.p const QPoint &p -- point +// +//.u Description +// Called by SliderBase +// +//------------------------------------------------------------ +void ThinSlider::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& /*modifiers*/, int &scrollMode, int &direction ) +{ + if(cursorHoming() && button == Qt::LeftButton) + { + if(d_sliderRect.contains(p)) + { + scrollMode = ScrMouse; + direction = 0; + + int mp = 0; + QRect cr; + QPoint cp; + int ipos,dist1; + double rpos; + + cr = d_sliderRect; + + rpos = (value() - minValue()) / (maxValue() - minValue()); + + if(d_orient == Qt::Horizontal) + { + dist1 = int(double(cr.width() - d_thumbLength) * rpos); + ipos = cr.x() + dist1; + mp = ipos + d_thumbHalf; + + p.setX(mp); + cp = mapToGlobal( QPoint(mp, p.y()) ); + } + else + { + dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); + ipos = cr.y() + dist1; + mp = ipos + d_thumbHalf; + p.setY(mp); + cp = mapToGlobal( QPoint(p.x(), mp) ); + } + cursor().setPos(cp.x(), cp.y()); + } + } + else + { + int currentPos; + if (d_orient == Qt::Horizontal) + currentPos = p.x(); + else + currentPos = p.y(); + + if (d_sliderRect.contains(p)) + { + if ((currentPos > markerPos - d_thumbHalf) + && (currentPos < markerPos + d_thumbHalf)) + { + scrollMode = ScrMouse; + direction = 0; + } + else + { + scrollMode = ScrPage; + if (((currentPos > markerPos) && (d_orient == Qt::Horizontal)) + || ((currentPos <= markerPos) && (d_orient != Qt::Horizontal))) + direction = 1; + else + direction = -1; + } + } + else + { + scrollMode = ScrNone; + direction = 0; + } + + } +} + +//------------------------------------------------------------ +//.F ThinSlider::paintEvent +// Qt paint event +// +//.u Syntax +//.f void ThinSlider::paintEvent(QPaintEvent *e) +//------------------------------------------------------------ + +void ThinSlider::paintEvent(QPaintEvent* ev) + { + QPainter p(this); + + /* Scale is not supported + if (p.begin(this)) { + if (d_scalePos != None) { + p.fillRect(rect(), palette().window()); + d_scale.draw(&p); + } + drawSlider(&p, d_sliderRect); + } + p.end(); + */ + drawSlider(&p, d_sliderRect); + p.setPen(Qt::red); + p.drawRect(ev->rect()); + } + +//------------------------------------------------------------ +//.F ThinSlider::resizeEvent +// Qt resize event +// +//.u Parameters +//.p QResizeEvent *e +// +//.u Syntax +//.f void ThinSlider::resizeEvent(QResizeEvent *e) +//------------------------------------------------------------ + +void ThinSlider::resizeEvent(QResizeEvent *e) +{ + SliderBase::resizeEvent(e); + d_resized = true; + QSize s = e->size(); + /* Scale is not supported + int sliderWidth = d_thumbWidth; + + // reposition slider + if(d_orient == Qt::Horizontal) + { + switch(d_scalePos) + { + case Top: + + d_sliderRect.setRect(this->rect().x() + d_xMargin, + this->rect().y() + s.height() - 1 + - d_yMargin - sliderWidth, + s.width() - 2 * d_xMargin, + sliderWidth); + d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, + d_sliderRect.y() - d_scaleDist, + d_sliderRect.width() - d_thumbLength, + ScaleDraw::Top); + + break; + + case Bottom: + + d_sliderRect.setRect(this->rect().x() + d_xMargin, + this->rect().y() + d_yMargin, + s.width() - 2*d_xMargin, + sliderWidth); + d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, + d_sliderRect.y() + d_sliderRect.height() + d_scaleDist, + d_sliderRect.width() - d_thumbLength, + ScaleDraw::Bottom); + + break; + + default: + d_sliderRect.setRect(this->rect().x(), this->rect().x(), + s.width(), s.height()); + break; + } + } + else // d_orient == Qt::Vertical + { + switch(d_scalePos) + { + case Left: + d_sliderRect.setRect(this->rect().x() + s.width() + - sliderWidth - 1 - d_xMargin, + this->rect().y() + d_yMargin, + sliderWidth, + s.height() - 2 * d_yMargin); + d_scale.setGeometry(d_sliderRect.x() - d_scaleDist, + d_sliderRect.y() + d_thumbHalf, + s.height() - d_thumbLength, + ScaleDraw::Left); + + break; + case Right: + d_sliderRect.setRect(this->rect().x() + d_xMargin, + this->rect().y() + d_yMargin, + sliderWidth, + s.height() - 2* d_yMargin); + d_scale.setGeometry(this->rect().x() + d_sliderRect.width() + + d_scaleDist, + d_sliderRect.y() + d_thumbHalf, + s.height() - d_thumbLength, + ScaleDraw::Right); + break; + default: + d_sliderRect.setRect(this->rect().x(), this->rect().x(), + s.width(), s.height()); + break; + } + } + */ + d_sliderRect.setRect(this->rect().x(), this->rect().y(), + s.width(), s.height()); +} + +//------------------------------------------------------------ +//.- +//.F ThinSlider::valueChange +// Notify change of value +// +//.u Syntax +//.f void ThinSlider::valueChange() +// +//------------------------------------------------------------ + +void ThinSlider::valueChange() + { + update(); + SliderBase::valueChange(); + } + +//------------------------------------------------------------ +//.- +//.F ThinSlider::rangeChange +// Notify change of range +// +//.u Description +// +//.u Syntax +//.f void ThinSlider::rangeChange() +// +//------------------------------------------------------------ +void ThinSlider::rangeChange() +{ + if (!hasUserScale()) + d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); + SliderBase::rangeChange(); + repaint(); +} + +//------------------------------------------------------------ +// +//.F ThinSlider::setMargins +// Set distances between the widget's border and +// internals. +// +//.u Syntax +//.f void ThinSlider::setMargins(int hor, int vert) +// +//.u Parameters +//.p int hor, int vert -- Margins +// +//------------------------------------------------------------ +void ThinSlider::setMargins(int hor, int vert) +{ + d_xMargin = MusECore::qwtMax(0, hor); + d_yMargin = MusECore::qwtMax(0, vert); + resize(this->size()); +} + +//------------------------------------------------------------ +// +//.F ThinSlider::sizeHint +// Return a recommended size +// +//.u Syntax +//.f QSize ThinSlider::sizeHint() const +// +//.u Note +// The return value of sizeHint() depends on the font and the +// scale. +//------------------------------------------------------------ + +QSize ThinSlider::sizeHint() const + { + /* Scale is not supported + int w = 40; + int h = 40; + QPainter p; + int msWidth = 0, msHeight = 0; + + if (d_scalePos != None) { + if (p.begin(this)) { + msWidth = d_scale.maxWidth(&p, FALSE); + msHeight = d_scale.maxHeight(&p); + } + p.end(); + + switch(d_orient) { + case Qt::Vertical: + w = 2*d_xMargin + d_thumbWidth + msWidth + d_scaleDist + 2; + break; + case Qt::Horizontal: + h = 2*d_yMargin + d_thumbWidth + msHeight + d_scaleDist; + break; + } + } + else { // no scale + switch(d_orient) { + case Qt::Vertical: + w = 16; + break; + case Qt::Horizontal: + h = 16; + break; + } + } + */ + return QSize(horizontal_hint, vertical_hint); + } + +//--------------------------------------------------------- +// setOrientation +//--------------------------------------------------------- + +void ThinSlider::setOrientation(Qt::Orientation o) + { + d_orient = o; + /* Scale is not supported + ScaleDraw::OrientationX so = ScaleDraw::Bottom; + switch(d_orient) { + case Qt::Vertical: + if (d_scalePos == Right) + so = ScaleDraw::Right; + else + so = ScaleDraw::Left; + break; + case Qt::Horizontal: + if (d_scalePos == Bottom) + so = ScaleDraw::Bottom; + else + so = ScaleDraw::Top; + break; + } + + d_scale.setGeometry(0, 0, 40, so); + if (d_orient == Qt::Vertical) + setMinimumSize(10,20); + else + setMinimumSize(20,10); + QRect r = geometry(); + setGeometry(r.x(), r.y(), r.height(), r.width()); + update(); + */ + + switch(d_orient) { + case Qt::Vertical: + horizontal_hint = 32; + vertical_hint = 64; + break; + case Qt::Horizontal: + horizontal_hint = 64; + vertical_hint = 32; + break; + } + } + +Qt::Orientation ThinSlider::orientation() const + { + return d_orient; + } + +double ThinSlider::lineStep() const + { + return 1.0; + } + +double ThinSlider::pageStep() const + { + return 1.0; + } + +void ThinSlider::setLineStep(double) + { + } + +void ThinSlider::setPageStep(double) + { + } + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/thinslider.h muse-3.0.2+ds1/muse/widgets/thinslider.h --- muse-2.1.2/muse/widgets/thinslider.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/thinslider.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,117 @@ +//========================================================= +// MusE +// Linux Music Editor +// $Id: slider.h,v 1.3.2.2 2008/01/19 13:33:47 wschweer Exp $ +// +// Copyright (C) 1997 Josef Wilgen +// (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.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 __SLIDER_H__ +#define __SLIDER_H__ + +#include "sclif.h" +#include "sliderbase.h" +#include "scldraw.h" + +namespace MusEGui { + +//--------------------------------------------------------- +// Slider +//--------------------------------------------------------- + +class ThinSlider : public SliderBase, public ScaleIf + { + Q_OBJECT + + public: + enum ScalePos { None, Left, Right, Top, Bottom }; + + private: + Q_PROPERTY( double lineStep READ lineStep WRITE setLineStep ) + Q_PROPERTY( double pageStep READ pageStep WRITE setPageStep ) + Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation ) + + QRect d_sliderRect; + + int d_thumbLength; + int d_thumbHalf; + int d_thumbWidth; + int d_scaleDist; + int d_xMargin; + int d_yMargin; + int d_mMargin; + + QColor d_fillColor; + + bool d_resized; + bool d_autoResize; + double d_scaleStep; + + Qt::Orientation d_orient; + ScalePos d_scalePos; + int d_bgStyle; + int markerPos; + + uint vertical_hint; + uint horizontal_hint; + + void drawHsBgSlot(QPainter *, const QRect&, const QRect&,const QBrush&); + void drawVsBgSlot(QPainter *, const QRect&, const QRect&,const QBrush&); + + protected: + virtual void drawSlider (QPainter *p, const QRect &r); + double getValue(const QPoint &p); + // Determine the value corresponding to a specified mouse movement. + double moveValue(const QPoint& /*deltaP*/, bool /*fineMode*/ = false); + void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction); + virtual void resizeEvent(QResizeEvent *e); + virtual void paintEvent (QPaintEvent *e); + void valueChange(); + void rangeChange(); + void scaleChange(); + void fontChange(const QFont &oldFont); + + public: + ThinSlider(QWidget *parent, const char *name = 0, + Qt::Orientation orient = Qt::Vertical, + ScalePos scalePos = None, + QColor fillColor = QColor(100, 100, 255)); + + ~ThinSlider(); + void setThumbLength(int l); + void setThumbWidth(int w); + + void setOrientation(Qt::Orientation o); + Qt::Orientation orientation() const; + + double lineStep() const; + double pageStep() const; + + void setLineStep(double); + void setPageStep(double); + + void setMargins(int x, int y); + virtual QSize sizeHint() const; + void setSizeHint(uint w, uint h); + }; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/widgets/tools.cpp muse-3.0.2+ds1/muse/widgets/tools.cpp --- muse-2.1.2/muse/widgets/tools.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/tools.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -109,7 +109,7 @@ continue; ToolB* t = &toolList[i]; - Action* a = new Action(action, 1<tip).toAscii().data(), true); + Action* a = new Action(action, 1<tip).toLatin1().data(), true); actions[n] = a; //a->setIconSet(QIcon(**(t->icon))); a->setIcon(QIcon(**(t->icon))); @@ -126,9 +126,9 @@ // Note: Does not take ownership. addActions(action->actions()); - connect(action, SIGNAL(selected(QAction*)), SLOT(toolChanged(QAction*))); + connect(action, SIGNAL(triggered(QAction*)), SLOT(toolChanged(QAction*))); - toolShortcuts[PointerTool] = SHRT_TOOL_CURSOR; + toolShortcuts[PointerTool] = SHRT_TOOL_POINTER; toolShortcuts[PencilTool] = SHRT_TOOL_PENCIL; toolShortcuts[RubberTool] = SHRT_TOOL_RUBBER; toolShortcuts[CutTool] = SHRT_TOOL_SCISSORS; diff -Nru muse-2.1.2/muse/widgets/trackinfo_layout.cpp muse-3.0.2+ds1/muse/widgets/trackinfo_layout.cpp --- muse-2.1.2/muse/widgets/trackinfo_layout.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/trackinfo_layout.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,428 @@ +//========================================================= +// MusE +// Linux Music Editor +// trackinfo_layout.cpp +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 + +#include +#include +#include +#include +#include +#include + +#include "trackinfo_layout.h" +#include "splitter.h" +#include "scrollbar.h" +#include "widget_stack.h" +#include "scrollscale.h" +#include "ttoolbutton.h" + +namespace MusEGui { + +TrackInfoLayout::TrackInfoLayout(QWidget *parent, WidgetStack* stack, ScrollBar* sb, Splitter* splitter) + : QHBoxLayout(parent), _stack(stack), _sb(sb), _splitter(splitter) +{ + _inSetGeometry = false; + setContentsMargins(0, 0, 0, 0); + setSpacing(0); + _sbShowPending = false; + _stackLi = new QWidgetItem(_stack); + _sbLi = new QWidgetItem(_sb); + + addItem(_stackLi); + addItem(_sbLi); +} + +TrackInfoLayout::~TrackInfoLayout() +{ +} + +//--------------------------------------------------------- +// setGeometry +//--------------------------------------------------------- + +void TrackInfoLayout::setGeometry(const QRect &rect) + { + QHBoxLayout::setGeometry(rect); + return; + + if(_inSetGeometry) + return; + + int w = rect.width(); + int h = rect.height(); + + QSize s0; + QWidget* widget = _stack->visibleWidget(); + if(widget) { + s0 = widget->minimumSizeHint(); + if (!s0.isValid()) // widget has no geometry management + s0 = widget->size(); + } + else + s0 = _stack->minimumSizeHint(); + + int range = s0.height() - h; + if (range < 0) + range = 0; +// fprintf(stderr, "TrackInfoLayout::setGeometry sb w:%d visible:%d split count:%d w:%d h:%d s0 height:%d range:%d\n", +// _sb->width(), _sb->isVisible(), _splitter->count(), w, h, s0.height(), range); + + if (range) + { + _sb->blockSignals(true); + _sb->setMaximum(range); + _sb->blockSignals(false); + } + + const bool vis = range != 0; + + // Was a show pending and the scrollbar is now visible? Reset the pending flag. + if(_sbShowPending && _sb->isVisible()) + _sbShowPending = false; + + if(_sb->isVisible() != vis) + { + if(_sb->isVisible()) + { + int sw = w - _sb->width(); + if(sw < 0) + sw = 0; + _sb->setVisible(false); + if(_splitter) + { + //fprintf(stderr, "TrackInfoLayout::setGeometry hide sb: split pos:%d\n", sw); + _inSetGeometry = true; + _splitter->setPosition(1, sw); // FIXME: Causes too wide on first startup, also when maximizing. + _inSetGeometry = false; + } + _stackLi->setGeometry(QRect(0, 0, sw, h)); + //fprintf(stderr, "TrackInfoLayout::setGeometry hide sb: widget:%p w:%d\n", widget, sw); + if(widget) + { +// QSize r(sw, y2 < s0.height() ? s0.height() : y2); +// //fprintf(stderr, "TrackInfoLayout::setGeometry hide sb: widget w:%d\n", +// // r.width()); +// //widget->setGeometry(0, -_sb->value(), r.width(), r.height()); +// widget->setGeometry(0, 0, r.width(), r.height()); + widget->move(0, 0); + } + } + else + { + // If an ancestor is NOT visible this will not happen until the ancestor becomes visible. + // Simply reading isVisible() immediately afterwards (below) would return FALSE. + _sb->setVisible(true); + _sbShowPending = true; + + const int sbw = _sb->isVisible() ? _sb->width() : _sbLi->sizeHint().width(); + + if(_splitter) + { + //fprintf(stderr, "TrackInfoLayout::setGeometry show sb: split pos:%d\n", + // w + sbw); + _inSetGeometry = true; + _splitter->setPosition(1, w + sbw); // FIXME: Causes too wide on first startup, also when maximizing. + _inSetGeometry = false; + } + _stackLi->setGeometry(QRect(0, 0, w, h)); + //fprintf(stderr, "TrackInfoLayout::setGeometry show sb: widget:%p w:%d\n", widget, w); +// _stackLi->setGeometry(QRect(0, 0, w, y2 < s0.height() ? s0.height() : y2)); + if(widget) + { +// QSize r(w, y2 < s0.height() ? s0.height() : y2); +// widget->setGeometry(0, -_sb->value(), r.width(), r.height()); + widget->move(0, -_sb->value()); + } + } + } + else + { + int ww = w; + if(_sb->isVisible() || _sbShowPending) + ww -= _sb->isVisible() ? _sb->width() : _sbLi->sizeHint().width(); + + //fprintf(stderr, "TrackInfoLayout::setGeometry not show/hide sb: widget:%p w:%d\n", widget, ww); + _stackLi->setGeometry(QRect(0, 0, ww, h)); +// _stackLi->setGeometry(QRect(0, 0, ww, h < s0.height() ? s0.height() : h)); + if(widget) + { +// QSize r(ww, y2 < s0.height() ? s0.height() : y2); +// widget->setGeometry(0, -_sb->value(), r.width(), r.height()); + if(_sb->isVisible() || _sbShowPending) + widget->move(0, -_sb->value()); + } + } + + if(_sb->isVisible() || _sbShowPending) + { + const int sbw = _sb->isVisible() ? _sb->width() : _sbLi->sizeHint().width(); + int sbx = w + (_sb->isVisible() ? -sbw : sbw); + if(sbx < 0) + sbx = 0; + //fprintf(stderr, "TrackInfoLayout::setGeometry: sb visible or pending: setting _sbLi: x:%d w:%d\n", sbx, sbw); + _sbLi->setGeometry(QRect(sbx, 0, sbw, h)); + } + else + { + //fprintf(stderr, "TrackInfoLayout::setGeometry: sb not visible nor pending: setting _sbLi: x:%d w:%d\n", w, 0); + _sbLi->setGeometry(QRect(w, 0, 0, h)); + } +} + +//--------------------------------------------------------- +// sizeHint +//--------------------------------------------------------- + +QSize TrackInfoLayout::sizeHint() const + { + return QSize(150, 100); + } + +//--------------------------------------------------------- +// minimumSize +//--------------------------------------------------------- + +QSize TrackInfoLayout::minimumSize() const + { + int w = _stack->minimumSizeHint().width(); + if(_sb->isVisible()) + w += _sbLi->sizeHint().width(); + return QSize(w, 50); + } + +//--------------------------------------------------------- +// maximumSize +//--------------------------------------------------------- + +QSize TrackInfoLayout::maximumSize() const + { + return QSize(440, 100000); + } + + +//--------------------------------------------------------- +// ArrangerCanvasLayout +//--------------------------------------------------------- + +void ArrangerCanvasLayout::setGeometry(const QRect &rect) +{ + QGridLayout::setGeometry(rect); + // Tell the hbox to update as well, as if it was part of this layout. + //_hBox->activate(); + _hBox->update(); +} + +//--------------------------------------------------------- +// ArrangerHScrollLayout +//--------------------------------------------------------- + +ArrangerHScrollLayout::ArrangerHScrollLayout(QWidget *parent, + CompactToolButton* trackinfoButton, + CompactToolButton* trackinfoAltButton, + ScrollScale* sb, + QWidget* editor) + : QHBoxLayout(parent), + _trackinfoButton(trackinfoButton), + _trackinfoAltButton(trackinfoAltButton), + _sb(sb), + _editor(editor), + _trackinfoButtonLi(0), + _trackinfoAltButtonLi(0) +{ + _trackinfoButtonLi = new QWidgetItem(_trackinfoButton); + if(_trackinfoAltButton) + _trackinfoAltButtonLi = new QWidgetItem(_trackinfoAltButton); + _spacerLi = new QSpacerItem(0, 0); + _sbLi = new QWidgetItem(_sb); + + addItem(_trackinfoButtonLi); + if(_trackinfoAltButtonLi) + addItem(_trackinfoAltButtonLi); + addItem(_spacerLi); + addItem(_sbLi); +}; + +ArrangerHScrollLayout::~ArrangerHScrollLayout() +{ +} + +void ArrangerHScrollLayout::setGeometry(const QRect &rect) +{ + _trackinfoButtonLi->setGeometry(QRect(rect.x() , + rect.y(), + _trackinfoButton->sizeHint().width(), + rect.height())); + + if(_trackinfoAltButtonLi) + _trackinfoAltButtonLi->setGeometry(QRect(_trackinfoButton->sizeHint().width() + spacing(), + rect.y(), + _trackinfoAltButton->sizeHint().width(), + rect.height())); + + const int ti_w = _trackinfoButtonLi->sizeHint().width() + spacing() + + (_trackinfoAltButtonLi ? (_trackinfoAltButtonLi->sizeHint().width() + spacing()) : 0); + + if(_editor->width() > 0) + { + _sb->setVisible(true); + int x = _editor->x(); + + if(x < ti_w) + x = ti_w; + + int w = rect.width() - x; + + if(w < _sb->minimumSizeHint().width()) + { + w = _sb->minimumSizeHint().width(); + x = rect.width() - w; + } + + QRect r(x, rect.y(), w, rect.height()); + _sbLi->setGeometry(r); + _spacerLi->setGeometry(QRect(ti_w, rect.y(), rect.width() - ti_w - w, rect.height())); + } + else + { + _sb->setVisible(false); + _spacerLi->setGeometry(QRect(ti_w, rect.y(), rect.width() - ti_w, rect.height())); + } +} + +//--------------------------------------------------------- +// TrackInfoWidget +//--------------------------------------------------------- + +TrackInfoWidget::TrackInfoWidget(QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f) +{ + _stack = new WidgetStack(this, "trackInfoStack", WidgetStack::VisibleHint); + _scrollBar = new ScrollBar(Qt::Vertical, true, this); + _scrollBar->setObjectName("infoScrollBar"); + _trackInfoLayout = new TrackInfoLayout(this, _stack, _scrollBar); + connect(_scrollBar, SIGNAL(valueChanged(int)), SLOT(scrollValueChanged(int))); + connect(_stack, SIGNAL(redirectWheelEvent(QWheelEvent*)), _scrollBar, SLOT(redirectedWheelEvent(QWheelEvent*))); +} + +void TrackInfoWidget::scrollValueChanged(int val) +{ + if(_stack->visibleWidget()) + _stack->visibleWidget()->move(0, -val); +} + +void TrackInfoWidget::doResize(const QSize& newSize) +{ + if(QWidget* widget = _stack->visibleWidget()) + { + QSize wsz = widget->minimumSizeHint(); + if(!wsz.isValid()) + wsz = widget->minimumSize(); + + QSize sz(newSize); + + if(sz.width() < wsz.width()) + sz.setWidth(wsz.width()); + if(sz.height() < wsz.height()) + sz.setHeight(wsz.height()); + + if(_scrollBar) + { + int range = sz.height() - height(); + if(range < 0) + range = 0; + if(range) + { + //fprintf(stderr, "TrackInfoWidget::doResize sb range:%d\n", range); + _scrollBar->blockSignals(true); + _scrollBar->setMaximum(range); + _scrollBar->blockSignals(false); + } + const bool vis = range != 0; + + // We can't do this check. An ancestor might not be visible yet. + //if(_scrollBar->isVisible() != vis) + { + + //fprintf(stderr, "TrackInfoWidget::doResize before setting sb visible:%d\n", vis); + _scrollBar->setVisible(vis); + //fprintf(stderr, "TrackInfoWidget::doResize after setting sb visible:%d\n", vis); + + } + + } + } +} + +void TrackInfoWidget::doMove() +{ + if(QWidget* widget = _stack->visibleWidget()) + { + if(_scrollBar->isVisible()) + widget->move(0, -_scrollBar->value()); + else + widget->move(0, 0); + } +} + +void TrackInfoWidget::resizeEvent(QResizeEvent* e) +{ + e->ignore(); + QWidget::resizeEvent(e); + //doResize(e->size()); + doResize(_stack->size()); + doMove(); +} + + +void TrackInfoWidget::raiseWidget(int idx) +{ + _stack->raiseWidget(idx); + doResize(_stack->size()); + doMove(); + _trackInfoLayout->activate(); + _trackInfoLayout->update(); +} + +void TrackInfoWidget::addWidget(QWidget* w, unsigned int idx) +{ + _stack->addWidget(w, idx); + doResize(_stack->size()); +} + +QWidget* TrackInfoWidget::getWidget(unsigned int idx) +{ + return _stack->getWidget(idx); +} + +QWidget* TrackInfoWidget::visibleWidget() const +{ + return _stack->visibleWidget(); +} + +int TrackInfoWidget::curIdx() const +{ + return _stack->curIdx(); +} + + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/trackinfo_layout.h muse-3.0.2+ds1/muse/widgets/trackinfo_layout.h --- muse-2.1.2/muse/widgets/trackinfo_layout.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/trackinfo_layout.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,162 @@ +//========================================================= +// MusE +// Linux Music Editor +// trackinfo_layout.h +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __TRACKINFO_LAYOUT_H__ +#define __TRACKINFO_LAYOUT_H__ + +#include +#include +#include +#include + +class QLayoutItem; +class QSize; +class QRect; + +namespace MusEGui { + +class Splitter; +class ScrollBar; +class WidgetStack; +class ScrollScale; +class CompactToolButton; + +//--------------------------------------------------------- +// TrackInfoLayout +// For laying out a widget stack and scrollbar which always appears +// to the right of the stack instead of intruding into its space. +// An optional Splitter will be resized when the scrollbar appears. +//--------------------------------------------------------- + +class TrackInfoLayout : public QHBoxLayout + { + Q_OBJECT + + bool _inSetGeometry; + WidgetStack* _stack; + ScrollBar* _sb; + QLayoutItem* _stackLi; + QLayoutItem* _sbLi; + + bool _sbShowPending; + + // This is not actually in the layout, but used and/or adjusted anyway. + Splitter* _splitter; + + public: + TrackInfoLayout(QWidget *parent, WidgetStack* stack, ScrollBar* sb, Splitter* splitter = 0); + virtual ~TrackInfoLayout(); + + virtual QSize sizeHint() const; + virtual QSize minimumSize() const; + virtual QSize maximumSize() const; + + virtual void setGeometry(const QRect &rect); + }; + +//--------------------------------------------------------- +// ArrangerCanvasLayout +// For laying out a canvas as a last splitter widget and +// automatically adjusting the width of its corresponding +// horizontal scrollbar which is in another layout. +//--------------------------------------------------------- + +class ArrangerCanvasLayout : public QGridLayout + { + Q_OBJECT + QHBoxLayout* _hBox; + + public: + ArrangerCanvasLayout(QWidget *parent, QHBoxLayout* hBox) : QGridLayout(parent), _hBox(hBox) { }; + virtual void setGeometry(const QRect &rect); + }; + +//--------------------------------------------------------- +// ArrangerHScrollLayout +// For laying out the bottom buttons and hscroll in the arranger. +//--------------------------------------------------------- + +class ArrangerHScrollLayout : public QHBoxLayout + { + Q_OBJECT + CompactToolButton* _trackinfoButton; + CompactToolButton* _trackinfoAltButton; + ScrollScale* _sb; + + // This is not actually in the layout, but used anyway. + QWidget* _editor; + + QWidgetItem* _trackinfoButtonLi; + QWidgetItem* _trackinfoAltButtonLi; + QSpacerItem* _spacerLi; + QWidgetItem* _sbLi; + + public: + ArrangerHScrollLayout(QWidget *parent, + CompactToolButton* trackinfoButton, + CompactToolButton* trackinfoAltButton, + ScrollScale* sb, + QWidget* editor); + ~ArrangerHScrollLayout(); + virtual void setGeometry(const QRect &rect); + }; + +//--------------------------------------------------------- +// TrackInfoWidget +// Widget for containing a trackinfo layout. +//--------------------------------------------------------- + +class TrackInfoWidget : public QWidget +{ + Q_OBJECT + + private: + WidgetStack* _stack; + ScrollBar* _scrollBar; + TrackInfoLayout* _trackInfoLayout; + + void doResize(const QSize&); + void doMove(); + + private slots: + void scrollValueChanged(int); + + protected: + //virtual void wheelEvent(QWheelEvent* e); + virtual void resizeEvent(QResizeEvent*); + + + public: + TrackInfoWidget(QWidget* parent = 0, Qt::WindowFlags f = 0); + + // Wrappers/catchers for stack functions: + void raiseWidget(int idx); + void addWidget(QWidget* w, unsigned int idx); + QWidget* getWidget(unsigned int idx); + QWidget* visibleWidget() const; + int curIdx() const; +}; + +} + +#endif + diff -Nru muse-2.1.2/muse/widgets/tracks_duplicate_base.ui muse-3.0.2+ds1/muse/widgets/tracks_duplicate_base.ui --- muse-2.1.2/muse/widgets/tracks_duplicate_base.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/tracks_duplicate_base.ui 2017-12-04 21:01:19.000000000 +0000 @@ -6,14 +6,14 @@ 0 0 - 238 - 261 + 308 + 398 Duplicate tracks - + @@ -56,27 +56,39 @@
    - - - Copy all routes - - - true - - - - - - - Default routing - - - - - - - No routes - + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Copy all routes + + + true + + + + + + + Default routing + + + + + + + No routes + + + + @@ -87,10 +99,52 @@
    - - - Copy parts - + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Duplicate all parts + + + + + + + Make independent copies of all parts + + + false + + + + + + + Make clones of all parts + + + false + + + + + + + No parts + + + true + + + + @@ -110,7 +164,7 @@ - Copy standard controllers (vol, pan) + Copy standard (vol, pan) and synth controllers true @@ -119,9 +173,6 @@ - - false - Copy effects rack plugins diff -Nru muse-2.1.2/muse/widgets/tracks_duplicate.cpp muse-3.0.2+ds1/muse/widgets/tracks_duplicate.cpp --- muse-2.1.2/muse/widgets/tracks_duplicate.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/tracks_duplicate.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -33,7 +33,7 @@ pluginsCheckBox->setVisible(audio); pluginCtrlsCheckBox->setVisible(audio); copyDrumlistCheckBox->setVisible(newdrum); - + connect(okPushButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelPushButton, SIGNAL(clicked()), this, SLOT(reject())); diff -Nru muse-2.1.2/muse/widgets/tracks_duplicate.h muse-3.0.2+ds1/muse/widgets/tracks_duplicate.h --- muse-2.1.2/muse/widgets/tracks_duplicate.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/tracks_duplicate.h 2017-12-04 21:01:19.000000000 +0000 @@ -49,7 +49,10 @@ bool allRoutes() const { return allRoutesRadioButton->isChecked(); } bool defaultRoutes() const { return defaultRoutesRadioButton->isChecked(); } - bool copyParts() const { return copyPartsCheckBox->isChecked(); } + bool duplicateParts() const { return duplicatePartsRadioButton->isChecked(); } + bool copyParts() const { return copyPartsRadioButton->isChecked(); } + bool cloneParts() const { return clonePartsRadioButton->isChecked(); } + bool copyDrumlist() const { return copyDrumlistCheckBox->isChecked(); } }; diff -Nru muse-2.1.2/muse/widgets/transformbase.ui muse-3.0.2+ds1/muse/widgets/transformbase.ui --- muse-2.1.2/muse/widgets/transformbase.ui 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/transformbase.ui 2018-01-06 20:31:35.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 826 - 545 + 885 + 562 @@ -17,7 +17,16 @@ true - + + 11 + + + 11 + + + 11 + + 11 @@ -28,7 +37,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -107,7 +125,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -138,7 +165,16 @@ Processing - + + 11 + + + 11 + + + 11 + + 11 @@ -418,8 +454,8 @@ - - 99999999 + + 99999999.000000000000000 @@ -450,26 +486,31 @@ Divide + + + Randomize + +
    - - - 99999999 + + + 99999999.000000000000000 - - - 99999999 + + + 99999999.000000000000000 - - - 99999999 + + + 99999999.000000000000000 @@ -482,7 +523,16 @@ Filter - + + 11 + + + 11 + + + 11 + + 11 @@ -798,20 +848,20 @@ - - + + 3 - - - 1000 - - + + 3 + + 1000.000000000000000 + @@ -822,7 +872,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -834,7 +893,16 @@ 6 - + + 11 + + + 11 + + + 11 + + 11 @@ -842,7 +910,16 @@ 6 - + + 0 + + + 0 + + + 0 + + 0 @@ -885,7 +962,16 @@ 6 - + + 11 + + + 11 + + + 11 + + 11 @@ -921,7 +1007,16 @@ 6 - + + 11 + + + 11 + + + 11 + + 11 @@ -974,7 +1069,7 @@ - + @@ -985,11 +1080,11 @@ - - MusEGui::SpinBoxFP - QDoubleSpinBox -
    spinboxFP.h
    -
    + + MusEGui::SpinBoxFP + QDoubleSpinBox +
    spinboxFP.h
    +
    MusEGui::ComboQuant QComboBox diff -Nru muse-2.1.2/muse/widgets/ttoolbutton.cpp muse-3.0.2+ds1/muse/widgets/ttoolbutton.cpp --- muse-2.1.2/muse/widgets/ttoolbutton.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/ttoolbutton.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -21,7 +21,9 @@ //========================================================= #include +#include #include +#include #include "ttoolbutton.h" #include "gconfig.h" @@ -30,6 +32,10 @@ namespace MusEGui { //--------------------------------------------------------- +// TransparentToolButton +//--------------------------------------------------------- + +//--------------------------------------------------------- // drawButton //--------------------------------------------------------- @@ -42,5 +48,134 @@ const QPixmap pm(icon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize), mode, state)); p->drawPixmap(QPoint((w - pm.width())/2, (h - pm.height())/2), pm); } + +//--------------------------------------------------------- +// CompactToolButton +//--------------------------------------------------------- + +CompactToolButton::CompactToolButton(QWidget* parent, const QIcon& icon, bool hasFixedIconSize, bool drawFlat, const char* name) + : QToolButton(parent), _icon(icon), _hasFixedIconSize(hasFixedIconSize), _drawFlat(drawFlat) +{ + setObjectName(name); + _blinkPhase = false; +} + +QSize CompactToolButton::sizeHint() const +{ + // TODO Ask style for margins. + const QSize isz = iconSize(); + const int fmw = fontMetrics().width(text()); + const int fmh = fontMetrics().lineSpacing() + 5; + + const int iw = isz.width() + 2; + const int ih = isz.height() + 2; + + const int w = (_hasFixedIconSize && iw > fmw) ? iw : fmw; + const int h = (_hasFixedIconSize && ih > fmh) ? ih : fmh; + + return QSize(w, h); +} + +void CompactToolButton::setHasFixedIconSize(bool v) +{ + _hasFixedIconSize = v; + updateGeometry(); +} + +void CompactToolButton::setDrawFlat(bool v) +{ + _drawFlat = v; + update(); +} + +void CompactToolButton::setIcon(const QIcon & icon) +{ + _icon = icon; + update(); +} + +void CompactToolButton::setBlinkPhase(bool v) +{ + if(_blinkPhase == v) + return; + _blinkPhase = v; + if(isEnabled()) + update(); +} + +void CompactToolButton::paintEvent(QPaintEvent* ev) +{ + if(!_drawFlat) + QToolButton::paintEvent(ev); + + QIcon::Mode mode; + if(isEnabled()) + mode = hasFocus() ? QIcon::Selected : QIcon::Normal; + else + mode = QIcon::Disabled; + QIcon::State state = (isChecked() && (!_blinkPhase || !isEnabled())) ? QIcon::On : QIcon::Off; + + QPainter p(this); + _icon.paint(&p, rect(), Qt::AlignCenter, mode, state); + +// TODO Bah! Just want a mouse-over rectangle for flat mode but some styles do this or that but not the other thing. +// if(const QStyle* st = style()) +// { +// st = st->proxy(); +// // QStyleOptionToolButton o; +// // initStyleOption(&o); +// // o.rect = rect(); +// // //o.state |= QStyle::State_MouseOver; +// // o.state = QStyle::State_Active | +// // QStyle::State_Enabled | +// // QStyle::State_AutoRaise | // This is required to get rid of the panel. +// // QStyle::State_MouseOver; +// // st->drawPrimitive(QStyle::PE_PanelButtonTool, &o, &p); +// +// // QStyleOptionFrame o; +// // //initStyleOption(&o); +// // o.rect = rect(); +// // o.features = QStyleOptionFrame::Rounded; +// // o.frameShape = QFrame::Box; +// // o.lineWidth = 2; +// // o.midLineWidth = 4; +// // o.state |= QStyle::State_MouseOver; +// // st->drawPrimitive(QStyle::PE_Frame, &o, &p); +// +// +// QStyleOptionFocusRect o; +// //o.QStyleOption::operator=(option); +// //o.rect = st->subElementRect(QStyle::SE_ItemViewItemFocusRect, &option); +// o.rect = rect(); +// o.state |= QStyle::State_KeyboardFocusChange; +// o.state |= QStyle::State_Item | +// QStyle::State_Active | +// QStyle::State_Enabled | +// QStyle::State_HasFocus | +// +// //QStyle::State_Raised | +// QStyle::State_Sunken | +// +// QStyle::State_Off | +// //QStyle::State_On | +// +// QStyle::State_Selected | +// +// //QStyle::State_AutoRaise | // This is required to get rid of the panel. +// +// QStyle::State_MouseOver; +// +// // QPalette::ColorGroup cg = +// // (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; +// // o.backgroundColor = option.palette.color(cg, +// // (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); +// st->drawPrimitive(QStyle::PE_FrameFocusRect, &o, &p); +// +// } + + + ev->accept(); +} + } // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/ttoolbutton.h muse-3.0.2+ds1/muse/widgets/ttoolbutton.h --- muse-2.1.2/muse/widgets/ttoolbutton.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/ttoolbutton.h 2017-12-04 21:01:19.000000000 +0000 @@ -25,6 +25,9 @@ #include +class QIcon; +class QPaintEvent; + namespace MusEGui { //--------------------------------------------------------- @@ -41,6 +44,40 @@ : QToolButton(parent) {setObjectName(name);} }; +//--------------------------------------------------------- +// CompactToolButton +// Supports two icons. +//--------------------------------------------------------- + +class CompactToolButton : public QToolButton { + Q_OBJECT + + private: + QIcon _icon; + bool _hasFixedIconSize; + bool _drawFlat; + bool _blinkPhase; + + protected: + virtual void paintEvent(QPaintEvent*); + + public: + CompactToolButton(QWidget* parent = 0, const QIcon& icon = QIcon(), bool hasFixedIconSize = true, bool drawFlat = false, const char* name = 0); + + bool hasFixedIconSize() const { return _hasFixedIconSize; } + void setHasFixedIconSize(bool v); + + bool drawFlat() const { return _drawFlat; } + void setDrawFlat(bool v); + + // If _hasFixedIconSize is true, this relies on iconSize(). Be sure to set iconSize to the desired value. + virtual QSize sizeHint() const; + + void setIcon(const QIcon & icon); + bool blinkPhase() const { return _blinkPhase; } + void setBlinkPhase(bool v); + }; + } // namespace MusEGui #endif diff -Nru muse-2.1.2/muse/widgets/utils.cpp muse-3.0.2+ds1/muse/widgets/utils.cpp --- muse-2.1.2/muse/widgets/utils.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/utils.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -37,6 +37,7 @@ #include #include #include +#include #include "audio.h" #include "audiodev.h" @@ -163,9 +164,9 @@ s += " "; QString ns; if (first == i-1) - ns.sprintf("%d", first+1); + ns = QString::number(first + 1); else - ns.sprintf("%d-%d", first+1, i); + ns = QString("%1-%2").arg(first + 1).arg(i); s += ns; needSpace = true; } @@ -210,11 +211,9 @@ s += " "; QString ns; if (first == i-1) - ns.sprintf("%d", first+1); - //ns.sprintf("%u", first+1); + ns = QString::number(first + 1); else - ns.sprintf("%d-%d", first+1, i); - //ns.sprintf("%u-%u", first+1, i); + ns = QString("%1-%2").arg(first + 1).arg(i); s += ns; needSpace = true; } @@ -234,16 +233,17 @@ { int val = 0; QString ss = str.simplified(); - QByteArray ba = ss.toLatin1(); - const char* s = ba.constData(); -//printf("string2bitmap <%s>\n", s); - if (s == 0) + if (ss.isEmpty()) return 0; - if (strcmp(s, "all") == 0) + if (ss == QString("all")) return 0xffff; - if (strcmp(s, "none") == 0) + if (ss == QString("none")) return 0; + + QByteArray ba = ss.toLatin1(); + const char* s = ba.constData(); + // printf("str2bitmap: <%s> ", str.toLatin1); int tval = 0; bool range = false; @@ -292,17 +292,17 @@ //int val = 0; unsigned int val = 0; QString ss = str.simplified(); - QByteArray ba = ss.toLatin1(); - const char* s = ba.constData(); -//printf("string2bitmap <%s>\n", s); - if (s == 0) + if (ss.isEmpty()) return 0; - if (strcmp(s, "all") == 0) - //return 0xffff; - return 0xffffffff; - if (strcmp(s, "none") == 0) + if (ss == QString("all")) + return 0xffff; + if (ss == QString("none")) return 0; + + QByteArray ba = ss.toLatin1(); + const char* s = ba.constData(); + // printf("str2bitmap: <%s> ", str.toLatin1); int tval = 0; //unsigned int tval = 0; @@ -347,60 +347,387 @@ return val; } +// //--------------------------------------------------------- +// // autoAdjustFontSize +// // w: Widget to auto adjust font size +// // s: String to fit +// // ignoreWidth: Set if dealing with a vertically constrained widget - one which is free to resize horizontally. +// // ignoreHeight: Set if dealing with a horizontally constrained widget - one which is free to resize vertically. +// // Returns false if text would not fit even at min size. +// // Caller should enable word wrap (if available) if false is returned, or disable word wrap if true is returned. +// // Otherwise if word wrap is enabled all the time, there is the possibility it will break the line prematurely. +// //--------------------------------------------------------- +// +// bool autoAdjustFontSize(QFrame* w, const QString& s, bool ignoreWidth, bool ignoreHeight, int max, int min) +// { +// // In case the max or min was obtained from QFont::pointSize() which returns -1 +// // if the font is a pixel font, or if min is greater than max... +// // if(!w || (min < 0) || (max < 0) || (min > max)) +// if(!w) +// return false; +// if(min > max) +// min = max; +// +// // Make the minimum about 3/4 the maximum font size. +// min = int(double(max) * 0.7); +// +// // Limit the minimum and maximum sizes to something at least readable. +// if(max < 6) +// max = 6; +// if(min < 6) +// min = 6; +// +// QRect cr = w->contentsRect(); +// QRect r; +// QFont fnt = w->font(); +// // An extra amount just to be sure - I found it was still breaking up two words which would fit on one line. +// // int extra = 4; +// int extra = 0; +// +// int fin_sz = max; +// // Allow at least one loop. min can be equal to max. +// // for(int i = max; i >= min; --i) +// for( ; fin_sz >= min; --fin_sz) +// { +// fnt.setPointSize(fin_sz); +// QFontMetrics fm(fnt); +// r = fm.boundingRect(s); +// // Would the text fit within the widget? +// if((ignoreWidth || (r.width() <= (cr.width() - extra))) && (ignoreHeight || (r.height() <= cr.height()))) +// break; +// } +// //printf("autoAdjustFontSize: ptsz:%d widget:%s before setFont x:%d y:%d w:%d h:%d\n", fnt.pointSize(), w->name(), w->x(), w->y(), w->width(), w->height()); +// +// // Here we will always have a font ranging from min to max point size. +// w->setFont(fnt); +// // w->setStyleSheet(MusECore::font2StyleSheet(fnt)); +// //printf("autoAdjustFontSize: ptsz:%d widget:%s x:%d y:%d w:%d h:%d frame w:%d rw:%d rh:%d\n", fnt.pointSize(), w->name(), w->x(), w->y(), w->width(), w->height(), w->frameWidth(), cr.width(), cr.height()); +// +// // ----------------------------------------------------------- +// // This is an alternate faster method. But the one below is better. +// // ----------------------------------------------------------- +// +// // QFont fnt = w->font(); +// // //const int req_w = w->fontMetrics().width(s) + 4; +// // const int req_w = w->fontMetrics().boundingRect(s).width() + 4; +// // if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. +// // { +// // if(fnt.pointSize() != max) +// // { +// // fnt.setPointSize(max); +// // w->setFont(fnt); +// // } +// // } +// // else +// // { +// // float factor = (float)w->rect().width() / (float)req_w; +// // //if((factor < 1) || (factor > 1.25)) +// // if((factor < 1) || (factor > 1)) +// // { +// // //qreal new_sz = fnt.pointSizeF() * factor; +// // int new_sz = (float)fnt.pointSize() * factor; +// // bool do_check = true; +// // if(new_sz < min) +// // { +// // new_sz = min; +// // do_check = false; +// // } +// // else if(new_sz > max) +// // { +// // new_sz = max; +// // do_check = false; +// // } +// // +// // //if(fnt.pointSizeF() != new_sz) +// // if(fnt.pointSize() != new_sz) +// // { +// // //fnt.setPointSizeF(new_sz); +// // fnt.setPointSize(new_sz); +// // if(do_check) +// // { +// // const QFontMetrics fm(fnt); +// // const int check_w = fm.boundingRect(s).width() + 4; +// // if(check_w > w->rect().width()) +// // { +// // --new_sz; +// // fnt.setPointSize(new_sz); +// // } +// // } +// // w->setFont(fnt); +// // } +// // } +// // } +// +// +// // ----------------------------------------------------------- +// // This is an alternate faster method. The accuracy is poorer +// // than the top method, and somewhat unreliable. Maybe with more tweaking... +// // ----------------------------------------------------------- +// +// // //qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); +// // //QRectF r = boundingRect(); +// // QRectF r = w->rect(); +// // //QFont f = painter->font(); +// // QFont fnt = w->font(); +// +// // //if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. +// // if(ignoreWidth || s.isEmpty()) // Also avoid divide by zero below. +// // { +// // if(fnt.pointSize() != max) +// // { +// // fnt.setPointSize(max); +// // w->setFont(fnt); +// // w->setStyleSheet(MusECore::font2StyleSheet(fnt)); +// // } +// // } +// // else +// // { +// // //qreal aspectRatio = painter->fontMetrics().lineSpacing() / painter->fontMetrics().averageCharWidth(); +// // qreal aspectRatio = w->fontMetrics().lineSpacing() / w->fontMetrics().averageCharWidth(); +// // int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (s.length() * 3)) * aspectRatio; +// // fnt.setPixelSize(pixelsize); +// // //int flags = Qt::AlignCenter|Qt::TextDontClip|Qt::TextWordWrap; +// // int flags = Qt::AlignCenter; +// // //if ((pixelsize * lod) < 13) +// // // flags |= Qt::TextWrapAnywhere; +// // QFontMetricsF fm(fnt); +// // QRectF tbr = fm.boundingRect(r,flags,s); +// // pixelsize = fnt.pixelSize() * qMin(r.width() * 0.95 / tbr.width(), r.height() * 0.95 / tbr.height()); +// // // if(pixelsize < min) +// // // pixelsize = min; +// // // else if(pixelsize > max) +// // // pixelsize = max; +// // fnt.setPixelSize(pixelsize); +// // const QFontInfo fi(fnt); +// // const int pointsize = fi.pointSize(); +// // if(pointsize <= min) +// // fnt.setPointSize(min); +// // else if(pointsize >= max) +// // fnt.setPointSize(max); +// // w->setFont(fnt); +// // w->setStyleSheet(MusECore::font2StyleSheet(fnt)); +// // //painter->drawText(r,flags,stitle); +// // } +// +// +// +// // Force minimum height. Use the expected height for the highest given point size. +// // This way the mixer strips aren't all different label heights, but can be larger if necessary. +// // Only if ignoreHeight is set (therefore the height is adjustable). +// if(ignoreHeight) +// { +// fnt.setPointSize(max); +// const QFontMetrics fm(fnt); +// // Set the label's minimum height equal to the height of the font. +// w->setMinimumHeight(fm.height() + 2 * w->frameWidth()); +// } +// +// // return true; +// +// // If the text still wouldn't fit at the min size, tell the caller to turn on word wrap. +// return fin_sz >= min; +// } + //--------------------------------------------------------- // autoAdjustFontSize // w: Widget to auto adjust font size -// s: String to fit +// txt: String to fit +// targetFont: Font input and output variable. Widget font is NOT automatically set due to stylesheets +// completely overriding them, therefore stylesheet must be 'composed' from the resulting font along with OTHER settings. // ignoreWidth: Set if dealing with a vertically constrained widget - one which is free to resize horizontally. -// ignoreHeight: Set if dealing with a horizontally constrained widget - one which is free to resize vertically. +// ignoreHeight: Set if dealing with a horizontally constrained widget - one which is free to resize vertically. +// Returns false if text would not fit even at min size. +// Caller should enable word wrap (if available) if false is returned, or disable word wrap if true is returned. +// Otherwise if word wrap is enabled all the time, there is the possibility it will break the line prematurely. //--------------------------------------------------------- -bool autoAdjustFontSize(QFrame* w, const QString& s, bool ignoreWidth, bool ignoreHeight, int max, int min) +bool autoAdjustFontSize(QFrame* widget, const QString& txt, QFont& targetFont, bool ignoreWidth, bool ignoreHeight, int max, int min) { - // In case the max or min was obtained from QFont::pointSize() which returns -1 + // In case the max or min was obtained from QFont::pointSize() which returns -1 // if the font is a pixel font, or if min is greater than max... - if(!w || (min < 0) || (max < 0) || (min > max)) +// if(!w || (min < 0) || (max < 0) || (min > max)) + if(!widget) return false; - + + if(min > max) + min = max; + + // Make the minimum about 3/4 the maximum font size. + min = int(double(max) * 0.7); + // Limit the minimum and maximum sizes to something at least readable. - if(max < 4) - max = 4; - if(min < 4) - min = 4; - - QRect cr = w->contentsRect(); + if(max < 6) + max = 6; + if(min < 6) + min = 6; + + QRect cr = widget->contentsRect(); QRect r; - QFont fnt = w->font(); +// QFont fnt = widget->font(); +// QFont fnt = targetFont; // Make a copy for later. + + // Force minimum height. Use the expected height for the highest given point size. + // This way the mixer strips aren't all different label heights, but can be larger if necessary. + // Only if ignoreHeight is set (therefore the height is adjustable). + if(ignoreHeight) + { + targetFont.setPointSize(max); + const QFontMetrics fm(targetFont); + // Set the label's minimum height equal to the height of the font. +// w->setMinimumHeight(fm.height() + 2 * w->frameWidth()); + widget->setMinimumHeight(fm.height() + 2 * widget->frameWidth()); + } + // An extra amount just to be sure - I found it was still breaking up two words which would fit on one line. - int extra = 4; +// int extra = 4; + int extra = 0; + + int fin_sz = max; // Allow at least one loop. min can be equal to max. - for(int i = max; i >= min; --i) +// for(int i = max; i >= min; --i) + for( ; fin_sz >= min; --fin_sz) { - fnt.setPointSize(i); - QFontMetrics fm(fnt); - r = fm.boundingRect(s); +// fnt.setPointSize(fin_sz); + targetFont.setPointSize(fin_sz); +// QFontMetrics fm(fnt); + QFontMetrics fm(targetFont); + r = fm.boundingRect(txt); // Would the text fit within the widget? if((ignoreWidth || (r.width() <= (cr.width() - extra))) && (ignoreHeight || (r.height() <= cr.height()))) +// if((ignoreWidth || (r.width() <= (sz.width() - extra))) && (ignoreHeight || (r.height() <= sz.height()))) break; } //printf("autoAdjustFontSize: ptsz:%d widget:%s before setFont x:%d y:%d w:%d h:%d\n", fnt.pointSize(), w->name(), w->x(), w->y(), w->width(), w->height()); - + // Here we will always have a font ranging from min to max point size. - w->setFont(fnt); +// w->setFont(fnt); +// w->setStyleSheet(MusECore::font2StyleSheet(fnt)); //printf("autoAdjustFontSize: ptsz:%d widget:%s x:%d y:%d w:%d h:%d frame w:%d rw:%d rh:%d\n", fnt.pointSize(), w->name(), w->x(), w->y(), w->width(), w->height(), w->frameWidth(), cr.width(), cr.height()); - - // Force minimum height. Use the expected height for the highest given point size. - // This way the mixer strips aren't all different label heights, but can be larger if necessary. - // Only if ignoreHeight is set (therefore the height is adjustable). - if(ignoreHeight) - { - fnt.setPointSize(max); - QFontMetrics fm(fnt); - // Set the label's minimum height equal to the height of the font. - w->setMinimumHeight(fm.height() + 2 * w->frameWidth()); - } - - return true; + +// ----------------------------------------------------------- +// This is an alternate faster method. But the one below is better. +// ----------------------------------------------------------- + +// QFont fnt = w->font(); +// //const int req_w = w->fontMetrics().width(s) + 4; +// const int req_w = w->fontMetrics().boundingRect(s).width() + 4; +// if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. +// { +// if(fnt.pointSize() != max) +// { +// fnt.setPointSize(max); +// w->setFont(fnt); +// } +// } +// else +// { +// float factor = (float)w->rect().width() / (float)req_w; +// //if((factor < 1) || (factor > 1.25)) +// if((factor < 1) || (factor > 1)) +// { +// //qreal new_sz = fnt.pointSizeF() * factor; +// int new_sz = (float)fnt.pointSize() * factor; +// bool do_check = true; +// if(new_sz < min) +// { +// new_sz = min; +// do_check = false; +// } +// else if(new_sz > max) +// { +// new_sz = max; +// do_check = false; +// } +// +// //if(fnt.pointSizeF() != new_sz) +// if(fnt.pointSize() != new_sz) +// { +// //fnt.setPointSizeF(new_sz); +// fnt.setPointSize(new_sz); +// if(do_check) +// { +// const QFontMetrics fm(fnt); +// const int check_w = fm.boundingRect(s).width() + 4; +// if(check_w > w->rect().width()) +// { +// --new_sz; +// fnt.setPointSize(new_sz); +// } +// } +// w->setFont(fnt); +// } +// } +// } + + +// ----------------------------------------------------------- +// This is an alternate faster method. The accuracy is poorer +// than the top method, and somewhat unreliable. Maybe with more tweaking... +// ----------------------------------------------------------- + +// //qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); +// //QRectF r = boundingRect(); +// QRectF r = w->rect(); +// //QFont f = painter->font(); +// QFont fnt = w->font(); + +// //if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. +// if(ignoreWidth || s.isEmpty()) // Also avoid divide by zero below. +// { +// if(fnt.pointSize() != max) +// { +// fnt.setPointSize(max); +// w->setFont(fnt); +// w->setStyleSheet(MusECore::font2StyleSheet(fnt)); +// } +// } +// else +// { +// //qreal aspectRatio = painter->fontMetrics().lineSpacing() / painter->fontMetrics().averageCharWidth(); +// qreal aspectRatio = w->fontMetrics().lineSpacing() / w->fontMetrics().averageCharWidth(); +// int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (s.length() * 3)) * aspectRatio; +// fnt.setPixelSize(pixelsize); +// //int flags = Qt::AlignCenter|Qt::TextDontClip|Qt::TextWordWrap; +// int flags = Qt::AlignCenter; +// //if ((pixelsize * lod) < 13) +// // flags |= Qt::TextWrapAnywhere; +// QFontMetricsF fm(fnt); +// QRectF tbr = fm.boundingRect(r,flags,s); +// pixelsize = fnt.pixelSize() * qMin(r.width() * 0.95 / tbr.width(), r.height() * 0.95 / tbr.height()); +// // if(pixelsize < min) +// // pixelsize = min; +// // else if(pixelsize > max) +// // pixelsize = max; +// fnt.setPixelSize(pixelsize); +// const QFontInfo fi(fnt); +// const int pointsize = fi.pointSize(); +// if(pointsize <= min) +// fnt.setPointSize(min); +// else if(pointsize >= max) +// fnt.setPointSize(max); +// w->setFont(fnt); +// w->setStyleSheet(MusECore::font2StyleSheet(fnt)); +// //painter->drawText(r,flags,stitle); +// } + + + +// // Force minimum height. Use the expected height for the highest given point size. +// // This way the mixer strips aren't all different label heights, but can be larger if necessary. +// // Only if ignoreHeight is set (therefore the height is adjustable). +// if(ignoreHeight) +// { +// fnt.setPointSize(max); +// const QFontMetrics fm(fnt); +// // Set the label's minimum height equal to the height of the font. +// // w->setMinimumHeight(fm.height() + 2 * w->frameWidth()); +// widget->setMinimumHeight(fm.height() + 2 * widget->frameWidth()); +// } + +// return true; + + // If the text still wouldn't fit at the min size, tell the caller to turn on word wrap. + return fin_sz >= min; } QGradient gGradientFromQColor(const QColor& c, const QPointF& start, const QPointF& finalStop) @@ -419,7 +746,7 @@ return gradient; } -QPainterPath roundedPath(QRect r, int xrad, int yrad, Corner roundCorner) +QPainterPath roundedPath(const QRect& r, int xrad, int yrad, Corner roundCorner) { return roundedPath(r.x(), r.y(), r.width(), r.height(), @@ -471,6 +798,56 @@ return rounded_rect; } +void addRoundedPath(QPainterPath* path, const QRect& r, int xrad, int yrad, Corner roundCorner) +{ + addRoundedPath(path, r.x(), r.y(), + r.width(), r.height(), + xrad, yrad, + roundCorner); +} + +void addRoundedPath(QPainterPath* path, int x, int y, int w, int h, int xrad, int yrad, Corner roundCorner) +{ + QPainterPath& pp = *path; + pp.addRect(x, y, w, h); + + if (roundCorner & UpperLeft) + { + QPainterPath top_left_corner; + top_left_corner.addRect(x, y, xrad, yrad); + top_left_corner.moveTo(x + xrad, y + yrad); + top_left_corner.arcTo(x, y, xrad*2, yrad*2, 180, -90); + pp -= top_left_corner; + } + + if (roundCorner & UpperRight) + { + QPainterPath top_right_corner; + top_right_corner.addRect(x + w - xrad, y, xrad, yrad); + top_right_corner.moveTo(x + w - xrad, y + yrad); + top_right_corner.arcTo(x + w - xrad * 2, y, xrad*2, yrad*2, 90, -90); + pp -= top_right_corner; + } + + if (roundCorner & LowerLeft) + { + QPainterPath bottom_left_corner; + bottom_left_corner.addRect(x, y + h - yrad, xrad, yrad); + bottom_left_corner.moveTo(x + xrad, y + h - yrad); + bottom_left_corner.arcTo(x, y + h - yrad*2, xrad*2, yrad*2, 180, 90); + pp -= bottom_left_corner; + } + + if (roundCorner & LowerRight) + { + QPainterPath bottom_right_corner; + bottom_right_corner.addRect(x + w - xrad, y + h - yrad, xrad, yrad); + bottom_right_corner.moveTo(x + w - xrad, y + h - yrad); + bottom_right_corner.arcTo(x + w - xrad*2, y + h - yrad*2, xrad*2, yrad*2, 270, 90); + pp -= bottom_right_corner; + } +} + //--------------------------------------------------------- // colorRect // paints a rectangular icon with a given color @@ -532,7 +909,7 @@ if (tag == "part") { Part* p = 0; - p = readXmlPart(xml, NULL, false, false); + p = Part::readFromXml(xml, NULL, false, false); if (p) { @@ -542,7 +919,7 @@ if (p->endTick() > end_tick) end_tick=p->endTick(); - unchainClone(p); + p->unchainClone(); // just for safety; shouldn't be chained anyway. delete p; } } @@ -598,5 +975,51 @@ return false; } +QString font2StyleSheet(const QFont& fnt) +{ + QString st; + switch(fnt.style()) + { + case QFont::StyleNormal: + st = "normal"; + break; + case QFont::StyleItalic: + st = "italic"; + break; + case QFont::StyleOblique: + st = "oblique"; + break; + } + + QString wt; + switch(fnt.weight()) + { + case QFont::Normal: + wt = "normal"; + break; + case QFont::Bold: + wt = "bold"; + break; + default: + // QFont::weight() : "Qt uses a weighting scale from 0 to 99..." + // Stylesheets : "The weight of a font:" + // normal + // | bold + // | 100 + // | 200 + // ... + // | 900 + wt = QString::number( (int)(((double)fnt.weight() / 99.0) * 8) * 100 + 100 ); + break; + } + + QString sz; + if(fnt.pointSize() > 0) + sz = QString("%1pt").arg(fnt.pointSize()); + else if(fnt.pixelSize() > 0) + sz = QString("%1px").arg(fnt.pixelSize()); + + return QString("font: %1 %2 %3 \"%4\"; ").arg(wt).arg(st).arg(sz).arg(fnt.family()); +} } // namespace MusECore diff -Nru muse-2.1.2/muse/widgets/utils.h muse-3.0.2+ds1/muse/widgets/utils.h --- muse-2.1.2/muse/widgets/utils.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/utils.h 2017-12-04 21:01:19.000000000 +0000 @@ -26,6 +26,7 @@ #define MAX(a,b) (((a)>(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b)) +class QFont; class QFrame; class QString; class QWidget; @@ -43,7 +44,7 @@ extern int string2bitmap(const QString& str); extern QString u32bitmap2String(unsigned int bm); extern unsigned int string2u32bitmap(const QString& str); -extern bool autoAdjustFontSize(QFrame* w, const QString& s, bool ignoreWidth = false, bool ignoreHeight = false, int max = 10, int min = 4); +extern bool autoAdjustFontSize(QFrame* w, const QString& s, QFont& targetFont, bool ignoreWidth = false, bool ignoreHeight = false, int max = 10, int min = 4); extern QGradient gGradientFromQColor(const QColor& c, const QPointF& start, const QPointF& finalStop); extern int num2cols(int min, int max); @@ -52,14 +53,19 @@ extern void dump(const unsigned char* p, int n); extern double curTime(); -extern QPainterPath roundedPath(QRect r, int xrad, int yrad, Corner roundCorner); +extern QPainterPath roundedPath(const QRect& r, int xrad, int yrad, Corner roundCorner); extern QPainterPath roundedPath(int x, int y, int w, int h, int xrad, int yrad, Corner roundCorner); +extern void addRoundedPath(QPainterPath* path, const QRect& r, int xrad, int yrad, Corner roundCorner); +extern void addRoundedPath(QPainterPath* path, int x, int y, int w, int h, int xrad, int yrad, Corner roundCorner); + extern QIcon colorRect(const QColor& color, int width, int height); extern int get_paste_len(); extern bool getUniqueFileName(const QString& filename, QString& newAbsFilePath); +extern QString font2StyleSheet(const QFont& fnt); + } // namespace MusECore #endif diff -Nru muse-2.1.2/muse/widgets/verticalmeter.cpp muse-3.0.2+ds1/muse/widgets/verticalmeter.cpp --- muse-2.1.2/muse/widgets/verticalmeter.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/verticalmeter.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -33,6 +33,7 @@ #include "fastlog.h" #include "mmath.h" #include "utils.h" +#include "muse_math.h" namespace MusEGui { @@ -78,7 +79,7 @@ if(mtype == DBMeter) { - double minScaleLin = pow(10.0, minScale/20.0); + double minScaleLin = muse_db2val(minScale); if((v >= minScaleLin && val != v) || val >= minScaleLin) { val = v; diff -Nru muse-2.1.2/muse/widgets/view.cpp muse-3.0.2+ds1/muse/widgets/view.cpp --- muse-2.1.2/muse/widgets/view.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/view.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -22,6 +22,7 @@ //========================================================= #include "view.h" +#include "gconfig.h" #include #include #include @@ -625,7 +626,7 @@ ///unsigned x = AL::sigmap.bar2tick(bar, 0, 0); unsigned xb = AL::sigmap.bar2tick(bar, 0, 0); int xt = mapx(xb); - p.setPen(Qt::black); + p.setPen(MusEGlobal::config.midiCanvasBarColor); ///p.drawLine(x, y, x, y2); p.drawLine(xt, my, xt, y2); int z, n; @@ -638,7 +639,7 @@ //if (q < 8) // grid too dense if (rmapx(raster) < 8) // grid too dense qq *= 2; - p.setPen(Qt::lightGray); + p.setPen(MusEGlobal::config.midiCanvasBeatColor); if (raster>=4) { ///int xx = x + qq; //int xx = mapx(xb + qq); @@ -654,7 +655,8 @@ } //xx = xxx; } - p.setPen(Qt::gray); + + p.setPen(Qt::darkGray); for (int beat = 1; beat < z; beat++) { ///int xx = AL::sigmap.bar2tick(bar, beat, 0); xx = mapx(AL::sigmap.bar2tick(bar, beat, 0)); diff -Nru muse-2.1.2/muse/widgets/visibletracks.cpp muse-3.0.2+ds1/muse/widgets/visibletracks.cpp --- muse-2.1.2/muse/widgets/visibletracks.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/visibletracks.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -68,7 +68,7 @@ for (unsigned i = 0; i < sizeof(visTrackList)/sizeof(*visTrackList); ++i) { VisibleToolB* t = &visTrackList[i]; - Action* a = new Action(action, i, tr(t->tip).toAscii().data(), true); + Action* a = new Action(action, i, tr(t->tip).toLatin1().data(), true); actions[n] = a; //a->setIconSet(QIcon(**(t->icon))); a->setIcon(QIcon(**(t->icon))); @@ -131,7 +131,8 @@ default: break; } - MusEGlobal::muse->changeConfig(true); // save settings + // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. + MusEGlobal::muse->changeConfig(true); emit visibilityChanged(); } diff -Nru muse-2.1.2/muse/widgets/vst_native_editor.cpp muse-3.0.2+ds1/muse/widgets/vst_native_editor.cpp --- muse-2.1.2/muse/widgets/vst_native_editor.cpp 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/vst_native_editor.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -104,21 +104,17 @@ VstNativeEditor::~VstNativeEditor() { - if(_sif) - { - _sif->dispatch(effEditClose, 0, 0, NULL, 0.0f); - _sif->editorDeleted(); - _sif = NULL; - } + } //--------------------------------------------------------------------- // open //--------------------------------------------------------------------- -void VstNativeEditor::open(MusECore::VstNativeSynthIF* sif) +void VstNativeEditor::open(MusECore::VstNativeSynthIF* sif, MusECore::VstNativePluginWrapper_State *state) { _sif = sif; + _pstate = state; // Start the proper (child) editor... long value = 0; @@ -128,15 +124,26 @@ #endif MusECore::VstRect* pRect; - if(_sif->dispatch(effEditGetRect, 0, 0, &pRect, 0.0f)) + + AEffect *vstPlug = _sif ? _sif->_plugin : _pstate->plugin; + + vstPlug->dispatcher(vstPlug, effEditOpen, 0, value, ptr, 0.0f); + + if(vstPlug->dispatcher(vstPlug, effEditGetRect, 0, 0, &pRect, 0.0f)) { int w = pRect->right - pRect->left; int h = pRect->bottom - pRect->top; if (w > 0 && h > 0) - QWidget::setFixedSize(w, h); + { + QWidget::setMinimumSize(w, h); + if((w != width()) || (h != height())) + { + setFixedSize(w, h); + } + } } - _sif->dispatch(effEditOpen, 0, value, ptr, 0.0f); + //int rv = _sif->dispatch(effEditOpen, 0, value, ptr, 0.0f); //fprintf(stderr, "VstNativeEditor::open effEditOpen returned:%d effEditGetRect rect l:%d r:%d t:%d b:%d\n", rv, pRect->left, pRect->right, pRect->top, pRect->bottom); // REMOVE Tim. @@ -146,18 +153,26 @@ _vstEventProc = getXEventProc(_display, _vstEditor); #endif - if(_sif->track()) + QString windowTitle = "VST plugin editor"; + if(_sif && _sif->track()) { - QString title = _sif->track()->name() + ":" + _sif->pluginLabel(); - setWindowTitle(title); + windowTitle = _sif->track()->name() + ":" + _sif->pluginLabel(); } + else if(_pstate && _pstate->pluginI->track()) + { + windowTitle = _pstate->pluginI->track()->name() + ":" + _pstate->pluginWrapper->_synth->name(); + } + + setWindowTitle(windowTitle); //_sif->editorOpened(); if(!isVisible()) show(); raise(); activateWindow(); - _sif->idleEditor(); + ///_sif->idleEditor(); // REMOVE Tim. Or keep. + + //resizeTimerId = startTimer(500); } #if defined(Q_WS_X11) @@ -205,6 +220,8 @@ if(_sif) _sif->editorOpened(); + if(_pstate) + _pstate->editorOpened(); } //--------------------------------------------------------------------- @@ -213,10 +230,28 @@ void VstNativeEditor::closeEvent(QCloseEvent *pCloseEvent) { - if(_sif) - _sif->editorClosed(); - - QWidget::closeEvent(pCloseEvent); + /*if(resizeTimerId) + { + killTimer(resizeTimerId); + resizeTimerId = 0; + }*/ + if(_sif) + { + _sif->dispatch(effEditClose, 0, 0, NULL, 0.0f); + _sif->editorClosed(); + _sif->editorDeleted(); + _sif = NULL; + } + + if(_pstate) + { + _pstate->plugin->dispatcher(_pstate->plugin, effEditClose, 0, 0, NULL, 0.0f); + _pstate->editorClosed(); + _pstate->editorDeleted(); + _pstate = NULL; + } + + QWidget::closeEvent(pCloseEvent); } //--------------------------------------------------------------------- @@ -235,6 +270,38 @@ #endif } +void VstNativeEditor::resizeEvent(QResizeEvent *pResizeEvent) +{ + //setFixedSize(pResizeEvent->size()); + pResizeEvent->accept(); +} + +void VstNativeEditor::timerEvent(QTimerEvent *event) +{ + if(event->timerId() == resizeTimerId) + { + MusECore::VstRect* pRect; + bool bGotRect = false; + if(_sif) + bGotRect = _sif->dispatch(effEditGetRect, 0, 0, &pRect, 0.0f); + else if(_pstate) + bGotRect = _pstate->plugin->dispatcher(_pstate->plugin, effEditGetRect, 0, 0, &pRect, 0.0f); + if(bGotRect) + { + int w = pRect->right - pRect->left; + int h = pRect->bottom - pRect->top; + if (w > 0 && h > 0) + { + if((w != width()) || (h != height())) + { + setFixedSize(w, h); + } + } + } + + } +} + } // namespace MusEGui #endif // VST_NATIVE_SUPPORT diff -Nru muse-2.1.2/muse/widgets/vst_native_editor.h muse-3.0.2+ds1/muse/widgets/vst_native_editor.h --- muse-2.1.2/muse/widgets/vst_native_editor.h 2013-03-28 15:17:35.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/vst_native_editor.h 2017-12-04 21:01:19.000000000 +0000 @@ -50,6 +50,8 @@ namespace MusECore { class VstNativeSynthIF; +class VstNativePluginWrapper; +class VstNativePluginWrapper_State; } #endif // VST_NATIVE_SUPPORT @@ -70,18 +72,22 @@ #endif MusECore::VstNativeSynthIF* _sif; + MusECore::VstNativePluginWrapper_State* _pstate; + int resizeTimerId; protected: virtual void showEvent(QShowEvent *pShowEvent); virtual void closeEvent(QCloseEvent *pCloseEvent); virtual void moveEvent(QMoveEvent *pMoveEvent); + virtual void resizeEvent(QResizeEvent *pResizeEvent); + virtual void timerEvent(QTimerEvent * event); public: VstNativeEditor(QWidget *parent, Qt::WindowFlags wflags = 0); ~VstNativeEditor(); - void open(MusECore::VstNativeSynthIF* sif); + void open(MusECore::VstNativeSynthIF* sif, MusECore::VstNativePluginWrapper_State *state); //void close(); #if defined(Q_WS_X11) diff -Nru muse-2.1.2/muse/widgets/widget_stack.cpp muse-3.0.2+ds1/muse/widgets/widget_stack.cpp --- muse-2.1.2/muse/widgets/widget_stack.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/widget_stack.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,200 @@ +//========================================================= +// MusE +// Linux Music Editor +// widget_stack.cpp +// (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 "widget_stack.h" +#include "scrollbar.h" + +#include +#include + +namespace MusEGui { + +//--------------------------------------------------------- +// WidgetStack +//--------------------------------------------------------- + +WidgetStack::WidgetStack(QWidget* parent, const char* name, SizeHintMode sizeHintMode) + : QWidget(parent), _sizeHintMode(sizeHintMode) +{ + setObjectName(name); + top = -1; +} + +//--------------------------------------------------------- +// raiseWidget +//--------------------------------------------------------- + +void WidgetStack::raiseWidget(int idx) + { + if (top != -1) { + if (stack[top]) + stack[top]->hide(); + } + top = idx; + if (idx == -1) + return; + int n = stack.size(); + if (idx >= n) + return; + if (stack[idx]) + { + resizeStack(size()); + stack[idx]->show(); + } + } + +//--------------------------------------------------------- +// addWidget +//--------------------------------------------------------- + +void WidgetStack::addWidget(QWidget* w, unsigned int n) + { + if (w) + w->hide(); + if (stack.size() <= n ) + stack.push_back(w); + else + { + stack[n] = w; + resizeStack(size()); + } + } + +QWidget* WidgetStack::getWidget(unsigned int n) + { + if (stack.size() <= n ) + return 0; + return stack[n]; + } + +//--------------------------------------------------------- +// visibleWidget +//--------------------------------------------------------- + +QWidget* WidgetStack::visibleWidget() const + { + if (top != -1) + return stack[top]; + return 0; + } + +//--------------------------------------------------------- +// minimumSizeHint +//--------------------------------------------------------- + +QSize WidgetStack::minimumSizeHint() const + { + if (top == -1) + return (QSize(0, 0)); + + QSize s(0,0); + + // Check if we want only the visible widget... + if(sizeHintMode() == VisibleHint && stack[top]) + { + QSize ss = stack[top]->minimumSizeHint(); + if (!ss.isValid()) + { + //fprintf(stderr, "WidgetStack::minimumSizeHint: minimumSizeHint invalid, getting minimumSize\n"); + ss = stack[top]->minimumSize(); + } + //fprintf(stderr, "WidgetStack::minimumSizeHint w:%d h:%d\n", ss.width(), ss.height()); + return ss; + } + + for (unsigned int i = 0; i < stack.size(); ++i) { + if (stack[i]) { + QSize ss = stack[i]->minimumSizeHint(); + if (!ss.isValid()) + ss = stack[i]->minimumSize(); + s = s.expandedTo(ss); + } + } + + return s; + } + +//--------------------------------------------------------- +// wheelEvent +//--------------------------------------------------------- + +void WidgetStack::wheelEvent(QWheelEvent* ev) + { + emit redirectWheelEvent(ev); + } + +void WidgetStack::resizeStack(const QSize& newSize) +{ + if(QWidget* widget = visibleWidget()) + { + QSize wsz = widget->minimumSizeHint(); + if(!wsz.isValid()) + wsz = widget->minimumSize(); + + QSize sz(newSize); + if(sz.width() < wsz.width()) + sz.setWidth(wsz.width()); + if(sz.height() < wsz.height()) + sz.setHeight(wsz.height()); + + widget->resize(sz); + } +} + +void WidgetStack::resizeEvent(QResizeEvent* e) +{ + e->ignore(); + QWidget::resizeEvent(e); + resizeStack(e->size()); +} + +QSize WidgetStack::sizeHint() const +{ + QSize s(0,0); + // Check if we want only the visible widget... + if(sizeHintMode() == VisibleHint) + { + if(top == -1 || !stack[top]) + return s; + + QSize ss = stack[top]->sizeHint(); + if(ss.isValid()) + return ss; + else + return s; + } + + for(unsigned int i = 0; i < stack.size(); ++i) + { + if(stack[i]) + { + QSize ss = stack[i]->sizeHint(); + if(ss.isValid()) + s = s.expandedTo(ss); + } + } + return s; +} + + +} // namespace MusEGui diff -Nru muse-2.1.2/muse/widgets/widget_stack.h muse-3.0.2+ds1/muse/widgets/widget_stack.h --- muse-2.1.2/muse/widgets/widget_stack.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/muse/widgets/widget_stack.h 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,78 @@ +//========================================================= +// MusE +// Linux Music Editor +// widget_stack.h +// (C) Copyright 1999 Werner Schweer (ws@seh.de) +// (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) +// +// 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 __WIDGET_STACK_H__ +#define __WIDGET_STACK_H__ + +#include + +class QWheelEvent; +class QResizeEvent; + +namespace MusEGui { + +//--------------------------------------------------------- +// WidgetStack +//--------------------------------------------------------- + +class WidgetStack : public QWidget { + Q_OBJECT + public: + // StackHint: Use the maximum of all the stack widgets. + // VisibleHint: Use only the current visible widget. + // If it is invalid, use the StackHint. + enum SizeHintMode { StackHint=0, VisibleHint=1 }; + + private: + SizeHintMode _sizeHintMode; + + std::vector stack; + int top; + + void resizeStack(const QSize&); + + protected: + virtual void wheelEvent(QWheelEvent* e); + virtual void resizeEvent(QResizeEvent* e); + + signals: + void redirectWheelEvent(QWheelEvent*); + + public: + WidgetStack(QWidget* parent = 0, const char* name = 0, SizeHintMode sizeHintMode = VisibleHint); + void raiseWidget(int idx); + void addWidget(QWidget* w, unsigned int idx); + QWidget* getWidget(unsigned int idx); + QWidget* visibleWidget() const; + int curIdx() const { return top; } + virtual QSize minimumSizeHint() const; + SizeHintMode sizeHintMode() const { return _sizeHintMode; } + void setSizeHintMode(SizeHintMode mode) { _sizeHintMode = mode; update(); } + + QSize sizeHint() const; + + }; + +} // namespace MusEGui + +#endif diff -Nru muse-2.1.2/muse/xml.cpp muse-3.0.2+ds1/muse/xml.cpp --- muse-2.1.2/muse/xml.cpp 2013-03-28 15:17:41.000000000 +0000 +++ muse-3.0.2+ds1/muse/xml.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -35,12 +35,15 @@ namespace MusECore { +const int Xml::_latestMajorVersion = 3; // Latest known songfile major version (as of this release) +const int Xml::_latestMinorVersion = 1; // Latest known songfile minor version (as of this release) + //--------------------------------------------------------- // Note: // this code is a Q/D hack for reading/parsing and // writing XML-Files // - can only handle the XML subset used by MusE -// - may not handle misformed XML (eg. when manually +// - may not handle malformed XML (eg. when manually // editing MusE output) //--------------------------------------------------------- @@ -165,7 +168,7 @@ else if (strcmp(entity, "gt") == 0) c = '>'; else if (strcmp(entity, "apos") == 0) - c = '\\'; + c = '\''; else entity[k] = c; break; @@ -219,8 +222,10 @@ bool endFlag = false; nextc(); if (c == EOF) { - printf("unexpected EOF reading xml file at level %d, line %d, <%s><%s><%s>\n", - level, _line, _tag.toLatin1().constData(), _s1.toLatin1().constData(), _s2.toLatin1().constData()); + //if (level > 0 || MusEGlobal::debugMsg) + if (level > 0) + printf("WARNING: unexpected EOF reading xml file at level %d, line %d, <%s><%s><%s>\n", + level, _line, _tag.toLatin1().constData(), _s1.toLatin1().constData(), _s2.toLatin1().constData()); return level == 0 ? End : Error; } @@ -400,7 +405,7 @@ else if (strcmp(name, "gt") == 0) c = '>'; else if (strcmp(name, "apos") == 0) - c = '\\'; + c = '\''; else if (strcmp(name, "quot") == 0) c = '"'; else if (strcmp(name, "amp") == 0) @@ -660,7 +665,7 @@ case '&': fprintf(f, "&"); break; case '<': fprintf(f, "<"); break; case '>': fprintf(f, ">"); break; - case '\\': fprintf(f, "'"); break; + case '\'': fprintf(f, "'"); break; case '"': fprintf(f, """); break; default: fputc(*val, f); break; } @@ -681,6 +686,11 @@ name, color.red(), color.green(), color.blue(), name); } +void Xml::colorTag(int level, const QString& name, const QColor& color) +{ + colorTag(level, name.toLocal8Bit().constData(), color); +} + //--------------------------------------------------------- // geometryTag //--------------------------------------------------------- @@ -707,9 +717,14 @@ void Xml::strTag(int level, const char* name, const QString& val) { - strTag(level, name, val.toLatin1().constData()); + strTag(level, name, val.toLocal8Bit().constData()); } +void Xml::strTag(int level, const QString& name, const QString& val) +{ + strTag(level, name.toLocal8Bit().constData(), val.toLocal8Bit().constData()); +} + //--------------------------------------------------------- // Xml::skip //--------------------------------------------------------- diff -Nru muse-2.1.2/muse/xml.h muse-3.0.2+ds1/muse/xml.h --- muse-2.1.2/muse/xml.h 2013-03-28 15:17:39.000000000 +0000 +++ muse-3.0.2+ds1/muse/xml.h 2017-12-04 21:01:19.000000000 +0000 @@ -47,8 +47,16 @@ int level; bool inTag; bool inComment; - int _minorVersion; - int _majorVersion; + // NOTICE: Whenever the MusE song file structure changes - even for a small reason, bump these up! + // To preserve user data please try to add song-loading converters to accompany any changes so no data is lost. + // The user will be automatically informed and warned (via GUI) of loading an OLD or FUTURE song file, + // and that the song might be converted if saved! + // TODO: Always make this equal the MusE version? + // But how do we bump up the MusE version safely before releasing, for testing/coding purposes? + static const int _latestMinorVersion; // Latest known songfile minor version (as of this release) + static const int _latestMajorVersion; // Latest known songfile major version (as of this release) + int _minorVersion; // Currently loaded songfile minor version + int _majorVersion; // Currently loaded songfile major version int c; // current char char lbuffer[512]; @@ -64,8 +72,13 @@ public: enum Token {Error, TagStart, TagEnd, Flag, Proc, Text, Attribut, End}; + int latestMajorVersion() const { return _latestMajorVersion; } + int latestMinorVersion() const { return _latestMinorVersion; } int majorVersion() const { return _majorVersion; } int minorVersion() const { return _minorVersion; } + bool isVersionEqualToLatest() const { return _majorVersion == _latestMajorVersion && _minorVersion == _latestMinorVersion; } + bool isVersionLessThanLatest() const { return _majorVersion < _latestMajorVersion || _minorVersion < _latestMinorVersion; } + bool isVersionGreaterThanLatest() const { return _majorVersion > _latestMajorVersion || _minorVersion > _latestMinorVersion; } void setVersion(int maj, int min) { _minorVersion = min; _majorVersion = maj; @@ -99,7 +112,9 @@ void floatTag(int level, const char* const name, float val); void strTag(int level, const char* const name, const char* val); void strTag(int level, const char* const name, const QString& s); + void strTag(int level, const QString& name, const QString& val); void colorTag(int level, const char* name, const QColor& color); + void colorTag(int level, const QString& name, const QColor& color); void geometryTag(int level, const char* name, const QWidget* g); void qrectTag(int level, const char* name, const QRect& r); static QString xmlString(const QString&); diff -Nru muse-2.1.2/muse.pro muse-3.0.2+ds1/muse.pro --- muse-2.1.2/muse.pro 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/muse.pro 2017-12-04 21:01:18.000000000 +0000 @@ -91,6 +91,8 @@ ./muse/event.h \ ./muse/audioprefetch.h \ ./muse/dssihost.h \ + ./muse/lv2host.h \ + ./muse/lv2extui.h \ ./muse/fastlog.h \ ./muse/pos.h \ ./muse/waveedit/waveview.h \ diff -Nru muse-2.1.2/packaging/CMakeLists.txt muse-3.0.2+ds1/packaging/CMakeLists.txt --- muse-2.1.2/packaging/CMakeLists.txt 2013-03-28 15:15:42.000000000 +0000 +++ muse-3.0.2+ds1/packaging/CMakeLists.txt 2018-01-24 21:54:26.000000000 +0000 @@ -30,7 +30,7 @@ ) # -# install the desktop and mime files +# install the appdata, desktop and mime files # install( FILES muse_icon.png DESTINATION ${SHARE_INSTALL_PREFIX}/icons/hicolor/64x64/apps/ @@ -41,4 +41,7 @@ install( FILES muse.xml DESTINATION ${SHARE_INSTALL_PREFIX}/mime/packages/ ) +install( FILES muse.appdata.xml + DESTINATION ${SHARE_INSTALL_PREFIX}/metainfo/ + ) diff -Nru muse-2.1.2/packaging/muse.appdata.xml muse-3.0.2+ds1/packaging/muse.appdata.xml --- muse-2.1.2/packaging/muse.appdata.xml 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/packaging/muse.appdata.xml 2018-01-24 21:54:26.000000000 +0000 @@ -0,0 +1,34 @@ + + + + muse.desktop + CC0-1.0 + GPL-2.0+ + MusE + The open source sequencer + + +

    + MusE is a MIDI/Audio sequencer with recording and editing capabilities. It can + perform audio effects like chorus/flanger in real-time via LASH and it supports + Jack and ALSA interfaces. MusE aims to be a complete multitrack virtual studio + for Linux. +

    +
    + + + http://www.muse-sequencer.org/pic/mypics/muse2.1/muse2.1_song.png + + + http://www.muse-sequencer.org/pic/mypics/muse2.0/main_window_with_midi_editor.png + + + http://www.muse-sequencer.org/pic/mypics/muse2.0/main_window_with_tracks.png + + + http://www.muse-sequencer.org/pic/mypics/muse2.0/mixer.png + + + http://muse-sequencer.org/ + lmuse-user_at_lists.sourceforge.net +
    diff -Nru muse-2.1.2/packaging/muse.desktop.in muse-3.0.2+ds1/packaging/muse.desktop.in --- muse-2.1.2/packaging/muse.desktop.in 2013-03-28 15:15:42.000000000 +0000 +++ muse-3.0.2+ds1/packaging/muse.desktop.in 2017-12-04 21:01:19.000000000 +0000 @@ -1,7 +1,9 @@ [Desktop Entry] Name=MusE GenericName=Audio/MIDI Sequencer +GenericName[fr]=Séquenceur audio/MIDI Comment=Midi Music Editor +Comment[fr]=Éditeur MIDI de musique Icon=muse_icon Type=Application Exec=${MusE_EXEC_NAME} diff -Nru muse-2.1.2/pch.txt muse-3.0.2+ds1/pch.txt --- muse-2.1.2/pch.txt 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/pch.txt 2017-12-04 21:01:19.000000000 +0000 @@ -18,20 +18,12 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #============================================================================= -add_custom_command( - OUTPUT ${PROJECT_BINARY_DIR}/all.h - COMMAND cp ${PROJECT_SOURCE_DIR}/all.h ${PROJECT_BINARY_DIR}/all.h - DEPENDS ${PROJECT_SOURCE_DIR}/all.h - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - ) +file (COPY ${PROJECT_SOURCE_DIR}/all.h DESTINATION ${PROJECT_BINARY_DIR}) +file (RENAME ${PROJECT_BINARY_DIR}/all.h ${PROJECT_BINARY_DIR}/all-pic.h) -add_custom_command( - OUTPUT ${PROJECT_BINARY_DIR}/all-pic.h - COMMAND cp ${PROJECT_SOURCE_DIR}/all.h ${PROJECT_BINARY_DIR}/all-pic.h - DEPENDS ${PROJECT_SOURCE_DIR}/all.h - WORKING_DIRECTORY ${PROJECT_BINARY_DIR} - ) +file (COPY ${PROJECT_SOURCE_DIR}/all.h DESTINATION ${PROJECT_BINARY_DIR}) + # Disable precompilation of this header file for now since it is empty - Orcan 2012-05-20 if(0) add_custom_command( diff -Nru muse-2.1.2/plugins/freeverb/readme.txt muse-3.0.2+ds1/plugins/freeverb/readme.txt --- muse-2.1.2/plugins/freeverb/readme.txt 2013-03-28 15:17:17.000000000 +0000 +++ muse-3.0.2+ds1/plugins/freeverb/readme.txt 2018-01-26 21:59:38.000000000 +0000 @@ -27,7 +27,7 @@ of instances of the filters, with some features to control the filters at a macro level. You will need to link these classes into another program that interfaces with them. The files in the components drawer are completely - independant, and can be built without dependancies on anything else. + independent, and can be built without dependencies on anything else. Because of the simple interface, it should be possible to interface these files to any system - VST, DirectX, anything - without changing them AT ALL. @@ -37,7 +37,7 @@ It was built on a PC but may compile properly for the Macintosh with no problems. I don't know - I don't have a Macintosh. If you've figured out how to compile the examples in the Steinberg VST - Development Kit, then you should easilly figure out how to bring the + Development Kit, then you should easily figure out how to bring the files into a project and get it working in a few minutes. It should be very simple. @@ -79,7 +79,7 @@ I mean. Coding conventions typically promote issues that are irrelevant up to the status of appearing supremely important. It may have helped back people in the days when compilers where somewhat feeble in their type-safety, -but not in the new millenium with advanced C++ compilers. +but not in the new millennium with advanced C++ compilers. Imagine if we rewrote the English language to conform to coding conventions. After all, The arguments should be just as valid for the English language as diff -Nru muse-2.1.2/README muse-3.0.2+ds1/README --- muse-2.1.2/README 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/README 2018-01-26 21:59:38.000000000 +0000 @@ -5,9 +5,40 @@ Welcome to MusE, the open source MIDI/Audio sequencer. MusE is distributed under the GNU General Public License (GPL). -Please check out the file COPYING in this directory for more +Please check out the file COPYING in the muse3/ directory for more details. + + ============================= + Features: + ============================= + + MIDI sequencing: + - Realtime record/playback + - Midi file import/export + - Input filter + - Support for internal softsynth plugins using + LV2, DSSI, VST, LinuxVST and MESS + - much more + + Audio sequencing: + - Realtime Record/Playback several mono/stereo inputs/outputs + - Graphical editing of automation + - Support for LADSPA, LV2, DSSI, VST, LinuxVST plugins + + Technologies supported: + - LASH - for session control with external LASH enabled applications + - JACK - The Jack Audio Connection Kit can be used for audio and midi + - RtAudio - basic audio input/output for Pulse, ALSA and OSS + - ALSA midi + - Plugin formats: LV2, DSSI, VST, LinuxVST, MESS + - Support Inst(rument) Patch Library for enhanced fluidsynth features. + - FLAM plugin guis. + - Built on Qt5 + + + + ,-----------------------------------------------------------------. | NOTICE | |-----------------------------------------------------------------| @@ -23,9 +54,11 @@ - CMake >= 2.4 http:/www.cmake.org/HTML/Download.html - - QT: Qt >= 4.2.0 (development files) - http://qt.nokia.com/products/ + - Qt: Qt >= 5.1.0 (development files) + http://qt-project.org/ MusE does _not_ compile with older versions + For ubuntu packages can be installed via: + sudo apt-get install qt5-default qttools5-dev qttools5-dev-tools - gcc >= 4.x.x or clang >= 2.9 http://gcc.gnu.org/ @@ -40,25 +73,58 @@ - JACK >= 0.103 (development files) http://jackaudio.org/ - - libuuid (development files) - e2fsprogs package http://e2fsprogs.sourceforge.net/ - Some distros may include it in another package such as 'libuuid', or offer a choice. - - LADSPA (development file ladspa.h) www.ladspa.org + ============================= Optional: ============================= + + - ALSA >= 0.9.0 Advanced Linux Sound Architecture (development files) + http://www.alsa-project.org/ - fluidsynth >= 1.0.3 (formerly known as iiwusynth) (development files) http://sourceforge.net/apps/trac/fluidsynth/ - - liblo Lightweight OSC (Open Sound Control) (development files) - http://liblo.sourceforge.net/ + - libinstpatch >= 1.0 Instrument Patch Library (development files) + http://www.swamiproject.org/ + + - RtAudio >=5.0 audio input/output support. + https://www.music.mcgill.ca/~gary/rtaudio/ + + - LV2 (LADSPA Version 2) plugin support: + + MusE contains built-in versions of these LV2 support libraries: + lv2, lilv, serd, sord and sratom, from: + http://drobilla.net/software + + But if it finds system-installed versions (development files) + greater than or equal to all of the built-in versions, + the system-installed versions will be used. + + The cmake option ENABLE_LV2_SUPPLIED (default off) + forces built-in versions to be used. + + (The built-in versions may be slightly modified from their + corresponding official versions. See muse/lv2Support/ChangeLog.) + + - LV2 Gtk2 User Interface support: + + Some LV2 plugins may provide a Gtk2 based graphical User Interface. + To view them, the following is required (development files): + gtkmm-2 + http://www.gtkmm.org + gtk+-2 + http://www.gtk.org + - DSSI Disposable Soft Synth Interface (development files) http://dssi.sourceforge.net/ - (Both recommended - DSSI alone will have no native GUIs, OSC alone does nothing, for now.) + + - liblo Lightweight OSC (Open Sound Control) (development files) + http://liblo.sourceforge.net/ + + (Both recommended - DSSI can use OSC, OSC alone does nothing, for now.) - dssi-vst Support for DSSI vst plugins http://www.breakfastquay.com/dssi-vst/ @@ -73,22 +139,45 @@ Python support is required for some experimental features. Currently it's NOT recommended to build with python support. + - PyQt4 is used by some of the bundled midi scripts + + Quick instructions for Ubuntu and similar distributions: + + + ================================= + Building MusE under Ubuntu: + ================================= + + 1. Open terminal and type (either on one line or with several 'sudo apt-get install 2.6.x - A modern, standard destop kernel might suit your needs. - However, for the best performance a so-called 'low latency' or 'realtime' - kernel may be required. Check your distro's packages for availability. + A modern, standard destop kernel should suit virtually all needs. + For even stricter realtime performance a so-called 'low latency' + or 'realtime' kernel may be available. Check your distro's packages + for availability. - create an "audio" group if it does not already exists and put yourself into this group @@ -169,18 +259,7 @@ @audio - rtprio 95 @audio - memlock unlimited - - make sure you can acces the realtime clock (RTC) - chmod 660 /dev/rtc - chgrp audio /dev/rtc - - - make sure MusE can set the rtc clock: - echo 8192 > /proc/sys/dev/rtc/max-user-freq - inspect with: - cat /proc/sys/dev/rtc/max-user-freq - - - some distros do not load the alsa sequencer module by default. - if necessary, load the alsa sequencer module with: - /sbin/modprobe snd-seq + Some distros have a graphical menu or tool which does this for you. - Start jack, typically by using the qjackctl application. MusE can also be run without Jack. MusE will use a dummy audio @@ -190,9 +269,9 @@ Running: ------------------ start MusE by typing: - muse2 + muse3 - Some are: + Some are (complete list is printed by running muse3 -h): -h help -v print version -d debug mode: no threads, no RT @@ -203,6 +282,7 @@ -M debug mode: trace midi Output -s debug mode: trace sync -a no audio + -A Force inclusion of ALSA midi even if using Jack -P n set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) -Y n force midi real time priority to n (default: audio driver prio +2) @@ -216,14 +296,73 @@ same user id) + Startup troubleshooting tips: + ----------------------------- + + - Some rare distros might not load the alsa sequencer module by default. + If necessary, load the alsa sequencer module with: + /sbin/modprobe snd-seq + + - Timer accuracy for ALSA support: + + If ALSA support is enabled, a reliable source of timing is + desired for playback of midi notes. + + By default, the timer is attempted to run at 1024 Hz (ticks/second). + + MusE will try the Real Time Clock (RTC) first. + If that fails it will try ALSA timers instead - typically the + ALSA High Resolution (HR) timer or the ALSA system timer. + + The RTC is recommended, with sufficient permissions, for best accuracy. + + To ensure you have permissions to use the RTC, on modern systems + using udev rules, create this file if it does not exist: + "/etc/udev/rules.d/40-timer-permissions.rules" + and add the following lines to the file: + KERNEL=="rtc0",GROUP="audio" + KERNEL=="hpet",GROUP="audio" + and, as stated in "Recommended setup" above, ensure you are + part of the audio group. + + (The hpet is not supported, but that line can help other applications.) + + It is possible to use the ALSA system timer instead of the RTC, + but it usually requires a kernel built with a 1000 Hz system timer + (low-latency, realtime, or custom kernel as in "Recommended setup" above). + With most desktop kernels, the system timer is limited to 250 Hz, + while the HR timer is limited to 1000 Hz. + On such distros, without modification, MusE should end up picking + the 1000 Hz HR timer. + + But if you set your RTC as above, you can run even higher rates. + See the Global Settings dialog for user-adjustable high rates. + + Run MusE in a terminal to see what it picks. -D option gives more info. + + + (The following is old information! But may work for those without udev.) + Make sure you can access the realtime clock (RTC) + chmod 660 /dev/rtc + chgrp audio /dev/rtc + Make sure MusE can set the rtc clock: + echo 8192 > /proc/sys/dev/rtc/max-user-freq + inspect with: + cat /proc/sys/dev/rtc/max-user-freq + ============================= known bugs ============================= - - not so many: only some usability quirks we're working on currently - - automation recording displays odd things in the arranger - while recording; the display is corrected once recording is - complete. + The odd wierdness happens and some things may not be completely implemeneted + though we hope it is as stable for you as it is for us! + + If you differ in this opinion we are grateful for all reported issues. + + See the bug tracker for a better view of issues + https://github.com/muse-sequencer/muse/issues + + ==================================================================== Let us know whether MusE works for you !!! diff -Nru muse-2.1.2/README.de muse-3.0.2+ds1/README.de --- muse-2.1.2/README.de 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/README.de 2017-12-04 21:01:18.000000000 +0000 @@ -6,7 +6,7 @@ dies ist MusE, der Open Source MIDI/Audio Sequencer. MusE steht unter der GNU General Public Licence (GPL). -Für Einzelheiten siehe COPYING. +Für Einzelheiten siehe muse3/COPYING. ,-----------------------------------------------------------------. | HINWEIS | @@ -24,8 +24,8 @@ - CMake >= 2.4 http:/www.cmake.org/HTML/Download.html - - qt 4.2.0 oder neuer - ftp://ftp.trolltech.com/qt/source + - qt 5.1.0 oder neuer + http://qt-project.org/ MsE kompiliert nicht mit älteren Versionen! - gcc >= 4.x.x oder clang >= 2.9 @@ -65,7 +65,7 @@ Kompilierung ============================= - - download source von http://lmuse.sourceforge.net/ + - download source von http://muse-sequencer.org/ - untar source: tar xvofj muse-current-tar.bz2 - konfigurieren: @@ -78,6 +78,6 @@ ==================================================================== Aktuelle(re) Infos gibt es auf der MusE Homepage -http://lmuse.sourceforge.net/ +http://muse-sequencer.org/ und in der englischen README diff -Nru muse-2.1.2/README.developer muse-3.0.2+ds1/README.developer --- muse-2.1.2/README.developer 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/README.developer 2018-01-30 20:54:54.000000000 +0000 @@ -0,0 +1,57 @@ +MusE is really not big on making developers do it the MusE way. +For good and bad (probably mostly bad) it's a bit of happy anarchy out there. +But whatever, it's a labor of love. + +Anyway, maybe some things should be a bit more clearly defined so I started this +document with some pointers for best practices and procedures. + +* STYLE * +whatever floats your boat. Okay, maybe try to keep it atleast somewhat consistent :) + +* TOOLS * +MusE is highly dependent on Qt, mostly this is a good thing, especially as we want to +make MusE even more portable. So, when implementing stuff that requires some support +library, do check if Qt can provide the necessary functionality. Many times it can. + +* DEVELOPMENT * + + +* RELEASE PROCEDURE * +When making a release there are some steps that should always be done to make the +packaging consistent and traceable. + +1. make sure that version info is updated. + CMakeLists.txt:SET(MusE_VERSION "3.0.0pre1") + CMakeLists.txt:SET(MusE_VERSION_FULL "3.0.0pre1") + +2. add a release-line to the ChangeLog (search for old ones for some format hints) + +3. commit everything to git + +4. now create the tarball from the build dir (could be a good idea to create a new build dir) + $ make clean (otherwise the tarball will way too big) + $ make package_source + feel free to ctrl-c out of this as soon as it is finished + +5. verify that the built package has the right name and can be built + + NOTE: if you are making a point release (e.g. 3.0.1) the file name is based of the path so + the package will be called muse-3.0.tar.gz while it really should be muse-3.0.1. Easiest + way to fix this for now is to untar the package, rename the folder (in this case) from + muse-3.0 to muse-3.0.1 and tar it back into muse-3.0.1.tar.gz. + +6. make a tag in git for this particular checkin when you are satisfied the release is correct + 'git tag -l' lists all of the previous tags and gives a hint as to the format to use. + Use git tag -a to create an annotated tag. + To push the tag to master: git push --tags + +7. create a releasenote + +8. upload both releasenote and tarball to the sourceforge page (or whatever is in fashion when + it's time) + +9. Update the frontpage and news page at muse-sequencer.org (the front page requires access + to the html pages) + +10. send out a release mail + diff -Nru muse-2.1.2/README.effects-rack muse-3.0.2+ds1/README.effects-rack --- muse-2.1.2/README.effects-rack 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/README.effects-rack 2018-01-26 21:59:38.000000000 +0000 @@ -5,7 +5,7 @@ All audio track types (Input, Output, Group, Wave, Synth, and Aux) have an effects rack into which audio plugins can be inserted in a chain. -Currently each rack can accomodate up to four plugins. +Currently each rack can accommodate up to four plugins. MusE currently supports LADSPA plugins and DSSI effects plugins. diff -Nru muse-2.1.2/README.instruments muse-3.0.2+ds1/README.instruments --- muse-2.1.2/README.instruments 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/README.instruments 2017-12-04 21:01:18.000000000 +0000 @@ -1,15 +1,31 @@ ================================================ MusE loadable Midi Instrument Definitions - (valid from around MusE 0.8.1, Edited by Tim NOV.11.2012, at MusE 2.1rc1) ================================================ - File Extension ".idf" - - searched in Subdirectory "instruments" - (/usr/share/muse/instruments on my system) + - Searched in "instruments" in 'share' installation subdirectory. - Can be viewed, created or edited with menu 'Midi -> Edit Instrument' + - In the Instrument Editor, when an existing instrument is modified + and saved, a COPY of that instrument is saved in the user's + ~/.config/MusE directory, and it is used instead. This preserves + original instruments while allowing modifications. All found instrument definitions are presented by MusE - in Config->MidiPorts in Pulldown in column "Instrument". + in Settings->Config->MidiPorts in Pulldown in column "Instrument". + They can also be selected directly from within a midi mixer strip. + + You choose an appropriate Instrument for the playback device being used. + For example if playing back to a Yamaha S-90 keyboard, choose Yamaha S-90. + If the Yamaha model is not listed, it may be a Yamaha XG compatible device + so try the more general XG. Similarly there is a more general Roland SG. + More specific Instrument models are defined as well. + When in doubt, or if all else does not seem to work, try GM (General Midi), + it is always a safe bet with most devices, and is the default Instrument. + + As of MusE-2.1 or so, Drum Maps can be integrated into the .idf file. + The Instrument Editor has a facility for editing them. + See the GM2, GS, or XG instruments for examples. + TODO: Formal specs. ------------------------------------------------- Example of Instrument Definition File: @@ -18,9 +34,21 @@ + + + + + Switch General Midi mode on + 7e 7f 09 01 + + + - ...MusE event list which initializes instrument + + 7e 7f 09 01 + + @@ -52,7 +80,7 @@ ... - (2) An "Instrument Definition File" can define more than on + (2) An "Instrument Definition File" can define more than one Instrument, but it is recommended to use one file for one instrument @@ -82,11 +110,12 @@ From version 2.1: ----------------- There is no 'mode' parameter. The selection of GM, GS, or XG instruments - or other instruments with an sysex 'Init' section, determines what 'mode' + or other instruments with a sysex 'Init' section, determines what 'mode' the device is in. (4) example for MusE event (Sysex "XG-On"): + 43 10 4c 00 00 7e 00 @@ -125,7 +154,7 @@ h: controller number MSB. Default 0 if omitted. l: controller number LSB. Default 0 if omitted. For per-pitch controllers, 'l' can also be "pitch" indicating each 'note' or - 'drum sound' has its own controller. Some devices such as XG support this. + 'drum sound' has its own controller. Some devices such as XG or GM2 support this. min: minimum value for controller max: maximum value for controller @@ -146,4 +175,4 @@ Here is a per-pitch drum controller example from the XG instrument definition file: - \ No newline at end of file + diff -Nru muse-2.1.2/README.svn-branch muse-3.0.2+ds1/README.svn-branch --- muse-2.1.2/README.svn-branch 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/README.svn-branch 1970-01-01 00:00:00.000000000 +0000 @@ -1,125 +0,0 @@ -Branches are handy for developing larger features (especially if you -temporarily break muse and then fix it again). You might want to ask -why you shouldn't simply develop in your local working copy, and then -commit a huge chunk. Well, this has multiple reasons: - o with branches, you'll have a history, because there are many small - commits. this makes bisecting for finding a bug possible. - o when you develop your feature publicly, others can check out half-done - versions, and already test the one half. they also could fix bugs. - o another advantage of keeping it public is: others can see whether you - may exclude some use case and inform you about that in time. otherwise - you'd spend lots of work in a design which was obsolete from the - beginning. - o and it shows that there's something going on :) - -also, branching makes "feature freezes" easier, for release planning. - -General note: ^/trunk means [url of the repo]/trunk. when you're inside -a working copy, svn understands the ^/trunk notation. -i assume you're inside some working copy - -whenever merging, make sure you're in the correct directory! - -CREATING A BRANCH - the following command creates a branch called yourbranch in the branches - directory, which is built upon the current (NOT the checked out!) trunk - revision: - - svn copy ^/trunk ^/branches/yourbranch - - svn copy does a "light copy", that is, as long as you don't change files, - they don't occupy any disk space. - -USING THE BRANCH - you might want to checkout every branch relevant to you into another local - copy. believe me, it makes life easier. alternatively, svn switch is your - friend. - just develop inside the working copy, then commit. - -MERGING WITH THE PARENT BRANCH (in my example: the trunk) - from time to time, you want to update your branch to represent the - current trunk plus your changes (and not an ancient trunk plus your - changes). to be safe, only merge with the parent branch, and only - merge in one direction (usually from trunk into your branch), unless - you know what you're doing. if you're reading and not skimming this, - you're probably NOT knowing. svn help and google are your friends. - - be in your branch'es working directory root (the dir which is containing - all the files/dirs also trunk (the parent) is containing as well. - - svn merge ^/trunk --accept postpone - - does the job for you. there might be conflicts, when both in your branch - and in trunk some file has been changes at a similar location. svn by - default asks you what to do then, which is annoying. --accept postpone - turns this off, and gives you a summary at the end of the merge. - - If There Were Conflicts: - if any file in "svn status"'s output has a C in front of it, there are - conflicts. open the file in your editor, and look for markers like - "<<<<<", "=====" and ">>>>>". these show what code is in the trunk - (between <<<< and ====), and what code is in your branch (between - ==== and >>>>) (or vice versa. svn tells you). - you have to make it work again and save the file. - - with "svn resolved FILENAME" or "svn resolved -R some/directory" you - mark the conflicts for FILENAME or all files below some/directory as - solved. - - Another word about conflicts: there may be conflicts, even if svn doesn't - note them. ALWAYS recompile the merged code and test it. - - if done, you can commit the merge normally using "svn commit" - -PUTTING YOUR WORK BACK INTO THE PARENT BRANCH (in my example: trunk) - do a final merge from your parent branch into your branch. compile and - test. - then there are several ways to proceed: - o use svn merge --reintegrate, which doesn't work with the old repo - version muse is using :( - o go into the trunk (or the parent branch'es directory), and issue - svn merge ^/branches/yourbranch --accept theirs-full - the problem with the merge is, that every previous merge from trunk - into your branch will be applied a second time, which doesn't work. - --accept theirs-full will basically use the files in your branch. - you might want to verify with diff: - diff -R /path/to/local/trunk /path/to/local/yourbranch - there should be no differences. - - commit that to trunk: svn commit - - then, "fake-merge" trunk into your branch again. otherwise, with the - next merge from trunk into your branch, we would have the duplicate - changes problem again. if you're _SURE_ that you aren't using the - branch any more, you can leave this step out. - - svn merge ^/trunk ^/branches/yourbranch --record-only - svn commit - - - this solution is a bit hackish :( but it works - - -NOTES FOR RELEASE BRANCHES - after creating the release branch, ALL commits which are fixing bugs - must go into the release branch. ALL commits which are adding features - must go into trunk or other branches. - the team should focus on fixing bugs in the release branch. - to get the fixes into the trunk, from time to time run: - - svn merge ^/branches/releasebranch ^/trunk - svn commit (in trunk's local copy) - - when releasing the release branch, merge it into the trunk a last time, - and then never touch the release branch again. - for the next release, create a new one. - -TAGGING - when there's any reason for tagging a revision, simply do - svn copy ^/whatever ^/tags/yourtagname - read the svn manual for details - -GETTING HELP: - svn help (usage notes, short explanations) - google (everything) - the svn book (->google) (long explanations) diff -Nru muse-2.1.2/README.translate muse-3.0.2+ds1/README.translate --- muse-2.1.2/README.translate 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/README.translate 2017-12-04 21:01:18.000000000 +0000 @@ -54,9 +54,9 @@ If your system locale is set to the same language as your translation, simply start MusE: - muse2 + muse3 Otherwise, start MusE with the desired locale using the -l flag - muse2 -l fr + muse3 -l fr diff -Nru muse-2.1.2/README.usage muse-3.0.2+ds1/README.usage --- muse-2.1.2/README.usage 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/README.usage 2017-12-04 21:01:18.000000000 +0000 @@ -2,7 +2,7 @@ Here is some updated info about general editing operations and keys/buttons in MusE as of the version 2.1.2 massive changes by Tim: -This info should be in the doc tex file. +!! This info should be moved into the doc tex file. !! See also the README.shortcuts file. =============================================== diff -Nru muse-2.1.2/README.vstsdk muse-3.0.2+ds1/README.vstsdk --- muse-2.1.2/README.vstsdk 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/README.vstsdk 2018-01-26 21:59:38.000000000 +0000 @@ -0,0 +1,24 @@ +============================================================= + Short instructions for compiling MusE + with native Steinberg VST SDK +============================================================= + +By default MusE utilizes the VESTIGE compatibility headers for VST support, it is however +possible to compile with Steinbergs SDK for some possible improvements (vst chunks has +been implemented so I actually don't know if there is any reason to use the VST SDK anymore) + +To fulfill the requirements for this follow these steps: +1. download and extract vstsdk (last it was seen here: http://www.steinberg.net/en/company/developers.html) +2. find the folder containing aeffectx.h (usually it's under /pluginterfaces/vst2.x/ +3. Edit the aeffect.h and change +#define VST_2_4_EXTENSIONS 1 +to +#define VST_2_4_EXTENSIONS 0 +4. Configure MusE to disable VESTIGE and point to the SDK HEADERS, for instance with adding these + arguments to the cmake commandline: + cmake -DENABLE_VST_VESTIGE=OFF -DVST_HEADER_PATH=/pluginterfaces/vst2.x/ + Note that by default the sdk extracts into a folder with spaces, this may work badly, + safest to rename the path without spaces. +5. run cmake, make and make install as you usually would. + + diff -Nru muse-2.1.2/resources.qrc muse-3.0.2+ds1/resources.qrc --- muse-2.1.2/resources.qrc 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/resources.qrc 2018-01-06 20:31:35.000000000 +0000 @@ -0,0 +1,50 @@ + + + + svg/routing_input.svg + svg/routing_output.svg + svg/routing_input_unconnected.svg + svg/routing_output_unconnected.svg + + svg/headphones_off.svg + svg/headphones_on.svg + + svg/mute_off.svg + svg/mute_on.svg + svg/mute_on_X.svg + svg/mute_proxy_on.svg + svg/mute_and_proxy_on.svg + + svg/track_off.svg + svg/track_on.svg + + svg/stereo_off.svg + svg/stereo_on.svg + + svg/pre_fader_off.svg + svg/pre_fader_on.svg + + svg/monitor_off_default_col.svg + svg/monitor_off.svg + svg/monitor_on.svg + + svg/rec_arm_off.svg + svg/rec_arm_off_default_col.svg + svg/rec_arm_on.svg + + svg/solo_spotlight_off.svg + svg/solo_spotlight_on.svg + svg/solo_spotlight_on_alone.svg + svg/solo_proxy_spotlight_on.svg + svg/solo_proxy_spotlight_on_alone.svg + svg/solo_and_proxy_spotlight_on.svg + + svg/stop.svg + svg/play_off.svg + svg/play_on.svg + svg/fast_forward.svg + svg/rewind.svg + svg/rewind_to_start.svg + + + diff -Nru muse-2.1.2/SECURITY muse-3.0.2+ds1/SECURITY --- muse-2.1.2/SECURITY 2013-03-28 15:17:24.000000000 +0000 +++ muse-3.0.2+ds1/SECURITY 2018-01-26 21:59:38.000000000 +0000 @@ -1,3 +1,12 @@ +========================== +DEC 16 2016: +SOME OF THIS INFORMATION IS SEVERELY OBSOLETE. +IT IS PROVIDED FOR REFERENCE. +MODERN SYSTEMS HAVE NO TROUBLE RUNNING MUSE. +SEE THE README FILE FOR MOST OF WHAT YOU NEED TO KNOW. +========================== + + MusE Security Information ========================= @@ -141,7 +150,7 @@ What is givertcap and how do I use it ? --------------------------------------- -"givertcap" (give real-time capabilites) is a small wrapper written by Tommi +"givertcap" (give real-time capabilities) is a small wrapper written by Tommi Ilmonen. When enabled, it is executed by MusE and gives to it just the capabilities needed to set the timer and get real-time scheduling, but not the full set of @@ -186,7 +195,7 @@ # chmod 755 muse . -For more information about givertcap and kernel capabilites, see +For more information about givertcap and kernel capabilities, see http://www.tml.hut.fi/~tilmonen/givertcap/ and http://ftp.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.4/capfaq-0.2. @@ -214,7 +223,7 @@ An alternative approach, using a kernel module: http://arctrix.com/nas/linux/capwrap.tar.gz -Kernel capabilites: +Kernel capabilities: http://ftp.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.4/capfaq-0.2. txt diff -Nru muse-2.1.2/share/CMakeLists.txt muse-3.0.2+ds1/share/CMakeLists.txt --- muse-2.1.2/share/CMakeLists.txt 2013-03-28 15:16:00.000000000 +0000 +++ muse-3.0.2+ds1/share/CMakeLists.txt 2017-12-04 21:01:19.000000000 +0000 @@ -34,6 +34,7 @@ scoreglyphs locale themes + metronome ) install (FILES didyouknow.txt splash.png DESTINATION ${MusE_SHARE_DIR} ) diff -Nru muse-2.1.2/share/didyouknow.txt muse-3.0.2+ds1/share/didyouknow.txt --- muse-2.1.2/share/didyouknow.txt 2013-03-28 15:15:54.000000000 +0000 +++ muse-3.0.2+ds1/share/didyouknow.txt 2018-01-06 20:31:35.000000000 +0000 @@ -1,4 +1,33 @@ To get started with MusE why don't you visit the tutorials at
    http://muse-sequencer.org + MusE can add effects in realtime for audio tracks,
    check the effect rack on the mixer strip + For routing and selection dialogs that allow multiple selections
    use ctrl when clicking to keep the dialog open -What are you waiting for? Make music! :) + +MusE can use Windows VST synths if the dssi-vst bridge is installed! + +If you regularly make the same type of connections you can make a
    template-project with all of your basic settings already prewired.
    Just store the song as a template using the project dialog. + +It's possible to create midi filters or generators in any
    language, see instructions and examples under
    muse2/share/scripts in the source. + +Click on the automation column in the track list to make
    automation lines visible for a parameter. + +Adding effects will add more automatable parameters to the
    automation column. + +Most functions in MusE are reachable from the keyboard.
    See 'Configure Shortcuts' under 'MusE Settings'.
    For instance: A - pointer, D - draw, R - erase, S - cut. + +Midi parts can be cloned. All clone parts contain the same data,
    change one and all will be changed. + +Parts can be copied and deleted using the standard cut/copy/paste
    but it is also possible to click and drag parts to duplicate.
    ctrl+click+drag to duplicate parts
    ctrl+alt+click+drag to create clones + +When using the dark theme, make sure to select Qt theme plastique
    or the theme will not be consistent. + +If it's hard to be precise while moving automation points with the mouse,
    trying holding the SHIFT key, the movement will be slower. + +During recording it is often useful to restart the recording since the previous
    +take wasn't perfect or you simply want several takes. While recording press
    +shift+space (default shortcut), a new track is created and the recording is
    +restarted from the initial position.
    +If recording was done on multiple tracks, all the armed tracks will be duplicated. + +MusE 3.0 defaults to connecting to Pulse Audio instead of Jack to ease startup for new users. To connect to Jack change the default sound output on the Audio configuration tab in Global Settings. diff -Nru muse-2.1.2/share/drummaps/addictive_drums.map muse-3.0.2+ds1/share/drummaps/addictive_drums.map --- muse-2.1.2/share/drummaps/addictive_drums.map 2013-03-28 15:15:52.000000000 +0000 +++ muse-3.0.2+ds1/share/drummaps/addictive_drums.map 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/drummaps/AKAI_SG01v_Bank_dr1.map muse-3.0.2+ds1/share/drummaps/AKAI_SG01v_Bank_dr1.map --- muse-2.1.2/share/drummaps/AKAI_SG01v_Bank_dr1.map 2013-03-28 15:15:52.000000000 +0000 +++ muse-3.0.2+ds1/share/drummaps/AKAI_SG01v_Bank_dr1.map 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + # Drum Set List for AKAI SG01v Bank dr1 diff -Nru muse-2.1.2/share/drummaps/AKAI_SG01v_Bank_dr2.map muse-3.0.2+ds1/share/drummaps/AKAI_SG01v_Bank_dr2.map --- muse-2.1.2/share/drummaps/AKAI_SG01v_Bank_dr2.map 2013-03-28 15:15:52.000000000 +0000 +++ muse-3.0.2+ds1/share/drummaps/AKAI_SG01v_Bank_dr2.map 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + # Drum Set List for AKAI SG01v Bank dr2 diff -Nru muse-2.1.2/share/drummaps/dx200_drum.map muse-3.0.2+ds1/share/drummaps/dx200_drum.map --- muse-2.1.2/share/drummaps/dx200_drum.map 2013-03-28 15:15:52.000000000 +0000 +++ muse-3.0.2+ds1/share/drummaps/dx200_drum.map 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + PulseBass C100 PulseBass C#111 diff -Nru muse-2.1.2/share/drummaps/KURZWEIL_SP2X_Drum_Map_GM_ReMap.map muse-3.0.2+ds1/share/drummaps/KURZWEIL_SP2X_Drum_Map_GM_ReMap.map --- muse-2.1.2/share/drummaps/KURZWEIL_SP2X_Drum_Map_GM_ReMap.map 2013-03-28 15:15:52.000000000 +0000 +++ muse-3.0.2+ds1/share/drummaps/KURZWEIL_SP2X_Drum_Map_GM_ReMap.map 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + # Drum Set List for KURZWEIL SP2X Drum Map GM ReMap diff -Nru muse-2.1.2/share/drummaps/KURZWEIL_SP2X_Drum_Map_Normal.map muse-3.0.2+ds1/share/drummaps/KURZWEIL_SP2X_Drum_Map_Normal.map --- muse-2.1.2/share/drummaps/KURZWEIL_SP2X_Drum_Map_Normal.map 2013-03-28 15:15:52.000000000 +0000 +++ muse-3.0.2+ds1/share/drummaps/KURZWEIL_SP2X_Drum_Map_Normal.map 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/drummaps/sc88-pc1.map muse-3.0.2+ds1/share/drummaps/sc88-pc1.map --- muse-2.1.2/share/drummaps/sc88-pc1.map 2013-03-28 15:15:52.000000000 +0000 +++ muse-3.0.2+ds1/share/drummaps/sc88-pc1.map 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + # Drum Set List SC-88 Drum Set 1 diff -Nru muse-2.1.2/share/instruments/Access_Virus.idf muse-3.0.2+ds1/share/instruments/Access_Virus.idf --- muse-2.1.2/share/instruments/Access_Virus.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Access_Virus.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Akai-SG01v.idf muse-3.0.2+ds1/share/instruments/Akai-SG01v.idf --- muse-2.1.2/share/instruments/Akai-SG01v.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Akai-SG01v.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/AlesisQS6.idf muse-3.0.2+ds1/share/instruments/AlesisQS6.idf --- muse-2.1.2/share/instruments/AlesisQS6.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/AlesisQS6.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Alesis-QS-78R.idf muse-3.0.2+ds1/share/instruments/Alesis-QS-78R.idf --- muse-2.1.2/share/instruments/Alesis-QS-78R.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Alesis-QS-78R.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Classic_cantabile_sp-250.idf muse-3.0.2+ds1/share/instruments/Classic_cantabile_sp-250.idf --- muse-2.1.2/share/instruments/Classic_cantabile_sp-250.idf 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Classic_cantabile_sp-250.idf 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,2250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + High Q + + + Slap + + + Scratch Push + + + Scratch Pull + + + Sticks + + + Square Click + + + Metronome Click + + + Metronome Bell + + + Acoustic Bass Drum + + + Bass Drum 1 + + + Side Stick + + + Acoustic Snare + + + Hand Clap + + + Electric Snare + + + Low Floor Tom + + + Closed Hi-hat + + + High Floor Tom + + + Pedal Hi-hat + + + Low Tom + + + Open Hi-hat + + + Low-Mid Tom + + + High Mid Tom + + + High Tom 1 + + + China Cymbal + + + Ride Bell + + + Tambourine + + + Cowbell + + + Vibra-slap + + + High Bongo + + + Low Bongo + + + Low Conga + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + Short Guiro + + + Long Guiro + + + Claves + + + LowWood Block + + + Mute Cuica + + + Open Cuica + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo + + + Open Surdo + + + Brush Snare 1 + + + Brush Snare 2 + + + Brush Swirl l + + + Snare Roll + + + Castanets + + + Room Snare 1 + + + Sticks + + + Kick + + + Pop Snare + + + + + + + + High Q + + + Slap + + + Scratch Push + + + Scratch Pull + + + Sticks + + + Square Click + + + Metronome Click + + + Metronome Bell + + + Room Kick 2 + + + Room Kick 1 + + + Side Stick + + + Room Snare 1 + + + Hand Clap + + + Room Snare 2 + + + Room LowTom2 + + + Closed Hi-hat + + + Room LowTom1 + + + Pedal Hi-hat + + + Room MidTom2 + + + Open Hi-hat + + + Room MidTom1 + + + Room Hi Tom2 + + + Room Hi Tom1 + + + China Cymbal + + + Ride Bell + + + Tambourine + + + Cowbell + + + Vibra-slap + + + High Bongo + + + Low Bongo + + + Low Conga + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + Short Guiro + + + Long Guiro + + + Claves + + + LowWood Block + + + Mute Cuica + + + Open Cuica + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo + + + Open Surdo + + + Brush Snare 1 + + + Brush Snare 2 + + + Brush Swirl l + + + Snare Roll + + + Castanets + + + Room Snare 1 + + + Sticks + + + Kick + + + Pop Snare + + + + + + + + High Q + + + Slap + + + Scratch Push + + + Scratch Pull + + + Sticks + + + Square Click + + + Metronome Click + + + Metronome Bell + + + + + + Power Kick Drum + + + Side Stick + + + Power Snare Drum + + + Hand Clap + + + Electric Snare + + + PowerLowTom2 + + + Closed Hi-hat + + + PowerLowTom1 + + + Pedal Hi-hat + + + PowerMidTom2 + + + Open Hi-hat + + + PowerMidTom1 + + + Power HiTom2 + + + Power HiTom1 + + + China Cymbal + + + Ride Bell + + + Tambourine + + + Cowbell + + + Vibra-slap + + + High Bongo + + + Low Bongo + + + Low Conga + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + Short Guiro + + + Long Guiro + + + Claves + + + LowWood Block + + + Mute Cuica + + + Open Cuica + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo + + + Open Surdo + + + Brush Snare 1 + + + Brush Snare 2 + + + Brush Swirl l + + + Snare Roll + + + Castanets + + + Room Snare 1 + + + Sticks + + + Kick + + + Pop Snare + + + + + + + + High Q + + + Slap + + + Scratch Push + + + Scratch Pull + + + Sticks + + + Square Click + + + Metronome Click + + + Metronome Bell + + + Electric Bass Drum 2 + + + Electric Bass Drum + + + Side Stick + + + Electric Snare 1 + + + Hand Clap + + + Electric Snare 2 + + + Electric Low Tom 2 + + + Closed Hi-hat + + + Electric Low Tom 1 + + + Pedal Hi-hat + + + Electric Mid Tom 2 + + + Open Hi-hat + + + Electric Mid Tom 1 + + + Electric Hi Tom 2 + + + High Tom 1 + + + ReverseCymbal + + + Ride Bell + + + Tambourine + + + Cowbell + + + Vibra-slap + + + High Bongo + + + Low Bongo + + + Low Conga + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + Short Guiro + + + Long Guiro + + + Claves + + + LowWood Block + + + Mute Cuica + + + Open Cuica + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo + + + Open Surdo + + + Brush Snare 1 + + + Brush Snare 2 + + + Brush Swirl l + + + Snare Roll + + + Castanets + + + Room Snare 1 + + + Sticks + + + Kick + + + Pop Snare + + + + + + + + High Q + + + Slap + + + Scratch Push + + + Scratch Pull + + + Sticks + + + Square Click + + + Metronome Click + + + Metronome Bell + + + Analog Bass Drum2 + + + Analog Bass Drum + + + Analog Rim Shot + + + Analog Snare 1 + + + Analog Clap + + + Analog Snare 2 + + + Analog Low Tom2 + + + Analog Closed Hi-hat + + + Analog Low Tom1 + + + Analog ClosedHi-hat + + + Analog Mid Tom2 + + + Analog Closed Hi-hat + + + Analog Mid Tom1 + + + Analog Hi Tom2 + + + Analog Cymbal + + + Analog Hi Tom1 + + + China Cymbal + + + Ride Bell + + + Tambourine + + + Analog Cowbell + + + Vibra-slap + + + High Bongo + + + Low Bongo + + + Analog Hi Conga + + + Analog Mid Conga + + + Analog Low Conga + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Analog Maracas + + + Short Guiro + + + Long Guiro + + + Analog Claves + + + LowWood Block + + + Mute Cuica + + + Open Cuica + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo + + + Open Surdo + + + Brush Snare 1 + + + Brush Snare 2 + + + Brush Swirl l + + + Snare Roll + + + Castanets + + + Room Snare 1 + + + Sticks + + + Kick + + + Pop Snare + + + + + + + + High Q + + + Slap + + + Scratch Push + + + Scratch Pull + + + Sticks + + + Square Click + + + Metronome Click + + + Metronome Bell + + + Jazz Kick 2 + + + Jazz Kick 1 + + + Side Stick + + + Jazz Snare 1 + + + Hand Clap + + + Jazz Snare 2 + + + Low Floor Tom + + + Closed Hi-hat + + + High Floor Tom + + + Pedal Hi-hat + + + Low Tom + + + Open Hi-hat + + + Low-Mid Tom + + + High Mid Tom + + + High Tom 1 + + + China Cymbal + + + Ride Bell + + + Tambourine + + + Cowbell + + + Vibra-slap + + + High Bongo + + + Low Bongo + + + Low Conga + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + Short Guiro + + + Long Guiro + + + Claves + + + LowWood Block + + + Mute Cuica + + + Open Cuica + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo + + + Open Surdo + + + Brush Snare 1 + + + Brush Snare 2 + + + Brush Swirl l + + + Snare Roll + + + Castanets + + + Room Snare 1 + + + Sticks + + + Kick + + + Pop Snare + + + + + + + + High Q + + + Slap + + + Scratch Push + + + Scratch Pull + + + Sticks + + + Square Click + + + Metronome Click + + + Metronome Bell + + + Jazz Kick 2 + + + Jazz Kick 1 + + + Side Stick + + + Brush Tap + + + Hand Clap + + + Brush Slap + + + Brush Low Tom2 + + + Closed Hi-hat + + + Brush Low Tom1 + + + Pedal Hi-hat + + + Brush Mid Tom2 + + + Open Hi-hat + + + Brush Mid Tom1 + + + Brush Hi Tom2 + + + Brush Hi Tom1 + + + China Cymbal + + + Ride Bell + + + Tambourine + + + Cowbell + + + Vibra-slap + + + High Bongo + + + Low Bongo + + + Low Conga + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + Short Guiro + + + Long Guiro + + + Claves + + + LowWood Block + + + Mute Cuica + + + Open Cuica + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo + + + Open Surdo + + + Brush Snare 1 + + + Brush Snare 2 + + + Brush Swirl l + + + Snare Roll + + + Castanets + + + Room Snare 1 + + + Sticks + + + Kick + + + Pop Snare + + + + + + + + Closed Hi-hat 2 + + + Pedal Hi-hat + + + Open Hi-hat 2 + + + Ride Cymbal 1 + + + Sticks + + + Square Click + + + Metronome Click + + + Metronome Bell + + + Concert BD 2 + + + Concert BD 1 + + + Side Stick + + + Concert SD + + + Castanets + + + Concert SD + + + Timpani F + + + Timpani F# + + + Timpani G + + + Timpani G# + + + Timpani A + + + Timpani A# + + + Timpani B + + + Timpani c + + + Timpani c# + + + Timpani d + + + Timpani d# + + + Timpani e + + + Timpani f + + + Tambourine + + + Cowbell + + + Concert Cymbal 2 + + + Vibra-slap + + + Concert Cymbal 1 + + + High Bongo + + + Low Bongo + + + Low Conga + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + Short Guiro + + + Long Guiro + + + Claves + + + LowWood Block + + + Mute Cuica + + + Open Cuica + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo + + + Open Surdo + + + Applause + + + Brush Snare 2 + + + Brush Swirl l + + + Snare Roll + + + Castanets + + + Room Snare 1 + + + Sticks + + + Kick + + + Pop Snare + + + + + + + + High Q + + + Slap + + + Scratch Push + + + Scratch Pull + + + Sticks + + + Square Click + + + Metronome Click + + + Metronome Bell + + + Guitar Fret Noise + + + Guitar Cutting Noise Up + + + Guitar Cutting Noise Down + + + String Slap Of Double Bass + + + Fl.Key Click + + + Laughing + + + Scream + + + Punch + + + Heart Beat + + + Footsteps 1 + + + Footsteps 2 + + + Applause + + + Door Creaking + + + Door + + + Scratch + + + Wind Chimes + + + Car-Engine + + + Car-Stop + + + Car-Pass + + + Car-Crash + + + Siren + + + Train + + + Jetplane + + + Helicopter + + + Starship + + + Gun Shot + + + Machine Gun + + + Lasergun + + + Explosion + + + Dog + + + Horse-Gallop + + + Birds + + + Rain + + + Thunder + + + Wind + + + Seashore + + + Stream + + + Bubble + + + Guitar fx 1 + + + Guitar fx 2 + + + Guitar fx 3 + + + Guitar fx 4 + + + Guitar fx 5 + + + + + + diff -Nru muse-2.1.2/share/instruments/Emu-4mbgsgmmt-sf.idf muse-3.0.2+ds1/share/instruments/Emu-4mbgsgmmt-sf.idf --- muse-2.1.2/share/instruments/Emu-4mbgsgmmt-sf.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Emu-4mbgsgmmt-sf.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + diff -Nru muse-2.1.2/share/instruments/emuproteus2000.idf muse-3.0.2+ds1/share/instruments/emuproteus2000.idf --- muse-2.1.2/share/instruments/emuproteus2000.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/emuproteus2000.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/gm2.idf muse-3.0.2+ds1/share/instruments/gm2.idf --- muse-2.1.2/share/instruments/gm2.idf 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/gm2.idf 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,576 @@ + + + + + Switch General Midi Level 2 mode on + 7e 7f 09 03 + + + + 7e 7f 09 03 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + High Q + Slap + Scratch Push [EXC7] + Scratch Pull [EXC7] + Sticks + Square Click + Metronome Click + Metronome Bell + Acoustic Bass Drum + Bass Drum 1 + Side Stick + Acoustic Snare + Hand Clap + Electric Snare + Low Floor Tom + Closed Hi-hat [EXC1] + High Floor Tom + Pedal Hi-hat [EXC1] + Low Tom + Open Hi-hat [EXC1] + Low-Mid Tom + High Mid Tom + Crash Cymbal 1 + High Tom + Ride Cymbal 1 + Chinese Cymbal + Ride Bell + Tambourine + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibra-slap + Ride Cymbal 2 + High Bongo + Low Bongo + Mute Hi Conga + Open Hi Conga + Low Conga + High Timbale + Low Timbale + High Agogo + Low Agogo + Cabasa + Maracas + Short Whistle [EXC2] + Long Whistle [EXC2] + Short Guiro [EXC3] + Long Guiro [EXC3] + Claves + Hi Wood Block + Low Wood Block + Mute Cuica [EXC4] + Open Cuica [EXC4] + Mute Triangle [EXC5] + Open Triangle [EXC5] + Shaker + Jingle Bell + Bell Tree + Castanets + Mute Surdo [EXC6] + Open Surdo [EXC6] + + + + + + Room Low Tom 2 + Room Low Tom 1 + Room Mid Tom 2 + Room Mid Tom 1 + Room Hi Tom 2 + Room Hi Tom 1 + + + + + + Power Kick Drum + Power Snare Drum + Power Low Tom 2 + Power Low Tom 1 + Power Mid Tom 2 + Power Mid Tom 1 + Power Hi Tom 2 + Power Hi Tom 1 + + + + + + Electric Bass Drum + Electric Snare 1 + Electric Snare 2 + Electric Low Tom 2 + Electric Low Tom 1 + Electric Mid Tom 2 + Electric Mid Tom 1 + Electric Hi Tom 2 + Electric Hi Tom 1 + Reverse Cymbal + + + + + + Analog Bass Drum + Analog Rim Shot + Analog Snare 1 + Analog Low Tom 2 + Analog CHH 1 [EXC1] + Analog Low Tom 1 + Analog CHH 2 [EXC1] + Analog Mid Tom 2 + Analog OHH [EXC1] + Analog Mid Tom 1 + Analog Hi Tom 2 + Analog Cymbal + Analog Hi Tom 1 + Analog Cowbell + Analog High Conga + Analog Mid Conga + Analog Low Conga + Analog Maracas + Analog Claves + + + + + + Jazz Kick 2 + Jazz Kick 1 + + + + + + JazzKick 2 + JazzKick 1 + Brush Tap + Brush Slap + Brush Swirl + + + + + + Closed Hi-hat 2 [EXC1] + Pedal Hi-hat [EXC1] + Open Hi-hat 2 [EXC1] + Ride Cymbal 1 + Concert BD 2 + Concert BD 1 + Concert SD + Castanets + Concert SD + Timpani F + Timpani F# + Timpani G + Timpani G# + Timpani A + Timpani A# + Timpani B + Timpani c + Timpani c# + Timpani d + Timpani d# + Timpani e + Timpani f + Concert Cymbal 2 + Concert Cymbal 1 + Applause + + + + + + High Q + Slap + Scratch Push [EXC7] + Scratch Pull [EXC7] + Sticks + Square Click + Metronome Click + Metronome Bell + Guitar Fret + Guitar Cutting Noise Up + Guitar Cutting Noise Down + String Slap of Double Bass + Fl. Key Click + Laughing + Scream + Punch + Heart Beat + Footsteps 1 + Footsteps 2 + Applause + Door Creaking + Door + Scratch + Wind Chimes + Car-Engine + Car-Stop + Car-Pass + Car-Crash + Siren + Train + Jetplane + Helicopter + Starship + Gun Shot + Machine Gun + Lasergun + Explosion + Dog + Horse-Gallop + Birds + Rain + Thunder + Wind + Seashore + Stream + Bubble + + + + + diff -Nru muse-2.1.2/share/instruments/gm.idf muse-3.0.2+ds1/share/instruments/gm.idf --- muse-2.1.2/share/instruments/gm.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/gm.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + Switch General Midi mode on 7e 7f 09 01 @@ -171,7 +171,7 @@ - + @@ -203,5 +203,58 @@ + + + + Acoustic Bass Drum + Bass Drum 1 + Side Stick + Acoustic Snare + Hand Clap + Electric Snare + Low Floor Tom + Closed Hi-Hat + High Floor Tom + Pedal Hi-Hat + Low Tom + Open Hi-Hat + Low-Mid Tom + Hi-Mid Tom + Crash Cymbal 1 + High Tom + Ride Cymbal 1 + Chinese Cymbal + Ride Bell + Tambourine + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Hi Bongo + Low Bongo + Mute Hi Conga + Open Hi Conga + Low Conga + High Timbale + Low Timbale + High Agogo + Low Agogo + Cabasa + Maracas + Short Whistle + Long Whistle + Short Guiro + Long Guiro + Claves + Hi Wood Block + Low Wood Block + Mute Cuica + Open Cuica + Mute Triangle + Open Triangle + + + diff -Nru muse-2.1.2/share/instruments/gs.idf muse-3.0.2+ds1/share/instruments/gs.idf --- muse-2.1.2/share/instruments/gs.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/gs.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + Switch General Midi mode on 7e 7f 09 01 diff -Nru muse-2.1.2/share/instruments/Hammond_XB-1.idf muse-3.0.2+ds1/share/instruments/Hammond_XB-1.idf --- muse-2.1.2/share/instruments/Hammond_XB-1.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Hammond_XB-1.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Korg Krome (Bank Map GM2).idf" "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Korg Krome (Bank Map GM2).idf" --- "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Korg Krome (Bank Map GM2).idf" 1970-01-01 00:00:00.000000000 +0000 +++ "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Korg Krome (Bank Map GM2).idf" 2018-01-26 21:59:38.000000000 +0000 @@ -0,0 +1,6622 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Closed + Hi-Hat-Whispy + Hi-Hat Dance 4 Open + Timbales-Paila / Shaker + Hi-Hat-Crisp Open/Chili/ Crisp Open + Hi-Hat-Vintage + Hi-Hat 2-Pedal Open/Closed/Pedal + BD-Dance 04 L/R + BD-Dance 02 + BD-Dance 04 + Dance 07 + BD-Dance 10 + Orch.SD Roll 1 + Orch.SD Roll 2 + Orch.SD Roll 1 + Orch.SD Roll 2 + SD-Sidestick Hi + Cymbal Reverse + SD-Reverse IFX1 + BD-01a-dry + BD-03a-dry + BD-03a-dry + BD-02b-dry + BD-02a-dry + SD-06-dry + SD-05-dry + SD-04-dry + SD-03-dry + SD-02-dry + SD-07-dry + BD-01b-dry + BD-01a-dry + Sidestick-dry + SD-01a-dry + Hand Claps 1 + SD-01b-dry + Tom Lo-dry + HiHat-Closed-dry + Tom Lo-dry + HiHat-Pedal1-dry + Tom Mid-dry + HiHat-Open-dry + Tom Mid-dry + Tom Hi-dry + Crash 01-dry + Tom Hi-dry + Ride 01-dry + China-dry + Ride 02-dry + Tambourine 3/1-Push/1-Pull + Splash-dry + Cowbell 2 + Crash 02-dry + Vibraslap + Ride + Bongo 2 Hi-Open f/mf/f + Bongo 2 Lo-Muffled f/mp/Lo-Closed + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/mf/f/mp + Conga 2 Lo-OpenSlap/mf/f + Timbales Hi-Rim 1/2/Hi-Edge + Timbales Lo-Rim/Open + Agogo Bell IFX5 Off + Agogo Bell IFX5 Off + Cabasa-Up/Down + Maracas-Pull/Push + Samba Whistle 2 + Samba Whistle 2 + Guiro-Short + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 2-Hi + Cuica 2-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + BD-Dance 13-EXs5 + BD-Dance 12-EXs5 + BD-Mad Analog 03-EXs5 + BD-HipHop 07-EXs5 + BD-Dance 15-EXs5 + SD-Mad Analog 11-EXs5 + SD-Granular 05-EXs5-R + SD-Mad Analog 05-EXs5 + SD-Dance 12-EXs5 + SD-Mad Analog 13-EXs5 + BD-Mad Analog 06-EXs5 + BD-Mad Analog 07-EXs5 + BD-Dance 11 + BD-Dance 03 + BD-Dance 08 + BD-Dance 01 + BD-Dance 07/Tom-Dance + SD-Dance 4 (Old) + SD-Dance 5 + Noise-White/SD-Sidestick Ambi + SD-Rap 2 + Dance Claps 3/Click + SD-Dance 6 + BD-Dance 06 (R&B) + BD-Dance 07 + Rimshot-Dance 2 + SD-Dance 3 (New) + Dance Claps 1 + SD-Dance 2/SD-Sidestick Dry + Tom-Dance + Hi-Hat Dance 3 Closed + Tom-Dance + Hi-Hat Dance 3 Open + Tom-Dance + Hi-Hat Dance 3 Open + Dance Perc 8 Maraca + Hi-Hat Dance 4 Closed + Crash-Dance 3 + Hi-Hat Dance 4 Open + Ride-Dance 1 + Crash-Dance 2 DDD1 + Ride-Dance 2 DDD1 + Tambourine 3/2 + Noise-VPM Mod + Dance Perc 7 Cowbell + Crash-Dance 1 + Cricket Spectrum/Tambourine 3 + Perc-Mad Analog 06-EXs5 + Zap 1 + Zap 2 + Dance Perc 6 Clave/BD-Dance 05 (Rap) + Dance Perc 5 Conga + Dance Perc 5 Conga + HiHat Mad Analog 01-EXs5 + Triangle-Mute/BD-Dance 05 (Rap) + HiHat Mad Analog 16-EXs5 + Triangle-Open/Tom-Brush Hi + Hi-Hat Dance 5 Closed/DJ-SD Rub + Hi-Hat Dance 5 Open + Rimshot-Dance 1 + PC Vox-Cks/BD-Woofer + Hi-Hat Dance 1 Closed/BD-Dry 1 + Noise-White/SD-Amb.Piccolo 1 + Hi-Hat Dance 2 Open/DJ-Record Stop + PC Vox-Sis/SD-Paper + PC Vox-Dunc/Vocal SD + Hi-Hat-Alpo Closed + PC Vox-For/Tom-Brush Hi + Hi-Hat-Amb Crackle/Ride-Dance 1 + PC Vox-Zee/Tom-Jazz Hi Rim + Hi-Hat-Crisp Open/Ride-Dance 1 + PC Vox-Pa/Swish Terra + PC Vox-Tu/BD-Jazz 2 + Crash Cymbal 2 + China Cymbal + Ride Cymbal 4 Edge + Conga 1 Hi-Slap 1/Lo-Open + Conga 1 Hi-Open/Lo-MuteSlap + Ride Cymbal Cup 2 + Conga 1 Hi-Slap 2/Hi-Mute + Cowbell + Chacha Bell + Cowbell 1 + Guiro-Short/BD-Dry 1 + Guiro-Tap + Metal Hit/Finger Cymbal + Dance Perc 1 Hip/BD-Dry 1 + Swish Terra + Cyber Drum + Tambourine 1-Push + Air Vortex + Tubular + Telephone Ring + Footstep 2 + Triangle-Mute + Tribe + Dance Perc 1 Hip/BD-Dry 1 + + + + + + + + BD-amb 1 mf -L + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open + Hi-Hat-Chili + Hi-Hat-Old Open + Timbales-Paila + Hi-Hat-Crisp Open + Hi-Hat-Vintage 1 + Hi-Hat 3-Pedal Open + BD-Dance 01-EXs5 + BD-Dance 14-EXs5 + BD-TekClub 12-EXs5 + BD-HipHop 08b/a-EXs5 + BD-Hip 2 + SD-Dance 09-EXs5 + SD-TekClub 04-EXs5 + SD-HipHop 07-EXs5 + SD-Mad Analog 15-EXs5 + SD-TekClub 16-EXs5 + Cymbal Reverse + SD-Ringy + BD-Dance 15-EXs5 + BD-Dance 10-EXs5 + BD-TekClub 03-EXs5 + BD-Dance 08-EXs5 + BD-Dance 11-EXs5 + SD-Dance 14-EXs5 + SD-Dance 19-EXs5 + SD-Dance 12-EXs5 + SD-Dance 20-EXs5 + SD-Dance 21-EXs5 + SD-Dance 18/17-EXs5 + BD-Dance 12-EXs5 + BD-Dance 13-EXs5 + SD-Dance 13-EXs5 + SD-Dance 15-EXs5 + SD-Dance 16-EXs5 + SD-Dance 14-EXs5 + Tom-Dance d-EXs5 + Hi-Hat Dance 06-EXs5 + Tom-Dance d-EXs5 + Hi-Hat Dance 07-EXs5 + Tom-Dance e-EXs5 + Hi-Hat Dance 11-EXs5 + Tom-Dance f-EXs5 + Tom-Dance g-EXs5 + Crash Dance 1-EXs5 + Tom-Dance h-EXs5 + Crash Dance 4-EXs5 + Crash Dance 3-EXs5 + Ride HipHop 2-EXs5 + Tambourine 3/1-EXs5 + Crash Dance 2-EXs5 + Perc-Dance 01-EXs5 + Crash HipHop 1-EXs5-R + Perc-Dance 09-EXs5 + Ride HipHop 1-EXs5 + Perc-Dance 02-EXs5 + Perc-Dance 03-EXs5 + Perc-Dance 12-EXs5 + Perc-Dance 05-EXs5 + Perc-Dance 04-EXs5 + Hi-Hat Dance 09-EXs5 + Hi-Hat Dance 10-EXs5 + Perc-Mad Analog 19-EXs5 + Perc-Mad Analog 19-EXs5 + Shaker 4-EXs5 + Shaker 5-EXs5 + Orchestral BD + Orchestral BD + Zap-EXs5 + Perc-Dance 07-EXs5 + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Closed + Hi-Hat-Whispy + Hi-Hat Dance 4 Open + Timbales-Paila / Shaker + Hi-Hat-Crisp Open/Chili/ Crisp Open + Hi-Hat-Vintage + Hi-Hat 2-Pedal Open/Closed/Pedal + BD-Dance 04 L/R + BD-Dance 02 + BD-Dance 04 + Dance 07 + BD-Dance 10 + Orch.SD Roll 1 + Orch.SD Roll 2 + Orch.SD Roll 1 + Orch.SD Roll 2 + SD-Sidestick Hi + Cymbal Reverse + SD-Reverse IFX1 + BD-01a-dry + BD-03a-dry + BD-03a-dry + BD-02b-dry + BD-02a-dry + SD-06-dry + SD-05-dry + SD-04-dry + SD-03-dry + SD-02-dry + SD-07-dry + BD-01b-dry + BD-01a-dry + Sidestick-dry + SD-01a-dry + Hand Claps 1 + SD-01b-dry + Tom Lo-dry + HiHat-Closed-dry + Tom Lo-dry + HiHat-Pedal1-dry + Tom Mid-dry + HiHat-Open-dry + Tom Mid-dry + Tom Hi-dry + Crash 01-dry + Tom Hi-dry + Ride 01-dry + China-dry + Ride 02-dry + Tambourine 3/1-Push/1-Pull + Splash-dry + Cowbell 2 + Crash 02-dry + Vibraslap + Ride + Bongo 2 Hi-Open f/mf/f + Bongo 2 Lo-Muffled f/mp/Lo-Closed + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/mf/f/mp + Conga 2 Lo-OpenSlap/mf/f + Timbales Hi-Rim 1/2/Hi-Edge + Timbales Lo-Rim/Open + Agogo Bell IFX5 Off + Agogo Bell IFX5 Off + Cabasa-Up/Down + Maracas-Pull/Push + Samba Whistle 2 + Samba Whistle 2 + Guiro-Short + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 2-Hi + Cuica 2-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + HHat-Dance5-amb1 + HHat-Dance7-amb1 + HHat-Whispy-amb1 + HHat-Dance4/1-amb1 + TimbalesH-Paila-a1 / Shaker 1-amb1 + H.Hat-Crisp/Chili-amb1 + H.Hat-Old-amb1 + HHat-Pedal3/2-amb1 + BD-amb-01 + BD-amb-02 + BD-amb-03 + BD-amb-04 + BD-amb-05 + SD-Orch Roll-amb1 + SD-Orch Roll-amb1 + SD-Orch Roll-amb1 + SD-Orch Roll-amb1 + Stick Hit-amb + Reverse Cymbal-amb + SD Reverse-amb + BD-01a-amb1 + BD-03b-amb1 + BD-03a-amb1 + BD-02b-amb1 + BD-02a-amb1 + SD-06-amb1 + SD-05-amb1 + SD-04-amb1 + SD-03-amb1 + SD-02-amb1 + SD-07-amb1 + BD-01b-amb1 + BD-01a-amb1 + Sidestick-amb1 + SD-01a-amb1 + Claps-Dance-amb1 + SD-01b-amb1 + Tom Lo-amb1 + HiHat-Cl-amb1 + Tom Lo-amb1 + HHat-Pdl1-amb1 + Tom Mid-amb1 + HiHat-Op-amb1 + Tom Mid-amb1 + Tom Hi-amb1 + Crash 01-amb1 + Tom Hi-amb1 + Ride 01-amb1 + China-amb1 + Ride 02-amb1 + Tambourine 3/1/2-amb1 + Splash-amb1 + Cowbell 2-amb1 + Crash 02-amb1 + Vibra-Slap-amb1 + Ride 03-amb1 + Bongo H-Op f-amb1 + Bongo L-Muf1/L-Muf2/Lo-Cl-amb1 + Conga H-Slp2/Hi-Op1-amb1 + Conga H-Slp1/Hi-Op2/Hi-Op3/Hi-Op1-amb1 + Conga L-Slap/Lo-Op1/Lo-Op2-amb1 + Timbales H-Rim2/H-Rim1/H-Edge-a1 + Timbales L-Ege1/L-Op-a1 + Agogo Lo-amb1 + Agogo Hi-amb1 + Cabasa 1/3-amb1 + Maracas-Pull1-amb1 + Whistle 3-amb1 + Whistle 3-amb1 + Guiro-Short a-amb1 + Guiro-Long a-amb1 + Claves b-amb1 + Woodblock 1/2-amb1 + Woodblock 3/4-amb1 + Cuica Hi c-amb1 + Cuica Lo-amb1 + Triangle-Mute1/2-a1 + Triangle-Mute1/2-a1 + Shaker 1/2-amb1 + Marc Tree-amb1 + BD-Dance-amb-01 + HHat Dance8-amb1 + SD-Dance-amb1 + HHat Dance7-amb1 + Crash-Dance-amb1 + Perc-Dance1-amb1 + Claps-Dance-amb1 + Perc-Dance2-amb1 + SD-Orch Roll-amb1 + SD-Orch-amb1 + Bell Tree-amb1 + Stadium-amb1 + Applause-amb1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute1/3 + Baya-Mute5/4 + Tabla-Open + Tabla-Tin/Mute4 + Drumstick Hit/BD-Dry 1 + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry + BD-Dry 1 + BD-Dry 4 + BD-Soft + BD-Soft Room + BD-Pillow + SD-Full Room + SD-Classic Room + SD-Amb.Piccolo 1 + SD-Paper + SD-Dry 4 + SD-Dry 2 + BD-Dry 3 + BD-Dry 2 + SD-Sidestick Dry + SD-Amb.Piccolo 2 + Hand Claps 2 + SD-Big Rock + Tom 3-Floor + Hi-Hat 2-Closed 2 + Tom 3-Floor + Hi-Hat 2-Pedal Open + Tom 3-Lo + Hi-Hat 2-Open 1/2 + Tom 3-Lo + Tom 3-Hi + Crash Cymbal 3/BD-Dry 1 + Tom 3-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2 + Splash Cymbal + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push + Samba Whistle 1/Chinese Gong + Samba Whistle 1/Chinese Gong + Guiro-Short/Tap + Guiro-Long/BD-Dry 1 + Claves/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute/3 + Baya-Mute5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit/BD-Dry 1 + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-Tubby + BD-Gated + BD-Chili + BD-Hip 4 + BD-PhatJaz + SD-Chili/SD-Jazz Ring + SD-Ambi Hit 2/SD-Classic Room + SD-Brasser/SD-Paper + SD-Vintage 4/SD-Brush Tap 1 + SD-Hip 5 + SD-GhostSet roll + BD-Dry 6 + BD-Dry 5 + SD-Sidestick Ambi + SD-GhostSet f/p2 + Hand Claps 2 + SD-Klanger/GhostSet p1 + Tom 2-Floor + Hi-Hat 3-Closed 2/1 + Tom 2-Floor + Hi-Hat 3-Pedal Open / Pedal + Tom 2-Lo + Hi-Hat 3-Sizzle/Open 1 + Tom 2-Lo + Tom 2-Hi + Crash Cymbal 3/BD-Dry 1 + Tom 2-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2 / 1-Push + Splash Cymbal + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open/Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push + Samba Whistle 1/Chinese Gong + Samba Whistle 1/Chinese Gong + Guiro-Short/Tap + Guiro-Long/BD-Dry 1 + Claves/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap?Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit + Cymbal Reverse/SD-Reverse + SD-Reverse + BD-Tight/BD-amb 2 mf -L + BD-Squash/BD-amb 2 f -L + BD-Gated/BD-amb 1 f -L + BD-Tubby/BD-amb 1 mf -L + BD-Tight/BD-amb 2 mf -L + SD-Full Room/SD-dry 1 f -L + SD-Dry 2/SD-dry 1 mp -L + SD-Amb.Piccolo 2/SD-dry 1 fff -L + SD-Dry 3/SD-dry 1 mp -L + SD-GhostSet f/SD-dry 1 f -L + SD-Yowie/SD-dry 1 f -L + BD-Dry 3/BD-amb 2 mf -L + BD-Dry 2/BD-amb 2 f -L + SD-Sidestick Ambi/SD-Sidestick mf -L + SD-Sidestick Ambi/SD-dry 1 f -L + Hand Claps 2/1 -L + SD-GhostSet f/SD-dry 1 p -L + Tom 2-Floor/Tom 1-Lo f -L + Hi-Hat 1-Closed ff/mf + Tom 2-Floor/Tom 1-MidLo f -L + Hi-Hat 1-Pedal ff/1-Pedal mf + Tom 2-Floor/Tom 1-MidHi f -L + Hi-Hat 1-Open ff/mf + Tom 2-Floor/Tom 1-MidHi f -L + Tom 3-Floor/Tom 1-Hi mf -L + Crash Cymbal 3/1 mf -L + Tom 3-Floor/Tom 1-Hi mf -L + Ride Cymbal 4 Edge / 1 -L + China Cymbal + Ride Cymbal Cup 2/Cup 1 -L + Tambourine 2/1-Push + Splash Cymbal/Crash Cymbal 1 mf -L + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/Crash Cymbal 1 mf -L + Vibraslap/BD-Dry 1 + Crash Cymbal 4/Ride Cymbal 1 -L + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Tom-Processed + Timbales Hi-Rim 1/Hi-Edge + Tom-Processed + Timbales Lo-Open + Tom-Processed + Maracas-Push + Tom-Processed + Tom-Processed + Cabasa-Tap/Up + Tom-Processed + Claves + Woodblock 1 + Woodblock 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-Tight + BD-Squash + BD-Gated + BD-Tubby + BD-Woofer + SD-Full Room + SD-Cracker Room + SD-Amb.Piccolo 2 + SD-Processed 1 + SD-Yowie + SD-Atomic + BD-Terminator + BD-Mondo Kill + SD-Sidestick Ambi + SD-Big Rock + Hand Claps 2 + SD-Processed 2 + Tom 4-Floor + Hi-Hat 3-Closed 2/1 + Tom 4-Lo + Hi-Hat 3-Pedal Open/Pedal + Tom 4-Lo + Hi-Hat 3-Open 1/2 + Tom 4-Lo + Tom 4-Hi + Crash Cymbal 3/BD-Dry 1 + Tom 4-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2/1-Push + Splash Cymbal + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Tom-Processed + Timbales Hi-Rim 1/Hi-Edge + Tom-Processed + Timbales Lo-Open + Tom-Processed + Maracas-Push + Tom-Processed + Tom-Processed + Cabasa-Tap/Up + Tom-Processed + Claves + Woodblock 1 + Woodblock 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca + Orch.SD Roll 2/1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-Tight/BD-amb 2 mf -L + BD-Squash/BD-amb 2 mf -L + BD-Gated/BD-amb 1 f -L + BD-Tubby/BD-amb 1 mf -L + BD-Woofer/BD-amb 1 mf -L + SD-Full Room/SD-dry 1 mp -L + SD-Cracker/SD-dry 1 ff -L + SD-Amb.Piccolo 2/SD-dry 1 ff -L + SD-Processed 1/SD-dry 1 ff -L + SD-Yowie/SD-dry 1 fff -L + SD-Atomic/SD-dry 1 fff -L + BD-Terminator/BD-amb 2 mf -L + BD-Mondo Kill/BD-amb 2 f -L + SD-Sidestick Ambi/SD-Sidestick mf -L + SD-Big Rock/SD-dry 1 fff -L + Hand Claps 2/1 -L + SD-Processed 2/SD-dry 1 mf -L + Tom 3-Floor/Tom 1-Lo f -L + Hi-Hat 1-Closed ff/mf + Tom 3-Lo/Tom 1-MidLo f -L + Hi-Hat 1-Pedal ff/mf + Tom 3-Lo/Tom 1-MidHi f -L + Hi-Hat 1-Open ff/mf + Tom 3-Lo/Tom 1-MidHi f -L + Tom 3-Hi/Tom 1-Hi mf -L + Crash Cymbal 3/1 mf -L + Tom 3-Hi/Tom 1-Hi mf -L + Ride Cymbal 4 Edge/Ride Cymbal 1 -L + China Cymbal + Ride Cymbal Cup 2/Cup 1 -L + Tambourine 2/1-Push + Splash Cymbal/Crash Cymbal 1 mf -L + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/1 mf -L + Vibraslap/BD-Dry 1 + Cymbal 4 Edge/Ride Cymbal 2 -L + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/Conga 1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Tom-Processed + Timbales Hi-Rim 1/Hi-Edge + Tom-Processed + Timbales Lo-Open + Tom-Processed + Maracas-Push + Tom-Processed + Tom-Processed + Cabasa-Tap/Up + Tom-Processed + Claves + Woodblock 1 + Woodblock 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Tom 2-Floor + Tsuzumi + Tom 2-Lo + Taiko-Rim/Open + Tom 2-Hi + Tom 2-Hi + Chacha Bell/BD-Dry 1 + Finger Snap + Hand Claps 2 + Drumstick Hit/BD-Dry 1 + BD-Tubby + BD-Dry 3 + BD-Woofer + BD-Dry 4 + BD-Soft + BD-Jazz 1 + BD-Jazz 2 + SD-Paper + SD-Dry 3 + SD-Dry 2 + SD-Amb.Piccolo 1 + SD-Jazz Ring + SD-Brush Hit/BD-Dry 1 + BD-Jazz 2 + BD-Pillow + SD-Sidestick Dry + SD-Brush Tap 2/BD-Dry 1 + SD-Brush Tap 1/BD-Dry 1 + SD-Brush Swirl/BD-Dry 1 + Tom-Brush Floor + Hi-Hat 2-Closed 2/1 + Tom-Brush Hi + Hi-Hat 2-Pedal + Tom-Brush Hi + Hi-Hat 2-Open 1/2 + Tom-Jazz Floor + Tom-Jazz Hi Rim/Hi + Crash Cymbal 2/BD-Dry 1 + Tom-Jazz Hi Rim/Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2/1-Push + Splash Cymbal + Cowbell 1 + Crash Cymbal 4 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push/BD-Dry 1 + Samba Whistle 1/Chinese Gong + Samba Whistle 1/Chinese Gong + Guiro-Short/Tap + Guiro-Long/BD-Dry 1 + Claves/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Closed + Hi-Hat-Whispy + Hi-Hat Dance 4 Open + Timbales-Paila / Shaker + Hi-Hat-Crisp Open/Chili/ Crisp Open + Hi-Hat-Vintage + Hi-Hat 2-Pedal Open/Closed/Pedal + BD-Dance 04 L/R + BD-Dance 02 + BD-Dance 04 + Dance 07 + BD-Dance 10 + Orch.SD Roll 1 + Orch.SD Roll 2 + Orch.SD Roll 1 + Orch.SD Roll 2 + SD-Sidestick Hi + Cymbal Reverse + SD-Reverse IFX1 + BD-01a-dry + BD-03a-dry + BD-03a-dry + BD-02b-dry + BD-02a-dry + SD-06-dry + SD-05-dry + SD-04-dry + SD-03-dry + SD-02-dry + SD-07-dry + BD-01b-dry + BD-01a-dry + Sidestick-dry + SD-01a-dry + Hand Claps 1 + SD-01b-dry + Tom Lo-dry + HiHat-Closed-dry + Tom Lo-dry + HiHat-Pedal1-dry + Tom Mid-dry + HiHat-Open-dry + Tom Mid-dry + Tom Hi-dry + Crash 01-dry + Tom Hi-dry + Ride 01-dry + China-dry + Ride 02-dry + Tambourine 3/1-Push/1-Pull + Splash-dry + Cowbell 2 + Crash 02-dry + Vibraslap + Ride + Bongo 2 Hi-Open f/mf/f + Bongo 2 Lo-Muffled f/mp/Lo-Closed + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/mf/f/mp + Conga 2 Lo-OpenSlap/mf/f + Timbales Hi-Rim 1/2/Hi-Edge + Timbales Lo-Rim/Open + Agogo Bell IFX5 Off + Agogo Bell IFX5 Off + Cabasa-Up/Down + Maracas-Pull/Push + Samba Whistle 2 + Samba Whistle 2 + Guiro-Short + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 2-Hi + Cuica 2-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Closed + Hi-Hat-Whispy + Hi-Hat Dance 4 Open + Timbales-Paila / Shaker + Hi-Hat-Crisp Open/Chili/ Crisp Open + Hi-Hat-Vintage + Hi-Hat 2-Pedal Open/Closed/Pedal + BD-Dance 04 L/R + BD-Dance 02 + BD-Dance 04 + Dance 07 + BD-Dance 10 + Orch.SD Roll 1 + Orch.SD Roll 2 + Orch.SD Roll 1 + Orch.SD Roll 2 + SD-Sidestick Hi + Cymbal Reverse + SD-Reverse IFX1 + BD-01a-dry + BD-03a-dry + BD-03a-dry + BD-02b-dry + BD-02a-dry + SD-06-dry + SD-05-dry + SD-04-dry + SD-03-dry + SD-02-dry + SD-07-dry + BD-01b-dry + BD-01a-dry + Sidestick-dry + SD-01a-dry + Hand Claps 1 + SD-01b-dry + Tom Lo-dry + HiHat-Closed-dry + Tom Lo-dry + HiHat-Pedal1-dry + Tom Mid-dry + HiHat-Open-dry + Tom Mid-dry + Tom Hi-dry + Crash 01-dry + Tom Hi-dry + Ride 01-dry + China-dry + Ride 02-dry + Tambourine 3/1-Push/1-Pull + Splash-dry + Cowbell 2 + Crash 02-dry + Vibraslap + Ride + Bongo 2 Hi-Open f/mf/f + Bongo 2 Lo-Muffled f/mp/Lo-Closed + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/mf/f/mp + Conga 2 Lo-OpenSlap/mf/f + Timbales Hi-Rim 1/2/Hi-Edge + Timbales Lo-Rim/Open + Agogo Bell IFX5 Off + Agogo Bell IFX5 Off + Cabasa-Up/Down + Maracas-Pull/Push + Samba Whistle 2 + Samba Whistle 2 + Guiro-Short + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 2-Hi + Cuica 2-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + Hi-Hat 3-Closed 1 + Hi-Hat 1-Closed mf + Hi-Hat 1-Open mf + Hi-Hat-Whispy + Hi-Hat 1-Open ff + Hi-Hat-Chili + Hi-Hat-Alpo Closed + DJ Vinyl Sliced 04 + BD-Dry 3 + DJ Vinyl Sliced 13 + BD-Dance 03 + BD-Dance 06 (R&B) + BD-Ambi + SD-Brasser + "kkk" + Voice 20 + SD-Ambi Pop + SD-Dry 2 + Cymbal Reverse + Finger Snap + BD-Dance 06-EXs5 + BD-Dance 05-EXs5 + BD-Dance 03-EXs5 + BD-Dance 06-EXs5 + BD-Dance 09-EXs5 + SD-Dance 05-EXs5 + SD-Dance 10-EXs5 + SD-Dance 07-EXs5 + SD-Dance 09-EXs5 + SD-Dance 02-EXs5 + SD-Dance 04-EXs5 + BD-Dance 04-EXs5 + BD-Dance 08-EXs5 + SD-Dance 06-EXs5 + SD-Dance 03-EXs5 + SD-Dance 01-EXs5 + SD-Dance 07-EXs5 + Tom-Dance i-EXs5 + Hi-Hat Dance + Tom-Dance j-EXs5 + Hi-Hat Dance 02-EXs5 + Tom-Dance k-EXs5 + Hi-Hat Dance 04-EXs5 + Tom-Dance a-EXs5 + Tom-Dance b-EXs5 + Crash Dance 1-EXs5 + Tom-Dance c-EXs5 + Ride Dance-EXs5 + Crash Dance 2-EXs5 + Orch Cymbal-Open + Tambourine 1-EXs5 + Cymbal Reverse 1-EXs5 + Perc-Dance 01-EXs5 + Crash HipHop 1-EXs5-L + Sleigh Bell + Crash Dance 4-EXs5 + Perc-Dance 02-EXs5 + Perc-Dance 03-EXs5 + Perc-Dance 12-EXs5 + Perc-Dance 05-EXs5 + Perc-Dance 04-EXs5 + Hi-Hat Dance 03-EXs5 + Hi-Hat Dance 05-EXs5 + Tambourine 2-EXs5 + Tambourine 3-EXs5 + Shaker 1-EXs5 + Shaker 3-EXs5 + DJ-Scratch 2-EXs5 + DJ-Horn-EXs5 + DJ-Scratch 1-EXs5 + DJ-Big Cannon-EXs5 + Perc-Mad Analog 37-EXs5 + DJ-Vinyl Sliced 09/03/11/05-EXs5 + DJ-Vinyl Sliced 12/09/12/02-EXs5 + DJ Vinyl Sliced 05/04/07/06-EXs5 + DJ-Record Stop-EXs5 + Triangle-Mute-EXs5 + Triangle Open 1-EXs5 + Shaker 2-EXs5 + Marc Tree Rev-EXs5 + BD-Dance 01-EXs5 + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Voice 12 + Voice 09 + Bell Tree + Stadium + Applause + Orchestral SD + Voice 21 + Voice 23 + Voice 24 + Voice 25 + Voice 26 + GunShot + + + + + + + + BD-amb 1 mf -L + Hi-Hat Dance 3 Closed + Hi-Hat Dance 3 Ope + Hi-Hat-Old Closed 2 + Hi-Hat Dance 4 Closed + Timbales-Paila/Shaker 4-EXs5 + Hi-Hat 3-Closed 1/Hi-Hat-Chili/Hi-Hat Dance 5 Closed + Hi-Hat-Vintage 1/Hi-Hat-Crisp Closed 2 + Hi-Hat 2-Pedal Open / 2-Closed 1 / 2-Pedal + BD-HipHop 01b/02a-EXs5 + BD-HipHop 11-EXs5 + BD-HipHop 10/09-EXs5 + BD-HipHop 14b/a-EXs5 + BD-HipHop 04-EXs5 + SD-HipHop 09-EXs5-L + SD-HipHop 02-EXs5 + SD-HipHop 04-EXs5 + SD-HipHop 06a-EXs5 + SD-HipHop 10-EXs5 + Crash-Dance 1 + SD-Yowie + BD-HipHop 11-EXs5/BD-Dance 11-EXs5 + BD-HipHop 06-EXs5 + BD-HipHop 12/03-EXs5 + BD-HipHop 02b/a-EXs5 + BD-HipHop 06/12-EXs5 + SD-HipHop 07-EXs5/SD-HipHop 11-EXs5-L + SD-HipHop 08-EXs5 + SD-HipHop 03-EXs5-L + SD-HipHop 13-EXs5/SD-dry 1 ff -L/mp -L/p -L + SD-HipHop 12-EXs5-L + SD-HipHop 07-EXs5 + BD-HipHop 13b/a-EXs5-L + BD-HipHop 07/08b/08a-EXs5 + Sidestick-HipHop-EXs5-L + SD-HipHop 15d/c/b/a-EXs5-L + Clap HipHop 1/2-EXs5 + SD-HipHop 15a-EXs5-L/Perc-Granular 21-EXs5 + Tom-HipHop Lo 3/2/1-EXs5 + Hi-Hat HipHop 03/02/01-EXs5 + Tom-HipHop Mid Lo 4/3/2/1-EXs5 + Hi-Hat HipHop 06/07-EXs5 + Tom-HipHop Mid Hi 4/3/2/1-EXs5 + Hi-Hat HipHop 05/04-EXs5 + Tom-HipHop Mid Hi 4/3/2/1-EXs5 + Tom-HipHop Hi 4/3/2/1-EXs5 + Crash HipHop 1-EXs5-L + Tom-HipHop Hi 4/3/2/1-EXs5 + Ride Cymbal 4 Edge + Hi-Hat Dance 5 Open + Ride HipHop 2-EXs5 + Perc-Granular 36/39/37-EXs5 + Crash-Dance 3 + Perc-Mad Analog 19-EXs5 + Crash HipHop 2-EXs5 + Wind + Ride HipHop 1-EXs5 + Perc-Mad Analog 32-EXs5 + Perc-Mad Analog 34-EXs5 + Bongo 1 Lo-Stick + Conga 1 Hi-Open + Conga 1 Hi-Slap 1 + Perc-Mad Analog 22/21/20-EXs5 + Perc-Mad Analog 24-EXs5 + Perc-Mad Analog 28-EXs5 + Perc-Mad Analog 29-EXs5 + Perc-Mad Analog 23-EXs5 + Perc-Granular 36-EXs5 + Perc-Mad Analog 06-EXs5 + Perc-Mad Analog 05-EXs5 + Perc-Mad Analog 12-EXs5 + Perc-Mad Analog 13-EXs5 + Perc-Mad Analog 37-EXs5 + Perc-Mad Analog 30-EXs5 + Perc-Mad Analog 30-EXs5 + Perc-TekClub 18-EXs5 + Perc-TekClub 18-EXs5 + Perc-Granular 46-EXs5 + Perc-Granular 46-EXs5 + Perc-Mad Analog 35-EXs5 + Marc Tree + BD-HipHop 03-EXs5 + Hi-Hat Dance 3 Closed + SD-HipHop 02-EXs5 + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Tabla-Mute 4 + Drumstick Hit/BD-Dry 1 + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-PhatJaz/BD-Dry 1 + BD-Hip 1/BD-Dry 4 + BD-Dance 04/BD-Dance 04 + BD-AmbiRocker + BD-Dance 13/BD-Pillow + SD-Vintage 5/SD-Full Room + SD-Ambi Hit 1/SD-Classic Room + SD-AmbiCrackle 2/SD-Amb.Piccolo 1 + SD-Vintage 2/SD-Paper + SD-Hip 3/SD-Dry 4 + SD-Ambi House/SD-Dry 2 + BD-Dance 07/BD-Dry 3 + BD-Ambi/BD-Dry 2 + SD-Sidestick Dry + SD-Whopper/SD-Amb.Piccolo 2 + Hand Claps 4 Happy/2 + SD-Vintage 1/SD-Big Rock + Tom-Dirty Funk + Hi-Hat-Amb Crackle/Hi-Hat 2-Closed 2 + Tom-Dirty Funk + Hi-Hat-Old Closed 2/Hi-Hat 2-Pedal + Tom-Dirty Funk + Hi-Hat-Crisp Open / 2-Open 2 + Tom-Old R&B + Tom-Old R&B + Crash Cymbal 3 + Tom-Old R&B + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2 / 1-Push + Splash Cymbal + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push + Samba Whistle 1/Chinese Gong + Samba Whistle 1/Chinese Gong + Guiro-Short/Guiro-Tap + Guiro-Long/BD-Dry 1 + Claves/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06/05 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + DJ Vinyl Sliced 15 + DJ Vinyl Sliced 13 + Voice Perc-Kick 2b-EXs5 + BD-Mad Analog 08-EXs5 + BD-Mad Analog 12-EXs5 + SD-HipHop 12-EXs5-R + Sidestick-HipHop-EXs5-L + Voice Perc-Snare 3-EXs5 + SD-Mad Analog 12-EXs5 + SD-HipHop 03-EXs5-R + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-Dance Soft/BD-Dry 1 + DJ-BD Rub + BD-Dance 08/Zap 1 + BD-Dance 03/Tom-VPM + BD-Dance 06 (R&B)/BD-Pillow + DJ-SD Rub/SD-Dry 2 + Dance Claps 3/Scream + SD-AmbiCrackle 2/SD-Amb.Piccolo 1 + SD-Dance 2/SD-Sidestick Dry + SD-Sidestick mf -L/Dance Claps 2 + SD-Vintage 5/SD-Full Room + BD-Dance 12/Orchestral BD + BD-Dance 13/Taiko-Open + Rimshot-Dance 1 + Dance Claps 3/SD-Sidestick mf -L + Dance Claps 1/Hand Claps 1 -L + SD-Hip 4/SD-Dance 7 + BD-Hip 1/Explosion + Hi-Hat Dance 3 Closed / 2-Pedal + Taiko-Open/Explosion + Hi-Hat Dance 3 Open / 2-Closed 2 + BD-Hip 1/Explosion + Hi-Hat Dance 4 Open / 2-Open 2 + Vocal Cowbell/Tom-Old R&B + Vocal Cowbell/Tom-Old R&B + Crash Cymbal 3/BD-Dry 1 + Claves/Tom-Old R&B + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Dance Perc 2b/2a + Tribe/Chinese Gong + Tambura/Gamelan + Orch Cymbal-Open/Timpani + Applause/Stadium + Ride Cymbal 1 -L / 4 Edge + Bongo 1 Hi-Slap/Hi-Open + Bongo 1 Lo-Slap/Lo-Open + Conga 1 Hi-Mute/1-Toe + Conga 1 Hi-MtSlap/Hi-Open + Conga 1 Lo-Slap/Lo-Open + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open + Tambourine 3 + Tambourine 2 + Cabasa-Up/Down + Maracas-Push + DJ-BD Rub + DJ-SD Rub + DJ Vinyl Sliced 11/12 + DJ Vinyl Sliced 09/10 + DJ-Reverse + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B)/05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Tabla-Mute 4 + Drumstick Hit + Cymbal Reverse + SD-Reverse + BD-Jazz 2 + BD-Dance 06 (R&B)/BD-Dry 5 + BD-Pillow/DJ-Record Stop + BD-Ringy/Dance Perc 2b + BD-Ambi Crackle/DJ-Old Record + SD-AmbiCrackle 3/DJ-Old Record + Orchestral SD + SD-Brush Swirl/SD-Brush Tap 1 + SD-Vintage 2/SD-Brush Hit + SD-Hip 3/SD-GhostSet f + SD-Jazz Ring/SD-Brush Tap 2 + BD-Hip 4 + BD-Dance Soft + SD-Sidestick mf -R + SD-Brush Hit/Tom 2-Hi + Dance Claps 1/Hand Claps 1 -L + SD-Hip 5/SD-Sidestick Dry + Tom-Old R&B + Hi-Hat-Crisp Closed 1 / Hi-Hat 1-Closed mf /BD-Dry 1 + Tom-Old R&B + Hi-Hat-Crisp Closed 1 / Hi-Hat 1-Pedal mf + Tom-Old R&B L/R Off + Hi-Hat-Old Open/Hi-Hat 1-Open mf + Tom-Dance + Tom-Dance + Crash Cymbal 1 mf -L + Tom-Dance + Ride Cymbal 1 -L / 4 Edge + Crash-Dance 2 DDD1 / China Cymbal + Ride Cymbal Cup 1 -R / Ride-Dance 1 + Tambourine 2/1-Push + Crash-Dance 1 + Finger Snap + Crash Cymbal 2 + Finger Cymbal/BD-Dry 1 + Ride-Dance 1 + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell L/R Off + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push/Pull + Uhh Solo + Yea Solo + Guiro-Short/Tap + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 1-Hi + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + Bell Tree + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-amb 1 mf -L + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open/Closed + Hi-Hat Dance 03-EXs5 + Hi-Hat Dance 3 Closed + Timbales-Paila/Shaker 1 + Hi-Hat-Crisp Open/Chili/Whispy + Hi-Hat-Vintage 1 + Hi-Hat 2-Pedal Open/Closed 1/Pedal + BD-TekClub 03-EXs5 + BD-TekClub 11-EXs5 + BD-TekClub 12-EXs5 + BD-TekClub 06-EXs5 + BD-TekClub 04-EXs5/BD-Mad Analog 14-EXs5 + SD-TekClub 03-EXs5 + SD-TekClub 06/07-EXs5 + SD-TekClub 08-EXs5-L + SD-TekClub 11-EXs5 + SD-TekClub 17-EXs5 + Cymbal Reverse + SD-Processed + BD-TekClub + BD-TekClub + BD-TekClub + BD-TekClub 10-EXs5 + BD-TekClub 08/07/06-EXs5 + SD-TekClub 05/04-EXs5-L + SD-TekClub 01-EXs5 + SD-TekClub 12-EXs5 + SD-TekClub 15/07-EXs5 + Crash-Granular-EXs5-L/SD-HipHop 08-EXs5 + SD-TekClub-EXs5 + BD-TekClub 05-EXs5 + BD-TekClub 04-EXs5 + SD-Mad Analog 02-EXs5 + SD-TekClub 08-EXs5-L + SD-TekClub 10-EXs5-L + SD-TekClub 14-EXs5 + Tom-TekClub-EXs5-L + Hat-TekClub 05-EXs5-L + Tom-TekClub-EXs5-L + Hat-TekClub 10-EXs5/01-EXs5-R + Tom-TekClub-EXs5-L + Hat-TekClub 11-EXs5/03-EXs5-L + Tom-Granular 6-EXs5 + Tom-Granular 5-EXs5 + Crash HipHop 1-EXs5-L + Tom-Granular 4-EXs5 + Ride HipHop 2-EXs5 + China Cymbal + Perc-TekClub 14-EXs5 + Tambourine 2/1-Push/1-Pull + Crash-Granular-EXs5-R + Perc-TekClub 10-EXs5 + Crash HipHop 2-EXs5 + HiHat Granular 06-EXs5-R + Perc-TekClub 02-EXs5 + Perc-TekClub 08-EXs5 + Perc-TekClub 08-EXs5 + Perc-Mad Analog 32-EXs5 + Perc-Mad Analog 34-EXs5 + Perc-Mad Analog 33-EXs5 + Perc-TekClub 10-EXs5 + Perc-TekClub 10-EXs5 + Hi-Hat Dance 5 Open + Hi-Hat Dance 5 Open + Perc-Mad Analog 23-EXs5 + Maracas-Pull + Perc-Granular 35/34-EXs5 + Voice 35 "Aow"/Voice 39 "Uuh" + Hat-TekClub 10-EXs5 + Hat-TekClub 11-EXs5 + Perc-TekClub 01-EXs5 + Perc-TekClub 06/05-EXs5 + Perc-TekClub 12/13-EXs5 + Perc-TekClub 04-EXs5 + Perc-TekClub 17-EXs5-R/16-EXs5 + Triangle-Mute + Triangle-Roll + Dance Perc 8 Maraca + Marc Tree + BD-Mad Analog 04-EXs5 + HiHat Mad Analog 02-EXs5 + SD-Mad Analog 10-EXs5 + HiHat Mad Analog 03-EXs5 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + BD-HipHopf 03-EXs5 + BD-TekClub 07-EXs5 + BD-Granular01-EXs5-L/BD-Dance 01 + BD-TekClub 04-EXs5 + BD-Mad Analog 01-EXs5 + SD-TekClub 08-EXs5-L + SD-HipHop 04-EXs5 + SD-Dance 12-EXs5 + SD-Dance 05-EXs5 + SD-Dance 02-EXs5 + BD-Mad Analog 04-EXs5 + BD-Dance 01 + BD-Dance 11 + BD-Dance 04 + BD-Dance 08 + BD-Squash + BD-Dance 06 (R&B) + SD-Dance 3 (New) + SD-Dance 5 + SD-Hip 4 + SD-Sizzle + SD-Noise/SD-Vintage 4 + SD-Hip 1/SD-Chili + BD-Dance 12 + BD-Dance 09 + Rimshot-Dance 2 + SD-Dance 4 (Old) + Dance Claps 1 + SD-Dance 8 + Tom-Dance + Hi-Hat Dance 1 Closed + Tom-Dance + Hi-Hat Dance 1 Open + Tom-Dance + Hi-Hat Dance 1 Open + Hi-Hat-Whispy/BD-Dry 1 + Hi-Hat Dance 3 Closed + Crash-Dance 3 + Hi-Hat Dance 3 Open + Ride-Dance 1 + Crash Cymbal 3/China Cymbal + Ride-Dance 2 DDD1 + Tambourine 3/2 + Splash Cymbal + Dance Perc 7 Cowbell + Crash-Dance 1 + Zap 3 + Cymbal Reverse + Zap 1 + Zap 2 + Dance Perc 6 Clave/BD-Dance 05 (Rap) + Dance Perc 5 Conga + Dance Perc 5 Conga + Metal Hit/DJ-Reverse + Hi-Hat-Chili/BD-Dry 1 + Industry Loop 2/DJ-Old Record + Hi-Hat 3-Pedal/DJ-Record Stop + Dance Perc 4 House/DJ-SD Rub + Dance Perc 8 Maraca/SD-Noise + Vocal SD + Car Crash/BD-Woofer + Triangle-Mute/BD-Dance 05 (Rap) + Noise-White/SD-Amb.Piccolo 1 + Triangle-Open/Tom-Brush Hi + E.Guitar Cut Noise 1/SD-Paper + Hi-Hat-Hip 2/Vocal SD + Hi-Hat-Alpo Closed + String Slap/Tom-Brush Hi + Hi-Hat-Amb Crackle/Ride-Dance 1 + Dist.Slide 1/Tom-Jazz Hi Rim + Hi-Hat-Crisp Open/Ride-Dance 1 + Swish Terra + Swish Terra/BD-Jazz 2 + Crash Cymbal 2 + China Cymbal + Ride Cymbal 4 Edge + Conga 1 Hi-Slap 1/Lo-Open + Conga 1 Hi-Open/Lo-MuteSlap + Ride Cymbal Cup 2 + Conga 1 Hi-Slap 2/Hi-Mute + Cowbell 1 + Chacha Bell + Cowbell 1 + Guiro-Short/BD-Dry 1 + Guiro-Tap + Metal Hit/Finger Cymbal + Dance Perc 1 Hip/BD-Dry 1 + Swish Terra + Cyber Drum + Tambourine 1-Push + Air Vortex + Tubular + Telephone Ring + Footstep 2 + Triangle-Mute + Tribe + Dance Perc 1 Hip/BD-Dry 1 + + + + + + + + BD-Mad Analog 10-EXs5 + BD-HipHop 08b-EXs5 + BD-Granular 02-EXs5-R/L + BD-TekClub 02-EXs5 + BD-Granular 20-EXs5-L/R + SD-Dance 09-EXs5 + SD-dry 1 p -R + SD-Granular 20-EXs5-L + SD-TekClub 13-EXs5 + SD-Dance 02-EXs5 + BD-Dance 10 + BD-Dance 01 + BD-Dance 03 + BD-Dance 01 + BD-Squash + BD-Dance 13/Hi-Hat 3-Closed 1 + BD-Dance 01/Zap 1 + SD-Dance 3 (New) + SD-Dance 5 + SD-Dance 2 IFX1 + Dance Claps 3 + SD-Dance 2/Hand Claps 2 + SD-Dance 8 + BD-Dance 10/Hi-Hat Dance 3 Closed + BD-Dance 11/Zap 1 + Rimshot-Dance 1/2 + SD-Dance 7 + Dance Claps 2 + SD-Dance 6 + Tom-Dance/Tom 4-Floor + Hi-Hat Dance 1 Closed + Tom-Dance/Tom 4-Lo + Hi-Hat Dance 2 Closed + Tom-Dance/Tom 4-Hi + Hi-Hat Dance 2 Open + Dance Perc 2b/2a + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open + Hi-Hat Dance 3 Open + Ride-Dance 1 + Crash-Dance 1 + Ride-Dance 2 DDD1 + Tambourine 3/2 + Splash Cymbal + Dance Perc 7 Cowbell + Crash-Dance 1 + PC Vox-One + Cymbal Reverse + Zap 1 + Zap 2 + Dance Perc 6 Clave/BD-Dance 05 (Rap) + Dance Perc 5 Conga + Dance Perc 5 Conga + Tabla-Na/DJ-Reverse + Hi-Hat Dance 3 Closed/BD-Dry 1 + Baya-Mute 2/DJ-Old Record + Hi-Hat Dance 3 Open/DJ-Record Stop + Baya-Open/DJ-SD Rub + Dance Perc 8 Maraca/SD-Noise + CompuVoice-Beep/Vocal SD + Dance Claps 3/BD-Woofer + Triangle-Mute/BD-Dance 05 (Rap) + Hand Claps 1 -L/SD-Amb.Piccolo 1 + Triangle-Open/Tom-Brush Hi + Finger Snap/SD-Paper + Hi-Hat-Old Closed 3/Vocal SD + Hi-Hat-Alpo Closed + E.Guitar Pick 1/Tom-Brush Hi + Hi-Hat-Amb Crackle/Ride-Dance 1 + Dist.Slide 1/Tom-Jazz Hi Rim + Hi-Hat-Crisp Open/Ride-Dance 1 + Dist.Slide 2/Swish Terra + Guitar Scratch/BD-Jazz 2 + Crash Cymbal 2/Crash Cymbal 2 + China Cymbal + Ride Cymbal 4 Edge + Conga 1 Hi-Slap 1/Bongo 1 Lo-Open + Conga 1 Hi-Open/Lo-MuteSlap + Ride Cymbal Cup 2 + Conga 1 Hi-Slap 2/Hi-Mute + Cowbell 1 + Chacha Bell + Cowbell 1 + Guiro-Short/BD-Dry 1 + Guiro-Tap + Metal Hit/Finger Cymbal + Dance Perc 1 Hip/BD-Dry 1 + Swish Terra + Cyber Drum + Tambourine 1-Push + Air Vortex + Tubular + Telephone Ring + Footstep 2 + Triangle-Mute + Tribe + Dance Perc 1 Hip/BD-Dry 1 + + + + + + + + BD-Mad Analog 04-EXs5 + BD-Dance 06-EXs5 + BD-Mad Analog 05-EXs5 + BD-HipHop 03-EXs5 + BD-Dance 14-EXs5 + SD-Mad Analog 13-EXs5 + SD-TekClub 04-EXs5 + SD-TekClub 13-EXs5 + SD-Dance 12-EXs5/DJ-SD Rub + SD-Dance 03-EXs5 + BD-TekClub 01-EXs5 + BD-Dance 01-EXs5/BD-Mad Analog 13-EXs5 + BD-Dance 13 + BD-Dance 04/Dance Perc 4 House + BD-Dance 08/BD-Jungle + BD-Mad Analog 06-EXs5/07-EXs5/08-EXs5 + HiHat Mad Analog 16-EXs5/BD-Dance 06 (R&B) + SD-Dance 5 + SD-Dance 3 (New) + SD-Hip 4 + SD-Rap 2/Dance Claps 1 + SD-Noise/Dance Claps 3/SD-Noise + SD-Dance 2/7 + BD-Dance 06 (R&B)/BD-amb 1 f -R + BD-Dance 13 + Rimshot-Dance 1 + SD-Dance 2/Dance Claps 2 + Dance Claps 1/Dance Claps 3 + SD-Dance 5 + Tom-Dance + Hi-Hat/Dance Perc 2a + Tom-Dance + Hi-Hat Dance 3 Open/Dance Perc 2b + Tom-Dance + Hi-Hat Dance 3/4 Open + Hi-Hat Dance 1 Closed + Dance Perc 8 Maraca + Crash-Dance 3 + Hi-Hat Dance 2 Open + Ride-Dance 1 + Crash-Dance 1/China Cymbal + Ride-Dance 2 DDD1 + Dance Perc 2b/2a + Industry Loop 2 + HiHat Mad Analog 09-EXs5 + Crash-Dance 1 + 568:HiHat Mad Analog 10-EXs5 + Perc-Mad Analog 07-EXs5 + Zap 1 + Zap 2 + Dance Perc 6 Clave + Dance Perc 5 Conga + Dance Perc 5 Conga + Noise-VPM Mod/DJ-Reverse + Hi-Hat-Chili + Noise-White + Hi-Hat 3-Pedal/DJ-Record Stop + Dance Perc 3 House + Hi-Hat 2-Open 2 + Vocal Cowbell + GunShot + Triangle-Mute + Noise-VPM Mod/SD-Amb.Piccolo 1 + Triangle-Open/Tom-Brush Hi + HiHat Granular 19-EXs5-L/R + Hi-Hat-Ambi + Hi-Hat-Alpo Closed + CompuVoice-When/Tom-Brush Hi + Hi-Hat-Amb Crackle/Ride-Dance 1 + CompuVoice-Do it/Tom-Jazz Hi Rim + Hi-Hat-Crisp Open/Ride-Dance 1 + CompuVoice-Ch + Year solo/BD-Jazz 2 + Crash Cymbal 2 + China Cymbal + Ride Cymbal 4 Edge + Conga 1 Hi-Slap 1/Bongo 1 Lo-Open + Conga 1 Hi-Open/Lo-MuteSlap + Ride Cymbal Cup 2 + Conga 1 Hi-Slap 2/Hi-Mute + Cowbell 1 + Chacha Bell + Cowbell 1 + Guiro-Short/BD-Dry 1 + Guiro-Tap + Metal Hit/Finger Cymbal + Dance Perc 1 Hip/BD-Dry 1 + Swish Terra + Cyber Drum + Tambourine 1-Push + Air Vortex + Tubular + Telephone Ring + Footstep 2 + Triangle-Mute + Tribe + Dance Perc 1 Hip/BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit/BD-Dry 1 + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-PhatJaz + BD-Tubby/BD-Dry 4 + BD-Dry 6 + BD-Klanger/BD-Hip 1 + BD-Pillow + SD-Ambi Hit 3 + SD-Hip 3 + SD-Klanger/SD-Amb.Piccolo 1 + SD-Ringy + SD-Chili + SD-Dry 2 + BD-Ringy/BD-Dance 08 + BD-Ambi Crackle/BD-Ambi + SD-Sidestick Dry/Rimshot-Dance 2 + SD-Chili + Hand Claps 4 Happy/3 Hip + SD-AmbiCrackle 3 + Tom-Old R&B + Hi-Hat-Crisp Closed 2 + Tom-Old R&B + Hi-Hat 2-Closed 2 + Tom-Old R&B + Hi-Hat-Crisp Open / 2-Open 2 + Hi-Hat-Vintage 1 + Hi-Hat-Amb Crackle + Crash Cymbal 3/BD-Dry 1 + Hi-Hat-Old Open + Ride Cymbal 4 Edge + Noise-VPM Mod/Cricket Spectrum + Ride Cymbal Cup 2 + Tambourine 2/1-Push + Tambourine 3/1-Pull + Hi-Hat-Hip 2/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + MachineGun + Ride-Dance 2 DDD1 + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Dance Perc 1 Hip/BD-Dry 1 + Dance Perc 1 Hip/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push + SD-GhostSet f + SD-GhostSet roll + Train/Chinese Gong + Space Lore + Noise-VPM Mod/BD-Dry 1 + Air Vortex/BD-Dry 1 + Cricket Spectrum + Car Engine/BD-Dry 1 + Door Slam/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Bell Tree/BD-Dry 1 + BD-Dance 06/05 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Dance 06 (R&B)/Perc-Granular 16-EXs5-L + BD-Granular 28-EXs5 + BD-Dance 05 (Rap)/BD-Hip 4 + BD-Mad Analog 04-EXs5 + BD-Pop + SD-HipHop 03-EXs5-L + SD-HipHop 15d-EXs5-L + SD-Granular 06-EXs5/SD-dry 1 f -R + SD-Granular 14-EXs5-L/SD-HipHop 06b-EXs5 + SD-Sizzle/SD-HipHop 04-EXs5 + Cymbal Reverse + SD-Reverse + BD-Terminator/BD-Dance 13 + BD-Dance 13/BD-Dry 3 + BD-Ringy/BD-Dance 13 + BD-HipHop 14a-EXs5 + BD-Dance 13/BD-Dance 08-EXs5 + SD-TekClub 05-EXs5-L/SD-Dance 2 + SD-Jungle + SD-Dance 15-EXs5 + SD-Hip 5/SD-Sidestick Ambi + SD-Jungle + SD-Big Rock + BD-Tight/BD-Dance 08 + BD-Dance 13/BD-Terminator + SD-Sidestick Dry/SD-Sizzle + SD-Dance 2/Dance Claps 1 + Dance Claps 2/Hand Claps 3 Hip + SD-Dance 2/SD-Ambi Hit 3 + Tom-Old R&B + Hi-Hat-Vintage 1 + Tom-Old R&B + Hi-Hat-Amb Crackle + Tom-Old R&B + Splash Cymbal/Noise-White + Hi-Hat 1-Closed mf/Noise-White + Hi-Hat 1-Closed ff/Noise-White + Crash-Dance 1/BD-Dry 1 + Hi-Hat-Old Open + Ride Cymbal 4 Edge + China Cymbal/Stadium + Ride Cymbal Cup 2 + HiHat Granular 03-EXs5-L/R + Crash-Jungle + Dance Perc 2a + Crash Cymbal 2/Crash-Jungle + Dance Perc 2b + Ride-Dance 2 DDD1 + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Hi-Hat Dance 06-EXs5 + Hi-Hat Dance 10-EXs5 + Hat-TekClub 12-EXs5-L + Hat-TekClub 12-EXs5-R + Tambourine 2 + Tambourine 3 + SD-GhostSet f + SD-GhostSet roll + Zap 2 + Zap-EXs5 + Noise-VPM Mod + Noise-VPM Mod + Tambura/Cricket Spectrum + Voice 21 + Marc Tree + Triangle-Mute + Triangle-Open + Shaker 1 + Bell Tree + BD-Dance 06/05 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applausef + Voice Perc-Kick 1-EXs5 + Voice Perc-Kick 2a-EXs5 + Voice Perc-Kick 2b-EXs5 + BD-HipHop 14a-EXs5/BD-Jungle + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Ambi Soft/"Tuunn" + BD-Mad Analog 03-EXs5 + DJ Vinyl Sliced 15/Perc-Mad Analog 17-EXs5 + BD-Mad Analog 08-EXs5/CompuVoice-Do it + Perc-Mad Analog 25-EXs5 + SD-Mad Analog 05-EXs5 + Clap Mad Analog 5-EXs5/Voice Perc-Snare 2-EXs5 + Perc-Granular 32-EXs5 + SD-Mad Analog 07-EXs5 + SD-Mad Analog 09-EXs5 + BD-Granular 10-EXs5-R/L + BD-Mad Analog 06/07-EXs5 + BD-Dance 13/Perc-Granular 20-EXs5 + BD-Dance 09/Click + DJ-Old Record/BD-Dance 08 + BD-Dry 5/BD-Dance 07 + BD-Dance 07/Hi-Hat Dance 4 Open + SD-Dry 3/Timbales Hi-Edge + SD-Dance 3 (New) + SD-Jungle/Hand Claps 4 Happy + SD-Ambi Hit 1 + Dance Claps 3/1 + Dance Perc 8 Maraca/SD-Sidestick Dry + Noise-White/Cyber Drum + Hart Beat/Zap 2 + CompuVoice-Beep + Noise-White + Noise-White/Applause + SD-Dance 6/SD-Air Explosion + Tom-Synth/Noise-White + Noise-White/Click + Tom-Synth/Noise-White + Noise-White/Click + Tom-Synth/Noise-White + Noise-White/Click + Tom-Synth/Noise-White + Hi-Hat Dance 4 Closed + Noise-VPM Mod/Noise-White + Hi-Hat Dance 4 Open + Ride-Dance 2 DDD1/Telephone Ring + Crash-Dance 3/China Cymbal + Ride-Dance 2 DDD1 + Cabasa-Up/Down + Splash Cymbal + Shaker 2/1 + Crash-Dance 2 DDD1 + Dance Perc 6 Clave + Cymbal Reverse + Dance Perc 5 Conga/Bongo 1 Hi-Open + Dance Perc 5 Conga/Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/Conga 1-Toe + Dance Perc 5 Conga/Conga 1 Hi-Open + Dance Perc 5 Conga/Conga 1 Lo-Open + Tambourine 1-Push/1-Pull + Tambourine 3/2 + Dance Perc 7 Cowbell/BD-Dry 1 + Rimshot-Dance 2 + Cabasa-Up/Down + Maracas-Push + Dance Perc 8 Maraca/Chinese Gong + SD-Metal Striker/Chinese Gong + Car Engine/Guiro-Tap + Industry Loop 2 + Industry Loop 2 + Perc-Mad Analog 19-EXs5 + Tambourine 3/BD-Dry 1 + Perc-Granular 39-EXs5 + String Slap/BD-Dry 1 + Perc-Mad Analog 06-EXs5 + Zap 1/BD-Dry 1 + Perc-Granular 28-EXs5-L + Car Crash/BD-Dry 1 + BD-Dance 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 4 (Old) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Jungle + Dance Perc 6 Clave + Wind/BD-Dry 1 + Stream/BD-Dry 1 + "Two..." Solo/"One..." Solo + "Four..." Solo/"Three..." Solo + Bell Tree/BD-Dry 1 + Space Lore/BD-Dry 1 + Hart Beat/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-amb 1 mf -L + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open/Closed + Hi-Hat Dance 03-EXs5 + Hi-Hat Dance 3 Closed + Timbales-Paila/Shaker 1 + Hi-Hat-Crisp Open/Hi-Hat-Chili/Hi-Hat-Whispy + Hi-Hat-Vintage 1 + Hi-Hat 2-Pedal Open/2-Closed 1/Hi-Hat 2-Pedal + BD-Mad Analog 03-EXs5 + BD-Mad Analog 15-EXs5 + BD-Mad Analog 12-EXs5 + BD-Mad Analog 13/14-EXs5 + BD-Mad Analog 09-EXs5 + SD-Mad Analog 01-EXs5 + SD-Mad Analog 04-EXs5 + SD-Mad Analog 11-EXs5 + SD-Mad Analog 07-EXs5/SD-Granular 22-EXs5-L + Cymbal Reverse + SD-Mad Analog 01-EXs5 + BD-Mad Analog 01-EXs5 + BD-Mad Analog 05-EXs5 + Perc-Granular 18-EXs5 + BD-Mad Analog 02-EXs5/BD-TekClub 10-EXs5 + BD-Mad Analog 11-EXs5/BD-Mad Analog 10-EXs5 + SD-Mad Analog 14-EXs5/Perc-Granular 44-EXs5 + SD-TekClub 04-EXs5 + SD-Mad Analog 15-EXs5 + SD-Mad Analog 12-EXs5/Hi-Hat Granular 01-EXs5 + SD-Mad Analog 13-EXs5 + SD-Mad Analog 02-EXs5 + BD-Mad Analog 06/07/08-EXs5 + BD-Mad Analog 04-EXs5 + Sidestick-Mad Ana 2-EXs5/SD-Mad Analog 11-EXs5 + SD-Mad Analog 10-EXs5 + SD-Granular 21-EXs5-L/Clap Mad Analog 2-EXs5 + SD-Mad Analog 13-EXs5/SD-Mad Analog 12-EXs5 + Tom-Mad Analog 3-EXs5 + HiHat Mad Analog 01-EXs5 + Tom-Mad Analog 2-EXs5 + HiHat Mad Analog 10/09-EXs5 + Tom-Mad Analog + HiHat Mad Analog 16/15/14-EXs5 + Tom-Mad Analog 5b/5a-EXs5 + Tom-Mad Analog 6b/6a-EXs5 + Hi-Hat-Clangy Open/Industry Loop 2 + Tom-Mad Analog 4b/4a-EXs5 + Perc-Mad Analog 08-EXs5 + Splash Cymbal + Ride Cymbal Cup 1 -L + Dance Perc 2a + Crash-Granular-EXs5-L + Perc-Granular 22-EXs5 + Crash HipHop 2-EXs5 + Perc-Mad Analog 03-EXs5 + HiHat Granular 18-EXs5-R + Perc-TekClub 08-EXs5 + Perc-TekClub 08-EXs5 + Perc-Mad Analog 32-EXs5/Conga 2 Hi-Closed + Conga 2 Hi-OpenSlap/Perc-Mad Analog 34-EXs5 + Conga 2 Lo-OpenSlap/Perc-Mad Analog 33-EXs5 + Perc-Mad Analog 12-EXs5 + Perc-Mad Analog 15-EXs5 + Perc-Granular 23-EXs5 + Perc-Granular 23-EXs5 + Perc-Mad Analog 23-EXs5 + Perc-Mad Analog 09-EXs5 + Perc-Mad Analog 05-EXs5 + Perc-Mad Analog 04-EXs5 + HiHat Mad Analog 08-EXs5 + HiHat Mad Analog 07-EXs5 + Perc-Mad Analog 12/11-EXs5 + Perc-Mad Analog 15/14/13-EXs5 + Perc-Mad Analog 18/17/16-EXs5 + Perc-Mad Analog 31-EXs5 + Perc-Mad Analog 31-EXs5 + Triangle-Mute + Triangle-Open + Shaker 1/2 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbel + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + BD-amb 1 mf -L L/R Off + Hi-Hat Dance 4 Closed IFX4 Off + 534:Hi-Hat Dance 4 Open/Closed + Hi-Hat Dance 03-EXs5f + Hi-Hat Dance 3 Closedf + Timbales-Paila/Shaker 1 + Hi-Hat-Crisp Open/Chili/Whispy + Hi-Hat-Vintage 1 + Hi-Hat 2-Pedal Open/Closed 1/Pedal + BD-Granular 06-EXs5 + BD-Granular 13-EXs5 + BD-Granular 11-EXs5-L + BD-Granular 17-EXs5 + BD-Granular 20-EXs5-L + SD-Granular 01-EXs5 + SD-Granular 02-EXs5 + SD-Granular 20-EXs5-L + SD-Granular 19-EXs5-L + SD-Granular 16-EXs5-L + Cymbal Reverse L/R Off + SD-Processed + BD-Granular 10-EXs5-L + BD-Granular 21/23-EXs5 + BD-Granular 09/08-EXs5 + BD-Granular 28-EXs5 + BD-Granular 27/26-EXs5-L + SD-Granular 26-EXs5 + SD-Granular 08-EXs5-L/R + SD-Granular 03-EXs5 + SD-Granular 24/23-EXs5-L + SD-Granular 18-EXs5-L + SD-Granular 15-EXs5-L + BD-Granular 22-EXs5-L + BD-Granular 18-EXs5-L + Perc-Granular 23-EXs5/Sidestick-Mad Ana 1-EXs5 + SD-Granular 07-EXs5-L + SD-Granular 14-EXs5-L + SD-Granular 05/04-EXs5-L + Tom-Granular 2-EXs5-L + HiHat Granular 21/20-EXs5 + Tom-Granular 3-EXs5-L + HiHat Granular 18/17-EXs5-L + Tom-Granular 1-EXs5-L + HiHat Granular 18-EXs5-L + Tom-Granular 6-EXs5 + Tom-Granular 5-EXs5 + Crash-Granular-EXs5-L + Tom-Granular 4-EXs5 + HiHat Granular 19/18-EXs5-L + Splash Cymbal + Ride Cymbal + Perc-Granular 36/37/38-EXs5 + Perc-Granular 12-EXs5 + Perc-Granular 04-EXs5 + Crash HipHop 2-EXs5 + Perc-Granular 32-EXs5 + Perc-TekClub 09-EXs5-L + Perc-Granular 07-EXs5 + Perc-Granular 08-EXs5 + Perc-Granular 05-EXs5 + Tsuzumi + Tsuzumi + Perc-Granular 29/04-EXs5 + Perc-Granular 29/04-EXs5 + Perc-Granular 06-EXs5 + Perc-Granular 06-EXs5 + Dance Perc 8 Maraca + Shaker 1/2 + Perc-Granular 03-EXs5 + Perc-Granular 03-EXs5 + SD-Granular 16-EXs5-L + SD-Granular 17-EXs5-L + Perc-Granular 20/19-EXs5 + Perc-Granular 33-EXs5 + Perc-Granular 45-EXs5 + Perc-Granular 12-EXs5 + Perc-Granular 21-EXs5 + Triangle-Mute + Triangle-Roll + Perc-Granular 26-EXs5 + Marc Tree + BD-Granular 07-EXs5 + HiHat Granular 21-EXs5 + SD-Granular 13-EXs5 + HiHat Granular 22-EXs5 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + BD-Dry 1 + BD-HipHop 07-EXs5 + BD-TekClub 06-EXs5 + BD-Dance 10 + BD-Dance 05 (Rap) + BD-Dance 13-EXs5 + SD-HipHop 09-EXs5-L + SD-Mad Analog 01-EXs5 + SD-HipHop 06b-EXs5 + SD-Mad Analog 13-EXs5 + SD-Dance 04-EXs5 + Cymbal Reverse + SD-Reverse + BD-Dance 06 (R&B) + BD-Dark/BD-Dance 13 + BD-Hip 1/2 + BD-Dance 13 + BD-Dance Soft/08 + SD-AmbiHop + SD-Ambi Hit 1/SD-Classic Room + SD-Brasser/SD-Amb.Piccolo 1 + SD-Ambi Pop/SD-AmbiCrackle 1 + SD-Dry Present/SD-Big Rock + SD-Vintage 6 + BD-Dry 6/BD-Dark + BD-Pop/BD-Dance 08 + SD-Sidestick Ambi/Dry + SD-Vintage 1 + Hand Claps 3 Hip/4 Happy + SD-Hip 6/SD-Dry 4 + Tom-Dance + Hi-Hat-Tight 1/2 + Tom-Dance + Hi-Hat-Old Closed 2/Hi-Hat-Hip 1 + Tom-Dance + Hi-Hat-Clangy Open/Hi-Hat-Crisp Open + Tom-Old R&B + Tom-Old R&B + Noise-VPM Mod/BD-Dry 1 + Tom-Old R&B + Ride Cymbal 5 Edge + Crash-Dance 3/China Cymbal + Ride Cymbal Cup 2 + Tambourine 2/1-Push + Explosion/BD-Dry 1 + Cowbell 1/BD-Dry 1 + Crash-Dance 2 DDD1/Splash Cymbal + Church Bell/BD-Dry 1 + Crickets 2 + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Hat-TekClub 02-EXs5-R/L + Hi-Hat Dance 1 Open/Closed + Tambourine 3-EXs5 + Tambourine 1-EXs5 + Cabasa-Up/Down + Maracas-Push + Maraca/Chinese Gong + SD-Metal Striker/Chinese Gong + Car Engine/Guiro-Tap + Industry Loop 2/Guiro-Tap + Industry Loop 2/BD-Dry 1 + Car Stop/BD-Dry 1 + Tambourine 3/BD-Dry 1 + Industry Loop 2/BD-Dry 1 + String Slap/BD-Dry 1 + Dance Perc 1 Hip/Triangle-Mute + Zap 1/BD-Dry 1 + Bubble/Shaker 1 + Car Crash/BD-Dry 1 + BD-Dance 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 4 (Old) + Hi-Hat Dance 3 Open/BD-Dry 1 + Chinese Gong/Crash-Dance 3 + Dance Perc 6 Clave + SD-Dance 01-EXs5 + Stream/BD-Dry 1 + "Two..." Solo/"One..." Solo + "Four..." Solo/"Three..." Solo + Bell Tree/BD-Dry 1 + Space Lore/BD-Dry 1 + Hart Beat/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Granular 11-EXs5-R + BD-HipHop 10-EXs5 + BD-Granular 25-EXs5 + BD-Granular 26-EXs5-L + BD-Granular 28-EXs5 + SD-Sizzle + SD-Granular 06-EXs5 + SD-Rap 2 + SD-HipHop 03-EXs5-L + SD-Granular 07-EXs5-L + Cymbal Reverse + SD-Reverse + BD-Squash + BD-Jazz 2 + Zap 1 + Footstep 1/2 + Cyber Drum + Tabla-Na/Open + Orchestral SD + SD-Rap 2 + SD-Dance 1 + SD-Amb.Piccolo 2/SD-Off Center + SD-Atomic + Djembe-Open + BD-Dance 02 + Timbales Hi-Rim 1 + SD-Jazz Ring + Zap 3 + SD-Dance 4 (Old) + Tom 3-Lo + Hi-Hat Dance 1 Closed + Tom-Brush Floor + Hi-Hat Dance 3 Open + Tom-Synth + Triangle-Open/Hi-Hat Dance 1 Open + Tom-Synth + Tom-Jazz Hi + Orch Cymbal-Open + Tom 4-Lo/BD-Dry 1 + Ride Cymbal-Jazz + Air Vortex + Ride Cymbal Cup 1 -R + Tambourine 3/2 + Splash Cymbal + Dist.Slide 1/2 + Ride Cymbal 2 -L + Bird 1 + Sleigh Bell + Bongo 2 Hi-Open f/mf + Bongo 2 Lo-Muffled f/mp + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/Hi-Open mf + Conga 2 Lo-OpenSlap/Lo-Open mf + Perc-Mad Analog 35-EXs5 + Perc-Mad Analog 36-EXs5 + Perc-Granular 39/38-EXs5 + Tambourine 2 / 1-Pull + Shaker 3/2-EXs5 + Dance Perc 8 Maraca + Finger Cymbal + Guiro-Long + Vocal SD + Taiko-Open/Gamelan + Perc-Granular 28-EXs5-L + Telephone Ring + Footstep 1/2 + Zap 1 + Cricket Spectrum + Noise-VPM Mod + Noise-White + Noise-VPM Mod + Agogo Bell + GunShot + Finger Cymbal/Marc Tree + Samba Whistle 1 + HiHat Granular 18-EXs5-R/L + DJ-BD Rub + Industry + Dist.Slide 2/1 + Harp-Glissando/Rainstick + Castanet-Double 1/Vibraslap + DJ-Record Stop/DJ-Reverse + DJ-Scratch 2/1 + Tribe + Dance Perc 7 Cowbell + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-amb 1 mf -L + BD-Dance 10 + DJ Vinyl Sliced 13 + BD-Dance 07 + BD-Dance 08 + SD-HipHop 04-EXs5 + SD-Vintage 2 + Dance Perc 3 House/BD-Dry 5 + Voice 20 + SD-Jazz Ring + SD-Full Room + Cymbal Reverse + SD-Whopper + BD-Hip 2 + BD-Klanger + BD-PhatJaz + BD-Dance 09 + Zap 1 + SD-Brasser + DJ Vinyl Sliced 16 + SD-Ambi Hit 1 + SD-AmbiHop + SD-Rap 2 + SD-Dance 1 + Voice Perc-Kick 1/3-EXs5 + Voice Perc-Kick 2a/2b-EXs5 + Voice Perc-Snare 4-EXs5 + Voice Perc-Snare 1-EXs5 + Voice Perc-Snare 3-EXs5 + Voice Perc-Snare 2-EXs5 + Voice Perc-Tom-Lo-EXs5 + VoicePerc-HH-Close1-EXs5 + Voice Perc-Tom-Lo-EXs5 + VoicePerc-HH-Close2-EXs5 + Voice Perc-Tom-Mid-EXs5 + Voice Perc-Cymbal-EXs5 + Voice Perc-Tom-Mid-EXs5 + Voice Perc-Tom-Hi-EXs5 + Voice Perc-Cymbal-EXs5 + Voice Perc-Tom-Hi-EXs5 + Ride HipHop 2-EXs5 + Car Stop IFX5 Off + Voice Perc-"Pa"-EXs5 + Industry Loop 1 + Perc-Granular 32-EXs5 + Voice Perc-Cowbell-EXs5 + Voice Perc-Cymbal-EXs5 + Voice Perc-Shaker-EXs5 + Perc-Granular 27-EXs5 + Zap 2 + DJ Vinyl Sliced 18 + DJ Vinyl Sliced 17 + DJ-Vinyl Sliced 08-EXs5 + DJ Vinyl Sliced 05 + Voice 23 + DJ-Reverse + DJ Vinyl Sliced 03 + Voice 21 + DJ-Scratch 3b + Voice 26 + DJ-Hit Rub + Voice 09 + DJ Vinyl Sliced 13 + DJ-Scratch 1-EXs5 + DJ-Scratch 2-EXs5 + DJ-Record Stop-EXs5 + DJ Vinyl Sliced 08 + DJ Vinyl Sliced 04 + DJ Vinyl Sliced 05 + Triangle-Mute + Triangle-Open + DJ-Vocal Rub 1 + DJ Vinyl Sliced 02 + DJ Vinyl Sliced 06 + DJ Vinyl Sliced 07 + DJ-Scratch 2-EXs5 + DJ Vinyl Sliced 09 + DJ Vinyl Sliced 10 + DJ-Vocal Rub 2 + DJ Vinyl Sliced 11 + DJ Vinyl Sliced 12 + DJ-Vocal Rub 2 + Voice 07 + PC Vox-Cks + DJ-Scratch 1 + DJ Vinyl Sliced 18 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Tabla-Mute 4 + Tabla-Mute 2/3 + Tabla-Na/Tabla-Mute 1 + Tabla-Mute 1/2 + Orchestral BD/BD-Dry 1 + Tribe/BD-Dry 1 + Finger Snap/BD-Dry 1 + Hand Claps 2/BD-Dry 1 + Triangle-Mute/BD-Dry 1 + Triangle-Open/BD-Dry 1 + Castanet-Double 2/BD-Dry 1 + Castanet-Single/BD-Dry 1 + Castanet-Double 1/BD-Dry 1 + Guiro-Long/BD-Dry 1 + Guiro-Short + Guiro-Tap/BD-Dry 1 + Vibraslap/BD-Dry 1 + Claves/BD-Dry 1 + Dance Perc 6 Clave/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Timbales Lo-Open/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Timbales Lo-Rim/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Timbales Lo-Mute/BD-Dry 1 + Vocal Woodblock/BD-Dry 1 + Timbales Hi-Edge/BD-Dry 1 + Timbales Hi-Rim 2/BD-Dry 1 + Chacha Bell/BD-Dry 1 + Timbales Hi-Rim 1/BD-Dry 1 + Mambo Bell/BD-Dry 1 + Timbales-Paila/BD-Dry 1 + Bongo 1 Lo-Open/BD-Dry 1 + Cowbell 1/BD-Dry 1 + Bongo 1 Lo-Slap/BD-Dry 1 + Cowbell 2/BD-Dry 1 + Bongo 1 Hi-Open/BD-Dry 1 + Vocal Cowbell/BD-Dry 1 + Bongo 1 Hi-Slap + Conga 1 Lo-Open + Conga 1 Lo-Slap/BD-Dry 1 + Conga 1 Lo-MuteSlap/BD-Dry 1 + Conga 1 Hi-MtSlap/BD-Dry 1 + Conga 1 Hi-Open + Conga 1 Hi-Mute/BD-Dry 1 + Conga 1-Heel/BD-Dry 1 + Conga 1 Hi-Slap 1/BD-Dry 1 + Conga 1-Toe/BD-Dry 1 + Conga 1 Hi-Slap 2/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Maracas-Pull/Push + Shaker 1 + Shaker 2 + Cabasa-Tap/Down + Cabasa-Tap/Up + Caxixi-Hard/Caxixi-Soft + Tambourine 1-Push/BD-Dry 1 + Tambourine 2 + Tambourine 1-Pull/BD-Dry 1 + Tambourine 3 + Sleigh Bell + Samba Whistle 1/BD-Dry 1 + Samba Whistle 1/BD-Dry 1 + Bell Tree/BD-Dry 1 + Samba Whistle 1/BD-Dry 1 + Marc Tree/BD-Dry 1 + Samba Whistle 1/BD-Dry 1 + Rap Sleigh bell + Rainstick/BD-Dry 1 + Chinese Gong/BD-Dry 1 + Bird 1/BD-Dry 1 + Bird 2/BD-Dry 1 + Bird 1/BD-Dry 1 + Cricket Spectrum/BD-Dry 1 + Marc Tree/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + + + + + + + + BD-Dry 1 + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestral BD + Orch.SD Roll 1 + Orchestral SD + Orch.SD Roll 2 + Orchestral SD + Orch Cymbal-Open + Orch Cymbal-Closed + Taiko-Open + Orch.SD Roll 1 + Orchestral SD + Orch.SD Roll 2 + Orchestral SD + Triangle-Mute + Triangle-Roll + Triangle-Open + Chinese Gong + Tambourine 3 + Woodblock 1 + Castanet-Double 1 + Woodblock 1 + Castanet-Double 2 + Woodblock 1 + Castanet-Single + Woodblock 1 + Woodblock 1 + Timpani + Timpani + Timpani + Timpani + Djembe-Open + Djembe-Mute + Djembe-Slap + Caxixi-Hard/Soft + Sleigh Bell + Cabasa-Up/Down + Finger Cymbal + Baya-Ghe + Baya-Open + Baya-Mute 1 + Baya-Mute 2 + Baya-Mute 3 + Baya-Mute 4 + Baya-Mute 5 + Tabla-Na + Tabla-Open + Tabla-Tin + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Mute 3 + Tabla-Mute 4 + Taiko-Open + Tsuzumi/Vibraslap + Taiko-Rim + Conga 1 Lo-Open/BD-Dry 1 + Conga 1 Lo-MuteSlap / Hi-Slap 2 + Conga 1 Hi-Mute / Bongo 1 Hi-Slap + Conga 1 Hi-Slap 1 / BD-Dry 1 + Cabasa-Up + Rainstick + Harp-Glissando + Rainstick + BD-Dry 1 + BD-Dry 1 + Gamelan + Gamelan + Bird 1 + Cricket Spectrum/BD-Dry 1 + Tribe + BD-Dry 1 + Gamelan + Gamelan/Tambura + Gamelan + Gamelan + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-amb 1 mf -L + BD-amb 1 f -L + BD-amb 2 mf -L + BD-amb 2 f -L + BD-Dry 1 + BD-Dry 2 + BD-Dry 3 + BD-Dry 4 + BD-Dry 5 + BD-Soft + BD-Soft Room + BD-Jazz 1 + BD-Jazz 2 + BD-Pillow + BD-Woofer + BD-Mondo Kill + BD-Terminator + BD-Tubby + BD-Gated + BD-Tight + BD-Squash + BD-Dance 03 + BD-Dark + BD-Ringy + BD-Dance Soft + BD-PhatJaz + Orchestral BD + SD-Dry 2 + SD-Dry 3 + SD-Dry 4 + SD-Normal + SD-GhostSet roll + SD-GhostSet p2 + SD-GhostSet p1 + SD-GhostSet f + SD-Full Room + SD-Off Center + SD-Jazz Ring + SD-Amb.Piccolo 1 + SD-Amb.Piccolo 2 + SD-Paper + SD-Classic Room + SD-Brush Hit + SD-Brush Tap 1 + SD-Brush Tap 2 + SD-Brush Swirl + SD-Atomic + SD-Big Rock + SD-Yowie + SD-Processed 1 + SD-Processed 2 + SD-Cracker Room + Orch.SD Roll 1 + SD-Sidestick Dry + Orch.SD Roll 2 + Crash Cymbal 2 + Orchestral SD + SD-Reverse + Hi-Hat 2-Closed 1 + SD-Hip 5 + Hi-Hat 2-Pedal + SD-AmbiHop + Hi-Hat 2-Open 1 + SD-Whopper + SD-Ambi Pop + SD-dry 1 p -L + SD-dry 1 mp -L + SD-dry 1 mf -L + SD-dry 1 f -L + SD-dry 1 ff -L + SD-dry 1 ff -L + SD-dry 1 fff -L + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 6 + BD-Dance 01 + BD-Dance 02 + BD-Dance 11 + BD-Dance 04 + BD-Hip 1 + BD-Hip 2 + BD-Hip 3 + BD-Hip 4 + BD-Pop + BD-Dance 08 + BD-Dance 14 + BD-Ambi + BD-Ambi Crackle + BD-Ambi Soft + BD-AmbiRocker + BD-Chili + BD-Dance 13 + BD-Dance 07 + BD-Cannon + BD-Klanger + BD-Dance 05 (Rap) + BD-Dance 06 (R&B) + BD-Dance 09 + BD-Dance 10 + BD-Dance 12 + SD-Ambi House + SD-Dance 1 + SD-Rap 1 + SD-Rap 2 + SD-Noise + SD-Sizzle + SD-Hip 1 + SD-Hip 2 + SD-Hip 3 + SD-Hip 4 + SD-Hip 6 + SD-Ringy + SD-Tiny + SD-Vintage 1 + SD-Vintage 2 + SD-Vintage 3 + SD-Vintage 4 + SD-Vintage 5 + SD-Vintage 6 + SD-AmbiCrackle 1 + SD-AmbiCrackle 2 + SD-AmbiCrackle 3 + SD-Brasser + SD-Chili + SD-Klanger + SD-Dry Present + SD-Ambi Hit 1 + SD-Ambi Hit 2 + Hi-Hat Dance 3 Closed + SD-Ambi Hit 3 + Hi-Hat Dance 1 Closed + SD-Dance 3 (New) + Hi-Hat Dance 1 Open + SD-Dance 4 (Old) + SD-Dance 5 + SD-Dance 8 + Vocal SD + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Maracas-Push + Maracas-Pull + Caxixi-Hard + Dance Perc 1 Hip + Tambourine 1-Push + Tambourine 1-Pull + Tambourine 2 + Tambourine 3 + BD-Hip 3 + Hi-Hat 3-Closed 1 + SD-Chili + Hi-Hat 3-Closed 2 + Hi-Hat 2-Closed 1 + Hi-Hat 2-Closed 2 + Hi-Hat 3-Open 2 + Hi-Hat 2-Open 2 + Hi-Hat 3-Open 1 + Hi-Hat 2-Open 1 + Hi-Hat 3-Sizzle + Hi-Hat 2-Sizzle + Hi-Hat 2-Pedal + Hi-Hat 3-Pedal + Hi-Hat 2-Pedal Open + Hi-Hat 3-Pedal Open + Hi-Hat-Chili + Hi-Hat-Crisp Closed 1 + Hi-Hat-Vintage 1 + Hi-Hat-Crisp Closed 2 + Hi-Hat-Vintage 2 + Hi-Hat-Crisp Open + Hi-Hat-Tight 1 + Hi-Hat-Tight 2 + Hi-Hat-Old Open + Hi-Hat-Old Closed 1 + Hi-Hat-Clangy Open + Hi-Hat-Old Closed 2 + Hi-Hat-Old Closed 3 + Hi-Hat-Alpo Closed + Hi-Hat-Amb Crackle + Hi-Hat-Whispy + Hi-Hat-Ambi + Hi-Hat-Hip 2 + Hi-Hat-Hip 1 + Hi-Hat Dance 1 Closed + Sleigh Bell + Hi-Hat Dance 1 Open + Rap Sleigh bell + Hi-Hat Dance 3 Closed + Hi-Hat Dance 3 Open + Crash Cymbal 4 + Crash-Dance 3 + Crash Cymbal 2 + China Cymbal + Crash Cymbal 3 + Splash Cymbal + Ride-Dance 1 + Crash-Dance 1 + Ride-Dance 2 DDD1 + Crash-Dance 2 DDD1 + Ride Cymbal 4 Edge + Ride Cymbal 5 Edge + Ride Cymbal-Jazz + Ride Cymbal Cup 2 + Cymbal Reverse + Splash Cymbal + Crash-Dance 1 + Ride-Dance 1 + Ride Cymbal Cup 2 + Hi-Hat Dance 1 Open + Hi-Hat-Old Closed 2 + Hi-Hat-Old Closed 3 + Hi-Hat-Crisp Closed 2 + Hi-Hat Dance 1 Closed + Hi-Hat 3-Closed 1 + Hi-Hat Dance 3 Closed + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Tight + BD-Dry 3 + SD-Sidestick Ambi + SD-Full Room + Dance Claps 1 + SD-Full Room + Tom 3-Floor + Hi-Hat 2-Open 2 + Tom 3-Floor + Hi-Hat 3-Pedal + Tom 3-Lo + Hi-Hat 3-Open 2 + Tom 3-Lo + Tom 3-Hi + Crash Cymbal 3 + Tom 3-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Dry 2 + BD-Dry 3 + SD-Sidestick Ambi + SD-Yowie + Dance Claps 1 + SD-Yowie + Tom 4-Floor + Hi-Hat 2-Open 2 + Tom 4-Floor + Hi-Hat 3-Pedal + Tom 4-Lo + Hi-Hat 3-Open 2 + Tom 4-Lo + Tom 4-Hi + Crash Cymbal 3 + Tom 4-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Gated + BD-Mondo Kill + SD-Sidestick Ambi + SD-Big Rock + Dance Claps 1 + SD-Big Rock + Tom 4-Floor + Hi-Hat 2-Open 2 + Tom 4-Floor + Hi-Hat 3-Pedal + Tom 4-Lo + Hi-Hat 3-Open 2 + Tom 4-Lo + Tom 4-Hi + Crash Cymbal 3 + Tom 4-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Hip 1 + BD-Dance 14 + SD-Sidestick Ambi + Tom-Synth + Dance Claps 1 + SD-Ambi Hit 1 + Tom-Synth + Hi-Hat 3-Sizzle + Tom-Synth + Hi-Hat 3-Pedal + Tom-Synth + Hi-Hat 3-Sizzle + Tom-Synth + Tom-Synth + Crash Cymbal 3 + Tom-Synth + Ride Cymbal 4 Edge + Crash Cymbal 2 + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Dance 06 (R&B) + BD-Dance 09 + Rimshot-Dance 2 + SD-Dance 3 (New) + Dance Claps 1 + SD-Dance 4 (Old) + Tom-Dance + Hi-Hat Dance 3 Open + Tom-Dance + Hi-Hat Dance 3 Open + Tom-Dance + Hi-Hat Dance 3 Open + Tom-Dance + Tom-Dance + Ride-Dance 1 + Tom-Dance + Crash-Dance 3 + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Dry 4 + BD-Jazz 2 + SD-Sidestick Ambi + SD-Paper + Dance Claps 1 + SD-Jazz Ring + Tom-Jazz Floor + Hi-Hat 2-Closed 1 + Tom-Jazz Floor + Hi-Hat 2-Pedal + Tom-Jazz Hi + Hi-Hat 2-Open 1 + Tom-Jazz Hi + Tom-Jazz Hi + Crash Cymbal 2 + Tom-Jazz Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Dry 4 L/R Off + BD-Jazz 2 L/R Off + SD-Sidestick Ambi L/R Off + SD-Brush Tap 1 L/R Off + SD-Brush Hit L/R Off + SD-Brush Swirl L/R Off + Tom-Jazz Floor L/R Off + Hi-Hat 2-Closed 1 L/R 1 + Tom-Jazz Floor L/R Off + Hi-Hat 2-Pedal L/R 1 + Tom-Jazz Hi L/R Off + Hi-Hat 2-Open 1 L/R 1 + Tom-Jazz Hi L/R Off + Tom-Jazz Hi L/R Off + Crash Cymbal 2 L/R Off + Tom-Jazz Hi L/R Off + Ride Cymbal 4 Edge L/R Off + China Cymbal L/R Off + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Hi-Hat 2-Closed 1 + Hi-Hat 2-Pedal + Hi-Hat 2-Open 1 + Ride Cymbal 4 Edge + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Soft Room + Orchestral BD + SD-Sidestick Ambi + Orchestral SD + Conga 2 Hi-Open mf + Orchestral SD + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Baya-Openf + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Orch Cymbal-Open + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Tight + BD-Dry 3 + SD-Sidestick Ambi + SD-Full Room + Perc-Granular 17-EXs5-L + CompuVoice-Toi + Perc-Granular 21-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Finger Snap + "Cap" + Crickets 1 + Door Slam + Rainstick + Door Creak + Tribe + Finger Snap + "Tehh" + "Kaahh" + CompuVoice-Do it + CompuVoice-Test + "Ti" + "Pan" + "Thuum" + "Four..." Solo + "Three..." Solo + Perc-Granular 22-EXs5 + Tambourine 2-EXs5 + "Two..." Solo + "One..." Solo + PC Vox-Sis + PC Vox-Dunc + CompuVoice-Chi + PC Vox-For + Dog + PC Vox-One + Footstep 2 + PC Vox-If + PC Vox-Zee + PC Vox-Ae + PC Vox-Pa + "Haaa" + CompuVoice-Beep + "Chhi" L/R Off + CompuVoice-Ti + PC Vox-O + PC Vox-Tu + Dog + Stadium + Applause + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +When received this message, transmits INQUlRY MESSAGE REPLY +[ F0,7E,nn,06,01,F7 ] 3rd byte nn : Channel = 0 - F : Global Channel + = 7F : Any Channel + + F0 7E 7F 06 01 F7 + + + + +Received when in Sequencer mode +[ F0,7E,nn,09,01,F7 ] 3rd byte nn : Channel = 0 - F : Global Channel + = 7F : Any Channel + + F0 7E 7F 09 01 F7 + + + + +Master Volume +[ F0,7F,0g,04,01,vv,mm,F7 ] 3rd byte g : Global Channel + 6th byte vv : Value(LSB) + 7th byte mm : Value(MSB) + mm,vv = 00,00 - 7F,7F : Min - Max + + F0 7F 00 04 01 00 00 F7 + + + + +Master Balance +[ F0,7F,0g,04,02,vv,mm,F7 ] 3rd byte g : Global Channel + 6th byte vv : Value(LSB) + 7th byte mm : Value(MSB) + mm,vv = 00,00:Left, 40,00:Center, 7F,7F:Right + + F0 7F 00 04 02 00 00 F7 + + + + +Control Master Tune(cent) in Global +[ F0,7F,0g,04,03,vv,mm,F7 ] 3rd byte g : Global Channel + 6th byte vv : Value(LSB) + 7th byte mm : Value(MSB) + mm,vv = 20,00:-50, 40,00:+00, 60,00:+50 + + F0 7F 00 04 03 00 00 F7 + + + + +Control Transpose (chromatic step) in Global +[ F0,7F,0g,04,04,vv,mm,F7 ] 3rd byte g : Global Channel + 6th byte vv : Value(LSB) + 7th byte mm : Value(MSB) + mm,vv = 34,00:-12, 40,00:+00, 4C,00:+12 + + F0 7F 00 04 04 00 00 F7 + + + + +MODE REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 12 Function + F7 End of Excl +(Receives this message, and transmits Func=42 message) + + F0 42 30 00 01 15 12 F7 + + + + +MODE DATA + F0, 42, 3g, 00, 01, 15 Excl Header + 42 Function + 0000 0mmm Mode (*2) + 0000 0000 reserved + 0sss ssss data1 + 0ddd dddd data2 + 00 Reserved + F7 End of Excl +(Receives FUNC=12 message, and transmits this message & data) + + ss : bit 0,1 = 0 : Note Receive is EVEN, = 1 : ODD, = 2 : ALL + bit 3,4 = 0 : Seq Clock is internal, = 1 : External(MIDI), = 2 : External(USB) + + dd : bit 0 = 0 : Programs Memory is not protected, = 1 : protected + bit 1 = 0 : Combinations Memory is not protected, = 1 : protected + bit 2 = 0 : Sequence Memory is not protected, = 1 : protected + bit 3 = 0 : Drum Kits Memory is not protected, = 1 : protected + bit 4 = 0 : Arp Patterns Memory is not protected, = 1 : protected + + F0 42 30 00 01 15 42 00 00 00 00 00 F7 + + + + +MODE CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 4E Function + 0000 0mmm Mode (*2) + F7 End of Excl +(Receives this message & data, changes the Mode, and transmits Func=24 message) +When the Mode is changed by SW, transmits this message & data. + + F0 42 30 00 01 15 4E 00 F7 + + + + +PARAMETER CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 41 Function + 0000 0mmm Mode (*2) + 00 Parameter ID (MSB) + 0ppp pppp Parameter ID (LSB) (TABLE 1,2,5,10,11) + 00 Parameter SUB ID (MSB) + 0qqq qqqq Parameter SUB ID (LSB) (TABLE 1,2,5,10,11) + 0000 vvvv Value (Value bit28-31) + 0vvv vvvv Value (Value bit21-27) + 0vvv vvvv Value (Value bit14-20) + 0vvv vvvv Value (Value bit7-13) + 0vvv vvvv Value (Value bit0-6) + F7 End of Excl +(Even if it receives this message, a reply does not return) +When the Parameter No. is changed by SW, transmits this message & data. + + F0 42 30 00 01 15 41 00 00 00 00 00 00 00 00 00 00 F7 + + + + +DRUM KIT PARAMETER CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 53 Function + 0sss ssss Index No. (s = 00-7F ( : C-1-G9)) + 0bbb bbbb Drum Kit Bank (*3) + 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) + 00 Parameter No.(MSB) + 0ppp pppp Parameter No.(LSB) (TABLE 6) + 0000 vvvv Value (Value bit28-31) + 0vvv vvvv Value (Value bit21-27) + 0vvv vvvv Value (Value bit14-20) + 0vvv vvvv Value (Value bit7-13) + 0vvv vvvv Value (Value bit0-6) + F7 End of Excl +(Even if it receives this message, a reply does not return) + + F0 42 30 00 01 15 53 00 00 00 00 00 00 00 00 00 00 F7 + + + + +ARPEGGIO PATTERN PARAMETER CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 54 Function + 0000 000b Arpeggio AorB b = 0 : Arpeggio A, 1 : Arpeggio B + 0000 aaaa Pattern No. MSB (bit10-7) + 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) + 0sss ssss Step No. ss = 00-2F ( : 00-47) + 0ttt tttt Tone No. tt = 00-0B ( : 00-11) + 00 Parameter No. (MSB) + 0ppp pppp Parameter No. (LSB) (TABLE 4) + 0000 vvvv Value (Value bit28-31) + 0vvv vvvv Value (Value bit21-27) + 0vvv vvvv Value (Value bit14-20) + 0vvv vvvv Value (Value bit7-13) + 0vvv vvvv Value (Value bit0-6) + F7 End of Excl +(Even if it receives this message, a reply does not return) + + F0 42 30 00 01 15 54 00 00 00 00 00 00 00 00 00 00 00 00 F7 + + + + +PROGRAM PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 00 Object(Program) + 0bbb bbbb Program Bank (*3) + 00 Reserved + 0ppp pppp Program No. + F7 End of Excl +(Receives this message, and transmits Func=73/Object=00 or Func=24 message) + + F0 42 30 00 01 15 72 00 00 00 00 F7 + + + + +COMBINATION PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 01 Object(Combination) + 0bbb bbbb Combination Bank (*3) + 00 Reserved + 0ccc cccc Combination No. + F7 End of Excl +(Receives this message, and transmits Func=73/Object=01 or Func=24 message) + + F0 42 30 00 01 15 72 01 00 00 00 F7 + + + + +SONG TIMBRE SET DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 02 Object(Song Timbre Set) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=02 or Func=24 message) + + F0 42 30 00 01 15 72 02 00 00 00 F7 + + + + +GLOBAL DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 03 Object(Global) + 00 Reserved + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=03 or Func=24 message) + + F0 42 30 00 01 15 72 03 00 00 00 F7 + + + + +DRUM KIT PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 04 Object(Drum Kit) + 0bbb bbbb Drum Kit Bank (*3) + 00 Reserved + 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=04 or Func=24 message) + + F0 42 30 00 01 15 72 04 00 00 00 F7 + + + + +SONG CONTROL DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 08 Object(Song Control) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=08 or Func=24 message) + + F0 42 30 00 01 15 72 08 00 00 00 F7 + + + + +SONG EVENT DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 09 Object(Song Event) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=09 or Func=24 message) + + F0 42 30 00 01 15 72 09 00 00 00 F7 + + + + +DRUM TRACK PATTERN DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 0B Object(Drum Track Pattern) + 00 Reserved + 0000 0kkk Pattern No. MSB + 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=0B&0C or Func=24 message) + + F0 42 30 00 01 15 72 0B 00 00 00 F7 + + + + +PROGRAM BANK PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 0F Object(Program Bank) + 0bbb bbbb Program Bank (*3) + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=0F or Func=24 message) + + F0 42 30 00 01 15 72 0F 00 00 00 F7 + + + + +COMBINATION BANK PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 11 Object(Combination Bank) + 0bbb bbbb Combination Bank (*3) + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=11 or Func=24 message) + + F0 42 30 00 01 15 72 11 00 00 00 F7 + + + + +DRUM KIT BANK PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 12 Object(Drum Kit Bank) + 0bbb bbbb Drum Kit Bank (*3) + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=12 or Func=24 message) + + F0 42 30 00 01 15 72 12 00 00 00 F7 + + + + +ARPEGGIO PATTERN PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 16 Object(Arpeggio Pattern) + 00 Reserved + 0000 aaaa Pattern No. MSB (bit10-7) + 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=16 or Func=24 message) + + F0 42 30 00 01 15 72 16 00 00 00 F7 + + + + +ARPEGGIO PATTERN ALL PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 17 Object(Arpeggio Pattern All) + 00 Reserved + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=17 or Func=24 message) + + F0 42 30 00 01 15 72 17 00 00 00 F7 + + + + +PROGRAM PARAMETER (IN INTERNAL MEMORY) DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 00 Object(Program) + 0bbb bbbb Program Bank (*3) + 00 Reserved + 0ppp pppp Program No. + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 1) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=00 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 00 00 00 00 00 00 F7 + + + + +COMBINATION PARAMETER (IN INTERNAL MEMORY) DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 01 Object(Combination) + 0bbb bbbb Combination Bank (*3) + 00 Reserved + 0ccc cccc Combination No. + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 2) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=01 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 01 00 00 00 00 00 F7 + + + + +SONG TIMBRE SET DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 02 Object(Song Timbre Set) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 10) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=02 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 02 00 00 00 00 00 F7 + + + + +GLOBAL DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 03 Object(Global) + 00 Reserved + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 3) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=03 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 03 00 00 00 00 00 F7 + + + + +DRUM KIT PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 04 Object(Drum Kit) + 0bbb bbbb Drum Kit Bank (*3) + 00 Reserved + 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 6) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=04 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 04 00 00 00 00 00 F7 + + + + +SONG CONTROL DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 08 Object(Song Control) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 11) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=08 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 08 00 00 00 00 00 F7 + + + + +SONG EVENT DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 09 Object(Song Event) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 12) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=09 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 09 00 00 00 00 00 F7 + + + + +DRUM TRACK PATTERN DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 0B Object(Drum Track Pattern) + 00 Reserved + 0000 0kkk Pattern No. MSB + 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 7) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=0B message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 0B 00 00 00 00 00 F7 + + + + +DRUM TRACK PATTERN EVENT DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 0C Object(Drum Track Pattern Event) + 000n nnnn Drum Track Pattern Event Num Packet (0-26) + 0000 0kkk Pattern No. MSB + 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 8) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=0B message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 0C 00 00 00 00 00 F7 + + + + +CUELIST DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 0D Object(CueList) + 00 Reserved + 00 Reserved + 000c cccc Cuelist No. c = 0-13 ( : 0-19) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 9) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 0D 00 00 00 00 00 F7 + + + + +PROGRAM BANK PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 0F Object(Program Bank) + 0bbb bbbb Program Bank (*3) + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 1) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=0F message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 0F 00 00 00 00 00 F7 + + + + +COMBINATION BANK PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 11 Object(Combination Bank) + 0bbb bbbb Combination Bank (*3) + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 2) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=11 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 11 00 00 00 00 00 F7 + + + + +DRUM KIT BANK PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 12 Object(Drum Kit Bank) + 0bbb bbbb Drum Kit Bank (*3) + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 6) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=12 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 12 00 00 00 00 00 F7 + + + + +ARPEGGIO PATTERN PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 16 Object(Arpeggio Pattern) + 00 Reserved + 0000 aaaa Pattern No. MSB (bit10-7) + 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 4) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=16 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 16 00 00 00 00 00 F7 + + + + +ARPEGGIO PATTERN ALL PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 17 Object(Arpeggio Pattern All) + 00 Reserved + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 4) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=17 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 17 00 00 00 00 00 F7 + + + + +CURRENT OBJECT PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 74 Function + 0000 oooo Object (o = 0:Program/1:Combination/2:Song Timbre Set) + F7 End of Excl +(Receives this message, and transmits Func=75/Object=00(/01/02) or Func=24 message) + + F0 42 30 00 01 15 74 00 F7 + + + + +CURRENT PROGRAM PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 75 Function + 00 Object(Program) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 1) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=74/Object=00 message, and transmits this message & data. + + F0 42 30 00 01 15 75 00 00 00 F7 + + + + +CURRENT COMBINATION PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 75 Function + 01 Object(Combination) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 2) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=74/Object=01 message, and transmits this message & data. + + F0 42 30 00 01 15 75 01 00 00 F7 + + + + +CURRENT SONG TIMBRE SET DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 75 Function + 02 Object(Current Song Timbre) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 10) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=74/Object=02 message, and transmits this message & data. + + F0 42 30 00 01 15 75 02 00 00 F7 + + + + +STORE OBJECT PROGRAM REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 00 Object(Program) + 0bbb bbbb Bank No. (*3) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 00 00 F7 + + + + +STORE OBJECT COMBINATION REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 01 Object(Combination) + 0bbb bbbb Bank No. (*3) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 01 00 F7 + + + + +STORE OBJECT CURRENT SONG TIMBRE SET REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 02 Object(Song Timbre Set) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 02 00 F7 + + + + +STORE OBJECT DRUM KIT REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 04 Object(Drum Kit) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 04 00 F7 + + + + +STORE OBJECT PROGRAM BANK REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 0F Object(Program Bank) + 0bbb bbbb Bank No. (*3) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 0F 00 F7 + + + + +STORE OBJECT COMBINATION BANK REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 11 Object(Combination Bank) + 0bbb bbbb Bank No. (*3) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 11 00 F7 + + + + +STORE OBJECT DRUM KIT BANK REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 12 Object(Drum Kit Bank) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 12 00 F7 + + + + +STORE OBJECT ARPEGGIO PATTERN REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 16 Object(Arpeggio Pattern) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 16 00 F7 + + + + +STORE OBJECT ARPEGGIO PATTERN ALL REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 17 Object(Arpeggio Pattern All) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 17 00 F7 + + + + +OBJECT WRITE REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 77 Function + 0000 oooo Object (o = 0:Program/1:Combination) + 0bbb bbbb Bank (*3) + 00 Reserved + 0iii iiii Index No. (i = Program or Combination) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 77 00 00 00 00 F7 + + + + +RESULT REPORT + F0, 42, 3g, 00, 01, 15 Excl Header + 24 Function + 0ccc cccc Result Code + F7 End of Excl +(Transmits this message when there is a result in the MIDI IN message) + + cc = 0 : Result OK + 1 : Received Data Length is wrong + 2 : Dest Memory is protected + 3 : Dest Bank/Prog/Param is not exist + 4 : The mode is wrong + 5 : Memory over flow + 40 : Another type error + + F0 42 30 00 01 15 24 00 F7 + + + + +WAVEFORM CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 7F Function + 00 Object(CHANGE) + 0000 cccc MIDI Channel c = 0-15 + 0000 000o OSC Select o = 0 or 1 + 0000 0bbb Bank Select b = 0 (Mono) ~ 1 (Stereo) + 0nnn nnnn Number LSB + 0000 nnnn Number MSB + 0000 ssss Start Offset s = 0 (Off) ~ 8 (8ch) + 0000 000t OSC Switch t = 0 (Off) or 1 (On) + F7 End of Excl + + n = 0 ~ 1076 (In case of "Bank:Mono") + n = 0 ~ 6 (In case of "Bank:Stereo") + + F0 42 30 00 01 15 7F 00 00 00 00 00 00 00 00 F7 + + + + +WAVEFORM RESET + F0, 42, 3g, 00, 01, 15 Excl Header + 7F Function + 01 Object(RESET) + 0000 cccc MIDI Channel c = 0-15 + F7 End of Excl + + F0 42 30 00 01 15 7F 01 00 F7 + + + diff -Nru "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Korg Krome (Bank Map KORG).idf" "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Korg Krome (Bank Map KORG).idf" --- "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Korg Krome (Bank Map KORG).idf" 1970-01-01 00:00:00.000000000 +0000 +++ "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Korg Krome (Bank Map KORG).idf" 2018-01-26 21:59:38.000000000 +0000 @@ -0,0 +1,6622 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Closed + Hi-Hat-Whispy + Hi-Hat Dance 4 Open + Timbales-Paila / Shaker + Hi-Hat-Crisp Open/Chili/ Crisp Open + Hi-Hat-Vintage + Hi-Hat 2-Pedal Open/Closed/Pedal + BD-Dance 04 L/R + BD-Dance 02 + BD-Dance 04 + Dance 07 + BD-Dance 10 + Orch.SD Roll 1 + Orch.SD Roll 2 + Orch.SD Roll 1 + Orch.SD Roll 2 + SD-Sidestick Hi + Cymbal Reverse + SD-Reverse IFX1 + BD-01a-dry + BD-03a-dry + BD-03a-dry + BD-02b-dry + BD-02a-dry + SD-06-dry + SD-05-dry + SD-04-dry + SD-03-dry + SD-02-dry + SD-07-dry + BD-01b-dry + BD-01a-dry + Sidestick-dry + SD-01a-dry + Hand Claps 1 + SD-01b-dry + Tom Lo-dry + HiHat-Closed-dry + Tom Lo-dry + HiHat-Pedal1-dry + Tom Mid-dry + HiHat-Open-dry + Tom Mid-dry + Tom Hi-dry + Crash 01-dry + Tom Hi-dry + Ride 01-dry + China-dry + Ride 02-dry + Tambourine 3/1-Push/1-Pull + Splash-dry + Cowbell 2 + Crash 02-dry + Vibraslap + Ride + Bongo 2 Hi-Open f/mf/f + Bongo 2 Lo-Muffled f/mp/Lo-Closed + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/mf/f/mp + Conga 2 Lo-OpenSlap/mf/f + Timbales Hi-Rim 1/2/Hi-Edge + Timbales Lo-Rim/Open + Agogo Bell IFX5 Off + Agogo Bell IFX5 Off + Cabasa-Up/Down + Maracas-Pull/Push + Samba Whistle 2 + Samba Whistle 2 + Guiro-Short + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 2-Hi + Cuica 2-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + BD-Dance 13-EXs5 + BD-Dance 12-EXs5 + BD-Mad Analog 03-EXs5 + BD-HipHop 07-EXs5 + BD-Dance 15-EXs5 + SD-Mad Analog 11-EXs5 + SD-Granular 05-EXs5-R + SD-Mad Analog 05-EXs5 + SD-Dance 12-EXs5 + SD-Mad Analog 13-EXs5 + BD-Mad Analog 06-EXs5 + BD-Mad Analog 07-EXs5 + BD-Dance 11 + BD-Dance 03 + BD-Dance 08 + BD-Dance 01 + BD-Dance 07/Tom-Dance + SD-Dance 4 (Old) + SD-Dance 5 + Noise-White/SD-Sidestick Ambi + SD-Rap 2 + Dance Claps 3/Click + SD-Dance 6 + BD-Dance 06 (R&B) + BD-Dance 07 + Rimshot-Dance 2 + SD-Dance 3 (New) + Dance Claps 1 + SD-Dance 2/SD-Sidestick Dry + Tom-Dance + Hi-Hat Dance 3 Closed + Tom-Dance + Hi-Hat Dance 3 Open + Tom-Dance + Hi-Hat Dance 3 Open + Dance Perc 8 Maraca + Hi-Hat Dance 4 Closed + Crash-Dance 3 + Hi-Hat Dance 4 Open + Ride-Dance 1 + Crash-Dance 2 DDD1 + Ride-Dance 2 DDD1 + Tambourine 3/2 + Noise-VPM Mod + Dance Perc 7 Cowbell + Crash-Dance 1 + Cricket Spectrum/Tambourine 3 + Perc-Mad Analog 06-EXs5 + Zap 1 + Zap 2 + Dance Perc 6 Clave/BD-Dance 05 (Rap) + Dance Perc 5 Conga + Dance Perc 5 Conga + HiHat Mad Analog 01-EXs5 + Triangle-Mute/BD-Dance 05 (Rap) + HiHat Mad Analog 16-EXs5 + Triangle-Open/Tom-Brush Hi + Hi-Hat Dance 5 Closed/DJ-SD Rub + Hi-Hat Dance 5 Open + Rimshot-Dance 1 + PC Vox-Cks/BD-Woofer + Hi-Hat Dance 1 Closed/BD-Dry 1 + Noise-White/SD-Amb.Piccolo 1 + Hi-Hat Dance 2 Open/DJ-Record Stop + PC Vox-Sis/SD-Paper + PC Vox-Dunc/Vocal SD + Hi-Hat-Alpo Closed + PC Vox-For/Tom-Brush Hi + Hi-Hat-Amb Crackle/Ride-Dance 1 + PC Vox-Zee/Tom-Jazz Hi Rim + Hi-Hat-Crisp Open/Ride-Dance 1 + PC Vox-Pa/Swish Terra + PC Vox-Tu/BD-Jazz 2 + Crash Cymbal 2 + China Cymbal + Ride Cymbal 4 Edge + Conga 1 Hi-Slap 1/Lo-Open + Conga 1 Hi-Open/Lo-MuteSlap + Ride Cymbal Cup 2 + Conga 1 Hi-Slap 2/Hi-Mute + Cowbell + Chacha Bell + Cowbell 1 + Guiro-Short/BD-Dry 1 + Guiro-Tap + Metal Hit/Finger Cymbal + Dance Perc 1 Hip/BD-Dry 1 + Swish Terra + Cyber Drum + Tambourine 1-Push + Air Vortex + Tubular + Telephone Ring + Footstep 2 + Triangle-Mute + Tribe + Dance Perc 1 Hip/BD-Dry 1 + + + + + + + + BD-amb 1 mf -L + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open + Hi-Hat-Chili + Hi-Hat-Old Open + Timbales-Paila + Hi-Hat-Crisp Open + Hi-Hat-Vintage 1 + Hi-Hat 3-Pedal Open + BD-Dance 01-EXs5 + BD-Dance 14-EXs5 + BD-TekClub 12-EXs5 + BD-HipHop 08b/a-EXs5 + BD-Hip 2 + SD-Dance 09-EXs5 + SD-TekClub 04-EXs5 + SD-HipHop 07-EXs5 + SD-Mad Analog 15-EXs5 + SD-TekClub 16-EXs5 + Cymbal Reverse + SD-Ringy + BD-Dance 15-EXs5 + BD-Dance 10-EXs5 + BD-TekClub 03-EXs5 + BD-Dance 08-EXs5 + BD-Dance 11-EXs5 + SD-Dance 14-EXs5 + SD-Dance 19-EXs5 + SD-Dance 12-EXs5 + SD-Dance 20-EXs5 + SD-Dance 21-EXs5 + SD-Dance 18/17-EXs5 + BD-Dance 12-EXs5 + BD-Dance 13-EXs5 + SD-Dance 13-EXs5 + SD-Dance 15-EXs5 + SD-Dance 16-EXs5 + SD-Dance 14-EXs5 + Tom-Dance d-EXs5 + Hi-Hat Dance 06-EXs5 + Tom-Dance d-EXs5 + Hi-Hat Dance 07-EXs5 + Tom-Dance e-EXs5 + Hi-Hat Dance 11-EXs5 + Tom-Dance f-EXs5 + Tom-Dance g-EXs5 + Crash Dance 1-EXs5 + Tom-Dance h-EXs5 + Crash Dance 4-EXs5 + Crash Dance 3-EXs5 + Ride HipHop 2-EXs5 + Tambourine 3/1-EXs5 + Crash Dance 2-EXs5 + Perc-Dance 01-EXs5 + Crash HipHop 1-EXs5-R + Perc-Dance 09-EXs5 + Ride HipHop 1-EXs5 + Perc-Dance 02-EXs5 + Perc-Dance 03-EXs5 + Perc-Dance 12-EXs5 + Perc-Dance 05-EXs5 + Perc-Dance 04-EXs5 + Hi-Hat Dance 09-EXs5 + Hi-Hat Dance 10-EXs5 + Perc-Mad Analog 19-EXs5 + Perc-Mad Analog 19-EXs5 + Shaker 4-EXs5 + Shaker 5-EXs5 + Orchestral BD + Orchestral BD + Zap-EXs5 + Perc-Dance 07-EXs5 + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Closed + Hi-Hat-Whispy + Hi-Hat Dance 4 Open + Timbales-Paila / Shaker + Hi-Hat-Crisp Open/Chili/ Crisp Open + Hi-Hat-Vintage + Hi-Hat 2-Pedal Open/Closed/Pedal + BD-Dance 04 L/R + BD-Dance 02 + BD-Dance 04 + Dance 07 + BD-Dance 10 + Orch.SD Roll 1 + Orch.SD Roll 2 + Orch.SD Roll 1 + Orch.SD Roll 2 + SD-Sidestick Hi + Cymbal Reverse + SD-Reverse IFX1 + BD-01a-dry + BD-03a-dry + BD-03a-dry + BD-02b-dry + BD-02a-dry + SD-06-dry + SD-05-dry + SD-04-dry + SD-03-dry + SD-02-dry + SD-07-dry + BD-01b-dry + BD-01a-dry + Sidestick-dry + SD-01a-dry + Hand Claps 1 + SD-01b-dry + Tom Lo-dry + HiHat-Closed-dry + Tom Lo-dry + HiHat-Pedal1-dry + Tom Mid-dry + HiHat-Open-dry + Tom Mid-dry + Tom Hi-dry + Crash 01-dry + Tom Hi-dry + Ride 01-dry + China-dry + Ride 02-dry + Tambourine 3/1-Push/1-Pull + Splash-dry + Cowbell 2 + Crash 02-dry + Vibraslap + Ride + Bongo 2 Hi-Open f/mf/f + Bongo 2 Lo-Muffled f/mp/Lo-Closed + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/mf/f/mp + Conga 2 Lo-OpenSlap/mf/f + Timbales Hi-Rim 1/2/Hi-Edge + Timbales Lo-Rim/Open + Agogo Bell IFX5 Off + Agogo Bell IFX5 Off + Cabasa-Up/Down + Maracas-Pull/Push + Samba Whistle 2 + Samba Whistle 2 + Guiro-Short + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 2-Hi + Cuica 2-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + HHat-Dance5-amb1 + HHat-Dance7-amb1 + HHat-Whispy-amb1 + HHat-Dance4/1-amb1 + TimbalesH-Paila-a1 / Shaker 1-amb1 + H.Hat-Crisp/Chili-amb1 + H.Hat-Old-amb1 + HHat-Pedal3/2-amb1 + BD-amb-01 + BD-amb-02 + BD-amb-03 + BD-amb-04 + BD-amb-05 + SD-Orch Roll-amb1 + SD-Orch Roll-amb1 + SD-Orch Roll-amb1 + SD-Orch Roll-amb1 + Stick Hit-amb + Reverse Cymbal-amb + SD Reverse-amb + BD-01a-amb1 + BD-03b-amb1 + BD-03a-amb1 + BD-02b-amb1 + BD-02a-amb1 + SD-06-amb1 + SD-05-amb1 + SD-04-amb1 + SD-03-amb1 + SD-02-amb1 + SD-07-amb1 + BD-01b-amb1 + BD-01a-amb1 + Sidestick-amb1 + SD-01a-amb1 + Claps-Dance-amb1 + SD-01b-amb1 + Tom Lo-amb1 + HiHat-Cl-amb1 + Tom Lo-amb1 + HHat-Pdl1-amb1 + Tom Mid-amb1 + HiHat-Op-amb1 + Tom Mid-amb1 + Tom Hi-amb1 + Crash 01-amb1 + Tom Hi-amb1 + Ride 01-amb1 + China-amb1 + Ride 02-amb1 + Tambourine 3/1/2-amb1 + Splash-amb1 + Cowbell 2-amb1 + Crash 02-amb1 + Vibra-Slap-amb1 + Ride 03-amb1 + Bongo H-Op f-amb1 + Bongo L-Muf1/L-Muf2/Lo-Cl-amb1 + Conga H-Slp2/Hi-Op1-amb1 + Conga H-Slp1/Hi-Op2/Hi-Op3/Hi-Op1-amb1 + Conga L-Slap/Lo-Op1/Lo-Op2-amb1 + Timbales H-Rim2/H-Rim1/H-Edge-a1 + Timbales L-Ege1/L-Op-a1 + Agogo Lo-amb1 + Agogo Hi-amb1 + Cabasa 1/3-amb1 + Maracas-Pull1-amb1 + Whistle 3-amb1 + Whistle 3-amb1 + Guiro-Short a-amb1 + Guiro-Long a-amb1 + Claves b-amb1 + Woodblock 1/2-amb1 + Woodblock 3/4-amb1 + Cuica Hi c-amb1 + Cuica Lo-amb1 + Triangle-Mute1/2-a1 + Triangle-Mute1/2-a1 + Shaker 1/2-amb1 + Marc Tree-amb1 + BD-Dance-amb-01 + HHat Dance8-amb1 + SD-Dance-amb1 + HHat Dance7-amb1 + Crash-Dance-amb1 + Perc-Dance1-amb1 + Claps-Dance-amb1 + Perc-Dance2-amb1 + SD-Orch Roll-amb1 + SD-Orch-amb1 + Bell Tree-amb1 + Stadium-amb1 + Applause-amb1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute1/3 + Baya-Mute5/4 + Tabla-Open + Tabla-Tin/Mute4 + Drumstick Hit/BD-Dry 1 + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry + BD-Dry 1 + BD-Dry 4 + BD-Soft + BD-Soft Room + BD-Pillow + SD-Full Room + SD-Classic Room + SD-Amb.Piccolo 1 + SD-Paper + SD-Dry 4 + SD-Dry 2 + BD-Dry 3 + BD-Dry 2 + SD-Sidestick Dry + SD-Amb.Piccolo 2 + Hand Claps 2 + SD-Big Rock + Tom 3-Floor + Hi-Hat 2-Closed 2 + Tom 3-Floor + Hi-Hat 2-Pedal Open + Tom 3-Lo + Hi-Hat 2-Open 1/2 + Tom 3-Lo + Tom 3-Hi + Crash Cymbal 3/BD-Dry 1 + Tom 3-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2 + Splash Cymbal + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push + Samba Whistle 1/Chinese Gong + Samba Whistle 1/Chinese Gong + Guiro-Short/Tap + Guiro-Long/BD-Dry 1 + Claves/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute/3 + Baya-Mute5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit/BD-Dry 1 + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-Tubby + BD-Gated + BD-Chili + BD-Hip 4 + BD-PhatJaz + SD-Chili/SD-Jazz Ring + SD-Ambi Hit 2/SD-Classic Room + SD-Brasser/SD-Paper + SD-Vintage 4/SD-Brush Tap 1 + SD-Hip 5 + SD-GhostSet roll + BD-Dry 6 + BD-Dry 5 + SD-Sidestick Ambi + SD-GhostSet f/p2 + Hand Claps 2 + SD-Klanger/GhostSet p1 + Tom 2-Floor + Hi-Hat 3-Closed 2/1 + Tom 2-Floor + Hi-Hat 3-Pedal Open / Pedal + Tom 2-Lo + Hi-Hat 3-Sizzle/Open 1 + Tom 2-Lo + Tom 2-Hi + Crash Cymbal 3/BD-Dry 1 + Tom 2-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2 / 1-Push + Splash Cymbal + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open/Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push + Samba Whistle 1/Chinese Gong + Samba Whistle 1/Chinese Gong + Guiro-Short/Tap + Guiro-Long/BD-Dry 1 + Claves/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap?Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit + Cymbal Reverse/SD-Reverse + SD-Reverse + BD-Tight/BD-amb 2 mf -L + BD-Squash/BD-amb 2 f -L + BD-Gated/BD-amb 1 f -L + BD-Tubby/BD-amb 1 mf -L + BD-Tight/BD-amb 2 mf -L + SD-Full Room/SD-dry 1 f -L + SD-Dry 2/SD-dry 1 mp -L + SD-Amb.Piccolo 2/SD-dry 1 fff -L + SD-Dry 3/SD-dry 1 mp -L + SD-GhostSet f/SD-dry 1 f -L + SD-Yowie/SD-dry 1 f -L + BD-Dry 3/BD-amb 2 mf -L + BD-Dry 2/BD-amb 2 f -L + SD-Sidestick Ambi/SD-Sidestick mf -L + SD-Sidestick Ambi/SD-dry 1 f -L + Hand Claps 2/1 -L + SD-GhostSet f/SD-dry 1 p -L + Tom 2-Floor/Tom 1-Lo f -L + Hi-Hat 1-Closed ff/mf + Tom 2-Floor/Tom 1-MidLo f -L + Hi-Hat 1-Pedal ff/1-Pedal mf + Tom 2-Floor/Tom 1-MidHi f -L + Hi-Hat 1-Open ff/mf + Tom 2-Floor/Tom 1-MidHi f -L + Tom 3-Floor/Tom 1-Hi mf -L + Crash Cymbal 3/1 mf -L + Tom 3-Floor/Tom 1-Hi mf -L + Ride Cymbal 4 Edge / 1 -L + China Cymbal + Ride Cymbal Cup 2/Cup 1 -L + Tambourine 2/1-Push + Splash Cymbal/Crash Cymbal 1 mf -L + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/Crash Cymbal 1 mf -L + Vibraslap/BD-Dry 1 + Crash Cymbal 4/Ride Cymbal 1 -L + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Tom-Processed + Timbales Hi-Rim 1/Hi-Edge + Tom-Processed + Timbales Lo-Open + Tom-Processed + Maracas-Push + Tom-Processed + Tom-Processed + Cabasa-Tap/Up + Tom-Processed + Claves + Woodblock 1 + Woodblock 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-Tight + BD-Squash + BD-Gated + BD-Tubby + BD-Woofer + SD-Full Room + SD-Cracker Room + SD-Amb.Piccolo 2 + SD-Processed 1 + SD-Yowie + SD-Atomic + BD-Terminator + BD-Mondo Kill + SD-Sidestick Ambi + SD-Big Rock + Hand Claps 2 + SD-Processed 2 + Tom 4-Floor + Hi-Hat 3-Closed 2/1 + Tom 4-Lo + Hi-Hat 3-Pedal Open/Pedal + Tom 4-Lo + Hi-Hat 3-Open 1/2 + Tom 4-Lo + Tom 4-Hi + Crash Cymbal 3/BD-Dry 1 + Tom 4-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2/1-Push + Splash Cymbal + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Tom-Processed + Timbales Hi-Rim 1/Hi-Edge + Tom-Processed + Timbales Lo-Open + Tom-Processed + Maracas-Push + Tom-Processed + Tom-Processed + Cabasa-Tap/Up + Tom-Processed + Claves + Woodblock 1 + Woodblock 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca + Orch.SD Roll 2/1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-Tight/BD-amb 2 mf -L + BD-Squash/BD-amb 2 mf -L + BD-Gated/BD-amb 1 f -L + BD-Tubby/BD-amb 1 mf -L + BD-Woofer/BD-amb 1 mf -L + SD-Full Room/SD-dry 1 mp -L + SD-Cracker/SD-dry 1 ff -L + SD-Amb.Piccolo 2/SD-dry 1 ff -L + SD-Processed 1/SD-dry 1 ff -L + SD-Yowie/SD-dry 1 fff -L + SD-Atomic/SD-dry 1 fff -L + BD-Terminator/BD-amb 2 mf -L + BD-Mondo Kill/BD-amb 2 f -L + SD-Sidestick Ambi/SD-Sidestick mf -L + SD-Big Rock/SD-dry 1 fff -L + Hand Claps 2/1 -L + SD-Processed 2/SD-dry 1 mf -L + Tom 3-Floor/Tom 1-Lo f -L + Hi-Hat 1-Closed ff/mf + Tom 3-Lo/Tom 1-MidLo f -L + Hi-Hat 1-Pedal ff/mf + Tom 3-Lo/Tom 1-MidHi f -L + Hi-Hat 1-Open ff/mf + Tom 3-Lo/Tom 1-MidHi f -L + Tom 3-Hi/Tom 1-Hi mf -L + Crash Cymbal 3/1 mf -L + Tom 3-Hi/Tom 1-Hi mf -L + Ride Cymbal 4 Edge/Ride Cymbal 1 -L + China Cymbal + Ride Cymbal Cup 2/Cup 1 -L + Tambourine 2/1-Push + Splash Cymbal/Crash Cymbal 1 mf -L + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/1 mf -L + Vibraslap/BD-Dry 1 + Cymbal 4 Edge/Ride Cymbal 2 -L + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/Conga 1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Tom-Processed + Timbales Hi-Rim 1/Hi-Edge + Tom-Processed + Timbales Lo-Open + Tom-Processed + Maracas-Push + Tom-Processed + Tom-Processed + Cabasa-Tap/Up + Tom-Processed + Claves + Woodblock 1 + Woodblock 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) / 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Tom 2-Floor + Tsuzumi + Tom 2-Lo + Taiko-Rim/Open + Tom 2-Hi + Tom 2-Hi + Chacha Bell/BD-Dry 1 + Finger Snap + Hand Claps 2 + Drumstick Hit/BD-Dry 1 + BD-Tubby + BD-Dry 3 + BD-Woofer + BD-Dry 4 + BD-Soft + BD-Jazz 1 + BD-Jazz 2 + SD-Paper + SD-Dry 3 + SD-Dry 2 + SD-Amb.Piccolo 1 + SD-Jazz Ring + SD-Brush Hit/BD-Dry 1 + BD-Jazz 2 + BD-Pillow + SD-Sidestick Dry + SD-Brush Tap 2/BD-Dry 1 + SD-Brush Tap 1/BD-Dry 1 + SD-Brush Swirl/BD-Dry 1 + Tom-Brush Floor + Hi-Hat 2-Closed 2/1 + Tom-Brush Hi + Hi-Hat 2-Pedal + Tom-Brush Hi + Hi-Hat 2-Open 1/2 + Tom-Jazz Floor + Tom-Jazz Hi Rim/Hi + Crash Cymbal 2/BD-Dry 1 + Tom-Jazz Hi Rim/Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2/1-Push + Splash Cymbal + Cowbell 1 + Crash Cymbal 4 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push/BD-Dry 1 + Samba Whistle 1/Chinese Gong + Samba Whistle 1/Chinese Gong + Guiro-Short/Tap + Guiro-Long/BD-Dry 1 + Claves/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Closed + Hi-Hat-Whispy + Hi-Hat Dance 4 Open + Timbales-Paila / Shaker + Hi-Hat-Crisp Open/Chili/ Crisp Open + Hi-Hat-Vintage + Hi-Hat 2-Pedal Open/Closed/Pedal + BD-Dance 04 L/R + BD-Dance 02 + BD-Dance 04 + Dance 07 + BD-Dance 10 + Orch.SD Roll 1 + Orch.SD Roll 2 + Orch.SD Roll 1 + Orch.SD Roll 2 + SD-Sidestick Hi + Cymbal Reverse + SD-Reverse IFX1 + BD-01a-dry + BD-03a-dry + BD-03a-dry + BD-02b-dry + BD-02a-dry + SD-06-dry + SD-05-dry + SD-04-dry + SD-03-dry + SD-02-dry + SD-07-dry + BD-01b-dry + BD-01a-dry + Sidestick-dry + SD-01a-dry + Hand Claps 1 + SD-01b-dry + Tom Lo-dry + HiHat-Closed-dry + Tom Lo-dry + HiHat-Pedal1-dry + Tom Mid-dry + HiHat-Open-dry + Tom Mid-dry + Tom Hi-dry + Crash 01-dry + Tom Hi-dry + Ride 01-dry + China-dry + Ride 02-dry + Tambourine 3/1-Push/1-Pull + Splash-dry + Cowbell 2 + Crash 02-dry + Vibraslap + Ride + Bongo 2 Hi-Open f/mf/f + Bongo 2 Lo-Muffled f/mp/Lo-Closed + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/mf/f/mp + Conga 2 Lo-OpenSlap/mf/f + Timbales Hi-Rim 1/2/Hi-Edge + Timbales Lo-Rim/Open + Agogo Bell IFX5 Off + Agogo Bell IFX5 Off + Cabasa-Up/Down + Maracas-Pull/Push + Samba Whistle 2 + Samba Whistle 2 + Guiro-Short + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 2-Hi + Cuica 2-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Closed + Hi-Hat-Whispy + Hi-Hat Dance 4 Open + Timbales-Paila / Shaker + Hi-Hat-Crisp Open/Chili/ Crisp Open + Hi-Hat-Vintage + Hi-Hat 2-Pedal Open/Closed/Pedal + BD-Dance 04 L/R + BD-Dance 02 + BD-Dance 04 + Dance 07 + BD-Dance 10 + Orch.SD Roll 1 + Orch.SD Roll 2 + Orch.SD Roll 1 + Orch.SD Roll 2 + SD-Sidestick Hi + Cymbal Reverse + SD-Reverse IFX1 + BD-01a-dry + BD-03a-dry + BD-03a-dry + BD-02b-dry + BD-02a-dry + SD-06-dry + SD-05-dry + SD-04-dry + SD-03-dry + SD-02-dry + SD-07-dry + BD-01b-dry + BD-01a-dry + Sidestick-dry + SD-01a-dry + Hand Claps 1 + SD-01b-dry + Tom Lo-dry + HiHat-Closed-dry + Tom Lo-dry + HiHat-Pedal1-dry + Tom Mid-dry + HiHat-Open-dry + Tom Mid-dry + Tom Hi-dry + Crash 01-dry + Tom Hi-dry + Ride 01-dry + China-dry + Ride 02-dry + Tambourine 3/1-Push/1-Pull + Splash-dry + Cowbell 2 + Crash 02-dry + Vibraslap + Ride + Bongo 2 Hi-Open f/mf/f + Bongo 2 Lo-Muffled f/mp/Lo-Closed + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/mf/f/mp + Conga 2 Lo-OpenSlap/mf/f + Timbales Hi-Rim 1/2/Hi-Edge + Timbales Lo-Rim/Open + Agogo Bell IFX5 Off + Agogo Bell IFX5 Off + Cabasa-Up/Down + Maracas-Pull/Push + Samba Whistle 2 + Samba Whistle 2 + Guiro-Short + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 2-Hi + Cuica 2-Lo + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + Hi-Hat 3-Closed 1 + Hi-Hat 1-Closed mf + Hi-Hat 1-Open mf + Hi-Hat-Whispy + Hi-Hat 1-Open ff + Hi-Hat-Chili + Hi-Hat-Alpo Closed + DJ Vinyl Sliced 04 + BD-Dry 3 + DJ Vinyl Sliced 13 + BD-Dance 03 + BD-Dance 06 (R&B) + BD-Ambi + SD-Brasser + "kkk" + Voice 20 + SD-Ambi Pop + SD-Dry 2 + Cymbal Reverse + Finger Snap + BD-Dance 06-EXs5 + BD-Dance 05-EXs5 + BD-Dance 03-EXs5 + BD-Dance 06-EXs5 + BD-Dance 09-EXs5 + SD-Dance 05-EXs5 + SD-Dance 10-EXs5 + SD-Dance 07-EXs5 + SD-Dance 09-EXs5 + SD-Dance 02-EXs5 + SD-Dance 04-EXs5 + BD-Dance 04-EXs5 + BD-Dance 08-EXs5 + SD-Dance 06-EXs5 + SD-Dance 03-EXs5 + SD-Dance 01-EXs5 + SD-Dance 07-EXs5 + Tom-Dance i-EXs5 + Hi-Hat Dance + Tom-Dance j-EXs5 + Hi-Hat Dance 02-EXs5 + Tom-Dance k-EXs5 + Hi-Hat Dance 04-EXs5 + Tom-Dance a-EXs5 + Tom-Dance b-EXs5 + Crash Dance 1-EXs5 + Tom-Dance c-EXs5 + Ride Dance-EXs5 + Crash Dance 2-EXs5 + Orch Cymbal-Open + Tambourine 1-EXs5 + Cymbal Reverse 1-EXs5 + Perc-Dance 01-EXs5 + Crash HipHop 1-EXs5-L + Sleigh Bell + Crash Dance 4-EXs5 + Perc-Dance 02-EXs5 + Perc-Dance 03-EXs5 + Perc-Dance 12-EXs5 + Perc-Dance 05-EXs5 + Perc-Dance 04-EXs5 + Hi-Hat Dance 03-EXs5 + Hi-Hat Dance 05-EXs5 + Tambourine 2-EXs5 + Tambourine 3-EXs5 + Shaker 1-EXs5 + Shaker 3-EXs5 + DJ-Scratch 2-EXs5 + DJ-Horn-EXs5 + DJ-Scratch 1-EXs5 + DJ-Big Cannon-EXs5 + Perc-Mad Analog 37-EXs5 + DJ-Vinyl Sliced 09/03/11/05-EXs5 + DJ-Vinyl Sliced 12/09/12/02-EXs5 + DJ Vinyl Sliced 05/04/07/06-EXs5 + DJ-Record Stop-EXs5 + Triangle-Mute-EXs5 + Triangle Open 1-EXs5 + Shaker 2-EXs5 + Marc Tree Rev-EXs5 + BD-Dance 01-EXs5 + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Voice 12 + Voice 09 + Bell Tree + Stadium + Applause + Orchestral SD + Voice 21 + Voice 23 + Voice 24 + Voice 25 + Voice 26 + GunShot + + + + + + + + BD-amb 1 mf -L + Hi-Hat Dance 3 Closed + Hi-Hat Dance 3 Ope + Hi-Hat-Old Closed 2 + Hi-Hat Dance 4 Closed + Timbales-Paila/Shaker 4-EXs5 + Hi-Hat 3-Closed 1/Hi-Hat-Chili/Hi-Hat Dance 5 Closed + Hi-Hat-Vintage 1/Hi-Hat-Crisp Closed 2 + Hi-Hat 2-Pedal Open / 2-Closed 1 / 2-Pedal + BD-HipHop 01b/02a-EXs5 + BD-HipHop 11-EXs5 + BD-HipHop 10/09-EXs5 + BD-HipHop 14b/a-EXs5 + BD-HipHop 04-EXs5 + SD-HipHop 09-EXs5-L + SD-HipHop 02-EXs5 + SD-HipHop 04-EXs5 + SD-HipHop 06a-EXs5 + SD-HipHop 10-EXs5 + Crash-Dance 1 + SD-Yowie + BD-HipHop 11-EXs5/BD-Dance 11-EXs5 + BD-HipHop 06-EXs5 + BD-HipHop 12/03-EXs5 + BD-HipHop 02b/a-EXs5 + BD-HipHop 06/12-EXs5 + SD-HipHop 07-EXs5/SD-HipHop 11-EXs5-L + SD-HipHop 08-EXs5 + SD-HipHop 03-EXs5-L + SD-HipHop 13-EXs5/SD-dry 1 ff -L/mp -L/p -L + SD-HipHop 12-EXs5-L + SD-HipHop 07-EXs5 + BD-HipHop 13b/a-EXs5-L + BD-HipHop 07/08b/08a-EXs5 + Sidestick-HipHop-EXs5-L + SD-HipHop 15d/c/b/a-EXs5-L + Clap HipHop 1/2-EXs5 + SD-HipHop 15a-EXs5-L/Perc-Granular 21-EXs5 + Tom-HipHop Lo 3/2/1-EXs5 + Hi-Hat HipHop 03/02/01-EXs5 + Tom-HipHop Mid Lo 4/3/2/1-EXs5 + Hi-Hat HipHop 06/07-EXs5 + Tom-HipHop Mid Hi 4/3/2/1-EXs5 + Hi-Hat HipHop 05/04-EXs5 + Tom-HipHop Mid Hi 4/3/2/1-EXs5 + Tom-HipHop Hi 4/3/2/1-EXs5 + Crash HipHop 1-EXs5-L + Tom-HipHop Hi 4/3/2/1-EXs5 + Ride Cymbal 4 Edge + Hi-Hat Dance 5 Open + Ride HipHop 2-EXs5 + Perc-Granular 36/39/37-EXs5 + Crash-Dance 3 + Perc-Mad Analog 19-EXs5 + Crash HipHop 2-EXs5 + Wind + Ride HipHop 1-EXs5 + Perc-Mad Analog 32-EXs5 + Perc-Mad Analog 34-EXs5 + Bongo 1 Lo-Stick + Conga 1 Hi-Open + Conga 1 Hi-Slap 1 + Perc-Mad Analog 22/21/20-EXs5 + Perc-Mad Analog 24-EXs5 + Perc-Mad Analog 28-EXs5 + Perc-Mad Analog 29-EXs5 + Perc-Mad Analog 23-EXs5 + Perc-Granular 36-EXs5 + Perc-Mad Analog 06-EXs5 + Perc-Mad Analog 05-EXs5 + Perc-Mad Analog 12-EXs5 + Perc-Mad Analog 13-EXs5 + Perc-Mad Analog 37-EXs5 + Perc-Mad Analog 30-EXs5 + Perc-Mad Analog 30-EXs5 + Perc-TekClub 18-EXs5 + Perc-TekClub 18-EXs5 + Perc-Granular 46-EXs5 + Perc-Granular 46-EXs5 + Perc-Mad Analog 35-EXs5 + Marc Tree + BD-HipHop 03-EXs5 + Hi-Hat Dance 3 Closed + SD-HipHop 02-EXs5 + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Tabla-Mute 4 + Drumstick Hit/BD-Dry 1 + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-PhatJaz/BD-Dry 1 + BD-Hip 1/BD-Dry 4 + BD-Dance 04/BD-Dance 04 + BD-AmbiRocker + BD-Dance 13/BD-Pillow + SD-Vintage 5/SD-Full Room + SD-Ambi Hit 1/SD-Classic Room + SD-AmbiCrackle 2/SD-Amb.Piccolo 1 + SD-Vintage 2/SD-Paper + SD-Hip 3/SD-Dry 4 + SD-Ambi House/SD-Dry 2 + BD-Dance 07/BD-Dry 3 + BD-Ambi/BD-Dry 2 + SD-Sidestick Dry + SD-Whopper/SD-Amb.Piccolo 2 + Hand Claps 4 Happy/2 + SD-Vintage 1/SD-Big Rock + Tom-Dirty Funk + Hi-Hat-Amb Crackle/Hi-Hat 2-Closed 2 + Tom-Dirty Funk + Hi-Hat-Old Closed 2/Hi-Hat 2-Pedal + Tom-Dirty Funk + Hi-Hat-Crisp Open / 2-Open 2 + Tom-Old R&B + Tom-Old R&B + Crash Cymbal 3 + Tom-Old R&B + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Tambourine 2 / 1-Push + Splash Cymbal + Cowbell 1/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + Vibraslap/BD-Dry 1 + Ride Cymbal 5 Edge + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push + Samba Whistle 1/Chinese Gong + Samba Whistle 1/Chinese Gong + Guiro-Short/Guiro-Tap + Guiro-Long/BD-Dry 1 + Claves/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06/05 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + DJ Vinyl Sliced 15 + DJ Vinyl Sliced 13 + Voice Perc-Kick 2b-EXs5 + BD-Mad Analog 08-EXs5 + BD-Mad Analog 12-EXs5 + SD-HipHop 12-EXs5-R + Sidestick-HipHop-EXs5-L + Voice Perc-Snare 3-EXs5 + SD-Mad Analog 12-EXs5 + SD-HipHop 03-EXs5-R + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-Dance Soft/BD-Dry 1 + DJ-BD Rub + BD-Dance 08/Zap 1 + BD-Dance 03/Tom-VPM + BD-Dance 06 (R&B)/BD-Pillow + DJ-SD Rub/SD-Dry 2 + Dance Claps 3/Scream + SD-AmbiCrackle 2/SD-Amb.Piccolo 1 + SD-Dance 2/SD-Sidestick Dry + SD-Sidestick mf -L/Dance Claps 2 + SD-Vintage 5/SD-Full Room + BD-Dance 12/Orchestral BD + BD-Dance 13/Taiko-Open + Rimshot-Dance 1 + Dance Claps 3/SD-Sidestick mf -L + Dance Claps 1/Hand Claps 1 -L + SD-Hip 4/SD-Dance 7 + BD-Hip 1/Explosion + Hi-Hat Dance 3 Closed / 2-Pedal + Taiko-Open/Explosion + Hi-Hat Dance 3 Open / 2-Closed 2 + BD-Hip 1/Explosion + Hi-Hat Dance 4 Open / 2-Open 2 + Vocal Cowbell/Tom-Old R&B + Vocal Cowbell/Tom-Old R&B + Crash Cymbal 3/BD-Dry 1 + Claves/Tom-Old R&B + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal Cup 2 + Dance Perc 2b/2a + Tribe/Chinese Gong + Tambura/Gamelan + Orch Cymbal-Open/Timpani + Applause/Stadium + Ride Cymbal 1 -L / 4 Edge + Bongo 1 Hi-Slap/Hi-Open + Bongo 1 Lo-Slap/Lo-Open + Conga 1 Hi-Mute/1-Toe + Conga 1 Hi-MtSlap/Hi-Open + Conga 1 Lo-Slap/Lo-Open + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open + Tambourine 3 + Tambourine 2 + Cabasa-Up/Down + Maracas-Push + DJ-BD Rub + DJ-SD Rub + DJ Vinyl Sliced 11/12 + DJ Vinyl Sliced 09/10 + DJ-Reverse + Woodblock 1/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Marc Tree/BD-Dry 1 + BD-Dance 06 (R&B)/05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Tabla-Mute 4 + Drumstick Hit + Cymbal Reverse + SD-Reverse + BD-Jazz 2 + BD-Dance 06 (R&B)/BD-Dry 5 + BD-Pillow/DJ-Record Stop + BD-Ringy/Dance Perc 2b + BD-Ambi Crackle/DJ-Old Record + SD-AmbiCrackle 3/DJ-Old Record + Orchestral SD + SD-Brush Swirl/SD-Brush Tap 1 + SD-Vintage 2/SD-Brush Hit + SD-Hip 3/SD-GhostSet f + SD-Jazz Ring/SD-Brush Tap 2 + BD-Hip 4 + BD-Dance Soft + SD-Sidestick mf -R + SD-Brush Hit/Tom 2-Hi + Dance Claps 1/Hand Claps 1 -L + SD-Hip 5/SD-Sidestick Dry + Tom-Old R&B + Hi-Hat-Crisp Closed 1 / Hi-Hat 1-Closed mf /BD-Dry 1 + Tom-Old R&B + Hi-Hat-Crisp Closed 1 / Hi-Hat 1-Pedal mf + Tom-Old R&B L/R Off + Hi-Hat-Old Open/Hi-Hat 1-Open mf + Tom-Dance + Tom-Dance + Crash Cymbal 1 mf -L + Tom-Dance + Ride Cymbal 1 -L / 4 Edge + Crash-Dance 2 DDD1 / China Cymbal + Ride Cymbal Cup 1 -R / Ride-Dance 1 + Tambourine 2/1-Push + Crash-Dance 1 + Finger Snap + Crash Cymbal 2 + Finger Cymbal/BD-Dry 1 + Ride-Dance 1 + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Agogo Bell L/R Off + Agogo Bell/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push/Pull + Uhh Solo + Yea Solo + Guiro-Short/Tap + Guiro-Long + Claves + Woodblock 1 + Woodblock 1 + Cuica 1-Hi + Cuica 1-Lo/BD-Dry 1 + Triangle-Mute + Triangle-Open + Shaker 1 + Marc Tree + Bell Tree + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-amb 1 mf -L + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open/Closed + Hi-Hat Dance 03-EXs5 + Hi-Hat Dance 3 Closed + Timbales-Paila/Shaker 1 + Hi-Hat-Crisp Open/Chili/Whispy + Hi-Hat-Vintage 1 + Hi-Hat 2-Pedal Open/Closed 1/Pedal + BD-TekClub 03-EXs5 + BD-TekClub 11-EXs5 + BD-TekClub 12-EXs5 + BD-TekClub 06-EXs5 + BD-TekClub 04-EXs5/BD-Mad Analog 14-EXs5 + SD-TekClub 03-EXs5 + SD-TekClub 06/07-EXs5 + SD-TekClub 08-EXs5-L + SD-TekClub 11-EXs5 + SD-TekClub 17-EXs5 + Cymbal Reverse + SD-Processed + BD-TekClub + BD-TekClub + BD-TekClub + BD-TekClub 10-EXs5 + BD-TekClub 08/07/06-EXs5 + SD-TekClub 05/04-EXs5-L + SD-TekClub 01-EXs5 + SD-TekClub 12-EXs5 + SD-TekClub 15/07-EXs5 + Crash-Granular-EXs5-L/SD-HipHop 08-EXs5 + SD-TekClub-EXs5 + BD-TekClub 05-EXs5 + BD-TekClub 04-EXs5 + SD-Mad Analog 02-EXs5 + SD-TekClub 08-EXs5-L + SD-TekClub 10-EXs5-L + SD-TekClub 14-EXs5 + Tom-TekClub-EXs5-L + Hat-TekClub 05-EXs5-L + Tom-TekClub-EXs5-L + Hat-TekClub 10-EXs5/01-EXs5-R + Tom-TekClub-EXs5-L + Hat-TekClub 11-EXs5/03-EXs5-L + Tom-Granular 6-EXs5 + Tom-Granular 5-EXs5 + Crash HipHop 1-EXs5-L + Tom-Granular 4-EXs5 + Ride HipHop 2-EXs5 + China Cymbal + Perc-TekClub 14-EXs5 + Tambourine 2/1-Push/1-Pull + Crash-Granular-EXs5-R + Perc-TekClub 10-EXs5 + Crash HipHop 2-EXs5 + HiHat Granular 06-EXs5-R + Perc-TekClub 02-EXs5 + Perc-TekClub 08-EXs5 + Perc-TekClub 08-EXs5 + Perc-Mad Analog 32-EXs5 + Perc-Mad Analog 34-EXs5 + Perc-Mad Analog 33-EXs5 + Perc-TekClub 10-EXs5 + Perc-TekClub 10-EXs5 + Hi-Hat Dance 5 Open + Hi-Hat Dance 5 Open + Perc-Mad Analog 23-EXs5 + Maracas-Pull + Perc-Granular 35/34-EXs5 + Voice 35 "Aow"/Voice 39 "Uuh" + Hat-TekClub 10-EXs5 + Hat-TekClub 11-EXs5 + Perc-TekClub 01-EXs5 + Perc-TekClub 06/05-EXs5 + Perc-TekClub 12/13-EXs5 + Perc-TekClub 04-EXs5 + Perc-TekClub 17-EXs5-R/16-EXs5 + Triangle-Mute + Triangle-Roll + Dance Perc 8 Maraca + Marc Tree + BD-Mad Analog 04-EXs5 + HiHat Mad Analog 02-EXs5 + SD-Mad Analog 10-EXs5 + HiHat Mad Analog 03-EXs5 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + BD-HipHopf 03-EXs5 + BD-TekClub 07-EXs5 + BD-Granular01-EXs5-L/BD-Dance 01 + BD-TekClub 04-EXs5 + BD-Mad Analog 01-EXs5 + SD-TekClub 08-EXs5-L + SD-HipHop 04-EXs5 + SD-Dance 12-EXs5 + SD-Dance 05-EXs5 + SD-Dance 02-EXs5 + BD-Mad Analog 04-EXs5 + BD-Dance 01 + BD-Dance 11 + BD-Dance 04 + BD-Dance 08 + BD-Squash + BD-Dance 06 (R&B) + SD-Dance 3 (New) + SD-Dance 5 + SD-Hip 4 + SD-Sizzle + SD-Noise/SD-Vintage 4 + SD-Hip 1/SD-Chili + BD-Dance 12 + BD-Dance 09 + Rimshot-Dance 2 + SD-Dance 4 (Old) + Dance Claps 1 + SD-Dance 8 + Tom-Dance + Hi-Hat Dance 1 Closed + Tom-Dance + Hi-Hat Dance 1 Open + Tom-Dance + Hi-Hat Dance 1 Open + Hi-Hat-Whispy/BD-Dry 1 + Hi-Hat Dance 3 Closed + Crash-Dance 3 + Hi-Hat Dance 3 Open + Ride-Dance 1 + Crash Cymbal 3/China Cymbal + Ride-Dance 2 DDD1 + Tambourine 3/2 + Splash Cymbal + Dance Perc 7 Cowbell + Crash-Dance 1 + Zap 3 + Cymbal Reverse + Zap 1 + Zap 2 + Dance Perc 6 Clave/BD-Dance 05 (Rap) + Dance Perc 5 Conga + Dance Perc 5 Conga + Metal Hit/DJ-Reverse + Hi-Hat-Chili/BD-Dry 1 + Industry Loop 2/DJ-Old Record + Hi-Hat 3-Pedal/DJ-Record Stop + Dance Perc 4 House/DJ-SD Rub + Dance Perc 8 Maraca/SD-Noise + Vocal SD + Car Crash/BD-Woofer + Triangle-Mute/BD-Dance 05 (Rap) + Noise-White/SD-Amb.Piccolo 1 + Triangle-Open/Tom-Brush Hi + E.Guitar Cut Noise 1/SD-Paper + Hi-Hat-Hip 2/Vocal SD + Hi-Hat-Alpo Closed + String Slap/Tom-Brush Hi + Hi-Hat-Amb Crackle/Ride-Dance 1 + Dist.Slide 1/Tom-Jazz Hi Rim + Hi-Hat-Crisp Open/Ride-Dance 1 + Swish Terra + Swish Terra/BD-Jazz 2 + Crash Cymbal 2 + China Cymbal + Ride Cymbal 4 Edge + Conga 1 Hi-Slap 1/Lo-Open + Conga 1 Hi-Open/Lo-MuteSlap + Ride Cymbal Cup 2 + Conga 1 Hi-Slap 2/Hi-Mute + Cowbell 1 + Chacha Bell + Cowbell 1 + Guiro-Short/BD-Dry 1 + Guiro-Tap + Metal Hit/Finger Cymbal + Dance Perc 1 Hip/BD-Dry 1 + Swish Terra + Cyber Drum + Tambourine 1-Push + Air Vortex + Tubular + Telephone Ring + Footstep 2 + Triangle-Mute + Tribe + Dance Perc 1 Hip/BD-Dry 1 + + + + + + + + BD-Mad Analog 10-EXs5 + BD-HipHop 08b-EXs5 + BD-Granular 02-EXs5-R/L + BD-TekClub 02-EXs5 + BD-Granular 20-EXs5-L/R + SD-Dance 09-EXs5 + SD-dry 1 p -R + SD-Granular 20-EXs5-L + SD-TekClub 13-EXs5 + SD-Dance 02-EXs5 + BD-Dance 10 + BD-Dance 01 + BD-Dance 03 + BD-Dance 01 + BD-Squash + BD-Dance 13/Hi-Hat 3-Closed 1 + BD-Dance 01/Zap 1 + SD-Dance 3 (New) + SD-Dance 5 + SD-Dance 2 IFX1 + Dance Claps 3 + SD-Dance 2/Hand Claps 2 + SD-Dance 8 + BD-Dance 10/Hi-Hat Dance 3 Closed + BD-Dance 11/Zap 1 + Rimshot-Dance 1/2 + SD-Dance 7 + Dance Claps 2 + SD-Dance 6 + Tom-Dance/Tom 4-Floor + Hi-Hat Dance 1 Closed + Tom-Dance/Tom 4-Lo + Hi-Hat Dance 2 Closed + Tom-Dance/Tom 4-Hi + Hi-Hat Dance 2 Open + Dance Perc 2b/2a + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open + Hi-Hat Dance 3 Open + Ride-Dance 1 + Crash-Dance 1 + Ride-Dance 2 DDD1 + Tambourine 3/2 + Splash Cymbal + Dance Perc 7 Cowbell + Crash-Dance 1 + PC Vox-One + Cymbal Reverse + Zap 1 + Zap 2 + Dance Perc 6 Clave/BD-Dance 05 (Rap) + Dance Perc 5 Conga + Dance Perc 5 Conga + Tabla-Na/DJ-Reverse + Hi-Hat Dance 3 Closed/BD-Dry 1 + Baya-Mute 2/DJ-Old Record + Hi-Hat Dance 3 Open/DJ-Record Stop + Baya-Open/DJ-SD Rub + Dance Perc 8 Maraca/SD-Noise + CompuVoice-Beep/Vocal SD + Dance Claps 3/BD-Woofer + Triangle-Mute/BD-Dance 05 (Rap) + Hand Claps 1 -L/SD-Amb.Piccolo 1 + Triangle-Open/Tom-Brush Hi + Finger Snap/SD-Paper + Hi-Hat-Old Closed 3/Vocal SD + Hi-Hat-Alpo Closed + E.Guitar Pick 1/Tom-Brush Hi + Hi-Hat-Amb Crackle/Ride-Dance 1 + Dist.Slide 1/Tom-Jazz Hi Rim + Hi-Hat-Crisp Open/Ride-Dance 1 + Dist.Slide 2/Swish Terra + Guitar Scratch/BD-Jazz 2 + Crash Cymbal 2/Crash Cymbal 2 + China Cymbal + Ride Cymbal 4 Edge + Conga 1 Hi-Slap 1/Bongo 1 Lo-Open + Conga 1 Hi-Open/Lo-MuteSlap + Ride Cymbal Cup 2 + Conga 1 Hi-Slap 2/Hi-Mute + Cowbell 1 + Chacha Bell + Cowbell 1 + Guiro-Short/BD-Dry 1 + Guiro-Tap + Metal Hit/Finger Cymbal + Dance Perc 1 Hip/BD-Dry 1 + Swish Terra + Cyber Drum + Tambourine 1-Push + Air Vortex + Tubular + Telephone Ring + Footstep 2 + Triangle-Mute + Tribe + Dance Perc 1 Hip/BD-Dry 1 + + + + + + + + BD-Mad Analog 04-EXs5 + BD-Dance 06-EXs5 + BD-Mad Analog 05-EXs5 + BD-HipHop 03-EXs5 + BD-Dance 14-EXs5 + SD-Mad Analog 13-EXs5 + SD-TekClub 04-EXs5 + SD-TekClub 13-EXs5 + SD-Dance 12-EXs5/DJ-SD Rub + SD-Dance 03-EXs5 + BD-TekClub 01-EXs5 + BD-Dance 01-EXs5/BD-Mad Analog 13-EXs5 + BD-Dance 13 + BD-Dance 04/Dance Perc 4 House + BD-Dance 08/BD-Jungle + BD-Mad Analog 06-EXs5/07-EXs5/08-EXs5 + HiHat Mad Analog 16-EXs5/BD-Dance 06 (R&B) + SD-Dance 5 + SD-Dance 3 (New) + SD-Hip 4 + SD-Rap 2/Dance Claps 1 + SD-Noise/Dance Claps 3/SD-Noise + SD-Dance 2/7 + BD-Dance 06 (R&B)/BD-amb 1 f -R + BD-Dance 13 + Rimshot-Dance 1 + SD-Dance 2/Dance Claps 2 + Dance Claps 1/Dance Claps 3 + SD-Dance 5 + Tom-Dance + Hi-Hat/Dance Perc 2a + Tom-Dance + Hi-Hat Dance 3 Open/Dance Perc 2b + Tom-Dance + Hi-Hat Dance 3/4 Open + Hi-Hat Dance 1 Closed + Dance Perc 8 Maraca + Crash-Dance 3 + Hi-Hat Dance 2 Open + Ride-Dance 1 + Crash-Dance 1/China Cymbal + Ride-Dance 2 DDD1 + Dance Perc 2b/2a + Industry Loop 2 + HiHat Mad Analog 09-EXs5 + Crash-Dance 1 + 568:HiHat Mad Analog 10-EXs5 + Perc-Mad Analog 07-EXs5 + Zap 1 + Zap 2 + Dance Perc 6 Clave + Dance Perc 5 Conga + Dance Perc 5 Conga + Noise-VPM Mod/DJ-Reverse + Hi-Hat-Chili + Noise-White + Hi-Hat 3-Pedal/DJ-Record Stop + Dance Perc 3 House + Hi-Hat 2-Open 2 + Vocal Cowbell + GunShot + Triangle-Mute + Noise-VPM Mod/SD-Amb.Piccolo 1 + Triangle-Open/Tom-Brush Hi + HiHat Granular 19-EXs5-L/R + Hi-Hat-Ambi + Hi-Hat-Alpo Closed + CompuVoice-When/Tom-Brush Hi + Hi-Hat-Amb Crackle/Ride-Dance 1 + CompuVoice-Do it/Tom-Jazz Hi Rim + Hi-Hat-Crisp Open/Ride-Dance 1 + CompuVoice-Ch + Year solo/BD-Jazz 2 + Crash Cymbal 2 + China Cymbal + Ride Cymbal 4 Edge + Conga 1 Hi-Slap 1/Bongo 1 Lo-Open + Conga 1 Hi-Open/Lo-MuteSlap + Ride Cymbal Cup 2 + Conga 1 Hi-Slap 2/Hi-Mute + Cowbell 1 + Chacha Bell + Cowbell 1 + Guiro-Short/BD-Dry 1 + Guiro-Tap + Metal Hit/Finger Cymbal + Dance Perc 1 Hip/BD-Dry 1 + Swish Terra + Cyber Drum + Tambourine 1-Push + Air Vortex + Tubular + Telephone Ring + Footstep 2 + Triangle-Mute + Tribe + Dance Perc 1 Hip/BD-Dry 1 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Mute 4 + Drumstick Hit/BD-Dry 1 + Cymbal Reverse/BD-Dry 1 + SD-Reverse/BD-Dry 1 + BD-PhatJaz + BD-Tubby/BD-Dry 4 + BD-Dry 6 + BD-Klanger/BD-Hip 1 + BD-Pillow + SD-Ambi Hit 3 + SD-Hip 3 + SD-Klanger/SD-Amb.Piccolo 1 + SD-Ringy + SD-Chili + SD-Dry 2 + BD-Ringy/BD-Dance 08 + BD-Ambi Crackle/BD-Ambi + SD-Sidestick Dry/Rimshot-Dance 2 + SD-Chili + Hand Claps 4 Happy/3 Hip + SD-AmbiCrackle 3 + Tom-Old R&B + Hi-Hat-Crisp Closed 2 + Tom-Old R&B + Hi-Hat 2-Closed 2 + Tom-Old R&B + Hi-Hat-Crisp Open / 2-Open 2 + Hi-Hat-Vintage 1 + Hi-Hat-Amb Crackle + Crash Cymbal 3/BD-Dry 1 + Hi-Hat-Old Open + Ride Cymbal 4 Edge + Noise-VPM Mod/Cricket Spectrum + Ride Cymbal Cup 2 + Tambourine 2/1-Push + Tambourine 3/1-Pull + Hi-Hat-Hip 2/BD-Dry 1 + Crash Cymbal 2/BD-Dry 1 + MachineGun + Ride-Dance 2 DDD1 + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Timbales Hi-Rim 1/Hi-Edge + Timbales Lo-Open + Dance Perc 1 Hip/BD-Dry 1 + Dance Perc 1 Hip/BD-Dry 1 + Cabasa-Up/Down + Maracas-Push + SD-GhostSet f + SD-GhostSet roll + Train/Chinese Gong + Space Lore + Noise-VPM Mod/BD-Dry 1 + Air Vortex/BD-Dry 1 + Cricket Spectrum + Car Engine/BD-Dry 1 + Door Slam/BD-Dry 1 + Triangle-Mute + Triangle-Open/BD-Dry 1 + Shaker 1 + Bell Tree/BD-Dry 1 + BD-Dance 06/05 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1/BD-Dry 1 + Dance Perc 8 Maraca/BD-Dry 1 + Orch.SD Roll 2/BD-Dry 1 + Orchestral SD + Bell Tree/BD-Dry 1 + Stadium/BD-Dry 1 + Applause/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Dance 06 (R&B)/Perc-Granular 16-EXs5-L + BD-Granular 28-EXs5 + BD-Dance 05 (Rap)/BD-Hip 4 + BD-Mad Analog 04-EXs5 + BD-Pop + SD-HipHop 03-EXs5-L + SD-HipHop 15d-EXs5-L + SD-Granular 06-EXs5/SD-dry 1 f -R + SD-Granular 14-EXs5-L/SD-HipHop 06b-EXs5 + SD-Sizzle/SD-HipHop 04-EXs5 + Cymbal Reverse + SD-Reverse + BD-Terminator/BD-Dance 13 + BD-Dance 13/BD-Dry 3 + BD-Ringy/BD-Dance 13 + BD-HipHop 14a-EXs5 + BD-Dance 13/BD-Dance 08-EXs5 + SD-TekClub 05-EXs5-L/SD-Dance 2 + SD-Jungle + SD-Dance 15-EXs5 + SD-Hip 5/SD-Sidestick Ambi + SD-Jungle + SD-Big Rock + BD-Tight/BD-Dance 08 + BD-Dance 13/BD-Terminator + SD-Sidestick Dry/SD-Sizzle + SD-Dance 2/Dance Claps 1 + Dance Claps 2/Hand Claps 3 Hip + SD-Dance 2/SD-Ambi Hit 3 + Tom-Old R&B + Hi-Hat-Vintage 1 + Tom-Old R&B + Hi-Hat-Amb Crackle + Tom-Old R&B + Splash Cymbal/Noise-White + Hi-Hat 1-Closed mf/Noise-White + Hi-Hat 1-Closed ff/Noise-White + Crash-Dance 1/BD-Dry 1 + Hi-Hat-Old Open + Ride Cymbal 4 Edge + China Cymbal/Stadium + Ride Cymbal Cup 2 + HiHat Granular 03-EXs5-L/R + Crash-Jungle + Dance Perc 2a + Crash Cymbal 2/Crash-Jungle + Dance Perc 2b + Ride-Dance 2 DDD1 + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Hi-Hat Dance 06-EXs5 + Hi-Hat Dance 10-EXs5 + Hat-TekClub 12-EXs5-L + Hat-TekClub 12-EXs5-R + Tambourine 2 + Tambourine 3 + SD-GhostSet f + SD-GhostSet roll + Zap 2 + Zap-EXs5 + Noise-VPM Mod + Noise-VPM Mod + Tambura/Cricket Spectrum + Voice 21 + Marc Tree + Triangle-Mute + Triangle-Open + Shaker 1 + Bell Tree + BD-Dance 06/05 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applausef + Voice Perc-Kick 1-EXs5 + Voice Perc-Kick 2a-EXs5 + Voice Perc-Kick 2b-EXs5 + BD-HipHop 14a-EXs5/BD-Jungle + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Ambi Soft/"Tuunn" + BD-Mad Analog 03-EXs5 + DJ Vinyl Sliced 15/Perc-Mad Analog 17-EXs5 + BD-Mad Analog 08-EXs5/CompuVoice-Do it + Perc-Mad Analog 25-EXs5 + SD-Mad Analog 05-EXs5 + Clap Mad Analog 5-EXs5/Voice Perc-Snare 2-EXs5 + Perc-Granular 32-EXs5 + SD-Mad Analog 07-EXs5 + SD-Mad Analog 09-EXs5 + BD-Granular 10-EXs5-R/L + BD-Mad Analog 06/07-EXs5 + BD-Dance 13/Perc-Granular 20-EXs5 + BD-Dance 09/Click + DJ-Old Record/BD-Dance 08 + BD-Dry 5/BD-Dance 07 + BD-Dance 07/Hi-Hat Dance 4 Open + SD-Dry 3/Timbales Hi-Edge + SD-Dance 3 (New) + SD-Jungle/Hand Claps 4 Happy + SD-Ambi Hit 1 + Dance Claps 3/1 + Dance Perc 8 Maraca/SD-Sidestick Dry + Noise-White/Cyber Drum + Hart Beat/Zap 2 + CompuVoice-Beep + Noise-White + Noise-White/Applause + SD-Dance 6/SD-Air Explosion + Tom-Synth/Noise-White + Noise-White/Click + Tom-Synth/Noise-White + Noise-White/Click + Tom-Synth/Noise-White + Noise-White/Click + Tom-Synth/Noise-White + Hi-Hat Dance 4 Closed + Noise-VPM Mod/Noise-White + Hi-Hat Dance 4 Open + Ride-Dance 2 DDD1/Telephone Ring + Crash-Dance 3/China Cymbal + Ride-Dance 2 DDD1 + Cabasa-Up/Down + Splash Cymbal + Shaker 2/1 + Crash-Dance 2 DDD1 + Dance Perc 6 Clave + Cymbal Reverse + Dance Perc 5 Conga/Bongo 1 Hi-Open + Dance Perc 5 Conga/Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/Conga 1-Toe + Dance Perc 5 Conga/Conga 1 Hi-Open + Dance Perc 5 Conga/Conga 1 Lo-Open + Tambourine 1-Push/1-Pull + Tambourine 3/2 + Dance Perc 7 Cowbell/BD-Dry 1 + Rimshot-Dance 2 + Cabasa-Up/Down + Maracas-Push + Dance Perc 8 Maraca/Chinese Gong + SD-Metal Striker/Chinese Gong + Car Engine/Guiro-Tap + Industry Loop 2 + Industry Loop 2 + Perc-Mad Analog 19-EXs5 + Tambourine 3/BD-Dry 1 + Perc-Granular 39-EXs5 + String Slap/BD-Dry 1 + Perc-Mad Analog 06-EXs5 + Zap 1/BD-Dry 1 + Perc-Granular 28-EXs5-L + Car Crash/BD-Dry 1 + BD-Dance 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 4 (Old) + Hi-Hat Dance 3 Open/BD-Dry 1 + Crash-Jungle + Dance Perc 6 Clave + Wind/BD-Dry 1 + Stream/BD-Dry 1 + "Two..." Solo/"One..." Solo + "Four..." Solo/"Three..." Solo + Bell Tree/BD-Dry 1 + Space Lore/BD-Dry 1 + Hart Beat/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-amb 1 mf -L + Hi-Hat Dance 4 Closed + Hi-Hat Dance 4 Open/Closed + Hi-Hat Dance 03-EXs5 + Hi-Hat Dance 3 Closed + Timbales-Paila/Shaker 1 + Hi-Hat-Crisp Open/Hi-Hat-Chili/Hi-Hat-Whispy + Hi-Hat-Vintage 1 + Hi-Hat 2-Pedal Open/2-Closed 1/Hi-Hat 2-Pedal + BD-Mad Analog 03-EXs5 + BD-Mad Analog 15-EXs5 + BD-Mad Analog 12-EXs5 + BD-Mad Analog 13/14-EXs5 + BD-Mad Analog 09-EXs5 + SD-Mad Analog 01-EXs5 + SD-Mad Analog 04-EXs5 + SD-Mad Analog 11-EXs5 + SD-Mad Analog 07-EXs5/SD-Granular 22-EXs5-L + Cymbal Reverse + SD-Mad Analog 01-EXs5 + BD-Mad Analog 01-EXs5 + BD-Mad Analog 05-EXs5 + Perc-Granular 18-EXs5 + BD-Mad Analog 02-EXs5/BD-TekClub 10-EXs5 + BD-Mad Analog 11-EXs5/BD-Mad Analog 10-EXs5 + SD-Mad Analog 14-EXs5/Perc-Granular 44-EXs5 + SD-TekClub 04-EXs5 + SD-Mad Analog 15-EXs5 + SD-Mad Analog 12-EXs5/Hi-Hat Granular 01-EXs5 + SD-Mad Analog 13-EXs5 + SD-Mad Analog 02-EXs5 + BD-Mad Analog 06/07/08-EXs5 + BD-Mad Analog 04-EXs5 + Sidestick-Mad Ana 2-EXs5/SD-Mad Analog 11-EXs5 + SD-Mad Analog 10-EXs5 + SD-Granular 21-EXs5-L/Clap Mad Analog 2-EXs5 + SD-Mad Analog 13-EXs5/SD-Mad Analog 12-EXs5 + Tom-Mad Analog 3-EXs5 + HiHat Mad Analog 01-EXs5 + Tom-Mad Analog 2-EXs5 + HiHat Mad Analog 10/09-EXs5 + Tom-Mad Analog + HiHat Mad Analog 16/15/14-EXs5 + Tom-Mad Analog 5b/5a-EXs5 + Tom-Mad Analog 6b/6a-EXs5 + Hi-Hat-Clangy Open/Industry Loop 2 + Tom-Mad Analog 4b/4a-EXs5 + Perc-Mad Analog 08-EXs5 + Splash Cymbal + Ride Cymbal Cup 1 -L + Dance Perc 2a + Crash-Granular-EXs5-L + Perc-Granular 22-EXs5 + Crash HipHop 2-EXs5 + Perc-Mad Analog 03-EXs5 + HiHat Granular 18-EXs5-R + Perc-TekClub 08-EXs5 + Perc-TekClub 08-EXs5 + Perc-Mad Analog 32-EXs5/Conga 2 Hi-Closed + Conga 2 Hi-OpenSlap/Perc-Mad Analog 34-EXs5 + Conga 2 Lo-OpenSlap/Perc-Mad Analog 33-EXs5 + Perc-Mad Analog 12-EXs5 + Perc-Mad Analog 15-EXs5 + Perc-Granular 23-EXs5 + Perc-Granular 23-EXs5 + Perc-Mad Analog 23-EXs5 + Perc-Mad Analog 09-EXs5 + Perc-Mad Analog 05-EXs5 + Perc-Mad Analog 04-EXs5 + HiHat Mad Analog 08-EXs5 + HiHat Mad Analog 07-EXs5 + Perc-Mad Analog 12/11-EXs5 + Perc-Mad Analog 15/14/13-EXs5 + Perc-Mad Analog 18/17/16-EXs5 + Perc-Mad Analog 31-EXs5 + Perc-Mad Analog 31-EXs5 + Triangle-Mute + Triangle-Open + Shaker 1/2 + Marc Tree + BD-Dance 06 (R&B) + Hi-Hat Dance 3 Closed + SD-Dance 3 (New) + Hi-Hat Dance 3 Open + Crash-Dance 3 + Dance Perc 7 Cowbel + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + BD-amb 1 mf -L L/R Off + Hi-Hat Dance 4 Closed IFX4 Off + 534:Hi-Hat Dance 4 Open/Closed + Hi-Hat Dance 03-EXs5f + Hi-Hat Dance 3 Closedf + Timbales-Paila/Shaker 1 + Hi-Hat-Crisp Open/Chili/Whispy + Hi-Hat-Vintage 1 + Hi-Hat 2-Pedal Open/Closed 1/Pedal + BD-Granular 06-EXs5 + BD-Granular 13-EXs5 + BD-Granular 11-EXs5-L + BD-Granular 17-EXs5 + BD-Granular 20-EXs5-L + SD-Granular 01-EXs5 + SD-Granular 02-EXs5 + SD-Granular 20-EXs5-L + SD-Granular 19-EXs5-L + SD-Granular 16-EXs5-L + Cymbal Reverse L/R Off + SD-Processed + BD-Granular 10-EXs5-L + BD-Granular 21/23-EXs5 + BD-Granular 09/08-EXs5 + BD-Granular 28-EXs5 + BD-Granular 27/26-EXs5-L + SD-Granular 26-EXs5 + SD-Granular 08-EXs5-L/R + SD-Granular 03-EXs5 + SD-Granular 24/23-EXs5-L + SD-Granular 18-EXs5-L + SD-Granular 15-EXs5-L + BD-Granular 22-EXs5-L + BD-Granular 18-EXs5-L + Perc-Granular 23-EXs5/Sidestick-Mad Ana 1-EXs5 + SD-Granular 07-EXs5-L + SD-Granular 14-EXs5-L + SD-Granular 05/04-EXs5-L + Tom-Granular 2-EXs5-L + HiHat Granular 21/20-EXs5 + Tom-Granular 3-EXs5-L + HiHat Granular 18/17-EXs5-L + Tom-Granular 1-EXs5-L + HiHat Granular 18-EXs5-L + Tom-Granular 6-EXs5 + Tom-Granular 5-EXs5 + Crash-Granular-EXs5-L + Tom-Granular 4-EXs5 + HiHat Granular 19/18-EXs5-L + Splash Cymbal + Ride Cymbal + Perc-Granular 36/37/38-EXs5 + Perc-Granular 12-EXs5 + Perc-Granular 04-EXs5 + Crash HipHop 2-EXs5 + Perc-Granular 32-EXs5 + Perc-TekClub 09-EXs5-L + Perc-Granular 07-EXs5 + Perc-Granular 08-EXs5 + Perc-Granular 05-EXs5 + Tsuzumi + Tsuzumi + Perc-Granular 29/04-EXs5 + Perc-Granular 29/04-EXs5 + Perc-Granular 06-EXs5 + Perc-Granular 06-EXs5 + Dance Perc 8 Maraca + Shaker 1/2 + Perc-Granular 03-EXs5 + Perc-Granular 03-EXs5 + SD-Granular 16-EXs5-L + SD-Granular 17-EXs5-L + Perc-Granular 20/19-EXs5 + Perc-Granular 33-EXs5 + Perc-Granular 45-EXs5 + Perc-Granular 12-EXs5 + Perc-Granular 21-EXs5 + Triangle-Mute + Triangle-Roll + Perc-Granular 26-EXs5 + Marc Tree + BD-Granular 07-EXs5 + HiHat Granular 21-EXs5 + SD-Granular 13-EXs5 + HiHat Granular 22-EXs5 + Crash-Dance 3 + Dance Perc 7 Cowbell + Dance Claps 1 + Dance Perc 8 Maraca + Orch.SD Roll 2 + Orchestral SD + Bell Tree + Stadium + Applause + + + + + + + + BD-Dry 1 + BD-HipHop 07-EXs5 + BD-TekClub 06-EXs5 + BD-Dance 10 + BD-Dance 05 (Rap) + BD-Dance 13-EXs5 + SD-HipHop 09-EXs5-L + SD-Mad Analog 01-EXs5 + SD-HipHop 06b-EXs5 + SD-Mad Analog 13-EXs5 + SD-Dance 04-EXs5 + Cymbal Reverse + SD-Reverse + BD-Dance 06 (R&B) + BD-Dark/BD-Dance 13 + BD-Hip 1/2 + BD-Dance 13 + BD-Dance Soft/08 + SD-AmbiHop + SD-Ambi Hit 1/SD-Classic Room + SD-Brasser/SD-Amb.Piccolo 1 + SD-Ambi Pop/SD-AmbiCrackle 1 + SD-Dry Present/SD-Big Rock + SD-Vintage 6 + BD-Dry 6/BD-Dark + BD-Pop/BD-Dance 08 + SD-Sidestick Ambi/Dry + SD-Vintage 1 + Hand Claps 3 Hip/4 Happy + SD-Hip 6/SD-Dry 4 + Tom-Dance + Hi-Hat-Tight 1/2 + Tom-Dance + Hi-Hat-Old Closed 2/Hi-Hat-Hip 1 + Tom-Dance + Hi-Hat-Clangy Open/Hi-Hat-Crisp Open + Tom-Old R&B + Tom-Old R&B + Noise-VPM Mod/BD-Dry 1 + Tom-Old R&B + Ride Cymbal 5 Edge + Crash-Dance 3/China Cymbal + Ride Cymbal Cup 2 + Tambourine 2/1-Push + Explosion/BD-Dry 1 + Cowbell 1/BD-Dry 1 + Crash-Dance 2 DDD1/Splash Cymbal + Church Bell/BD-Dry 1 + Crickets 2 + Bongo 1 Hi-Open + Bongo 1 Lo-Open + Conga 1 Hi-MtSlap/1-Toe + Conga 1 Hi-Open + Conga 1 Lo-Open + Hat-TekClub 02-EXs5-R/L + Hi-Hat Dance 1 Open/Closed + Tambourine 3-EXs5 + Tambourine 1-EXs5 + Cabasa-Up/Down + Maracas-Push + Maraca/Chinese Gong + SD-Metal Striker/Chinese Gong + Car Engine/Guiro-Tap + Industry Loop 2/Guiro-Tap + Industry Loop 2/BD-Dry 1 + Car Stop/BD-Dry 1 + Tambourine 3/BD-Dry 1 + Industry Loop 2/BD-Dry 1 + String Slap/BD-Dry 1 + Dance Perc 1 Hip/Triangle-Mute + Zap 1/BD-Dry 1 + Bubble/Shaker 1 + Car Crash/BD-Dry 1 + BD-Dance 05 (Rap) + Hi-Hat Dance 3 Closed + SD-Dance 4 (Old) + Hi-Hat Dance 3 Open/BD-Dry 1 + Chinese Gong/Crash-Dance 3 + Dance Perc 6 Clave + SD-Dance 01-EXs5 + Stream/BD-Dry 1 + "Two..." Solo/"One..." Solo + "Four..." Solo/"Three..." Solo + Bell Tree/BD-Dry 1 + Space Lore/BD-Dry 1 + Hart Beat/BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Granular 11-EXs5-R + BD-HipHop 10-EXs5 + BD-Granular 25-EXs5 + BD-Granular 26-EXs5-L + BD-Granular 28-EXs5 + SD-Sizzle + SD-Granular 06-EXs5 + SD-Rap 2 + SD-HipHop 03-EXs5-L + SD-Granular 07-EXs5-L + Cymbal Reverse + SD-Reverse + BD-Squash + BD-Jazz 2 + Zap 1 + Footstep 1/2 + Cyber Drum + Tabla-Na/Open + Orchestral SD + SD-Rap 2 + SD-Dance 1 + SD-Amb.Piccolo 2/SD-Off Center + SD-Atomic + Djembe-Open + BD-Dance 02 + Timbales Hi-Rim 1 + SD-Jazz Ring + Zap 3 + SD-Dance 4 (Old) + Tom 3-Lo + Hi-Hat Dance 1 Closed + Tom-Brush Floor + Hi-Hat Dance 3 Open + Tom-Synth + Triangle-Open/Hi-Hat Dance 1 Open + Tom-Synth + Tom-Jazz Hi + Orch Cymbal-Open + Tom 4-Lo/BD-Dry 1 + Ride Cymbal-Jazz + Air Vortex + Ride Cymbal Cup 1 -R + Tambourine 3/2 + Splash Cymbal + Dist.Slide 1/2 + Ride Cymbal 2 -L + Bird 1 + Sleigh Bell + Bongo 2 Hi-Open f/mf + Bongo 2 Lo-Muffled f/mp + Conga 2 Hi-ClosedSlap/Hi-Closed + Conga 2 Hi-OpenSlap/Hi-Open mf + Conga 2 Lo-OpenSlap/Lo-Open mf + Perc-Mad Analog 35-EXs5 + Perc-Mad Analog 36-EXs5 + Perc-Granular 39/38-EXs5 + Tambourine 2 / 1-Pull + Shaker 3/2-EXs5 + Dance Perc 8 Maraca + Finger Cymbal + Guiro-Long + Vocal SD + Taiko-Open/Gamelan + Perc-Granular 28-EXs5-L + Telephone Ring + Footstep 1/2 + Zap 1 + Cricket Spectrum + Noise-VPM Mod + Noise-White + Noise-VPM Mod + Agogo Bell + GunShot + Finger Cymbal/Marc Tree + Samba Whistle 1 + HiHat Granular 18-EXs5-R/L + DJ-BD Rub + Industry + Dist.Slide 2/1 + Harp-Glissando/Rainstick + Castanet-Double 1/Vibraslap + DJ-Record Stop/DJ-Reverse + DJ-Scratch 2/1 + Tribe + Dance Perc 7 Cowbell + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-amb 1 mf -L + BD-Dance 10 + DJ Vinyl Sliced 13 + BD-Dance 07 + BD-Dance 08 + SD-HipHop 04-EXs5 + SD-Vintage 2 + Dance Perc 3 House/BD-Dry 5 + Voice 20 + SD-Jazz Ring + SD-Full Room + Cymbal Reverse + SD-Whopper + BD-Hip 2 + BD-Klanger + BD-PhatJaz + BD-Dance 09 + Zap 1 + SD-Brasser + DJ Vinyl Sliced 16 + SD-Ambi Hit 1 + SD-AmbiHop + SD-Rap 2 + SD-Dance 1 + Voice Perc-Kick 1/3-EXs5 + Voice Perc-Kick 2a/2b-EXs5 + Voice Perc-Snare 4-EXs5 + Voice Perc-Snare 1-EXs5 + Voice Perc-Snare 3-EXs5 + Voice Perc-Snare 2-EXs5 + Voice Perc-Tom-Lo-EXs5 + VoicePerc-HH-Close1-EXs5 + Voice Perc-Tom-Lo-EXs5 + VoicePerc-HH-Close2-EXs5 + Voice Perc-Tom-Mid-EXs5 + Voice Perc-Cymbal-EXs5 + Voice Perc-Tom-Mid-EXs5 + Voice Perc-Tom-Hi-EXs5 + Voice Perc-Cymbal-EXs5 + Voice Perc-Tom-Hi-EXs5 + Ride HipHop 2-EXs5 + Car Stop IFX5 Off + Voice Perc-"Pa"-EXs5 + Industry Loop 1 + Perc-Granular 32-EXs5 + Voice Perc-Cowbell-EXs5 + Voice Perc-Cymbal-EXs5 + Voice Perc-Shaker-EXs5 + Perc-Granular 27-EXs5 + Zap 2 + DJ Vinyl Sliced 18 + DJ Vinyl Sliced 17 + DJ-Vinyl Sliced 08-EXs5 + DJ Vinyl Sliced 05 + Voice 23 + DJ-Reverse + DJ Vinyl Sliced 03 + Voice 21 + DJ-Scratch 3b + Voice 26 + DJ-Hit Rub + Voice 09 + DJ Vinyl Sliced 13 + DJ-Scratch 1-EXs5 + DJ-Scratch 2-EXs5 + DJ-Record Stop-EXs5 + DJ Vinyl Sliced 08 + DJ Vinyl Sliced 04 + DJ Vinyl Sliced 05 + Triangle-Mute + Triangle-Open + DJ-Vocal Rub 1 + DJ Vinyl Sliced 02 + DJ Vinyl Sliced 06 + DJ Vinyl Sliced 07 + DJ-Scratch 2-EXs5 + DJ Vinyl Sliced 09 + DJ Vinyl Sliced 10 + DJ-Vocal Rub 2 + DJ Vinyl Sliced 11 + DJ Vinyl Sliced 12 + DJ-Vocal Rub 2 + Voice 07 + PC Vox-Cks + DJ-Scratch 1 + DJ Vinyl Sliced 18 + + + + + + + + Taiko-Rim/Open + Tsuzumi + Djembe-Open + Djembe-Slap/Mute + Baya-Ghe/Open + Baya-Mute 1/3 + Baya-Mute 5/4 + Tabla-Open + Tabla-Tin/Tabla-Mute 4 + Tabla-Mute 2/3 + Tabla-Na/Tabla-Mute 1 + Tabla-Mute 1/2 + Orchestral BD/BD-Dry 1 + Tribe/BD-Dry 1 + Finger Snap/BD-Dry 1 + Hand Claps 2/BD-Dry 1 + Triangle-Mute/BD-Dry 1 + Triangle-Open/BD-Dry 1 + Castanet-Double 2/BD-Dry 1 + Castanet-Single/BD-Dry 1 + Castanet-Double 1/BD-Dry 1 + Guiro-Long/BD-Dry 1 + Guiro-Short + Guiro-Tap/BD-Dry 1 + Vibraslap/BD-Dry 1 + Claves/BD-Dry 1 + Dance Perc 6 Clave/BD-Dry 1 + Cuica 1-Lo/BD-Dry 1 + Cuica 1-Hi/BD-Dry 1 + Timbales Lo-Open/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Timbales Lo-Rim/BD-Dry 1 + Woodblock 1/BD-Dry 1 + Timbales Lo-Mute/BD-Dry 1 + Vocal Woodblock/BD-Dry 1 + Timbales Hi-Edge/BD-Dry 1 + Timbales Hi-Rim 2/BD-Dry 1 + Chacha Bell/BD-Dry 1 + Timbales Hi-Rim 1/BD-Dry 1 + Mambo Bell/BD-Dry 1 + Timbales-Paila/BD-Dry 1 + Bongo 1 Lo-Open/BD-Dry 1 + Cowbell 1/BD-Dry 1 + Bongo 1 Lo-Slap/BD-Dry 1 + Cowbell 2/BD-Dry 1 + Bongo 1 Hi-Open/BD-Dry 1 + Vocal Cowbell/BD-Dry 1 + Bongo 1 Hi-Slap + Conga 1 Lo-Open + Conga 1 Lo-Slap/BD-Dry 1 + Conga 1 Lo-MuteSlap/BD-Dry 1 + Conga 1 Hi-MtSlap/BD-Dry 1 + Conga 1 Hi-Open + Conga 1 Hi-Mute/BD-Dry 1 + Conga 1-Heel/BD-Dry 1 + Conga 1 Hi-Slap 1/BD-Dry 1 + Conga 1-Toe/BD-Dry 1 + Conga 1 Hi-Slap 2/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Agogo Bell/BD-Dry 1 + Maracas-Pull/Push + Shaker 1 + Shaker 2 + Cabasa-Tap/Down + Cabasa-Tap/Up + Caxixi-Hard/Caxixi-Soft + Tambourine 1-Push/BD-Dry 1 + Tambourine 2 + Tambourine 1-Pull/BD-Dry 1 + Tambourine 3 + Sleigh Bell + Samba Whistle 1/BD-Dry 1 + Samba Whistle 1/BD-Dry 1 + Bell Tree/BD-Dry 1 + Samba Whistle 1/BD-Dry 1 + Marc Tree/BD-Dry 1 + Samba Whistle 1/BD-Dry 1 + Rap Sleigh bell + Rainstick/BD-Dry 1 + Chinese Gong/BD-Dry 1 + Bird 1/BD-Dry 1 + Bird 2/BD-Dry 1 + Bird 1/BD-Dry 1 + Cricket Spectrum/BD-Dry 1 + Marc Tree/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + Click/BD-Dry 1 + + + + + + + + BD-Dry 1 + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestra Hit + Orchestral BD + Orch.SD Roll 1 + Orchestral SD + Orch.SD Roll 2 + Orchestral SD + Orch Cymbal-Open + Orch Cymbal-Closed + Taiko-Open + Orch.SD Roll 1 + Orchestral SD + Orch.SD Roll 2 + Orchestral SD + Triangle-Mute + Triangle-Roll + Triangle-Open + Chinese Gong + Tambourine 3 + Woodblock 1 + Castanet-Double 1 + Woodblock 1 + Castanet-Double 2 + Woodblock 1 + Castanet-Single + Woodblock 1 + Woodblock 1 + Timpani + Timpani + Timpani + Timpani + Djembe-Open + Djembe-Mute + Djembe-Slap + Caxixi-Hard/Soft + Sleigh Bell + Cabasa-Up/Down + Finger Cymbal + Baya-Ghe + Baya-Open + Baya-Mute 1 + Baya-Mute 2 + Baya-Mute 3 + Baya-Mute 4 + Baya-Mute 5 + Tabla-Na + Tabla-Open + Tabla-Tin + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Mute 3 + Tabla-Mute 4 + Taiko-Open + Tsuzumi/Vibraslap + Taiko-Rim + Conga 1 Lo-Open/BD-Dry 1 + Conga 1 Lo-MuteSlap / Hi-Slap 2 + Conga 1 Hi-Mute / Bongo 1 Hi-Slap + Conga 1 Hi-Slap 1 / BD-Dry 1 + Cabasa-Up + Rainstick + Harp-Glissando + Rainstick + BD-Dry 1 + BD-Dry 1 + Gamelan + Gamelan + Bird 1 + Cricket Spectrum/BD-Dry 1 + Tribe + BD-Dry 1 + Gamelan + Gamelan/Tambura + Gamelan + Gamelan + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-amb 1 mf -L + BD-amb 1 f -L + BD-amb 2 mf -L + BD-amb 2 f -L + BD-Dry 1 + BD-Dry 2 + BD-Dry 3 + BD-Dry 4 + BD-Dry 5 + BD-Soft + BD-Soft Room + BD-Jazz 1 + BD-Jazz 2 + BD-Pillow + BD-Woofer + BD-Mondo Kill + BD-Terminator + BD-Tubby + BD-Gated + BD-Tight + BD-Squash + BD-Dance 03 + BD-Dark + BD-Ringy + BD-Dance Soft + BD-PhatJaz + Orchestral BD + SD-Dry 2 + SD-Dry 3 + SD-Dry 4 + SD-Normal + SD-GhostSet roll + SD-GhostSet p2 + SD-GhostSet p1 + SD-GhostSet f + SD-Full Room + SD-Off Center + SD-Jazz Ring + SD-Amb.Piccolo 1 + SD-Amb.Piccolo 2 + SD-Paper + SD-Classic Room + SD-Brush Hit + SD-Brush Tap 1 + SD-Brush Tap 2 + SD-Brush Swirl + SD-Atomic + SD-Big Rock + SD-Yowie + SD-Processed 1 + SD-Processed 2 + SD-Cracker Room + Orch.SD Roll 1 + SD-Sidestick Dry + Orch.SD Roll 2 + Crash Cymbal 2 + Orchestral SD + SD-Reverse + Hi-Hat 2-Closed 1 + SD-Hip 5 + Hi-Hat 2-Pedal + SD-AmbiHop + Hi-Hat 2-Open 1 + SD-Whopper + SD-Ambi Pop + SD-dry 1 p -L + SD-dry 1 mp -L + SD-dry 1 mf -L + SD-dry 1 f -L + SD-dry 1 ff -L + SD-dry 1 ff -L + SD-dry 1 fff -L + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 6 + BD-Dance 01 + BD-Dance 02 + BD-Dance 11 + BD-Dance 04 + BD-Hip 1 + BD-Hip 2 + BD-Hip 3 + BD-Hip 4 + BD-Pop + BD-Dance 08 + BD-Dance 14 + BD-Ambi + BD-Ambi Crackle + BD-Ambi Soft + BD-AmbiRocker + BD-Chili + BD-Dance 13 + BD-Dance 07 + BD-Cannon + BD-Klanger + BD-Dance 05 (Rap) + BD-Dance 06 (R&B) + BD-Dance 09 + BD-Dance 10 + BD-Dance 12 + SD-Ambi House + SD-Dance 1 + SD-Rap 1 + SD-Rap 2 + SD-Noise + SD-Sizzle + SD-Hip 1 + SD-Hip 2 + SD-Hip 3 + SD-Hip 4 + SD-Hip 6 + SD-Ringy + SD-Tiny + SD-Vintage 1 + SD-Vintage 2 + SD-Vintage 3 + SD-Vintage 4 + SD-Vintage 5 + SD-Vintage 6 + SD-AmbiCrackle 1 + SD-AmbiCrackle 2 + SD-AmbiCrackle 3 + SD-Brasser + SD-Chili + SD-Klanger + SD-Dry Present + SD-Ambi Hit 1 + SD-Ambi Hit 2 + Hi-Hat Dance 3 Closed + SD-Ambi Hit 3 + Hi-Hat Dance 1 Closed + SD-Dance 3 (New) + Hi-Hat Dance 1 Open + SD-Dance 4 (Old) + SD-Dance 5 + SD-Dance 8 + Vocal SD + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + + + + + + + + Maracas-Push + Maracas-Pull + Caxixi-Hard + Dance Perc 1 Hip + Tambourine 1-Push + Tambourine 1-Pull + Tambourine 2 + Tambourine 3 + BD-Hip 3 + Hi-Hat 3-Closed 1 + SD-Chili + Hi-Hat 3-Closed 2 + Hi-Hat 2-Closed 1 + Hi-Hat 2-Closed 2 + Hi-Hat 3-Open 2 + Hi-Hat 2-Open 2 + Hi-Hat 3-Open 1 + Hi-Hat 2-Open 1 + Hi-Hat 3-Sizzle + Hi-Hat 2-Sizzle + Hi-Hat 2-Pedal + Hi-Hat 3-Pedal + Hi-Hat 2-Pedal Open + Hi-Hat 3-Pedal Open + Hi-Hat-Chili + Hi-Hat-Crisp Closed 1 + Hi-Hat-Vintage 1 + Hi-Hat-Crisp Closed 2 + Hi-Hat-Vintage 2 + Hi-Hat-Crisp Open + Hi-Hat-Tight 1 + Hi-Hat-Tight 2 + Hi-Hat-Old Open + Hi-Hat-Old Closed 1 + Hi-Hat-Clangy Open + Hi-Hat-Old Closed 2 + Hi-Hat-Old Closed 3 + Hi-Hat-Alpo Closed + Hi-Hat-Amb Crackle + Hi-Hat-Whispy + Hi-Hat-Ambi + Hi-Hat-Hip 2 + Hi-Hat-Hip 1 + Hi-Hat Dance 1 Closed + Sleigh Bell + Hi-Hat Dance 1 Open + Rap Sleigh bell + Hi-Hat Dance 3 Closed + Hi-Hat Dance 3 Open + Crash Cymbal 4 + Crash-Dance 3 + Crash Cymbal 2 + China Cymbal + Crash Cymbal 3 + Splash Cymbal + Ride-Dance 1 + Crash-Dance 1 + Ride-Dance 2 DDD1 + Crash-Dance 2 DDD1 + Ride Cymbal 4 Edge + Ride Cymbal 5 Edge + Ride Cymbal-Jazz + Ride Cymbal Cup 2 + Cymbal Reverse + Splash Cymbal + Crash-Dance 1 + Ride-Dance 1 + Ride Cymbal Cup 2 + Hi-Hat Dance 1 Open + Hi-Hat-Old Closed 2 + Hi-Hat-Old Closed 3 + Hi-Hat-Crisp Closed 2 + Hi-Hat Dance 1 Closed + Hi-Hat 3-Closed 1 + Hi-Hat Dance 3 Closed + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + Click + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Tight + BD-Dry 3 + SD-Sidestick Ambi + SD-Full Room + Dance Claps 1 + SD-Full Room + Tom 3-Floor + Hi-Hat 2-Open 2 + Tom 3-Floor + Hi-Hat 3-Pedal + Tom 3-Lo + Hi-Hat 3-Open 2 + Tom 3-Lo + Tom 3-Hi + Crash Cymbal 3 + Tom 3-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Dry 2 + BD-Dry 3 + SD-Sidestick Ambi + SD-Yowie + Dance Claps 1 + SD-Yowie + Tom 4-Floor + Hi-Hat 2-Open 2 + Tom 4-Floor + Hi-Hat 3-Pedal + Tom 4-Lo + Hi-Hat 3-Open 2 + Tom 4-Lo + Tom 4-Hi + Crash Cymbal 3 + Tom 4-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Gated + BD-Mondo Kill + SD-Sidestick Ambi + SD-Big Rock + Dance Claps 1 + SD-Big Rock + Tom 4-Floor + Hi-Hat 2-Open 2 + Tom 4-Floor + Hi-Hat 3-Pedal + Tom 4-Lo + Hi-Hat 3-Open 2 + Tom 4-Lo + Tom 4-Hi + Crash Cymbal 3 + Tom 4-Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Hip 1 + BD-Dance 14 + SD-Sidestick Ambi + Tom-Synth + Dance Claps 1 + SD-Ambi Hit 1 + Tom-Synth + Hi-Hat 3-Sizzle + Tom-Synth + Hi-Hat 3-Pedal + Tom-Synth + Hi-Hat 3-Sizzle + Tom-Synth + Tom-Synth + Crash Cymbal 3 + Tom-Synth + Ride Cymbal 4 Edge + Crash Cymbal 2 + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Dance 06 (R&B) + BD-Dance 09 + Rimshot-Dance 2 + SD-Dance 3 (New) + Dance Claps 1 + SD-Dance 4 (Old) + Tom-Dance + Hi-Hat Dance 3 Open + Tom-Dance + Hi-Hat Dance 3 Open + Tom-Dance + Hi-Hat Dance 3 Open + Tom-Dance + Tom-Dance + Ride-Dance 1 + Tom-Dance + Crash-Dance 3 + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Dry 4 + BD-Jazz 2 + SD-Sidestick Ambi + SD-Paper + Dance Claps 1 + SD-Jazz Ring + Tom-Jazz Floor + Hi-Hat 2-Closed 1 + Tom-Jazz Floor + Hi-Hat 2-Pedal + Tom-Jazz Hi + Hi-Hat 2-Open 1 + Tom-Jazz Hi + Tom-Jazz Hi + Crash Cymbal 2 + Tom-Jazz Hi + Ride Cymbal 4 Edge + China Cymbal + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Dry 4 L/R Off + BD-Jazz 2 L/R Off + SD-Sidestick Ambi L/R Off + SD-Brush Tap 1 L/R Off + SD-Brush Hit L/R Off + SD-Brush Swirl L/R Off + Tom-Jazz Floor L/R Off + Hi-Hat 2-Closed 1 L/R 1 + Tom-Jazz Floor L/R Off + Hi-Hat 2-Pedal L/R 1 + Tom-Jazz Hi L/R Off + Hi-Hat 2-Open 1 L/R 1 + Tom-Jazz Hi L/R Off + Tom-Jazz Hi L/R Off + Crash Cymbal 2 L/R Off + Tom-Jazz Hi L/R Off + Ride Cymbal 4 Edge L/R Off + China Cymbal L/R Off + Ride Cymbal-Jazz + Baya-Open + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Ride-Dance 2 DDD1 + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Hi-Hat 2-Closed 1 + Hi-Hat 2-Pedal + Hi-Hat 2-Open 1 + Ride Cymbal 4 Edge + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Soft Room + Orchestral BD + SD-Sidestick Ambi + Orchestral SD + Conga 2 Hi-Open mf + Orchestral SD + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Timpani + Baya-Openf + China Cymbal + Timbales-Paila + Crash Cymbal 3 + Bongo 2 Hi-Fingernail + Orch Cymbal-Open + Conga 1 Hi-Mute + Conga 2 Lo-Toe + Conga 2 Lo-OpenSlapFlam + Conga 2 Lo-Open f + Conga 1 Lo-Open + Guiro-Long + Djembe-Mute + Timbales Hi-Rim 1 + Timbales Hi-Rim 1 + Woodblock 2 + Conga 2 Hi-Heel + Triangle-Mute-EXs5 + Triangle-Mute-EXs5 + Conga 2 Hi-Closed + Conga 2 Hi-Muffled + Bongo 2 Hi-Cuptone + Bongo 2 Hi-Slap + Bongo 2 Hi-Slap + Tabla-Mute 1 + Tabla-Mute 2 + Tabla-Na + Baya-Mute 3 + Timbales Lo-Open + Tambourine 2 + Tambourine 2-EXs5 + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + BD-Dry 1 + Orchestral SD + Orch.SD Roll 2 + Finger Snap + Perc-Granular 17-EXs5-L + Dog + Perc-Granular 22-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Perc-Granular 16-EXs5R + "Cap" + Baya-Mute 3 + BD-Tight + BD-Dry 3 + SD-Sidestick Ambi + SD-Full Room + Perc-Granular 17-EXs5-L + CompuVoice-Toi + Perc-Granular 21-EXs5 + Perc-Granular 22-EXs5 + Drumstick Hit + Finger Snap + "Cap" + Crickets 1 + Door Slam + Rainstick + Door Creak + Tribe + Finger Snap + "Tehh" + "Kaahh" + CompuVoice-Do it + CompuVoice-Test + "Ti" + "Pan" + "Thuum" + "Four..." Solo + "Three..." Solo + Perc-Granular 22-EXs5 + Tambourine 2-EXs5 + "Two..." Solo + "One..." Solo + PC Vox-Sis + PC Vox-Dunc + CompuVoice-Chi + PC Vox-For + Dog + PC Vox-One + Footstep 2 + PC Vox-If + PC Vox-Zee + PC Vox-Ae + PC Vox-Pa + "Haaa" + CompuVoice-Beep + "Chhi" L/R Off + CompuVoice-Ti + PC Vox-O + PC Vox-Tu + Dog + Stadium + Applause + Conga 2 Hi-Open mf + Djembe-Slap + Djembe-Mute + "Pehh" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +When received this message, transmits INQUlRY MESSAGE REPLY +[ F0,7E,nn,06,01,F7 ] 3rd byte nn : Channel = 0 - F : Global Channel + = 7F : Any Channel + + F0 7E 7F 06 01 F7 + + + + +Received when in Sequencer mode +[ F0,7E,nn,09,01,F7 ] 3rd byte nn : Channel = 0 - F : Global Channel + = 7F : Any Channel + + F0 7E 7F 09 01 F7 + + + + +Master Volume +[ F0,7F,0g,04,01,vv,mm,F7 ] 3rd byte g : Global Channel + 6th byte vv : Value(LSB) + 7th byte mm : Value(MSB) + mm,vv = 00,00 - 7F,7F : Min - Max + + F0 7F 00 04 01 00 00 F7 + + + + +Master Balance +[ F0,7F,0g,04,02,vv,mm,F7 ] 3rd byte g : Global Channel + 6th byte vv : Value(LSB) + 7th byte mm : Value(MSB) + mm,vv = 00,00:Left, 40,00:Center, 7F,7F:Right + + F0 7F 00 04 02 00 00 F7 + + + + +Control Master Tune(cent) in Global +[ F0,7F,0g,04,03,vv,mm,F7 ] 3rd byte g : Global Channel + 6th byte vv : Value(LSB) + 7th byte mm : Value(MSB) + mm,vv = 20,00:-50, 40,00:+00, 60,00:+50 + + F0 7F 00 04 03 00 00 F7 + + + + +Control Transpose (chromatic step) in Global +[ F0,7F,0g,04,04,vv,mm,F7 ] 3rd byte g : Global Channel + 6th byte vv : Value(LSB) + 7th byte mm : Value(MSB) + mm,vv = 34,00:-12, 40,00:+00, 4C,00:+12 + + F0 7F 00 04 04 00 00 F7 + + + + +MODE REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 12 Function + F7 End of Excl +(Receives this message, and transmits Func=42 message) + + F0 42 30 00 01 15 12 F7 + + + + +MODE DATA + F0, 42, 3g, 00, 01, 15 Excl Header + 42 Function + 0000 0mmm Mode (*2) + 0000 0000 reserved + 0sss ssss data1 + 0ddd dddd data2 + 00 Reserved + F7 End of Excl +(Receives FUNC=12 message, and transmits this message & data) + + ss : bit 0,1 = 0 : Note Receive is EVEN, = 1 : ODD, = 2 : ALL + bit 3,4 = 0 : Seq Clock is internal, = 1 : External(MIDI), = 2 : External(USB) + + dd : bit 0 = 0 : Programs Memory is not protected, = 1 : protected + bit 1 = 0 : Combinations Memory is not protected, = 1 : protected + bit 2 = 0 : Sequence Memory is not protected, = 1 : protected + bit 3 = 0 : Drum Kits Memory is not protected, = 1 : protected + bit 4 = 0 : Arp Patterns Memory is not protected, = 1 : protected + + F0 42 30 00 01 15 42 00 00 00 00 00 F7 + + + + +MODE CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 4E Function + 0000 0mmm Mode (*2) + F7 End of Excl +(Receives this message & data, changes the Mode, and transmits Func=24 message) +When the Mode is changed by SW, transmits this message & data. + + F0 42 30 00 01 15 4E 00 F7 + + + + +PARAMETER CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 41 Function + 0000 0mmm Mode (*2) + 00 Parameter ID (MSB) + 0ppp pppp Parameter ID (LSB) (TABLE 1,2,5,10,11) + 00 Parameter SUB ID (MSB) + 0qqq qqqq Parameter SUB ID (LSB) (TABLE 1,2,5,10,11) + 0000 vvvv Value (Value bit28-31) + 0vvv vvvv Value (Value bit21-27) + 0vvv vvvv Value (Value bit14-20) + 0vvv vvvv Value (Value bit7-13) + 0vvv vvvv Value (Value bit0-6) + F7 End of Excl +(Even if it receives this message, a reply does not return) +When the Parameter No. is changed by SW, transmits this message & data. + + F0 42 30 00 01 15 41 00 00 00 00 00 00 00 00 00 00 F7 + + + + +DRUM KIT PARAMETER CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 53 Function + 0sss ssss Index No. (s = 00-7F ( : C-1-G9)) + 0bbb bbbb Drum Kit Bank (*3) + 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) + 00 Parameter No.(MSB) + 0ppp pppp Parameter No.(LSB) (TABLE 6) + 0000 vvvv Value (Value bit28-31) + 0vvv vvvv Value (Value bit21-27) + 0vvv vvvv Value (Value bit14-20) + 0vvv vvvv Value (Value bit7-13) + 0vvv vvvv Value (Value bit0-6) + F7 End of Excl +(Even if it receives this message, a reply does not return) + + F0 42 30 00 01 15 53 00 00 00 00 00 00 00 00 00 00 F7 + + + + +ARPEGGIO PATTERN PARAMETER CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 54 Function + 0000 000b Arpeggio AorB b = 0 : Arpeggio A, 1 : Arpeggio B + 0000 aaaa Pattern No. MSB (bit10-7) + 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) + 0sss ssss Step No. ss = 00-2F ( : 00-47) + 0ttt tttt Tone No. tt = 00-0B ( : 00-11) + 00 Parameter No. (MSB) + 0ppp pppp Parameter No. (LSB) (TABLE 4) + 0000 vvvv Value (Value bit28-31) + 0vvv vvvv Value (Value bit21-27) + 0vvv vvvv Value (Value bit14-20) + 0vvv vvvv Value (Value bit7-13) + 0vvv vvvv Value (Value bit0-6) + F7 End of Excl +(Even if it receives this message, a reply does not return) + + F0 42 30 00 01 15 54 00 00 00 00 00 00 00 00 00 00 00 00 F7 + + + + +PROGRAM PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 00 Object(Program) + 0bbb bbbb Program Bank (*3) + 00 Reserved + 0ppp pppp Program No. + F7 End of Excl +(Receives this message, and transmits Func=73/Object=00 or Func=24 message) + + F0 42 30 00 01 15 72 00 00 00 00 F7 + + + + +COMBINATION PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 01 Object(Combination) + 0bbb bbbb Combination Bank (*3) + 00 Reserved + 0ccc cccc Combination No. + F7 End of Excl +(Receives this message, and transmits Func=73/Object=01 or Func=24 message) + + F0 42 30 00 01 15 72 01 00 00 00 F7 + + + + +SONG TIMBRE SET DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 02 Object(Song Timbre Set) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=02 or Func=24 message) + + F0 42 30 00 01 15 72 02 00 00 00 F7 + + + + +GLOBAL DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 03 Object(Global) + 00 Reserved + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=03 or Func=24 message) + + F0 42 30 00 01 15 72 03 00 00 00 F7 + + + + +DRUM KIT PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 04 Object(Drum Kit) + 0bbb bbbb Drum Kit Bank (*3) + 00 Reserved + 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=04 or Func=24 message) + + F0 42 30 00 01 15 72 04 00 00 00 F7 + + + + +SONG CONTROL DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 08 Object(Song Control) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=08 or Func=24 message) + + F0 42 30 00 01 15 72 08 00 00 00 F7 + + + + +SONG EVENT DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 09 Object(Song Event) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=09 or Func=24 message) + + F0 42 30 00 01 15 72 09 00 00 00 F7 + + + + +DRUM TRACK PATTERN DATA DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 0B Object(Drum Track Pattern) + 00 Reserved + 0000 0kkk Pattern No. MSB + 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=0B&0C or Func=24 message) + + F0 42 30 00 01 15 72 0B 00 00 00 F7 + + + + +PROGRAM BANK PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 0F Object(Program Bank) + 0bbb bbbb Program Bank (*3) + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=0F or Func=24 message) + + F0 42 30 00 01 15 72 0F 00 00 00 F7 + + + + +COMBINATION BANK PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 11 Object(Combination Bank) + 0bbb bbbb Combination Bank (*3) + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=11 or Func=24 message) + + F0 42 30 00 01 15 72 11 00 00 00 F7 + + + + +DRUM KIT BANK PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 12 Object(Drum Kit Bank) + 0bbb bbbb Drum Kit Bank (*3) + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=12 or Func=24 message) + + F0 42 30 00 01 15 72 12 00 00 00 F7 + + + + +ARPEGGIO PATTERN PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 16 Object(Arpeggio Pattern) + 00 Reserved + 0000 aaaa Pattern No. MSB (bit10-7) + 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) + F7 End of Excl +(Receives this message, and transmits Func=73/Object=16 or Func=24 message) + + F0 42 30 00 01 15 72 16 00 00 00 F7 + + + + +ARPEGGIO PATTERN ALL PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 72 Function + 17 Object(Arpeggio Pattern All) + 00 Reserved + 00 Reserved + 00 Reserved + F7 End of Excl +(Receives this message, and transmits Func=73/Object=17 or Func=24 message) + + F0 42 30 00 01 15 72 17 00 00 00 F7 + + + + +PROGRAM PARAMETER (IN INTERNAL MEMORY) DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 00 Object(Program) + 0bbb bbbb Program Bank (*3) + 00 Reserved + 0ppp pppp Program No. + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 1) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=00 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 00 00 00 00 00 00 F7 + + + + +COMBINATION PARAMETER (IN INTERNAL MEMORY) DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 01 Object(Combination) + 0bbb bbbb Combination Bank (*3) + 00 Reserved + 0ccc cccc Combination No. + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 2) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=01 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 01 00 00 00 00 00 F7 + + + + +SONG TIMBRE SET DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 02 Object(Song Timbre Set) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 10) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=02 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 02 00 00 00 00 00 F7 + + + + +GLOBAL DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 03 Object(Global) + 00 Reserved + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 3) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=03 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 03 00 00 00 00 00 F7 + + + + +DRUM KIT PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 04 Object(Drum Kit) + 0bbb bbbb Drum Kit Bank (*3) + 00 Reserved + 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 6) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=04 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 04 00 00 00 00 00 F7 + + + + +SONG CONTROL DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 08 Object(Song Control) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 11) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=08 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 08 00 00 00 00 00 F7 + + + + +SONG EVENT DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 09 Object(Song Event) + 00 Reserved + 00 Reserved + 0ccc cccc Song No. cc = 00-7F ( : 0-127) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 12) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=09 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 09 00 00 00 00 00 F7 + + + + +DRUM TRACK PATTERN DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 0B Object(Drum Track Pattern) + 00 Reserved + 0000 0kkk Pattern No. MSB + 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 7) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=0B message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 0B 00 00 00 00 00 F7 + + + + +DRUM TRACK PATTERN EVENT DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 0C Object(Drum Track Pattern Event) + 000n nnnn Drum Track Pattern Event Num Packet (0-26) + 0000 0kkk Pattern No. MSB + 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 8) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=0B message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 0C 00 00 00 00 00 F7 + + + + +CUELIST DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 0D Object(CueList) + 00 Reserved + 00 Reserved + 000c cccc Cuelist No. c = 0-13 ( : 0-19) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 9) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 0D 00 00 00 00 00 F7 + + + + +PROGRAM BANK PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 0F Object(Program Bank) + 0bbb bbbb Program Bank (*3) + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 1) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=0F message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 0F 00 00 00 00 00 F7 + + + + +COMBINATION BANK PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 11 Object(Combination Bank) + 0bbb bbbb Combination Bank (*3) + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 2) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=11 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 11 00 00 00 00 00 F7 + + + + +DRUM KIT BANK PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 12 Object(Drum Kit Bank) + 0bbb bbbb Drum Kit Bank (*3) + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 6) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=12 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 12 00 00 00 00 00 F7 + + + + +ARPEGGIO PATTERN PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 16 Object(Arpeggio Pattern) + 00 Reserved + 0000 aaaa Pattern No. MSB (bit10-7) + 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 4) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=16 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 16 00 00 00 00 00 F7 + + + + +ARPEGGIO PATTERN ALL PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 73 Function + 17 Object(Arpeggio Pattern All) + 00 Reserved + 00 Reserved + 00 Reserved + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 4) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=72/Object=17 message, and transmits this message & data. +Transmits this message & data when DATA DUMP is executed. + + F0 42 30 00 01 15 73 17 00 00 00 00 00 F7 + + + + +CURRENT OBJECT PARAMETER DUMP REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 74 Function + 0000 oooo Object (o = 0:Program/1:Combination/2:Song Timbre Set) + F7 End of Excl +(Receives this message, and transmits Func=75/Object=00(/01/02) or Func=24 message) + + F0 42 30 00 01 15 74 00 F7 + + + + +CURRENT PROGRAM PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 75 Function + 00 Object(Program) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 1) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=74/Object=00 message, and transmits this message & data. + + F0 42 30 00 01 15 75 00 00 00 F7 + + + + +CURRENT COMBINATION PARAMETER DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 75 Function + 01 Object(Combination) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 2) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=74/Object=01 message, and transmits this message & data. + + F0 42 30 00 01 15 75 01 00 00 F7 + + + + +CURRENT SONG TIMBRE SET DATA DUMP + F0, 42, 3g, 00, 01, 15 Excl Header + 75 Function + 02 Object(Current Song Timbre) + 0ooo oooo Object Version (*4) + 0ddd dddd Data (*1, TABLE 10) + : + F7 End of Excl +(Receives this message & data, and transmits Func=24 message) +Receives Func=74/Object=02 message, and transmits this message & data. + + F0 42 30 00 01 15 75 02 00 00 F7 + + + + +STORE OBJECT PROGRAM REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 00 Object(Program) + 0bbb bbbb Bank No. (*3) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 00 00 F7 + + + + +STORE OBJECT COMBINATION REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 01 Object(Combination) + 0bbb bbbb Bank No. (*3) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 01 00 F7 + + + + +STORE OBJECT CURRENT SONG TIMBRE SET REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 02 Object(Song Timbre Set) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 02 00 F7 + + + + +STORE OBJECT DRUM KIT REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 04 Object(Drum Kit) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 04 00 F7 + + + + +STORE OBJECT PROGRAM BANK REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 0F Object(Program Bank) + 0bbb bbbb Bank No. (*3) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 0F 00 F7 + + + + +STORE OBJECT COMBINATION BANK REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 11 Object(Combination Bank) + 0bbb bbbb Bank No. (*3) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 11 00 F7 + + + + +STORE OBJECT DRUM KIT BANK REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 12 Object(Drum Kit Bank) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 12 00 F7 + + + + +STORE OBJECT ARPEGGIO PATTERN REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 16 Object(Arpeggio Pattern) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 16 00 F7 + + + + +STORE OBJECT ARPEGGIO PATTERN ALL REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 76 Function + 17 Object(Arpeggio Pattern All) + 00 Reserved + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 76 17 00 F7 + + + + +OBJECT WRITE REQUEST + F0, 42, 3g, 00, 01, 15 Excl Header + 77 Function + 0000 oooo Object (o = 0:Program/1:Combination) + 0bbb bbbb Bank (*3) + 00 Reserved + 0iii iiii Index No. (i = Program or Combination) + F7 End of Excl +(Receives this message, write the data and transmits Func=24 message) + + F0 42 30 00 01 15 77 00 00 00 00 F7 + + + + +RESULT REPORT + F0, 42, 3g, 00, 01, 15 Excl Header + 24 Function + 0ccc cccc Result Code + F7 End of Excl +(Transmits this message when there is a result in the MIDI IN message) + + cc = 0 : Result OK + 1 : Received Data Length is wrong + 2 : Dest Memory is protected + 3 : Dest Bank/Prog/Param is not exist + 4 : The mode is wrong + 5 : Memory over flow + 40 : Another type error + + F0 42 30 00 01 15 24 00 F7 + + + + +WAVEFORM CHANGE + F0, 42, 3g, 00, 01, 15 Excl Header + 7F Function + 00 Object(CHANGE) + 0000 cccc MIDI Channel c = 0-15 + 0000 000o OSC Select o = 0 or 1 + 0000 0bbb Bank Select b = 0 (Mono) ~ 1 (Stereo) + 0nnn nnnn Number LSB + 0000 nnnn Number MSB + 0000 ssss Start Offset s = 0 (Off) ~ 8 (8ch) + 0000 000t OSC Switch t = 0 (Off) or 1 (On) + F7 End of Excl + + n = 0 ~ 1076 (In case of "Bank:Mono") + n = 0 ~ 6 (In case of "Bank:Stereo") + + F0 42 30 00 01 15 7F 00 00 00 00 00 00 00 00 F7 + + + + +WAVEFORM RESET + F0, 42, 3g, 00, 01, 15 Excl Header + 7F Function + 01 Object(RESET) + 0000 cccc MIDI Channel c = 0-15 + F7 End of Excl + + F0 42 30 00 01 15 7F 01 00 F7 + + + diff -Nru "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/KORG microSTATION combinations.idf" "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/KORG microSTATION combinations.idf" --- "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/KORG microSTATION combinations.idf" 2013-03-28 15:15:57.000000000 +0000 +++ "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/KORG microSTATION combinations.idf" 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/KORG microSTATION.idf" "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/KORG microSTATION.idf" --- "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/KORG microSTATION.idf" 2013-03-28 15:15:57.000000000 +0000 +++ "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/KORG microSTATION.idf" 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Korg-MS2000R.idf muse-3.0.2+ds1/share/instruments/Korg-MS2000R.idf --- muse-2.1.2/share/instruments/Korg-MS2000R.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Korg-MS2000R.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Korg Wavestation EX.idf" "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Korg Wavestation EX.idf" --- "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Korg Wavestation EX.idf" 1970-01-01 00:00:00.000000000 +0000 +++ "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Korg Wavestation EX.idf" 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,447 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kick + + + + + + AmbiKick + + + Crack Snar + + + Crack Snar Low + + + Snare + + + Snare High + + + Sidestick + + + Sidestick High + + + Tenny Hit + + + Tenny Hit High + + + Tom Low + + + Tom Lo-Mid + + + + + + Tom Mid + + + + + + Tom High + + + HiHat Closed + + + HiHat Closed Low + + + HiHat Open + + + HiHat Open Low + + + Conga Low + + + + + + Conga Mid + + + Conga High + + + + + + Claves + + + Claves High + + + Tick Hit + + + Pot Hit + + + + + + Hammer + + + + + + Thong + + + + + + Noise Vibe + + + Piano Hit + + + + + + "Tuunn" + + + + + + "Pehh" + + + "Thuum" + + + + + + "Kaahh" + + + + + + "Tchh" + + + "Pan" + + + "Ti" + + + "Cap" + + + "Chhi" + + + "Tinn" + + + "Haaa" + + + Crickets + + + Noise 2 Mid + + + Noise 2 Low + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + Noise 2 High + + + + + + diff -Nru muse-2.1.2/share/instruments/Korg-X50.idf muse-3.0.2+ds1/share/instruments/Korg-X50.idf --- muse-2.1.2/share/instruments/Korg-X50.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Korg-X50.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Korg-X5DR-PresetA.idf muse-3.0.2+ds1/share/instruments/Korg-X5DR-PresetA.idf --- muse-2.1.2/share/instruments/Korg-X5DR-PresetA.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Korg-X5DR-PresetA.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Korg-X5DR-PresetB.idf muse-3.0.2+ds1/share/instruments/Korg-X5DR-PresetB.idf --- muse-2.1.2/share/instruments/Korg-X5DR-PresetB.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Korg-X5DR-PresetB.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Kurzweil-SP2X.idf muse-3.0.2+ds1/share/instruments/Kurzweil-SP2X.idf --- muse-2.1.2/share/instruments/Kurzweil-SP2X.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Kurzweil-SP2X.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Lexicon-MX200.idf muse-3.0.2+ds1/share/instruments/Lexicon-MX200.idf --- muse-2.1.2/share/instruments/Lexicon-MX200.idf 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Lexicon-MX200.idf 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru muse-2.1.2/share/instruments/MC303.idf muse-3.0.2+ds1/share/instruments/MC303.idf --- muse-2.1.2/share/instruments/MC303.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/MC303.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/MC505.idf muse-3.0.2+ds1/share/instruments/MC505.idf --- muse-2.1.2/share/instruments/MC505.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/MC505.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/ns5r.idf muse-3.0.2+ds1/share/instruments/ns5r.idf --- muse-2.1.2/share/instruments/ns5r.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/ns5r.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Roland-E28.idf muse-3.0.2+ds1/share/instruments/Roland-E28.idf --- muse-2.1.2/share/instruments/Roland-E28.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Roland-E28.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + diff -Nru muse-2.1.2/share/instruments/Roland_FantomXR.idf muse-3.0.2+ds1/share/instruments/Roland_FantomXR.idf --- muse-2.1.2/share/instruments/Roland_FantomXR.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Roland_FantomXR.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Roland-JV90.idf muse-3.0.2+ds1/share/instruments/Roland-JV90.idf --- muse-2.1.2/share/instruments/Roland-JV90.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Roland-JV90.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Roland-MT32.idf muse-3.0.2+ds1/share/instruments/Roland-MT32.idf --- muse-2.1.2/share/instruments/Roland-MT32.idf 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Roland-MT32.idf 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bass Drum + Bass Drum + Side Stick + Accoustic Snare + Hand Clap + Electric Snare + Low Floor Tom + Closed Hi-Hat + Low Tom + Pedal Hi-Hat + Mid Tom + Open Hi-Hat + Mid Tom + High Tom + Crash Cymbal + High Tom + Ride Cymbal + Tambourine + Cowbell + High Bongo + Low Bongo + Mute High Conga + Open HighConga + Low Conga + High Timbales + Low Timbales + High Agogo + Low Agogo + Cabasa + Maracas + Short Whistle + Long Whistle + Guiro + Claves + + + + + + + + + + + + + + + + + + Byte 3 = Unit Number + F0 41 00 16 11 10 00 00 00 00 17 59 F7 + + + + Byte 3 = Unit Number + F0 41 00 16 11 05 00 00 03 00 00 78 F7 + + + + Byte 3 = Unit Number + F0 41 00 16 11 08 00 00 02 00 00 76 F7 + + + diff -Nru muse-2.1.2/share/instruments/Roland-SCD70.idf muse-3.0.2+ds1/share/instruments/Roland-SCD70.idf --- muse-2.1.2/share/instruments/Roland-SCD70.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Roland-SCD70.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + diff -Nru "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Roland SD-50.idf" "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Roland SD-50.idf" --- "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Roland SD-50.idf" 1970-01-01 00:00:00.000000000 +0000 +++ "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Roland SD-50.idf" 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,5845 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 7e 7f 09 03 + + + + 7e 7f 09 03 + + + + + + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Kick Drum 2 + + + Kick Drum 1 + + + Aco.Snare + + + Elec.Snare + + + Low Tom 2 + + + ClosedHi-hat [EXC 1] + + + Low Tom 1 + + + Pedal Hi-hat [EXC 1] + + + Mid Tom 2 + + + Open Hi-hat [EXC 1] + + + Mid Tom 1 + + + High Tom 2 + + + CrashCymbal1 + + + High Tom 1 Room + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Hi WoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + + + + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Kick Drum 2 + + + Kick Drum 1 + + + Aco.Snare + + + Elec.Snare + + + Room LowTom2 + + + ClosedHi-hat [EXC 1] + + + Room LowTom1 + + + Pedal Hi-hat [EXC 1] + + + Room MidTom2 + + + Open Hi-hat [EXC 1] + + + Room MidTom1 + + + Room HiTom2 + + + CrashCymbal1 + + + Hi Tom1 + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + + + + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Kick Drum 2 + + + Power Kick + + + PowerSnare + + + Elec.Snare + + + PowerLowTom2 + + + ClosedHi-hat [EXC 1] + + + PowerLowTom1 + + + Pedal Hi-hat [EXC 1] + + + PowerMidTom2 + + + Open Hi-hat [EXC 1] + + + PowerMidTom1 + + + Power HiTom2 + + + CrashCymbal1 + + + Power HiTom1 + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + + + + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Kick Drum 2 + + + Elec.Kick + + + E.SnareDrum1 + + + E.SnareDrum2 + + + E.Low Tom 2 + + + ClosedHi-hat [EXC 1] + + + E.Low Tom 1 + + + Pedal Hi-hat [EXC 1] + + + E.Mid Tom 2 + + + Open Hi-hat [EXC 1] + + + E.Mid Tom 1 + + + E.Hi Tom 2 + + + CrashCymbal1 + + + E.Hi Tom 1 + + + Ride Cymbal1 + + + Reverse Cym. + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + + + + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Kick Drum 2 + + + Ana.Kick + + + Ana.Rim Sho + + + Ana.Snare + + + Hand Clap + + + Elec.Snare + + + Ana.Low Tom2 + + + Ana.ClosedHH [EXC 1] + + + Ana.Low Tom1 + + + Ana.ClosedHH [EXC 1] + + + Ana.Mid Tom2 + + + Ana.Open HH [EXC 1] + + + Ana.Mid Tom1 + + + Ana.Hi Tom2 + + + Ana.Cymbal + + + Ana.Hi Tom1 + + + Ride Cymbal1 + + + China Cymbal + + + Ride Bell + + + Tambourine + + + SplashCymbal + + + Ana.Cowbell + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + Low Bongo + + + Ana.Hi Conga + + + Ana.MidConga + + + Ana.LowConga + + + High Timbale + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Ana.Maracas + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Ana.Claves + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + + + + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Jazz Kick 2 + + + Jazz Kick 1 + + + Aco.Snare + + + Elec.Snare + + + Low Tom 2 + + + ClosedHi-hat [EXC 1] + + + Low Tom 1 + + + Pedal Hi-hat [EXC 1] + + + Mid Tom 2 + + + Open Hi-hat [EXC 1] + + + Mid Tom 1 + + + High Tom 2 + + + CrashCymbal1 + + + High Tom 1 + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Hi WoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + + + + + + High Q + + + Slap Pedal + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Jazz Kick 2 + + + Jazz Kick 1 + + + Brush Tap + + + Brush Slap + + + Brush Swirl + + + Low Tom 2 + + + ClosedHi-hat [EXC 1] + + + Low Tom 1 + + + Pedal Hi-hat [EXC 1] + + + Mid Tom 2 + + + Open Hi-hat [EXC 1] + + + Mid Tom 1 + + + High Tom 2 + + + CrashCymbal1 + + + High Tom 1 + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + + + + + + ClosedHi-hat [EXC 1] + + + Hi-hat [EXC 1] + + + Open Hi-hat [EXC 1] + + + Ride Cymbal1 + + + Sticks – + + + Square Click + + + Metron Click + + + Metron Bell + + + Concert BD 2 + + + Concert BD 1 + + + Concert SD + + + Castanets + + + Concert + + + Timpani F + + + Timpani F# + + + Timpani G + + + Timpani G# + + + Timpani A + + + Timpani A# + + + Timpani B + + + Timpani c + + + Timpani c# + + + Timpani d + + + Timpani d# + + + Timpani e + + + Timpani f + + + SplashCymbal + + + Concert Cym2 + + + Vibra-slap + + + Concert Cym1 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + + + + + + High Q + + + SD Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + GtFret Noise + + + Cut Noise Up + + + Cut Noise Dw + + + Slap St.Bass + + + Fl.Key Click + + + Laughing + + + Scream + + + Punch + + + Heart Beat + + + Footsteps 1 + + + Footsteps 2 + + + Applause + + + Door Creak + + + Door + + + Scratch + + + Wind Chimes + + + Car Engine + + + Car Stop + + + Car Pass + + + Car Crash + + + Siren + + + Train + + + Jetplane + + + Helicopter + + + Starship + + + Gun Shot + + + Machine Gun + + + Lasergun + + + Explosion + + + Dog + + + Horse Gallop + + + Birds + + + Rain + + + Thunder + + + Wind + + + Seashore + + + Stream + + + Bubble + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 7e 7f 09 02 + + + + 7e 7f 09 02 + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Kick Drum 2 + + + Kick Drum 1 + + + Reg.Snr 2 + + + Reg.Snr 1 + + + Reg.F.Tom + + + ClosedHi-hat [EXC 1] + + + Reg.L.Tom + + + Pedal Hi-hat [EXC 1] + + + Reg.M.Tom + + + Open Hi-hat [EXC 1] + + + Reg.M.Tom + + + Reg.H.Tom + + + CrashCymbal1 + + + Reg.H.Tom + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Hi WoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Kick Drum 2 + + + Kick Drum 1 + + + Snare 1 + + + Snare 2 + + + Low Tom 2 + + + ClosedHi-hat [EXC 1] + + + Low Tom 1 + + + Pedal Hi-hat [EXC 1] + + + Mid Tom 2 + + + Open Hi-hat [EXC 1] + + + Mid Tom 1 + + + High Tom 2 + + + CrashCymbal1 + + + High Tom 1 + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Kick Drum 2 + + + Kick Drum 1 + + + Snare 1 + + + Snare 2 + + + Low Tom 2 + + + ClosedHi-hat [EXC 1] + + + Low Tom 1 + + + Pedal Hi-hat [EXC 1] + + + Mid Tom 2 + + + Open Hi-hat [EXC 1] + + + Mid Tom 1 + + + High Tom 2 + + + CrashCymbal1 + + + High Tom 1 + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Power Kick2 + + + Power Kick1 + + + Power Snare1 + + + Power Snare2 + + + Low Tom1 + + + ClosedHi-hat [EXC 1] + + + Low Tom2 + + + Pedal Hi-hat [EXC 1] + + + Mid Tom 1 + + + Open Hi-hat [EXC 1] + + + Mid Tom2 + + + High Tom1 + + + CrashCymbal1 + + + High Tom2 + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Jazz Kick 2 + + + Jazz Kick 1 + + + Side Stick + + + Jazz Snare1 + + + Hand Clap + + + Jazz Snare2 + + + Low Tom 2 + + + ClosedHi-hat [EXC 1] + + + Low Tom 1 + + + Pedal Hi-hat [EXC 1] + + + Mid Tom 2 + + + Open Hi-hat [EXC 1] + + + Mid Tom 1 + + + High Tom 2 + + + CrashCymbal1 + + + High Tom 1 + + + Ride Cymbal1 + + + China Cymbal + + + Ride Bell + + + Tambourine + + + SplashCymbal + + + Cowbell + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + Low Bongo + + + MuteHi Conga + + + OpenHi Conga + + + Low Conga + + + High Timbale + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Claves + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Jazz Kick 2 + + + Jazz Kick 1 + + + Brush Tap + + + Brush Slap + + + Brush Swirl + + + Low Tom 2 + + + ClosedHi-hat [EXC 1] + + + Low Tom 1 + + + Pedal Hi-hat [EXC 1] + + + Mid Tom 2 + + + Open Hi-hat [EXC 1] + + + Mid Tom 1 + + + High Tom 2 + + + CrashCymbal1 + + + High Tom 1 + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Hi WoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Ana.Kick 1 + + + Ana.Kick 2 + + + Ana.Rim Shot + + + Ana.Snare + + + Elec.Snare + + + Ana.Low Tom2 + + + Ana.Cl HH 1 [EXC 1] + + + Ana.Low Tom1 + + + Ana.Cl HH 2 [EXC 1] + + + Ana.Mid Tom2 + + + Ana.Open HH [EXC 1] + + + Ana.Mid Tom1 + + + Ana.Hi Tom2 + + + Ride Cymbal1 + + + Ana.Hi Tom1 + + + Ana.Cymbal + + + China Cymbal + + + SplashCymbal + + + Ana.Cowbell + + + CrashCymbal + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + Ana.Hi Conga + + + Ana.MidConga + + + Ana.LowConga + + + Ana.Maracas + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Ana.Claves + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Ana.Kick 1 + + + Ana.Kick 2 + + + Ana.Rim Shot + + + Ana.Snare + + + Elec.Snare + + + Ana.Low Tom2 + + + Ana.Cl HH 1 [EXC 1] + + + Ana.Low Tom1 + + + Ana.Cl HH 2 [EXC 1] + + + Ana.Mid Tom2 + + + Ana.Open HH [EXC 1] + + + Ana.Mid Tom1 + + + Ana.Hi Tom2 + + + Ride Cymbal1 + + + Ana.Hi Tom1 + + + Ana.Cymbal + + + China Cymbal + + + SplashCymbal + + + Ana.Cowbell + + + CrashCymbal + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + Ana.Hi Conga + + + Ana.MidConga + + + Ana.LowConga + + + Ana.Maracas + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Ana.Claves + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Slap + + + Scratch Push [EXC 7] + + + Scratch Pull [EXC 7] + + + Sticks + + + Square Click + + + Metron Click + + + Metron Bell + + + Kick 2 + + + Kick 1 + + + Snare 1 + + + Snare 2 + + + Reg.F.Tom + + + ClosedHi-hat [EXC 1] + + + Reg.L.Tom + + + Pedal Hi-hat [EXC 1] + + + Reg.M.Tom + + + Open Hi-hat [EXC 1] + + + Reg.M.Tom + + + Reg.H.Tom + + + CrashCymbal1 + + + Reg.H.Tom + + + Ride Cymbal1 + + + China Cymbal + + + SplashCymbal + + + CrashCymbal2 + + + Vibra-slap + + + Ride Cymbal2 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + Mix Kick1 + + + Analog Kick1 + + + Mix Snare4 + + + Analog Kick2 + + + Mix Snare3 + + + Mix Kick2 + + + HH Shaker [EXC 1] + + + Mix Kick4 + + + Mix Kick3 + + + TR808 Rim + + + Mix Snare1 + + + TR808 Clap + + + Mix Snare2 + + + Tom1 + + + TR808 ClHH [EXC 1] + + + Tom2 + + + Noise ClHH [EXC 1] + + + Tom3 + + + TR808 OpHH [EXC 1] + + + Tom4 + + + Tom5 + + + TR909 Cym + + + Tom6 + + + TR808 Cym + + + China Cymbal + + + Rock Rd Edge + + + Mix Perc + + + Mix Crash1 + + + Ana.Cowbell + + + Mix Crash2 + + + Roll FX + + + Ride Cym + + + High Bongo + + + Low Bongo + + + MuteHi Conga + + + OpenHi Conga + + + Low Conga + + + High Timbale + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Claves + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + TR808 Kick + + + HH Kick + + + Mix Snare2 + + + Mix Kick4 + + + Short Snare2 + + + Mix Kick3 + + + TR808 Cl HH [EXC 1] + + + Mix Kick1 + + + Mix Kick2 + + + Soft Stick + + + Short Snare1 + + + Hand Clap + + + Mix Snare1 + + + TR808 Tom1 + + + Cl Hihat1 [EXC 1] + + + TR808 Tom2 + + + Cl Hihat2 [EXC 1] + + + TR808 Tom3 + + + Op Hihat [EXC 1] + + + TR808 Tom4 + + + TR808 Tom5 + + + TR909 Cym + + + TR808 Tom6 + + + TR808 Cym + + + China Cymbal + + + Rock Rd Edge + + + Mix Hat + + + Mix Crash1 + + + Ana.Cowbell + + + Mix Crash2 + + + Vibra-slap + + + Ride Cym + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + Cabasa + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Hi WoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + TR909 Kick + + + SH32 Kick1 + + + AnalogSnare + + + Analog Kick + + + TR808 Snare + + + SH32 Kick2 + + + Syn ClHH1 [EXC 1] + + + Mix Kick2 + + + Mix Kick1 + + + Mix Rim + + + Mix Snare1 + + + TR808 Clap + + + Mix Snare2 + + + Mix Tom1 + + + Mix ClHH1 [EXC 1] + + + Mix Tom2 + + + Mix ClHH2 [EXC 1] + + + Mix Tom3 + + + Op Hihat [EXC 1] + + + Mix Tom4 + + + Mix Tom5 + + + Mix Crash + + + Mix Tom6 Deep + + + Rock Rd Edge + + + China Cymbal + + + Ride Cymbal + + + Tambourine + + + Syn Splash + + + Cowbell + + + Concert Cym + + + Vibraslap + + + TR808 Cym + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + Cabasa + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + SH32 Kick1 + + + TR909 Kick1 + + + AnalogSnare1 + + + Analog Kick1 + + + TR808 Snare + + + SH32 Kick2 + + + Pedal Hihat [EXC 1] + + + TR909 Kick2 + + + Analog Kick2 + + + Synth Rim + + + DistNz Snare + + + TR808 Clap + + + DistNz Snare + + + Deep Tom1 + + + Syn ClHH1 [EXC 1] + + + Deep Tom2 + + + Syn ClHH2 [EXC 1] + + + Deep Tom3 + + + Syn OpHH [EXC 1] + + + Deep Tom4 + + + Deep Tom5 + + + TR808 OpHH + + + Tom6 Mix + + + Wide Syn Cym + + + TR808 Cym1 + + + Ride Cym1 + + + Castanet + + + TR808 Cym2 + + + TR808Cowbell + + + Ride Cym2 + + + Syn Cowbell + + + Ride Cym3 + + + High Bongo + + + MuteHi Conga + + + OpenHi Conga + + + Cabasa + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz + + + + + + + + MC500 Beep1 + + + MC500 Beep2 + + + Concert SD + + + Snare Roll + + + Snap + + + High Q + + + TR808 Kick + + + Mix Kick1 + + + TR909 Snare1 + + + Mix Kick2 + + + TR909 Snare2 + + + Mix Kick3 + + + Thin ClHH [EXC 1] + + + TR909 Kick + + + Analog Kick + + + Mix Rim + + + Mix Snare2 + + + Mix Clap + + + Mix Snare1 + + + Mix Tom1 + + + Mix ClHH1 [EXC 1] + + + Mix Tom2 + + + Mix ClHH2 [EXC 1] + + + Mix Tom3 + + + Op Hihat [EXC 1] + + + Mix Tom4 + + + Mix Tom5 + + + Mix Crash + + + Tom6 + + + Rock Rd Edge + + + China Cymbal + + + Ride Cymbal + + + Tambourine + + + Splash Cym + + + Cowbell + + + Concert Cym + + + Vibraslap + + + Syn China + + + High Bongo + + + Low Bongo + + + MuteHi Conga + + + OpenHi Conga + + + Low Conga + + + High Timbale + + + Low Timbale + + + High Agogo + + + Low Agogo + + + Cabasa + + + Maracas + + + ShortWhistle [EXC 2] + + + Long Whistle [EXC 2] + + + Short Guiro [EXC 3] + + + Long Guiro [EXC 3] + + + Claves + + + HiWoodBlock + + + LowWoodBlock + + + Mute Cuica [EXC 4] + + + Open Cuica [EXC 4] + + + MuteTriangle [EXC 5] + + + OpenTriangle [EXC 5] + + + Shaker + + + Jingle Bell + + + Bell Tree + + + Castanets + + + Mute Surdo [EXC 6] + + + Open Surdo [EXC 6] + + + Applause + + + SnareGhost1 + + + SnareGhost2 + + + Hand Clap1 + + + Hand Clap2 + + + Scratch1 + + + Scratch2 + + + Scratch3 + + + Hit1 + + + Hit2 + + + Hit3 + + + Hit4 + + + Hit5 + + + Low Tom1 + + + Low Tom2 + + + Cajon1 + + + Cajon2 + + + Syn Burst Nz + + + Sweep Down + + + Laser + + + Syn Back Nz 1PS + + + + + + diff -Nru muse-2.1.2/share/instruments/Roland_SRX-02.idf muse-3.0.2+ds1/share/instruments/Roland_SRX-02.idf --- muse-2.1.2/share/instruments/Roland_SRX-02.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Roland_SRX-02.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Roland_SRX-09.idf muse-3.0.2+ds1/share/instruments/Roland_SRX-09.idf --- muse-2.1.2/share/instruments/Roland_SRX-09.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Roland_SRX-09.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Roland-XP30.idf muse-3.0.2+ds1/share/instruments/Roland-XP30.idf --- muse-2.1.2/share/instruments/Roland-XP30.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Roland-XP30.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Waldorf-microQ-Factory2000.idf muse-3.0.2+ds1/share/instruments/Waldorf-microQ-Factory2000.idf --- muse-2.1.2/share/instruments/Waldorf-microQ-Factory2000.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Waldorf-microQ-Factory2000.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Waldorf-microQ-Factory2001.idf muse-3.0.2+ds1/share/instruments/Waldorf-microQ-Factory2001.idf --- muse-2.1.2/share/instruments/Waldorf-microQ-Factory2001.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Waldorf-microQ-Factory2001.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Waldorf-microQ.idf muse-3.0.2+ds1/share/instruments/Waldorf-microQ.idf --- muse-2.1.2/share/instruments/Waldorf-microQ.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Waldorf-microQ.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Waldorf-microQ-Phoenix.idf muse-3.0.2+ds1/share/instruments/Waldorf-microQ-Phoenix.idf --- muse-2.1.2/share/instruments/Waldorf-microQ-Phoenix.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Waldorf-microQ-Phoenix.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Waldorf_Microwave-I.idf muse-3.0.2+ds1/share/instruments/Waldorf_Microwave-I.idf --- muse-2.1.2/share/instruments/Waldorf_Microwave-I.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Waldorf_Microwave-I.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Waldorf-Q.idf muse-3.0.2+ds1/share/instruments/Waldorf-Q.idf --- muse-2.1.2/share/instruments/Waldorf-Q.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Waldorf-Q.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/xg.idf muse-3.0.2+ds1/share/instruments/xg.idf --- muse-2.1.2/share/instruments/xg.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/xg.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + Switch General Midi mode on 7e 7f 09 01 diff -Nru muse-2.1.2/share/instruments/Yamaha-01v.idf muse-3.0.2+ds1/share/instruments/Yamaha-01v.idf --- muse-2.1.2/share/instruments/Yamaha-01v.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-01v.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Yamaha-9000pro.idf muse-3.0.2+ds1/share/instruments/Yamaha-9000pro.idf --- muse-2.1.2/share/instruments/Yamaha-9000pro.idf 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-9000pro.idf 2018-01-26 21:59:38.000000000 +0000 @@ -0,0 +1,4171 @@ + + + + + + + + 43 10 4c 00 00 7e 00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Brush Tap Swirl + Snare Roll + Castanet + Snare Soft + Sticks + Kick Soft + Open Rim Shot + Kick Tight + Kick + Side Stick + Snare + Hand Clap + Snare Tight + Floor Tom L + Hi-Hat Closed + Floor Tom H + Hi-Hat Pedal + Low Tom + Hi-Hat Open + Mid Tom L + Mid Tom H + Crash Cymbal 1 + High Tom + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Brush Tap Swirl + Snare Roll + Castanet + Snare Soft 2 + Sticks + Kick Soft + Open Rim Shot H Short + Kick Tight + Kick Short + Side Stick Light + Snare Short + Hand Clap + Snare Tight H + Floor Tom L + Hi-Hat Closed + Floor Tom H + Hi-Hat Pedal + Low Tom + Hi-Hat Open + Mid Tom L + Mid Tom H + Crash Cymbal 1 + High Tom + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Brush Tap Swirl + Snare Roll + Castanet + Snare Electro + Sticks + Kick Tight L + Snare Pitched + Kick Wet + Kick Tight H + Stick Ambient + Snare Ambient + Hand Clap + Snare Tight 2 + Hybrid Tom 1 + Hi-Hat Closed 2 + Hybrid Tom 2 + Hi-Hat Pedal 2 + Hybrid Tom 3 + Hi-Hat Open 2 + Hybrid Tom 4 + Hybrid Tom 5 + Crash Cymbal 1 + Hybrid Tom 6 + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine Light + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Brush Tap Swirl + Snare Roll + Castanet + Snare Soft + Sticks + Kick Soft + Open Rim Shot + Kick Tight + Kick + Side Stick + Snare SNappy + Hand Clap + Snare Tight Snappy + Tom Room 1 + Hi-Hat Closed + Tom Room 2 + Hi-Hat Pedal + Tom Room 3 + Hi-Hat Open + Tom Room 4 + Tom Room 5 + Crash Cymbal 1 + Tom Room 6 + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Brush Tap Swirl + Snare Roll + Castanet + Snare Noisy + Sticks + Kick Soft + Open Rim Shot + Kick 2 + Kick Gate + Side Stick + Snare Rock + Hand Clap + Snare Rock Tight + Tom Rock 1 + Hi-Hat Closed + Tom Rock 2 + Hi-Hat Pedal + Tom Rock 3 + Hi-Hat Open + Tom Rock 4 + Tom Rock 5 + Crash Cymbal 1 + Tom Rock 6 + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Reverse Cymbal + Snare Roll + Hi Q 2 + Snare Snappy Electro + Sticks + Kick 3 + Open Rim Shot + Kick Gate + Kick Gate Heavy + Side Stick + Snare Noisy 2 + Hand Clap + Snare Noisy 3 + Tom Electro 1 + Hi-Hat Closed + Tom Electro 2 + Hi-Hat Pedal + Tom Electro 3 + Hi-Hat Open + Tom Electro 4 + Tom Electro 5 + Crash Cymbal 1 + Tom Electro 6 + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Scratch H2 + Scratch L2 + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Reverse Cymbal + Snare Roll + Hi Q 2 + Snare Noisy 4 + Sticks + Kick 3 + Open Rim Shot + Kick Analog Short + Kick Analog + Side Stick Analog + Snare Analog + Hand Clap + Snare Analog 2 + Tom Analog 1 + Hi-Hat Closed Analog + Floor Analog 2 + Hi-Hat Closed Analog 2 + Low Analog 3 + Hi-Hat Open Analog + Tom Analog 4 + Tom Analog 5 + Crash Analog + Tom Analog 6 + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell Analog + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga Analog H + Conga Analog M + Conga Analog L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas 2 + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves 2 + Wood Block H + Wood Block L + Scratch H2 + Scratch L2 + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Reverse Cymbal + Snare Roll + Hi Q 2 + Snare Techno + Sticks + Kick Techno Q + Rim Gate + Kick Techno L + Kick Techno + Side Stick Analog + Snare Clap + Hand Clap + Snare Dry + Tom Analog 1 + Hi-Hat Closed 3 + Floor Analog 2 + Hi-Hat Closed Analog 3 + Low Analog 3 + Hi-Hat Open 3 + Tom Analog 4 + Tom Analog 5 + Crash Analog + Tom Analog 6 + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell Analog + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga Analog H + Conga Analog M + Conga Analog L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas 2 + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves 2 + Wood Block H + Wood Block L + Scratch H2 + Scratch L2 + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Brush Tap Swirl + Snare Roll + Castanet + Snare Jazz H + Sticks + Kick Soft + Open Rim Shot + Kick Tight + Kick Jazz + Side Stick Light + Snare Jazz L + Hand Clap + Snare Jazz M + Floor Tom L + Hi-Hat Closed + Floor Tom H + Hi-Hat Pedal + Low Tom + Hi-Hat Open + Mid Tom L + Mid Tom H + Crash Cymbal 1 + High Tom + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Brush Tap Swirl + Snare Roll + Castanet + Brush Slap 2 + Sticks + Kick Soft + Open Rim Shot Light + Kick Tight + Kick Jazz + Side Stick Light + Brush Slap 3 + Hand Clap + Brush Tap 2 + Tom Brush 1 + Hi-Hat Closed + Tom Brush 2 + Hi-Hat Pedal + Tom Brush 3 + Hi-Hat Open + Tom Brush 4 + Tom Brush 5 + Crash Cymbal 1 + Tom Brush 6 + Ride Cymbal 1 + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell + Crash Cymbal 2 + Vibraslap + Ride Cymbal 2 + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap + Brush Swirl + Brush Slap + Brush Tap Swirl + Snare Roll + Castanet + Snare Soft + Sticks + Kick Soft 2 + Open Rim Shot + Gran Cassa + Gran Cassa Mute + Side Stick + Band Snare + Hand Clap + Band Snare 2 + Floor Tom L + Hi-Hat Closed + Floor Tom H + Hi-Hat Pedal + Low Tom + Hi-Hat Open + Mid Tom L + Mid Tom H + Hand Cymbal + High Tom + Hand Cymbal Short + Chinese Cymbal + Ride Cymbal Cup + Tambourine + Splash Cymbal + Cowbell + Hand Cymbal 2 + Vibraslap + Handy Cymbal 2 Short + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Snare L Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Light Stereo + Kick Std Stereo + Side Stick Stereo + Snare M Stereo + Hand Clap + Snare H Stereo + Floor Tom L Stereo + Hi-Hat Closed Stereo + Floor Tom H Stereo + Hi-Hat Pedal Stereo + Low Tom Stereo + Hi-Hat Open Stereo + Mid Tom L Stereo + Mid Tom H Stereo + Crash Cymbal 1 Stereo + High Tom Stereo + Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Ride Cymbal Cup Stereo + Tambourine + Splash Cymbal Stereo + Cowbell + Crash Cymbal 2 Stereo + Vibraslap + Ride Cymbal 2 Stereo + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Snare L Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Light Stereo + Kick Std Stereo + Side Stick Stereo + Snare M Stereo + Hand Clap + Snare H Stereo + Floor Tom L Stereo + Hi-Hat Closed Stereo + Floor Tom H Stereo + Hi-Hat Pedal Stereo + Low Tom Stereo + Hi-Hat Open Stereo + Mid Tom L Stereo + Mid Tom H Stereo + Crash Cymbal 1 Stereo + High Tom Stereo + Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Ride Cymbal Cup Stereo + Tambourine + Splash Cymbal Stereo + Cowbell + Crash Cymbal 2 Stereo + Vibraslap + Ride Cymbal 2 Stereo + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Snare Funk L Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Std Stereo + Kick Funk Stereo + Side Stick Stereo + Snare Funk M Stereo + Hand Clap + Snare Funk H Stereo + Floor Tom L Stereo + Hi-Hat Closed Stereo + Floor Tom H Stereo + Hi-Hat Pedal Stereo + Low Tom Stereo + Hi-Hat Open Stereo + Mid Tom L Stereo + Mid Tom H Stereo + Crash Cymbal 1 Stereo + High Tom Stereo + Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Ride Cymbal Cup Stereo + Tambourine + Splash Cymbal Stereo + Cowbell + Crash Cymbal 2 Stereo + Vibraslap + Ride Cymbal 2 Stereo + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Snare Funk L Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Std Stereo + Kick Funk Stereo + Side Stick Stereo + Snare Funk M Stereo + Hand Clap + Snare Funk H Stereo + Floor Tom L Stereo + Hi-Hat Closed Stereo + Floor Tom H Stereo + Hi-Hat Pedal Stereo + Low Tom Stereo + Hi-Hat Open Stereo + Mid Tom L Stereo + Mid Tom H Stereo + Crash Cymbal 1 Stereo + High Tom Stereo + Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Ride Cymbal Cup Stereo + Tambourine + Splash Cymbal Stereo + Cowbell + Crash Cymbal 2 Stereo + Vibraslap + Ride Cymbal 2 Stereo + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Brush Slap 2 Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Std Stereo + Kick Funk Stereo + Side Stick Stereo + Snare Brush M Stereo + Hand Clap + Snare Brush H Stereo + Brush Floor Tom L Stereo + Hi-Hat Closed Stereo + Brush Floor Tom H Stereo + Hi-Hat Pedal Stereo + Brush Low Tom Stereo + Hi-Hat Open Stereo + Brush Mid Tom L Stereo + Brush Mid Tom H Stereo + Brush Crash Cymbal 1 Stereo + Brush High Tom Stereo + Brush Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Brush Ride Cymbal Cup Stereo + Tambourine + Splash Cymbal Stereo + Cowbell + Brush Crash Cymbal 2 Stereo + Vibraslap + Brush Ride Cymbal 2 Stereo + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Timbale H + Timbale L + Agogo H + Agogo L + Cabasa + Maracas + Samba Whistle H + Samba Whistle L + Guiro Short + Guiro Long + Claves + Wood Block H + Wood Block L + Cuica Mute + Cuica Open + Triangle Mute + Triangle Open + Shaker + Jingle Bells + Bell Tree + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Snare L Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Light Stereo + Kick Std Stereo + Side Stick Stereo + Snare M Stereo + Hand Clap Stereo + Snare H Stereo + Floor Tom L Stereo + Hi-Hat Closed Stereo + Floor Tom H Stereo + Hi-Hat Pedal Stereo + Low Tom Stereo + Hi-Hat Open Stereo + Mid Tom L Stereo + Mid Tom H Stereo + Crash Cymbal 1 Stereo + High Tom Stereo + Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Ride Cymbal Cup Stereo + Tambourine Stereo + Splash Cymbal Stereo + Cowbell Stereo + Crash Cymbal 2 Stereo + Vibraslap + Ride Cymbal 2 Stereo + Bongo H Stereo + Bongo L Stereo + Conga H Mute Stereo + Conga H Open Stereo + Conga L Stereo + Timbale H Stereo + Timbale L Stereo + Agogo H + Agogo L + Cabasa Stereo + Maracas Stereo + Samba Whistle H + Samba Whistle L + Guiro Short Stereo + Guiro Long Stereo + Claves + Wood Block H + Wood Block L + Cuica Mute Stereo + Cuica Open Stereo + Triangle Mute Stereo + Triangle Open Stereo + Shaker Stereo + Jingle Bells + Wind Chime Stereo + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Snare L Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Light Stereo + Kick Std Stereo + Side Stick Stereo + Snare M Stereo + Hand Clap Stereo + Snare H Stereo + Floor Tom L Stereo + Hi-Hat Closed Stereo + Floor Tom H Stereo + Hi-Hat Pedal Stereo + Low Tom Stereo + Hi-Hat Open Stereo + Mid Tom L Stereo + Mid Tom H Stereo + Crash Cymbal 1 Stereo + High Tom Stereo + Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Ride Cymbal Cup Stereo + Tambourine Stereo + Splash Cymbal Stereo + Cowbell Stereo + Crash Cymbal 2 Stereo + Vibraslap + Ride Cymbal 2 Stereo + Bongo H Stereo + Bongo L Stereo + Conga H Mute Stereo + Conga H Open Stereo + Conga L Stereo + Timbale H Stereo + Timbale L Stereo + Agogo H + Agogo L + Cabasa Stereo + Maracas Stereo + Samba Whistle H + Samba Whistle L + Guiro Short Stereo + Guiro Long Stereo + Claves + Wood Block H + Wood Block L + Cuica Mute Stereo + Cuica Open Stereo + Triangle Mute Stereo + Triangle Open Stereo + Shaker Stereo + Jingle Bells + Wind Chime Stereo + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Snare L Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Std Stereo + Kick Funk Stereo + Side Stick Stereo + Snare Funk M Stereo + Hand Clap Stereo + Snare Funk H Stereo + Floor Tom L Stereo + Hi-Hat Closed Stereo + Floor Tom H Stereo + Hi-Hat Pedal Stereo + Low Tom Stereo + Hi-Hat Open Stereo + Mid Tom L Stereo + Mid Tom H Stereo + Crash Cymbal 1 Stereo + High Tom Stereo + Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Ride Cymbal Cup Stereo + Tambourine Stereo + Splash Cymbal Stereo + Cowbell Stereo + Crash Cymbal 2 Stereo + Vibraslap + Ride Cymbal 2 Stereo + Bongo H Stereo + Bongo L Stereo + Conga H Mute Stereo + Conga H Open Stereo + Conga L Stereo + Timbale H Stereo + Timbale L Stereo + Agogo H + Agogo L + Cabasa Stereo + Maracas Stereo + Samba Whistle H + Samba Whistle L + Guiro Short Stereo + Guiro Long Stereo + Claves + Wood Block H + Wood Block L + Cuica Mute Stereo + Cuica Open Stereo + Triangle Mute Stereo + Triangle Open Stereo + Shaker Stereo + Jingle Bells + Wind Chime Stereo + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Snare L Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Std Stereo + Kick Funk Stereo + Side Stick Stereo + Snare Funk M Stereo + Hand Clap Stereo + Snare Funk H Stereo + Floor Tom L Stereo + Hi-Hat Closed Stereo + Floor Tom H Stereo + Hi-Hat Pedal Stereo + Low Tom Stereo + Hi-Hat Open Stereo + Mid Tom L Stereo + Mid Tom H Stereo + Crash Cymbal 1 Stereo + High Tom Stereo + Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Ride Cymbal Cup Stereo + Tambourine Stereo + Splash Cymbal Stereo + Cowbell Stereo + Crash Cymbal 2 Stereo + Vibraslap + Ride Cymbal 2 Stereo + Bongo H Stereo + Bongo L Stereo + Conga H Mute Stereo + Conga H Open Stereo + Conga L Stereo + Timbale H Stereo + Timbale L Stereo + Agogo H + Agogo L + Cabasa Stereo + Maracas Stereo + Samba Whistle H + Samba Whistle L + Guiro Short Stereo + Guiro Long Stereo + Claves + Wood Block H + Wood Block L + Cuica Mute Stereo + Cuica Open Stereo + Triangle Mute Stereo + Triangle Open Stereo + Shaker Stereo + Jingle Bells + Wind Chime Stereo + + + + + + + + Surdo Mute + Surdo Open + Hi Q + Whip SLap + Scratch H + Scratch L + Finger Snap + Click Noise + Metronome Click + Metronome Bell + Seq Click L + Seq Click H + Brush Tap Stereo + Brush Swirl Stereo + Brush Slap Stereo + Brush Tap Swirl Stereo + Snare Roll Stereo + Castanet + Brush Slap 2 Stereo + Sticks + Kick Soft Stereo + Open Rim Shot Stereo + Kick Std Stereo + Kick Funk Stereo + Side Stick Stereo + Snare Brush M Stereo + Hand Clap Stereo + Snare Brush H Stereo + Brush Floor Tom L Stereo + Hi-Hat Closed Stereo + Brush Floor Tom H Stereo + Hi-Hat Pedal Stereo + Brush Low Tom Stereo + Hi-Hat Open Stereo + Brush Mid Tom L Stereo + Brush Mid Tom H Stereo + Brush Crash Cymbal 1 Stereo + Brush High Tom Stereo + Brush Ride Cymbal 1 Stereo + Chinese Cymbal Stereo + Brush Ride Cymbal Cup Stereo + Tambourine Stereo + Splash Cymbal Stereo + Cowbell Stereo + Brush Crash Cymbal 2 Stereo + Vibraslap + Brush Ride Cymbal 2 Stereo + Bongo H Stereo + Bongo L Stereo + Conga H Mute Stereo + Conga H Open Stereo + Conga L Stereo + Timbale H Stereo + Timbale L Stereo + Agogo H + Agogo L + Cabasa Stereo + Maracas Stereo + Samba Whistle H + Samba Whistle L + Guiro Short Stereo + Guiro Long Stereo + Claves + Wood Block H + Wood Block L + Cuica Mute Stereo + Cuica Open Stereo + Triangle Mute Stereo + Triangle Open Stereo + Shaker Stereo + Jingle Bells + Wind Chime Stereo + + + + + + + + Nakarazan Dom + Cabasa + Nakarazan Edge + Hager Dom + Hager Edge + Bongo H + Bongo L + Conga H Mute + Conga H Open + Conga L + Zagrouda H + Zagrouda L + Kick Soft + Side Stick + Snare Soft + Arabic Hand Clap + Snare + Floor Tom L + Hi-Hat Closed + Floor Tom H + Hi-Hat Pedal + Low Tom + Hi-Hat Open + Mid Tom L + Mid Tom H + Crash Cymbal 1 + High Tom + Ride Cymbal 1 + Crash Cymbal 2 + Duhulla Dom + Tambourine + Duhulla Tak + Cowbell + Duhulla Sak + Claves + Doff Dom + Katem Dom + Katem Tak + Katem Sak + Katem Tak + Doff Tak + Tabla Dom + Tabla Tak1 + Tabla Tik + Tabla Tak2 + Tabla Sak + Tabla Roll of Edge + Tabla Flam + Sagat 1 + Tabel Dom + Sagat 3 + Tabel Tak + Sagat 2 + Rik Dom + Rik Tak 2 + Rik Finger 1 + Rik Tak 1 + Rik Finger 2 + Rik Brass Tremolo + Rik Sak + Rik Tik + + + + + + + + Cutting Noise + Cutting Noise 2 + String Slap + Flute Key Click + Shower + Thunder + Wind + Stream + Bubble + Feed + Dog + Horse + Bird Tweet 2 + Ghost + Maou + + + + + + + + Phone Call + Door Squeak + Door Slam + Scratch Cut + Scratch H 3 + Wind Chime + Telephone Ring 2 + Car Engine Ignition + Car Tires Squeal + Car Passing + Car Crash + Siren + Train + Jet Plane + Starship + Burst + Roller Coaster + Submarine + Laugh + Scream + Punch + Heart Beat + Foot Steps + Machine Gun + Laser Gun + Explosion + Firework + + + + + + + + Conga H Tip Stereo + Conga H Heel Stereo + Conga H Open Stereo + Conga H Mute Stereo + Conga H Slap Open Stereo + Conga H Slap Stereo + Conga H Slap Mute Stereo + Conga L Tip Stereo + Conga L Heel Stereo + Conga L Open Stereo + Conga L Mute Stereo + Conga L Slap Open Stereo + Conga L Slap Stereo + Conga L Slide Stereo + Bongo H Open 1 Finger Stereo + Bongo H Open 3 Finger Stereo + Bongo H Rim Stereo + Bongo H Tip Stereo + Bongo H Heel Stereo + Bongo H Slap Stereo + Bongo L Open 1 Finger Stereo + Bongo L Open 3 Finger Stereo + Bongo L Rim Stereo + Bongo L Tip Stereo + Bongo L Heel Stereo + Bongo L Slap Stereo + Timbale L Open Stereo + Timbale H Open Stereo + Cowbell Top Stereo + Guiro Short Stereo + Guiro Long Stereo + Tambourine Stereo + Maracas Stereo + Shaker Stereo + Cabasa Stereo + + + + + + + + Hand Clap Stereo + Conga H Tip Stereo + Conga H Heel Stereo + Conga H Open Stereo + Conga H Mute Stereo + Conga H Slap Open Stereo + Conga H Slap Stereo + Conga H Slap Mute Stereo + Conga L Tip Stereo + Conga L Heel Stereo + Conga L Open Stereo + Conga L Mute Stereo + Conga L Slap Open Stereo + Conga L Slap Stereo + Conga L Slide Stereo + Bongo H Open 1 Finger Stereo + Bongo H Open 3 Finger Stereo + Bongo H Rim Stereo + Bongo H Tip Stereo + Bongo H Heel Stereo + Bongo H Slap Stereo + Bongo L Open 1 Finger Stereo + Bongo L Open 3 Finger Stereo + Bongo L Rim Stereo + Bongo L Tip Stereo + Bongo L Heel Stereo + Bongo L Slap Stereo + Timbale L Open Stereo + Timbale H Open Stereo + Cowbell Top Stereo + Guiro Short Stereo + Guiro Long Stereo + Tambourine Stereo + Maracas Stereo + Shaker Stereo + Cabasa Stereo + Cuica Mute Stereo + Cuica Open Stereo + Triangle Mute Stereo + Triangle Open Stereo + Wind Chime Stereo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Switch General Midi mode on: Ignored by the 9000pro + f0 7e 7f 09 01 f7 + + + + Switch XG mode on: Good practice to send though ignored by the 9000pro + f0 43 10 4c 00 00 7e 00 f7 + + + + +Change Style Section + +0xF0 0x43 0x7E 0x00 ss dd 0xF7 + +ss = Style Section + 0x00-0x01: INTRO II + 0x02-0x03: INTRO III + 0x04-0x07: INTRO I + 0x08: MAIN A + 0x09: MAIN B + 0x0A: MAIN C + 0x0B-0x0F: MAIN D + 0x10: FILL IN A + 0x11: FILL IN B + 0x12: FILL IN C + 0x13-0x17: FILL IN D + 0x18: BREAK FILL A + 0x19: BREAK FILL B + 0x1A BREAK FILL C + 0x1B-0x1F: BREAK FILL D + 0x20-0x21: ENDING II + 0x22-0x23: ENDING III + 0x24-0x27: ENDING I + +dd = Switch On/Off +On/Off + 0x00 Off + 0x7H On + + f0 43 7e 00 00 00 f7 + + + + +Tempo setting of internal sequencer or style accompaniment? + +0xF0 0x43 0x7E 0x01 t4 t3 t2 t1 0xF7 + +t4: Tempo 4 +t3: Tempo 3 +t2: Tempo 2 +t1: Tempo 4 + + f0 43 7e 01 00 00 00 00 f7 + + + + +Played chord by style accompaniment + +0xF0 0x43 0x7E 0x02 cr ct bn bt 0xF7 + +cr = Chord Root + First Nibble: Flat, Natural or Major + 0x0? - 0x6? = bbb, bb, b, natural, #, ##, ### + + Second Nibble: Root Note + 0x?1 - 0x?7 = C, D, E, F, G, A, B + +ct = Chord Type: 0x00 - 0x22 + Maj, Maj6, Maj7, Maj7(#11), Maj(9), Maj7(9), Maj6(9), aug, + min, min6, min7, min7b5, min(9), min7(9), min7(11), + minMaj7, minMaj7(9), dim, dim7, 7th, 7sus4, 7b5, 7(9), + 7(#11), 7(13), 7(b9), 7(b13), 7(#9), Maj7aug, 7aug, 1+8, + 1+5, sus4, 1+2+5, Chord Cancel + +bn = Bass Note + Same as Chord Root + 0x7F: No Bass Chord + +bt = Bass Type + Same as Chord Type + 0x7F: No Bass Chord + + f0 43 7e 02 31 00 31 00 f7 + + + + +Played chord by style accompaniment + +0xF0 0x43 0x7E 03 note1 note2 note3 ... note10 0xF7 + + f0 43 7e 03 00 00 00 00 00 00 00 00 00 00 f7 + + + + +Overall volume of all parts + +0xF0 0x7F 0x7F 0x04 0x01 ll mm 0xF7 + +ll: Volume LSB (Ignored) +mm: Volume MSB + + f0 7f 7f 04 01 00 7f f7 + + + + +Fine control of standard XG parameters (Manual p. 235ff) + +0xF0 0x43 0x0? 0x4C ah am al dd ... 0xF7 + +0x0?: Device Number (Transmit: 0x00, Receive 0x00 - 0x0F) +ah: Address High +am: Address Mid +al: Address Low +dd: Parameter data (variable length) + + f0 43 00 4c 00 00 00 f7 + + + + +Bulk dump of panel configuration? + +0xF0 0x43 0x1? 0x4C bh bl ah am al dd ... cc 0xF7 + +0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) +bh: Byte Count MSB +bl: Byte Count LSB +ah: Address High +am: Address Mid +al: Address Low +dd: Parameter data (variable length) +cc: Checksum + + f0 43 10 4c 00 00 00 00 00 00 f7 + + + + +Request parameter value + +0xF0 0x43 0x3? 0x4C ah am al 0xF7 + +0x3?: Device Number +ah: Address High +am: Address Mid +al: Address Low + + f0 43 30 4c 00 00 00 f7 + + + + +Request bulk dump + +0xF0 0x43 0x2? 0x4C ah am al 0xF7 + +0x2?: Device Number +ah: Address High +am: Address Mid +al: Address Low + + f0 43 20 4c 00 00 00 f7 + + + + +Internal clock status? (Clavinova compliance) + + f0 43 73 01 02 f7 + + + + +External clock status? (Clavinova compliance) + + f0 43 73 01 03 f7 + + + + +Organ Flute register settings. Use in conjunction with the Organ Flute program change. + +0xF0 0x43 0x73 0x01 0x06 0x0B 0x00 0x00 0x01 0x06 0x0? (21 Data Bytes) Checksum 0xF7 + +0x01: Model ID (Clavinova Common ID) +0x06: Bulk ID +0x0B: Bulk Number (Organ Flutes) +0x00 0x00 0x01 0x06: Data Length = 16 Bytes ?? +0x0?: MIDI Channel + +21 Data Bytes: + 1' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + 1 1/3' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + 1 3/5' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + 2' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + 2 2/3' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + 4' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + 5 1/3' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + 8' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + 16' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + Attack 2': 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + Attack 2 2/3': 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 + Attack Length: 0x00 - 0x07 = ? + Response: 0x00 - 0x07 = ? + Attack Mode: 0x00 = Each, 0x01 = First + Wave Variation: 0x00 = Sine, 0x01 = Tone Wheel + Volume: 0x00 - 0x07 + Aux 4: Always 0x00 + Aux 5: Always 0x00 + Aux 6: Always 0x00 + Aux 7: Always 0x00 + +Checksum: + 0-Sum(Data Bytes) + + f0 43 73 01 06 0B 00 00 01 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f7 + + + + +Special MIDI receive mode with the following channel assignments (when On): + +1 - 10: Melody +15: Rhythm Part +16: Control (incl. SysEx) + + f0 43 73 01 14 f7 + + + + +Special MIDI receive mode with the following channel assignments (when On): + +1 - 10: Melody +15: Rhythm Part +16: Control (incl. SysEx) + + f0 43 73 01 13 f7 + + + + f0 43 73 01 61 f7 + + + + f0 43 73 01 62 f7 + + + + +Enable of disable realtime control of volume, expression and pan for a single channel + +0xF0 0x43 0x73 0x01 0x11 0x0? 0x45 dd 0xF7 + +0x0?: MIDI Channel +dd: 0x00 = Realtime on, 0x7F = Realtime off + + f0 43 73 01 11 00 45 00 f7 + + + + +Master tuning of the whole instrument + +0xF0 0x43 0x1? 0x27 0x30 0x00 0x00 mm ll cc 0xF7 + +0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) +mm: Master Tuning MSB +ll: Master Tuning LSB +cc: Ignored + + f0 43 10 27 30 00 00 00 00 00 f7 + + + + +Some weird flag for the internal sequencer? + +0xF0 0x43 0x73 0x01 0x11 0x0? 0x54 dd 0xF7 + +0x0?: Sequencer Track Number +dd: Ignored + + f0 43 73 01 11 00 54 00 f7 + + + + +0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x00 value 0xF7 + +Value = Pitch to Note switch + 0x00: Off + 0x01: On + + f0 43 73 01 11 00 50 00 00 f7 + + + + +0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x01 value 0xF7 + +Value = Pitch to Note part + 0x00: Right1 + 0x01: Right2 + 0x02: Left + 0x03: Lead + 0x04: Upper + + f0 43 73 01 11 00 50 01 00 f7 + + + + +0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x10 value 0xF7 + +Value = Harmony Part + 0x00: Off + 0x01: Upper + 0x02: Lower + + f0 43 73 01 11 00 50 10 00 f7 + + + + +0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x11 value 0xF7 + +Value = Reverb depth: 0x00 - 0x7F + + f0 43 73 01 11 00 50 11 00 f7 + + + + +0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x12 value 0xF7 + +Value = Chorus depth: 0x00 - 0x7F + + f0 43 73 01 11 00 50 12 00 f7 + + + + +0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x13 value 0xF7 + +Value = VH LED switch + 0x00: LED Off + 0x7F: LEF On + + f0 43 73 01 11 00 50 13 00 f7 + + + diff -Nru muse-2.1.2/share/instruments/Yamaha-CS1x.idf muse-3.0.2+ds1/share/instruments/Yamaha-CS1x.idf --- muse-2.1.2/share/instruments/Yamaha-CS1x.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-CS1x.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + diff -Nru muse-2.1.2/share/instruments/Yamaha-Motif.idf muse-3.0.2+ds1/share/instruments/Yamaha-Motif.idf --- muse-2.1.2/share/instruments/Yamaha-Motif.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-Motif.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + diff -Nru muse-2.1.2/share/instruments/Yamaha-Motif-Rack.idf muse-3.0.2+ds1/share/instruments/Yamaha-Motif-Rack.idf --- muse-2.1.2/share/instruments/Yamaha-Motif-Rack.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-Motif-Rack.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + diff -Nru "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Yamaha-Motif XS.idf" "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Yamaha-Motif XS.idf" --- "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/instruments/Yamaha-Motif XS.idf" 1970-01-01 00:00:00.000000000 +0000 +++ "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/instruments/Yamaha-Motif XS.idf" 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,7854 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 1St + Stick1 + Bd Funk St1-2 + Sd Basic St2-3 + Bd High St + Bd Normal St1-2 + Sd Closed Rim St + Sd Normal St1-4 + Clap T9 + Sd Basic St1-2 + Tom Floor St + HH Closed 1-4St + Tom Low St + HH Pedal 3 St + Tom Low St + HH Open 2St + Tom Mid St + Tom Mid St + Crash2 St + Tom Hi St + Ride1 St + China St + Ride Cup1 St + Tambourine1 Sw + Splash St + Cowbell2 + Crash2 RL + Vibraslap + Ride1 RL + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Blues L1-3 + Sd Tight St1-4 + Sd BluesHeart R1-3 + Sd Hip St1-2 + Bd Low St + Sd LdwHMono + Bd R&&B1 + Sd Heavy + Bd Room1 + Sd Ambience + Bd Funk Son St + Bd Room2 + + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd PowerD 1St + Stick1 + Bd PowerB 1-2St + Sd PowerD 5St + Bd PowerA 1-2St + Bd Jazz1-2St + Sd PowerB SSt + Sd PowerC 1-5St + HandClap St + Sd PowerD 1-5St + Tom Power05 1-2St + HH Power ClTip 1-4St + Tom Power05 1-2St + HH Power ClPedal St + Tom Power04 1-2St + HH Power Open EdgeSt + Tom Power03 1-2St + Tom Power03 1-2St + Crash2 St + Tom Power01 1-2St + Ride1 St + China St + Ride Cup1 St + Tambourine1 Sw + Splash St + Cowbell2 + Crash2 RL + Vibraslap + Ride2 St + Bongo Hi 1Finger 1-2 + Bongo Lo 1Finger 1-2 + Conga Hi SlapMute1-2 + Conga Hi Open 1-2 + Conga Lo Open 1-2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1/3 + Sleigh Bell + Wind Chime + Sd Bld St1-4 + Sd Blues 3St + Sd Hip St1-2 + Sd Funk St1-3 + Sd Soul St1-4 + Sd LdwHMono + Sd Heavy + Sd Tight St1-4 + Sd BluesHeart St1-3 + Bd Snap 1-2 + Bd Tight 1-2 + Bd Funk St1-2 + + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd PowerA 1St + Stick1 + Bd PowerB 1-2St + Sd PowerA 5St + Bd PowerA 1-2St + Bd Jazz1-2St + Sd PowerB SSt + Sd PowerB 1-5St + HandClap St + Sd PowerA 1-5St + Tom Power05 1-2St + HH Power ClEgde1-4St + Tom Power05 1-2St + HH Power ClPedal St + Tom Power04 1-2St + HH Power Open EdgeSt + Tom Power03 1-2St + Tom Power03 1-2St + Crash2 St + Tom Power01 1-2St + Ride1 St + China St + Ride Cup1 St + Tambourine1 Sw + Splash St + Cowbell2 + Crash2 RL + Vibraslap + Ride2 St + Bongo Hi 1Finger 1-2 + Bongo Lo 1Finger 1-2 + Conga Hi SlapMute1-2 + Conga Hi Open 1-2 + Conga Lo Open 1-2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1/3 + Sleigh Bell + Wind Chime + Sd Bld St1-4 + Sd Blues 3St + Sd Hip St1-2 + Sd Funk St1-3 + Sd Soul St1-4 + Sd LdwHMono + Sd Heavy + Sd Tight St1-4 + Sd BluesHeart St1-3 + Bd Snap 1-2 + Bd Tight 1-2 + Bd Funk St1-2 + + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 1St + Stick1 + Bd Funk Son St + Sd Basic St1-3 + Bd Snap 1 + Bd H Son St + Sd Closed Rim St + Sd SonA St1-4 + Clap T9 + Sd Tight St1-4 + Tom Floor St + HH Closed Foot St + Tom Floor St + HH Half Open2 St + Tom Low St + HH Open2 St + Tom Low St + Tom Mid St + Crash2 RL + Tom Hi St + Ride1 St + China St + Ride Cup1 St + Tambourine1 Sw + Splash St + Cowbell2 + Crash2 RL + Vibraslap + Ride1 RL + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Bld St1-4 + Sd Blues St1-3 + Sd BluesHeart R1-3 + Sd Hip St1-2 + Sd Normal St1-4 + Sd LdwHMono + Sd Soul St1-3 + Sd Heavy + Sd Funk 1St + Bd Normal 1St + Bd Funk Son St + Bd Snap 1-2 + + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 1St + Stick1 + Bd Funk St1-2 + Sd Basic St2-3 + Bd High St + Bd Normal St1-2 + Sd Closed Rim St + Sd Normal St1-4 + Clap T9 + Sd Basic St1-2 + Tom Floor St + HH Closed 1-4St + Tom Floor St + HH Pedal 3 St + Tom Low St + HH Open 1-2St + Tom Low St + Tom Mid St + Crash2 RL + Tom Hi St + Ride1 St + China St + Ride Cup1 St + Tambourine1 Sw + Splash St + Cowbell2 + Crash2 RL + Vibraslap + Ride1 RL + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Slp Open + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Bld St1-4 + Sd Blues L1-3 + Sd Hip St1-2 + Sd Funk St1-3 + Sd Soul St2-4 + Sd LdwHMono + Sd Heavy + Sd Tight St1-4 + Sd BluesHeart St1/3 + Bd Snap 1-2 + Bd Tight 1-2 + Bd Funk St1-2 + + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Rock Open1 R + Stick1 + Bd Room1 + Sd Rng St4 + Bd Jazz2St + Bd Room3 + Sd Rock Stick2 St + Sd Strong St1-4 + HipHop clap6 + Sd PowerA 1-5St + Tom Floor St + HH Closed Rock Sw St + Tom Low St + HH Half Open Rock St + Tom Low St + HH Open Rock St + Tom Mid St + Tom Mid St + Crash1 RL + Tom Hi St + Ride2 St + China St + Ride Cup2 St + Tambourine1 Sw + Splash St + Cowbell2 + Crash1 St + Vibraslap + Ride Cup1 RL + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Blues St1/3 + Sd Tight St1-4 + Sd BluesHeart R1-3 + Sd Tight R1-4 + Bd Low St + Sd PowerC 4St + Bd R&&B1 + Sd PowerB 1-5St + Bd Room1 + Sd PowerA 1-5St + Bd Funk Son St + Bd Room2 + + + + + + + + + Analog Click + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd RockRoll St + Castanet + Sd Rock Open2 St + Stick1 + Bd Room3 + Sd Rock Rim1 St + Bd Rock2 St + Bd Rock1 St + Sd Rock Stick1 St + Sd Rock Open1 St + HipHop clap3 + Sd Rock Mute1 St + Tom Rock Low St + HH Closed Rock Sw St + Tom Rock Low St + HH Pedal Rock Sw St + Tom Rock Mid St + HH Open Rock Sw St + Tom Rock Mid St + Tom Rock Hi St + Crash1 RL + Tom Rock Hi St + Ride2 St + China St + Ride Cup2 St + Tambourine RX5 + Splash St + Cowbell1 + Crash1 St + Vibraslap + Ride2 RL + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Rock Flam St + Sd Rock Open2 St + Sd Rock Rim2 St + Sd Rock Mute2 St + Bd Funk 1St + Sd RockRollD St + Bd H Son St + Sd Tight St1-4 + Bd Funk Son St + Sd SonA 3St + Bd Funk R1-2 + Bd H Son St + + + + + + + + + Analog Click + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd RockRoll St + Castanet + Sd Rock Open2 St + Stick1 + Bd Room1 + Sd Rock Rim2 St + Bd Ambience L1-2 + Bd Rock2 St + Sd Rock Stick2 St + Sd Rock Open2 St + HandClap St + Sd Rock Mute2 St + Tom Rock Low St + HH Closed Rock 2St + Tom Rock Low St + HH Half Open Rock St + Tom Rock Mid St + HH Open Rock St + Tom Rock Mid St + Tom Rock Hi St + Crash2 St + Tom Rock Hi St + Ride2 St + China St + Ride Cup2 St + Tambourine RX5 + Splash St + Cowbell1 + Crash2 RL + Vibraslap + Ride2 RL + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Rock Flam St + Sd Rock Open1 St + Sd Rock Rim1 St + Sd Rock Mute1 St + Bd Rock1 St + Sd RockRollD St + Bd Funk 2St + Sd Tight St1-4 + Bd High St + Sd SonA 4St + Bd L Son St + Bd Rock1 St + + + + + + + + + Bd HipHop2 + Cabasa2 + Sd HipHop9 + Scratch Lo R + Scratch Vo F + Sd RockRollD St + Clave + Sd HipHop5 + Sd HipHop Rim3 + Bd HipHop8 + Sd HipHop Rim6 + Bd HipHop9 + Bd HipHop5 + Sd HipHop Rim2 + Sd HipHop11 + HipHop clap2 + Sd HipHop2 + Tom Floor St + HH Closed Hip + Tom Low St + HH Pedal Hip + Tom Mid St + HH Open Hip + Tom Hi St + Tom Hi St + Crash4 + Tom Hi St + Ride2 L + Crash2 St + Ride Mono + Tambourine1 Hit + Hand Cymbal Open St + Mambo Body + China R + Handbell L + Ride Cup Mono2 + Conga Hi Slp Open + Conga Lo Open 1 + Conga Lo Tip + Conga Lo SlapOpen + Conga Lo Open 2 + Bd HipHop4 + HipHop clap3 + Agogo Hi + Agogo Lo + Shaker 2 + Shaker 1 + Bd HipHop10 + Bd HipHop6 + Sd HipHop Rim4 + Sd HipHop6 + HipHop Snap1 + Bd HipHop10 + Sd HipHop7 + HipHop clap1 + Paila L + Triangle Mute + Triangle Open + Tambourine1 Sw + Shaker 2 + Bd HipHop8 + Clap T9 + Sd T8-1 + Sd T8-1 + Sd T8-4 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open T8-2 + Tom T8 + Crash T8 + + + + + + + + + Bd HipHop3 + Hi Q2 + Scratch Hip2 + Scratch Sd R + Scratch Vo R + Sd Break Roll + Sd T9Rim + Sd HipHop2 + Sd T8 Rim + Bd HipHop9 + Sd HipHop Rim1 + Bd HipHop6 + Bd HipHop5 + Sd HipHop Rim2 + Sd HipHop1 + HipHop clap6 + Sd HipHop9 + Tom T9 Lo + HH Closed Hip + Tom T9 Lo + HH Pedal Hip + Tom T9 Hi + HH Open Hip + Tom T9 Hi + Tom T8 + Crash3 + Tom T8 + Ride T9 + Ride T9 + Crash Brush Cup St + Tambourine RX5 + Crash Syn + Cowbell AnCR + Crash T9 + MetalGuiro Long + Ride Mono + Cajon Tip 1-2 + Bongo Lo Tip + Tmbourim Finger + Tumbo 2 + Tumba Heel + Paila H + Paila L + Cowbell T8 + Cowbell AnCR + Shekere Up + Caxixi1 + Grain2 St + Grain2 L + Cajon Tip 1 + HipHop clap7 + Clave + Wood Block + Sd HipHop Rim4 + HipHop Snap1 + HipHop Snap2 + Electric Triangle + Triangle Open + Shaker 1 + MetalGuiro Long + Wind Chime + Sd HipHop Rim4 + Sd T8-1 + Sd T8-2 + Sd T8-4 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open T8-2 + Tom T8 + HipHop flex1 + + + + + + + + + Sd Hip Stick2 + Vox Hit Rev + TurnTable + Hit Brass + Scratch Sd R + Sd LiveRoll R + Clave T8 + Sd Hip 2R + Sd Hip Stick6 R + Bd HipHop9 + Sd Garg L + Bd T8-3 + Bd T9-1 + Sd HipHop Rim3 + Sd HipHop10 + HipHop clap6 + Sd HipHop1 + Tom T8 + HH Closed T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash4 + Tom T8 + Ride Brush1 R + Crash T8 + Ride T9 + Tambourine RX5 + Splash R + Mambo Body + Crash T9 + Vibraslap + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Hip Stick8 + Sd Jungle3 + HandClap R + Sd Hip Rim5 + Scratch H + Scratch L + Scratch Down + Scratch Hi R + Scratch Bd F + Scratch Combi + Scratch Vo R + Bd Break2 + + + + + + + + + Bd T8-3 + Sd T8 Rim + Sd Hip1 + Clap T9 + Sd Syn + Sd LiveRoll R + Sd Hip Stick4 + Sd HipHop2 + Sd HipHop Rim6 + Bd Hip Deep + Sd R&&B1 Rim + Bd Break Heavy + Bd Hip Deep + Sd HipHop Rim2 + Sd Hip3 + HandClap St + Sd Lo-Fi + Tom Floor R + HH Closed Lo-Fi + Tom Low L + HH Closed Break + Tom Low L + HH Open Lo-Fi + Tom Low L + Tom Hi L + Crash3 + Tom Hi L + Ride T9 + China2 + Ride Cup Mono + Tambourine RX5 + Crash3 + Cowbell RX11 + Crash2 R + Cowbell T8 + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Radio Noise + Radio Noise + Agogo Lo + Agogo Lo + Cabasa1 + MaracasT8 + FxGun2 + FxGun1 + Scratch Spin + Scratch Stop + Clave T8 + Door Squeak + Hi Q2 + Scratch Bd F + Scratch Bd R + Triangle Mute + Triangle Open + Analog Shaker + Tambourine1 Hit + Wind Chime + Sd Human2 + Sd Break Roll + Stab Hard + Sd Jungle1 + Bd Rock1 St + Scratch Down + Wataiko + HH Hit + Stab Organ2 + Sonar1 + TurnTable + Police + + + + + + + + + Sd T8 Rim + Vox Hit Rev + TurnTable + Stab Hard + Scratch Vo R + Sd Roll Mono + Clave T8 + Sd Hip 1R + Sd Hip Stick4 + Bd AnCR + Sd Hip Rim3 + Bd T9-3 + Bd Hip Deep + Sd Hip Stick5 + Sd Hip Gate + HipHop clap6 + Sd Lo-Fi + Tom T8 + HH Closed Hip + Tom T8 + HH Pedal Hip + Tom T8 + HH Open Hip + Tom T8 + Tom T8 + Crash4 + Tom T8 + Ride2 R + Crash T8 + Ride T9 + Tambourine RX5 + Splash R + Mambo Body + Crash T9 + Vibraslap + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Shaker Hip1 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Hip Stick1 + Sd Jungle1 + HandClap R + Sd Break Heavy + Scratch L + Scratch Down + Scratch Hi F + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Bd Break Heavy + + + + + + + + + Sd Hip Stick1 + Hi Q1 + TurnTable + Hit Uhh + Scratch Stop + Sd LiveRoll L + Clave T8 + Sd Cut + Sd Rock Stick1 R + Bd T9-2 + Sd Hip Rim1 + Bd R&&B2 + Bd Break Deep + Sd Closed Rim3 + Sd Hip Rim2 + HipHop clap4 + Sd R&&B1 + Conga T8 + HH Closed T8-1 + Conga T8 + HH Closed T8-2 + Conga T8 + HH Open T8-2 + Conga T8 + Conga T8 + Crash4 + Conga T8 + Ride2 R + Crash T8 + Ride T9 + Tambourine RX5 + Splash R + Mambo Body + Crash T9 + Vibraslap + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Closed Rim R + Sd AnSm Rim + HandClap St + Sd Hip3 + Scratch Sd R + Scratch Hip + Scratch Bd R + Scratch Vo R + Scratch Hip2 + Scratch Combi2 + Scratch Combi4 + Bd Hip Deep + + + + + + + + + Sd Hip Rim4 + Hi Q1 + TurnTable + Hit Orchestra1 + Scratch Bd R + Sd LiveRoll R + Analog Click + Sd Hip Rim3 + Sd AnCR Rim + Bd HipHop9 + Sd HipHop Rim6 + Bd T8-3 + Bd T8-2 + Sd T8 Rim + Sd Hip Stick4 + HandClap R + Sd Hip Stick3 + Tom T8 + HH Closed T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash4 + Tom T8 + Ride2 R + Crash T8 + Ride T9 + Tambourine RX5 + Splash R + Mambo Body + Crash T9 + Vibraslap + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd AnSm + Sd Closed Rim3 + Analog Click + Sd Closed Rim2 + Scratch H + Scratch L + Scratch Down + Scratch Hi R + Scratch Bd F + Scratch Combi + Scratch Vo R + Bd Jungle2 + + + + + + + + + Sd Hip Rim3 + Hi Q2 + Scratch H + Scratch H + Scratch Vo R + Sd Roll Mono + Analog Click + Sd Hip Rim4 + Sd T8 Rim + Bd HipHop9 + Sd Hip Stick4 + Bd Hard Long + Bd ElecClick + Sd Hip Stick1 + Sd Hip Stick2 + HipHop clap8 + Sd Hip Stick6 St + Tom T9 Lo + HH Closed T8-1 + Tom T9 Lo + HH Open T8-1 + Tom T9 Lo + HH Open T8-1 + Tom T9 Lo + Tom T9 Lo + Crash T9 + Tom T9 Lo + Ride1 RL + Crash T8 + Ride Cup Mono2 + Electric Tambourine2 + Splash R + Cowbell T8 + Crash T9 + Vibraslap + Crash Syn + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd T8 Rim + Sd Hip Stick1 + Sd Hip Stick2 + Sd Closed Rim2 + Scratch Down + Scratch Hi F + Scratch Lo F + Scratch Lo R + Scratch Bd R + Scratch Combi + Scratch Stop + Bd HipHop8 + + + + + + + + + Bd T9 Hd3 + Sd AnCR + Sd AnSm Rim + Sd ClapAna + Sd T9Gate + Sd Roll Mono + Sd Timbr + Sd FM Rim + Sd JungleFX + Bd T9-4 + Sd Hip Rim5 + Bd T9-3 + Bd Elec2 + Sd T8 Rim + Sd Lo-Fi + HipHop clap2 + Sd R&&B1 + Bd T8-3 + HH Closed T9 + Bd T8-3 + HH Pedal Hip + Bd T8-3 + HH Open T9 + Bd T8-3 + Bd T8-3 + Crash1 RL + Bd T8-3 + Ride2 St + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash1 St + Cowbell T8 + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Hi Q1 + Hi Q1 + Agogo Lo + Agogo Lo + Analog Shaker + Maracas Slur + Vox Drum + Vox Drum + Hi Q1 + Hi Q2 + Clave T8 + Hi Q1 + Scratch Spin + Scratch Hi R + Scratch Hi F + Triangle Mute + Triangle Open + Shaker 2 + Tambourine1 Hit + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udo High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + + Bd T9-1 + Sd AnCR Rim + Sd Hip Gate + Sd Break Roll + Sd T9-2 + Sd LiveRoll St + HipHop Snap2 + Sd HipHop9 + Sd HipHop Rim2 + Bd HipHop5 + Sd Hip Rim4 + Bd T9-1 + Bd T8-2 + Sd HipHop Rim1 + HipHop Snap2 + HipHop clap8 + Sd T8-5 + Tom T8 + HH Closed T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash1 RL + Tom T8 + Ride2 St + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash1 St + Cowbell T8 + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Hi Q1 + Hi Q1 + Agogo Lo + Agogo Lo + Analog Shaker + Maracas Slur + Vox Drum + Vox Drum + Hi Q1 + Hi Q2 + Clave T8 + Hi Q1 + Scratch Spin + Scratch Hi R + Scratch Hi F + Triangle Mute + Triangle Open + Shaker 2 + Tambourine1 Hit + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udo High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + + Bd T9 Hd2 + Sd AnCR + Sd Break2 + Sd Break Roll + Sd T9Gate + Sd Roll Mono + HipHop Snap2 + Sd HipHop2 + Sd HipHop Rim3 + Bd T9-2 + Sd Hip Rim3 + Bd T9-1 + Bd T8-2 + Sd HipHop Rim3 + Sd R&&B1 + HipHop clap6 + Sd Hip Gate + Tom T9 Lo + HH Closed T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + Tom T9 Hi + Crash1 RL + Bd T8-3 + Ride2 St + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash1 St + Cowbell T8 + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Hi Q1 + Hi Q1 + Agogo Lo + Agogo Lo + Analog Shaker + Maracas Slur + Vox Drum + Vox Drum + Hi Q1 + Hi Q2 + Clave T8 + Hi Q1 + Scratch Spin + Scratch Hi R + Scratch Hi F + Triangle Mute + Triangle Open + Shaker 2 + Tambourine1 Hit + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udo High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + + Bd T9-2 + Sd AnSm Rim + Sd Elec9 + Clap AnSm + Sd Elec7 + Sd Elec11 + Sd Elec7 + Sd T8-1 + Sd T9-5 + Bd T9 Hd2 + Sd Elec12 + Bd T9 Hd3 + Bd Hard Long + Sd T9Rim + Sd Elec5 + Clap T9 + Sd T9-2 + Sd Syn + HH Closed T9 + Sd Syn + HH Open T9 + Sd Syn + HH Open T9 + Sd Syn + Sd Syn + Crash T9 + Sd Syn + Ride T9 + Crash Syn + Ride Cup Mono + Tambourine1 Hit + Crash T8 + Cowbell T8 + China St + Cowbell T8 + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Slap + Conga Hi Slp Open + Conga Hi Open 2 + Tom Syn1 + Tom Syn1 + Analog Click + Noise Burst + Cabasa2 + Maracas Slur + Neo Seq Em-3 + Neo Seq2 Em-5 + Electric Perc1 + Shaker Hip1 + Clave T8 + Tambourine RX5 + Cowbell RX11 + Shaker Hip2 + Electric Perc10 + Triangle Mute + Triangle Open + Shaker Hip4 + Zap2 + Bd HipHop8 + Bd T9 Hd1 + Bd ElecComp3 + Bd T9-1 + Bd T9 Hd3 + Bd Hard Long + HH Closed T8-1 + Bd ElecComp2 + HH Open T8-2 + Bd T9 Hd1 + HH Open T8-1 + Bd T9 Hd2 + Bd ElecComp3 + + + + + + + + + LFO Seq + Trance Choir + Tibetan Bowl + Scratch Stop + Fx Female Voice St + Stab Organ2 + Door Squeak + Radio Noise + Door Squeak + Acoustic Bass FX06 + Electric Bass FX 02 + Acoustic Bass FX06 + Bd BlpHd + Bd Blip + Sd Hip Gate + Sd SonA St1-4 + Noise Burst + Tom Dist Lo + HH Closed T9 + Tom Dist Hi + HH Closed 2St + Tom T9 Hi + HH Open T9 + Tom T9 Hi + Tom T9 Hi + China St + Tom T9 Hi + Splash St + Crash T9 + Ride T9 + Pandeiro Tip + Electric Tambourine1 + Hi Q2 + Bd Gate + Hi Q2 + Acoustic Bass FX03 + Acoustic Bass FX01 + Mini Blip6 + Mini Blip1 + Mini Blip2 + Quinto1 Slap + Tabla Bya + Djembe Edge + Tabla Nah + Barasim Tak + Electric Perc8 + Electric Perc13 + Hop FX1 + Motion Beam + Zap2 + Digi Voice Mod + Human Whistle + Digital Wind + Analog Click + HH Closed T8-1 + Sd FM + HH Half Open2 RL + Clave T8 + HH Open T8-1 + Reverse Cymbal + Bd Analog Tight + Bd ElecComp3 + Bd T9 Hd3 + Bd ZapHard + Bd Slimy + Bd Break Hard + Sd BreakRim + Sd Soul St1-3 + Sd Ambience + Sd Rock Mute1 R + Sd Cut + Sd D&&B3 + Sd FX1 + + + + + + + + + Bd T8-1 + Sd T8 Rim + Sd T8-2 + Electric Clap1 + Sd Elec11 + Sd Elec7 + Sd T8-4 + Bd Elec2 + Sd T8-1 + Bd HipHop8 + Sd T8-3 + Bd HipHop9 + Bd HipHop8 + Sd AnSm Rim + Sd HipHop5 + Clap AnSm + Sd HipHop6 + Tom T8 + HH Closed T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash3 + Tom T8 + Ride T9 + Splash St + Ride Cup Mono + Tambourine1 Hit + Crash T8 + Cowbell T8 + China St + Cowbell T8 + Ride1 St + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Conga Hi Slap + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Cabasa2 + Maracas Slur + Fx Female Voice St + Portamento Lead Seq + Electric Perc9 + Shaker Hip1 + Clave T8 + Hi Q1 + Hi Q2 + Shaker Hip2 + Electric Perc15 + Triangle Mute + Triangle Open + Shaker Hip4 + Ripper + Bd Elec4 + Sd Hip1 + Sd Elec9 + Sd Elec6 + Sd HipHop2 + Bd ElecGate + HH Closed T8-2 + Bd ThickSkin + HH Open T8-2 + Bd Break Heavy + HH Open T8-2 + Bd Break Deep + Bd R&&B2 + + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Brush Soft St + Stick1 + Bd Funk St1-2 + Sd Basic 3St + Bd Jazz1-2St + Bd FK Jazz St + Sd Closed Rim4 St + Sd Brush Med St + HandClap St + Sd Brush vari St + Tom Brush floor St + HH Power ClTip 1-4St + Tom Brush Low St + HH Power ClPedal St + Tom Brush Low St + HH Power Open EdgeSt + Tom Brush Mid St + Tom Brush Mid St + Crash Brush1 St + Tom Brush Hi St + Ride Brush1 St + China St + Crash Brush Cup St + Tambourine1 Sw + Splash St + Cowbell2 + Crash Brush2 St + Vibraslap + Ride Brush2 St + Bongo Hi 1Finger 1-2 + Bongo Lo 1Finger 1-2 + Conga Hi SlapMute1-2 + Conga Hi Open 1-2 + Conga Lo Open 1-2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1/3 + Sleigh Bell + Wind Chime + Sd Bld St1-4 + Sd Blues 3St + Sd Hip St1-2 + Sd Funk St1-3 + Sd Soul St1-4 + Sd LdwHMono + Sd Heavy + Sd Tight St1-4 + Sd BluesHeart St1-3 + Bd Snap 1-2 + Bd Tight 1-2 + Bd Funk St1-2 + + + + + + + + + Bd Jazz1-2St + Sd Brush Soft R + Sd Brush SwirAtt St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Hip 1St + Stick1 + Bd Funk 1 L + Sd Tight 3St + Bd FK Jazz St + Bd Funk St1-2 + Sd Closed Rim St + Sd Tight 1St + Sd RockRoll St + Sd BluesHeart St1-3 + Tom Floor St + HH Power ClTip 1-4St + Tom Floor St + HH Power ClPedal St + Tom Low St + HH Power Open EdgeSt + Tom Mid St + Tom Mid St + Crash2 St + Tom Hi St + Ride2 St + China St + Ride Cup Mono2 + Tambourine1 Hit + Splash St + Cowbell1 + Crash2 St + Vibraslap + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Timbale Hi1 + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + Sd Brush Soft R + Sd Brush Swir St + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Timbale Paila H + Timbale Paila L + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + Wind Chime + Sd Blues L1-3 + Sd Funk Ay St + Sd BluesHeart R1-3 + Sd Blues St1-2 + Bd Low L + Sd LdwHMono + Bd Dry2 + Sd Brush SwirAtt St + Bd Hip Deep + Sd Roll Mono + Bd Normal 2R + Crash Brush2 St + + + + + + + + + Clave + Cajon Kick + Cajon Slap + Cajon Tip 1-2 + Clave + Wood Block + HandClap St + Ride2 L + HipHop Snap2 + Castanet + Conga Hi Tip + Conga Hi Heel + Conga Hi Open 1-2 + Conga Hi Mute + Conga Hi Slp Open + Conga Hi Slap + Conga Hi SlapMute1-2 + Conga Lo Tip + Conga Lo Heel + Conga Lo Open 1-2 + Conga Lo Mute + Conga Lo SlapOpen + Conga Lo SlapOpen + Conga Lo Open 1 + Bongo Hi 1Finger 1-2 + Bongo Hi 3Finger 1-2 + Bongo Hi Rim + Bongo Hi Tip + Bongo Hi Heel + Bongo Hi Slap + Bongo Lo 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Bongo Lo Rim + Bongo Lo Tip + Bongo Lo 3Finger 1-2 + Bongo Hi Slap + Timbale Lo1-2 + Quinto1 Slap + Quinto1 Open + Conga Hi Tip + Tumba Open + Paila L + Timbale Hi1-3 + Pandeiro Open + Pandeiro Thump + Pandeiro Tip + Pandeiro Heel + Paila H + Cowbell2 + Cowbell2 + Agogo Hi + Agogo Hi + Guiro Short + Guiro Long + MetalGuiro Short1-2 + MetalGuiro Long + Tambourine1 Sw + Tmbourim Open + Tmbourim Mute + Tmbourim Finger + Maracas Sw + Shaker 1/3 + Cabasa1-2 + Cuica Lo + Cuica Hi + Cowbell1 + Mambo Body + Shekere Down + Shekere Sap + Triangle Mute + Triangle Open + Whistle + Wind Chime + + + + + + + + + Bd T9-2 + Sd AnCR + Sd T9-5 + Clap AnSm + Sd T9Gate + Sd RockRoll St + Sd T9-3 + Sd T9-4 + Sd T9Gate + Bd T9-4 + Sd T9-5 + Bd T9-1 + Bd T9-3 + Sd T9Rim + Sd T9-1 + Clap T9 + Sd T9-2 + Tom T9 Lo + HH Closed T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + China2 + Ride Cup Mono + Tambourine RX5 + Crash3 + Cowbell1 + Crash4 + Cowbell T8 + Ride2 R + Conga T8 + Conga T8 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Analog Click + Conga T8 + Cabasa1-2 + Maracas Slur + FxGun2 + FxGun1 + Scratch H + Scratch Down + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Piccolo + Sd T8-5 + Sd RockRollD St + Sd Brush Med St + Bd BlpHd + Sd Jungle1 + Bd Sustain + Sd D&&B1 + Bd Break2 + Sd Dist + Bd TekPower + Bd Distortion RM + + + + + + + + + Bd T8-1 + Sd AnCR + Sd T8-4 + Sd ClapAna + Sd T8-3 + Tom T8 + Sd T8-5 + Bd T8-3 + Sd T8-4 + Bd T8-2 + Sd T8-3 + T8 Kick Bass + Bd T8-2 + Sd T8 Rim + Sd T8-2 + Clap T9 + Sd T8-1 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash T8 + Tom T8 + Ride T9 + China2 + Ride Cup Mono + Tambourine RX5 + Splash R + Cowbell T8 + Crash4 + Vibraslap + Ride2 R + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Timbale Hi1 + Timbale Lo1 + Glass + Glass + Cabasa1-2 + MaracasT8 + FxGun2 + FxGun1 + Analog Shaker + Analog Shaker + Clave T8 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Hip1 + Sd Hip2 + Sd Hip Gate + Sd Break1 + Bd Blip + Sd FX1 + Bd FxHammer + Sd Hammer + Bd ZapHard + Sd Garg L + Bd TekPower + Bd Slimy + + + + + + + + + Bd Slimy + Sd T9Rim + Sd T8-5 + Clap T9 + Sd T9-3 + Sd Human1 + Sd Hip3 + Sd T9-4 + Sd Hip2 + Bd ThickSkin + Sd T9-2 + Bd Distortion4 + Bd ZapMid + Sd T8 Rim + Sd T8-5 + Clap T9 + Sd Syn + Tom T8 + HH Closed Lo-Fi + Tom T8 + HH Open Lo-Fi + Tom T8 + HH Open Lo-Fi + Tom T8 + Tom T8 + Crash T8 + Tom T8 + Ride T9 + Crash3 + Ride Cup Mono + Tambourine AnCR + Crash2 L + Cowbell T8 + Crash T9 + Cowbell T8 + Crash Syn + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Timbale Hi1-3 + Timbale Lo1-2 + Glass + Glass + Cabasa1 + MaracasT8 + FxGun2 + FxGun1 + Scratch H + Scratch Down + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Bd Elec4 + Bd ElecComp3 + Bd ElecFX2 + Bd ElecClick + Bd ElecPower1 + Bd ElecPower2 + Bd Elec3 + Bd Elec1 + Sd Elec6 + Sd Elec7 + Sd Elec8 + Sd Elec9 + Sd Elec12 + + + + + + + + + Bd T9-4 + Sd T8 Rim + Sd T8-5 + HandClap St + Sd Garg L + Sd LiveRoll St + Sd T9-3 + Sd T8-1 + Sd T9-5 + Bd T9-1 + Sd T9Gate + Bd T9-2 + Bd T9-2 + Sd T9Rim + Sd T9-1 + Clap T9 + Sd T9-2 + Tom T9 Lo + HH Closed T8-1 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + Crash3 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 St + Cowbell T8 + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Timbale Hi1 + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + Vox Drum + Vox Drum + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udo High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + + Bd T9-2 + Sd T8 Rim + Brite Piano + Clap T9 + Sd T9-2 + Sd T9Gate + Sd Human2 + Sd FX1 + Sd Lo-Fi + Bd T9 Hd2 + Sd T9Rim + Bd T9-3 + Bd T9 Hd1 + Sd T9Rim + Sd T9-2 + Noise Burst + Sd Strong L1-4 + Sd Syn + HH Closed T9 + Sd Syn + HH Open T9 + Sd Syn + HH Open T9 + Sd Syn + Sd Syn + Crash T9 + Sd Syn + Ride T9 + Crash Syn + Ride Cup Mono + Tambourine1 Hit + Crash T8 + Cowbell1 + Crash3 + Cowbell T8 + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Heel + Conga Hi Slp Open + Conga Hi Open 2 + Tom Syn1 + Tom Syn1 + Analog Click + Sd Rim SE + Cabasa1 + Maracas Slur + Vox Drum + Vox Drum + Tambourine AnCR + Analog Shaker + Clave T8 + Tambourine RX5 + Cowbell RX11 + Analog Shaker + Scratch Hi F + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Tabla Bya + Tabla Nah + Tabla Mute + Vox Bell + Bd Pop + Udo High + Bd FX Gate + Noise Burst + Bd T9-1 + Sd Break Roll + Crash3 + TurnTable + + + + + + + + + Bd T9-4 + Sd T8 Rim + Sd T8-5 + HandClap St + Sd Garg L + Sd LiveRoll St + Sd T9-3 + Sd T8-1 + Sd T9-5 + Bd T9-1 + Sd T9Gate + Bd T9-2 + Bd T9-3 + Sd T9Rim + Sd T9-1 + Clap T9 + Sd T9-2 + Tom T9 Lo + HH Closed T8-1 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + Crash3 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 St + Cowbell T8 + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Timbale Hi1 + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + Vox Drum + Vox Drum + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udo High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + + Bd FX Gate + Bongo Lo 3Finger 1-2 + Sd GM + HandClap St + Sd Gate2 + Sd RockRoll St + Sd Rock Rim1 St + Sd Piccolo + Sd Rock Flam St + Bd Room2 + Sd Rock Stick1 St + Bd Room3 + Bd Gate + Sd Mid Rim + Sd Gate1 + Clap AnSm + Sd Ambience + Tom Ambience Lo + HH Closed D&&B + Tom Ambience Lo + HH Pedal D&&B + Tom Ambience Lo + HH Open D&&B + Tom Ambience Hi + Tom Ambience Hi + Crash2 St + Tom Ambience Hi + Ride1 St + China St + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 St + Vibraslap + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Bd +HH + Bd +HH + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + FxGun2 + FxGun1 + Scratch H + Scratch Down + Clave + Wood Block + Wood Block + Scratch L + Scratch L + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + Wind Chime + Sd Dist + Sd FM + Sd FM Rim + Sd Clappy + Bd Room3 + Sd Timbr + Bd Rock1 St + Sd Rim SE + Bd Rock2 St + Sd Break Roll + Bd D&&B1 + Bd Room2 + + + + + + + + + Bd Break Deep + Sd Hip 1St + Sd Lo-Fi + Sd Clappy + Sd LdwHMono + Sd RockRollD St + Sd Gate1 + Sd Mid + Sd BreakRim + Bd Break Heavy + Sd Hip Rim4 + Bd Break2 + Bd Break1 + Sd Hip Rim1 + Sd Break3 + Sd Break1 + Sd Break2 + Tom Low St + HH Closed Rock S R + Tom Low St + HH Pedal Rock R + Tom Low St + HH Half Open Rock R + Tom Hi St + Tom Hi St + Crash2 L + Tom Hi St + Ride2 L + China L + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 R + Cowbell RX11 + Ride Mono + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Open 2 + Agogo Lo + Cabasa1 + Maracas Slur + Timbale Hi1 + Timbale Lo1 + Scratch H + Scratch Down + Clave + Wood Block + Wood Block + Scratch L + Scratch L + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + TurnTable + Sd Break Roll + FM Bass5 + Mind Bell + Sd FX1 + Bd Rock2 St + LFO Seq + Ripper + Noise Burst + Police + Fairwave + Swarm + Radio Noise + + + + + + + + + Bd Hip Deep + Bongo Lo 3Finger 1-2 + Sd R&&B2 + Sd Timbr + Sd D&&B3 + Sd Hammer + Glass + Bd D&&B2 + HH Pedal 3 St + Bd Hip Deep + HH Open2 St + Bd Break Hard + Bd D&&B1 + Sd Mid Rim + Sd D&&B1 + Noise Burst + Sd D&&B2 + Tom Ambience Lo + HH Closed D&&B + Tom Ambience Lo + HH Pedal D&&B + Tom Ambience Lo + HH Open D&&B + Tom Ambience Hi + Tom Ambience Hi + Crash2 L + Tom Ambience Hi + Ride1 St + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 St + Vibraslap + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Sd D&&B4 + HH Reverse D&&B + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + FxGun2 + FxGun1 + Guiro Short + Guiro Long + Clave + Electric Perc8 + Electric Perc2 + P50-1 + Electric Perc13 + TurnTable + Triangle Open + Shaker 2 + Sleigh Bell + Wind Chime + Sd Dist + Sd FM + Sd FM Rim + Sd Clappy + Bd D&&B1 + Sd Timbr + Bd Distortion5 + Sd Rim SE + Bd Distortion4 + Sd Hip3 + Bd Slimy + Bd +HH + + + + + + + + + Bd Analog Tight + Sd T8 Rim + Sd Jungle3 + HandClap St + Sd T8-1 + Tom Syn1 + Sd BluesHeart L1-3 + Sd BluesHeart 3R + Sd SonA St1-3 + Bd Funk St1-2 + Sd Basic St1-3 + Bd Blip + Bd T9-4 + Sd T9Rim + Sd FM + Clap T9 + Sd AnCR + Tom T9 Lo + HH Closed Tek + Tom T9 Lo + HH Closed Tek + Tom T9 Lo + HH Open Tek + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + Crash3 + Ride Cup2 L + Tambourine1 Hit + Crash3 + Cowbell1 + Splash St + Cowbell T8 + Ride Mono + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Timbale Hi1 + Agogo Hi + Agogo Lo + Cabasa1 + Maracas Slur + FxGun2 + FxGun1 + Scratch H + Scratch Down + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd T9-5 + Sd T9-5 + Sd T9-5 + Sd T9-5 + Bd Human1 + Sd D&&B1 + Acoustic Bass FX03 + DigiOsc4 + DigiOsc3 + Sd Break1 + Bd Jungle1 + Bd Break Heavy + + + + + + + + + Bd Jungle2 + Sd Mid Rim + Sd Jungle3 + Sd Jungle3 + Sd Piccolo Rim1 + Sd Roll Mono + Sd Wood + Sd R&&B2 + Sd D&&B2 + Bd Jungle2 + Sd JungleFX + Bd T8-1 + Bd Jungle1 + Sd Hip Rim4 + Sd Jungle2 + Sd JungleFX + Sd Jungle3 + Tom Floor L + HH Closed Rock Sw R + Tom Low L + HH Pedal Rock Sw R + Tom Mid R + HH Open Rock Sw R + Tom Mid R + Tom Hi R + Crash2 L + Tom Hi R + Ride2 R + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 St + Vibraslap + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Tabla Nah + Tabla Bya + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + Timbale Hi1 + Timbale Lo1 + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + Wind Chime + HH Reverse D&&B + RainLoop + Scratch H + Scratch L + Bd BlpHd + Sd Syn + IndusTom + Pandeiro Open + Ripper + TelRing + Scratch Lo R + Tape Rewind + + + + + + + + + Bd AnCR + Bd Blip + Sd T8-5 + Clap AnSm + Sd Hip Rim2 + Sd Roll Mono + Sd T8-1 + Sd AnCR + Sd T8-2 + Bd T8-1 + Sd T8-4 + Bd Blip + Bd T8-2 + Sd T8 Rim + Sd T8-3 + Clap T9 + Clap AnSm + Tom Syn2 + HH Closed T8-2 + Tom Syn2 + HH Open T8-1 + Tom Syn2 + HH Open T8-2 + Tom Syn2 + Tom Syn2 + Crash T9 + Tom Syn2 + Ride T9 + Crash3 + Crash Syn + Crash T8 + Crash3 + Sd Rim SE + Crash2 St + Cowbell T8 + Crash Syn + Tom T8 + Tom T8 + Tom T8 + Tom T8 + Tom T8 + Applause + Door Squeak + Heart Beat + Bomb + HH Open Lo-Fi + Shaker 1 + Zap2 + Zap1 + Scratch H + Scratch L + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Hit Orchestra2 + HH Closed FX + HH Open FX + Analog Shaker + Analog Shaker + TurnTable + Sd Break Roll + FM Bass5 + Mind Bell + Sd Break2 + Bd Rock2 St + Radio Noise + Ripper + Noise Burst + Scratch Spin + Bell Choir + Hi-Lo + Radio Noise + + + + + + + + + Bd Human1 + Scratch Vo F + Hit Uhh + Scratch Vo R + Sd JungleFX + Sd RockRoll St + Sd Break1 + Sd AnCR + Sd T9-1 + Bd Sustain + Sd Hip Stick1 + Bd Human2 + Bd Human2 + Sd Wood + Sd Human1 + Sd Garg H + Sd Human2 + Bd Human1 + HH Open Human + Bd Human1 + HH Open Human + Bd Human1 + HH Open Human + Bd Human1 + Bd Human1 + Noise Burst + Bd Human1 + Crash T8 + China2 + Ride Cup Mono + Scratch Sd R + Splash St + Vox Drum + Crash4 + Electric Tambourine2 + Stab Aah + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + FxGun2 + Bomb + Scratch H + Scratch Down + Scratch Lo F + Scratch Lo R + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + TurnTable + Sd Rock Rim1 R + Helicopter + Stab Hard + Sd Gate1 + Bd Rock1 St + Scratch Down + Bd AnSm + Sonar1 + Wataiko + Wataiko + HH Hit + Stab Organ2 + + + + + + + + + Bd Distortion3 + Sd Wood + Sd Gate1 + HandClap St + Sd Gate2 + Sd Roll Mono + Sd ClapAna + Sd Hip3 + Ripper + Bd Distortion RM + Sd T8 Rim + Bd Distortion2 + Bd Distortion1 + Sd Mid Rim + Sd Dist + Noise Burst + Sd FX2 + Tom Dist Lo + HH Closed T9 + Tom Dist Lo + HH Open T9 + Tom Dist Lo + HH Open T9 + Tom Dist Hi + Tom Dist Hi + Crash T9 + Tom Dist Hi + Ride T9 + China2 + Ride Cup Mono + Tambourine AnCR + Crash3 + Cowbell T8 + Crash2 St + Vibraslap + Ride1 St + Sd Timbr + Sd Timbr + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Bd +HH + Bd +HH + Glass + Glass + Cabasa1 + Maracas Slur + FxGun2 + FxGun1 + Scratch H + Scratch Down + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Dist + Sd FM + Sd FM Rim + Sd Clappy + Bd FxHammer + IndusTom + Bd D&&B1 + IndusTom + Bd Room3 + IndusTom + Bd T9-4 + Sd Hammer + + + + + + + + + Analog Click + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 1St + Stick1 + Bd Rock2 R + Sd Tight 1St + Bd Break1 + Bd Rock2 R + Sd Rock Stick2 R + Sd SonA 1L + HandClap L + Sd Piccolo Rim1 + Tom Ambience Lo + HH Closed Rock 2R + Tom Ambience Lo + HH Pedal Rock R + Tom Ambience Lo + HH Open Rock R + Tom Ambience Hi + Tom Ambience Hi + Crash2 L + Tom Ambience Hi + Ride2 R + China L + Ride Cup2 R + Tambourine1 Sw + Splash R + Mambo Bl2 + Crash2 L + Vibraslap + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Scratch Combi2 + Scratch Hi R + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Piccolo + Sd Heavy + Sd ClapAna + Sd Piccolo Rim1 + Bd Room2 + Sd Mid + Bd Room3 + Sd Mid Rim + Bd Gate + Sd Gate1 + Bd FX Gate + Bd D&&B1 + + + + + + + + + Bd Blip + Hi Q2 + Sd Piccolo Rim2 + Hi Q1 + Sd AnCR + Tom Syn1 + HH Closed Syn + Tom Syn1 + HH Closed FX + Tom Syn2 + HH Open Syn + Bd AnCR + Bd T9-1 + Sd Hip Stick3 + Sd Jungle1 + Clap T9 + Sd Jungle1 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-2 + Tom T8 + Tom T8 + Crash Syn + Tom T8 + Ride T9 + Crash3 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell T8 + Crash2 St + Vibraslap + Ride1 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Glass + Glass + Cabasa1 + MaracasT8 + Bird + Engine Room St + Electric Perc14 + Gunshot + Clave T8 + Wood Block + Wood Block + Tambourine AnCR + Cowbell AnCR + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd T8-5 + Sonar1 + Sd T8-5 + Sonar1 + Bubble + Sd T8-5 + Bubble + Sd T8-5 + Eery + Sd T8-5 + FX Chime + FX Chime + + + + + + + + + Bd Hard Long + Bd T9-1 + Bd Slimy + Bd AnaHard + Bd Distortion4 + Bd ElecComp3 + Bd Hip Deep + Bd FxHammer + Bd T9-3 + Bd T8-3 + Bd Sustain + Bd R&&B2 + Bd T9 Hd1 + Bd Slimy + Sd T8-1 + Sd T8 Rim + Sd Clappy + Sd Blues 3St + HH Open 2St + HH Closed T8-2 + HH Open 2St + HH Open T8-2 + HH Open 2St + HH Open T8-2 + Clap AnSm + Crash2 St + Crash T9 + Ride1 St + Ride T9 + Analog Click + Clave T8 + Clap AnSm + Clap T9 + Analog Shaker + Conga T8 + Conga T8 + Sd AnCR + Sd RockRoll St + Agogo Hi + China RL + Hi Q2 + Hi Q1 + Zap2 + Zap1 + Zap2 + Door Squeak + Scratch Combi3 + AcousticGtr FX27 St + Play FX Mix + Scratch Hi F + Brite Piano + Brite Piano + Brite Piano + Resonance X-Fade + Sonar1 + Vox Drum + Vox Drum + Vox Drum + HH Open Syn + HH Closed Syn + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + + + + + + + + + Bd HipHop8 + Bd T9-1 + Bd Blip + Bd Break Hard + Bd ElecFX1 + Bd ElecGate + Bd ElecFX2 + Bd Distortion2 + Bd ThickSkin + Bd ElecComp2 + Bd Elec2 + Bd ElecClick + Bd ElecClick + Sd T9Rim + Sd Elec12 + Clap T9 + Sd Elec8 + Tom T9 Lo + HH Closed T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + Crash T9 + Ride T9 + Electric Tambourine2 + Crash T9 + Electric Cowbell + Crash T9 + Electric Tambourine1 + Ride T9 + Electric Perc5 + Electric Perc4 + Electric Perc12 + Electric Perc6 + Electric Perc1 + Bell FX1 + Bell FX2 + Scratch Attack + Scratch R&&B + Cabasa1-2 + Maracas Slur + Electric Perc14 + Electric Perc15 + Shaker Hip2 + Electric Perc3 + Electric Perc7 + Electric Perc8 + Electric Perc9 + Electric Perc10 + Electric Perc13 + Triangle Mute + Triangle Open + Electric Maracas + Electric Triangle + Electric Perc11 + Sd Elec1 + Sd Elec9 + Sd Elec10 + Sd Elec11 + Sd Elec2 + Sd Elec3 + Sd Elec4 + Sd Elec5 + Sd Elec6 + Sd Elec7 + Electric Clap1 + Electric Clap2 + + + + + + + + + Analog Click + Sd Brush Med L + Sd Brush Swir R + Sd Brush Med L + Sd Brush SwirAtt R + Sd Orch Roll St + Castanet + Sd Orch1 S St + Stick1 + Bd Gran Casa Mute St + Sd Orch1 H L + Bd Gran Casa Mute St + Bd Gran Casa Open St + Sd Closed Rim R + Sd Orch1 H St + HandClap St + Sd Orch2 H St + Timpani Sw St + HH Closed 1-4St + Timpani Sw St + HH Pedal 1-3 R + Timpani Sw St + HH Open 1-2St + Timpani Sw St + Timpani Sw St + Hand Cymbal Open St + Timpani Sw St + Ride1 St + China St + Ride Cup1 St + Tambourine1 Hit + Hand Cymbal Mute St + Cowbell2 + Crash1 St + Vibraslap + Ride2 St + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + + + + + + + + + Bd Hard Long + Bd TekPower + Bd Distortion5 + Bd Distortion3 + Bd Distortion1 + Bd D&&B1 + Bd Blip + Bd AnSm + Bd T8-2 + Bd T8-3 + Bd T9 Hd3 + Bd T9-2 + Bd T9-3 + Sd T9Rim + Sd T9-1 + Clap T9 + Sd T9-4 + Tom T9 Lo + HH Closed T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + Tom T9 Lo + Crash T9 + Conga T8 + Ride T9 + Conga T8 + Analog Click + Clave T8 + MaracasT8 + Tambourine AnCR + Analog Shaker + Cowbell T8 + Cowbell AnCR + Sd T8-1 + Sd T8-2 + Sd T8-3 + Sd AnCR + Sd Jungle1 + Sd D&&B1 + Sd Hip1 + Sd R&&B1 + Sd R&&B2 + Sd Hip1 + Sd Wood + Sd Timbr + HH Closed T8-1 + HH Open T8-1 + HH Closed T8-2 + HH Open T8-2 + HH Pedal 3 St + HH Open 2St + HH Open 2St + HH Closed Lo-Fi + HH Open Lo-Fi + HH Closed Syn + HH Open Syn + Analog Shaker + Tambourine RX5 + Tambourine RX5 + Electric Cowbell + Conga T8 + Electric Triangle + Clave T8 + Analog Shaker + Electric Clap1 + Electric Clap2 + Tambourine AnCR + Cowbell T8 + Analog Click + Cowbell RX11 + + + + + + + + + Bd Normal St1-2 + Sd Rock Stick1 St + Sd Rock Open2 St + Sd RockRoll St + Sd Rock Open1 St + Tom Low St + HH Closed Foot St + Tom Low St + HH Pedal 3 St + Tom Mid St + HH Open 2St + Tom Hi St + Bd Rock2 St + Sd Rock Stick1 St + Sd Rock Open2 St + HandClap St + Sd Rock Mute2 St + Tom Rock Low St + HH Closed Rock Sw St + Tom Rock Low St + HH Pedal Rock Sw St + Tom Rock Mid St + HH Open Rock Sw St + Tom Rock Mid St + Tom Rock Hi St + Crash2 St + Tom Rock Hi St + Ride1 St + China St + Ride Cup1 St + Tambourine1 Sw + Splash St + Cowbell1 + Crash2 St + Shaker 1 + Ride1 St + Bd Rock1 St + Sd Rock Stick2 St + Sd Rock Open1 St + Clap T9 + Sd Rock Mute1 St + Tom Rock Low St + HH Closed Rock Sw St + Tom Rock Low St + HH Pedal Rock St + Tom Rock Mid St + HH Half Open Rock St + Tom Rock Mid St + Tom Rock Hi St + Crash2 St + Tom Rock Hi St + Ride2 L + China St + Ride Cup1 St + Tambourine1 Hit + Splash St + Mambo Bl2 + Crash2 St + Shekere Down + Ride2 R + Bd Funk St1-2 + Sd Rock Stick1 St + Sd Rock Rim1 St + Sd Rock Flam St + Sd Rock Rim2 St + Tom Floor St + HH Closed 4St + Tom Low St + HH Pedal 3 St + Tom Mid St + HH Open2 St + Tom Mid St + Tom Hi St + + + + + + + + + Bd T8-3 + Sd T8 Rim + Sd HipHop11 + Clap T9 + Sd HipHop9 + Bd T8-3 + HH Closed Tek + Bd T8-3 + HH Open Tek + Bd T8-3 + HH Open Tek + Bd T8-3 + Bd Hip Deep + Sd Hip Stick4 + Sd Hip Rim1 + HipHop clap1 + Sd Hip2 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open AnCR + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash T9 + Tom T8 + Crash Syn + Crash Syn + Crash T8 + Tambourine RX5 + Splash R + Cowbell T8 + Crash T9 + Cowbell AnCR + Ride T9 + Bd Hip Deep + Sd Hip Stick5 + Sd Hip Rim2 + HipHop clap2 + Sd Hip1 + Tom T9 Lo + HH Closed RX5 + Tom T9 Lo + HH Closed AnCR + Tom T9 Lo + HH Open T8-2 + Tom T9 Lo + Tom T9 Hi + Crash2 R + Tom T9 Hi + Crash T8 + Crash Syn + Crash T8 + Tambourine1 Hit + China R + Cowbell RX11 + Crash2 L + Cowbell1 + Ride2 R + Bd Hip Deep + Sd HipHop Rim4 + Sd HipHop2 + HipHop clap2 + Sd Hip Rim5 + Tom Dist Lo + HH Closed Hip + Tom Dist Lo + HH Pedal Hip + Tom Dist Hi + HH Open Hip + Tom Dist Hi + Tom Dist Hi + + + + + + + + + Wataiko + Udo High + Djembe Open + Quinto1 Open + Udo Low + Udo Finger + Udo High + Tabla Bya + Tmbourim Open + Tabla Mute + Tabla Nah + Tabla Mute + Timpani Sw St + Hit Uhh + Bongo Hi 1Finger 1-2 + Clap T9 + Triangle Mute + Triangle Open + Castanet + Castanet + Castanet + Guiro Long + Guiro Swp + Guiro Short + Vibraslap + Clave + Clave T8 + Cuica Lo + Cuica Hi + Timbale Lo1-2 + Wood Block + Timbale Lo2 + Wood Block + Timbale Hi1-3 + ChaCha + Timbale Hi2 + Timbale Paila L + Timbale Paila L + Timbale Paila H + Mambo Bl1 + Bongo Lo 3Finger 1-2 + Bongo Lo 3Finger 1-2 + Handbell L + Bongo Hi 1Finger 1-2 + Handbell H + Bongo Hi 1Finger 1-2 + Agogo Lo + Agogo Hi + Conga Hi Heel + Conga Hi Tip + Tumba Slap + Maracas Slur + Tumba Open + Conga Hi Heel + Conga Hi Tip + Conga Hi Slp Open + Shaker 1 + Conga Hi Open 2 + Cabasa1 + Cabasa1-2 + Conga Hi Heel + Conga Hi Tip + Quinto1 Slap + Shaker 1 + Quinto2 Open + Caxixi1-2 + Tambourine1 Shake + Tambourine1 Hit + Tambourine1 Sw + Tambourine1 Hit + Sleigh Bell + Whistle + Whistle + + + + + + + + + Bd L Son St + Sd Rock Stick2 St + Sd Funk St1/3 + Crash2 St + Splash St + Tom Low St + HH Closed 1-4St + Tom Low St + HH Pedal 3 St + Tom Mid St + HH Open 2St + Tom Hi St + Conga Hi Tip + Conga Hi Heel + Tumba Slap + Clave + Tumba Open + Conga Hi Tip + Conga Hi Heel + Conga Hi Slp Open + Handbell L + Conga Hi Open 2 + Handbell H + Mambo Bl1 + Conga Hi Tip + Conga Hi Heel + Quinto1 Slap + ChaCha + Quinto1 Open + Bongo Lo 3Finger 1-2 + Bongo Hi 1Finger 1-2 + Bongo Hi 3Finger 1-2 + Timbale Lo1 + Timbale Paila L + Timbale Hi1 + Timbale Paila H + Wood Block + Wood Block + Shekere Down + Shekere Up + Shekere Sap + Maracas Slur + Maracas Staccato + Guiro Long + Guiro Swp + Guiro Short + Surdo Mute + Surdo Open + Pandeiro Tip + Pandeiro Heel + Pandeiro Thump + Pandeiro Open + Tmbourim Finger + Tmbourim Open + Tmbourim Mute + Agogo Lo Tap + Agogo Lo + Agogo Hi + Triangle Open + Triangle Mute + Shaker 2 + Shaker 1 + Shaker 3 + Cabasa1 + Cabasa2 + Caxixi1 + Caxixi2 + Cuica Lo + Cuica Hi + Whistle + Whistle + Vibraslap + Wind Chime + + + + + + + + + Acoustic Bass FX06 + Acoustic Bass FX06 + Play FX Mix + Power Chord Mono + Power Chord Mono + Acoustic Bass FX01 + AcousticGtr FX02 St + Electric Bass FX 05 + Electric Bass FX 16 + Electric Bass FX 01 + ElectricGtr FX06 + Electric Bass FX 17 + Electric Bass FX 04 + Electric Bass FX 10 + Play FX Mix + Electric Bass FX 11 + Play FX Mix + Electric Bass FX 18 + Electric Bass FX 16 + Electric Bass FX 18 + Electric Bass FX 17 + Electric Bass FX 18 + ElectricGtr FX13 + Electric Bass FX 18 + Electric Bass FX 18 + AcousticGtr FX29 St + Electric Bass FX 18 + Electric Bass FX 11 + AcousticGtr FX29 St + Electric Bass FX 11 + Electric Bass FX 10 + AcousticGtr FX29 St + Play FX Mix + AcousticGtr FX29 St + Electric Bass FX 13 + Electric Bass FX 11 + Electric Bass FX 10 + Electric Bass FX 10 + Electric Bass FX 02 + Electric Bass FX 08 + Electric Bass FX 18 + Acoustic Bass FX09 + Acoustic Bass FX09 + Acoustic Bass FX07 + Acoustic Bass FX07 + Acoustic Bass FX06 + Acoustic Bass FX09 + Play FX Mix + Play FX Mix + Electric Bass FX 22 + AcousticGtr FX06 St + Electric Bass FX 16 + Electric Bass FX 16 + Electric Bass FX 03 + Play FX Mix + Play FX Mix + Electric Bass FX 11 + Electric Bass FX 17 + AcousticGtr FX02 St + Electric Bass FX 14 + ElectricGtr FX21 + ElectricGtr FX20 + ElectricGtr FX19 + Play FX Mix + Play FX Mix + Play FX Mix + Play FX Mix + Electric Bass FX + Electric Bass FX 22 + Electric Bass FX 05 + Electric Bass FX 15 + Electric Bass FX + Electric Bass FX 19 + + + + + + + + + Electric Bass FX 01 + Zap2 + HH Pedal D&&B + HandClap L + Scratch Vo F + IndusTom + Sd Wood + IndusTom + Sd ClapAna + IndusTom + Sd FM Rim + Bd FX Gate + Bd FxHammer + Conga Hi Slp Open + Sd FM + HandClap L + Sd Timbr + Tom Syn2 + HH Closed Syn + Tom Syn2 + HH Open Syn + Tom Syn2 + HH Open Syn + Tom Syn2 + Tom Syn2 + HH Open Mono + Sonar1 + HH Open T8-1 + HH Open Mono + Ride Cup2 L + Sleigh Bell + HH Open T8-2 + Resonance X-Fade + Crash2 L + RainLoop + Ride Cup2 R + AirHose L + AirHose L + Udo High + Mg Bass2 + Mg Bass3 + Acoustic Bass FX06 + Acoustic Bass FX06 + Agogo Lo + Agogo Lo + HH Open Human + Vox Hit Rev + Hi Q1 + Hi Q1 + LFO Seq + LFO Seq + BellAir + HH Hit + HH Hit + Ooh Feedback + Ooh Feedback + Mind Bell + Glass + Swarm + FxGun2 + Lektronica + Play FX Mix + Gunshot + Police + Ripper + Play FX Mix + Tape Rewind + FxGun1 + Bomb + Zap2 + Noise Burst + Stab Giant + Stab Orchestra + + + + + + + + + Clave T8 + Sd Hip Stick6 L + Guiro Long + Sd Hip Stick6 R + Guiro Long + AcousticGtr FX13 St + Castanet + Electric Bass FX 11 + Stick1 + Electric Bass FX 08 + Electric Bass FX 07 + Tom T9 Hi + Electric Bass FX 17 + Sd Closed Rim3 + Stick2 + Guiro Long + Stick2 + Surdo Open + Sd AnSm Rim + Surdo Open + Clap AnSm + Surdo Open + Vibraslap + Surdo Open + Surdo Open + Seashore + Surdo Open + Sd Rock Stick2 St + Applause + Sd Rock Stick1 L + Tambourine1 Shake + Seashore + Wood Block + Applause + Helicopter + Sd Rock Stick2 R + Conga Hi Slp Open + Conga Hi Slp Open + Castanet + Castanet + Wood Block + Timbale Paila L + Timbale Paila L + Stick1 + Stick2 + Caxixi1-2 + Caxixi2 + Door Squeak + Door Squeak + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Door Squeak + Door Squeak + Wood Block + Clave + Door Squeak + HandClap L + ChaCha + Sd Rock Open2 St + Bd Jungle3 + Quinto1 Slap + Bd Room2 + Bd H Son St + Sd Heavy + Surdo Open + Sd Rock Stick1 St + Bd Slimy + Sd Gate1 + Bd Funk Son St + Bd Gate + + + + + + + + + Cowbell T8 + HH Closed 4R + Ride Cup2 R + HH Open Syn + Ride Cup2 L + Ride Cup1 St + FM Metal2 + Ride1 St + Electric Perc7 + Cowbell2 + HH Closed 1St + ChaCha + Cowbell2 + HH Closed Syn + HH Open Tek + HH Closed T8-2 + HH Closed RX5 + Cowbell2 + HH Open T8-1 + Cowbell2 + HH Closed Syn + Cowbell2 + HH Open Syn + Cowbell2 + Cowbell2 + HH Open FX + Cowbell2 + Ride Cup1 St + HH Open AnCR + Ride Cup1 St + Tambourine1 Sw + HH Open FX + Mambo Bl2 + HH Open FX + Sleigh Bell + Ride Mono + Brite Piano + Brite Piano + Tubular Bell + Bell + Brass Bell + Timbale Paila H + Timbale Paila L + Bell + Bell + HH Closed 1St + HH Open AnCR + BellAir + FX Chime + HH Open Rock L + Lektronica + Agogo Lo + Cowbell1 + Cowbell2 + Hi-Lo + Hi-Lo + Triangle Mute + Triangle Open + FX Chime + Sleigh Bell + Wind Chime + Small Bell + Small Bell + Brass Bell + Hibashi + Brite Piano + Sd LdwHMono + Brite Piano + Sd BreakRim + Gamelan2 + Udo Finger + Finger Cymbal + Bell + + + + + + + + + Tabla Mute + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + AcousticGtr FX29 St + Stick2 + Pandeiro Thump + Tabla Mute + Electric Bass FX 16 + AcousticGtr FX17 St + Tabla Combi + Wataiko + Quinto1 Slap + Sd Brush Med St + HandClap St + Sd Funk Ay St + Udo Low + Tabla Mute + Udo Low + Tabla Bya + Udo High + Tabla Nah + Udo High + Udo Finger + Ride2 R + Udo Finger + Ride Cup1 St + Ride Cup Mono2 + Ride Cup1 St + Tambourine1 Hit + Ride Cup1 St + Cowbell1 + Ride Mono + Vibraslap + Ride2 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Tmbourim Finger + Tmbourim Finger + HandClap L + HandClap L + Guiro Long + Guiro Long + Udo Finger + Tabla Bya + Conga Hi Tip + Udo High + Udo High + Udo Finger + Finger Cymbal + Tabla Mute + Sleigh Bell + Wind Chime + Conga Hi Slp Open + Tumba Tip + Tumba Tip + Electric Bass FX 08 + Tumba Tip + Electric Bass FX 01 + Tumba Open + Electric Bass FX 06 + AcousticGtr FX04 St + Udo Low + Djembe Combi + Tumba Slap + + + + + + + + + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Hi R + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Scratch Sd F + Scratch Sd R + Scratch Vo F + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Hi R + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Scratch Sd F + Scratch Sd R + Scratch Vo F + Scratch Vo R + Scratch Stop + Scratch Spin + Scratch Hip + Scratch Hip2 + Scratch Combi + Scratch Combi2 + Scratch Combi3 + Scratch Combi4 + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Hi R + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Scratch Sd F + Scratch Sd R + Scratch Vo F + Scratch Vo R + Scratch Stop + Scratch Spin + Scratch Hip + Scratch Hip2 + Scratch Combi + Scratch Combi2 + Scratch Combi3 + Scratch Combi4 + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Hi R + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Scratch Sd F + Scratch Sd R + Scratch Vo F + Scratch Vo R + Scratch Stop + Scratch Spin + Scratch Hip + Scratch Hip2 + Scratch Combi + Scratch Combi2 + + + + + + + + + Sd Normal 1St + Sd Normal 2St + Sd Normal 3St + Sd Normal 4St + Sd Funk 1St + Sd Funk 2St + Sd Funk 3St + Sd Funk Ay St + Sd Basic 1St + Sd Basic 2St + Sd Basic 3St + Sd SonA 1St + Sd SonA 2St + Sd SonA 3St + Sd SonA 4St + Sd Blues 1St + Sd Blues 2St + Sd Blues 3St + Sd BluesHeart 1St + Sd BluesHeart 2St + Sd BluesHeart 3St + Sd Tight 1St + Sd Tight 2St + Sd Tight 3St + Sd Tight 4St + Sd Hip 1St + Sd Hip 2St + Sd Rock Open1 St + Sd Rock Mute1 St + Sd Rock Rim1 St + Sd Rock Open2 St + Sd Rock Mute2 St + Sd Rock Rim2 St + Sd Rock Flam St + Sd Rock Open1 St + Sd Rock Mute1 St + Sd Rock Rim1 St + Sd Rock Open2 St + Sd Rock Mute2 St + Sd Rock Rim2 St + Sd Rock Flam St + Sd Normal 3St + Sd Funk Ay St + Sd Basic 3St + Sd SonA 3St + Sd BluesHeart 3St + Sd Tight 3St + Sd Tight 3R + Sd Bld St1-4 + Sd Soul St1-4 + Sd Strong St1-4 + Sd Rng St1-4 + Sd Mid Rim + Sd Ambience + Sd Gate1 + Sd LdwHMono + Sd BreakRim + Sd Dry + Sd Heavy + Sd GM + Sd PowerB 1-5St + Sd PowerC 1-5St + Sd PowerD 1-4St + Sd GM + Sd Brush Soft St + Sd Brush Med St + Sd Brush vari St + Sd Brush SwirAtt St + Sd Orch1 S St + Sd Orch2 SwSt + Sd Orch Roll St + Sd Orch1 H St + Sd Brush Med St + + + + + + + + + Sd Normal St1-4 + Sd Normal St1-3 + Sd Normal St2-4 + Sd Normal St1-2 + Sd Normal St2-3 + Sd Normal St3-4 + Sd Normal 1St + Sd Normal 2St + Sd Normal 3St + Sd Normal 4St + Sd Funk St1-3 + Sd Funk St1-2 + Sd Funk St2-3 + Sd Funk St1/3 + Sd Funk 1St + Sd Funk 2St + Sd Funk 3St + Sd Funk Ay St + Sd Basic St1-3 + Sd Basic St1-2 + Sd Basic St2-3 + Sd Basic St1/3 + Sd Basic 1St + Sd Basic 2St + Sd Basic 3St + Sd SonA St1-4 + Sd SonA St1-3 + Sd SonA St2-4 + Sd SonA St1-2 + Sd SonA St2-3 + Sd SonA St3-4 + Sd SonA 1St + Sd SonA 2St + Sd SonA 3St + Sd SonA 4St + Sd Blues St1-3 + Sd Blues St1-2 + Sd Blues St2-3 + Sd Blues St1/3 + Sd Blues 1St + Sd Blues 2St + Sd Blues 3St + Sd BluesHeart St1-3 + Sd BluesHeart St1-2 + Sd BluesHeart St2-3 + Sd BluesHeart St1/3 + Sd BluesHeart 1St + Sd BluesHeart 2St + Sd BluesHeart 3St + Sd Tight St1-4 + Sd Tight St1-3 + Sd Tight St2-4 + Sd Tight St1-2 + Sd Tight St2-3 + Sd Tight St3-4 + Sd Tight 1St + Sd Tight 2St + Sd Tight 3St + Sd Tight 4St + Sd PowerA 1St + Sd PowerA 2St + Sd PowerA 3St + Sd PowerA 4St + Sd PowerA 5St + Sd PowerB 1St + Sd PowerB 2St + Sd PowerB 3St + Sd PowerB 4St + Sd PowerB 5St + Sd PowerC 1-5St + Sd PowerD 1-4St + Sd Blues St1-3 + Sd BluesHeart St1-3 + + + + + + + + + Bd Normal St1-2 + Bd Snap 1-2 + Bd Tight 1 + Bd Normal L1-2 + Bd Funk St1-2 + Bd Ambience 1-2 + Bd Funk 2St + Bd High St + Bd Low St + Bd H Son St + Bd L Son St + Bd Funk Son St + Bd Rock1 St + Bd Rock2 St + Bd Dry1 + Bd Dry2 + Bd Pop + Bd Var1 + Bd Var2 + Bd Room1 + Bd Room2 + Bd Room3 + Bd Gate + Bd FX Gate + Bd PowerA 1-2St + Bd PowerB 1-2St + Bd FK Jazz St + Bd T9-4 + Bd T9 Hd1 + Bd T9 Hd2 + Bd T9 Hd3 + Bd T8-1 + Bd T8-2 + Bd T8-3 + Bd AnSm + Bd AnCR + Bd Human1 + Bd Human2 + Bd Blip + Bd BlpHd + Bd Jungle1 + Bd Jungle2 + Bd Jungle3 + Bd D&&B1 + Bd D&&B2 + Bd R&&B1 + Bd R&&B2 + Bd Hip Deep + Bd Break Deep + Bd Break1 + Bd Break2 + Bd Break Heavy + Bd Break Hard + Bd Distortion1 + Bd Distortion2 + Bd Distortion3 + Bd Distortion4 + Bd Distortion5 + Bd Distortion RM + Bd +HH + Bd FxHammer + Bd ZapHard + Bd ZapMid + Bd Sustain + Bd TekPower + Bd Hard Long + Bd Analog Tight + Bd AnaHard + Bd Lo-Fi + Bd ThickSkin + Bd Slimy + Bd HipHop8 + Bd HipHop10 + + + + + + + + + Bd T9-1 + Bd T9-1 + Bd T9-2 + Bd T9-3 + Bd T9-3 + Bd T9-4 + Bd T9 Hd1 + Bd T9 Hd2 + Bd T9 Hd3 + Bd T9 Hd3 + Bd Hard Long + Bd AnCR + Bd T9-1 + Bd T9-2 + Bd T9-3 + Bd T9-4 + Bd T9 Hd1 + Bd T9 Hd2 + Bd T9 Hd3 + Bd T8-1 + Bd T8-2 + Bd T8-3 + Bd Elec1 + Bd AnCR + Bd Human1 + Bd Elec1 + Bd Blip + Bd BlpHd + Bd Elec3 + Bd ElecFX1 + Bd ElecFX2 + Bd ElecGate + Bd ElecClick + Bd ElecPower1 + Bd R&&B2 + Bd Hip Deep + Bd Break Deep + Bd Break1 + Bd ElecPower2 + Bd Break Heavy + Bd Break Hard + Bd Distortion1 + Bd Distortion2 + Bd Distortion3 + Bd Distortion4 + Bd Distortion5 + Bd Elec2 + Bd ElecComp1 + Bd FxHammer + Bd ZapHard + Bd ZapMid + Bd Sustain + Bd TekPower + Bd Hard Long + Bd Analog Tight + Bd AnaHard + Bd ElecComp2 + Bd ElecComp3 + Bd Slimy + Bd Gate + Bd FX Gate + Bd HipHop1 + Bd HipHop2 + Bd HipHop3 + Bd HipHop4 + Bd HipHop5 + Bd HipHop6 + Bd HipHop7 + Bd HipHop8 + Bd HipHop9 + Bd HipHop10 + Bd Hard Long + Bd Analog Tight + + + + + + + + + Wind Chime + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 1St + Stick1 + Bd T9-2 + Sd Basic St1-3 + Bd High St + Bd Normal St1-2 + Sd Funk St1-3 + Sd Bld St1-4 + HandClap St + Sd Soul St1-4 + Tom Rock Low St + HH Closed 1-4St + Tom Rock Mid St + HH Pedal 1-3St + Tom Floor St + HH Open 1-2St + Tom Low St + Tom Mid St + Crash2 St + Tom Hi St + HH Open 1-2St + Reverse Cymbal + Timbale Lo1-2 + Conga Hi Open 2 + Timbale Hi1-3 + Conga Hi Slp Open + Tambourine1 Sw + Tumba Open + Cowbell1 + Clave + Bongo Hi 1Finger 1-2 + Wood Block + Bongo Lo 3Finger 1-2 + Wood Block + Dohol Low + Cabasa1-2 + Dohol High + Shaker 1 + Castanet + Maracas Sw + Castanet + Twaisat3 + Twaisat + Twaisat2 + Twaisat4 + Twaisat4 + Small Tabla Dom + Barasim Dom + Small Tabla Snap + Barasim Sak + Small Tabla Sak + Barasim Tak + Small Tabla Tak + Big Tabla Dom + Segal Dom + Big Tabla Tak + Segal Tak + Big Tabla Sak + Tabla Dom + Tabla Tak1 + Tabla Tremoro + Tabla Tak2 + Tabla Tik1 + Tabla Sak1 + Tabla Tik3 + Tabla Tik2 + + + + + + + + + Tabla Tik2 + Wataiko + Djembe Edge + Djembe Mute + Djembe Open + Djembe Combi + Tabla Bya + Tabla Nah + Tabla Mute + Tabla Combi + Small Tabla Dom + Small Tabla Dom + Bd Hard Long + Bd T9-1 + Sd T9-2 + Electric Clap1 + Sd Strong L1-4 + Tom Dist Lo + HH Closed T9 + Tom Dist Hi + HH Closed T8-1 + Tom Syn1 + HH Open T9 + Tom Syn2 + Sd Hip1 + Crash4 + Sd D&&B4 + HH Closed Break + Reverse Cymbal + Timbale Lo1-2 + Conga Hi Open 2 + Timbale Hi1-3 + Conga Hi Slp Open + Tambourine1 Sw + Tumba Open + Cowbell1 + Clave + Bongo Hi 1Finger 1-2 + Wood Block + Bongo Lo 3Finger 1-2 + Wood Block + Dohol Low + Cabasa1-2 + Dohol High + Shaker 1 + Castanet + Maracas Sw + Castanet + Twaisat3 + Twaisat + Twaisat2 + Twaisat4 + Twaisat4 + Small Tabla Dom + Barasim Dom + Small Tabla Snap + Barasim Sak + Small Tabla Sak + Barasim Tak + Small Tabla Tak + Big Tabla Dom + Segal Dom + Big Tabla Tak + Segal Tak + Big Tabla Sak + Tabla Dom + Tabla Tak1 + Tabla Tremoro + Tabla Tak2 + Tabla Tik1 + Tabla Sak1 + Tabla Tik3 + Tabla Tik2 + + + + + + + + + Sd FX1 + Sd FX2 + Sd FX3 + Sd FX4 + Sd Dist + Sd Cut + Sd Garg H + Sd Garg L + Sd FM + Sd Syn + Sd Hammer + Sd Lo-Fi + Sd T9-1 + Sd T9-2 + Sd T9-3 + Sd T9-4 + Sd T9-5 + Sd T9Gate + Sd T8-1 + Sd T8-2 + Sd T8-3 + Sd T8-4 + Sd T8-5 + Sd AnSm + Sd AnCR + Sd Human1 + Sd Jungle1 + Sd Jungle2 + Sd Jungle3 + Sd JungleFX + Sd D&&B1 + Sd D&&B2 + Sd D&&B3 + Sd D&&B4 + Sd R&&B1 + Sd R&&B1 Rim + Sd R&&B2 + Sd Hip1 + Sd Hip2 + Sd Hip3 + Sd Hip Gate + Sd Hip Rim1 + Sd Hip Rim2 + Sd Hip Rim3 + Sd Hip Rim4 + Sd Hip Rim5 + Sd Break2 + Sd Break Heavy + Sd Elec1 + Sd Elec2 + Sd Elec3 + Sd Elec4 + Sd Elec5 + Sd Elec6 + Sd Elec7 + Sd Elec8 + Sd Elec9 + Sd Elec10 + Sd Elec11 + Sd Elec12 + Sd Elec1 + Sd T8-2 + Sd Elec3 + Sd Elec4 + Sd Elec5 + Sd Elec6 + Sd Elec7 + Sd Elec1 + Sd Elec8 + Sd Elec10 + Sd Elec11 + Sd Elec12 + Sd T9-3 + + + + + + + + + Mini Blip2 + Mini Blip1 + Mini Blip3 + Mini Blip4 + Mini Blip5 + Mini Blip6 + Bell FX1 + Bell FX2 + Electric Perc4 + Electric Perc6 + Electric Perc15 + Electric Perc5 + Electric Perc2 + Electric Perc3 + Electric Perc7 + Electric Perc8 + Electric Perc9 + Electric Perc10 + Electric Perc14 + Electric Perc13 + Neo Seq Em-3 + Spectrum1 + Spectrum3 + Spectrum2 + Whisper Seq Bb + Digi Voice Mod + Moving Chime St + Engine Room St + Raspberry + DigiHeli + Hop FX1 + Willow St + Anemone St + Magnitude St + Radio Noise + Quiek + Stab Giant + HH Hit + Stab Hard + Stab Organ2 + Stab Aah + Hit Uhh + Hit Orchestra2 + Hit Orchestra1 + Hit Brass + Orchestra Hit1 St + Orchestra Hit2 St + Brass Sect Fall + Play FX Mix + Play FX Mix + ElectricGtr FX23 + Acoustic Bass FX04 + Acoustic Bass FX05 + Acoustic Bass FX01 + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Stop + Tape Rewind + Ripper + Noise Burst + Zap1 + Zap2 + Hi Q1 + Hi Q2 + Bomb + FxGun1 + Motion Beam + Birch St + Tri Wave + FM Bass2 + FM Metal2 + + + + + + + + + Bd Elec2 + Scratch Stop + Sd Brush Swir L + Sd Brush Med R + Sd Brush Swir St + Sd Roll Mono + FM Bass6 + Seashore + Electric Perc4 + Bd ElecComp3 + Sd Strong R2 + Bd Elec2 + Bd Slimy + Timbale Paila H + Sd Piccolo + Clap AnSm + Sd Elec10 + Mg Bass2 + Hi Q2 + Mg Bass2 + Electric Perc7 + Tom T8 + HH Open T8-2 + Tom T8 + Tom T8 + Crash3 + Tom T8 + Ride Cup Mono + Ride Cup Mono + Ride Cup Mono + Scratch Sd F + Ride Cup1 RL + Electric Perc7 + Crash3 + Hi-Lo + China2 + Ring Mod + Electric Perc8 + Ring Mod + Electric Perc7 + Electric Perc11 + Paila H + Paila H + Hop FX1 + Hop FX2 + Scratch H + Scratch Hi F + Zap2 + LFO Seq + LFO Seq + Mini Bass3a + Quiek + Digi Breath + DigiHeli + Electric Perc7 + FM Organ1 + Tibetan Bowl + Tibetan Bowl + Segal Dom + Engine Room R + Electric Perc8 + Steel Slide St + Mini Blip2 + Spectrum2 + Mini Blip5 + Noise + Bd D&&B1 + Spectrum2 + SabreTone + Electric Perc14 + Electric Perc8 + DigiHeli + Wind Chime + + + + + + + + + Fat Saw Mn + Scratch Attack + Spectrum2 + 3o3 Saw2 + TurnTable + Sd Roll Mono + FM Bass6 + Tape Rewind + Electric Perc5 + Bd ElecComp2 + Sd Normal St1-4 + Bd Slimy + Bd Elec2 + Mini Bass3a + Sd BluesHeart St1-3 + 3o3 Square + Sd SonA 3L + Tom Dist Hi + DigiHeli + Tom Dist Hi + Electric Perc2 + Tom Dist Hi + HH Open T8-2 + Tom Dist Hi + Tom Dist Hi + Crash3 + Tom Dist Hi + Ride Cup1 R + Ride2 RL + Ride Cup Mono2 + Scratch Hi F + Splash L + Mini Bass1 + Crash1 St + Engine Room St + Ride Cup1 RL + Sync LFO + P5 SawDown 0 dg + OB Strings + Sd Normal St1-3 + Electric Perc11 + PWM Slow + LFO Seq + BigSyn + BigSyn + Hit Orchestra2 + Small Tabla Dom + Digi 03 + Digi 03 + Digi 01 + Digi 01 + Sonar1 + Stab Hard + Engine Room St + Chatter + Hoo + Mondira + Mondira + Rotor + Raspberry + DigiHeli + 3o3 Square + Hop FX2 + DigiBell3 + Vox Hit Rev + Hi Q1 + Tape Rewind + FxGun2 + FxGun1 + Thunder + Police + Bomb + Bomb + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Switch General Midi mode on: Ignored by Motif XS + f0 7e 7f 09 01 f7 + + + + +Request Device Number Code from the instrument. The instrument answers +with a "Identity Reply" and returns the Device ID 0x35 0x06 for XS6, +0x36 x06 for XS7 or 0x37 x06 for XS8. + +0xF0 0x7E 0x0? 0x06 0x01 0xF7 + +0x0?: Device number (any number) + + f0 7e 00 06 01 f7 + + + + +Reply message to an "Identity Request" + +0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 + +dd dd: Device Number + 0x35 x06 = Motif XS6 + 0x36 x06 = Motif XS7 + 0x37 x06 = Motif XS8 + + F0 7E 7F 06 02 43 00 41 35 06 00 00 00 7F F7 + + + + +Reply message to an "Identity Request" + +0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 + +dd dd: Device Number + 0x35 x06 = Motif XS6 + 0x36 x06 = Motif XS7 + 0x37 x06 = Motif XS8 + + F0 7E 7F 06 02 43 00 41 36 06 00 00 00 7F F7 + + + + +Reply message to an "Identity Request" + +0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 + +dd dd: Device Number + 0x35 x06 = Motif XS6 + 0x36 x06 = Motif XS7 + 0x37 x06 = Motif XS8 + + F0 7E 7F 06 02 43 00 41 37 06 00 00 00 7F F7 + + + + +Overall volume of all parts + +0xF0 0x7F 0x7F 0x04 0x01 ll mm 0xF7 + +ll: Volume LSB (Ignored) +mm: Volume MSB + + f0 7f 7f 04 01 00 7f f7 + + + + +Fine control of all voice or performance parameters (Data List p. 58ff) + +0xF0 0x43 0x1? 0x7F 0x03 ah am al dd ... 0xF7 + +0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) +ah: Address High +am: Address Mid +al: Address Low +dd: Parameter data (variable length) + + f0 43 10 7f 03 00 00 00 00 f7 + + + + +Bulk dump of all settings (Data List p. 57ff) + +0xF0 0x43 0x0? 0x7F 0x03 bh bl ah am al dd ... cc 0xF7 + +0x0?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) +bh: Byte Count MSB +bl: Byte Count LSB +ah: Address High +am: Address Mid +al: Address Low +dd: Parameter data (variable length) +cc: Checksum + + f0 43 00 7f 03 00 00 00 00 00 00 f7 + + + + +Request bulk dump (Data List p. 57ff) + +0xF0 0x43 0x2? 0x7F 0x03 ah am al 0xF7 + +0x2?: Device Number +ah: Address High +am: Address Mid +al: Address Low + + f0 43 20 7f 03 00 00 00 f7 + + + + +Request parameter value (Data List p. 56) + +0xF0 0x43 0x3? 0x7F 0x03 ah am al 0xF7 + +0x3?: Device Number +ah: Address High +am: Address Mid +al: Address Low + + f0 43 30 7f 03 00 00 00 f7 + + + diff -Nru muse-2.1.2/share/instruments/Yamaha-MX49_MX61.idf muse-3.0.2+ds1/share/instruments/Yamaha-MX49_MX61.idf --- muse-2.1.2/share/instruments/Yamaha-MX49_MX61.idf 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-MX49_MX61.idf 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,6675 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd PowerD 1St + Stick1 + Bd PowerB 2L + Sd PowerD 5St + Bd PowerA 2L + Bd Jazz2L + Sd PowerB SL + Sd PowerC 1-5St + HandClap L + Sd PowerD 1-5St + Tom Power05 2L + HH Power ClTip 1-4R + Tom Power05 2L + HH Power ClPedal R + Tom Power04 2L + HH Power Open EdgeR + Tom Power03 2R + Tom Power03 2R + Crash2 L + Tom Power01 2R + Ride1 L + China L + Ride Cup1 L + Tambourine1 Sw + Splash R + Cowbell2 + Crash2 L + Vibraslap + Ride2 L + Bongo Hi 1Finger 1-2 + Bongo Lo 1Finger 1-2 + Conga Hi SlapMute1-2 + Conga Hi Open 1-2 + Conga Lo Open 1-2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1/3 + Sleigh Bell + Wind Chime + Sd Bld St3-4 + Sd Blues 3St + Sd Hip St1-2 + Sd Funk St2-3 + Sd Soul St3-4 + Sd LdwHMono + Sd Heavy + Sd Tight St3-4 + Sd BluesHeart St2-3 + Bd Snap R2 + Bd Tight R2 + Bd Funk 2 R + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd PowerA 1St + Stick1 + Bd PowerB 2L + Sd PowerA 5St + Bd PowerA 2L + Bd Jazz2L + Sd PowerB SL + Sd PowerB 1-5St + HandClap L + Sd PowerA 1-5St + Tom Power05 2L + HH Power ClEgde1-4R + Tom Power05 2L + HH Power ClPedal R + Tom Power04 2L + HH Power Open EdgeR + Tom Power03 2R + Tom Power03 2R + Crash2 L + Tom Power01 2R + Ride1 L + China L + Ride Cup1 L + Tambourine1 Sw + Splash R + Cowbell2 + Crash2 L + Vibraslap + Ride2 L + Bongo Hi 1Finger 1-2 + Bongo Lo 1Finger 1-2 + Conga Hi SlapMute1-2 + Conga Hi Open 1-2 + Conga Lo Open 1-2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1/3 + Sleigh Bell + Wind Chime + Sd Bld St3-4 + Sd Blues 3St + Sd Hip St1-2 + Sd Funk St2-3 + Sd Soul St3-4 + Sd LdwHMono + Sd Heavy + Sd Tight St3-4 + Sd BluesHeart St2-3 + Bd Snap R2 + Bd Tight R2 + Bd Funk 2 R + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 3St + Stick1 + Bd Funk Son L + Sd Basic St2-3 + Bd Snap R2 + Bd H Son L + Sd Closed Rim St + Sd SonA St3-4 + Clap T9 + Sd Tight St3-4 + Tom Floor L + HH Closed Foot R + Tom Floor L + HH Half Open2 R + Tom Low L + HH Open2 R + Tom Low L + Tom Mid R + Crash2 L + Tom Hi R + Ride1 L + China L + Ride Cup1 L + Tambourine1 Sw + Splash R + Cowbell2 + Crash2 L + Vibraslap + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Bld St3-4 + Sd Blues St2-3 + Sd BluesHeart 3R + Sd Hip St1-2 + Sd Normal St3-4 + Sd LdwHMono + Sd Soul St3 + Sd Heavy + Sd Funk 2St + Bd Normal 2L + Bd Funk Son L + Bd Snap R2 + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 3St + Stick1 + Bd Funk 2 R + Sd Basic St2-3 + Bd High L + Bd Normal 2L + Sd Closed Rim St + Sd Normal St3-4 + Clap T9 + Sd Basic 2St + Tom Floor L + HH Closed 1-4R + Tom Floor L + HH Pedal 3 R + Tom Low L + HH Open 2R + Tom Low L + Tom Mid R + Crash2 L + Tom Hi R + Ride1 L + China L + Ride Cup1 L + Tambourine1 Sw + Splash R + Cowbell2 + Crash2 L + Vibraslap + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Slp Open + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Bld St3-4 + Sd Blues 3L + Sd Hip St1-2 + Sd Funk St2-3 + Sd Soul St3-4 + Sd LdwHMono + Sd Heavy + Sd Tight St3-4 + Sd BluesHeart St2-3 + Bd Snap R2 + Bd Tight R2 + Bd Funk 2 R + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Rock Open1 R + Stick1 + Bd Room1 + Sd Strong St4 + Bd Jazz2L + Bd Room3 + Sd Rock Stick2 R + Sd Strong St3-4 + HipHop clap6 + Sd PowerA 1-5St + Tom Floor L + HH Closed Rock Sw R + Tom Low L + HH Half Open Rock R + Tom Low L + HH Open Rock R + Tom Mid R + Tom Mid R + Crash1 L + Tom Hi R + Ride2 L + China L + Ride Cup2 L + Tambourine1 Sw + Splash R + Cowbell2 + Crash1 L + Vibraslap + Ride Cup1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Blues St2-3 + Sd Tight St3-4 + Sd BluesHeart 3R + Sd Tight 4R + Bd Low L + Sd PowerC 4St + Bd R&&B1 + Sd PowerB 1-5St + Bd Room1 + Sd PowerA 1-5St + Bd Funk Son L + Bd Room2 + + + + + + + + Analog Click + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd RockRoll St + Castanet + Sd Rock Open2 R + Stick1 + Bd Room3 + Sd Rock Rim1 R + Bd Rock2 L + Bd Rock1 L + Sd Rock Stick1 R + Sd Rock Open1 R + HipHop clap3 + Sd Rock Mute1 R + Tom Rock Low L + HH Closed Rock Sw R + Tom Rock Low L + HH Pedal Rock Sw R + Tom Rock Mid R + HH Open Rock Sw R + Tom Rock Mid R + Tom Rock Hi R + Crash1 L + Tom Rock Hi R + Ride2 L + China L + Ride Cup2 L + Tambourine RX5 + Splash R + Cowbell1 + Crash1 L + Vibraslap + Ride2 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Rock Flam R + Sd Rock Open2 R + Sd Rock Rim2 R + Sd Rock Mute2 R + Bd Funk 2 R + Sd RockRollD St + Bd H Son L + Sd Tight St3-4 + Bd Funk Son L + Sd SonA 3St + Bd Funk 2 R + Bd H Son L + + + + + + + + Analog Click + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd RockRoll St + Castanet + Sd Rock Open2 R + Stick1 + Bd Room1 + Sd Rock Rim2 R + Bd Ambience R2 + Bd Rock2 L + Sd Rock Stick2 R + Sd Rock Open2 R + HandClap L + Sd Rock Mute2 R + Tom Rock Low L + HH Closed Rock 2R + Tom Rock Low L + HH Half Open Rock R + Tom Rock Mid R + HH Open Rock R + Tom Rock Mid R + Tom Rock Hi R + Crash2 L + Tom Rock Hi R + Ride2 L + China L + Ride Cup2 L + Tambourine RX5 + Splash R + Cowbell1 + Crash2 L + Vibraslap + Ride2 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Rock Flam R + Sd Rock Open1 R + Sd Rock Rim1 R + Sd Rock Mute1 R + Bd Rock1 L + Sd RockRollD St + Bd Funk 2 R + Sd Tight St3-4 + Bd High L + Sd SonA 4St + Bd L Son L + Bd Rock1 L + + + + + + + + Bd HipHop2 + Cabasa2 + Sd HipHop9 + Scratch Lo R + Scratch Vo F + Sd RockRollD St + Clave + Sd HipHop5 + Sd HipHop Rim3 + Bd HipHop8 + Sd HipHop Rim6 + Bd HipHop9 + Bd HipHop5 + Sd HipHop Rim2 + Sd HipHop11 + HipHop clap2 + Sd HipHop2 + Tom Floor L + HH Closed Hip + Tom Low L + HH Pedal Hip + Tom Mid R + HH Open Hip + Tom Hi R + Tom Hi R + Crash4 + Tom Hi R + Ride2 L + Crash2 L + Ride Mono + Tambourine1 Hit + Hand Cymbal Open R + Mambo Body + China L + Handbell L + Ride Cup Mono2 + Conga Hi Slp Open + Conga Lo Open 1 + Conga Lo Tip + Conga Lo SlapOpen + Conga Lo Open 2 + Bd HipHop4 + HipHop clap3 + Agogo Hi + Agogo Lo + Shaker 2 + Shaker 1 + Bd HipHop10 + Bd HipHop6 + Sd HipHop Rim4 + Sd HipHop6 + HipHop Snap1 + Bd HipHop10 + Sd HipHop7 + HipHop clap1 + Paila L + Triangle Mute + Triangle Open + Tambourine1 Sw + Shaker 2 + Bd HipHop8 + Clap T9 + Sd T8-1 + Sd T8-1 + Sd T8-4 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open T8-2 + Tom T8 + Crash T8 + + + + + + + + Bd HipHop3 + Hi Q2 + Scratch Hip2 + Scratch Sd R + Scratch Vo R + Sd Break Roll + Sd T9Rim + Sd HipHop2 + Sd T8 Rim + Bd HipHop9 + Sd HipHop Rim1 + Bd HipHop6 + Bd HipHop5 + Sd HipHop Rim2 + Sd HipHop1 + HipHop clap6 + Sd HipHop9 + Tom T9 Lo + HH Closed Hip + Tom T9 Lo + HH Pedal Hip + Tom T9 Hi + HH Open Hip + Tom T9 Hi + Tom T8 + Crash3 + Tom T8 + Ride T9 + Ride T9 + Crash Brush Cup L + Tambourine RX5 + Crash Syn + Cowbell AnCR + Crash T9 + MetalGuiro Long + Ride Mono + Cajon Tip 1-2 + Bongo Lo Tip + Tmbourim Finger + Tumbo 2 + Tumba Heel + Paila H + Paila L + Cowbell T8 + Cowbell AnCR + Shekere Up + Caxixi1 + Grain2 R + Grain2 R + Cajon Tip 1 + HipHop clap7 + Clave + Wood Block + Sd HipHop Rim4 + HipHop Snap1 + HipHop Snap2 + Electric Triangle + Triangle Open + Shaker 1 + MetalGuiro Long + Wind Chime + Sd HipHop Rim4 + Sd T8-1 + Sd T8-2 + Sd T8-4 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open T8-2 + Tom T8 + HipHop flex1 + + + + + + + + Sd Hip Stick2 + Vox Hit Rev + TurnTable + Hit Brass + Scratch Sd R + Sd LiveRoll R + Clave T8 + Sd Hip 2R + Sd Hip Stick6 R + Bd HipHop9 + Sd Garg L + Bd T8-3 + Bd T9-1 + Sd HipHop Rim3 + Sd HipHop10 + HipHop clap6 + Sd HipHop1 + Tom T8 + HH Closed T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash4 + Tom T8 + Ride Brush1 L + Crash T8 + Ride T9 + Tambourine RX5 + Splash R + Mambo Body + Crash T9 + Vibraslap + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Hip Stick8 + Sd Jungle3 + HandClap L + Sd Hip Rim5 + Scratch H + Scratch L + Scratch Down + Scratch Hi R + Scratch Bd F + Scratch Combi + Scratch Vo R + Bd Break2 + + + + + + + + Bd T8-3 + Sd T8 Rim + Sd Hip1 + Clap T9 + Sd Syn + Sd LiveRoll R + Sd Hip Stick4 + Sd HipHop2 + Sd HipHop Rim6 + Bd Hip Deep + Sd R&&B1 Rim + Bd Break Heavy + Bd Hip Deep + Sd HipHop Rim2 + Sd Hip3 + HandClap L + Sd Lo-Fi + Tom Floor R + HH Closed Lo-Fi + Tom Low L + HH Closed Break + Tom Low L + HH Open Lo-Fi + Tom Low L + Tom Hi L + Crash3 + Tom Hi L + Ride T9 + China2 + Ride Cup Mono + Tambourine RX5 + Crash3 + Cowbell RX11 + Crash2 R + Cowbell T8 + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Radio Noise + Radio Noise + Agogo Lo + Agogo Lo + Cabasa1 + MaracasT8 + FxGun2 + FxGun1 + Scratch Spin + Scratch Stop + Clave T8 + Door Squeak + Hi Q2 + Scratch Bd F + Scratch Bd R + Triangle Mute + Triangle Open + Analog Shaker + Tambourine1 Hit + Wind Chime + Sd Human2 + Sd Break Roll + Stab Hard + Sd Jungle1 + Bd Rock1 L + Scratch Down + Wataiko + HH Hit + Stab Organ2 + Sonar1 + TurnTable + Police + + + + + + + + Sd T8 Rim + Vox Hit Rev + TurnTable + Stab Hard + Scratch Vo R + Sd Roll Mono + Clave T8 + Sd Hip 1R + Sd Hip Stick4 + Bd AnCR + Sd Hip Rim3 + Bd T9-3 + Bd Hip Deep + Sd Hip Stick5 + Sd Hip Gate + HipHop clap6 + Sd Lo-Fi + Tom T8 + HH Closed Hip + Tom T8 + HH Pedal Hip + Tom T8 + HH Open Hip + Tom T8 + Tom T8 + Crash4 + Tom T8 + Ride2 L + Crash T8 + Ride T9 + Tambourine RX5 + Splash R + Mambo Body + Crash T9 + Vibraslap + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Shaker Hip1 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Hip Stick1 + Sd Jungle1 + HandClap L + Sd Break Heavy + Scratch L + Scratch Down + Scratch Hi F + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Bd Break Heavy + + + + + + + + Sd Hip Stick1 + Hi Q1 + TurnTable + Hit Uhh + Scratch Stop + Sd LiveRoll L + Clave T8 + Sd Cut + Sd Rock Stick1 R + Bd T9-2 + Sd Hip Rim1 + Bd R&&B2 + Bd Break Deep + Sd Closed Rim3 + Sd Hip Rim2 + HipHop clap4 + Sd R&&B1 + Conga T8 + HH Closed T8-1 + Conga T8 + HH Closed T8-2 + Conga T8 + HH Open T8-2 + Conga T8 + Conga T8 + Crash4 + Conga T8 + Ride2 L + Crash T8 + Ride T9 + Tambourine RX5 + Splash R + Mambo Body + Crash T9 + Vibraslap + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Closed Rim R + Sd AnSm Rim + HandClap L + Sd Hip3 + Scratch Sd R + Scratch Hip + Scratch Bd R + Scratch Vo R + Scratch Hip2 + Scratch Combi2 + Scratch Combi4 + Bd Hip Deep + + + + + + + + Sd Hip Rim4 + Hi Q1 + TurnTable + Hit Orchestra1 + Scratch Bd R + Sd LiveRoll R + Analog Click + Sd Hip Rim3 + Sd AnCR Rim + Bd HipHop9 + Sd HipHop Rim6 + Bd T8-3 + Bd T8-2 + Sd T8 Rim + Sd Hip Stick4 + HandClap L + Sd Hip Stick3 + Tom T8 + HH Closed T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash4 + Tom T8 + Ride2 L + Crash T8 + Ride T9 + Tambourine RX5 + Splash R + Mambo Body + Crash T9 + Vibraslap + Ride T9 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd AnSm + Sd Closed Rim3 + Analog Click + Sd Closed Rim2 + Scratch H + Scratch L + Scratch Down + Scratch Hi R + Scratch Bd F + Scratch Combi + Scratch Vo R + Bd Jungle2 + + + + + + + + Sd Hip Rim3 + Hi Q2 + Scratch H + Scratch H + Scratch Vo R + Sd Roll Mono + Analog Click + Sd Hip Rim4 + Sd T8 Rim + Bd HipHop9 + Sd Hip Stick4 + Bd Hard Long + Bd ElecClick + Sd Hip Stick1 + Sd Hip Stick2 + HipHop clap8 + Sd Hip Stick6 R + Tom T9 Lo + HH Closed T8-1 + Tom T9 Lo + HH Open T8-1 + Tom T9 Lo + HH Open T8-1 + Tom T9 Lo + Tom T9 Lo + Crash T9 + Tom T9 Lo + Ride1 L + Crash T8 + Ride Cup Mono2 + Electric Tambourine2 + Splash R + Cowbell T8 + Crash T9 + Vibraslap + Crash Syn + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd T8 Rim + Sd Hip Stick1 + Sd Hip Stick2 + Sd Closed Rim2 + Scratch Down + Scratch Hi F + Scratch Lo F + Scratch Lo R + Scratch Bd R + Scratch Combi + Scratch Stop + Bd HipHop8 + + + + + + + + Bd T9 Hd3 + Sd AnCR + Sd AnSm Rim + Sd ClapAna + Sd T9Gate + Sd Roll Mono + Sd Timbr + Sd FM Rim + Sd JungleFX + Bd T9-4 + Sd Hip Rim5 + Bd T9-3 + Bd Elec2 + Sd T8 Rim + Sd Lo-Fi + HipHop clap2 + Sd R&&B1 + Bd T8-3 + HH Closed T9 + Bd T8-3 + HH Pedal Hip + Bd T8-3 + HH Open T9 + Bd T8-3 + Bd T8-3 + Crash1 L + Bd T8-3 + Ride2 L + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash1 L + Cowbell T8 + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Hi Q1 + Hi Q1 + Agogo Lo + Agogo Lo + Analog Shaker + Maracas Slur + Vox Drum + Vox Drum + Hi Q1 + Hi Q2 + Clave T8 + Hi Q1 + Scratch Spin + Scratch Hi R + Scratch Hi F + Triangle Mute + Triangle Open + Shaker 2 + Tambourine1 Hit + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udu High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + Bd T9-1 + Sd AnCR Rim + Sd Hip Gate + Sd Break Roll + Sd T9-2 + Sd LiveRoll St + HipHop Snap2 + Sd HipHop9 + Sd HipHop Rim2 + Bd HipHop5 + Sd Hip Rim4 + Bd T9-1 + Bd T8-2 + Sd HipHop Rim1 + HipHop Snap2 + HipHop clap8 + Sd T8-5 + Tom T8 + HH Closed T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash1 L + Tom T8 + Ride2 L + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash1 L + Cowbell T8 + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Hi Q1 + Hi Q1 + Agogo Lo + Agogo Lo + Analog Shaker + Maracas Slur + Vox Drum + Vox Drum + Hi Q1 + Hi Q2 + Clave T8 + Hi Q1 + Scratch Spin + Scratch Hi R + Scratch Hi F + Triangle Mute + Triangle Open + Shaker 2 + Tambourine1 Hit + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udu High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + Bd T9 Hd2 + Sd AnCR + Sd Break2 + Sd Break Roll + Sd T9Gate + Sd Roll Mono + HipHop Snap2 + Sd HipHop2 + Sd HipHop Rim3 + Bd T9-2 + Sd Hip Rim3 + Bd T9-1 + Bd T8-2 + Sd HipHop Rim3 + Sd R&&B1 + HipHop clap6 + Sd Hip Gate + Tom T9 Lo + HH Closed T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + Tom T9 Hi + Crash1 L + Bd T8-3 + Ride2 L + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash1 L + Cowbell T8 + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Hi Q1 + Hi Q1 + Agogo Lo + Agogo Lo + Analog Shaker + Maracas Slur + Vox Drum + Vox Drum + Hi Q1 + Hi Q2 + Clave T8 + Hi Q1 + Scratch Spin + Scratch Hi R + Scratch Hi F + Triangle Mute + Triangle Open + Shaker 2 + Tambourine1 Hit + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udu High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + Bd T9-2 + Sd AnSm Rim + Sd Elec9 + Clap AnSm + Sd Elec7 + Sd Elec11 + Sd Elec7 + Sd T8-1 + Sd T9-5 + Bd T9 Hd2 + Sd Elec12 + Bd T9 Hd3 + Bd Hard Long + Sd T9Rim + Sd Elec5 + Clap T9 + Sd T9-2 + Sd Syn + HH Closed T9 + Sd Syn + HH Open T9 + Sd Syn + HH Open T9 + Sd Syn + Sd Syn + Crash T9 + Sd Syn + Ride T9 + Crash Syn + Ride Cup Mono + Tambourine1 Hit + Crash T8 + Cowbell T8 + China L + Cowbell T8 + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Slap + Conga Hi Slp Open + Conga Hi Open 2 + Tom Syn1 + Tom Syn1 + Analog Click + Noise Burst + Cabasa2 + Maracas Slur + Neo Seq Em-3 + Neo Seq2 Em-5 + Electric Perc1 + Shaker Hip1 + Clave T8 + Tambourine RX5 + Cowbell RX11 + Shaker Hip2 + Electric Perc10 + Triangle Mute + Triangle Open + Shaker Hip4 + Zap2 + Bd HipHop8 + Bd T9 Hd1 + Bd ElecComp3 + Bd T9-1 + Bd T9 Hd3 + Bd Hard Long + HH Closed T8-1 + Bd ElecComp2 + HH Open T8-2 + Bd T9 Hd1 + HH Open T8-1 + Bd T9 Hd2 + Bd ElecComp3 + + + + + + + + LFO Seq + Trance Choir + Tibetan Bowl + Scratch Stop + Fx Female Voice L + Stab Organ2 + Door Squeak + Radio Noise + Door Squeak + Acoustic Bass FX06 + Electric Bass FX 02 + Acoustic Bass FX06 + Bd BlpHd + Bd Blip + Sd Hip Gate + Sd SonA St3-4 + Noise Burst + Tom Dist Lo + HH Closed T9 + Tom Dist Hi + HH Closed 2R + Tom T9 Hi + HH Open T9 + Tom T9 Hi + Tom T9 Hi + China L + Tom T9 Hi + Splash R + Crash T9 + Ride T9 + Pandeiro Tip + Electric Tambourine1 + Hi Q2 + Bd Gate + Hi Q2 + Acoustic Bass FX03 + Acoustic Bass FX01 + Mini Blip6 + Mini Blip1 + Mini Blip2 + Quinto1 Slap + Tabla Bya + Djembe Edge + Tabla Nah + Barasim Tak + Electric Perc8 + Electric Perc13 + Hop FX2 + Motion Beam + Zap2 + Digi Voice Mod + Human Whistle + Digital Wind + Analog Click + HH Closed T8-1 + Sd FM + HH Half Open2 R + Clave T8 + HH Open T8-1 + Reverse Cymbal + Bd Analog Tight + Bd ElecComp3 + Bd T9 Hd3 + Bd ZapHard + Bd Slimy + Bd Break Hard + Sd BreakRim + Sd Soul St3 + Sd Ambience + Sd Rock Mute1 R + Sd Cut + Sd D&&B3 + Sd FX1 + + + + + + + + Bd T8-1 + Sd T8 Rim + Sd T8-2 + Electric Clap1 + Sd Elec11 + Sd Elec7 + Sd T8-4 + Bd Elec2 + Sd T8-1 + Bd HipHop8 + Sd T8-3 + Bd HipHop9 + Bd HipHop8 + Sd AnSm Rim + Sd HipHop5 + Clap AnSm + Sd HipHop6 + Tom T8 + HH Closed T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash3 + Tom T8 + Ride T9 + Splash R + Ride Cup Mono + Tambourine1 Hit + Crash T8 + Cowbell T8 + China L + Cowbell T8 + Ride1 L + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Conga Hi Slap + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Cabasa2 + Maracas Slur + Fx Female Voice L + Portamento Lead Seq + Electric Perc9 + Shaker Hip1 + Clave T8 + Hi Q1 + Hi Q2 + Shaker Hip2 + Electric Perc15 + Triangle Mute + Triangle Open + Shaker Hip4 + Ripper + Bd Elec4 + Sd Hip1 + Sd Elec9 + Sd Elec6 + Sd HipHop2 + Bd ElecGate + HH Closed T8-2 + Bd ThickSkin + HH Open T8-2 + Bd Break Heavy + HH Open T8-2 + Bd Break Deep + Bd R&&B2 + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Brush Soft St + Stick1 + Bd Funk 2 R + Sd Basic 3St + Bd Jazz2L + Bd FK Jazz R + Sd Closed Rim4 St + Sd Brush Med St + HandClap L + Sd Brush vari St + Tom Brush Floor L + HH Power ClTip 1-4R + Tom Brush Low L + HH Power ClPedal R + Tom Brush Low L + HH Power Open EdgeR + Tom Brush Mid R + Tom Brush Mid R + Crash Brush1 L + Tom Brush Hi R + Ride Brush1 L + China L + Crash Brush Cup L + Tambourine1 Sw + Splash R + Cowbell2 + Crash Brush2 R + Vibraslap + Ride Brush2 L + Bongo Hi 1Finger 1-2 + Bongo Lo 1Finger 1-2 + Conga Hi SlapMute1-2 + Conga Hi Open 1-2 + Conga Lo Open 1-2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1/3 + Sleigh Bell + Wind Chime + Sd Bld St3-4 + Sd Blues 3St + Sd Hip St1-2 + Sd Funk St2-3 + Sd Soul St3-4 + Sd LdwHMono + Sd Heavy + Sd Tight St3-4 + Sd BluesHeart St2-3 + Bd Snap R2 + Bd Tight R2 + Bd Funk 2 R + + + + + + + + Bd Jazz2L + Sd Brush Soft R + Sd Brush SwirAtt St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Hip 1St + Stick1 + Bd Funk 2 R + Sd Tight 3St + Bd FK Jazz R + Bd Funk 2 R + Sd Closed Rim St + Sd Tight 1St + Sd RockRoll St + Sd BluesHeart St2-3 + Tom Floor L + HH Power ClTip 1-4R + Tom Floor L + HH Power ClPedal R + Tom Low L + HH Power Open EdgeR + Tom Mid R + Tom Mid R + Crash2 L + Tom Hi R + Ride2 L + China L + Ride Cup Mono2 + Tambourine1 Hit + Splash R + Cowbell1 + Crash2 L + Vibraslap + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Timbale Hi1 + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + Sd Brush Soft R + Sd Brush Swir St + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Timbale Paila H + Timbale Paila L + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + Wind Chime + Sd Blues 3L + Sd Funk Ay L + Sd BluesHeart 3R + Sd Blues 2St + Bd Low L + Sd LdwHMono + Bd Dry2 + Sd Brush SwirAtt St + Bd Hip Deep + Sd Roll Mono + Bd Normal 2L + Crash Brush2 R + + + + + + + + Bd T9-2 + Sd AnCR + Sd T9-5 + Clap AnSm + Sd T9Gate + Sd RockRoll St + Sd T9-3 + Sd T9-4 + Sd T9Gate + Bd T9-4 + Sd T9-5 + Bd T9-1 + Bd T9-3 + Sd T9Rim + Sd T9-1 + Clap T9 + Sd T9-2 + Tom T9 Lo + HH Closed T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + China2 + Ride Cup Mono + Tambourine RX5 + Crash3 + Cowbell1 + Crash4 + Cowbell T8 + Ride2 L + Conga T8 + Conga T8 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Analog Click + Conga T8 + Cabasa1-2 + Maracas Slur + FxGun2 + FxGun1 + Scratch H + Scratch Down + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Piccolo + Sd T8-5 + Sd RockRollD St + Sd Brush Med St + Bd BlpHd + Sd Jungle1 + Bd Sustain + Sd D&&B1 + Bd Break2 + Sd Dist + Bd TekPower + Bd Distortion RM + + + + + + + + Bd T8-1 + Sd AnCR + Sd T8-4 + Sd ClapAna + Sd T8-3 + Tom T8 + Sd T8-5 + Bd T8-3 + Sd T8-4 + Bd T8-2 + Sd T8-3 + T8 Kick Bass + Bd T8-2 + Sd T8 Rim + Sd T8-2 + Clap T9 + Sd T8-1 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash T8 + Tom T8 + Ride T9 + China2 + Ride Cup Mono + Tambourine RX5 + Splash R + Cowbell T8 + Crash4 + Vibraslap + Ride2 L + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Timbale Hi1 + Timbale Lo1 + Glass + Glass + Cabasa1-2 + MaracasT8 + FxGun2 + FxGun1 + Analog Shaker + Analog Shaker + Clave T8 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Hip1 + Sd Hip2 + Sd Hip Gate + Sd Break1 + Bd Blip + Sd FX1 + Bd FxHammer + Sd Hammer + Bd ZapHard + Sd Garg L + Bd TekPower + Bd Slimy + + + + + + + + Bd Slimy + Sd T9Rim + Sd T8-5 + Clap T9 + Sd T9-3 + Sd Human1 + Sd Hip3 + Sd T9-4 + Sd Hip2 + Bd ThickSkin + Sd T9-2 + Bd Distortion4 + Bd ZapMid + Sd T8 Rim + Sd T8-5 + Clap T9 + Sd Syn + Tom T8 + HH Closed Lo-Fi + Tom T8 + HH Open Lo-Fi + Tom T8 + HH Open Lo-Fi + Tom T8 + Tom T8 + Crash T8 + Tom T8 + Ride T9 + Crash3 + Ride Cup Mono + Tambourine AnCR + Crash2 L + Cowbell T8 + Crash T9 + Cowbell T8 + Crash Syn + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Conga T8 + Timbale Hi1-3 + Timbale Lo1-2 + Glass + Glass + Cabasa1 + MaracasT8 + FxGun2 + FxGun1 + Scratch H + Scratch Down + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Bd Elec4 + Bd ElecComp3 + Bd ElecFX2 + Bd ElecClick + Bd ElecPower1 + Bd ElecPower2 + Bd Elec3 + Bd Elec1 + Sd Elec6 + Sd Elec7 + Sd Elec8 + Sd Elec9 + Sd Elec12 + + + + + + + + Bd T9-4 + Sd T8 Rim + Sd T8-5 + HandClap L + Sd Garg L + Sd LiveRoll St + Sd T9-3 + Sd T8-1 + Sd T9-5 + Bd T9-1 + Sd T9Gate + Bd T9-2 + Bd T9-2 + Sd T9Rim + Sd T9-1 + Clap T9 + Sd T9-2 + Tom T9 Lo + HH Closed T8-1 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + Crash3 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 L + Cowbell T8 + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Timbale Hi1 + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + Vox Drum + Vox Drum + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udu High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + Bd T9-2 + Sd T8 Rim + Dohol High + Clap T9 + Sd T9-2 + Sd T9Gate + Sd Human2 + Sd FX1 + Sd Lo-Fi + Bd T9 Hd2 + Sd T9Rim + Bd T9-3 + Bd T9 Hd1 + Sd T9Rim + Sd T9-2 + Noise Burst + Sd Strong L3 + Sd Syn + HH Closed T9 + Sd Syn + HH Open T9 + Sd Syn + HH Open T9 + Sd Syn + Sd Syn + Crash T9 + Sd Syn + Ride T9 + Crash Syn + Ride Cup Mono + Tambourine1 Hit + Crash T8 + Cowbell1 + Crash3 + Cowbell T8 + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Heel + Conga Hi Slp Open + Conga Hi Open 2 + Tom Syn1 + Tom Syn1 + Analog Click + Sd Rim SE + Cabasa1 + Maracas Slur + Vox Drum + Vox Drum + Tambourine AnCR + Analog Shaker + Clave T8 + Tambourine RX5 + Cowbell RX11 + Analog Shaker + Scratch Hi F + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Tabla Bya + Tabla Nah + Tabla Mute + Vox Bell + Bd Pop + Udu High + Bd FX Gate + Noise Burst + Bd T9-1 + Sd Break Roll + Crash3 + TurnTable + + + + + + + + Bd T9-4 + Sd T8 Rim + Sd T8-5 + HandClap L + Sd Garg L + Sd LiveRoll St + Sd T9-3 + Sd T8-1 + Sd T9-5 + Bd T9-1 + Sd T9Gate + Bd T9-2 + Bd T9-3 + Sd T9Rim + Sd T9-1 + Clap T9 + Sd T9-2 + Tom T9 Lo + HH Closed T8-1 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + Crash3 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 L + Cowbell T8 + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Timbale Hi1 + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + Vox Drum + Vox Drum + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Break Roll + Noise Burst + Vox Bell + Sd R&&B1 + VoxAlk + Udu High + Trance Choir + Swarm + Bd Pop + Sd R&&B1 Rim + Bd R&&B2 + Crash3 + + + + + + + + Bd FX Gate + Bongo Lo 3Finger 1-2 + Sd GM + HandClap L + Sd Gate2 + Sd RockRoll St + Sd Rock Rim1 R + Sd Piccolo + Sd Rock Flam R + Bd Room2 + Sd Rock Stick1 R + Bd Room3 + Bd Gate + Sd Mid Rim + Sd Gate1 + Clap AnSm + Sd Ambience + Tom Ambience Lo + HH Closed D&&B + Tom Ambience Lo + HH Pedal D&&B + Tom Ambience Lo + HH Open D&&B + Tom Ambience Hi + Tom Ambience Hi + Crash2 L + Tom Ambience Hi + Ride1 L + China L + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 L + Vibraslap + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Bd +HH + Bd +HH + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + FxGun2 + FxGun1 + Scratch H + Scratch Down + Clave + Wood Block + Wood Block + Scratch L + Scratch L + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + Wind Chime + Sd Dist + Sd FM + Sd FM Rim + Sd Clappy + Bd Room3 + Sd Timbr + Bd Rock1 L + Sd Rim SE + Bd Rock2 L + Sd Break Roll + Bd D&&B1 + Bd Room2 + + + + + + + + Bd Break Deep + Sd Hip 1St + Sd Lo-Fi + Sd Clappy + Sd LdwHMono + Sd RockRollD St + Sd Gate1 + Sd Mid + Sd BreakRim + Bd Break Heavy + Sd Hip Rim4 + Bd Break2 + Bd Break1 + Sd Hip Rim1 + Sd Break3 + Sd Break1 + Sd Break2 + Tom Low L + HH Closed Rock S R + Tom Low L + HH Pedal Rock R + Tom Low L + HH Half Open Rock R + Tom Hi R + Tom Hi R + Crash2 L + Tom Hi R + Ride2 L + China L + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 R + Cowbell RX11 + Ride Mono + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Open 2 + Agogo Lo + Cabasa1 + Maracas Slur + Timbale Hi1 + Timbale Lo1 + Scratch H + Scratch Down + Clave + Wood Block + Wood Block + Scratch L + Scratch L + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + TurnTable + Sd Break Roll + FM Bass5 + Mind Bell + Sd FX1 + Bd Rock2 L + LFO Seq + Ripper + Noise Burst + Police + Fairwave + Swarm + Radio Noise + + + + + + + + Bd Hip Deep + Bongo Lo 3Finger 1-2 + Sd R&&B2 + Sd Timbr + Sd D&&B3 + Sd Hammer + Glass + Bd D&&B2 + HH Pedal 3 R + Bd Hip Deep + HH Open2 R + Bd Break Hard + Bd D&&B1 + Sd Mid Rim + Sd D&&B1 + Noise Burst + Sd D&&B2 + Tom Ambience Lo + HH Closed D&&B + Tom Ambience Lo + HH Pedal D&&B + Tom Ambience Lo + HH Open D&&B + Tom Ambience Hi + Tom Ambience Hi + Crash2 L + Tom Ambience Hi + Ride1 L + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 L + Vibraslap + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Sd D&&B4 + HH Reverse D&&B + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + FxGun2 + FxGun1 + Guiro Short + Guiro Long + Clave + Electric Perc8 + Electric Perc2 + P50-1 + Electric Perc13 + TurnTable + Triangle Open + Shaker 2 + Sleigh Bell + Wind Chime + Sd Dist + Sd FM + Sd FM Rim + Sd Clappy + Bd D&&B1 + Sd Timbr + Bd Distortion5 + Sd Rim SE + Bd Distortion4 + Sd Hip3 + Bd Slimy + Bd +HH + + + + + + + + Bd Analog Tight + Sd T8 Rim + Sd Jungle3 + HandClap L + Sd T8-1 + Tom Syn1 + Sd BluesHeart 3L + Sd BluesHeart 3R + Sd SonA 3St + Bd Funk 2 R + Sd Basic St2-3 + Bd Blip + Bd T9-4 + Sd T9Rim + Sd FM + Clap T9 + Sd AnCR + Tom T9 Lo + HH Closed Tek + Tom T9 Lo + HH Closed Tek + Tom T9 Lo + HH Open Tek + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + Crash3 + Ride Cup2 L + Tambourine1 Hit + Crash3 + Cowbell1 + Splash R + Cowbell T8 + Ride Mono + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1 + Timbale Hi1 + Agogo Hi + Agogo Lo + Cabasa1 + Maracas Slur + FxGun2 + FxGun1 + Scratch H + Scratch Down + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd T9-5 + Sd T9-5 + Sd T9-5 + Sd T9-5 + Bd Human1 + Sd D&&B1 + Acoustic Bass FX03 + DigiOsc4 + DigiOsc3 + Sd Break1 + Bd Jungle1 + Bd Break Heavy + + + + + + + + Bd Jungle2 + Sd Mid Rim + Sd Jungle3 + Sd Jungle3 + Sd Piccolo Rim1 + Sd Roll Mono + Sd Wood + Sd R&&B2 + Sd D&&B2 + Bd Jungle2 + Sd JungleFX + Bd T8-1 + Bd Jungle1 + Sd Hip Rim4 + Sd Jungle2 + Sd JungleFX + Sd Jungle3 + Tom Floor L + HH Closed Rock Sw R + Tom Low L + HH Pedal Rock Sw R + Tom Mid R + HH Open Rock Sw R + Tom Mid R + Tom Hi R + Crash2 L + Tom Hi R + Ride2 L + China2 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell1 + Crash2 L + Vibraslap + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Tabla Nah + Tabla Bya + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + Timbale Hi1 + Timbale Lo1 + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + Wind Chime + HH Reverse D&&B + RainLoop + Scratch H + Scratch L + Bd BlpHd + Sd Syn + IndusTom + Pandeiro Open + Ripper + TelRing + Scratch Lo R + Tape Rewind + + + + + + + + Bd AnCR + Bd Blip + Sd T8-5 + Clap AnSm + Sd Hip Rim2 + Sd Roll Mono + Sd T8-1 + Sd AnCR + Sd T8-2 + Bd T8-1 + Sd T8-4 + Bd Blip + Bd T8-2 + Sd T8 Rim + Sd T8-3 + Clap T9 + Clap AnSm + Tom Syn2 + HH Closed T8-2 + Tom Syn2 + HH Open T8-1 + Tom Syn2 + HH Open T8-2 + Tom Syn2 + Tom Syn2 + Crash T9 + Tom Syn2 + Ride T9 + Crash3 + Crash Syn + Crash T8 + Crash3 + Sd Rim SE + Crash2 L + Cowbell T8 + Crash Syn + Tom T8 + Tom T8 + Tom T8 + Tom T8 + Tom T8 + Ooh Feedback + Door Squeak + Heart Beat + Bomb + HH Open Lo-Fi + Shaker 1 + Zap2 + Zap1 + Scratch H + Scratch L + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Hit Orchestra2 + HH Closed FX + HH Open FX + Analog Shaker + Analog Shaker + TurnTable + Sd Break Roll + FM Bass5 + Mind Bell + Sd Break2 + Bd Rock2 L + Radio Noise + Ripper + Noise Burst + Scratch Spin + Bell Choir + Hi-Lo + Radio Noise + + + + + + + + Bd Human1 + Scratch Vo F + Hit Uhh + Scratch Vo R + Sd JungleFX + Sd RockRoll St + Sd Break1 + Sd AnCR + Sd T9-1 + Bd Sustain + Sd Hip Stick1 + Bd Human2 + Bd Human2 + Sd Wood + Sd Human1 + Sd Garg H + Sd Human2 + Bd Human1 + HH Open Human + Bd Human1 + HH Open Human + Bd Human1 + HH Open Human + Bd Human1 + Bd Human1 + Noise Burst + Bd Human1 + Crash T8 + China2 + Ride Cup Mono + Scratch Sd R + Splash R + Vox Drum + Crash4 + Electric Tambourine2 + Stab Aah + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Lo + Agogo Lo + Cabasa1 + Maracas Slur + FxGun2 + Bomb + Scratch H + Scratch Down + Scratch Lo F + Scratch Lo R + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Shaker 2 + Sleigh Bell + TurnTable + Sd Rock Rim1 R + Helicopter + Stab Hard + Sd Gate1 + Bd Rock1 L + Scratch Down + Bd AnSm + Sonar1 + Wataiko + Wataiko + HH Hit + Stab Organ2 + + + + + + + + Bd Distortion3 + Sd Wood + Sd Gate1 + HandClap L + Sd Gate2 + Sd Roll Mono + Sd ClapAna + Sd Hip3 + Ripper + Bd Distortion RM + Sd T8 Rim + Bd Distortion2 + Bd Distortion1 + Sd Mid Rim + Sd Dist + Noise Burst + Sd FX2 + Tom Dist Lo + HH Closed T9 + Tom Dist Lo + HH Open T9 + Tom Dist Lo + HH Open T9 + Tom Dist Hi + Tom Dist Hi + Crash T9 + Tom Dist Hi + Ride T9 + China2 + Ride Cup Mono + Tambourine AnCR + Crash3 + Cowbell T8 + Crash2 L + Vibraslap + Ride1 L + Sd Timbr + Sd Timbr + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Bd +HH + Bd +HH + Glass + Glass + Cabasa1 + Maracas Slur + FxGun2 + FxGun1 + Scratch H + Scratch Down + Hi Q1 + Hi Q1 + Hi Q2 + Scratch L + Scratch L + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd Dist + Sd FM + Sd FM Rim + Sd Clappy + Bd FxHammer + IndusTom + Bd D&&B1 + IndusTom + Bd Room3 + IndusTom + Bd T9-4 + Sd Hammer + + + + + + + + Analog Click + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 3St + Stick1 + Bd Rock2 L + Sd Tight 1St + Bd Break1 + Bd Rock2 L + Sd Rock Stick2 R + Sd PowerA 3L + HandClap L + Sd Piccolo Rim1 + Tom Ambience Lo + HH Closed Rock 2R + Tom Ambience Lo + HH Pedal Rock R + Tom Ambience Lo + HH Open Rock R + Tom Ambience Hi + Tom Ambience Hi + Crash2 L + Tom Ambience Hi + Ride2 L + China L + Ride Cup2 L + Tambourine1 Sw + Splash R + Mambo Bl2 + Crash2 L + Vibraslap + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Scratch Combi2 + Scratch Hi R + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Piccolo + Sd Heavy + Sd ClapAna + Sd Piccolo Rim1 + Bd Room2 + Sd Mid + Bd Room3 + Sd Mid Rim + Bd Gate + Sd Gate1 + Bd FX Gate + Bd D&&B1 + + + + + + + + Bd Blip + Hi Q2 + Sd Piccolo Rim2 + Hi Q1 + Sd AnCR + Tom Syn1 + HH Closed Syn + Tom Syn1 + HH Closed FX + Tom Syn2 + HH Open Syn + Bd AnCR + Bd T9-1 + Sd Hip Stick3 + Sd Jungle1 + Clap T9 + Sd Jungle1 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open T8-1 + Tom T8 + HH Open T8-2 + Tom T8 + Tom T8 + Crash Syn + Tom T8 + Ride T9 + Crash3 + Ride Cup Mono + Tambourine1 Hit + Crash3 + Cowbell T8 + Crash2 L + Vibraslap + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Glass + Glass + Cabasa1 + MaracasT8 + Bird + Engine Room L + Electric Perc14 + Gunshot + Clave T8 + Wood Block + Wood Block + Tambourine AnCR + Cowbell AnCR + Triangle Mute + Triangle Open + Analog Shaker + Sleigh Bell + Wind Chime + Sd T8-5 + Sonar1 + Sd T8-5 + Sonar1 + Bubble + Sd T8-5 + Bubble + Sd T8-5 + Eery + Sd T8-5 + FX Chime + FX Chime + + + + + + + + Bd Hard Long + Bd T9-1 + Bd Slimy + Bd AnaHard + Bd Distortion4 + Bd ElecComp3 + Bd Hip Deep + Bd FxHammer + Bd T9-3 + Bd T8-3 + Bd Sustain + Bd R&&B2 + Bd T9 Hd1 + Bd Slimy + Sd T8-1 + Sd T8 Rim + Sd Clappy + Sd Blues 3St + HH Open 2R + HH Closed T8-2 + HH Open 2R + HH Open T8-2 + HH Open 2R + HH Open T8-2 + Clap AnSm + Crash2 St + Crash T9 + Ride1 L + Ride T9 + Analog Click + Clave T8 + Clap AnSm + Clap T9 + Analog Shaker + Conga T8 + Conga T8 + Sd AnCR + Sd RockRoll St + Agogo Hi + China L + Hi Q2 + Hi Q1 + Zap2 + Zap1 + Zap2 + Door Squeak + Scratch Combi3 + AcousticGtr FX27 R + Distortion FX + Scratch Hi F + Wurli EP Med+ + Wurli EP Med+ + Wurli EP Med+ + Resonance X-Fade + Sonar1 + Vox Drum + Vox Drum + Vox Drum + HH Open Syn + HH Closed Syn + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + Tri Wave + + + + + + + + Bd HipHop8 + Bd T9-1 + Bd Blip + Bd Break Hard + Bd ElecFX1 + Bd ElecGate + Bd ElecFX2 + Bd Distortion2 + Bd ThickSkin + Bd ElecComp2 + Bd Elec2 + Bd ElecClick + Bd ElecClick + Sd T9Rim + Sd Elec12 + Clap T9 + Sd Elec8 + Tom T9 Lo + HH Closed T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Hi + Tom T9 Hi + Crash T9 + Tom T9 Hi + Ride T9 + Crash T9 + Ride T9 + Electric Tambourine2 + Crash T9 + Electric Cowbell + Crash T9 + Electric Tambourine1 + Ride T9 + Electric Perc5 + Electric Perc4 + Electric Perc12 + Electric Perc6 + Electric Perc1 + Bell FX1 + Bell FX2 + Scratch Attack + Scratch R&&B + Cabasa1-2 + Maracas Slur + Electric Perc14 + Electric Perc15 + Shaker Hip2 + Electric Perc3 + Electric Perc7 + Electric Perc8 + Electric Perc9 + Electric Perc10 + Electric Perc13 + Triangle Mute + Triangle Open + Electric Maracas + Electric Triangle + Electric Perc11 + Sd Elec1 + Sd Elec9 + Sd Elec10 + Sd Elec11 + Sd Elec2 + Sd Elec3 + Sd Elec4 + Sd Elec5 + Sd Elec6 + Sd Elec7 + Electric Clap1 + Electric Clap2 + + + + + + + + Analog Click + Sd Brush Med L + Sd Brush Swir R + Sd Brush Med L + Sd Brush SwirAtt R + Sd Orch Roll St + Castanet + Sd Orch1 S L + Stick1 + Bd Gran Casa Mute L + Sd Orch1 H L + Bd Gran Casa Mute L + Bd Gran Casa Open L + Sd Closed Rim R + Sd Orch1 H L + HandClap L + Sd Orch2 H L + Timpani Hard St + HH Closed 1-4R + Timpani Hard St + HH Pedal 3 R + Timpani Hard St + HH Open 2R + Timpani Hard St + Timpani Hard St + Hand Cymbal Open R + Timpani Hard St + Ride1 L + China L + Ride Cup1 L + Tambourine1 Hit + Hand Cymbal Mute R + Cowbell2 + Crash1 L + Vibraslap + Ride2 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + Wood Block + + + + + + + + Bd Hard Long + Bd TekPower + Bd Distortion5 + Bd Distortion3 + Bd Distortion1 + Bd D&&B1 + Bd Blip + Bd AnSm + Bd T8-2 + Bd T8-3 + Bd T9 Hd3 + Bd T9-2 + Bd T9-3 + Sd T9Rim + Sd T9-1 + Clap T9 + Sd T9-4 + Tom T9 Lo + HH Closed T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + HH Open T9 + Tom T9 Lo + Tom T9 Lo + Crash T9 + Conga T8 + Ride T9 + Conga T8 + Analog Click + Clave T8 + MaracasT8 + Tambourine AnCR + Analog Shaker + Cowbell T8 + Cowbell AnCR + Sd T8-1 + Sd T8-2 + Sd T8-3 + Sd AnCR + Sd Jungle1 + Sd D&&B1 + Sd Hip1 + Sd R&&B1 + Sd R&&B2 + Sd Hip1 + Sd Wood + Sd Timbr + HH Closed T8-1 + HH Open T8-1 + HH Closed T8-2 + HH Open T8-2 + HH Pedal 3 R + HH Open 2R + HH Open 2R + HH Closed Lo-Fi + HH Open Lo-Fi + HH Closed Syn + HH Open Syn + Analog Shaker + Tambourine RX5 + Tambourine RX5 + Electric Cowbell + Conga T8 + Electric Triangle + Clave T8 + Analog Shaker + Electric Clap1 + Electric Clap2 + Tambourine AnCR + Cowbell T8 + Analog Click + Cowbell RX11 + + + + + + + + Bd Normal 2L + Sd Rock Stick1 R + Sd Rock Open2 R + Sd RockRoll St + Sd Rock Open1 R + Tom Low L + HH Closed Foot R + Tom Low L + HH Pedal 3 R + Tom Mid R + HH Open 2R + Tom Hi R + Bd Rock2 L + Sd Rock Stick1 R + Sd Rock Open2 R + HandClap L + Sd Rock Mute2 R + Tom Rock Low L + HH Closed Rock Sw R + Tom Rock Low L + HH Pedal Rock Sw R + Tom Rock Mid R + HH Open Rock Sw R + Tom Rock Mid R + Tom Rock Hi R + Crash2 L + Tom Rock Hi R + Ride1 L + China L + Ride Cup1 L + Tambourine1 Sw + Splash R + Cowbell1 + Crash2 L + Shaker 1 + Ride1 L + Bd Rock1 L + Sd Rock Stick2 R + Sd Rock Open1 R + Clap T9 + Sd Rock Mute1 R + Tom Rock Low L + HH Closed Rock Sw R + Tom Rock Low L + HH Pedal Rock R + Tom Rock Mid R + HH Half Open Rock R + Tom Rock Mid R + Tom Rock Hi R + Crash2 L + Tom Rock Hi R + Ride2 L + China L + Ride Cup1 L + Tambourine1 Hit + Splash R + Mambo Bl2 + Crash2 L + Shekere Down + Ride2 L + Bd Funk 2 R + Sd Rock Stick1 R + Sd Rock Rim1 R + Sd Rock Flam R + Sd Rock Rim2 R + Tom Floor L + HH Closed 4R + Tom Low L + HH Pedal 3 R + Tom Mid R + HH Open2 R + Tom Mid R + Tom Hi R + + + + + + + + Bd T8-3 + Sd T8 Rim + Sd HipHop11 + Clap T9 + Sd HipHop9 + Bd T8-3 + HH Closed Tek + Bd T8-3 + HH Open Tek + Bd T8-3 + HH Open Tek + Bd T8-3 + Bd Hip Deep + Sd Hip Stick4 + Sd Hip Rim1 + HipHop clap1 + Sd Hip2 + Tom T8 + HH Closed T8-2 + Tom T8 + HH Open AnCR + Tom T8 + HH Open T8-1 + Tom T8 + Tom T8 + Crash T9 + Tom T8 + Crash Syn + Crash Syn + Crash T8 + Tambourine RX5 + Splash R + Cowbell T8 + Crash T9 + Cowbell AnCR + Ride T9 + Bd Hip Deep + Sd Hip Stick5 + Sd Hip Rim2 + HipHop clap2 + Sd Hip1 + Tom T9 Lo + HH Closed RX5 + Tom T9 Lo + HH Closed AnCR + Tom T9 Lo + HH Open T8-2 + Tom T9 Lo + Tom T9 Hi + Crash2 R + Tom T9 Hi + Crash T8 + Crash Syn + Crash T8 + Tambourine1 Hit + China L + Cowbell RX11 + Crash2 L + Cowbell1 + Ride2 L + Bd Hip Deep + Sd HipHop Rim4 + Sd HipHop2 + HipHop clap2 + Sd Hip Rim5 + Tom Dist Lo + HH Closed Hip + Tom Dist Lo + HH Pedal Hip + Tom Dist Hi + HH Open Hip + Tom Dist Hi + Tom Dist Hi + + + + + + + + Bd T9-1 + Bd T9-1 + Bd T9-2 + Bd T9-3 + Bd T9-3 + Bd T9-4 + Bd T9 Hd1 + Bd T9 Hd2 + Bd T9 Hd3 + Bd T9 Hd3 + Bd Hard Long + Bd AnCR + Bd T9-1 + Bd T9-2 + Bd T9-3 + Bd T9-4 + Bd T9 Hd1 + Bd T9 Hd2 + Bd T9 Hd3 + Bd T8-1 + Bd T8-2 + Bd T8-3 + Bd Elec1 + Bd AnCR + Bd Human1 + Bd Elec1 + Bd Blip + Bd BlpHd + Bd Elec3 + Bd ElecFX1 + Bd ElecFX2 + Bd ElecGate + Bd ElecClick + Bd ElecPower1 + Bd R&&B2 + Bd Hip Deep + Bd Break Deep + Bd Break1 + Bd ElecPower2 + Bd Break Heavy + Bd Break Hard + Bd Distortion1 + Bd Distortion2 + Bd Distortion3 + Bd Distortion4 + Bd Distortion5 + Bd Elec2 + Bd ElecComp1 + Bd FxHammer + Bd ZapHard + Bd ZapMid + Bd Sustain + Bd TekPower + Bd Hard Long + Bd Analog Tight + Bd AnaHard + Bd ElecComp2 + Bd ElecComp3 + Bd Slimy + Bd Gate + Bd FX Gate + Bd HipHop1 + Bd HipHop2 + Bd HipHop3 + Bd HipHop4 + Bd HipHop5 + Bd HipHop6 + Bd HipHop7 + Bd HipHop8 + Bd HipHop9 + Bd HipHop10 + Bd Hard Long + Bd Analog Tight + + + + + + + + Wind Chime + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 3St + Stick1 + Bd T9-2 + Sd Basic St2-3 + Bd High L + Bd Normal 2L + Sd Funk St2-3 + Sd Bld St3-4 + HandClap L + Sd Soul St3-4 + Tom Rock Low L + HH Closed 1-4R + Tom Rock Mid R + HH Pedal 3 R + Tom Floor L + HH Open 2R + Tom Low L + Tom Mid R + Crash2 St + Tom Hi R + HH Open 2R + Reverse Cymbal + Timbale Lo1-2 + Conga Hi Open 2 + Timbale Hi1-3 + Conga Hi Slp Open + Tambourine1 Sw + Tumba Open + Cowbell1 + Clave + Bongo Hi 1Finger 1-2 + Wood Block + Bongo Lo 3Finger 1-2 + Wood Block + Dohol Low + Cabasa1-2 + Dohol High + Shaker 1 + Castanet + Maracas Sw + Castanet + Twaisat3 + Twaisat + Twaisat2 + Twaisat4 + Twaisat4 + Small Tabla Dom + Barasim Dom + Small Tabla Snap + Barasim Sak + Small Tabla Sak + Barasim Tak + Small Tabla Tak + Big Tabla Dom + Segal Dom + Big Tabla Tak + Segal Tak + Big Tabla Sak + Tabla Dom + Tabla Tak1 + Tabla Tremolo + Tabla Tak2 + Tabla Tik1 + Tabla Sak1 + Tabla Tik3 + Tabla Tik2 + + + + + + + + Tabla Tik2 + Wataiko + Djembe Edge + Djembe Mute + Djembe Open + Djembe Combi + Tabla Bya + Tabla Nah + Tabla Mute + Tabla Combi + Small Tabla Dom + Small Tabla Dom + Bd Hard Long + Bd T9-1 + Sd T9-2 + Electric Clap1 + Sd Strong L4 + Tom Dist Lo + HH Closed T9 + Tom Dist Hi + HH Closed T8-1 + Tom Syn1 + HH Open T9 + Tom Syn2 + Sd Hip1 + Crash4 + Sd D&&B4 + HH Closed Break + Reverse Cymbal + Timbale Lo1-2 + Conga Hi Open 2 + Timbale Hi1-3 + Conga Hi Slp Open + Tambourine1 Sw + Tumba Open + Cowbell1 + Clave + Bongo Hi 1Finger 1-2 + Wood Block + Bongo Lo 3Finger 1-2 + Wood Block + Dohol Low + Cabasa1-2 + Dohol High + Shaker 1 + Castanet + Maracas Sw + Castanet + Twaisat3 + Twaisat + Twaisat2 + Twaisat4 + Twaisat4 + Small Tabla Dom + Barasim Dom + Small Tabla Snap + Barasim Sak + Small Tabla Sak + Barasim Tak + Small Tabla Tak + Big Tabla Dom + Segal Dom + Big Tabla Tak + Segal Tak + Big Tabla Sak + Tabla Dom + Tabla Tak1 + Tabla Tremolo + Tabla Tak2 + Tabla Tik1 + Tabla Sak1 + Tabla Tik3 + Tabla Tik2 + + + + + + + + Sd FX1 + Sd FX2 + Sd FX3 + Sd FX4 + Sd Dist + Sd Cut + Sd Garg H + Sd Garg L + Sd FM + Sd Syn + Sd Hammer + Sd Lo-Fi + Sd T9-1 + Sd T9-2 + Sd T9-3 + Sd T9-4 + Sd T9-5 + Sd T9Gate + Sd T8-1 + Sd T8-2 + Sd T8-3 + Sd T8-4 + Sd T8-5 + Sd AnSm + Sd AnCR + Sd Human1 + Sd Jungle1 + Sd Jungle2 + Sd Jungle3 + Sd JungleFX + Sd D&&B1 + Sd D&&B2 + Sd D&&B3 + Sd D&&B4 + Sd R&&B1 + Sd R&&B1 Rim + Sd R&&B2 + Sd Hip1 + Sd Hip2 + Sd Hip3 + Sd Hip Gate + Sd Hip Rim1 + Sd Hip Rim2 + Sd Hip Rim3 + Sd Hip Rim4 + Sd Hip Rim5 + Sd Break2 + Sd Break Heavy + Sd Elec1 + Sd Elec2 + Sd Elec3 + Sd Elec4 + Sd Elec5 + Sd Elec6 + Sd Elec7 + Sd Elec8 + Sd Elec9 + Sd Elec10 + Sd Elec11 + Sd Elec12 + Sd Elec1 + Sd T8-2 + Sd Elec3 + Sd Elec4 + Sd Elec5 + Sd Elec6 + Sd Elec7 + Sd Elec1 + Sd Elec8 + Sd Elec10 + Sd Elec11 + Sd Elec12 + Sd T9-3 + + + + + + + + Bd Elec2 + Scratch Stop + Sd Brush Swir L + Sd Brush Med R + Sd Brush Swir St + Sd Roll Mono + FM Bass6 + Seashore + Electric Perc4 + Bd ElecComp3 + Sd Strong R3 + Bd Elec2 + Bd Slimy + Timbale Paila H + Sd Piccolo + Clap AnSm + Sd Elec10 + Mg Bass2 + Hi Q2 + Mg Bass2 + Electric Perc7 + Tom T8 + HH Open T8-2 + Tom T8 + Tom T8 + Crash3 + Tom T8 + Ride Cup Mono + Ride Cup Mono + Ride Cup Mono + Scratch Sd F + Ride Cup1 L + Electric Perc7 + Crash3 + Hi-Lo + China2 + Ring Mod + Electric Perc8 + Ring Mod + Electric Perc7 + Electric Perc11 + Paila H + Paila H + Hop FX2 + Hop FX2 + Scratch H + Scratch Hi F + Zap2 + LFO Seq + LFO Seq + Mini Bass3a + Quiek + Digi Breath + DigiHeli + Electric Perc7 + FM Organ1 + Tibetan Bowl + Tibetan Bowl + Segal Dom + Engine Room L + Electric Perc8 + Steel Mute L + Mini Blip2 + Spectrum2 + Mini Blip5 + Noise + Bd D&&B1 + Spectrum2 + SabreTone + Electric Perc14 + Electric Perc8 + DigiHeli + Wind Chime + + + + + + + + Clave + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + Sd LiveRoll St + Castanet + Sd Normal 3St + Stick1 + Bd Funk 2 R + Sd Basic St2-3 + Bd High L + Bd Normal 2L + Sd Closed Rim St + Sd Normal St3-4 + Clap T9 + Sd Basic St2-3 + Tom Floor L + HH Closed 1-4R + Tom Low L + HH Pedal 3 R + Tom Low L + HH Open 2R + Tom Mid R + Tom Mid R + Crash2 L + Tom Hi R + Ride1 L + China L + Ride Cup1 L + Tambourine1 Sw + Splash R + Cowbell2 + Crash2 L + Vibraslap + Ride1 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Open 2 + Tumba Open + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Cabasa1-2 + Maracas Slur + Whistle + Whistle + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Cuica Hi + Cuica Lo + Triangle Mute + Triangle Open + Shaker 1 + Sleigh Bell + Wind Chime + Sd Blues 3L + Sd Tight St3-4 + Sd BluesHeart 3R + Sd Hip St1-2 + Bd Low L + Sd LdwHMono + Bd R&&B1 + Sd Heavy + Bd Room1 + Sd Ambience + Bd Funk Son L + Bd Room2 + + + + + + + + Clave + Cajon Kick + Cajon Slap + Cajon Tip 1-2 + Clave + Wood Block + HandClap L + Ride2 L + HipHop Snap2 + Castanet + Conga Hi Tip + Conga Hi Heel + Conga Hi Open 1-2 + Conga Hi Mute + Conga Hi Slp Open + Conga Hi Slap + Conga Hi SlapMute1-2 + Conga Lo Tip + Conga Lo Heel + Conga Lo Open 1-2 + Conga Lo Mute + Conga Lo SlapOpen + Conga Lo SlapOpen + Conga Lo Open 1 + Bongo Hi 1Finger 1-2 + Bongo Hi 3Finger 1-2 + Bongo Hi Rim + Bongo Hi Tip + Bongo Hi Heel + Bongo Hi Slap + Bongo Lo 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Bongo Lo Rim + Bongo Lo Tip + Bongo Lo 3Finger 1-2 + Bongo Hi Slap + Timbale Lo1-2 + Quinto1 Slap + Quinto1 Open + Conga Hi Tip + Tumba Open + Paila L + Timbale Hi1-3 + Pandeiro Open + Pandeiro Thump + Pandeiro Tip + Pandeiro Heel + Paila H + Cowbell2 + Cowbell2 + Agogo Hi + Agogo Hi + Guiro Short + Guiro Long + MetalGuiro Short1-2 + MetalGuiro Long + Tambourine1 Sw + Tmbourim Open + Tmbourim Mute + Tmbourim Finger + Maracas Sw + Shaker 1/3 + Cabasa1-2 + Cuica Lo + Cuica Hi + Cowbell1 + Mambo Body + Shekere Down + Shekere Sap + Triangle Mute + Triangle Open + Whistle + Wind Chime + + + + + + + + Wataiko + Udu High + Djembe Open + Quinto1 Open + Udu Low + Udu Finger + Udu High + Tabla Bya + Tmbourim Open + Tabla Mute + Tabla Nah + Tabla Mute + Timpani Hard St + Hit Uhh + Bongo Hi 1Finger 1-2 + Clap T9 + Triangle Mute + Triangle Open + Castanet + Castanet + Castanet + Guiro Long + Guiro Swp + Guiro Short + Vibraslap + Clave + Clave T8 + Cuica Lo + Cuica Hi + Timbale Lo1-2 + Wood Block + Timbale Lo2 + Wood Block + Timbale Hi1-3 + ChaCha + Timbale Hi2 + Timbale Paila L + Timbale Paila L + Timbale Paila H + Mambo Bl1 + Bongo Lo 3Finger 1-2 + Bongo Lo 3Finger 1-2 + Handbell L + Bongo Hi 1Finger 1-2 + Handbell H + Bongo Hi 1Finger 1-2 + Agogo Lo + Agogo Hi + Conga Hi Heel + Conga Hi Tip + Tumba Slap + Maracas Slur + Tumba Open + Conga Hi Heel + Conga Hi Tip + Conga Hi Slp Open + Shaker 1 + Conga Hi Open 2 + Cabasa1 + Cabasa1-2 + Conga Hi Heel + Conga Hi Tip + Quinto1 Slap + Shaker 1 + Quinto2 Open + Caxixi1-2 + Tambourine1 Shake + Tambourine1 Hit + Tambourine1 Sw + Tambourine1 Hit + Sleigh Bell + Whistle + Whistle + + + + + + + + Bd L Son L + Sd Rock Stick2 R + Sd Funk 3St + Crash2 St + Splash R + Tom Low L + HH Closed 1-4R + Tom Low L + HH Pedal 3 R + Tom Mid R + HH Open 2R + Tom Hi R + Conga Hi Tip + Conga Hi Heel + Tumba Slap + Clave + Tumba Open + Conga Hi Tip + Conga Hi Heel + Conga Hi Slp Open + Handbell L + Conga Hi Open 2 + Handbell H + Mambo Bl1 + Conga Hi Tip + Conga Hi Heel + Quinto1 Slap + ChaCha + Quinto1 Open + Bongo Lo 3Finger 1-2 + Bongo Hi 1Finger 1-2 + Bongo Hi 3Finger 1-2 + Timbale Lo1 + Timbale Paila L + Timbale Hi1 + Timbale Paila H + Wood Block + Wood Block + Shekere Down + Shekere Up + Shekere Sap + Maracas Slur + Maracas Staccato + Guiro Long + Guiro Swp + Guiro Short + Surdo Mute + Surdo Open + Pandeiro Tip + Pandeiro Heel + Pandeiro Thump + Pandeiro Open + Tmbourim Finger + Tmbourim Open + Tmbourim Mute + Agogo Lo Tap + Agogo Lo + Agogo Hi + Triangle Open + Triangle Mute + Shaker 2 + Shaker 1 + Shaker 3 + Cabasa1 + Cabasa2 + Caxixi1 + Caxixi2 + Cuica Lo + Cuica Hi + Whistle + Whistle + Vibraslap + Wind Chime + + + + + + + + Electric Bass FX 01 + Zap2 + HH Pedal D&&B + HandClap L + Scratch Vo F + IndusTom + Sd Wood + IndusTom + Sd ClapAna + IndusTom + Sd FM Rim + Bd FX Gate + Bd FxHammer + Conga Hi Slp Open + Sd FM + HandClap L + Sd Timbr + Tom Syn2 + HH Closed Syn + Tom Syn2 + HH Open Syn + Tom Syn2 + HH Open Syn + Tom Syn2 + Tom Syn2 + HH Open Mono + Sonar1 + HH Open T8-1 + HH Open Mono + Ride Cup2 L + Sleigh Bell + HH Open T8-2 + Resonance X-Fade + Crash2 L + RainLoop + Ride Cup2 L + AirHose L + AirHose L + Udu High + Mg Bass2 + Mg Bass3 + Acoustic Bass FX06 + Acoustic Bass FX06 + Agogo Lo + Agogo Lo + HH Open Human + Vox Hit Rev + Hi Q1 + Hi Q1 + LFO Seq + LFO Seq + BellAir + HH Hit + HH Hit + Ooh Feedback + Ooh Feedback + Mind Bell + Glass + Swarm + FxGun2 + Lektronica + Distortion FX + Gunshot + Police + Ripper + Distortion FX + Tape Rewind + FxGun1 + Bomb + Zap2 + Noise Burst + Stab Giant + Stab Orchestra + + + + + + + + Clave T8 + Sd Hip Stick6 R + Guiro Long + Sd Hip Stick6 R + Guiro Long + AcousticGtr FX13 R + Castanet + Electric Bass FX 11 + Stick1 + Electric Bass FX 08 + Electric Bass FX 07 + Tom T9 Hi + Electric Bass FX 17 + Sd Closed Rim3 + Stick2 + Guiro Long + Stick2 + Surdo Open + Sd AnSm Rim + Surdo Open + Clap AnSm + Surdo Open + Vibraslap + Surdo Open + Surdo Open + Seashore + Surdo Open + Sd Rock Stick2 R + Applause + Sd Rock Stick1 R + Tambourine1 Shake + Seashore + Wood Block + Applause + Helicopter + Sd Rock Stick2 R + Conga Hi Slp Open + Conga Hi Slp Open + Castanet + Castanet + Wood Block + Timbale Paila L + Timbale Paila L + Stick1 + Stick2 + Caxixi1-2 + Caxixi2 + Door Squeak + Door Squeak + Guiro Short + Guiro Long + Clave + Wood Block + Wood Block + Door Squeak + Door Squeak + Wood Block + Clave + Door Squeak + HandClap L + ChaCha + Sd Rock Open2 R + Bd Jungle3 + Quinto1 Slap + Bd Room2 + Bd H Son L + Sd Heavy + Surdo Open + Sd Rock Stick1 R + Bd Slimy + Sd Gate1 + Bd Funk Son L + Bd Gate + + + + + + + + Cowbell T8 + HH Closed 4R + Ride Cup2 L + HH Open Syn + Ride Cup2 L + Ride Cup1 L + FM Metal2 + Ride1 L + Electric Perc7 + Cowbell2 + HH Closed 1R + ChaCha + Cowbell2 + HH Closed Syn + HH Open Tek + HH Closed T8-2 + HH Closed RX5 + Cowbell2 + HH Open T8-1 + Cowbell2 + HH Closed Syn + Cowbell2 + HH Open Syn + Cowbell2 + Cowbell2 + HH Open FX + Cowbell2 + Ride Cup1 L + HH Open AnCR + Ride Cup1 L + Tambourine1 Sw + HH Open FX + Mambo Bl2 + HH Open FX + Sleigh Bell + Ride Mono + Brite Piano + Brite Piano + Tubular Bell + Bell + Brass Bell + Timbale Paila H + Timbale Paila L + Bell + Bell + HH Closed 1R + HH Open AnCR + BellAir + FX Chime + HH Open Rock R + Lektronica + Agogo Lo + Cowbell1 + Cowbell2 + Hi-Lo + Hi-Lo + Triangle Mute + Triangle Open + FX Chime + Sleigh Bell + Wind Chime + Small Bell + Small Bell + Brass Bell + Hibashi + Brite Piano + Sd LdwHMono + Brite Piano + Sd BreakRim + Gamelan2 + Udu Finger + Finger Cymbal + Bell + + + + + + + + Tabla Mute + Sd Brush Soft St + Sd Brush Swir St + Sd Brush Med St + Sd Brush SwirAtt St + AcousticGtr FX29 R + Stick2 + Pandeiro Thump + Tabla Mute + Electric Bass FX 16 + AcousticGtr FX17 R + Tabla Combi + Wataiko + Quinto1 Slap + Sd Brush Med St + HandClap L + Sd Funk Ay L + Udu Low + Tabla Mute + Udu Low + Tabla Bya + Udu High + Tabla Nah + Udu High + Udu Finger + Ride2 L + Udu Finger + Ride Cup1 L + Ride Cup Mono2 + Ride Cup1 L + Tambourine1 Hit + Ride Cup1 L + Cowbell1 + Ride Mono + Vibraslap + Ride2 L + Bongo Hi 1Finger 1-2 + Bongo Lo 3Finger 1-2 + Conga Hi Tip + Conga Hi Slp Open + Conga Hi Open 2 + Timbale Hi1-3 + Timbale Lo1-2 + Agogo Hi + Agogo Lo + Tmbourim Finger + Tmbourim Finger + HandClap L + HandClap L + Guiro Long + Guiro Long + Udu Finger + Tabla Bya + Conga Hi Tip + Udu High + Udu High + Udu Finger + Finger Cymbal + Tabla Mute + Sleigh Bell + Wind Chime + Conga Hi Slp Open + Tumba Tip + Tumba Tip + Electric Bass FX 08 + Tumba Tip + Electric Bass FX 01 + Tumba Open + Electric Bass FX 06 + AcousticGtr FX04 R + Udu Low + Djembe Combi + Tumba Slap + + + + + + + + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Hi R + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Scratch Sd F + Scratch Sd R + Scratch Vo F + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Hi R + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Scratch Sd F + Scratch Sd R + Scratch Vo F + Scratch Vo R + Scratch Stop + Scratch Spin + Scratch Hip + Scratch Hip2 + Scratch Combi + Scratch Combi2 + Scratch Combi3 + Scratch Combi4 + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Hi R + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Scratch Sd F + Scratch Sd R + Scratch Vo F + Scratch Vo R + Scratch Stop + Scratch Spin + Scratch Hip + Scratch Hip2 + Scratch Combi + Scratch Combi2 + Scratch Combi3 + Scratch Combi4 + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Hi R + Scratch Lo F + Scratch Lo R + Scratch Bd F + Scratch Bd R + Scratch Sd F + Scratch Sd R + Scratch Vo F + Scratch Vo R + Scratch Stop + Scratch Spin + Scratch Hip + Scratch Hip2 + Scratch Combi + Scratch Combi2 + + + + + + + + Mini Blip2 + Mini Blip1 + Mini Blip3 + Mini Blip4 + Mini Blip5 + Mini Blip6 + Bell FX1 + Bell FX2 + Electric Perc4 + Electric Perc6 + Electric Perc15 + Electric Perc5 + Electric Perc2 + Electric Perc3 + Electric Perc7 + Electric Perc8 + Electric Perc9 + Electric Perc10 + Electric Perc14 + Electric Perc13 + Neo Seq Em-3 + Spectrum1 + Spectrum3 + Spectrum2 + Whisper Seq Bb + Digi Voice Mod + Moving Chime L + Engine Room L + Raspberry + DigiHeli + Hop FX2 + Willow L + Anemone R + Magnitude R + Radio Noise + Quiek + Stab Giant + HH Hit + Stab Hard + Stab Organ2 + Stab Aah + Hit Uhh + Hit Orchestra2 + Hit Orchestra1 + Hit Brass + Orchestra Hit1 R + Orchestra Hit2 L + Brass Sect Fall + Dist/Overdrive FX + Distortion FX + ElectricGtr FX23 + Acoustic Bass FX04 + Acoustic Bass FX05 + Acoustic Bass FX01 + Scratch H + Scratch L + Scratch Down + Scratch Hi F + Scratch Stop + Tape Rewind + Ripper + Noise Burst + Zap1 + Zap2 + Hi Q1 + Hi Q2 + Bomb + FxGun1 + Motion Beam + Birch L + Tri Wave + FM Bass2 + FM Metal2 + + + + + + + + Fat Saw Mn + Scratch Attack + Spectrum2 + 3o3 Saw2 + TurnTable + Sd Roll Mono + FM Bass6 + Tape Rewind + Electric Perc5 + Bd ElecComp2 + Sd Normal St3-4 + Bd Slimy + Bd Elec2 + Mini Bass3a + Sd BluesHeart St2-3 + 3o3 Square + Sd SonA 3L + Tom Dist Hi + DigiHeli + Tom Dist Hi + Electric Perc2 + Tom Dist Hi + HH Open T8-2 + Tom Dist Hi + Tom Dist Hi + Crash3 + Tom Dist Hi + Ride Cup1 L + Ride2 L + Ride Cup Mono2 + Scratch Hi F + Splash R + Mini Bass1 + Crash1 L + Engine Room L + Ride Cup1 L + Sync LFO + P5 SawDown 0 dg + OB Strings + Sd Normal 3St + Electric Perc11 + PWM Slow + LFO Seq + BigSyn + BigSyn + Hit Orchestra2 + Twaisat4 + Digi 03 + Digi 03 + Digi 01 + Digi 01 + Sonar1 + Stab Hard + Engine Room L + Chatter + Hoo + Mondira + Mondira + Rotor + Raspberry + DigiHeli + 3o3 Square + Hop FX2 + DigiBell3 + Vox Hit Rev + Hi Q1 + Tape Rewind + FxGun2 + FxGun1 + Thunder + Police + Bomb + Bomb + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Switch General Midi mode on: Ignored by the MX49/MX61 + f0 7e 7f 09 01 f7 + + + + +Request Device Number Code from the instrument. The instrument answers +with a "Identity Reply" and returns the Device ID 0x47 0x06. + +0xF0 0x7E 0x0? 0x06 0x01 0xF7 + +0x0?: Device number (any number) + + f0 7e 00 06 01 f7 + + + + +Reply message to an "Identity Request" + +0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 + +dd dd: Device Number Code (always 0x47 0x06) + + F0 7E 7F 06 02 43 00 41 47 06 00 00 00 7F F7 + + + + +Overall volume of all parts + +0xF0 0x7F 0x7F 0x04 0x01 ll mm 0xF7 + +ll: Volume LSB (Ignored) +mm: Volume MSB + + f0 7f 7f 04 01 00 7f f7 + + + + +Fine control of all voice or performance parameters + +0xF0 0x43 0x1? 0x7F 0x17 ah am al dd ... 0xF7 + +0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) +ah: Address High +am: Address Mid +al: Address Low +dd: Parameter data (variable length) + + f0 43 10 f7 17 00 00 00 00 f7 + + + + +Bulk dump of all settings (Data List p. 57ff) + +0xF0 0x43 0x0? 0x7F 0x17 bh bl ah am al dd ... cc 0xF7 + +0x0?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) +bh: Byte Count MSB +bl: Byte Count LSB +ah: Address High +am: Address Mid +al: Address Low +dd: Parameter data (variable length) +cc: Checksum + + f0 43 00 7f 17 00 00 00 00 00 00 f7 + + + + +Request bulk dump (Data List p. 57ff) + +0xF0 0x43 0x2? 0x7F 0x17 ah am al 0xF7 + +0x2?: Device Number +ah: Address High +am: Address Mid +al: Address Low + + f0 43 20 7f 17 00 00 00 f7 + + + + +Request parameter value (Data List p. 57 left side) + +0xF0 0x43 0x3? 0x7F 0x17 ah am al 0xF7 + +0x3?: Device Number +ah: Address High +am: Address Mid +al: Address Low + + f0 43 30 7f 17 00 00 00 f7 + + + diff -Nru muse-2.1.2/share/instruments/Yamaha-P100.idf muse-3.0.2+ds1/share/instruments/Yamaha-P100.idf --- muse-2.1.2/share/instruments/Yamaha-P100.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-P100.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + diff -Nru muse-2.1.2/share/instruments/Yamaha-P50m.idf muse-3.0.2+ds1/share/instruments/Yamaha-P50m.idf --- muse-2.1.2/share/instruments/Yamaha-P50m.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-P50m.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - - + + diff -Nru muse-2.1.2/share/instruments/Yamaha-PSR275.idf muse-3.0.2+ds1/share/instruments/Yamaha-PSR275.idf --- muse-2.1.2/share/instruments/Yamaha-PSR275.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-PSR275.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Yamaha-PSR530.idf muse-3.0.2+ds1/share/instruments/Yamaha-PSR530.idf --- muse-2.1.2/share/instruments/Yamaha-PSR530.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-PSR530.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/instruments/Yamaha-Rex50.idf muse-3.0.2+ds1/share/instruments/Yamaha-Rex50.idf --- muse-2.1.2/share/instruments/Yamaha-Rex50.idf 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-Rex50.idf 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru muse-2.1.2/share/instruments/Yamaha-S30_S80.idf muse-3.0.2+ds1/share/instruments/Yamaha-S30_S80.idf --- muse-2.1.2/share/instruments/Yamaha-S30_S80.idf 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-S30_S80.idf 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,1864 @@ + + + + + + + + + 7e 7f 09 01 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dr BdVar5 + Dr SdPcBH + Dr SdWdH + Dr SdVar1 + Dr BdVar3 + Dr SdRoll + Dr SdBldH + Dr SdDry + Dr Stick + Dr BdVar4 + Dr SdPcBH + Dr BdVar1 + Dr BdPop + Dr C.Rim2 + Dr SdHvy + Pc Clap + Dr SdGM + Dr Tom2L + Dr HHCl5 + Dr Tom2L + Dr HHPdl2 + Dr Tom2L + Dr HHOpn + Dr Tom2H + Dr Tom2H + Dr Crash + Dr Tom2H + Dr Ride + Dr China + Dr Cup + Pc Tmborn + Dr Crash2 + Pc Cowbel + Dr Crash2 + Pc VbSlp + Dr Cup2 + Pc BongoH + Pc BongoL + Pc CngSlp + Pc CongaH + Pc CongaL + Pc TmblsH + Pc TmblsL + Pc AgogoL + Pc AgogoL + Pc Cabasa + Pc Maracs + Dr China + Dr Crash2 + Pc TrangL + Cp FngCym + Pc Claves + Pc Wdblck + Pc Wdblck + Cp Timpni + Cp Timpni + Pc TrangL + Pc TrangL + Pc Shaker + Pc JnglBl + Dr BdStd + Dr SdRim1 + Dr SdBldH + Dr SdHvy + Dr SdVar1 + Dr SdVar2 + Dr SdAmb1 + Dr SdFsnH + Dr SdAmb3 + Dr SdPclH + Dr BdHby + Dr BdHby + Dr BdLoJz + + + + + + + + Dr Crash + Dr BdLoJz + Pc CONG + Dr SdBldH + Pc CONG + Dr EuroBD + Dr T8BdBm + Dr T9BD2 + Dr T9BD3 + Dr T9Rim + Dr T9SD2 + Dr T8Rmst + Dr T9SD5 + Dr T9TomL + Dr T9HHC1 + Dr T9TomM + Dr T9HHC2 + Dr T9TomH + Dr T9HHO1 + Dr T9TomH + Dr T9HHO2 + Dr T9Crsh + Dr T9Ride + Dr T9Clap + Pc AnvlPp + Dr T9BD1 + Dr SdPcBH + Pc WdBlk + Pc AnvlPp + Pc CONG + Pc AnvlHt + Pc StlClp + Pc Ripper + Pc CONG + Dr T8CngH + Dr T8CngH + Dr T8CngH + Dr T8CngH + Dr T8Crsh + Dr T8HHCl + Dr T8HHOp + Pc TrangL + Dr T8Mrcs + Pc TrangL + Pc Shaker + Pc AnvlHt + Pc AnvlHt + Dr T8Clve + Pc VoxDrm + Dr DstKik + Dr T8Crsh + Pc JnglBl + Pc Tmborn + Pc Tmborn + Fx BigSyn + Fx BigSyn + Fx Melow + Fx VxAlk + Fx VxAlk + Fx VxAlk + Dr T8SD1 + Pc Whistl + Dr SdPclH + Dr T8BdBm + + + + + + + + Dr DstKik + Dr BdVar5 + Dr BdVar4 + Dr T8BdBm + Dr T9BD1 + Dr SdBldH + Dr SdPclH + Dr SdPrmH + Dr T8SD1 + Dr SdBkBt + Dr SdBrsL + Dr T8BdBm + Dr BdAna + Dr C.Rim2 + Dr SdRim1 + Dr T8Clap + Dr SdPcBH + Dr TomF + Dr HHCL2 + Dr TomF + Dr T8HHCl + Dr TomL + Dr HHOpn + Dr TomM + Dr TomHL + Dr Crash + Dr TomHH + Dr Ride2 + Dr China + Dr Cup2 + Pc Tmborn + Dr Crash2 + Pc CONG + Dr Crash2 + Pc VbSlp + Dr Ride + Fx AHoseL + Fx AHoseR + Pc CngSlp + Pc CongaH + Pc CongaL + Pc TmblsH + Pc TmblsL + Pc AnvlPp + Pc AnvlPp + Pc Shaker + Pc Maracs + Pc Scrch1 + Dr T8Bd + Pc StlClp + Dr SdFsnH + Dr T9Clap + Dr SdVar2 + Pc JnglBl + Dr T9HHC1 + Pc TrangL + Dr T9HHC2 + Pc TrangL + Dr T9HHO1 + Fx HiLo + Fx Eery + Dr T9Crsh + Dr China + Fx VoxBel + Fx Melow + Wv MogBs1 + Pc Tmborn + Pc Scrch2 + Dr T9Ride + Fx Eery + FX HH Hit + Fx TrnCho + Fx TrnCho + + + + + + + + Dr T8BdBm + Dr T8CngM + Dr T8Bd + Dr T8CngH + Dr T9BD2 + Dr T9BD2 + Pc CONG + Dr T9SD1 + Pc CONG + Dr EuroBD + Dr EuroBD + Dr T9BD2 + Dr T9BD3 + Dr T9Rim + Dr T9SD1 + Dr DstKik + Dr T9SD4 + Dr T9TomL + Dr T9HHC1 + Dr T9TomM + Dr T9HHC2 + Dr T9TomH + Dr T9HHO1 + Dr T9TomH + Dr T9HHO2 + Dr T9Crsh + Dr T9Ride + Dr T9Clap + Pc AnvlPp + Dr T9BD1 + Dr SdPcBH + Pc WdBlk + Pc AnvlPp + Pc Ripper + Pc AnvlHt + Pc StlClp + Dr T8BdBm + Pc CONG + Dr T8CngH + Dr T8CngH + Dr T8CngH + Dr T8CngH + Dr T8Crsh + Dr T8HHCl + Dr T8HHOp + Pc TrangL + Dr T8Mrcs + Pc TrangL + Fx WndChm + Fx BelAir + Fx BigSyn + Fx Electr + FX HH Hit + Fx VxAlk + Fx VxAlk + Fx HiLo + Fx HiLo + Fx VoxBel + Fx VoxBel + Fx Eery + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + Wv FMBs1 + + + + + + + + Dr T8Bd + Dr BdVar4 + Pc CongaL + Dr BdAna + Dr T9BD3 + Dr SdAmb2 + Dr SdRim2 + Dr T8SD1 + Dr T8SD3 + Dr SdBrsH + Dr SdAmb3 + Dr EuroBD + Dr T9BD3 + Fx HiLo + Dr T9SD4 + Dr T8Clap + Pc WdBlk + Wv Noise + Dr HHCl5 + Wv Noise + Dr T9HHC1 + Wv Noise + Dr T9HHO1 + Dr Tom2L + Dr Tom2H + Dr T9Crsh + Dr Tom2H + Fx Eery + Dr China + Dr Cup2 + Pc Tmborn + Dr Crash + Wv MogBs1 + Dr Crash2 + Pc VbSlp + Dr Ride + Fx VxAlk + FX HH Hit + Pc TmblsH + Pc CongaH + Pc CongaL + Fx BelAir + Fx Electr + Pc AnvlPp + Pc AnvlPp + Pc Shaker + Pc Maracs + Pc Bltree + Dr T8Bd + Fx VoxBel + Dr SdAmb2 + Dr T9Clap + Dr SdRim1 + Fx MltPno + Dr T8HHCl + Pc AnvlHt + Dr T9HHC2 + Pc AnvlHt + Dr T8HHOp + Fx Eery + Fx Eery + Dr T9Crsh + Dr China + Fx VoxBel + Dr T8Crsh + En VxNoiz + Pc Tmborn + Pc Ripper + Dr T9Ride + Dr T8CngL + Dr T8CngM + Dr TmJzH + Dr T8HHOp + + + + + + + + Pc Claves + Dr SdBrsL + Dr SdSwep + Dr SdBrsH + Dr SdSwep + Dr SdRoll + Pc Castnt + Dr SdBrsL + Dr Stick + Dr BdVar3 + Dr SdBrsH + Dr BdVar4 + Dr BdLoJz + Dr SdBrsH + Dr SdBrsL + Pc Clap + Dr SdBrsS + Dr TmJzL + Dr HHCl4 + Dr TmJzL + Dr HHPdl + Dr TmJzL + Dr HHOpn + Dr TmJzM + Dr TmJzH + Dr Crash + Dr TmJzH + Dr Ride + Dr Ride2 + Dr Cup + Pc Tmborn + Dr Crash2 + Pc Cowbel + Dr Crash2 + Dr Ride + Dr Cup2 + Pc BongoH + Pc BongoL + Pc CngSlp + Pc CongaH + Pc CongaL + Pc TmblsH + Pc TmblsL + Pc AgogoL + Pc AgogoL + Pc Cabasa + Pc Maracs + Pc Whistl + Pc Whistl + Pc GiroCl + Pc GuiroL + Pc Claves + Pc Wdblck + Pc Wdblck + Pc CuicaL + Pc CuicaH + Pc TrangL + Pc TrangL + Pc Shaker + Pc JnglBl + Pc Bltree + + + + + + + + Pc Claves + Dr SdBrsL + Dr SdSwep + Dr SdBrsH + Dr SdSwep + Dr SdRoll + Pc Castnt + Dr SdDry + Dr Stick + Dr BdVar3 + Dr SdPcBH + Dr BdVar4 + Dr BdHby + Dr C.Rim1 + Dr SdGM + Pc Clap + Dr SdGM + Dr TomL + Dr HHCl5 + Dr TomL + Dr HHPdl2 + Dr TomM + Dr HHOpn2 + Dr TomM + Dr TomHL + Dr Crash + Dr TomHH + Dr Ride + Dr China + Dr Cup + Pc Tmborn + Dr Crash2 + Pc Cowbel + Dr Crash2 + Pc VbSlp + Dr Cup2 + Pc BongoH + Pc BongoL + Pc CngSlp + Pc CongaH + Pc CongaL + Pc TmblsH + Pc TmblsL + Pc AgogoL + Pc AgogoL + Pc Cabasa + Pc Maracs + Pc Whistl + Pc Whistl + Pc GiroCl + Pc GuiroL + Pc Claves + Pc Wdblck + Pc Wdblck + Pc CuicaL + Pc CuicaH + Pc TrangL + Pc TrangL + Pc Shaker + Pc JnglBl + Pc Bltree + Dr SdBkBt + Dr SdBldS + Dr SdVar1 + Dr SdSonD + Dr SdRim1 + Dr SdWdH + Dr SdBldH + Dr SdPcBH + Dr SdLdwH + Dr BdPop + Dr BdHby + Dr BdVar5 + + + + + + + + Pc Claves + Dr SdBrsL + Dr SdSwep + Dr SdBrsH + Dr SdSwep + Dr SdRoll + Pc Castnt + Dr T8SD4 + Dr T8Rmst + Dr BdAna + Dr T8SD3 + Dr T8BdBm + Dr T8Bd + Dr T8Side + Dr T8SD1 + Dr T8Clap + Dr T8SD2 + Dr T8Tom + Dr T8HHCl + Dr T8Tom + Dr T8HHOp + Dr T8Tom + Dr T8HHOp + Dr T8Tom + Dr T8Tom + Dr T8Crsh + Dr T8Tom + Dr T8Crsh + Dr China + Dr Cup + Pc Tmborn + Dr Crash2 + Dr T8Cwbl + Dr Crash2 + Pc VbSlp + Dr Ride2 + Pc BongoH + Pc BongoL + Dr T8CngH + Dr T8CngM + Dr T8CngL + Pc TmblsH + Pc TmblsL + Pc AgogoL + Pc AgogoL + Pc Cabasa + Dr T8Mrcs + Pc Whistl + Pc Whistl + Pc GiroCl + Pc GuiroL + Dr T8Clve + Pc Wdblck + Pc Wdblck + Pc CuicaL + Pc CuicaH + Pc TrangL + Pc TrangL + Pc Shaker + Pc JnglBl + Pc Bltree + + + + + + + + Dr T8BdBm + Dr T8SD2 + Dr T8SD1 + Dr T8SD3 + Dr T8SD4 + Dr T9SD4 + FX HH Hit + Dr T9SD2 + Dr T8Rmst + Dr T8Bd + Dr T9SD5 + Dr T9BD3 + Dr T9BD1 + Dr C.Rim1 + Dr T9SD1 + Dr T9Clap + Dr T9SD3 + Dr T9TomL + Dr T9HHC2 + Dr T9TomL + Dr T9HHO2 + Dr T9TomM + Dr T9HHO1 + Dr T9TomM + Dr T9TomH + Dr T9Crsh + Dr T9TomH + Dr T9Ride + Dr T8Crsh + Dr T9Ride + Pc Tmborn + Dr T8Crsh + Dr T8Cwbl + Dr T9Crsh + Dr DstKik + Dr T8Crsh + Dr T8CngH + Dr T8CngL + Dr T8CngM + Wv MogBs1 + Wv Late-S + Dr SdBkBt + Dr SdBkBt + Dr T8Cwbl + Pc AgogoL + Pc Cabasa + Dr T8Mrcs + Fx HiLo + Fx HiLo + Pc AnvlHt + Pc Ripper + Dr T8Clve + Dr T8Clve + Pc Castnt + Pc Scrch1 + Pc Scrch2 + Pc TrangL + Pc TrangL + Pc Shaker + Pc JnglBl + Pc Bltree + Dr SdRim1 + Dr SdPclH + Pc Clap + Dr SdPrmH + Dr BdAna + Dr T8HHCl + Dr T9BD2 + Dr HHCl5 + Dr BdVar1 + Dr T8HHOp + Dr EuroBD + Dr BdHby + + + + + + + + Dr BdVar5 + Dr SdBrsL + Dr SdSwep + Dr SdBrsH + Dr SdSwep + Dr SdRoll + Wv Noise + Dr SdSonA + Dr Stick + Dr BdVar5 + Dr SdPcBH + Dr BdVar2 + Dr BdLoJz + Dr C.Rim2 + Dr SdGM + Pc Clap + Dr SdWdH + Dr TomF + Dr HHCl1 + Dr TomL + Dr HHPdl + Dr TomL + Dr HHOpn + Dr TomM + Dr TomHL + Dr Crash + Dr TomHH + Dr Ride + Dr China + Dr Cup + Pc Tmborn + Dr Crash2 + Dr T8Cwbl + Dr Crash2 + Pc VbSlp + Dr T8Crsh + Pc BongoH + Pc BongoL + Pc CngSlp + Pc CongaH + Pc CongaL + Pc TmblsH + Pc TmblsL + Pc AgogoL + Pc AgogoL + Pc Cabasa + Pc Maracs + Pc Whistl + Pc Whistl + Pc GiroCl + Pc GuiroL + Pc Claves + Pc Wdblck + Pc Wdblck + Pc CuicaL + Pc CuicaH + Pc TrangL + Pc TrangL + Pc Shaker + Pc JnglBl + Pc Bltree + Dr DstKik + Dr DstKik + Dr DstKik + Dr DstKik + Dr DstKik + Dr DstKik + Dr DstKik + Dr DstKik + Dr DstKik + Dr DstKik + Dr DstKik + Dr DstKik + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Switch General Midi mode on + +Received only when SYSTEM Receive GM/XG on is set to on in PERFORMANCE MODE. +The Part values will be reset according to the SYSTEM Internal Part settings. + + f0 7e 7f 09 01 f7 + + + + +Switch XG mode on + +Received only when SYSTEM Receive GM/XG on is set to on in PERFORMANCE MODE. +The Part values will be reset according to the SYSTEM Internal Part settings. +Takes about 170ms to execute. + + f0 43 10 4c 00 00 7e 00 f7 + + + + +Request Device Number Code from the instrument. The instrument answers +with a "Identity Reply" and returns the Device ID 0x23 0x04. + +0xF0 0x7E 0x0? 0x06 0x01 0xF7 + +0x0?: Device number (any number) + + f0 7e 00 06 01 f7 + + + + +Reply message to an "Identity Request" + +0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 + +dd dd: Device Number Code (always 0x23 0x04) + + f0 7e 7f 06 02 43 00 41 23 04 00 00 00 7f f7 + + + + +Overall volume of all parts + +0xF0 0x7F 0x7F 0x04 0x01 ll mm 0xF7 + +ll: Volume LSB (Ignored) +mm: Volume MSB + + f0 7f 7f 04 01 00 7f f7 + + + + +Remotely activate or deactivate panel buttons + +0xF0 0x43 0x1? 0x64 ah am al dd ... 0xF7 + +0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) +ah: Address High +am: Address Mid +al: Address Low +dd: Parameter data (variable length) + + f0 43 10 64 00 00 00 00 f7 + + + + +Bulk dump of all settings (Data List p. 29ff) + +0xF0 0x43 0x0? 0x64 bh bl ah am al dd ... cc 0xF7 + +0x0?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) +bh: Byte Count MSB +bl: Byte Count LSB +ah: Address High +am: Address Mid +al: Address Low +dd: Parameter data (variable length) +cc: Checksum + + f0 43 00 64 00 00 00 00 00 00 00 f7 + + + + +Request bulk dump (Data List p. 29ff) + +0xF0 0x43 0x2? 0x64 ah am al 0xF7 + +0x2?: Device Number +ah: Address High +am: Address Mid +al: Address Low + + f0 43 20 64 00 00 00 f7 + + + + +Request parameter value (Data List p. 29ff) + +0xF0 0x43 0x3? 0x64 ah am al 0xF7 + +0x3?: Device Number +ah: Address High +am: Address Mid +al: Address Low + + f0 43 30 64 00 00 00 f7 + + + diff -Nru muse-2.1.2/share/instruments/Yamaha-S90.idf muse-3.0.2+ds1/share/instruments/Yamaha-S90.idf --- muse-2.1.2/share/instruments/Yamaha-S90.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/Yamaha-S90.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + Master number 0 select diff -Nru muse-2.1.2/share/instruments/yam_mo6_v4.idf muse-3.0.2+ds1/share/instruments/yam_mo6_v4.idf --- muse-2.1.2/share/instruments/yam_mo6_v4.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/yam_mo6_v4.idf 2017-12-04 21:01:19.000000000 +0000 @@ -14,7 +14,7 @@ - + diff -Nru muse-2.1.2/share/instruments/ZynAdd-1_4.idf muse-3.0.2+ds1/share/instruments/ZynAdd-1_4.idf --- muse-2.1.2/share/instruments/ZynAdd-1_4.idf 2013-03-28 15:15:57.000000000 +0000 +++ muse-3.0.2+ds1/share/instruments/ZynAdd-1_4.idf 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + diff -Nru muse-2.1.2/share/locale/CMakeLists.txt muse-3.0.2+ds1/share/locale/CMakeLists.txt --- muse-2.1.2/share/locale/CMakeLists.txt 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/CMakeLists.txt 2017-12-04 21:01:19.000000000 +0000 @@ -24,9 +24,9 @@ file (GLOB TRANSLATIONS_FILES *.ts) if (UPDATE_TRANSLATIONS) - qt4_create_translation(QM_FILES ${FILES_TO_TRANSLATE} ${TRANSLATIONS_FILES}) + qt5_create_translation(QM_FILES ${FILES_TO_TRANSLATE} ${TRANSLATIONS_FILES}) else (UPDATE_TRANSLATIONS) - qt4_add_translation(QM_FILES ${TRANSLATIONS_FILES}) + QT5_ADD_TRANSLATION(QM_FILES ${TRANSLATIONS_FILES}) endif (UPDATE_TRANSLATIONS) add_custom_target (translations ALL DEPENDS ${QM_FILES}) diff -Nru muse-2.1.2/share/locale/muse_cs.ts muse-3.0.2+ds1/share/locale/muse_cs.ts --- muse-2.1.2/share/locale/muse_cs.ts 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/muse_cs.ts 2017-12-04 21:01:19.000000000 +0000 @@ -4,108 +4,103 @@ @default - Add Midi Track VytvoÅ™it stopu MIDI - - Add Drum Track VytvoÅ™it stopu bicích - Add Old Style Drum Track VytvoÅ™it stopu bicích ve starém stylu - Add New Style Drum Track VytvoÅ™it stopu bicích v novém stylu - Add Wave Track VytvoÅ™it stopu Wave - Add Audio Output VytvoÅ™it výstup zvuku - Add Audio Group VytvoÅ™it skupinu zvuku - Add Audio Input VytvoÅ™it vstup zvuku - Add Aux Send VytvoÅ™it Aux Send - Add Synth PÅ™idat syntetizátor - Select project directory Vybrat projektový adresář - Warning: No output devices! Varování: Žádná výstupní zařízení! - Open midi config... Otevřít nastavení MIDI... - + Output port/device + Výstupní přípojka/zařízení + + + default + Výchozí + + Empty ports Prázdné přípojky - + <none> <Žádný> - + Route Tok signálu - + + channel="%1" + kanál="%1" + + channelMask="%1" KanalMaske="%1" - + dest Cíl - + name="%1"/ Název="%1" - Bad timing - Å patné naÄasování + Å patné naÄasování - Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. @@ -152,30 +147,68 @@ ZveÅ™ejnÄ›no pod GNU Public License - Version 2 - Verze 2 + Verze 2 - (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License - (C) Autorské právo 1999-2012 Werner Schweer a další. + (C) Autorské právo 1999-2012 Werner Schweer a další. +Podívejte se na stránky http://www.muse-sequencer.org na nové verze a +zde hledejte další informace. + +ZveÅ™ejnÄ›no pod GNU Public License + + + (C) Copyright 1999-2014 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Autorské právo 1999-2014 Werner Schweer a další. Podívejte se na stránky http://www.muse-sequencer.org na nové verze a zde hledejte další informace. ZveÅ™ejnÄ›no pod GNU Public License - + + Version info (replaced programmatically) + Informace o verzi + + + + (C) Copyright 1999-2015 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Autorské právo 1999-2017 Werner Schweer a další. +Podívejte se na stránky http://www.muse-sequencer.org na nové verze a +zde hledejte další informace. + +ZveÅ™ejnÄ›no pod GNU Public License {1999-2015 ?} + + + + System information: + Informace o systému: + + + + TextLabel + Textový Å¡títek + + + &Keep On Rocking! &Zůstávejte ve spojení! - + Alt+K Alt+K @@ -263,242 +296,261 @@ Obrázek pozadí - + add PÅ™idat - + remove Odstranit - + clear Smazat - + show snap grid Ukázat magnetickou mřížku - + Colors Barvy - + Items Položky - + Palette Paleta - + add to palette PÅ™idat do palety - + B B - + S S - + H H - + V V - + G G - + R R - + Color name: Název barvy: - + Global opacity Celková neprůhlednost - + Style/Fonts Styl a písmo - QT Theme - Podoba Qt + Podoba Qt - + Windows Windows - + MusE MusE - + Metal Kov - + Norwegian Wood Norské dÅ™evo - + Platinum Platina - + CDE CDE - + Motif Motif - + Motif Plus Motif Plus - + May require restarting MusE for best results Může kvůli nejlepšímu výsledku vyžadovat opÄ›tovné spuÅ¡tÄ›ní MusE - + Style Sheet: Styl: - - - - - - - - - + + + + + + + + + ... ... - + + MusE color scheme + Schéma barev MusE + + + + current settings + NynÄ›jší nastavení + + + + Change + ZmÄ›nit + + + Fonts Písma - + Family Rodina písma - + Size Velikost - + Font 1 Písmo 1 - + Font 2 Písmo 2 - + Font 3 Písmo 3 - + Font 0 Písmo 0 - - - - - - - + + + + + + + Bold TuÄné - - - - - - - + + + + + + + Italic Kurzíva - + Font 4 Písmo 4 - + Font 5 Písmo 5 - + + Themes + Motivy + + + Font 6 Písmo 6 - + Apply Použít - + Ok OK - + Cancel ZruÅ¡it @@ -532,100 +584,92 @@ Typ ovladaÄe MIDI - Control7 - OvladaÄ 7 + OvladaÄ 7 - Control14 - OvladaÄ 14 + OvladaÄ 14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - RPN14 - RPN14 + RPN14 - NRPN14 - NRPN14 + NRPN14 - Pitch - Výška tónu + Výška tónu - Program - Program + Program - + H-Ctrl H-Ctrl - - + + Midi controller number high byte Vysoké bajtové Äíslo ovladaÄe MIDI - + L-Ctrl L-Ctrl - - + + Midi controller number low byte Nízké bajtové Äíslo ovladaÄe MIDI - + * wild card * - + affect CCs at Ovlivnit CCs pÅ™i - + begin of song ZaÄátek písnÄ› - + current position NynÄ›jší poloha - + &Add &PÅ™idat - + &Delete S&mazat - + Done Hotovo @@ -652,6 +696,40 @@ + ChooseSysexBase + + + Dialog + Dialog + + + + Hex: + Å estnáctkový: + + + + + TextLabel + Textový Å¡títek + + + + Comment: + Poznámka: + + + + &OK + &OK + + + + &Cancel + Z&ruÅ¡it + + + ClipListEditorBase @@ -731,116 +809,198 @@ + Default instrument: + Výchozí nástroj: + + + + Device Name metas trump Port metas if both exist + Názvy zařízení pÅ™etrumfnou Přípojky, pokud jsou oba + + + + Instrument Name metas trump Mode sysexes if both exist + Názvy nástrojů pÅ™etrumfnou Režimy SysEx, pokud jsou oba + + + Split tracks into parts, or one single part RozdÄ›lit stopy na Äásti, nebo jednu jedinou Äást - + Split tracks into &parts RozdÄ›lit stopy na Äá&sti - + Alt+P Alt+P - + Use new-style drum tracks Použít stopy bicích v novém stylu - + Use old-style drum tracks Použít stopy bicích ve starém stylu - + Export: Vyvést: - + 96 96 - + 192 192 - + 384 384 - + Enable extended smf format (currently not implemented) Povolit rozšířený formát smf (v souÄasnosti neprovedeno) - + Use &2-byte time signatures instead of standard 4 Použít &2-bajtová taktová oznaÄení namísto obvyklých 4 - + Alt+2 Alt+2 - + + Mode sysexes + Režim SysEx + + + + Instrument name metas + Název nástroje + + + + + Both + Oba + + + + Port metas + Přípojka + + + + Device name metas + Název zařízení + + + + Export a Port/Device meta for format 0 + Vyvedení a přípojka/zařízení pro formát 0 + + + Copyright: Autorské právo: - + Format: Formát: - + Note: Format 0 uses the FIRST midi track's name/comment in the arranger Poznámka: Formát 0 používá název/poznámku PRVNà stopy MIDI v aranžéru - + Division: RozdÄ›lení: - + Save space by replacing note-offs with &zero velocity note-ons - UÅ¡etÅ™it místo nahrazením vypnutí not zapnutím not o &nulové síle nárazu + UÅ¡etÅ™it místo nahrazením vypnutí not zapnutím not o &nulové síle tónu - + Alt+Z Alt+Z - + 0 (single track) 0 (jedna stopa) - + 1 (multiple tracks) 1 (více stop) - + &OK &OK - + &Cancel Z&ruÅ¡it + CopyOnWriteDialogBase + + + Copy Wave Files + Kopírovat soubor WAV + + + + Some sound files will be copied to the Project Directory, +either because they are not writable or because more +than one independent Wave Event shares them. +(If you prefer instead that the Wave Events are + inter-dependent, try using Clone Parts.) + +Multiple copies will be made in some cases. + +If no Project has been created yet, you will be asked to, +giving another chance to cancel. + Tyto soubory budou zkopírovány do adresáře s projektem, +buÄ protože nejsou zapisovatelné nebo protože je sdílí více +než jedna událost Wave Event. +(Pokud místo toho dáváte pÅ™ednost, aby byly události Wave + mezi-závislé, vyzkouÅ¡ejte klonování Äástí.) + +V nÄ›kterých případech bude udÄ›láno více kopií. + +Pokud jeÅ¡tÄ› nebyl vytvoÅ™en žádný projekt, budete dotázán, +dostanete další možnost k zruÅ¡ení. + + + + These files will be copied to the Project Directory: + Tyto soubory budou zkopírovány do adresáře s projektem: + + + CrescendoBase @@ -870,7 +1030,7 @@ Start velocity - PoÄáteÄní síla nárazu + PoÄáteÄní síla tónu @@ -881,7 +1041,7 @@ End velocity - Koncová síla nárazu + Koncová síla tónu @@ -914,314 +1074,314 @@ - - - - - - - - + + + + + + + + Critical Error Vážná chyba - - - - + + + + Cannot open file %1 Nelze otevřít soubor %1 - - - - + + + + Parsing error for file %1 Chyba pÅ™i zpracování u souboru %1 - + Load category dialog Nahrát dialog skupiny - + Load set dialog Nahrát dialog nastavení - + Save set dialog Uložit dialog nastavení - + New category Nová skupina - - + + Delete category Smazat skupinu - + Load category Nahrát skupinu - + Save category Uložit skupinu - + Load set Nahrát nastavení - + Save set Uložit nastavení - + Delete set Smazat nastavení - + New subcategory Nová podskupina - - + + Delete subcategory Smazat podskupinu - + Load subcategory Nahrát podskupinu - + Save subcategory Uložit podskupinu - + New preset Nové pÅ™ednastavení - - + + Delete preset Smazat pÅ™ednastavení - + Load preset Nahrát pÅ™ednastavení - + Save preset Uložit pÅ™ednastavení - + No more category supported Žádná další skupina není podporována - + You can not add more categories Nelze pÅ™idat více skupin - - - + + + Do you really want to delete %1 ? Opravdu chcete smazat %1? - - - + + + &Yes &Ano - - - + + + &No &Ne - - + + No category selected Nevybrána žádná skupina - - + + You must first select a category. Nejprve musíte vybrat skupinu. - - - + + + Replace or add Nahradit nebo pÅ™idat - + %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? PÅ™edpokládá se, že %1 bude ovlivnÄ›n k vysoké bance Äíslo %2, ale na tomto místÄ› už jedna je. Opravdu to chcete nahradit nebo to pÅ™idat do dalšího volného místa? - - - + + + &Replace &Nahradit - - - + + + &Add &PÅ™idat - - - + + + Download error Chyba pÅ™i stahování - + There is no more free category slot. Není žádné další volné místo pro skupinu. - + Save category dialog Uložit dialog skupiny - + No more subcategory supported Žádná další podskupina není podporována - + You can not add more subcategories Nelze pÅ™idat více podskupin - - + + No subcategory selected Nevybrána žádná podskupina - - + + You must first select a subcategory. Nejprve musíte vybrat podskupinu. - + Load subcategory dialog Nahrát dialog podskupiny - + %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? PÅ™edpokládá se, že %1 bude ovlivnÄ›n k nízké bance Äíslo %2, ale na tomto místÄ› už jedna je. Opravdu to chcete nahradit nebo to pÅ™idat do dalšího volného místa? - + There is no more free subcategory slot. Není žádné další volné místo pro podskupinu. - + Save subcategory dialog Uložit dialog podskupiny - + No more preset supported Žádné další pÅ™ednastavení není podporováno - + You can not add more presets Nelze pÅ™idat více pÅ™ednastavení - - - + + + No preset selected Nevybráno žádné pÅ™ednastavení - - - + + + You must first select a preset. Nejprve musíte vybrat pÅ™ednastavení. - + Load preset dialog Nahrát dialog pÅ™ednastavení - + %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? PÅ™edpokládá se, že %1 bude ovlivnÄ›n k programu Äíslo %2, ale na tomto místÄ› už jedna je. Opravdu to chcete nahradit nebo to pÅ™idat do dalšího volného místa? - + There is no more free preset slot. Není žádné další volné místo pro pÅ™ednastavení. - + Save preset dialog Uložit dialog pÅ™ednastavení - + Browse set dialog Procházet dialog nastavení - + Browse image dialog Procházet dialog obrázku @@ -1578,7 +1738,7 @@ Release Rate Rychlost uvolnÄ›ní - + @@ -1586,7 +1746,7 @@ 2° Decay Rate 2° Rychlost zpoždÄ›ní - + @@ -1594,7 +1754,7 @@ 1° Decay Level 1° Úroveň zpoždÄ›ní - + @@ -1737,7 +1897,7 @@ Keyboard Velocity Sensitivity - Citlivost síly úhozu do klávesnice + Citlivost síly tónu klávesnice @@ -1745,7 +1905,7 @@ Key Velocity Sensitivity - Citlivost síly nárazu not + Citlivost síly tónu not @@ -2384,17 +2544,17 @@ &Reverb - &OzvÄ›na + &Dozvuk Reverb Parameters - Parametry ozvÄ›ny + Parametry dozvuku &Config - &Nastavit + &Nastavení @@ -2665,17 +2825,17 @@ Víte, že? - + Don't show on startup Neukazovat pÅ™i spuÅ¡tÄ›ní - + Next tip Další rada - + Close Zavřít @@ -2719,26 +2879,30 @@ + Copy standard (vol, pan) and synth controllers + Kopírovat standardní (hlasitost, vyvážení) a syntetizátorové ovladaÄe + + Copy standard controllers (vol, pan) - Kopírovat standardní ovladaÄe (hlasitost, vyvážení) + Kopírovat standardní ovladaÄe (hlasitost, vyvážení) - + Copy effects rack plugins Kopírovat přídavné moduly pÅ™ihrádky efektů - + Copy plugin controllers Kopírovat ovladaÄe přídavných modulů - + Ok OK - + Cancel ZruÅ¡it @@ -2746,73 +2910,78 @@ EditCtrlBase - + MusE: Edit Controller Event MusE: Upravit událost ovladaÄe - + Time Position ÄŒasová poloha - + Available Controller: Dostupný ovladaÄ: - + Create New Controller VytvoÅ™it nový ovladaÄ - + textLabel3 Popis 3 - + Value Hodnota - + Controller OvladaÄ - + + Note + Nota + + + H-Bank Vysoká banka - + L-Bank Nízká banka - + Program Program - - + + off Vypnuto - + pushButton4 TlaÄítko 4 - + &OK &OK - + &Cancel Z&ruÅ¡it @@ -2914,14 +3083,14 @@ - + Properties Vlastnosti - - + + Name: Název: @@ -2984,248 +3153,251 @@ Záplatovat Äíslo programu - Drum patch - Záplata bicích + Záplata bicích - If set, the patch is available only for drum channels. - Je-li nastaveno, je záplata dostupná pouze pro kanály bicích. + Je-li nastaveno, je záplata dostupná pouze pro kanály bicích. - + + + Drum Bicí - + GM patch Záplata GM - + If set, the patch is available in a 'GM' or 'NO' midi song type. Je-li nastaveno, je záplata dostupná v typu písnÄ› MIDI 'GM' nebo 'NO'. - + GM GM - + GS patch Záplata GS - + If set, the patch is available in a 'GS' or 'NO' midi song type. Je-li nastaveno, je záplata dostupná v typu písnÄ› MIDI 'GS' nebo 'NO'. - + GS GS - + XG patch Záplata XG - + If set, the patch is available in an 'XG' or 'NO' midi song type. Je-li nastaveno, je záplata dostupná v typu písnÄ› MIDI 'XG' nebo 'NO'. - + XG XG - + + + Show in tracks: + Ukázat ve stopách: + + + + + + Midi + MIDI + + + Delete group or patch Smazat skupinu nebo záplatu - - - + + + + &Delete S&mazat - - - + + + Alt+D Alt+D - + New patch Nová záplata - + New &Patch Nová &záplata - + Alt+P Alt+P - + New group Nová skupina - + New &Group Nová &skupina - + Alt+G Alt+G - Contro&ller - &OvladaÄ + &OvladaÄ - Common: - Obecné: + Obecné: - This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. - Toto je seznam Äasto používaných ovladaÄů MIDI. + Toto je seznam Äasto používaných ovladaÄů MIDI. MusE ovÅ¡em zachází se vÅ¡emi ovladaÄi jako jsou "výška tónu" a "zmÄ›ny programů" jako s normálními ovladaÄi. - + List of defined controllers Seznam vymezených ovladaÄů - + List of defined controllers. Seznam vymezených ovladaÄů. - + Name Název - + Type Typ - - + + H-Ctrl H-Ctrl - - + + L-Ctrl L-Ctrl - + Min NejménÄ› - + Max Nejvíce - + Def Vymezení - + Midi controller name Název ovladaÄe MIDI - + Type: Typ: - - + + Midi controller type Typ ovladaÄe MIDI - Control7 - OvladaÄ 7 + OvladaÄ 7 - Control14 - OvladaÄ 14 + OvladaÄ 14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - RPN14 - RPN14 + RPN14 - NRPN14 - NRPN14 + NRPN14 - Pitch - Výška tónu + Výška tónu - Program - Program + Program - - + + Midi controller number high byte Vysoké bajtové Äíslo ovladaÄe MIDI - + Midi controller number low byte (* means drum controller) Nízké bajtové Äíslo ovladaÄe MIDI (* znamená ovladaÄ bicích) - + Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and @@ -3244,28 +3416,28 @@ OvladaÄe bících. - + * wild card * - + Range: Rozsah: - + Min NejménÄ› - + Minimum value. If negative, auto-translate. Nejmenší hodnota. Pokud je záporná, automatická promÄ›na. - + Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. @@ -3305,57 +3477,62 @@ Opravdový rozsah: Min: -8192 Max: 8191 (zkreslení 0) - + Max Nejvíce - - + + Maximum value NejvÄ›tší hodnota - + Default: Výchozí: - + L-Bank Nízká banka - - - - - + + + + + off Vypnuto - + Progr. Progr. - + ??? ??? - + H-Bank Vysoká banka - + + Drum&maps + &Rozložení bicích + + + Default value. Off: No default. Výchozí hodnota. Vypnuto: Žádná výchozí. - + Default (initial) value. Off means no default. If a default value is chosen, the value will be sent @@ -3374,60 +3551,58 @@ - + off dont care Vypnuto - + Add common controller PÅ™idat obecný ovladaÄ - - + &Add &PÅ™idat - + Alt+A Alt+A - + Delete controller Smazat ovladaÄ - + Create a new controller VytvoÅ™it nový ovladaÄ - + New &Controller Nový &ovladaÄ - + Alt+C Alt+C - Null Param Hi: - Nulový parametr vysoký: + Nulový parametr vysoký: - + Null parameter number High byte Nulový parametr vysoký bajt - - + + If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events @@ -3442,188 +3617,240 @@ Äíslo ovladaÄe RPN/NRPN. - + Lo: Nízký: - + Null parameter number Low byte Číslo nulového parametru nízký bajt - + S&ysEx S&ysEx - + SysEx List: Seznam SysEx: - + Hex Entry: Záznam Hex: - + New SysE&x Nový SysE&x - + Alt+X Alt+X - Drummaps - Rozložení bicích + Rozložení bicích - + Patch Collections: Sbírky záplat: - + &Copy &Kopírovat - + &Remove &Odstranit - + &Up &Nahoru - + &Down &Dolů - + Patch: Záplata: - - - + + + from od - - - + + + to do - + Bank Hi: Vysoká banka: - + Bank Lo: Nízká banka: - + + Contro&llers + &OvladaÄe + + + + Null Parameters: Hi: + Nulový parametr: vysoký: + + + + &Add Common... + &PÅ™idat obecný... + + + + W + W + + + + Comment: + Poznámka: + + + + &Initialization + &Inicializace + + + + Instrument initialization sequence: + InicializaÄní sekvence nástroje: + + + + &Add... + &PÅ™idat... + + + + &Change... + &ZmÄ›nit... + + + Tools Nástroje - + &File &Soubor - + &Help &NápovÄ›da - + &New &Nový - + New Nový - + Ctrl+N Ctrl+N - + &Open... &Otevřít... - + Open Otevřít - + Ctrl+O Ctrl+O - + &Save &Uložit - + Save Uložit - + Ctrl+S Ctrl+S - + Save &As... Uložit &jako... - + Save As Uložit jako - + + &Close + &Zavřít + + + + Close + Zavřít + + E&xit - &UkonÄit + &UkonÄit - Exit - UkonÄit + UkonÄit - - + + new item Nová položka - - + + What's this? Co je to? @@ -3685,18 +3912,36 @@ + Name: + Název: + + + Comment: Poznámka: - + + &Select... + &Vybrat... + + + + &OK + &OK + + + + &Cancel + Z&ruÅ¡it + + OK - OK + OK - Cancel - ZruÅ¡it + ZruÅ¡it @@ -3724,133 +3969,163 @@ FLUIDSynthGuiBase + FLUID Synth - FLUID Synth + FLUID Synth + Load - Nahrát + Nahrát + Delete - Smazat + Smazat + Dump Info - Vyklopit informace + Vypsat informace + ID - ID + ID + Fontname - Název písma + Název písma + Chnl - Kanál + Kanál + Soundfont - Zvukové písmo + Zvuková banka + Drum Chnl - Kanál bicích + Kanál bicích + + Level - Úroveň + Úroveň + Width - Šířka + Šířka + Damping - Vyklopení + Tlumení + Room Size - Velikost prostoru + Velikost prostoru + Reverb - OzvÄ›na + Dozvuk + CHANNEL SETUP - NASTAVENà KANÃLU + NASTAVENà KANÃLU + Sine - Sinus + Sinus + Triangle - Trojúhelník + Trojúhelník + Type - Typ + Typ + Number - PoÄet + PoÄet + Speed - Rychlost + Rychlost + Depth - Hloubka + Hloubka + Chorus - Sbor + Sbor + Gain - Zesílení + Zesílení + LOADED SOUNDFONTS - NAHRANà ZVUKOVà PÃSMA + NAHRANÉ ZVUKOVÉ BANKY FileDialogButtons - + fdialogbuttons TlaÄítkaProSouborovýDialog - + + Home + Domovská složka + + + Global Celkové - + User Uživatel - + Project Projekt - + read Midi Port Configuration PÅ™eÄíst nastavení přípojky MIDI - + write window states Zapisovat stavy @@ -3860,8 +4135,9 @@ FluidSynthGui + Choose soundfont - Vybrat zvukové písmo + Vybrat zvukovou banku @@ -3945,244 +4221,266 @@ Program - + Project directory Projektový adresář - + Projects: Projekty: - - - + + + ... ... - + + start &with last song + ZaÄít s &poslední písní + + + + start with &template + ZaÄít s &pÅ™edlohou + + + + sta&rt with song + ZaÄít s &urÄitou písní + + + Views Pohledy - - - - - + + + + + y-pos Poloha y - - - - + + + + show Ukázat - - - - - + + + + + x-pos Poloha x - - - - + + + + height Výška - - - - + + + + width Šířka - + Mixer A SměšovaÄ A - + Mixer B SměšovaÄ B - + Big Time Velký ukazatel Äasu - + Main Window Hlavní okno - + Transport - PÅ™esun + PÅ™ehrávání - - - - - + + + + + set current values Nastavit nynÄ›jší hodnoty - - - - - + + + + + Cur Nyní - + Start Muse Spustit MusE - + Choose start song or template Vybrat zaÄáteÄní píseň nebo pÅ™edlohu - + Reset to default Nastavit znovu na výchozí - + Start song ZaÄáteÄní píseň - start with last song ZaÄít s poslední písní - start with template ZaÄít s pÅ™edlohou - start with song ZaÄít s urÄitou písní - + On Launch PÅ™i spuÅ¡tÄ›ní - + show splash screen Ukázat úvodní obrazovku - + show "Did you know?" dialog Ukázat dialog Víte, že? - + Start template or song: Spustit pÅ™edlohu nebo píseň: - + Read MIDI Ports configuration from file, or else automatically configure Číst nastavení přípojek MIDI ze souboru, nebo nastavit automaticky - + Read MIDI Ports configuration PÅ™eÄíst nastavení přípojek MIDI - + + Warn if opening file versions different than current version + Upozornit, pokud se verze otevÅ™ených souborů liší od nynÄ›jší verze + + + + Auto save (every 5 minutes if not playing/recording) + Uložit automaticky (každých 5 minut, pokud se nepÅ™ehrává/nenahrává) + + + Audio Zvuk - + Mixer SměšovaÄ - - + + dB dB - + min. Meter Value Nejmenší hodnota regulátoru hladin - + min. Slider Val Nejmenší hodnota posuvníku - + Try to use Jack Freewheel Zkuste použít volnobÄ›h Jack - + Speeds bounce operations Urychlí operace odmíchání - + Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Použít volnobÄ›h Jack, je-li to možné. Tím se dramaticky zrychlí operace odmíchání. - + Enable denormal protection Povolit ochranu denormál - + Enable output limiter Povolit omezovaÄ výstupu - + VST in-place VST v místÄ› - + Enable VST in-place processing (restart required) Povolit zpracování VST v místÄ› (je vyžadováno nové spuÅ¡tÄ›ní) - + Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to @@ -4193,18 +4491,18 @@ schopné v místÄ›. Nastavení vyžaduje opÄ›tovné spuÅ¡tÄ›ní. - + Minimum control period Nejmenší kontrolní perioda - + Minimum audio controller process period (samples). Nejmenší perioda procesu ovladaÄe zvuku (vzorky). - + Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth @@ -4217,294 +4515,360 @@ s rychlostí, nastavte vyšší hodnotu. - + 1 1 - + 2 2 - + 4 4 - + 8 8 - - + + 16 16 - - + + 32 32 - - + + 64 64 - - + + 128 128 - - + + 256 256 - - + + 512 512 - - - + + + 1024 1024 - - - + + + 2048 2048 - + External Waveditor VnÄ›jší editor Wave - + External Waveditor command Příkaz pro vnÄ›jší editor Wave - + Note: External editor opened from the internal editor. Poznámka: VnÄ›jší editor otevÅ™ený z vnitÅ™ního editoru. - + Dummy Audio Driver (settings require restart) FaleÅ¡ný ovladaÄ zvuku (nastavení vyžaduje nové spuÅ¡tÄ›ní) - + Sample rate Vzorkovací kmitoÄet - + Hz Hz - + Period size (Frames per period): Velikost periody (snímků za periodu): - + Shorter periods give better midi playback resolution. Kratší periody dávají lepší rozliÅ¡ení pÅ™ehrávání MIDI. - + Midi MIDI - + + &Record all instruments + &Nahrávat vÅ¡echny nástroje + + + + &Don't record hidden instruments + &Nenahrávat skryté nástroje + + + + Don'&t record muted instruments + N&enahrávat ztlumené nástroje + + + + Don't record &hidden or muted instruments + Nenahrávat s&kryté nebo ztlumené nástroje + + + + Instrument initialization + Inicializace nástroje + + + + Send instrument initialization sequences + Poslat inicializaÄní sekvenci nástroje + + + + Warn if instrument initialization sequences pending + Upozornit, pokud inicializaÄní sekvence nástroje Äeká na vyřízení + + + + Send instrument controller default values if none in song, at init or rewind + Poslat výchozí hodnoty ovladaÄe nástroje, pokud není žádná hodnota v písní, v init nebo pÅ™evíjení zpÄ›t + + + Ticks Tiky - + RTC Resolution (Ticks/Sec) RozliÅ¡ení RTC (RealTimeClock) (Tiky/Sekundy) - + 4096 4096 - + 8192 8192 - + 16384 16384 - + 32768 32768 - + Midi Resolution (Ticks/Quarternote) RozliÅ¡ení MIDI (Tiky/ÄŒtvrÅ¥ová nota) - - + + &Only offer old-style drumtracks + &Pouze po stopách bicích ve starém stylu + + + + Only offer new-style &drumtracks + Pouze po stopách bicích v &novém stylu + + + + &Prefer old-style drumtracks + &UpÅ™ednostňovat stopy bicích ve starém stylu + + + + Prefer &new-style drumtracks + UpÅ™ednostňovat stopy bicích v no&vém stylu + + + + 48 48 - - + + 96 96 - - + + 192 192 - - + + 384 384 - - + + 768 768 - - + + 1536 1536 - - + + 3072 3072 - - + + 6144 6114 - - + + 12288 12288 - + Displayed Resolution (Ticks/Quarternote) Zobrazované rozliÅ¡ení (Tiky/ÄŒtvrÅ¥ová nota) - + Record new style drum tracks Nahrávat stopy bicích v novém stylu - Record all instruments Nahrávat vÅ¡echny nástroje - Don't record hidden instruments Nenahrávat skryté nástroje - Don't record muted instruments Nenahrávat ztlumené nástroje - Don't record hidden or muted instruments Nenahrávat skryté nebo ztlumené nástroje - + + Warn if timer frequency is inadequate + Upozornit, pokud je frekvence ÄasovaÄe nepÅ™iměřená + + + GUI Behaviour Chování rozhraní - + Behavior Chování - + + Track height + Výška stopy + + + GUI Refresh Rate Rychlost obnovování rozhraní - + /sec /s - + Use old-style stop shortcut: Použít klávesovou zkratku pro zastavení ve starém stylu: - + Move single armed track with selection PÅ™esunout jednu odjiÅ¡tÄ›nou stopu s výbÄ›rem - + Use project save dialog Použít dialog pro uložení projektu - + Some popup menus stay open (else hold Ctrl) NÄ›které vyskakovací nabídky zůstanou otevÅ™eny (držet Ctrl) - + Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. Umožní, aby nÄ›které vyskakovací nabídky zůstanou otevÅ™eny. Je pro to, aby zůstaly otevÅ™eny, potÅ™eba podržet klávesu Ctrl. - + In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the @@ -4516,105 +4880,142 @@ tlaÄítko myÅ¡i tlaÄítko levé. - + Use left mouse button for decreasing values Použít levé tlaÄítko myÅ¡i na zmenÅ¡ení hodnot - + Shift + Right click sets left range marker Shift + klepnutí pravým tlaÄítkem myÅ¡i nastaví znaÄku pro levý rozsah - + Allow adding hidden tracks in track list menu Povolit pÅ™idání skrytých stop v nabídce seznamu stop - + Unhide tracks when adding hidden tracks ZruÅ¡it skrytí stop pÅ™i pÅ™idávání skrytých stop - - + + Smart focus Chytré zaměření - + After editing, controls will return focus to their respective canvas Po dokonÄení úprav vrátí ovladaÄe zaměření na jim přísluÅ¡né plátno - + + Show newly created midi velocity graphs per-note + Ukázat novÄ› vytvoÅ™ené grafy síly tónu MIDI na notu + + + + px + px + + + + Enable borderless mouse. +For certain functions like zoom/pan. +Disable to use an alternate standard + method. + + Povolit myÅ¡ bez okraje. +Pro urÄité funkce, jako je zvÄ›tÅ¡ení/vyvážení. +Zakázat užití náhradního běžného postupu. + + + + Enable borderless mouse. +For certain functions like zoom. +Disable to use an alternate standard + method. + Povolit myÅ¡ bez okraje. +Pro urÄité funkce, jako je zvÄ›tÅ¡ení. +Zakázat užití náhradního běžného postupu. + + + + Borderless zoom/pan mouse (else use alternate method) + MyÅ¡ bez okraje pÅ™i zvÄ›tÅ¡ení/vyvážení (jinde použít náhradní postup) + + + + Scrollable submenus + Pohyblivé podnabídky + + + Drum tracks Stopy bicích - Only offer old-style drumtracks Pouze po stopách bicích ve starém stylu - Only offer new-style drumtracks Pouze po stopách bicích v novém stylu - Prefer old-style drumtracks UpÅ™ednostňovat stopy bicích ve starém stylu - Prefer new-style drumtracks UpÅ™ednostňovat stopy bicích v novém stylu - + GUI Style Styl rozhraní - + MDI-subwindowness and sharing menus Podokna a sdílení nabídek - + Presets: PÅ™ednastavení: - + traditional MusE SDI TradiÄní MusE SDI - + Cakewalk-like MDI MDI na způsob Cakewalku - + Borland-/Mac-like MDI MDI na způsob Borland/Mac - + &Apply &Použít - + &Ok &OK - + &Cancel Z&ruÅ¡it @@ -4794,137 +5195,190 @@ MusE: Nastavení metronomu - + Metronome Metronom - + Audio Beep VnitÅ™ní reproduktor poÄítaÄe - + Choose outputs... Vybrat výstupy... - + + + + + 50 50 - % Audio volume - Hlasitost zvuku % + Hlasitost zvuku % - + MIDI Click Klapnutí MIDI - + Midi Channel Kanál MIDI - + Measure Note Nota MIDI na zaÄátku taktu - + Measure Velocity - Síla nárazu noty MIDI na zaÄátku taktu + Síla tónu noty MIDI na zaÄátku taktu - + Beat Velocity - Síla nárazu na zaÄátku taktu + Síla tónu na zaÄátku taktu - + Beat Note Nota na zaÄátku doby - + Midi Port Přípojka MIDI - + + Disabled since jack does not support it + Zakázat, protože to JACK nepodporuje + + + Precount PÅ™edpoÄítat - + enable Povolit - + Bars Takty - + From Mastertrack Z hlavní stopy - + / / - + Signature Taktové oznaÄení - + Prerecord PÅ™ednahrát - + Preroll PÅ™etáÄení vpÅ™ed - + Hint: Enable metronome in Transportpanel - Rada: Povolte metronom v ovládacím panelu + Rada: Povolte metronom v ovládacím panelu pro pÅ™ehrávání + + + + Two samples (old samples) + Dva vzorky (staré vzorky) + + + + Four samples (new samples, with accents) + ÄŒtyÅ™i vzorky (nové vzorky, s přízvuky) + + + + Volume + Hlasitost + + + + Audio master + Řízení zvuku + + + + Meas + Takt + + + + Beat + Doba + + + + Accent1 + Důraz 1 + + + + Accent2 + Důraz 2 + + + + Sample + Vzorek - + &Apply &Použít - + Alt+A Alt+A - + &OK &OK - + Alt+O Alt+O - + &Cancel Z&ruÅ¡it - + Alt+C Alt+C @@ -4952,59 +5406,55 @@ Typ ovládání: - + + &Learn + Na&uÄit + + Control7 - OvladaÄ 7 + OvladaÄ 7 - Control14 - OvladaÄ 14 + OvladaÄ 14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - RPN14 - RPN 14 + RPN 14 - NRPN14 - NRPN 14 + NRPN 14 - Pitch - Výška tónu + Výška tónu - Program - Program + Program - + Hi: Vysoký: - + Lo: Nízký: - Learn - NauÄit se + NauÄit se @@ -5176,19 +5626,19 @@ - - - - + + + + Equal Stejný - - - - + + + + Unequal Nestejný @@ -5199,295 +5649,306 @@ - + Poly Pressure Vícenásobné stisknutí tlaÄítka - + Control Change ZmÄ›na ovládání - + Aftertouch DodÄ›lávka po - + Pitch Bend Ohyb výšky tónu - + NRPN NRPN - + RPN RPN - - - + + + Program + Program + + + + + Value 2 Hodnota 2 - - - - - + + + + + Value 1 Hodnota 1 - - + + Event Type Typ události - - - - + + + + Ignore PÅ™ehlížet - - - - - Higher - Vyšší - - - Lower - Nižší + Higher + Vyšší - Inside - UvnitÅ™ + Lower + Nižší + Inside + UvnitÅ™ + + + + + + Outside VnÄ› - - + + Channel Kanál - - + + Port Přípojka - + Processing Zpracování - - - - - + + + + + Keep Zachovat - - - - - + + + + + Fix Pevný - - - - + + + + Plus Plus - - - - + + + + Minus Minus - - - - + + + + Multiply Násobit - - - - + + + + Divide DÄ›lit - - - - + + + + Invert Obrátit - + ScaleMap Rozložení not - + Flip Obrátit - - - - + + + + Dyn Dyn - - - - + + + + Random Náhodný - + + Toggle + PÅ™epnout + + + Modules Moduly - + 1 1 - + 2 2 - + 3 3 - + 4 4 - + enable modul 1 Povolit modul 1 - + enable modul 2 Povolit modul 2 - + enable modul 3 Povolit modul 3 - + enable modul 4 Povolit modul 4 - + Preset PÅ™ednastavení - + Name: Název: - + Comment: Poznámka: - + Function Funkce - + create new preset VytvoÅ™it nové pÅ™ednastavení - + &New &Nový - + delete preset Smazat pÅ™ednastavení - + &Delete S&mazat - + &Dismiss &Zahodit - + PresetList Seznam pÅ™ednastavení @@ -5602,24 +6063,24 @@ Send and receive Jack transport - Poslat a pÅ™ijmout pÅ™esun JACK + Poslat a pÅ™ijmout pÅ™ehrávání JACK Send and receive Jack transport information, including stop, start and position. - Poslat a pÅ™ijmout informace o pÅ™esunu JACK, + Poslat a pÅ™ijmout informace o pÅ™ehrávání JACK, vÄetnÄ› zastavení, spuÅ¡tÄ›ní a polohy. Use Jack transport - Použít pÅ™esun JACK + Použít pÅ™ehrávání JACK Make MusE the Jack transport Timebase Master - UdÄ›lat MusE pánem nad řízením Äasu pÅ™esunu JACK + UdÄ›lat MusE pánem nad řízením Äasu pÅ™ehrávání JACK @@ -5629,7 +6090,7 @@ MusE will try to become master, but other Jack clients can also take over later. You can always click here again for Master. - UdÄ›lat MusE řízením Äasu pro pÅ™esun JACK. + UdÄ›lat MusE pánem nad řízením Äasu pÅ™ehrávání JACK. Dovolí JACKovi, aby ukazoval Äas jako takty, doby, tiky MusE. MusE se pokusí stát hlavním, ale jiní klienti JACK to mohou pozdÄ›ji také pÅ™evzít. Vždy můžete klepnout sem a nastavit MusE jako hlavní. @@ -5637,7 +6098,7 @@ Jack transport Timebase Master - Řízení Äasu pro pÅ™esun JACK + Řízení Äasu pro pÅ™ehrávání JACK @@ -5775,37 +6236,37 @@ MusE: Informace o stopÄ› - + output port Výstupní přípojka - + output channel Výstupní kanál - + all midi events are sent to this output channel VÅ¡echny události MIDI jsou poslány do tohoto výstupního kanálu - + Out ch Výstupní kanál - + input routing Vstupní signálový tok - + input detect ZjiÅ¡tÄ›ní vstupu - + Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. @@ -5814,163 +6275,194 @@ na vybraných kanálech na vybraných přípojkách MIDI. - + W W - + Midi thru MIDI thru - + Pass input events through ('thru') to output. Vést vstupní události skrz ('thru') do výstupu. - + output routing Výstupní signálový tok - - - - - - - + + + + + + + off Vypnuto - + Transp. PÅ™esun - + Delay ZpoždÄ›ní - - + + % % - + Length Délka - + Velocity - Síla nárazu + Síla tónu - + Compr. Kompr. - + Channel Info Informace o kanálu - + Select instrument patch Vybrat záplatu nástroje - + + <unknown> <neznámý> - + Rec: Nahr.: - + Add all settings to song PÅ™idat vÅ¡echna nastavení do písnÄ› - + All VÅ¡e - + Bank Select MSB. Ctrl-double-click on/off. VýbÄ›r banky MSB. Ctrl-dvojité klepnutí zapnuto/vypnuto. - - H-Bank - Vysoká banka + + Change note length in percent of actual length + ZmÄ›nit délku noty v procentech skuteÄné délky - - Bank Select LSB. Ctrl-double-click on/off. - VýbÄ›r banky LSB. Ctrl-dvojité klepnutí zapnuto/vypnuto. + + Offset playback of notes before or after actual note + Posun pÅ™ehrávání not pÅ™ed nebo po skuteÄné notÄ› - - L-Bank + + Transpose notes up or down + Transponovat noty nahoru nebo dolů + + + + H-Bank + Vysoká banka + + + + Bank Select LSB. Ctrl-double-click on/off. + VýbÄ›r banky LSB. Ctrl-dvojité klepnutí zapnuto/vypnuto. + + + + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + <html><head/><body><p>PÅ™idat nebo odebrat na síle tónu not ve stopÄ›.</p><p><span style=" font-style:italic;">Jelikož je rozsah not MIDI 0-127, <br/>může to znamenat, že noty nedosáhnout <br/>spojené síly tónu, nota + síla tónu.</span></p></body></html> + + + + Compress the notes velocity range, in percent of actual velocity + StlaÄit rozsah dynamiky not (procenta ze skuteÄné síly tónu) + + + + L-Bank Nízká banka - + Program. Ctrl-double-click on/off. Program. Dvojité klepnutí pro zapnuto/vypnuto. - + Add bank + prog settings to song PÅ™idat banku + nastavení programu do písnÄ› - + Prog Program - + Volume. Ctrl-double-click on/off. Hlasitost. Dvojité klepnutí pro zapnuto/vypnuto. - + Add vol setting to song PÅ™idat nastavení hlasitosti do písnÄ› - + Vol Hlasitost - - + + Change stereo position. Ctrl-double-click on/off. ZmÄ›nit polohu sterea. Ctrl-dvojité klepnutí zapnuto/vypnuto. - + Add pan setting to song PÅ™idat nastavení vyvážení do písnÄ› - + Pan Vyvážení + + + Select instrument + Vybrat nástroj + MidiTransformDialogBase @@ -6440,14 +6932,33 @@ + MusECore::AudioPreviewDialog + + + Auto play + PÅ™ehrát automaticky + + + + + Stop + Zastavit + + + + Play + PÅ™ehrát + + + MusECore::Song - + Jack shutdown! Vypnutí JACK! - + Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: @@ -6481,103 +6992,128 @@ zkuste Jack znovu spustit a klepnÄ›te na tlaÄítko "Spustit znovu". - - + + Automation: Automatizace: - + previous event PÅ™edchozí událost - + next event Další událost - - + + set event Nastavit událost - - + + add event PÅ™idat událost - - + + erase event Vymazat událost - + erase range Vymazat rozsah - + clear automation Smazat automatizaci - + Midi control Ovládání MIDI - + Assign PÅ™iÅ™adit - + Clear Smazat - + Clear all controller events? Smazat vÅ¡echny události ovladaÄe? - + &Ok &OK - + &Cancel Z&ruÅ¡it - + MusE: Tempo list MusE: Seznam tempa - + External tempo changes were recorded. Transfer them to master tempo list? Byly nahrány vnÄ›jší zmÄ›ny tempa. Mají se pÅ™evzít do hlavního seznamu tempa? - + + Do you want to process ALL or only selected events? + Chcete zpracovat VÅ ECHNY nebo pouze vybrané události? + + + + &Selected + &Vybráno + + + + &All + &VÅ¡e + + + MusE - external script failed MusE: VnÄ›jší skript selhal - + MusE was unable to launch the script, error message: %1 MusE se skript nepodaÅ™ilo spustit. Chybová zpráva: %1 + + + Und&o + &ZpÄ›t + + + + Re&do + Z&novu + MusEGui @@ -6714,7 +7250,7 @@ Transport controls. - Ovládání pÅ™esunu. + Ovládání pÅ™ehrávání. @@ -6762,22 +7298,37 @@ Vlastní - + Keep Qt system style Zachovat styl systému Qt - + + Do you really want to reset colors to theme default? + Opravdu chcete barvy nastavit znovu na výchozí vzhled? + + + + &Ok + &OK + + + + &Cancel + Z&ruÅ¡it + + + MusE: load image MusE: Nahrát obrázek - + Select style sheet Vybrat styl - + Qt style sheets (*.qss) Styly Qt (*.qss) @@ -6785,62 +7336,62 @@ MusEGui::Arranger - + Enable Recording Povolit nahrávání - + Mute/Off Indicator Ztlumit/Ukazatel vypnuto - + Solo Indicator Ukazatel "Solo" - + Track Type Typ stopy - + Track Name Název stopy - + Midi output channel number or audio channels Číslo kanálu výstupu MIDI nebo zvukové kanály - + Midi output port or synth midi port Přípojka výstupu MIDI nebo přípojka MIDI syntetizátoru - + Time Lock Zámek Äasu - + Automation parameter selection VýbÄ›r parametru automatizace - + Notation clef Notový klÃ­Ä - + Enable recording. Click to toggle. Zapnout nahrávání. KlepnÄ›te pro pÅ™epnutí. - + Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. @@ -6851,7 +7402,7 @@ Zapnuto/Vypnuto není! - + Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. @@ -6860,21 +7411,21 @@ ukázáno tmavým ÄtvereÄkem. - + Track type. Right-click to change midi and drum track types. Typ stopy. Klepnutí pravým tlaÄítkem myÅ¡i pro zmÄ›nu typů stopy MIDI a bicí. - + Track name. Double-click to edit. Right-click for more options. Typ stopy. Dvojité klepnutí tlaÄítkem myÅ¡i pro úpravy. Klepnutí pravým tlaÄítkem myÅ¡i pro více voleb. - + Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. @@ -6884,7 +7435,7 @@ myÅ¡i pro zmÄ›nu. - + Midi/drum track: Output port. Synth track: Assigned midi port. Left-click to change. @@ -6895,233 +7446,277 @@ Klepnutí pravým tlaÄítkem myÅ¡i pro ukázání rozhraní. - + Time lock Zámek Äasu - + Notation clef. Select this tracks notation clef. Notový klíÄ. Vybere notový klÃ­Ä této stopy. - + Arranger Aranžér - + Cursor Poloha ukazovátka - + Off Vypnuto - + Bar Takt - + Snap Magnet - + Len Délka - - + + song length - bars délka písnÄ› - takty - Type - Typ + Typ - NO - Žádný + Žádný - GM - GM + GM - GS - GS + GS - XG - XG + XG - - midi song type - Typ písnÄ› MIDI + Typ písnÄ› MIDI - + Pitch Výška tónu - + midi pitch ZmÄ›na výšky tónu MIDI - + global midi pitch shift Celková zmÄ›na výšky tónu MIDI - + Tempo Tempo - - + + midi tempo Tempo MIDI - + N N - + TrackInfo Informace o stopÄ› - + R L - + M M - + S S - + C C - + Track Stopa - + Port Přípojka - + Ch K - + T T - + Automation Automatizace - + Clef KlÃ­Ä + MusEGui::ArrangerColumns + + + Control7 + OvladaÄ 7 + + + + Control14 + OvladaÄ 14 + + + + RPN + RPN + + + + NPRN + NPRN + + + + RPN14 + RPN 14 + + + + NRPN14 + NRPN 14 + + + + Pitch + Výška tónu + + + + Program + Program + + + + Aftertouch + DodÄ›lávka po + + + MusEGui::ArrangerView - + MusE: Arranger MusE: Aranžér - + + D&elete + S&mazat + + + C&ut Vyj&mout - + &Copy &Kopírovat - + Copy in range Kopírovat v oblasti - + &Paste &Vložit - Paste (show dialog) - Vložit (ukázat dialog) + Vložit (ukázat dialog) - + Paste c&lone Vložit k&lon - Paste clone (show dialog) - Vložit klon (ukázat dialog) + Vložit klon (ukázat dialog) - + &Insert Empty Measure Vložit prázdný &takt - + Delete Selected Tracks Smazat vybrané stopy - + Duplicate Selected Tracks Zdvojit vybrané stopy - + Shrink selected parts Srazit vybrané Äásti - + Expand selected parts Roztáhnout vybrané Äásti @@ -7130,234 +7725,249 @@ Uklidit vybrané Äásti - + + Paste to selected &track + Vložit do vybrané &stopy + + + + Paste clone to selected trac&k + Vložit klon do vybrané s&topy + + + + Paste (show dialo&g) + Vložit (ukázat &dialog) + + + Purge hidden events from selected parts Zbavit skryté události vybraných Äástí - + Add Track PÅ™idat stopu - + Select Vybrat - + Select &All Vybrat &vÅ¡e - + &Deselect All &ZruÅ¡it výbÄ›r vÅ¡eho - + Invert &Selection Obrátit &výbÄ›r - + &Inside Loop &UvnitÅ™ smyÄky - + &Outside Loop &VnÄ› smyÄky - + All &Parts on Track VÅ¡echny Äá&sti na stopÄ› - + Score Notový zápis - + all tracks in one staff VÅ¡echny stopy v jedné osnovÄ› - + one staff per track Jedna osnova na stopu - + New score window Nové okno s notovým zápisem - + Pianoroll Editor váleÄku - + Drums Editor bicích - - + + List Editor seznamu - + Wave Editor Wave - + Mastertrack Hlavní stopa - + Graphic Grafický editor - + Midi &Transform &PromÄ›nit MIDI - + Global Cut Celkové vyjmutí - + Global Insert Celkové vložení - + Global Split Celkové rozdÄ›lení - + Global Cut - selected tracks Celkové vyjmutí - vybrané stopy - + Global Insert - selected tracks Celkové vložení - vybrané stopy - + Global Split - selected tracks Celkové rozdÄ›lení - vybrané stopy - + &Edit Úp&ravy - + &Structure &Stavba - + Functions Funkce - + &Quantize Notes &Kvantizovat noty - + Change note &length ZmÄ›nit &délku noty - + Change note &velocity - ZmÄ›nit &sílu nárazu noty + ZmÄ›nit &sílu tónu noty - + Crescendo/Decrescendo Crescendo/Decrescendo - + Transpose PÅ™evedení - + Erase Events (Not Parts) Smazat události (nikoli Äásti) - + Move Events (Not Parts) PÅ™esunout události (nikoli Äásti) - + Set Fixed Note Length Nastavit pevnou délku noty - + Delete Overlapping Notes Smazat pÅ™ekrývající se noty - + Legato Legato - + Window &Config &Aranžér - + Configure &custom columns Nastavit v&lastní sloupce - + Remove track(s) Odstranit stopu(y) - + Are you sure you want to remove this track(s)? Opravdu chcete odstranit stopu(y)? - - + + New Nový - + Changed Settings Nastavení zmÄ›nÄ›na - + Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. @@ -7433,98 +8043,102 @@ MusEGui::AudioStrip - + panorama Vyvážení - + aux send level Úroveň poslání Aux - Pan - Vyvážení + Vyvážení + + + + calibration gain + Kalibrace zesílení - + 1/2 channel 1/2 kanály - + Pre PÅ™ed - + pre fader - post fader PÅ™edprolínaÄ - poprolínaÄ - + dB dB - + record Nahrávat - + mute Ztlumit - + record downmix Nahrávat smíchání - - + + solo mode Režim sóla - + off Vypnuto - + input routing Vstupní signálový tok - + output routing Výstupní signálový tok - + Off Vypnuto - + Read Číst - + Touch Dotknout se - + Write Zapsat - + automation type Typ automatizace @@ -7532,54 +8146,62 @@ MusEGui::BigTime - + format display Zobrazení formátu - + bar Takt - + beat Doba - - + + tick Tik - + minute Minuta - + second Sekunda - - + + frame Snímek - + subframe Podsnímek - + MusE: Bigtime MusE: Velký ukazatel Äasu + MusEGui::Canvas + + + Tools: + Nástroje: + + + MusEGui::ClipListEdit @@ -7605,18 +8227,18 @@ MusEGui::CtrlCanvas - - + + Make the current part's track match the selected drumlist entry UdÄ›lat to tak, aby stopa nynÄ›jší Äásti odpovídala vybranému záznamu v seznamu bicích - + Drawing hint: Hold Ctrl to affect only existing events Rada ke kreslení: Podržte klávesu Ctrl pro ovlivnÄ›ní pouze stávající události - + Use pencil or line tool to draw new events Použijte tužku nebo nástroj na Äáry pro nakreslení nových událostí @@ -7628,93 +8250,88 @@ MusEGui::CtrlPanel - + S S - + select controller Vybrat ovladaÄ - + X X - + remove panel Zavřít pohled s ovladaÄem - + manual adjust RuÄní upravení - + ctrl-double-click on/off Ctrl-dvojité klepnutí pro zapnuto/vypnuto - + off Vypnuto - - + + all/per-note velocity mode + Režim síly tónu vÅ¡e/na notu + + Velocity - Síla nárazu + Síla nárazu - add new ... - PÅ™idat nový ovladaÄ... + PÅ™idat nový ovladaÄ... - - Instrument-defined - Vymezeno nástrojovÄ› + Vymezeno nástrojovÄ› - - Add ... - PÅ™idat... + PÅ™idat... - Others - Jiné + Jiné - Edit instrument ... - Upravit nástroj... + Upravit nástroj... - Common Controls - Obecné ovládání + Obecné ovládání MusEGui::DList - + hide this instrument Skrýt tento nástroj - + show this instrument Ukázat tento nástroj - + this turns a grayed out eye into a blue eye ZmÄ›ní Å¡edé oko na modré oko @@ -7722,50 +8339,50 @@ MusEGui::DrumCanvas - + Moving items failed PÅ™esun položek se nezdaÅ™il - + The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. VýbÄ›r se nepodaÅ™ilo pÅ™esunout, protože by alespoň jedna nota byla pÅ™esunuta do stopy, která je jiná než jak původní stopa, tak stopa nynÄ›jší Äásti. Může pomoci nynÄ›jší Äást zmÄ›nit pomocí Alt+Å¡ipka vlevo/vpravo. - + Creating event failed VytvoÅ™ení události se nezdaÅ™ilo - + Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Událost se nepodaÅ™ilo vytvoÅ™it, protože nyní vybraná Äást není stejnou stopou, a vybraný nástroj by mohl být buÄ na žádné nebo na více Äástech, což je nejednoznaÄné. Vyberte cílovou Äást, pak to zkuste znovu. - - + + Recording event failed Nahrání události se nezdaÅ™ilo - - + + Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Událost se nepodaÅ™ilo nahrát, protože nyní vybraná Äást není stejnou stopou, a nástroj, který se má nahrát, by mohl být buÄ na žádné nebo na více Äástech, což je nejednoznaÄné. Vyberte cílovou Äást, pak to zkuste znovu. - + Internal error VnitÅ™ní chyba - + Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. Vyskytla se, i když by jeden Å™ekl, že je to nemožné, jedna opravdu oÅ¡klivá vnitÅ™ní chyba. Prověřte výstup konzole. Nic nebylo nahráno. @@ -7773,187 +8390,175 @@ MusEGui::DrumEdit - - + + mute instrument Ztlumit nástroj - - + + sound name Název zvuku - - + + volume percent Procento hlasitosti - - + + quantisation Kvantizace - - + + this input note triggers the sound Tato vstupní nota spustí zvuk - - + + note length Délka noty - - + + this is the note which is played Toto je nota, která se pÅ™ehrává - output channel (hold ctl to affect all rows) - Výstupní kanál (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) + Výstupní kanál (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) - output port (hold ctl to affect all rows) - Výstupní přípojka (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) + Výstupní přípojka (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) - - shift + control key: draw velocity level 1 - Klávesy Shift + Ctrl: Kreslit úroveň síly nárazu (dynamiky) 1 + Klávesy Shift + Ctrl: Kreslit úroveň síly nárazu (dynamiky) 1 - - control key: draw velocity level 2 - Klávesa Ctrl: Kreslit úroveň síly nárazu (dynamiky) 2 + Klávesa Ctrl: Kreslit úroveň síly nárazu (dynamiky) 2 - - shift key: draw velocity level 3 - Klávesa Shift: Kreslit úroveň síly nárazu (dynamiky) 3 + Klávesa Shift: Kreslit úroveň síly nárazu (dynamiky) 3 - - draw velocity level 4 - Kreslit úroveň síly nárazu (dynamiky) 4 + Kreslit úroveň síly nárazu (dynamiky) 4 - output channel (ctl: affect all rows) - Výstupní kanál (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) + Výstupní kanál (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) - output port (ctl: affect all rows) - Výstupní přípojka (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) + Výstupní přípojka (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) &File &Soubor - + Load Map Nahrát rozložení bicích - + Save Map Uložit rozložení bicích - + Reset GM Map Nastavit rozložení GM znovu - + &Edit Úp&ravy - + Cut Vyjmout - + Copy Kopírovat - + Copy events in range Kopírovat události v rozsahu - + Paste Vložit - + Paste (with Dialog) Vložit (s dialogem) - + Delete Events Smazat události - + &Select &Vybrat - + Select All Vybrat vÅ¡e - + Select None Nevybrat nic - + Invert Obrátit - + Inside Loop UvnitÅ™ smyÄky - + Outside Loop VnÄ› smyÄky - + Previous Part PÅ™edchozí Äást - + Next Part Další Äást - + Fu&nctions &Funkce @@ -7962,263 +8567,312 @@ PÅ™euspořádat seznam - + Set Fixed Length Nastavit pevnou délku - + Modify Velocity - ZmÄ›nit sílu nárazu + ZmÄ›nit sílu tónu - + Crescendo/Decrescendo Crescendo/Decrescendo - + Quantize Kvantizovat - + Erase Event Smazat událost - + Move Notes PÅ™esunout noty - + Delete Overlaps Smazat pÅ™ekrytí - + &Plugins &Moduly - + Window &Config O&kno - + Drum tools Nástroje pro bicí - + Load Drummap Nahrát rozložení bicích - - + + hide instrument Skrýt nástroj - - Re-order map - PÅ™euspořádat rozložení + + override track output channel (hold ctl to affect all rows) + PotlaÄit výstupní kanál stopy (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) - - Group - Seskupit + + override track output port (hold ctl to affect all rows) + PotlaÄit výstupní kanál přípojky (držet klávesu Ctrl pro ovlivnÄ›ní vÅ¡ech řádků) - + + + control + meta keys: draw velocity level 1 + Klávesa Ctrl + meta klávesy: Kreslit úroveň síly tónu (dynamiky) 1 + + + + + meta key: draw velocity level 2 + Meta klávesa: Kreslit úroveň síly tónu (dynamiky) 3 + + + + + draw default velocity level 3 + Kreslit výchozí úroveň síly tónu (dynamiky) 3 + + + + + meta + alt keys: draw velocity level 4 + Meta + Alt klávesy: Kreslit úroveň síly tónu (dynamiky) 4 + + + + override track output channel (ctl: affect all rows) + PotlaÄit výstupní kanál stopy (klávesa Ctrl: ovlivní vÅ¡echny řádky) + + + + override track output port (ctl: affect all rows) + PotlaÄit výstupní přípojku stopy (klávesa Ctrl: ovlivní vÅ¡echny řádky) + + + + Re-order map + PÅ™euspořádat rozložení + + + + Group + Seskupit + + + Don't group Neseskupovat - + Group by channel Seskupit podle kanálu - + Group maximally Seskupit maximálnÄ› - + Show/Hide Ukázat/Skrýt - + Also show hidden instruments Ukázat i skryté nástroje - + Show all instruments Ukázat vÅ¡echny nástroje - + Hide all instruments Skrýt vÅ¡echny nástroje - + Only show used instruments Ukázat pouze použité nástroje - + Only show instruments with non-empty name or used instruments Ukázat pouze nástroje s vyplnÄ›ným názvem nebo použité nástroje - + Drum map tools Nástroje rozložení bicích - + Store Drummap Uložit rozložení bicích - + Step Record Nahrávat po taktu - + Midi Input Vstup MIDI - + + Play Events + PÅ™ehrát události + + + cursor tools Nástroje ukazovátka - + Cursor step: Krok ukazovátka: - + Set step size for cursor edit Nastavit délku kroku pro režim ukazovátka - + ctrl Ctrl - + Add Controller View PÅ™idat pohled ovladaÄe - + H H - + M M - + Sound Zvuk - + Vol Hlasitost - + QNT QNT - + E-Note Nota E - + Len Délka - + A-Note Nota A - + Ch K - + Port Přípojka - + LV1 LV1 - + LV2 LV2 - + LV3 LV3 - + LV4 LV4 - + Muse: Load Drum Map MusE: Nahrát rozložení bicích - + MusE: Store Drum Map MusE: Uložit rozložení bicích - + Drum map Rozložení bicích - + Reset the drum map with GM defaults? Nastavit rozložení bicích znovu s výchozím GM? - + Not all parts are displayed Ne vÅ¡echny Äásti jsou zobrazeny - + You selected both old-style-drumtracks and others (that is: new-style or midi tracks), but they cannot displayed in the same drum edit. I'll only display the old-style drumtracks in this editor, dropping the others. Vybral jste jak stopy bicích ve starém stylu a jiné (to je nový styl nebo stopy MIDI), ale tyto nelze zobrazit ve stejné úpravÄ› bicích. @@ -8228,30 +8882,27 @@ MusEGui::EditCAfterDialog - MusE: Enter Channel Aftertouch - MusE: Zadat dodÄ›lávku kanálu + MusE: Zadat dodÄ›lávku kanálu - Time Position - ÄŒasová poloha + ÄŒasová poloha - Pressure - Tlak úhozu + Tlak úhozu MusEGui::EditEventDialog - + Ok OK - + Cancel ZruÅ¡it @@ -8259,182 +8910,252 @@ MusEGui::EditInstrument - + + Control7 + OvladaÄ 7 + + + + Control14 + OvladaÄ 14 + + + + RPN + RPN + + + + NPRN + NPRN + + + + RPN14 + RPN 14 + + + + NRPN14 + NRPN 14 + + + + Pitch + Výška tónu + + + + Program + Program + + + + PolyAftertouch + Vícenásobná dodÄ›lávka po + + + + Aftertouch + DodÄ›lávka po + + + + Name Název - + Vol Hlasitost - + Quant Kvantizace - + E-Note Nota E - + + Len Délka - + A-Note Nota A - + LV1 LV1 - + LV2 LV2 - + LV3 LV3 - + LV4 LV4 - - + + Tick + Tik + + + + Data + Data + + + + MusE: Create file failed MusE: VytvoÅ™ení souboru se nezdaÅ™ilo - + MusE: Write File failed MusE: Zápis souboru se nezdaÅ™il - - + + MusE: Save Instrument Definition MusE: Uložit vymezení nástroje - - + + Instrument Definition (*.idf) Vymezení nástroje (*.idf) - - + + MusE: Save instrument as MusE: Uložit nástroj jako - + Enter a new unique instrument name: Zadat nový jedineÄný název nástroje: - + The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? Uživatelský nástroj '%1' již existuje. Toto pÅ™epíše jeho .idf soubor s nástrojem. Jste si jistý? - + MusE: Bad instrument name MusE: Å patný název nástroje - + Please choose a unique instrument name. (The name might be used by a hidden instrument.) Vyberte, prosím, jedineÄný název nástroje. (Název může být použit skrytým nástrojem.) - + MusE: Bad patch name MusE: Å patný název záplaty - + Please choose a unique patch name Vyberte, prosím, jedineÄný název pro záplatu - + MusE: Bad patchgroup name MusE: Å patný název skupiny záplat - + Please choose a unique patchgroup name Vyberte, prosím, jedineÄný název pro skupinu záplat - + MusE: Bad controller name MusE: Å patný název ovladaÄe - + Please choose a unique controller name Vyberte, prosím, jedineÄný název pro ovladaÄ - - + + New controller: Error + Nový ovladaÄ: Chyba + + + + Error! All control numbers are taken up! +Clean up the instrument! + Chyba! VÅ¡echna Äísla ovládání jsou zabrána! +UkliÄte nástroj! + + MusE: Cannot add common controller - MusE: Nelze pÅ™idat obecný ovladaÄ + MusE: Nelze pÅ™idat obecný ovladaÄ - A controller named '%1' already exists. - OvladaÄ s názvem '%1' již existuje. + OvladaÄ s názvem '%1' již existuje. - A controller number %1 already exists. - Číslo ovladaÄe '%1' již existuje. + Číslo ovladaÄe '%1' již existuje. - - + + MusE MusE - - + + The current Instrument contains unsaved data Save Current Instrument? NynÄ›jší nástroj obsahuje neuložená data. Uložit nynÄ›jší nástroj? - - + + &Save &Uložit - - + + &Nosave &Neukládat - + &Abort &ZruÅ¡it @@ -8442,22 +9163,22 @@ MusEGui::EditMetaDialog - + MusE: Enter Meta Event MusE: Zadání metaudálosti - + Time Position ÄŒasová poloha - + Meta Type Meta typ - + Enter Hex Zadat Hex @@ -8465,30 +9186,26 @@ MusEGui::EditPAfterDialog - MusE: Enter Poly Aftertouch - MusE: Zadat vícedodÄ›lávku + MusE: Zadat vícedodÄ›lávku - Time Position - ÄŒasová poloha + ÄŒasová poloha - Pitch - Výška tónu + Výška tónu - Pressure - Tlak úhozu + Tlak úhozu MusEGui::EditToolBar - + select Pointer Tool: with the pointer tool you can: select parts @@ -8501,7 +9218,7 @@ Kopírovat Äásti - + select Pencil Tool: with the pencil tool you can: create new parts @@ -8512,118 +9229,160 @@ ZmÄ›nit délku jednotlivých Äástí - + select Delete Tool: with the delete tool you can delete parts Nástroj gumy": Smazat Äásti - + select Cut Tool: with the cut tool you can split a part Nástroj pro stříhání: S tímto nástrojem se Äásti rozdÄ›lují - + select Glue Tool: with the glue tool you can glue two parts Nástroj pro pÅ™ilepení: S tímto nástrojem se dvÄ› jednotlivé Äásti spojují v jednu - + select Score Tool: Nástroj pro notový zápis: - + select Quantize Tool: insert display quantize event Nástroj pro kvantizaci: S tímto nástrojem se vkládají události kvantizace - + select Drawing Tool Nástroj tužky na kreslení - + select Muting Tool: click on part to mute/unmute Nástroj pro ztlumení: KlepnÄ›te na Äást pro zapnutí/vypnutí ztlumení - + Manipulate automation Pracovat na automatizaci - Cursor tool - Nástroj ukazovátka + Nástroj ukazovátka - + pointer Ukazovátko - + pencil Tužka - + eraser Guma - + cutter StÅ™ihadlo - + score Notový zápis - + glue Lepidlo - + + select Cursor (tracker mode) tool: +with the cursor tool you can: + navigate with arrow keys + use VBNM to place notes + change step with 0 and 9 + Vyberte nástroj ukazovátka (stopový režim): +Pomocí nástroj ukazovátka můžete: + pohybovat se pomocí kláves Å¡ipek + k umísÅ¥ování not používat VBNM + mÄ›nit krok pomocí 0 a 9 + + + + select Range Tool + Vybrat nástroj pro rozsahy + + + + select Panning Tool + Vybrat nástroj pro vyvážení + + + + select Zoom Tool + Vybrat nástroj pro zvÄ›tÅ¡ení + + + + range + Rozsah + + + + pan + Vyvážení + + + + zoom + ZvÄ›tÅ¡ení + + + quantize Kvantizovat - + draw Kreslit - + mute parts Ztlumit Äásti - + edit automation Upravit automatizaci - + cursor Ukazovátko - + Edit Tools Upravit nástroje @@ -8631,67 +9390,72 @@ MusEGui::EffectRack - + effect rack PÅ™ihrádka s efekty - + new Nový - + change ZmÄ›nit - + move up Posunout nahoru - + move down Posunout dolů - + remove Odstranit - + bypass Obejít - + show gui Ukázat uživatelské rozhraní - + show native gui Ukázat původní rozhraní - + save preset Uložit pÅ™ednastavení - + + Presets + PÅ™ednastavení + + + MusE: Save Preset MusE: Uložit pÅ™ednastavení - + Replace effect Nahradit efekt - + Do you really want to replace the effect %1? Opravdu chcete nahradit efekt %1? @@ -8699,7 +9463,7 @@ MusEGui::GlobalSettingsConfig - + MusE: Choose start template or song MusE: Vybrat zaÄáteÄní píseň nebo pÅ™edlohu @@ -8838,127 +9602,125 @@ MusEGui::ListEdit - + insert Note Vložit notu - + insert SysEx Vložit SysEx - + insert Ctrl Vložit Ctrl - + insert Meta Vložit Meta - insert Channel Aftertouch - Vložit dodÄ›lávku kanálu + Vložit dodÄ›lávku kanálu - insert Poly Aftertouch - Vložit více dodÄ›lávku kanálu + Vložit více dodÄ›lávku kanálu - + &Edit Úp&ravy - + Cut Vyjmout - + Copy Kopírovat - + Paste Vložit - + Delete Events Smazat události - + Increase Tick ZvÄ›tÅ¡it tiky - + Decrease Tick ZmenÅ¡it tiky - + Window &Config O&kno - + Insert tools Vložit nástroje - + Tick Tiky - + Bar Takt - + Type Typ - + Ch K - + Val A Hodnota A - + Val B Hodnota B - + Val C Hodnota C - + Len Délka - + Comment Poznámka - + MusE: List Editor MusE: Editor seznamu @@ -8966,259 +9728,335 @@ MusEGui::MPConfig - - + Default input connections Výchozí vstupní spojení - - + + Are you sure you want to apply to all existing midi tracks now? Jste si jistý, že chcete použít na vÅ¡echny existující stopy MIDI nyní? - - + Default output connections Výchozí výstupní spojení - - Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? - Nastavení se použije na nové stopy MIDI. + Nastavení se použije na nové stopy MIDI. Chcete použít na vÅ¡echny existující stopy MIDI nyní? - + MusE: bad device name MusE: Å patný název zařízení - + please choose a unique device name Vyberte, prosím, jedineÄný název pro zařízení - - + + in Vstup - - + + out Výstup - Show first aliases Ukázat první pÅ™ezdívku - Show second aliases Ukázat druhou pÅ™ezdívku - - + + Toggle all PÅ™epnout vÅ¡e - - + + Change all tracks now ZmÄ›nit vÅ¡echny stopy nyní - + + Remove + Odstranit + + + Create Jack device VytvoÅ™it zařízení Jack - - + + Port Number Číslo přípojky - + + Enable gui Povolit rozhraní - + + Enable reading Povolit Ätení - + + Enable writing Povolit zápis - + Port instrument Nástroj přípojky - + Midi device name. Click to edit (Jack) Název zařízení MIDI. Klepnout pro úpravu (Jack) - + Connections from Jack Midi outputs Spojení z výstupů MIDI Jack - + Connections to Jack Midi inputs Spojení do výstupů MIDI Jack - + Auto-connect these channels to new midi tracks Automaticky tyto kanály pÅ™ipojit do nových stop MIDI - + Auto-connect new midi tracks to these channels Automaticky pÅ™ipojit nové stopy MIDI do tÄ›chto kanálů - + Auto-connect new midi tracks to this channel Automaticky pÅ™ipojit nové stopy MIDI do tohoto kanálu + Device state Stav zařízení - + Enable gui for device Povolit rozhraní pro zařízení - + + Enable reading from device Povolit Ätení ze zařízení - + + Enable writing to device Povolit zápis na zařízení - + Name of the midi device associated with this port number. Click to edit Jack midi name. Název zařízení MIDI spojeného s tímto Äíslem přípojky. KlepnÄ›te pro zmÄ›nu názvu MIDI Jack. - + Instrument connected to port Nástroj spojen s přípojkou - + Connections from Jack Midi output ports Spojení z výstupních přípojek MIDI Jack - + Connections to Jack Midi input ports Spojení k vstupním přípojkám MIDI Jack - + Auto-connect these channels, on this port, to new midi tracks. Automaticky pÅ™ipojit tyto kanály, na této přípojce, do nových stop MIDI. - + Connect new midi tracks to these channels, on this port. PÅ™ipojit nové stopy MIDI do tÄ›chto kanálů, na této přípojce. - + Connect new midi tracks to this channel, on this port. PÅ™ipojit nové stopy MIDI do tohoto kanálu, na této přípojce. - + State: result of opening the device Stav: Událost otevÅ™ení zařízení - + Port Přípojka - + + GUI Rozhraní - + + I Vstup - + + O Výstup - + Instrument Nástroj - + + Device Name Název zařízení - + + + Midi device name + Název zařízení MIDI + + + + + Midi device type + Typ zařízení MIDI + + + + Connections from Jack Midi + Spojení od JACK MIDI + + + + Connections to Jack Midi + Spojení do JACK MIDI + + + + Result of opening the device: +OK: Assigned to a port and in use +Closed: Unassigned to a port, or closed +R/W Error: Unable to open for read or write +Unavailable: USB midi unplugged, or external + application not running, or synth plugin + not installed etc. +(Jack Midi devices have 'unavailable ports' + in the routes columns.) +Unavailable devices or ports can be purged + with 'Remove' or with the advanced router. + + + + + Enable Graphical User Interface for device + Povolit pro zařízení grafické uživatelské rozhraní + + + + Connections from Jack Midi ports + Spojení od přípojek JACK MIDI + + + + Connections to Jack Midi ports + Spojení do přípojek JACK MIDI + + + In routes Tok vstupního signálu - + Out routes Tok výstupního signálu - + Def in ch Výchozí vstupní kanál - + Def out ch Výchozí výstupní kanál - + + State Stav - + + Type + Typ + + + + In + Vstup + + + + Out + Výstup + + + <unknown> <neznámý> - - + <none> <Žádný> @@ -9226,7 +10064,7 @@ MusEGui::MTScale - + bar scale Měřítko taktu @@ -9234,7 +10072,7 @@ MusEGui::MTScaleFlo - + bar scale Měřítko taktu @@ -9376,10 +10214,58 @@ + MusEGui::MidiAudioControl + + + Control7 + OvladaÄ 7 + + + + Control14 + OvladaÄ 14 + + + + RPN + RPN + + + + NPRN + NPRN + + + + RPN14 + RPN 14 + + + + NRPN14 + NRPN 14 + + + + Pitch + Výška tónu + + + + Program + Program + + + + Aftertouch + DodÄ›lávka po + + + MusEGui::MidiInputTransformDialog - - + + New Nový @@ -9852,8 +10738,10 @@ MusEGui::MidiTrackInfo - - + + + + <unknown> <neznámý> @@ -9861,8 +10749,8 @@ MusEGui::MidiTransformerDialog - - + + New Nový @@ -9878,655 +10766,680 @@ MusEGui::MusE - - + + Failed to start audio! NepodaÅ™ilo se spustit zvuk! - + Was not able to start audio, check if jack is running. NepodaÅ™ilo se spustit zvuk. Ověřte, že JACK běží. - + Timeout waiting for audio to run. Check if jack is running. PÅ™ekroÄení Äasu pÅ™i Äekání na zvuk. Ověřte, že JACK běží. - + Und&o &ZpÄ›t - + Re&do Z&novu - + undo last change to song Vrátit poslední zmÄ›nu na písni - + redo last undo UdÄ›lat poslední vrácení o krok zpÄ›t - + Loop SmyÄka - + loop between left mark and right mark SmyÄka mezi levou znaÄkou a pravou znaÄkou - + Punchin ZaÄít nahrávání na levé znaÄce - + record starts at left mark Nahrávání zaÄne na levé znaÄce - + Punchout UkonÄit nahrávání na pravé znaÄce - + record stops at right mark Nahrávání skonÄí na pravé znaÄce - + Start Spustit - + rewind to start position SkoÄit na zaÄátek - + Rewind O jeden takt zpÄ›t - + rewind current position SkoÄit o jeden takt zpÄ›t - + Forward O jeden takt vpÅ™ed - + move current position SkoÄit o jeden takt vpÅ™ed - + Stop Zastavit - + stop sequencer Zastavit pÅ™ehrávání - + Play PÅ™ehrát - + start sequencer play Spustit pÅ™ehrávání - + Record Nahrávat - + to record press record and then play Pro nahrávání klepnÄ›te nejprve na tlaÄítko "Nahrávat" a potom na "PÅ™ehrávat" - - + + Panic Nouzové zastavení - + send note off to all midi channels Poslat příkaz "Nota vypnuta" vÅ¡em kanálům MIDI - + + turn on/off metronome + Zapnout/Vypnout metronom + + + &New &Nový - - + + Create New Song VytvoÅ™it novou píseň - + &Open &Otevřít píseň - - + + Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. KlepnÄ›te na toto tlaÄítko pro otevÅ™ení <em>Nové písnÄ›</em>.<br> Také můžete v nabídce Soubor vybrat příkaz <b>Nová píseň</b>. - + Open &Recent Otevřít &poslední píseň - - - + + + &Save &Uložit - - + + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. KlepnÄ›te na toto tlaÄítko pro uložení vámi právÄ› upravované písnÄ› s automaticky položeným dotazem na název souboru. Také můžete v nabídce Soubor vybrat příkaz Uložit. - + Save &As Uložit &jako - + Import Midifile Zavést soubor MIDI - + Export Midifile Vyvést soubor MIDI - + Import Part Zavést Äást - - - Import Wave File - Zavést soubor Wave + + + Import Audio File + Zavést soubor Audio - + Find unused wave files Najít nepoužívané soubory Wave - + &Quit &UkonÄit - + Song Info Informace o písni - + Transport Panel - Ovládací panel pÅ™esunu + Ovládací panel pÅ™ehrávání - + Bigtime Window Velký ukazatel Äasu - + Mixer A SměšovaÄ A - + Mixer B SměšovaÄ B - + Cliplist Seznam ukázek - + Marker View Pohled se znaÄkami - + Arranger View Pohled s aranžérem - + Fullscreen Celá obrazovka - + &Plugins &Přídavné moduly - + Edit Instrument Upravit nástroj - + Input Plugins Vstupní přídavné moduly - + Transpose PÅ™evedení - + Midi Input Transform PromÄ›nit vstup MIDI - + Midi Input Filter Vstupní filtr MIDI - + Midi Remote Control Vzdálené ovladání MIDI - + Rhythm Generator Generátor rytmu - + Reset Instr. Nastavit nástroj znovu - + Init Instr. Zapnout nástroj - + Local Off Místní vypnuto - + Bounce to Track Odmíchat na stopu - + Bounce to File Odmíchat do souboru - + Restart Audio Spustit zvuk znovu - + Mixer Automation Automatizace míchacího pultu - + Take Snapshot UdÄ›lat snímek - + Clear Automation Data Smazat data automatizace - + Cascade PÅ™ekrývat - + Tile Klást vedle sebe - + In rows V řádcích - + In columns Ve sloupcích - + Global Settings Celková nastavení - + Configure Shortcuts Nastavit klávesové zkratky - + Follow Song Sledovat píseň - + Don't Follow Song Nesledovat píseň - + Follow Page Sledovat píseň na stranách - + Follow Continuous Sledovat píseň stále - + + MusE: Song: + MusE: Píseň: + + + + + Metronome Metronom - + Midi Sync Seřízení MIDI - + Midi File Import/Export Zavedení/Vyvedení souboru MIDI - + Appearance Settings Nastavení vzhledu - + Midi Ports / Soft Synth Přípojky MIDI/Softwarové syntetizátory - + &Manual &PříruÄka - + &MusE Homepage Stránky &MusE - + &Report Bug... &Nahlásit chybu... - + &About MusE &O programu MusE - + Song Position Poloha písnÄ› - + Tempo Tempo - + Signature Taktové oznaÄení - + File Buttons TlaÄítka pro soubor - + Undo/Redo ZpÄ›t/Znovu - + Transport - PÅ™esun + PÅ™ehrávání + + + + Cpu load + Vytížení procesoru + + + + Measured CPU load + Měřené vytížení procesoru + + + + No CPU load data + Žádné údaje o vytížení procesoru - + &File &Soubor - + &View &Pohled - + &Midi &MIDI - + &Audio &Zvuk - + A&utomation A&utomatizace - + &Windows &Okna - + MusE Se&ttings Nas&tavení - + &Help &NápovÄ›da - + About &Qt O &Qt - + Cannot read template Nelze pÅ™eÄíst pÅ™edlohu - + File open error Chyba pÅ™i otevírání souboru - + File read error Chyba pÅ™i Ätení souboru - + Unknown File Format: %1 Neznámý formát souboru: %1 - - - + MusE: Song: %1 MusE: Píseň: %1 - + MusE: load project MusE: Nahrát projekt - + MusE: load template MusE: Nahrát pÅ™edlohu - + MusE: Write File failed MusE: Zápis souboru se nezdaÅ™il - + The current Project contains unsaved data Save Current Project? NynÄ›jší projekt obsahuje neuložená data. Uložit nynÄ›jší projekt? - - + + S&kip &PÅ™eskoÄit - + &Cancel Z&ruÅ¡it - + MusE: Save As MusE: Uložit jako - - + + Nothing to edit Není co upravovat - - - - - + + + + + MusE: Bounce to Track MusE: Odmíchat na stopu - + No wave tracks found Nebyly nalezeny žádné stopy Wave - - + + No audio output tracks found Nebyly nalezeny žádné zvukové výstupní stopy - + Select one audio output track, and one target wave track Vyberte jednu zvukovou výstupní stopu a jednu cílovou stopu Wave - + Select one target wave track Vyberte jednu cílovou stopu Wave - + Select one target wave track, and one audio output track Vyberte jednu cílovou stopu Wave a jednu zvukovou výstupní stopu - - + + MusE: Bounce to File MusE: Odmíchat do souboru - + Select one audio output track Vyberte jednu zvukovou výstupní stopu - + MusE: Bounce MusE: Odmíchat - + set left/right marker for bounce range Nastavit levou/pravou znaÄku pro oblast odmíchání - + The current Project contains unsaved data Load overwrites current Project: Save Current Project? @@ -10535,13 +11448,13 @@ Uložit nynÄ›jší projekt? - - + + &Abort &ZruÅ¡it - + This will clear all automation data on all audio tracks! Proceed? @@ -10550,7 +11463,7 @@ PokraÄovat? - + This takes an automation snapshot of all controllers on all audio tracks, at the current position. @@ -10561,83 +11474,97 @@ PokraÄovat? - + + MusE: Warning + MusE: Varování + + + + The song uses multiple ports but export format 0 (single track) is set. +The first track's port will be used. Playback will likely be wrong + unless the channels used in one port are different from all other ports. +Canceling and setting a different export format would be better. +Continue? + + + + MusE: Export Midi MusE: Vyvést MIDI - + no help found at: Nebyla nalezena žádná nápovÄ›da: - + MusE: Open Help MusE: Otevřít nápovÄ›du - + Unable to launch help Nelze spustit nápovÄ›du - + For some reason MusE has to launch the default browser on your machine. Z urÄitého důvodu musí MusE na vaÅ¡em stroji spustit výchozí prohlížeÄ. - + MusE: Import Midi MusE: Zavést MIDI - + Add midi file to current project? PÅ™idat soubor MIDI do nynÄ›jšího projektu? - + &Add to Project &PÅ™idat do projektu - + &Replace &Nahradit - + reading midifile ÄŒte se soubor MIDI - + failed: NepodaÅ™ilo se: - + Import part is only valid for midi and wave tracks! Zavedení Äásti je platné pouze pro stopy MIDI a Wave! - + MusE: load part MusE: Nahrát Äást - + No track selected for import Pro zavedení nebyla vybrána žádná stopa - + %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. @@ -10650,7 +11577,7 @@ - + %n part(s) could not be imported. Likely the selected track is the wrong type. @@ -10663,17 +11590,80 @@ - + to import an audio file you have first to selecta wave track pro zavedení zvukového souboru nejprve musíte vybrat stopu Wave - + Import Wavefile Zavést soubor Wave - + + This wave file has a samplerate of %1, +as opposed to current setting %2. +File will be resampled from %1 to %2 Hz. +Do you still want to import it? + Tento soubor Wave má vzorkovací kmitoÄet %1, +jako protiklad k nynÄ›jšímu nastavení %2. +Soubor bude pÅ™evzorkován z %1 na %2 Hz +Stále jeÅ¡tÄ› jej chcete zavést? + + + + + + Wave import error + Chyba pÅ™i zavedení WAVE + + + + There are too many wave files +of the same base name as imported wave file +Can not continue. + Je tu příliÅ¡ mnoho souborů WAVE +se stejným základním názvem, jako má +zavedený soubor WAVE. +Nelze pokraÄovat. + + + + Can't create new wav file in project folder! + + Nelze vytvoÅ™it nový soubor WAVE ve složce s projektem! + + + + Failed to initialize sample rate converter! + NepodaÅ™ilo se spustit mÄ›niÄ vzorkovacího kmitoÄtu! + + + + Cancel + ZruÅ¡it + + + + Resampling wave file +"%1" +from %2 to %3 Hz... + PÅ™evzorkovává se soubor WAVE +"%1" +z %2 na %3 Hz... + + + + Output has clipped +Resampling again and normalizing wave file +"%1" +Try %2 of %3... + Výstup byl zastÅ™ižen. +Znovu se pÅ™evzorkovává a normalizuje soubor WAVE +"%1" +Zkuste %2 z %3... + + This wave file has a samplerate of %1, as opposed to current setting %2. Do you still want to import it? @@ -10682,15 +11672,34 @@ Stále jeÅ¡tÄ› jej chcete zavést? - + &Yes &Ano - + &No &Ne + + + File version is %1.%2 +Current version is %3.%4 +Conversions may be applied if file is saved! + Verze souboru je %1.%2 +NynÄ›jší verze je %3.%4 +PÅ™evedení je možné použít, pokud je soubor uložen! + + + + Opening file + Otevírá se soubor + + + + Do not warn again + Nevarovat znovu + MusEGui::NoteInfo @@ -10733,101 +11742,116 @@ MusEGui::PartCanvas - + Cannot copy/move/clone to different Track-Type Nelze kopírovat/pÅ™esunovat/klonovat na jiný typ stopy - + + Part: + Část: + + + C&ut Vyj&mout - + &Copy &Kopírovat - + s&elect &Vybrat - + clones Klony - + rename PÅ™ejmenovat - + color Barva - + delete Smazat - + split RozdÄ›lit - + glue Lepidlo - + super glue (merge selection) Vynikající lepidlo (slouÄit výbÄ›r) - + de-clone ZruÅ¡it klonování - - - + + + save part to disk Uložit Äást na disk - + wave edit Editor Wave - + file info Informace o souboru - + + Normalize + Normalizovat + + + MusE: save part MusE: Uložit Äást - + Part name: %1 Files: Název Äásti: %1 Soubory: - + + Automation: + Automatizace: + + + Remove selected Odstranit vybrané - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -10840,7 +11864,7 @@ - + %n part(s) could not be pasted. Likely the selected track is the wrong type. @@ -10853,32 +11877,32 @@ - + Cannot paste: multiple tracks selected Nelze vložit: vybráno více stop - + Cannot paste: no track selected Nelze vložit: nevybrána žádná stopa - + Can only paste to midi/drum track Vložení možné jen do stopy MIDI/Bicí - + Can only paste to wave track Vložení možné jen do stopy Wave - + Can only paste to midi or wave track Vložení možné jen do stopy MIDI nebo Wave - + Cannot paste: wrong data type Nelze vložit: Nesprávný datový typ @@ -10934,192 +11958,192 @@ MusEGui::PianoRoll - + &Edit Úp&ravy - + C&ut Vyj&mout - + &Copy &Kopírovat - + Copy events in range Kopírovat události v rozsahu - + &Paste &Vložit - + Paste (with dialog) Vložit (s dialogem) - + Delete &Events Smazat &události - + &Select &Vybrat - + Select &All Vybrat &vÅ¡e - + &Deselect All &ZruÅ¡it výbÄ›r vÅ¡eho - + Invert &Selection Obrátit &výbÄ›r - + &Inside Loop &UvnitÅ™ smyÄky - + &Outside Loop &VnÄ› smyÄky - + &Previous Part &PÅ™edchozí Äást - + &Next Part &Další Äást - + Fu&nctions &Funkce - + Quantize Kvantizovat - + Modify Note Length ZmÄ›nit délku noty - + Modify Velocity - ZmÄ›nit sílu nárazu + ZmÄ›nit sílu tónu - + Crescendo/Decrescendo Crescendo/Decrescendo - + Transpose PÅ™evedení - + Erase Events Smazat události - + Move Notes PÅ™esunout noty - + Set Fixed Length Nastavit pevnou délku - + Delete Overlaps Smazat pÅ™ekrytí - + Legato Legato - + &Plugins &Moduly - + Window &Config O&kno - + &Event Color Barva &události - + &Blue &Modrá - + &Pitch colors Barvy pro &výšku tónu - + &Velocity colors - Barvy pro &sílu nárazu + Barvy pro &sílu tónu - + Pianoroll tools Nástroje pro váleÄek - + Step Record Nahrávat po taktu - + Midi Input Vstup MIDI - + Play Events PÅ™ehrát události - + ctrl Ctrl - + Add Controller View PÅ™idat pohled ovladaÄe @@ -11127,157 +12151,210 @@ MusEGui::PluginDialog - + MusE: select plugin MusE: Vybrat přídavný modul - + + Plugin categories. +Right-click on tabs to manage. +Right-click on plugins to add/remove from a category. + Skupiny přídavných modulů. +Klepnutí pravým tlaÄítkem myÅ¡i pro spravování. +Klepnutí pravým tlaÄítkem myÅ¡i na přídavné moduly pro pÅ™idání/odstranÄ›ní ze skupiny. + + + Type Typ - + Lib Lib - + Label Å títek - + Name Název - + AI Al - + AO AO - + CI Cl - + CO CO - + IP IP - + id ID - + Maker Výrobce - + Copyright Autorské právo - + Audio inputs Vstupy zvuku - + Audio outputs Výstupy zvuku - + Control inputs Vstupy ovládání - + Control outputs Výstupy ovládání - + In-place capable Schopen v místÄ› - + ID number Číslo ID - + + &create new group + &VytvoÅ™it novou skupinu + + + + &delete currently selected group + &Smazat nyní vybranou skupinu + + + + re&name currently selected group + &PÅ™ejmenovat nyní vybranou skupinu + + + + Associated categories + Sdružené skupiny + + + + You need to define some categories first. + Nejprve musíte stanovit nÄ›jaké skupiny. + + + + + new group + Nová skupina + + + + + Enter the new group name + Zadat nový název skupiny + + + + Wine VST + Wine VST + + Ok - OK + OK - Cancel - ZruÅ¡it + ZruÅ¡it - Show plugs: - Ukázat přídavné moduly: + Ukázat přídavné moduly: - Mono and Stereo - Mono a stereo + Mono a stereo - Stereo - Stereo + Stereo - Mono - Mono + Mono - Show All - Ukázat vÅ¡e + Ukázat vÅ¡e - + Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. Vyberte, které typy přídavných modulů mají být v seznamu viditelné.<br>UvÄ›domte si, že použití přídavných modulů monona stopy stereo není problém, dva budou použity souběžnÄ›.<br>Dejte si pozor na to, že alternativa 'vÅ¡e' zahrnuje přídavné moduly, které v pÅ™ihrádce s efekty nemusí být užiteÄné. - Search in 'Label' and 'Name': - Hledat ve 'Å títek' a 'Název': + Hledat ve 'Å títek' a 'Název': - + dssi synth syntetizátor dssi - + dssi effect efekt dssi - + + LV2 synth + Syntetizátor LV2 + + + + LV2 effect + Efekt LV2 + + + ladspa ladspa @@ -11285,43 +12362,51 @@ MusEGui::PluginGui - + File Buttons TlaÄítka pro soubor - + Load Preset Nahrát pÅ™ednastavení - + Save Preset Uložit pÅ™ednastavení - - + + bypass plugin PÅ™eskoÄit přídavný modul pro tok signálu - + MusE: load preset MusE: Nahrát pÅ™ednastavení - + Error reading preset. Might not be right type for this plugin Chyba pÅ™i Ätení pÅ™ednastavení. Nemusí to být správný typ pro tento přídavný modul - + MusE: save preset MusE: Uložit pÅ™ednastavení + MusEGui::PopupMenu + + + <More...> %1 + <Více...> %1 + + + MusEGui::ProjectCreateImpl @@ -11330,67 +12415,216 @@ + MusEGui::RouteDialog + + + Normal + Normální + + + + Alias 1 + PÅ™ezdívka 1 + + + + Alias 2 + PÅ™ezdívka 2 + + + + + Source + Zdroj + + + + + Destination + Cíl + + + + <none> + <Žádný> + + + MusEGui::RoutePopupMenu - - - - - - - - + Channel Kanál - - - - + <none> <Žádný> - - - + + + + Soloing chain ŘetÄ›z sóla - - + + Channel grouping: + Seskupení kanálů: + + + + Mono + Mono + + + + Stereo + Stereo + + + + + + + + + + + + Channels + Kanály + + + + + Midi ports/devices + Přípojky/Zařízení MIDI + + + + + + Omni + Omni + + + + Show aliases: + Ukázat pÅ™ezdívky: + + + + First + První + + + + Second + Druhá + + + + Show names + Ukázat názvy + + + + Show first aliases + Ukázat první pÅ™ezdívku + + + + Show second aliases + Ukázat druhou pÅ™ezdívku + + + + + Jack ports + Přípojky JACK + + + + + Connect + Spojit + + + + Unavailable + Nedostupné + + + + Open advanced router... + Otevřít pokroÄilý smÄ›rovaÄ... + + + + Output routes: + Výstupní signálový tok: + + + + Input routes: + Vstupní signálový tok: + + + + + Audio returns ZpáteÄní kanály zvuku - + + Midi sends + Odeslání MIDI + + + + Sources: + Zdroje: + + + Warning: No input devices! Varování: Žádná vstupní zařízení! - + Open midi config... Otevřít nastavení MIDI... - Toggle all PÅ™epnout vÅ¡e - + More... Více... - + + + Tracks + Stopy + + + + Destinations: + Cíle: + + + Audio sends Zvuk posílá - Midi port sends Přípojka MIDI posílá @@ -11398,42 +12632,42 @@ MusEGui::ScoreCanvas - + Treble Houslový klÃ­Ä - + Bass Basový klÃ­Ä - + Grand Staff Oba klíÄe - + Remove staff Odstranit osnovu - + Ambiguous part Nejasná Äást - + There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. Jsou dvÄ› nebo tÅ™i možné Äásti, ke kterým se dá nota pÅ™idat, ale žádná není vybrána. Vyberte, prosím, cílovou Äást klepnutím na jakoukoli notu k ní patřící, a zkuste to znovu, nebo pÅ™idejte novou notovou osnovu, která bude obsahovat jen cílovou Äást. - + No part Žádná Äást - + There are no parts you could add the note to. Nejsou tu žádné Äásti, do kterých byste mohl pÅ™idat notu. @@ -11441,246 +12675,246 @@ MusEGui::ScoreEdit - + Step recording tools Nástroje pro nahrávání po taktu - + Step Record Nahrávat po taktu - + Note settings Nastavení noty - + Note length: Délka noty: - + last poslední - - - + + + Apply to new notes: Použít na nové noty: - - + + Apply to selected notes: Použít na vybrané noty: - + Velocity: - Síla nárazu: + Síla tónu: - + Off-Velocity: - Síla nárazu vypnuto: + Síla tónu vypnuto: - + Quantisation settings Nastavení kvantizace - + Quantisation: Kvantizace: - + Pixels per whole: Pixelů na celou notu: - + &Edit Úp&ravy - + C&ut Vyj&mout - + &Copy &Kopírovat - + Copy events in range Kopírovat události v rozsahu - + &Paste &Vložit - + Paste (with dialog) Vložit (s dialogem) - + Delete &Events Smazat &události - + &Select &Vybrat - + Select &All Vybrat &vÅ¡e - + &Deselect All &ZruÅ¡it výbÄ›r vÅ¡eho - + Invert &Selection Obrátit &výbÄ›r - + &Inside Loop &UvnitÅ™ smyÄky - + &Outside Loop &VnÄ› smyÄky - + Fu&nctions &Funkce - + &Quantize &Kvantizovat - + Change note &length ZmÄ›nit &délku noty - + Change note &velocity - ZmÄ›nit &sílu nárazu noty + ZmÄ›nit &sílu tónu noty - + Crescendo/Decrescendo Crescendo/Decrescendo - + Transpose PÅ™evedení - + Erase Events Smazat události - + Move Notes PÅ™esunout noty - + Set Fixed Length Nastavit pevnou délku - + Delete Overlaps Smazat pÅ™ekrytí - + Legato Legato - + Window &Config O&kno - + Note head &colors &Barvy pro hlaviÄky not - + &Black ÄŒe&rná - + &Velocity - &Síla nárazu + &Síla tónu - + &Part Čá&st - + Set up &preamble Nastavit úv&od - + Display &key signature Zobrazit &pÅ™edznamenání - + Display &time signature Zobrazit &taktové oznaÄení - + Set Score &name Nastavit &název notového zápisu - - + + Enter the new score title Zadat nový název notového zápisu - + Error Chyba - + Changing score title failed: the selected title is not unique ZmÄ›na názvu notového zápisu se nezdaÅ™ila: @@ -11690,17 +12924,17 @@ MusEGui::ScrollScale - + next page Další strana - + previous page PÅ™edchozí strana - + current page number Číslo nynÄ›jší strany @@ -11708,27 +12942,68 @@ MusEGui::ShortcutCaptureDialog - + Ok OK - + Cancel ZruÅ¡it - + Shortcut conflicts with %1 Klávesová zkratka se stÅ™etává s %1 - + Undefined Nestanoveno + MusEGui::ShortcutConfig + + + Save printable text file + Uložit tisknutelný soubor s textem + + + + Text files (*.txt);;All files (*) + Textové soubory (*.txt);;VÅ¡echny soubory (*) + + + + + Error + Chyba + + + + Error opening file for saving + Chyba pÅ™i otevírání souboru pro uložení + + + + Shortcuts for selected category: + Zkratky pro vybranou skupinu: + + + + Legend: + + VysvÄ›tlivky: + + + + + An error occurred while saving + PÅ™i ukládání se vyskytla chyba + + + MusEGui::SigScale @@ -11752,7 +13027,7 @@ MusEGui::Strip - + Remove track? Odstranit stopu? @@ -11760,236 +13035,278 @@ MusEGui::TList - + <none> <Žádný> - + visible Viditelný - + no clef Žádný klÃ­Ä - + Treble Houslový klÃ­Ä - + Bass Basový klÃ­Ä - + Grand Oba klíÄe - - + + off Vypnuto - + <unknown> <neznámý> - + MusE: bad trackname MusE: Å patný název stopy - + please choose a unique track name Vyberte, prosím, jedineÄný název pro stopu - + Unused Devices Nepoužívaná zařízení - - Update drummap? - Obnovit rozložení bicích? + Obnovit rozložení bicích? - Do you want to use same port for all instruments in the drummap? - Chcete pro vÅ¡echny nástroje v rozložení bicích použít stejnou přípojku? + Chcete pro vÅ¡echny nástroje v rozložení bicích použít stejnou přípojku? - - &Yes - &Ano + &Ano - - &No - &Ne + &Ne - - + + show gui Ukázat uživatelské rozhraní - - + + show native gui Ukázat původní rozhraní - - Midi control - Ovládání MIDI + + + Presets + PÅ™ednastavení - - Assign - PÅ™iÅ™adit + + Clear all controller events? + Smazat vÅ¡echny události ovladaÄe? - - Clear - Smazat + + &Ok + &OK - - Treble clef - Houslový klÃ­Ä + + &Cancel + Z&ruÅ¡it - + + Change color + ZmÄ›nit barvu + + + + Midi control + Ovládání MIDI + + + + Assign + PÅ™iÅ™adit + + + + Clear + Smazat + + + + Other + Jiné + + + + clear automation + Smazat automatizaci + + + + Treble clef + Houslový klÃ­Ä + + + Bass clef Basový klÃ­Ä - + Grand Staff Oba klíÄe - + Viewable automation Viditelná automatizace - + Internal VnitÅ™ní - + Synth Syntetizátor - + Delete Track Smazat stopu - + Track Comment Poznámka ke stopÄ› - + Save track's drumlist Uložit seznam bicích stopy - + Save track's drumlist differences to initial state Uložit rozdíly seznamu bicích stopy do poÄáteÄního stavu - + Load track's drumlist Nahrát seznam bicích stopy - + Reset track's drumlist Nastavit seznam bicích stopy znovu - + Reset track's drumlist-ordering Nastavit znovu poÅ™adí seznamu bicích stopy - + Copy track's drumlist to all selected tracks Kopírovat seznam bicích stopy do vÅ¡ech vybraných stop - + Copy track's drumlist's differences to all selected tracks Kopírovat rozdíly seznamu bicích stopy do vÅ¡ech vybraných stop - + Insert Track Vložit stopu - - + + Drum map Rozložení bicích - + Reset the track's drum map with instrument defaults? Nastavit rozložení bicích stopy znovu s výchozími nástroji? - + Reset the track's drum map ordering? Nastavit znovu poÅ™adí rozložení bicích stopy? - + Muse: Load Track's Drum Map MusE: Nahrát rozložení bicích stopy - + + Drummap + Rozložení bicích + + + + This drummap was created with a previous version of MusE, +it is being read but the format has changed slightly so some +adjustments may be necessary. + Tato mapa bicích byla vytvoÅ™ena v pÅ™edchozí verzi MusE. +Dá se pÅ™eÄíst, ale formát se trochu zmÄ›nil, takže budou +potÅ™eba nÄ›jaké úpravy. + + + MusE: Store Track's Drum Map MusE: Uložit rozložení bicích stopy - + Midi MIDI - + Drum Bicí - + New style drum Bicí v novém stylu - Do you want to use same port and channel for all instruments in the drummap? - Chcete pro vÅ¡echny nástroje v rozložení bicích použít stejnou přípojku a týž kanál? + Chcete pro vÅ¡echny nástroje v rozložení bicích použít stejnou přípojku a týž kanál? @@ -12023,17 +13340,17 @@ Vypnuto - + Solo Sólo - + Cursor Poloha ukazovátka - + Snap Magnet @@ -12041,102 +13358,107 @@ MusEGui::TopWin - + As subwindow Jako podokno - + Shares tools and menu Sdílí nástroje a nabídku - + Fullscreen Celá obrazovka - + Undo/Redo tools Nástroje ZpÄ›t/Znovu - + Panic Nouzové zastavení - + + Metronome + Metronom + + + Transport - PÅ™esun + PÅ™ehrávání - + Song Position Poloha písnÄ› - + Tempo Tempo - + Signature Taktové oznaÄení - + Piano roll VáleÄek - + List editor Editor seznamu - + Drum editor Editor bicích - + Master track editor Editor hlavní stopy - + Master track list editor Editor seznamu hlavní stopy - + Wave editor Editor Wave - + Clip list Seznam ukázek - + Marker view Pohled se znaÄkami - + Score editor Editor notového zápisu - + Arranger Aranžér - + <unknown toplevel type> <Neznámý typ nejvyšší úrovnÄ›> @@ -12328,7 +13650,7 @@ Jack transport sync on/off - Seřízení pÅ™esunu JACK zapnuto/vypnuto + Seřízení pÅ™ehrávání JACK zapnuto/vypnuto @@ -12392,104 +13714,199 @@ + MusEGui::WaveCanvas + + + Adjust Wave Offset + Upravit posun WAVE + + + + Wave offset (frames) + Posun WAVE (snímky) + + + + Part creation failed + VytvoÅ™ení Äásti se nezdaÅ™ilo + + + + Left and right position markers must be placed inside the current part. + ZnaÄky pro levou a pravou polohu musí být umístÄ›ny uvnitÅ™ nynÄ›jší Äásti. + + + + Part created + Část vytvoÅ™ena + + + + The selected region has been copied to the clipboard and can be pasted in the arranger. + Vybraná oblast byla zkopírována do schránky a je možné ji vložit do aranžéru. + + + + MusE - external editor failed + MusE: VnÄ›jší editor selhal + + + + MusE was unable to launch the external editor +check if the editor setting in: +Global Settings->Audio:External Waveditor +is set to a valid editor. + MusE se nepodaÅ™ilo spustit vnÄ›jší editor. +Prověřte, zda je nastavení editoru v Celková +nastavení->Zvuk: VnÄ›jší editor Wave nastaven +na platný editor. + + + + MusE - file size changed + MusE: Velikost souboru zmÄ›nÄ›na + + + + When editing in external editor - you should not change the filesize +since it must fit the selected region. + +Missing data is muted + Když provádíte úpravy ve vnÄ›jším editoru, nemÄ›li byste mÄ›nit +velikost souboru, protože tato se musí vejít do vybrané oblasti. + +ChybÄ›jící data jsou ztlumena + + + MusEGui::WaveEdit - + &Edit Úp&ravy - + Func&tions &Funkce - + &Gain &Zesílení - + Other Jiné - + &Copy &Kopírovat - + + &Create Part from Region + &VytvoÅ™it Äást z oblasti + + + C&ut Vyj&mout - + &Paste &Vložit - + Edit in E&xternal Editor Upravit ve &vnÄ›jším editoru - + Mute Selection Ztlumit výbÄ›r - + Normalize Selection Normalizovat výbÄ›r - + Fade In Selection Postupné zesílení signálu ve výbÄ›ru - + Fade Out Selection Postupné zeslabení signálu ve výbÄ›ru - + Reverse Selection Obrátit výbÄ›r - + Select Vybrat - + Select &All Vybrat &vÅ¡e - + &Deselect All &ZruÅ¡it výbÄ›r vÅ¡eho - + + &Previous Part + &PÅ™edchozí Äást + + + + &Next Part + &Další Äást + + + Window &Config O&kno - + + &Event Color + Barva &události + + + + &Part colors + Barvy Äá&stí + + + + &Gray + Å &edá + + + WaveEdit tools Nástroje pro úpravy Wave - + Solo Sólo - + Cursor Poloha ukazovátka @@ -12497,33 +13914,29 @@ MusEGui::WaveView - MusE - external editor failed - MusE: VnÄ›jší editor selhal + MusE: VnÄ›jší editor selhal - MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. - MusE se nepodaÅ™ilo spustit vnÄ›jší editor. + MusE se nepodaÅ™ilo spustit vnÄ›jší editor. Prověřte, zda je nastavení editoru v Celková nastavení->Zvuk: VnÄ›jší editor Wave nastaven na platný editor. - MusE - file size changed - MusE: Velikost souboru zmÄ›nÄ›na + MusE: Velikost souboru zmÄ›nÄ›na - When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted - Když provádíte úpravy ve vnÄ›jším editoru, nemÄ›li byste mÄ›nit + Když provádíte úpravy ve vnÄ›jším editoru, nemÄ›li byste mÄ›nit velikost souboru, protože tato se musí vejít do vybrané oblasti. ChybÄ›jící data jsou ztlumena @@ -12649,7 +14062,7 @@ Velocity - Síla nárazu + Síla tónu @@ -12797,6 +14210,59 @@ + PluginDialogBase + + + Dialog + Dialog + + + + Ports: + Přípojky: + + + + Mono + Mono + + + + Mono + Stereo + Mono + Stereo + + + + Stereo + Stereo + + + + All + VÅ¡e + + + + Plugin type: + Druh přídavného modulu: + + + + Filter: + Filtr: + + + + &OK + &OK + + + + &Cancel + Z&ruÅ¡it + + + ProjectCreate @@ -12810,8 +14276,8 @@ - - + + ... ... @@ -12842,16 +14308,27 @@ + &Create project folder (recommended for audio projects) + &VytvoÅ™it složku s projektem (doporuÄeno pro projekty se zvukem) + + Create project folder (recommended for audio projects) - VytvoÅ™it složku s projektem (doporuÄeno pro projekty se zvukem) + VytvoÅ™it složku s projektem (doporuÄeno pro projekty se zvukem) - + Song information: Informace o písni: + QMenu + + Select gui type + Vybrat typ rozhraní + + + QObject @@ -12865,44 +14342,153 @@ Please first select the range for crescendo with the loop markers. Nejprve, prosím, zvolte rozsah crescenda se znaÄkami pro smyÄku. + + + Controller ! + OvladaÄ! + + + + Other ! + Jiné! + + + + Select gui type + Vybrat typ rozhraní + + + + Preset actions + ÄŒinnosti pÅ™ednastavení + + + + Save preset... + Uložit pÅ™ednastavení... + + + + Update list + Obnovit seznam + + + + Saved presets + Uložená pÅ™ednastavení + + + + No presets found + Nenalezeno žádné pÅ™ednastavení + + + + Enter new preset name + Zadat nový název pÅ™ednastavení + + + + Midi programs + Programy MIDI + + + + Presets + PÅ™ednastavení + + + + Switch on General Midi Level 1 mode + Zapnout režim Obecné MIDI úroveň 1 + + + + Switch on General Midi Level 2 mode + Zapnout režim Obecné MIDI úroveň 2 + + + + Switch off General Midi Level 1 or 2 + Vypnout režim Obecné MIDI úroveň 1 nebo 2 + + + + Switch on Roland GS mode + Zapnout režim Roland GS + + + + Switch on Yamaha XG mode + Zapnout režim Yamaha XG + + + + Tracks: + Stopy: + + + + Midi ports: + Přípojky MIDI: + + + + Midi devices: + Zařízení MIDI: + + + + Jack: + JACK. + + + + Jack midi: + MIDI JACK: + QWidget - - + + Cannot convert sysex string Nelze pÅ™evést Å™etÄ›zec sysex - - + Hex String too long (2048 bytes limit) Å estnáctkový Å™etÄ›zec je příliÅ¡ dlouhý (hranice je 2048 bytů) - + generic midi Obecné MIDI - + new Nový - - + + None Žádný - + create peakfile for VytvoÅ™it soubor s vrcholem hladiny pro - + + No wave events selected. + Nevybrány žádné události vlny. + + + No selection. Ignoring Žádný výbÄ›r. PÅ™ehlíží se @@ -12933,7 +14519,7 @@ VytvoÅ™ení adresáře se nezdaÅ™ilo - + File %1 exists. Overwrite? @@ -12942,12 +14528,12 @@ existuje. PÅ™epsat? - + MusE: write MusE: Zapsat - + Open File %1 failed: %2 @@ -12956,10 +14542,41 @@ se nepodaÅ™ilo otevřít: %2 - + MusE: Open File MusE: Otevřít soubor + + + Instrument-defined + Vymezeno nástrojovÄ› + + + + + Edit instrument ... + Upravit nástroj... + + + + Add + PÅ™idat + + + + Others + Jiné + + + + Common Controls + Obecné ovládání + + + + Velocity + Síla tónu + QuantBase @@ -13147,7 +14764,7 @@ Velocity - Síla nárazu + Síla tónu @@ -13168,8 +14785,8 @@ p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud není zaÅ¡krtnuto nic, je odstranÄ›no vÅ¡e.</p> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud je zaÅ¡krtnuta síla nárazu (dynamika), jsou odstranÄ›ny pouze noty se silou nárazu (dynamiky) &lt; práh.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud je zaÅ¡krtnuto obojí, jsou odstranÄ›ny noty se silou nárazu (dynamiky) &lt; práh NEBO noty s délkou &lt; práh.</p></body></html> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud je zaÅ¡krtnuta síla tónu (dynamika), jsou odstranÄ›ny pouze noty se silou tónu (dynamiky) &lt; práh.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud je zaÅ¡krtnuto obojí, jsou odstranÄ›ny noty se silou tónu (dynamiky) &lt; práh NEBO noty s délkou &lt; práh.</p></body></html> @@ -13190,52 +14807,142 @@ MusE: Tok signálu - + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. + Okno s pohledem na spojení. +Ukáže vÅ¡echna nynÄ›jší spojení. +Tlusté Äáry jsou cesty Omni. +Tenké Äáry jsou cesty kanálů. + + + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + + Itemized list of current connections. + Položkový seznam nynÄ›jších spojení. + + + + + Show only selected sources + Ukázat pouze vybrané zdroje + + + + + Show only destination routes for selected source + Ukázat pouze cílové cesty pro vybraný zdroj + + + + + Show only selected destinations + Ukázat pouze vybrané cíle + + + + + Show only source routes for selected destination + Ukázat pouze zdrojové cesty pro vybraný cíl + + + + + Show all Midi Ports + Ukázat vÅ¡echny přípojky MIDI + + + + Auto adjust column size + Automaticky pÅ™izpůsobit velikost sloupce + + + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + + + + + + Preferred route name or alias + UpÅ™ednostňovaný název cesty nebo pÅ™ezdívka + + + + + Connect source to destination + Spojit zdroj s cílem + + + + + Remove selected route + Odstranit vybraný tok signálu + + Add Route PÅ™idat tok signálu - Source: Zdroj: - Destination: Cíl: - connect source to destination Spojit zdroj s cílem - + Connect Spojit - Current Routes NynÄ›jší toky signálu - Source Zdroj - Destination Cíl - remove selected route Odstranit vybraný tok signálu - + Remove Odstranit @@ -13296,22 +15003,22 @@ SS_PluginFront - + Clear and unload effect Smazat a vyložit efekt - + Load effect Nahrát efekt - + Toggle display of effect parameters PÅ™epnout zobrazení parametrů efektů - + Turn effect on/off Zapnout/Vypnout efekt @@ -13465,31 +15172,66 @@ Alt+A Alt+A - - - SimpleDrumsGuiBase - - DrumSynth 0.1 - Syntetizátor bicích 0.1 + + &Printable file... + &Tisknutelný soubor... - - - SimpleSynthGui - + + Alt+P + Alt+P + + + + &Ok + &OK + + + + Alt+O + Alt+O + + + + SimpleDrumsGuiBase + + + DrumSynth 0.1 + Syntetizátor bicích 0.1 + + + + SimpleSynthGui + + + Mix + SmÄ›s + + + + Chn + Kan + + + + Channel routing + SmÄ›rování kanálu + + + &Load setup &Nahrát nastavení - + &Save setup &Uložit nastavení - + Load sample dialog - Dialog pro nahrání ukázky + Dialog pro nahrání vzorku @@ -13528,77 +15270,128 @@ SynthConfigBase - Midi Port and Soft Synth Configuration Nastavení přípojky MIDI a softwarového syntetizátoru - Instances Instance - - + Name Název - - + Type Typ - Midi Port Přípojka MIDI - Remove Instance Odstranit instanci - Midi connections Spojení MIDI - Soft Synthesizer Softwarový syntetizátor - Add Instance PÅ™idat instanci - + + Configure midi devices, midi ports, and synthesizers + Nastavit zařízení MIDI, přípojky MIDI a softwarové syntetizátory + + + + Available soft synthesizers + Dostupné softwarové syntetizátory + + + list of available software synthesizers Seznam dostupných softwarových syntetizátorů - + File Soubor - + Inst Inst - + Version Verze - + Description Popis + + + Useable devices + Použitelná zařízení + + + + Add: + PÅ™idat: + + + + Synth + Syntetizátor + + + + ALSA + ALSA + + + + JACK + JACK + + + + Rename + PÅ™ejmenovat + + + + Remove + Odstranit + + + + Device port assignments + PÅ™iÅ™azení přípojek zařízením + + + + &Apply + &Použít + + + + &OK + &OK + TransposeBase @@ -13693,13 +15486,13 @@ VAMGui - + MusE: Load VAM Presets MusE: Nahrát pÅ™ednastavení VAM - - + + MusE: Save VAM Presets MusE: Uložit pÅ™ednastavení VAM @@ -13889,7 +15682,7 @@ On Zapnuto - + VAM 1.0beta3 Virtual Analog for MusE @@ -13916,7 +15709,7 @@ MusE: Modify Velocity - MusE: ZmÄ›nit sílu nárazu + MusE: ZmÄ›nit sílu tónu @@ -13982,174 +15775,182 @@ file_patterns - + Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) - + Midi (*.mid *.MID *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) - + Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) - - - - - - - - - - - - + + + + + + + + + + + + All Files (*) VÅ¡echny soubory (*) - + Midi (*.mid) Midi (*.mid) - + Karaoke (*.kar) Karaoke (*.kar) - + all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) VÅ¡echny známé soubory (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) - + med Files (*.med *.med.gz *.med.bz2) Soubory med (*.med *.med.gz *.med.bz2) - - - + + + Uncompressed med Files (*.med) NestlaÄené soubory med (*.med) - - - + + + gzip compressed med Files (*.med.gz) Soubory med stlaÄené gzip (*.med.gz) - - - + + + bzip2 compressed med Files (*.med.bz2) Soubory med stlaÄené bzip2 (*.med.bz2) - + mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) Soubory mid (*.mid *.midi *.kar *.MID *.MIDI *.KAR) - + (*.jpg *.gif *.png) (*.jpg *.gif *.png) - + (*.jpg) (*.jpg) - + (*.gif) (*.gif) - + (*.png) (*.png) - + part Files (*.mpt *.mpt.gz *.mpt.bz2) Soubory part (*.mpt *.mpt.gz *.mpt.bz2) - + part Files (*.mpt) Soubory part (*.mpt) - + gzip compressed part Files (*.mpt.gz) Soubory part stlaÄené gzip (*.mpt.gz) - + bzip2 compressed part Files (*.mpt.bz2) Soubory part stlaÄené bzip2 (*.mpt.bz2) - + Presets (*.pre *.pre.gz *.pre.bz2) PÅ™ednastavení (*.pre *.pre.gz *.pre.bz2) - + Presets (*.pre) PÅ™ednastavení (*.pre) - + gzip compressed presets (*.pre.gz) PÅ™ednastavení stlaÄená gzip (*.pre.gz) - + bzip2 compressed presets (*.pre.bz2) PÅ™ednastavení stlaÄená bzip2 (*.pre.bz2) - + Presets (*.map *.map.gz *.map.bz2) PÅ™ednastavení (*.map *.map.gz *.map.bz2) - + Presets (*.map) PÅ™ednastavení (*.map) - + gzip compressed presets (*.map.gz) PÅ™ednastavení stlaÄená gzip (*.map.gz) - + bzip2 compressed presets (*.map.bz2) PÅ™ednastavení stlaÄená bzip2 (*.map.bz2) - + + Wave/Binary (*.wav *.ogg *.flac *.bin) + WAV/Binární (*.wav *.ogg *.flac *.bin) + + + + Wave (*.wav *.ogg *.flac) + WAV (*.wav *.ogg *.flac) + + Wave/Binary (*.wav *.ogg *.bin) - Wave/Binární (*.wav *.ogg *.bin) + Wave/Binární (*.wav *.ogg *.bin) - Wave (*.wav *.ogg) - Wave (*.wav *.ogg) + Wave (*.wav *.ogg) - + Binary (*.bin) Binární (*.bin) @@ -14444,882 +16245,1004 @@ + midiWarnInitPendingBase + + + Instrument initialization + Inicializace nástroje + + + + MusE should now send some Instrument Initialization Sequences. +The sequences (usually System Exclusive messages) are defined + by the selected instruments in the Settings -> Midi Ports dialog, + such as the GM (default), GS, or XG instruments. + +Typically you should answer yes here. +You can always do it manually from the Midi menu. + +Continue? + + + + + Don't ask me again + Neptat se znovu + + + shortcuts - + Transport: Start playback from current location - PÅ™esun: ZaÄít pÅ™ehrávání od nynÄ›jší polohy + PÅ™ehrávání: ZaÄít pÅ™ehrávání od nynÄ›jší polohy - + Transport: Toggle metronome - PÅ™esun: Zapnout/Vypnout metronom + PÅ™ehrávání: Zapnout/Vypnout metronom - + Transport: Stop Playback - PÅ™esun: Zastavit pÅ™ehrávání + PÅ™ehrávání: Zastavit pÅ™ehrávání - + Transport: Goto Start - PÅ™esun: Jít na zaÄátek + PÅ™ehrávání: Jít na zaÄátek - + Transport: Play, Stop, Rewind - PÅ™esun: PÅ™ehrát, Zastavit, PÅ™etoÄit + PÅ™ehrávání: PÅ™ehrát, Zastavit, PÅ™etoÄit + + + + Transport: Restart recording + PÅ™ehrávání: ZaÄít nahrávání znovu Transport: Goto left marker - PÅ™esun: Jít na levou znaÄku + PÅ™ehrávání: Jít na levou znaÄku Transport: Goto right marker - PÅ™esun: Jít na pravou znaÄku + PÅ™ehrávání: Jít na pravou znaÄku Transport: Toggle Loop section - PÅ™esun: PÅ™epnout smyÄku + PÅ™ehrávání: PÅ™epnout smyÄku Transport: Toggle Record - PÅ™esun: Zapnout/Vypnout nahrávání + PÅ™ehrávání: Zapnout/Vypnout nahrávání Transport: Clear all rec enabled tracks - PÅ™esun: Vrátit zpÄ›t vÅ¡echny stopy oznaÄené pro nahrávání + PÅ™ehrávání: Vrátit zpÄ›t vÅ¡echny stopy oznaÄené pro nahrávání Toggle fullscreen - PÅ™epnout režim na celou obrazovku + PÅ™epnout zobrazení na celou obrazovku - + Edit: Copy Úpravy: Kopírovat - + Edit: Copy in range Úpravy: Kopírovat v rozsahu - + Edit: Undo Úpravy: ZpÄ›t - + Edit: Redo Úpravy: Znovu - + Edit: Cut Úpravy: Vyjmout - + Edit: Paste Úpravy: Vložit - + + Edit: Paste to selected track + Úpravy: Vložit do vybrané stopy + + + + Edit: Paste clone + Úpravy: Vložit klona + + + + Edit: Paste clone to selected track + Úpravy: Vložit klona do vybrané stopy + + + Edit: Paste (with dialog) Úpravy: Vložit (s dialogem) - + Edit: Delete Úpravy: Smazat - + File: New project Soubor: Nový projekt - + File: Open from disk Soubor: Otevřít - + File: Save project Soubor: Uložit projekt - + File: Open recent file Soubor: Otevřít poslední soubor - + File: Save as Soubor: Uložit jako - + File: Load template Soubor: Nahrát pÅ™edlohu - + File: Import midi file Soubor: Zavést soubor MIDI - + File: Export midi file Soubor: Vyvést soubor MIDI - + File: Import midi part Soubor: Zavést MIDI part - + File: Import audio file Soubor: Zavést zvukový soubor - + File: Quit MusE Soubor: UkonÄit MusE - + Edit: Select parts on track Úpravy: Vybrat Äásti na stopÄ› - + Open pianoroll Otevřít váleÄek - + Open drumeditor Otevřít editor bicích - + Open listeditor Otevřít editor seznamu - + Open waveeditor Otevřít editor Wave - + Open graphical mastertrack editor Otevřít obrazový editor hlavní stopy - + Open list mastertrack editor Otevřít editor seznamu hlavní stopy - + Open midi transformer Otevřít mÄ›niÄ MIDI - + Add midi track PÅ™idat stopu MIDI - + Add drum track PÅ™idat stopu bicích - + Add new style drum track PÅ™idat stopu bicích v novém stylu - + Add wave track PÅ™idat stopu Wave - + Add audio output PÅ™idat výstup zvuku - + Add audio group PÅ™idat skupinu zvuku - + Add audio input PÅ™idat vstup zvuku - + Add audio aux PÅ™idat aux zvuku - + Structure: Global cut Stavba: Celkové vyjmutí - + Structure: Global insert Stavba: Celkové vložení - + Structure: Global split Stavba: Celkové rozdÄ›lení - + Structure: Cut events Stavba: Vyjmout události - + View: Open mixer #1 window Pohled: Otevřít okno se směšovaÄem 1 - + View: Open mixer #2 window Pohled: Otevřít okno se směšovaÄem 2 - + View: Toggle transport window - Pohled: PÅ™epnout okno pro pÅ™esun + Pohled: PÅ™epnout okno pro pÅ™ehrávání - + View: Toggle bigtime window Pohled: PÅ™epnout velký ukazatel Äasu - + View: Open marker window Pohled: Otevřít okno se znaÄkou - + Settings: Follow song by page Nastavení: Sledovat píseň na nÄ›kolika stranách - + Settings: Follow song off Nastavení: Nesledovat píseň - + Settings: Follow song continuous Nastavení: Sledovat píseň stále - + Settings: Global configuration Nastavení: Celková nastavení - + Settings: Configure shortcuts Nastavení: Nastavit klávesové zkratky - + Settings: Configure metronome Nastavení: Nastavit metronom - + Settings: Midi sync configuration Nastavení: Nastavení seřízení MIDI - + Settings: Midi file import/export configuration Nastavení: Nastavení zavedení/vyvedení souboru MIDI - + Settings: Appearance settings Nastavení: Nastavení vzhledu - + Settings: Midi ports / Soft Synth Nastavení: Přípojky MIDI/Softwarové syntetizátory - + Settings: Audio subsystem configuration Nastavení: Nastavení zvukového podsystému - + Midi: Edit midi instruments Midi: Upravit nástroje MIDI - + Midi: Open midi input transform Midi: Otevřít promÄ›nu vstupu MIDI - + Midi: Open midi input filter Midi: Otevřít filtr vstupu MIDI - + Midi: Midi input transpose Midi: PÅ™evedení vstupu MIDI - + Midi: Midi remote control Midi: Vzdálené ovladání MIDI - + Midi: Random rhythm generator Midi: Generátor náhodného rytmu - + Midi: Reset midi Midi: Nastavit MIDI znovu - + Midi: Init midi Midi: Inicializovat MIDI - + Midi: Midi local off Midi: Místní vypnuto - + Audio: Bounce audio to track Zvuk: Odmíchat zvuk na stopu - + Audio: Bounce audio to file Zvuk: Odmíchat zvuk do souboru - + Audio: Restart audio Zvuk: Spustit zvuk znovu - + Automation: Mixer automation Automatizace: Automatizace míchacího pultu - + Automation: Take mixer snapshot Automatizace: UdÄ›lat snímek směšovaÄe - + Automation: Clear mixer automation Automatizace: Smazat automatizaci směšovaÄe - + Help: Open Manual NápovÄ›da: Otevřít příruÄku - + Help: Toggle whatsthis mode NápovÄ›da: PÅ™epnout režim "Co je to?" - + Edit: Edit selected part Úpravy: Upravit vybranou Äást - + Edit: Select nearest part on track above Úpravy: Vybrat nejbližší Äást na pÅ™edchozí stopÄ› - + Edit: Add nearest part on track above Úpravy: PÅ™idat nejbližší Äást do pÅ™edchozí stopy - + Edit: Select nearest part on track below Úpravy: Vybrat nejbližší Äást na další stopÄ› - + Edit: Add nearest part on track below Úpravy: PÅ™idat nejbližší Äást do další stopy - + Edit: Insert empty measure Úpravy: Vložit prázdný takt - Edit: Paste as clones - Úpravy: Vložit jako klony + Úpravy: Vložit jako klony - Edit: Paste as clones (with dialog) - Úpravy: Vložit jako klony (s dialogem) + Úpravy: Vložit jako klony (s dialogem) + Edit: Duplicate track + Úpravy: Zdvojit stopu + + + Select track above Vybrat pÅ™edchozí stopu - + Select track below Vybrat další stopu - + + Edit selected track name + Upravit název vybrané stopy + + + Midi: Transpose Midi: PÅ™evést - + Edit: Select all Úpravy: Vybrat vÅ¡e - + Edit: Select none Úpravy: Nevybrat nic - + Edit: Invert Selection Úpravy: Obrátit výbÄ›r - + Edit: Select events/parts inside locators Úpravy: Vybrat vÅ¡echny události/Äásti v oblasti - + Edit: Select events/parts outside locators Úpravy: Vybrat vÅ¡echny události/Äásti vnÄ› oblasti - + Edit: Select previous part Úpravy: Vybrat pÅ™edchozí Äást - + Edit: Select next part Úpravy: Vybrat další Äást - + Edit: Select nearest part/event to the left or move cursor Úpravy: Vybrat nejbližší Äást/nejbližší událost nalevo anebo pÅ™esunout ukazovátko - + Edit: Add nearest part/event to the left to selection Úpravy: PÅ™idat nejbližší Äást/nejbližší událost nalevo do výbÄ›ru - + Edit: Select nearest part/event to the right or move cursor Úpravy: Vybrat nejbližší Äást/nejbližší událost napravo anebo pÅ™esunout ukazovátko - + Edit: Add nearest part/event to the right to selection Úpravy: PÅ™idat nejbližší Äást/nejbližší událost napravo do výbÄ›ru - + Edit: Set locators to selection Úpravy: Nastavit oblast z výbÄ›ru - + Edit: Increase pitch Úpravy: Zvýšit výšku tónu - + Edit: Decrease pitch Úpravy: Snížit výšku tónu - + Edit: Increase event position Úpravy: Zvýšit polohu události - + Edit: Decrease event position Úpravy: Snížit polohu události - + View: Zoom in Pohled: PÅ™iblížit - + View: Zoom out Pohled: Oddálit - + View: Goto Current Position Pohled: Jít na nynÄ›jší polohu - - + + View: Scroll left Pohled: ProjíždÄ›t doleva + Transport: Step record + PÅ™ehrávání: Krokové nahrávání + + + + Transport: Midi input + PÅ™ehrávání: Vstup MIDI + + + + Transport: Play events + PÅ™ehrávání: PÅ™ehrát události + + + + Edit: Increase velocity + Úpravy: Zvýšit sílu tónu + + + + Edit: Decrease velocity + Úpravy: Snížit sílu tónu + + + Edit: Set Fixed Length on Midi Events Úpravy: Nastavit pevnou délku pro události MIDI - + Quantize Kvantizovat - + Modify Note Length ZmÄ›nit délku noty - + Modify Velocity - ZmÄ›nit sílu nárazu + ZmÄ›nit sílu tónu - + Edit: Crescendo Úpravy: Crescendo - + Edit: Thin Out Úpravy: ProstÅ™ihávat - + Edit: Erase Event Úpravy: Smazat událost - + Edit: Delete Overlaps Úpravy: Smazat pÅ™ekrývající se noty - + Edit: Note Shift Úpravy: Posunout noty - + Edit: Move Clock Úpravy: Posunout hodiny - + Edit: Copy Measure Úpravy: Kopírovat takt - + Edit: Erase Measure Úpravy: Vymazat takt - + Edit: Delete Measure Úpravy: Smazat takt - + Edit: Create Measure Úpravy: VytvoÅ™it takt - + Edit: Change Event Color Úpravy: ZmÄ›nit barvu události - + + Move: Move to selected note + Posun: Posun do vybrané noty + + + Tool: Pointer Nástroj: Ukazatel - + Tool: Pencil Nástroj: Tužka - + Tool: Eraser Nástroj: Guma - + + Tool: Pan + Nástroj: Vyvážení + + + + Tool: Zoom + Nástroj: ZvÄ›tÅ¡ení + + + Tool: Line Draw Nástroj: Čára - + Tool: Cursor Nástroj: Ukazovátko - + Add note velocity 1 - PÅ™idat notu se silou nárazu (dynamika) 1 + PÅ™idat notu se silou tónu (dynamika) 1 - + Add note velocity 2 - PÅ™idat notu se silou nárazu (dynamika) 2 + PÅ™idat notu se silou tónu (dynamika) 2 - + Add note velocity 3 - PÅ™idat notu se silou nárazu (dynamika) 3 + PÅ™idat notu se silou tónu (dynamika) 3 - + Add note velocity 4 - PÅ™idat notu se silou nárazu (dynamika) 4 + PÅ™idat notu se silou tónu (dynamika) 4 - + Cursor step size: larger Velikost kroku ukazovátka: vÄ›tší - + Cursor step size: smaller Velikost kroku ukazovátka: menší - + Instrument/Cursor up Nástroj/Ukazovátko nahoru - + Instrument/Cursor down Nástroj/Ukazovátko dolů - + + Tool: Range + Nástroj: Rozsah + + + Tool: Scissor Nástroj: Nůžky - + Tool: Glue Nástroj: Lepidlo - + Tool: Mute Nástroj: Ztlumit - + Transport: Increase current position - PÅ™esun: Zvýšit nynÄ›jší polohu + PÅ™ehrávání: Zvýšit nynÄ›jší polohu - + Transport: Decrease current position - PÅ™esun: Snížit nynÄ›jší polohu + PÅ™ehrávání: Snížit nynÄ›jší polohu - + Transport: Increase current position, no snap - PÅ™esun: Zvýšit nynÄ›jší polohu, žádné zapadnutí + PÅ™ehrávání: Zvýšit nynÄ›jší polohu, žádné zapadnutí - + Transport: Decrease current position, no snap - PÅ™esun: Snížit nynÄ›jší polohu, žádné zapadnutí + PÅ™ehrávání: Snížit nynÄ›jší polohu, žádné zapadnutí - + Quantize: Set quantize to 1/1 note Kvantizovat: Nastavit kvantizaci na celou notu - + Quantize: Set quantize to 1/2 note Kvantizovat: Nastavit kvantizaci na půlovou notu - + Quantize: Set quantize to 1/4 note Kvantizovat: Nastavit kvantizaci na ÄtvrÅ¥ovou notu - + Quantize: Set quantize to 1/8 note Kvantizovat: Nastavit kvantizaci na osminovou notu - + Quantize: Set quantize to 1/16 note Kvantizovat: Nastavit kvantizaci na Å¡estnáctinovou notu - + Quantize: Set quantize to 1/32 note Kvantizovat: Nastavit kvantizaci na dvaatÅ™icetinovou notu - + Quantize: Set quantize to 1/64 note Kvantizovat: Nastavit kvantizaci na ÄtyÅ™iaÅ¡edesátinovou notu - + Quantize: Toggle triol quantization Kvantizovat: PÅ™epnout triolovou kvantizaci - + Quantize: Toggle punctuation quantization Kvantizovat: PÅ™epnout teÄkovanou kvantizaci - + Quantize: Toggle punctuation quantization (2) Kvantizovat: PÅ™epnout teÄkovanou kvantizaci (2) - + Edit: Insert at location Úpravy: Vložit v poloze - + Edit: Increase length Úpravy: ZvÄ›tÅ¡it délku - + Edit: Decrease length Úpravy: ZmenÅ¡it délku - + Insert Note Vložit notu - + Insert SysEx Vložit SysEx - + Insert Ctrl Vložit Ctrl - + Insert Meta Vložit Meta - + Insert Channel Aftertouch Vložit dodÄ›lávku kanálu - + Insert Key Aftertouch Vložit dodÄ›lávku tóniny - + Insert Tempo Vložit tempo - + Insert Signature Vložit taktové oznaÄení - + Change Event Position ZmÄ›nit polohu události - + Edit Event Value Upravit hodnotu události - + Insert Key Vložit tóninu - + Goto Next Marker Jít na další znaÄku - + Goto Prev Marker Jít na pÅ™edchozí znaÄku + + + Normalize + Normalizovat + + + + warnBadTimingBase + + + Bad timing + Å patné naÄasování + + + + Message here + Zpráva zde + + + + Don't warn me again + Nevarovat znovu + diff -Nru muse-2.1.2/share/locale/muse_de.ts muse-3.0.2+ds1/share/locale/muse_de.ts --- muse-2.1.2/share/locale/muse_de.ts 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/muse_de.ts 2017-12-04 21:01:19.000000000 +0000 @@ -1,71 +1,56 @@ - + @default - Add Midi Track Midispur hinzufügen - - Add Drum Track Schlagzeugspur hinzufügen - - Add Old Style Drum Track - - - - - Add New Style Drum Track - - - - Add Wave Track Wavespur hinzufügen - Add Audio Output Audioausgang hinzufügen - Add Audio Group Audiogruppe hinzufügen - Add Audio Input Audioeingang hinzufügen - Add Aux Send Aux Send hinzufügen - Select project directory Projektverzeichnis auswählen - Add Synth Synthesizer hinzufügen - + Route Signalfluss - + + channel="%1" + + + + dest Ziel @@ -75,37 +60,31 @@ Name="%1" - Warning: No output devices! Warnung: Keine Ausgangsgeräte! - Open midi config... Öffne MIDI-Konfig... - Empty ports Leere Ports - + <none> <kein> - channelMask="%1" KanalMaske="%1" - Bad timing - Schlechter Taktgeber + Schlechter Taktgeber - Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. @@ -154,25 +133,63 @@ Veröffentlicht unter der GNU Public License - Version 2 - Version 2 + Version 2 - (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License - (C) Copyright 1999-2012 Werner Schweer und andere. + (C) Copyright 1999-2012 Werner Schweer und andere. Siehe http://www.muse-sequencer.org für neue Versionen und mehr Informationen. Veröffentlicht unter der GNU Public License - + (C) Copyright 1999-2014 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer und andere. +Siehe http://www.muse-sequencer.org für neue Versionen und +mehr Informationen. + +Veröffentlicht unter der GNU Public License {1999-2014 ?} + + + + Version info (replaced programmatically) + + + + + (C) Copyright 1999-2015 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer und andere. +Siehe http://www.muse-sequencer.org für neue Versionen und +mehr Informationen. + +Veröffentlicht unter der GNU Public License {1999-2014 ?} {1999-2015 ?} + + + + System information: + + + + + TextLabel + + + + &Keep On Rocking! Weiterroc&ken! @@ -185,7 +202,7 @@ AppearanceDialogBase - + Apply Anwenden @@ -200,7 +217,7 @@ Abbrechen - + Arranger Arrangierer @@ -280,7 +297,7 @@ Hintergrundbild - + show snap grid Magnetisches Gitter anzeigen @@ -295,7 +312,7 @@ Items - + Color name: Farbname: @@ -310,12 +327,11 @@ Stil und Schriftart - QT Theme - Qt Thema + Qt Thema - + Windows Fenster @@ -360,87 +376,107 @@ Könnte einen Neustart von MusE erfordern für optimale Ergebnisse - + + MusE color scheme + + + + + current settings + + + + + Change + + + + Fonts Schriftarten - + Family Familie - + Size Größe - + Font 1 Schriftart 1 - + + Themes + + + + Font 2 Schriftart 2 - + Font 3 Schriftart 3 - + Font 0 Schriftart 0 - - - - - - - + + + + + + + Bold Fett - - + - - - + + + + Italic Kursiv - + - - - - - - + + + + + + ... ... - + Font 4 Schriftart 4 - + Font 5 Schriftart 5 - + Palette Palette @@ -455,7 +491,7 @@ entfernen - + add to palette Zur Palette hinzufügen @@ -490,17 +526,17 @@ R - + clear löschen - + Style Sheet: Formatvorlage: - + Font 6 Schriftart 6 @@ -534,47 +570,35 @@ Typ des Midi-Controllers - Control7 - Controller7 + Controller7 - Control14 - Controller14 + Controller14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - RPN14 - RPN14 + RPN14 - NRPN14 - NRPN14 - - - - Pitch - + NRPN14 - Program - Programm + Programm - + H-Ctrl H-Ctrl @@ -654,6 +678,40 @@ + ChooseSysexBase + + + Dialog + Dialog + + + + Hex: + + + + + + TextLabel + + + + + Comment: + Kommentar: + + + + &OK + &Bestätigen + + + + &Cancel + + + + ClipListEditorBase @@ -722,7 +780,7 @@ ConfigMidiFileBase - + &OK &Bestätigen @@ -732,7 +790,7 @@ &Abbrechen - + 0 (single track) 0 (einzelne Spur) @@ -742,12 +800,12 @@ 1 (mehrere Spuren) - + Format: Format: - + 96 96 @@ -762,17 +820,17 @@ 384 - + Division: Unterteilung: - + Copyright: Copyright: - + MusE: Config Midi File Import/Export MusE: Konfiguriere MIDI-Datei Import/Export @@ -782,7 +840,7 @@ Import: - + Split tracks into &parts Spuren in &Parts teilen @@ -797,22 +855,37 @@ Spuren in Parts teilen, oder einzelnen Part erstellen - + + Default instrument: + + + + + Device Name metas trump Port metas if both exist + + + + + Instrument Name metas trump Mode sysexes if both exist + + + + Use new-style drum tracks - + Use old-style drum tracks - + Export: Export: - + Enable extended smf format (currently not implemented) Aktiviere erweitertes SMF-Format (momentan nicht implementiert) @@ -827,12 +900,43 @@ Alt+2 - + + Mode sysexes + + + + + Instrument name metas + + + + + + Both + + + + + Port metas + + + + + Device name metas + + + + + Export a Port/Device meta for format 0 + + + + Note: Format 0 uses the FIRST midi track's name/comment in the arranger #Hinweis: Format 0 nutzt den Namen/Kommentar der ERSTEN Midispur im Arranger - + Save space by replacing note-offs with &zero velocity note-ons Platz sparen, indem Note-offs durch Note-ons mit Anschlagsdynamik &0 ersetzt werden @@ -843,6 +947,33 @@ + CopyOnWriteDialogBase + + + Copy Wave Files + + + + + Some sound files will be copied to the Project Directory, +either because they are not writable or because more +than one independent Wave Event shares them. +(If you prefer instead that the Wave Events are + inter-dependent, try using Clone Parts.) + +Multiple copies will be made in some cases. + +If no Project has been created yet, you will be asked to, +giving another chance to cancel. + + + + + These files will be copied to the Project Directory: + + + + CrescendoBase @@ -916,7 +1047,7 @@ - + @@ -928,8 +1059,8 @@ Kritischer Fehler - - + + @@ -937,8 +1068,8 @@ Kann Datei "%1" nicht öffnen - - + + @@ -946,8 +1077,8 @@ Fehler beim Parsen von Datei "%1" - - + + Load category dialog Kategorie laden @@ -1580,7 +1711,7 @@ Release Rate Release Rate - + @@ -1588,7 +1719,7 @@ 2° Decay Rate 2° Decay Rate - + @@ -1596,7 +1727,7 @@ 1° Decay Level 1° Decay Level - + @@ -2667,7 +2798,7 @@ Wussten Sie? - + Don't show on startup Nicht beim Starten zeigen @@ -2721,11 +2852,15 @@ + Copy standard (vol, pan) and synth controllers + + + Copy standard controllers (vol, pan) - Standard-Controller (Vol,Pan) kopieren + Standard-Controller (Vol,Pan) kopieren - + Copy effects rack plugins Effekt-Rack-Plugins kopieren @@ -2748,7 +2883,7 @@ EditCtrlBase - + MusE: Edit Controller Event MusE: Kontroller Ereignis bearbeiten @@ -2768,24 +2903,29 @@ Neuen Kontroller erstellen - + textLabel3 Beschriftung3 - + Value Wert - + Controller Kontroller - - H-Bank - H-Bank + + Note + Note + + + + H-Bank + H-Bank @@ -2900,26 +3040,29 @@ Programm: - - - + + + + &Delete &Löschen - - - + + + Alt+D Alt+D - + + + Drum Schlagzeug - + GM GM @@ -2934,88 +3077,86 @@ XG - This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. - Dies ist eine Liste häufig verwendeter Midi Kontroller. + Dies ist eine Liste häufig verwendeter Midi Kontroller. MusE behandelt allerdings Kontroller wie "pitch" und "program changes" wie normale Kontroller. - - + + Properties Eigenschaften - Control7 - Kontroller7 + Kontroller7 - Control14 - Kontroller14 + Kontroller14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - Pitch - Tonhöhe + Tonhöhe - Program - Programm + Programm - - + + H-Ctrl H-Ctrl - - + + L-Ctrl L-Ctrl - + Min Min - + Max Max - + Name Name - + + Drum&maps + + + + Type Typ - + Hex Entry: Hex Eintrag: - + &File &Datei @@ -3080,17 +3221,15 @@ Sichern &Unter... - Exit - Beenden + Beenden - E&xit - &Beenden + &Beenden - + Instrument Name: Name des Instruments: @@ -3121,13 +3260,13 @@ - - + + Name: Name: - + Group or patch name Gruppen- oder Patchname @@ -3170,17 +3309,15 @@ Programmnummer für den Patch - Drum patch - Schlagzeug-Patch + Schlagzeug-Patch - If set, the patch is available only for drum channels. - Wenn aktiviert, ist der Patch nur für Schlagzeugkanäle verfügbar. + Wenn aktiviert, ist der Patch nur für Schlagzeugkanäle verfügbar. - + GM patch GM Patch @@ -3210,7 +3347,20 @@ Wenn aktiviert, ist der Patch nur in einem "XG" oder "NO" Songtypen verfügbar. - + + + Show in tracks: + + + + + + + Midi + Midi + + + Delete group or patch Lösche Gruppe oder Patch @@ -3245,17 +3395,15 @@ Alt+G - Contro&ller - Contro&ller + Contro&ller - Common: - Gemeinsam: + Gemeinsam: - + List of defined controllers Liste von definierten Controllern @@ -3265,54 +3413,52 @@ Liste von definierten Controllern. - + Min Min - + Max Max - + Def Def - + Midi controller name Name des Midi-Controllers - + Type: Typ: - + Midi controller type Typ des Midi-Controllers - RPN14 - RPN14 + RPN14 - NRPN14 - NRPN14 + NRPN14 - + Midi controller number high byte Midi Controllernummer höchstwertiges Byte - + Midi controller number low byte (* means drum controller) Midi Controllernummer niederwertiges Byte (* bedeutet Schlagzeugcontroller) @@ -3344,12 +3490,12 @@ * - + Range: Bereich: - + Minimum value. If negative, auto-translate. Minimalwert. Wenn negativ, automatisch umsetzen. @@ -3395,47 +3541,47 @@ besitzt: Er geht von -8192 bis 8191 - + Maximum value Maximalwert - + Default: Standard: - + L-Bank L-Bank - - - - - + + + + + off aus - + Progr. Progr. - + ??? ??? - + H-Bank H-Bank - + Default value. Off: No default. Standardwert. "Aus": Kein Standardwert. @@ -3480,28 +3626,27 @@ aus - + Add common controller Füge gemeinsamen Controller hinzu - - + &Add &Hinzufügen - + Alt+A Alt+A - + Delete controller Controller löschen - + Create a new controller Neuen controller erstellen @@ -3516,18 +3661,17 @@ Alt+C - Null Param Hi: - Null Param Hi: + Null Param Hi: - + Null parameter number High byte Null Parameternummer höchstwertiges Byte - + If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events @@ -3543,17 +3687,17 @@ RPN/NRPN-Controllernummer gesetzt. - + Lo: Lo: - + Null parameter number Low byte Null Parameternummer niederwertiges Byte - + S&ysEx S&ysEx @@ -3563,7 +3707,7 @@ SysEx-Liste: - + New SysE&x Neuer SysE&x @@ -3573,12 +3717,7 @@ Alt+X - - Drummaps - - - - + Patch Collections: @@ -3632,12 +3771,67 @@ - + + Contro&llers + + + + + Null Parameters: Hi: + + + + + &Add Common... + + + + + W + W + + + + Comment: + Kommentar: + + + + &Initialization + + + + + Instrument initialization sequence: + + + + + &Add... + + + + + &Change... + + + + &Help &Hilfe - + + &Close + + + + + Close + Schließen + + + new item neuer Eintrag @@ -3706,18 +3900,36 @@ + Name: + Name: + + + Comment: Kommentar: - - OK - Bestätigen + + &Select... + + + + + &OK + &Bestätigen + &Cancel + + + + OK + Bestätigen + + Cancel - Abbrechen + Abbrechen @@ -3745,102 +3957,136 @@ FLUIDSynthGuiBase + FLUID Synth - FLUID Synth + FLUID Synth + Load - Laden + Laden + Delete - Löschen + Löschen + + + + Dump Info + + ID - ID + ID + Fontname - Name + Name + Chnl - Kanal + Kanal + Soundfont - Soundfont + Soundfont + Drum Chnl - Drumkanal + Drumkanal + + Level - Level + Level + Width - Breite + Breite + Damping - Dämpfung + Dämpfung + Room Size - Raumgröße + Raumgröße + Reverb - Reverb + Reverb + CHANNEL SETUP - KANAL SETUP + KANAL SETUP + Sine - Sinus + Sinus + Triangle - Dreieck + Dreieck + Type - Typ + Typ + Number - Anzahl + Anzahl + Speed - Geschwindigkeit + Geschwindigkeit + Depth - Stärke + Stärke + Chorus - Chorus + Chorus + Gain - Verstärkung + Verstärkung + LOADED SOUNDFONTS - GELADENE SOUNDFONTS + GELADENE SOUNDFONTS FileDialogButtons - + + Home + + + + Global Global @@ -3855,21 +4101,21 @@ Projekt - + read Midi Port Configuration lese Midi-Port- Konfiguration - + write window states schreibe Fenster - + fdialogbuttons fdialogbuttons @@ -3877,8 +4123,9 @@ FluidSynthGui + Choose soundfont - Soundfont auswählen + Soundfont auswählen @@ -3957,7 +4204,7 @@ MusE: Globale Einstellungen - + Audio Audio @@ -3983,21 +4230,21 @@ Midi - + Ticks Ticks - + - + 1024 1024 - + - + 2048 2048 @@ -4007,75 +4254,75 @@ 4096 - + Displayed Resolution (Ticks/Quarternote) Angezeigte Auflösung (Ticks/Viertelnote) - - + + 48 48 - - + + 96 96 - - + + 192 192 - - + + 384 384 - - + + 768 768 - - + + 1536 1536 - - + + 3072 3072 - - + + 6144 61144 - - + + 12288 12288 - + RTC Resolution (Ticks/Sec) RTC (RealTimeClock) Auflösung (Ticks/Sekunde) - + /sec /sek @@ -4085,12 +4332,22 @@ GUI Aktualisierungsrate - + + Warn if timer frequency is inadequate + + + + + Track height + + + + Use project save dialog Benutze Projekt-Speichern-Dialog - + Some popup menus stay open (else hold Ctrl) Manche Popup-Menüs bleiben offen (ansonsten Strg halten) @@ -4119,60 +4376,77 @@ Benutze linke Maustaste, um Werte zu erniedrigen - + Shift + Right click sets left range marker Shift + Rechtsklick setzt den linken Bereichs-Marker - + Allow adding hidden tracks in track list menu Erlaube es, verborgene Spurtypen hinzuzufügen in der Spurliste - + Unhide tracks when adding hidden tracks Verborgene Spurentypen anzeigen, wenn ein solcher Typ hinzugefügt wird - - + + Smart focus Intelligenter Fokus - + After editing, controls will return focus to their respective canvas Nach dem Editieren werden GUI-Elemente den Fokus zur jeweilgen Canvas zurückgeben - - Drum tracks + + Show newly created midi velocity graphs per-note - - Only offer old-style drumtracks + + px - - Only offer new-style drumtracks + + Enable borderless mouse. +For certain functions like zoom/pan. +Disable to use an alternate standard + method. + - Prefer old-style drumtracks + Enable borderless mouse. +For certain functions like zoom. +Disable to use an alternate standard + method. - - Prefer new-style drumtracks + + Borderless zoom/pan mouse (else use alternate method) - + + Scrollable submenus + + + + + Drum tracks + + + + GUI Style GUI-Stil @@ -4217,27 +4491,25 @@ Abb&rechen - + Application Anwendung - + Start Muse MusE starten - start with last song Mit letztem Lied starten - start with song Mit bestimmtem Lied starten - + Views Ansichten @@ -4312,12 +4584,12 @@ Jetzige Werte übernehmen - + show splash screen Begrüßungsbild anzeigen - + Mixer A Mixer A @@ -4327,7 +4599,7 @@ Mixer B - + show "Did you know?" dialog Zeige "Wussten Sie?"-Dialog @@ -4337,7 +4609,22 @@ Beim Starten geöffnetes Lied - + + start &with last song + + + + + start with &template + + + + + sta&rt with song + + + + min. Slider Val min. Schiebereglerwert @@ -4388,7 +4675,7 @@ 16 - + Project directory Projektverzeichnis @@ -4398,19 +4685,19 @@ Projekte: - - + + ... ... - + Main Window Hauptfenster - + Choose start song or template Wählen Sie ein Lied oder eine Vorlage für den Start @@ -4420,12 +4707,11 @@ Auf Standardwerte zurücksetzen - start with template mit Vorlage starten - + Start template or song: Mit Vorlage oder Lied starten: @@ -4442,7 +4728,17 @@ Lese MIDI-Port-Konfiguration - + + Warn if opening file versions different than current version + + + + + Auto save (every 5 minutes if not playing/recording) + + + + Try to use Jack Freewheel Versuche, Jack-Freewheel zu nutzen @@ -4565,7 +4861,47 @@ Kürzere Perioden führen zu einer besseren Midi-Abspiel-Auflösung. - + + &Record all instruments + + + + + &Don't record hidden instruments + + + + + Don'&t record muted instruments + + + + + Don't record &hidden or muted instruments + + + + + Instrument initialization + + + + + Send instrument initialization sequences + + + + + Warn if instrument initialization sequences pending + + + + + Send instrument controller default values if none in song, at init or rewind + + + + 8192 8192 @@ -4580,59 +4916,59 @@ 32768 - + Midi Resolution (Ticks/Quarternote) Midi Auflösung (Ticks/Viertelnote) - - Record new style drum tracks + + &Only offer old-style drumtracks - - Record all instruments + + Only offer new-style &drumtracks - Don't record hidden instruments + &Prefer old-style drumtracks - Don't record muted instruments + Prefer &new-style drumtracks - - Don't record hidden or muted instruments + + Record new style drum tracks - + GUI Behaviour GUI-Verhalten - + Use old-style stop shortcut: Benutze altes Stopp-Kürzel: - + Move single armed track with selection Bewege "für Aufnahme scharfschalten" mit Auswahl - + On Launch Beim Start - + Behavior Verhalten @@ -4812,22 +5148,67 @@ MusE: Metronom Konfiguration - + Metronome Metronom - + Audio Beep Interner PC Lautsprecher - + + Two samples (old samples) + + + + + Four samples (new samples, with accents) + + + + + Volume + Lautstärke + + + + Audio master + + + + + Meas + + + + + Beat + + + + + Accent1 + + + + + Accent2 + + + + + Sample + + + + MIDI Click MIDI Klick - + Midi Channel Midi Kanal @@ -4857,17 +5238,22 @@ Midi Anschluss - + + Disabled since jack does not support it + + + + Precount Einzählen - + enable einschalten - + Bars Takte @@ -4877,12 +5263,12 @@ Von der Masterspur - + / / - + Signature Taktmaß @@ -4897,7 +5283,7 @@ Vorlauf - + &Apply &Anwenden @@ -4912,22 +5298,25 @@ Abb&rechen - + Choose outputs... Wähle Ausgänge... - + + + + + 50 50 - % Audio volume - % Audiolautstärke + % Audiolautstärke - + Hint: Enable metronome in Transportpanel Hinweis: Aktivieren Sie das Metronom im Transportpanel @@ -4970,47 +5359,44 @@ Controller-Typ: - + + &Learn + + + Control7 - Controller7 + Controller7 - Control14 - Controller14 + Controller14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - RPN14 - RPN14 + RPN14 - NRPN14 - NRPN14 + NRPN14 - Pitch - Pitch-Bend + Pitch-Bend - Program - Programm + Programm - + Hi: @@ -5020,9 +5406,8 @@ - Learn - Lernen + Lernen @@ -5194,7 +5579,7 @@ - + @@ -5202,8 +5587,8 @@ gleich - - + + @@ -5211,64 +5596,70 @@ ungleich - + Note Note - + Poly Pressure Poly Anschlagsdruck - - + + Control Change Kontrollerwechsel - - + + Aftertouch Aftertouch - - + + Pitch Bend Pitch bend - - + + NRPN NRPN - - + + RPN RPN - - + + + Program + Programm + + + + Value 2 Wert 2 - - + + - + Value 1 Wert 1 - + Event Type Ereignistyp @@ -5315,81 +5706,81 @@ - + Channel Kanal - - + + Port Anschluss - + Processing Bearbeitung - + - + Keep behalten - - + + - + Fix fest - + - + Plus plus - + - + Minus minus - + - + Multiply multiplizieren - + - + Divide dividieren - + - + Invert invertieren - + ScaleMap Notenbelegung @@ -5401,21 +5792,26 @@ - + Dyn Dyn - + - + Random zufällig - + + Toggle + + + + Modules Module @@ -5771,49 +6167,49 @@ MusE: Spurinfo - + output channel Ausgangskanal - - + + % % - + output port Ausgangsanschluss - - - - + + + + off aus - + Transp. Transp. - + Channel Info Kanalinfo - + Rec: Rec: - + Prog Prog @@ -5828,12 +6224,12 @@ Pan - + Delay Delay - + Bank Select MSB. Ctrl-double-click on/off. Bank MSB. Ctrl+Doppelklick für an/aus. @@ -5843,42 +6239,42 @@ H-Bank - + Compr. Kompr. - + Bank Select LSB. Ctrl-double-click on/off. Bank LSB. Ctrl+Doppelklick für an/aus. - + L-Bank L-Bank - + Velocity Velocity - + Length Länge - + all midi events are sent to this output channel alle MIDI-Events werden zu diesem Ausgangskanal gesendet - + Out ch Aus.Kanal - + input routing Eingangs-Signalfluss @@ -5917,17 +6313,43 @@ Schleife Eingangsevents durch zum Ausgang. - + + Change note length in percent of actual length + + + + + Offset playback of notes before or after actual note + + + + + Transpose notes up or down + + + + + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + + + + Select instrument patch Instrumentenpatch auswählen + <unknown> <unbekannt> - + + Select instrument + + + + Add all settings to song Alle Einstellungen zum Lied hinzufügen @@ -5937,7 +6359,12 @@ alle - + + Compress the notes velocity range, in percent of actual velocity + + + + Program. Ctrl-double-click on/off. Programm. Ctrl+Doppelklick für an/aus. @@ -6436,9 +6863,28 @@ + MusECore::AudioPreviewDialog + + + Auto play + + + + + + Stop + Stopp + + + + Play + Wiedergabe + + + MusECore::Song - + Jack shutdown! Jack heruntergefahren! @@ -6478,13 +6924,13 @@ Knopf. - - + + Automation: Automatisierung: - + previous event vorheriges Ereignis @@ -6495,24 +6941,24 @@ - + set event setze Ereignis - - + + add event füge Ereignis hinzu - - + + erase event lösche Ereignis - + erase range lösche Bereich @@ -6552,7 +6998,7 @@ &Abbrechen - + MusE: Tempo list MusE: Tempo-Liste @@ -6564,7 +7010,22 @@ Sollen sie in die Master-Tempoliste übernommen werden? - + + Do you want to process ALL or only selected events? + + + + + &Selected + + + + + &All + + + + MusE - external script failed MusE - externes Skript fehlgeschlagen @@ -6575,6 +7036,16 @@ MusE konnte das Skript nicht starten, Fehlermeldung: %1 + + + Und&o + &Rückgängig + + + + Re&do + &Wiederholen + MusEGui @@ -6759,17 +7230,32 @@ Benutzerdefiniert - + Keep Qt system style Beim Qt-Systemstil belassen - + + Do you really want to reset colors to theme default? + + + + + &Ok + &Bestätigen + + + + &Cancel + + + + MusE: load image MusE: Bild laden - + Select style sheet Formatvorlage auswählen @@ -6782,7 +7268,7 @@ MusEGui::Arranger - + Enable Recording Aufnahme einschalten @@ -6911,7 +7397,7 @@ Zeiger - + Off Aus @@ -6921,7 +7407,7 @@ Takt - + Snap Magnet @@ -6937,38 +7423,31 @@ Liedlänge - Takte - Type - Typ + Typ - NO - Kein + Kein - GM - GM + GM - GS - GS + GS - XG - XG + XG - - midi song type - Midi Lied Typ + Midi Lied Typ - + Pitch Tonhöhe @@ -7055,14 +7534,67 @@ + MusEGui::ArrangerColumns + + + Control7 + Controller7 + + + + Control14 + + + + + RPN + RPN + + + + NPRN + + + + + RPN14 + RPN14 + + + + NRPN14 + NRPN14 + + + + Pitch + + + + + Program + Programm + + + + Aftertouch + Aftertouch + + + MusEGui::ArrangerView - + MusE: Arranger MusE: Arrangierer - + + D&elete + + + + C&ut &Ausschneiden @@ -7082,9 +7614,8 @@ &Einfügen - Paste (show dialog) - Einfügen (Dialog zeigen) + Einfügen (Dialog zeigen) @@ -7092,12 +7623,11 @@ K&lon Einfügen - Paste clone (show dialog) - Klon einfügen (Dialog zeigen) + Klon einfügen (Dialog zeigen) - + &Insert Empty Measure Leeren &Takt einfügen @@ -7126,7 +7656,22 @@ Ausgewählte Parts säubern + + Paste to selected &track + + + + + Paste clone to selected trac&k + + + + Paste (show dialo&g) + + + + Purge hidden events from selected parts @@ -7262,7 +7807,7 @@ &Bearbeiten - + &Structure &Struktur @@ -7332,7 +7877,7 @@ Benutzerdefinierte &Spalten konfigurieren - + Remove track(s) @@ -7342,13 +7887,13 @@ - + New Neu - + Changed Settings Einstellungen verändert @@ -7430,22 +7975,26 @@ MusEGui::AudioStrip - + panorama Panorama - + aux send level Aux Send Pegel - Pan - Pan + Pan - + + calibration gain + + + + 1/2 channel 1/2 Kanäle @@ -7460,7 +8009,7 @@ Vor Regler - nach Regler - + dB dB @@ -7529,7 +8078,7 @@ MusEGui::BigTime - + format display Formatanzeige @@ -7571,12 +8120,20 @@ Subframe - + MusE: Bigtime MusE: Große Zeitanzeige + MusEGui::Canvas + + + Tools: + + + + MusEGui::ClipListEdit @@ -7602,7 +8159,7 @@ MusEGui::CtrlCanvas - + Make the current part's track match the selected drumlist entry @@ -7625,7 +8182,7 @@ MusEGui::CtrlPanel - + S S @@ -7660,48 +8217,43 @@ aus - - + + all/per-note velocity mode + + + Velocity - Anschlagsdynamik + Anschlagsdynamik - add new ... - Neuen Kontroller hinzufügen ... + Neuen Kontroller hinzufügen ... - - Instrument-defined - Instrumenten-definiert + Instrumenten-definiert - - Add ... - Hinzufügen ... + Hinzufügen ... - Others - Andere + Andere - Edit instrument ... - Instrument bearbeiten ... + Instrument bearbeiten ... - Common Controls - Gemeinsame Controller + Gemeinsame Controller MusEGui::DList - + hide this instrument @@ -7719,7 +8271,7 @@ MusEGui::DrumCanvas - + Moving items failed @@ -7730,7 +8282,7 @@ - + Creating event failed @@ -7741,14 +8293,14 @@ - - + + Recording event failed - - + + Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. @@ -7767,7 +8319,7 @@ MusEGui::DrumEdit - + mute instrument Instrument stummschalten @@ -7809,55 +8361,43 @@ Das ist die Note, die gespielt wird - output channel (hold ctl to affect all rows) - Ausgangskanal (halten Sie CTRL, um alle Reihen zu beeinflussen) + Ausgangskanal (halten Sie CTRL, um alle Reihen zu beeinflussen) - output port (hold ctl to affect all rows) - Ausgangsport (halten Sie CTRL, um alle Reihen zu beeinflussen) + Ausgangsport (halten Sie CTRL, um alle Reihen zu beeinflussen) - - shift + control key: draw velocity level 1 - Shift + Ctrl: Zeichne Dynamiklevel 1 + Shift + Ctrl: Zeichne Dynamiklevel 1 - - control key: draw velocity level 2 - Ctrl: Zeichne Dynamiklevel 2 + Ctrl: Zeichne Dynamiklevel 2 - - shift key: draw velocity level 3 - Shift: Zeichne Dynamiklevel 3 + Shift: Zeichne Dynamiklevel 3 - - draw velocity level 4 - Zeichne Dynamiklevel 4 + Zeichne Dynamiklevel 4 - output channel (ctl: affect all rows) - Ausgangskanal (CTRL, um alle Reihen zu beeinflussen) + Ausgangskanal (CTRL, um alle Reihen zu beeinflussen) - output port (ctl: affect all rows) - Ausgangskanal (CTRL, um alle Reihen zu beeinflussen) + Ausgangskanal (CTRL, um alle Reihen zu beeinflussen) &File &Datei - + Load Map Schlagzeugbelegung laden @@ -8011,44 +8551,88 @@ Schlagzeugbelegung laden - + hide instrument - - Re-order map + + override track output channel (hold ctl to affect all rows) - - Group + + override track output port (hold ctl to affect all rows) - Don't group + + control + meta keys: draw velocity level 1 - - Group by channel + + + meta key: draw velocity level 2 - - Group maximally + + + draw default velocity level 3 - - Show/Hide + + + meta + alt keys: draw velocity level 4 - - Also show hidden instruments + + override track output channel (ctl: affect all rows) + + + + + override track output port (ctl: affect all rows) + + + + + Re-order map + + + + + Group + + + + + Don't group + + + + + Group by channel + + + + + Group maximally + + + + + Show/Hide + + + + + Also show hidden instruments @@ -8092,7 +8676,12 @@ Midi Eingang - + + Play Events + Ereignisse abspielen + + + cursor tools Cursor-Werkzeug @@ -8187,7 +8776,7 @@ LV4 - + Muse: Load Drum Map MusE: Schlagzeugbelegung laden @@ -8207,7 +8796,7 @@ Die Schlagzeugbelegung auf die GM-Standardwerte zurücksetzen? - + Not all parts are displayed @@ -8221,25 +8810,22 @@ MusEGui::EditCAfterDialog - MusE: Enter Channel Aftertouch - MusE: Channel Aftertouch festlegen + MusE: Channel Aftertouch festlegen - Time Position - Position + Position - Pressure - Anschlagsdruck + Anschlagsdruck MusEGui::EditEventDialog - + Ok Bestätigen @@ -8252,12 +8838,63 @@ MusEGui::EditInstrument - + + Control7 + Controller7 + + + + Control14 + + + + + RPN + RPN + + + + NPRN + + + + + RPN14 + RPN14 + + + + NRPN14 + NRPN14 + + + + Pitch + + + + + Program + Programm + + + + PolyAftertouch + + + + + Aftertouch + Aftertouch + + + + Name Name - + Vol Vol @@ -8273,11 +8910,12 @@ + Len Länge - + A-Note A-Note @@ -8302,8 +8940,18 @@ LV4 - - + + Tick + Tick + + + + Data + Daten + + + + MusE: Create file failed MusE: Erzeugen der Datei schlug fehl @@ -8313,19 +8961,19 @@ MusE: Datei schreiben schlug fehl - - + + MusE: Save Instrument Definition MusE: Instrumentendefinition speichern - - + + Instrument Definition (*.idf) Instrumentendefinition (*.idf) - + MusE: Save instrument as MusE: Instrument speichern unter @@ -8343,7 +8991,7 @@ Sind Sie sicher? - + MusE: Bad instrument name MusE: Ungeeigneter Instrumentenname @@ -8375,7 +9023,7 @@ Bitte eindeutigen Patchgruppennamen wählen - + MusE: Bad controller name MusE: Ungeeigneter Controllername @@ -8385,23 +9033,30 @@ Bitte eindeutigen Controllernamen wählen - - + + New controller: Error + + + + + Error! All control numbers are taken up! +Clean up the instrument! + + + MusE: Cannot add common controller - MusE: Kann gemeinsamen Controller nicht hinzufügen + MusE: Kann gemeinsamen Controller nicht hinzufügen - A controller named '%1' already exists. - Ein Controller namens "%1" existiert bereits. + Ein Controller namens "%1" existiert bereits. - A controller number %1 already exists. - Die Controllernummer %1 existiert bereits. + Die Controllernummer %1 existiert bereits. - + MusE MusE @@ -8435,7 +9090,7 @@ MusEGui::EditMetaDialog - + MusE: Enter Meta Event MusE: Meta-Event eingeben @@ -8458,30 +9113,26 @@ MusEGui::EditPAfterDialog - MusE: Enter Poly Aftertouch - MusE: Poly Aftertouch eingeben + MusE: Poly Aftertouch eingeben - Time Position - Position + Position - Pitch - Tonhöhe + Tonhöhe - Pressure - Anschlagsdruck + Anschlagsdruck MusEGui::EditToolBar - + select Pointer Tool: with the pointer tool you can: select parts @@ -8556,12 +9207,11 @@ Automatisierung manipulieren - Cursor tool - Zeiger-Werkzeug + Zeiger-Werkzeug - + pointer Zeiger @@ -8581,17 +9231,56 @@ Schnitt - + score Notensatz - + glue Verbinder + + select Cursor (tracker mode) tool: +with the cursor tool you can: + navigate with arrow keys + use VBNM to place notes + change step with 0 and 9 + + + + + select Range Tool + + + + + select Panning Tool + + + + + select Zoom Tool + + + + + range + + + + + pan + + + + zoom + + + + quantize Quantisierung @@ -8616,7 +9305,7 @@ Zeiger - + Edit Tools Werkzeuge bearbeiten @@ -8624,7 +9313,7 @@ MusEGui::EffectRack - + effect rack Effekteinschub @@ -8674,12 +9363,17 @@ Preset speichern - + + Presets + Voreinstellungen + + + MusE: Save Preset MusE: Preset speichern - + Replace effect Effekt ersetzen @@ -8692,7 +9386,7 @@ MusEGui::GlobalSettingsConfig - + MusE: Choose start template or song MusE: Lied oder eine Vorlage für den Start wählen @@ -8831,7 +9525,7 @@ MusEGui::ListEdit - + insert Note Note einfügen @@ -8851,17 +9545,15 @@ Meta einfügen - insert Channel Aftertouch - Channel Aftertouch einfügen + Channel Aftertouch einfügen - insert Poly Aftertouch - Poly Aftertouch einfügen + Poly Aftertouch einfügen - + &Edit B&earbeiten @@ -8959,33 +9651,29 @@ MusEGui::MPConfig - - + Default input connections Standard-Eingangsverbindungen - - + + Are you sure you want to apply to all existing midi tracks now? Sind Sie sicher, dass Sie das auf alle existierenden MIDI-Spuren anwenden wollen? - Default output connections Standard-Ausgangsverbindungen - - Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? - Die Einstellung wird auf neue MIDI-Spuren angewendet werden. + Die Einstellung wird auf neue MIDI-Spuren angewendet werden. Möchten Sie sie jetzt auf alle existierende MIDI-Spuren anwenden? - + MusE: bad device name MusE: ungeeigneter Gerätename @@ -8995,67 +9683,73 @@ Bitte wählen Sie einen einzigartigen Gerätenamen - - + + in ein - - + + out aus - Show first aliases Zeige erste Aliase - Show second aliases Zeige zweite Aliase - - + + Toggle all Alle umschalten - - + + Change all tracks now Alle Spuren jetzt ändern - + + Remove + Entfernen + + + Create Jack device Jack-Gerät erstellen - - + + Port Number Anschlussnummer - + + Enable gui GUI aktivieren - + + Enable reading Lesen aktivieren - + + Enable writing Schreiben aktivieren - + Port instrument Anschluss-Instrument @@ -9065,7 +9759,7 @@ Midigerätename. Klicken zum bearbeiten (Jack) - + Connections from Jack Midi outputs Verbindungen von Jack Midi Ausgängen @@ -9075,42 +9769,45 @@ Verbindungen zu Jack Midi Eingängen - + Auto-connect these channels to new midi tracks Verbinde diese Kanäle automatisch mit neuen Midispuren - + Auto-connect new midi tracks to these channels Verbinde neue Midispuren automatisch mit diesen Kanälen - + Auto-connect new midi tracks to this channel Verbinde neue Midispuren automatisch mit diesem Kanal - + + Device state Gerätestatus - + Enable gui for device GUI für Gerät aktivieren + Enable reading from device Lesen vom Gerät aktivieren - + + Enable writing to device Schreiben zum Gerät aktivieren - + Name of the midi device associated with this port number. Click to edit Jack midi name. Name des MIDI-Gerätes, welches mit dieser Anschlussnummer verknüpft ist. Klicken Sie, um den Jack Midi Namen zu ändern. @@ -9120,7 +9817,7 @@ Instrument mit Anschluss verbunden - + Connections from Jack Midi output ports Verbindungen von Jack Midi Ausgangsports @@ -9130,67 +9827,123 @@ Verbindungen zu Jack Midi Eingangsports - + Auto-connect these channels, on this port, to new midi tracks. Verbinde diese Kanäle auf diesem Port automatisch mit neuen MIDI-Spuren. - + Connect new midi tracks to these channels, on this port. Verbinde neue MIDI-Spuren mit diesen Kanälen auf diesem Port. - + Connect new midi tracks to this channel, on this port. Verbinde neue MIDI-Spuren mit diesem Kanal auf diesem Port. - + State: result of opening the device Status: Ergebnis vom Öffnen des Gerätes - + Port Anschluss - + + GUI GUI - + + I E - + + O A - + Instrument Instrument - + + Device Name Gerätename - - In routes - Eingangssignalfluss + + + Midi device name + - - Out routes - Ausgangssignalfluss - + + + Midi device type + + + + + Connections from Jack Midi + + + + + Connections to Jack Midi + + + + + Result of opening the device: +OK: Assigned to a port and in use +Closed: Unassigned to a port, or closed +R/W Error: Unable to open for read or write +Unavailable: USB midi unplugged, or external + application not running, or synth plugin + not installed etc. +(Jack Midi devices have 'unavailable ports' + in the routes columns.) +Unavailable devices or ports can be purged + with 'Remove' or with the advanced router. + + + + + Enable Graphical User Interface for device + + + + + Connections from Jack Midi ports + + + + + Connections to Jack Midi ports + + + + + In routes + Eingangssignalfluss + + Out routes + Ausgangssignalfluss + + + Def in ch Standard Ein-Kanal @@ -9200,18 +9953,33 @@ Standard Aus-Kanal - + + State Status - + + Type + Typ + + + + In + + + + + Out + + + + <unknown> <unbekannt> - - + <none> <kein> @@ -9219,7 +9987,7 @@ MusEGui::MTScale - + bar scale Taktskala @@ -9227,7 +9995,7 @@ MusEGui::MTScaleFlo - + bar scale Taktskala @@ -9369,9 +10137,57 @@ + MusEGui::MidiAudioControl + + + Control7 + Controller7 + + + + Control14 + + + + + RPN + RPN + + + + NPRN + + + + + RPN14 + RPN14 + + + + NRPN14 + NRPN14 + + + + Pitch + + + + + Program + Programm + + + + Aftertouch + Aftertouch + + + MusEGui::MidiInputTransformDialog - + New Neu @@ -9845,8 +10661,10 @@ MusEGui::MidiTrackInfo - - + + + + <unknown> <unbekannt> @@ -9854,7 +10672,7 @@ MusEGui::MidiTransformerDialog - + New Neu @@ -9871,7 +10689,7 @@ MusEGui::MusE - + Failed to start audio! Audio konnte nicht gestartet werden! @@ -9979,7 +10797,7 @@ Wiedergabe beenden - + Play Wiedergabe @@ -9989,7 +10807,7 @@ Wiedergabe beginnen - + Record Aufnahme @@ -9999,18 +10817,23 @@ Zur Aufnahme erst die Schaltfläche "Aufnahme" und dann "Wiedergabe" klicken - - + + Panic Panik - + send note off to all midi channels Panik - "Note aus" Befehl an alle Midikanäle senden - + + turn on/off metronome + + + + &New &Neu @@ -10038,13 +10861,13 @@ - - + + &Save &Speichern - + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. @@ -10072,13 +10895,13 @@ Part importieren - - - Import Wave File - Wavedatei importieren + + + Import Audio File + Audiodatei importieren - + Find unused wave files Finde ungenutzte Wave-Dateien @@ -10268,12 +11091,19 @@ Folge dem Lied ständig - + + MusE: Song: + + + + + + Metronome Metronom - + Midi Sync Midi Sync @@ -10343,7 +11173,22 @@ Transport - + + Cpu load + + + + + Measured CPU load + + + + + No CPU load data + + + + &File &Datei @@ -10388,12 +11233,12 @@ Ãœber &Qt - + Cannot read template Vorlage nicht lesbar - + File open error Fehler beim Datei öffnen @@ -10408,29 +11253,27 @@ Unbekanntes Dateiformat: %1 - - - + MusE: Song: %1 MusE: Lied: %1 - + MusE: load project Muse: Projekt laden - + MusE: load template Muse: Vorlage laden - + MusE: Write File failed MusE: Datei schreiben schlug fehl - + The current Project contains unsaved data Save Current Project? Das aktuelle Projekt enthält ungesicherte Daten @@ -10438,28 +11281,28 @@ - + S&kip &Ãœberspringen - + &Cancel &Abbrechen - + MusE: Save As MusE: Speichern unter - + Nothing to edit Es gibt nichts zu editieren - + @@ -10519,7 +11362,7 @@ Linken/rechten Marker für Abmischbereich einstellen - + The current Project contains unsaved data Load overwrites current Project: Save Current Project? @@ -10528,13 +11371,13 @@ Aktuelles Projekt sichern? - - + + &Abort &Abbrechen - + This will clear all automation data on all audio tracks! Proceed? @@ -10554,12 +11397,26 @@ Fortsetzen? - + + MusE: Warning + + + + + The song uses multiple ports but export format 0 (single track) is set. +The first track's port will be used. Playback will likely be wrong + unless the channels used in one port are different from all other ports. +Canceling and setting a different export format would be better. +Continue? + + + + MusE: Export Midi MusE: Midi exportieren - + no help found at: Hilfe wurde nicht gefunden: @@ -10569,7 +11426,7 @@ MusE: Hilfe öffnen - + Unable to launch help Konnte Hilfe nicht starten @@ -10615,7 +11472,7 @@ schlug fehl: - + Import part is only valid for midi and wave tracks! Part importieren ist nur möglich für MIDI und WAVE-Spuren! @@ -10630,7 +11487,7 @@ Keine Spur ist für den Import ausgewählt - + %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. @@ -10652,12 +11509,12 @@ - + to import an audio file you have first to selecta wave track um eine Audio-Datei zu importieren müssen Sie erst ein Wave-Spur auswählen - + Import Wavefile Wavedatei importieren @@ -10665,13 +11522,65 @@ This wave file has a samplerate of %1, as opposed to current setting %2. +File will be resampled from %1 to %2 Hz. +Do you still want to import it? + + + + + + + Wave import error + + + + + There are too many wave files +of the same base name as imported wave file +Can not continue. + + + + + Can't create new wav file in project folder! + + + + + + Failed to initialize sample rate converter! + + + + + Cancel + Abbrechen + + + + Resampling wave file +"%1" +from %2 to %3 Hz... + + + + + Output has clipped +Resampling again and normalizing wave file +"%1" +Try %2 of %3... + + + + This wave file has a samplerate of %1, +as opposed to current setting %2. Do you still want to import it? Diese Wavedatei hat eine samplerate von %1, im Gegensatz zur momentanen Einstellung %2. Möchten Sie sie dennoch importieren? - + &Yes &Ja @@ -10680,6 +11589,23 @@ &No &Nein + + + File version is %1.%2 +Current version is %3.%4 +Conversions may be applied if file is saved! + + + + + Opening file + + + + + Do not warn again + + MusEGui::NoteInfo @@ -10722,12 +11648,17 @@ MusEGui::PartCanvas - + Cannot copy/move/clone to different Track-Type Kopieren/verschieben/klonen auf anderen Spurtyp nicht möglich - + + Part: + + + + C&ut &Ausschneiden @@ -10799,24 +11730,34 @@ Dateiinfo - + + Normalize + + + + MusE: save part MusE: Speichere Part - + Part name: %1 Files: Part Name: %1 Dateien: - + + Automation: + Automatisierung: + + + Remove selected Löschen - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -10917,7 +11858,7 @@ MusEGui::PianoRoll - + &Edit &Bearbeiten @@ -11097,7 +12038,7 @@ Ereignisse abspielen - + ctrl Ctrl @@ -11110,11 +12051,18 @@ MusEGui::PluginDialog - + MusE: select plugin MusE: PlugIn wählen + + Plugin categories. +Right-click on tabs to manage. +Right-click on plugins to add/remove from a category. + + + Type Typ @@ -11205,39 +12153,47 @@ ID-Nummer - + + &create new group + + + + + &delete currently selected group + + + + + re&name currently selected group + + + Ok - Bestätigen + Bestätigen - Cancel - Abbrechen + Abbrechen - Show plugs: - Zeige Plugins: + Zeige Plugins: - Mono and Stereo - Mono und Stereo + Mono und Stereo - Stereo - Stereo + Stereo - Mono - Mono + Mono - Show All - Zeige alle + Zeige alle @@ -11245,7 +12201,34 @@ Wählen Sie aus, welche Plugintypen in der Liste sichtbar sein sollen.<br>Es ist kein Problem, Mono-Plugins auf Stereospuren zu verwenden; dann werden zwei parallel verwendet.<br>Beachten Sie, dass die "Alle"-Alternative Plugings beinhaltet, die nicht in einem Effekteinschub nützlich sein könnten. - + + Associated categories + + + + + You need to define some categories first. + + + + + + new group + + + + + + Enter the new group name + + + + + Wine VST + + + + dssi synth DSSI-Synth @@ -11255,20 +12238,29 @@ DSSI-Effekt - + + LV2 synth + + + + + LV2 effect + + + + ladspa LADSPA - Search in 'Label' and 'Name': - Suche in "Beschriftung" und "Name": + Suche in "Beschriftung" und "Name": MusEGui::PluginGui - + File Buttons Datei-Knöpfe @@ -11289,7 +12281,7 @@ Signalfluss PlugIn überspringen - + MusE: load preset MusE: Vorlage laden @@ -11305,6 +12297,14 @@ + MusEGui::PopupMenu + + + <More...> %1 + + + + MusEGui::ProjectCreateImpl @@ -11313,67 +12313,216 @@ + MusEGui::RouteDialog + + + Normal + Normal + + + + Alias 1 + + + + + Alias 2 + + + + + + Source + Quelle + + + + + Destination + Ziel + + + + <none> + <kein> + + + MusEGui::RoutePopupMenu - - - - - - - - + Channel Kanal - - - + + + + Soloing chain Solo-Kette - - + + + Audio returns Audio-Rückkanäle - + Warning: No input devices! Warnung: Keine Eingangsgeräte! + + Channel grouping: + + + + + Mono + + + + + Stereo + Stereo + + + + + + + + + + + + Channels + + + + + + Midi ports/devices + + + + + + + Omni + + + + + Show aliases: + + + + + First + + + + + Second + + + + + Show names + + + + + Show first aliases + Zeige erste Aliase + + + Show second aliases + Zeige zweite Aliase + + + + + Jack ports + + + + + + Connect + Verbinden + + + + Unavailable + + + + + Open advanced router... + + + + + Output routes: + + + + + Input routes: + + + + Open midi config... Öffne MIDI-Konfig... - - - - + <none> <kein> - + + Midi sends + + + + + Sources: + + + Toggle all Alles umwählen - + More... Mehr... - + + + Tracks + + + + + Destinations: + + + + Audio sends Audio-Sender - Midi port sends Midiport-Sender @@ -11381,7 +12530,7 @@ MusEGui::ScoreCanvas - + Treble Violinschlüssel @@ -11401,7 +12550,7 @@ Notenzeile entfernen - + Ambiguous part Unklarer Part @@ -11424,7 +12573,7 @@ MusEGui::ScoreEdit - + Step recording tools "Taktschlagweise Aufnahme"-Werkzeuge @@ -11674,7 +12823,7 @@ MusEGui::ScrollScale - + next page Nächste Seite @@ -11692,7 +12841,7 @@ MusEGui::ShortcutCaptureDialog - + Ok Bestätigen @@ -11713,6 +12862,46 @@ + MusEGui::ShortcutConfig + + + Save printable text file + + + + + Text files (*.txt);;All files (*) + + + + + + Error + Fehler + + + + Error opening file for saving + + + + + Shortcuts for selected category: + + + + + Legend: + + + + + + An error occurred while saving + + + + MusEGui::SigScale @@ -11736,7 +12925,7 @@ MusEGui::Strip - + Remove track? Spur entfernen? @@ -11744,12 +12933,12 @@ MusEGui::TList - + <none> <kein> - + visible sichtbar @@ -11775,12 +12964,12 @@ - + off aus - + <unknown> <unbekannt> @@ -11795,42 +12984,61 @@ Bitte eindeutigen Spurnamen wählen - - Update drummap? - Drumbelegung aktualisieren? + Drumbelegung aktualisieren? - Do you want to use same port for all instruments in the drummap? - Möchten Sie für alle Instrumente der Drumbelegung den selben Anschluss verwenden? + Möchten Sie für alle Instrumente der Drumbelegung den selben Anschluss verwenden? - - &Yes - &Ja + &Ja - - &No - &Nein + &Nein - - + + show gui GUI anzeigen - - + + show native gui Native GUI anzeigen - + + + Presets + Voreinstellungen + + + + Clear all controller events? + Alle Controller-Ereignisse löschen? + + + + &Ok + &Bestätigen + + + + &Cancel + + + + + Change color + + + + Midi control MIDI-Steuerung @@ -11845,7 +13053,17 @@ Löschen - + + Other + Andere + + + + clear automation + lösche Automatisierung + + + Treble clef Violinschlüssel @@ -11875,7 +13093,7 @@ Synth - + Delete Track Spur löschen @@ -11925,7 +13143,7 @@ Spur einfügen - + Drum map Schlagzeugbelegung @@ -11941,17 +13159,29 @@ - + Muse: Load Track's Drum Map - + + Drummap + + + + + This drummap was created with a previous version of MusE, +it is being read but the format has changed slightly so some +adjustments may be necessary. + + + + MusE: Store Track's Drum Map - + Midi Midi @@ -11966,12 +13196,11 @@ - Do you want to use same port and channel for all instruments in the drummap? - Möchten Sie für alle Instrumente der Drumbelegung den selben Anschluss und Kanal verwenden? + Möchten Sie für alle Instrumente der Drumbelegung den selben Anschluss und Kanal verwenden? - + Unused Devices Ungenutzte Geräte @@ -12007,17 +13236,17 @@ Aus - + Solo Solo - + Cursor Cursor - + Snap Magnet @@ -12025,7 +13254,7 @@ MusEGui::TopWin - + As subwindow Als Unterfenster @@ -12051,11 +13280,16 @@ + Metronome + Metronom + + + Transport Transport - + Song Position Song-Position @@ -12070,7 +13304,7 @@ Taktmaß - + Piano roll Pianoroll @@ -12376,9 +13610,74 @@ + MusEGui::WaveCanvas + + + Adjust Wave Offset + + + + + Wave offset (frames) + + + + + Part creation failed + + + + + Left and right position markers must be placed inside the current part. + + + + + Part created + + + + + The selected region has been copied to the clipboard and can be pasted in the arranger. + + + + + MusE - external editor failed + MusE - externer Editor fehlgeschlagen + + + + MusE was unable to launch the external editor +check if the editor setting in: +Global Settings->Audio:External Waveditor +is set to a valid editor. + MusE konnte den externen Editor nicht starten. +Ãœberprüfen Sie, ob die Editor-Einstellung in: +Globale Einstellungen -> Audio:Externer Waveeditor +stimmt. + + + + MusE - file size changed + MusE - Dateigröße geändert + + + + When editing in external editor - you should not change the filesize +since it must fit the selected region. + +Missing data is muted + Wenn Sie im externen Editor editieren, sollten Sie die Dateigröße +nicht verändern, weil die es in den ausgewählten Bereich passen muss. + +Fehlende Daten werden stummgeschaltet + + + MusEGui::WaveEdit - + &Edit &Bearbeiten @@ -12404,6 +13703,11 @@ + &Create Part from Region + + + + C&ut &Ausschneiden @@ -12418,7 +13722,7 @@ In e&xternem Editor bearbeiten - + Mute Selection Auswahl stummschalten @@ -12453,17 +13757,42 @@ Alles a&uswählen - - &Deselect All - Alles ab&wählen + + &Deselect All + Alles ab&wählen + + + + &Previous Part + Vorheriger &Part + + + + &Next Part + &Nächster Part + + + + Window &Config + Fenster&konfiguration + + + + &Event Color + &Eventfarbe + + + + &Part colors + - - Window &Config - Fenster&konfiguration + + &Gray + - + WaveEdit tools Werkzeuge "Wave-Edit" @@ -12481,33 +13810,29 @@ MusEGui::WaveView - MusE - external editor failed - MusE - externer Editor fehlgeschlagen + MusE - externer Editor fehlgeschlagen - MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. - MusE konnte den externen Editor nicht starten. + MusE konnte den externen Editor nicht starten. Ãœberprüfen Sie, ob die Editor-Einstellung in: Globale Einstellungen -> Audio:Externer Waveeditor stimmt. - MusE - file size changed - MusE - Dateigröße geändert + MusE - Dateigröße geändert - When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted - Wenn Sie im externen Editor editieren, sollten Sie die Dateigröße + Wenn Sie im externen Editor editieren, sollten Sie die Dateigröße nicht verändern, weil die es in den ausgewählten Bereich passen muss. Fehlende Daten werden stummgeschaltet @@ -12782,6 +14107,59 @@ + PluginDialogBase + + + Dialog + Dialog + + + + Ports: + + + + + Mono + Mono + + + + Mono + Stereo + + + + + Stereo + Stereo + + + + All + alle + + + + Plugin type: + + + + + Filter: + + + + + &OK + &Bestätigen + + + + &Cancel + + + + ProjectCreate @@ -12795,13 +14173,13 @@ - + ... ...... - + Project Name: Projekt-Name: @@ -12827,11 +14205,15 @@ + &Create project folder (recommended for audio projects) + + + Create project folder (recommended for audio projects) - Erzeuge Projektverzeichnis (empfohlen für Audioprojekte) + Erzeuge Projektverzeichnis (empfohlen für Audioprojekte) - + Song information: Lied-Information: @@ -12850,18 +14232,122 @@ Please first select the range for crescendo with the loop markers. Bitte wählen Sie erst den Bereich für das Crescendo mit den Bereichsmarkern aus. + + + Controller ! + + + + + Other ! + + + + + Select gui type + + + + + Preset actions + + + + + Save preset... + + + + + Update list + + + + + Saved presets + + + + + No presets found + + + + + Enter new preset name + + + + + Midi programs + + + + + Presets + Voreinstellungen + + + + Switch on General Midi Level 1 mode + + + + + Switch on General Midi Level 2 mode + + + + + Switch off General Midi Level 1 or 2 + + + + + Switch on Roland GS mode + + + + + Switch on Yamaha XG mode + + + + + Tracks: + + + + + Midi ports: + + + + + Midi devices: + + + + + Jack: + + + + + Jack midi: + + QWidget - - + + Cannot convert sysex string Sysex Kette nicht umwandelbar - - + Hex String too long (2048 bytes limit) Hex Kette ist zu lang (Grenze 2048 Bytes) @@ -12871,7 +14357,7 @@ Neu - + create peakfile for Pegelspitzendatei erzeugen für @@ -12902,7 +14388,7 @@ Verzeichnis erzeugen schlug fehl - + File %1 exists. Overwrite? @@ -12928,21 +14414,57 @@ MusE: Datei öffnen - - + + None Kein - + generic midi General Midi - + + No wave events selected. + + + + No selection. Ignoring Keine Auswahl. Ignoriert + + + Instrument-defined + Instrumenten-definiert + + + + + Edit instrument ... + Instrument bearbeiten ... + + + + Add + + + + + Others + Andere + + + + Common Controls + Gemeinsame Controller + + + + Velocity + + QuantBase @@ -13137,94 +14659,181 @@ Länge - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn nichts ausgewählt ist, wird alles gelöscht.</p> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn "Dynamik" ausgewählt ist, werden nur Noten mit Dynamik &lt; Schwelle gelöscht.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn beide ausgewählt sind, werden Noten mit Dynamik &lt; Schwelle ODER Länge &lt; Schwelle gelöscht.</p></body></html> + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn nichts ausgewählt ist, wird alles gelöscht.</p> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn "Dynamik" ausgewählt ist, werden nur Noten mit Dynamik &lt; Schwelle gelöscht.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn beide ausgewählt sind, werden Noten mit Dynamik &lt; Schwelle ODER Länge &lt; Schwelle gelöscht.</p></body></html> + + + + OK + Bestätigen + + + + Cancel + Abbrechen + + + + RhythmGenerator + + + Rhythmusgenerator + + + + RouteDialogBase + + + MusE: Routing + MusE: Signalfluss + + + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. + + + + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + + Itemized list of current connections. + + + + + + Show only selected sources + + + + + + Show only destination routes for selected source + + + + + + Show only selected destinations + + + + + + Show only source routes for selected destination + + + + + + Show all Midi Ports + + + + + Auto adjust column size + - - OK - Bestätigen + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + - - Cancel - Abbrechen + + + Preferred route name or alias + - - - RhythmGenerator - - Rhythmusgenerator + + + Connect source to destination + - - - RouteDialogBase - - MusE: Routing - MusE: Signalfluss + + + Remove selected route + - Add Route Signalfluss hinzufügen - Source: Quelle: - Destination: Ziel: - + Connect Verbinden - connect source to destination Quelle mit Ziel verbinden - Current Routes Aktueller Signalfluss - Source Quelle - Destination Ziel - + Remove Entfernen - remove selected route Ausgewählten Signalfluss entfernen @@ -13285,7 +14894,7 @@ SS_PluginFront - + Clear and unload effect Lösche und entlade den Effekt @@ -13454,6 +15063,26 @@ Alt+A Alt+A + + + &Printable file... + + + + + Alt+P + Alt+P + + + + &Ok + &Bestätigen + + + + Alt+O + Alt+O + SimpleDrumsGuiBase @@ -13466,7 +15095,22 @@ SimpleSynthGui - + + Mix + Mischen + + + + Chn + + + + + Channel routing + + + + &Load setup Setup &laden @@ -13476,7 +15120,7 @@ Setup &speichern - + Load sample dialog Sample-Laden-Dialog @@ -13517,23 +15161,20 @@ SynthConfigBase - Soft Synthesizer Software-Synthesizer - + File Datei - Instances Instanzen - - + Name Name @@ -13543,38 +15184,42 @@ Liste verfügbarer Software-Synthesizer - Add Instance Instanz hinzufügen - Remove Instance Instanz entfernen - Midi Port Midi Anschluss - Midi Port and Soft Synth Configuration MIDI-Port- und SoftSynth-Konfiguration - - + Type Typ - Midi connections Midi Verbindungen - + + Configure midi devices, midi ports, and synthesizers + + + + + Available soft synthesizers + + + + Inst Inst @@ -13588,6 +15233,56 @@ Description Beschreibung + + + Useable devices + + + + + Add: + + + + + Synth + Synth + + + + ALSA + + + + + JACK + + + + + Rename + + + + + Remove + Entfernen + + + + Device port assignments + + + + + &Apply + &Anwenden + + + + &OK + &Bestätigen + TransposeBase @@ -13682,7 +15377,7 @@ VAMGui - + MusE: Load VAM Presets MusE: VAM-Voreinstellungen laden @@ -13878,7 +15573,7 @@ On An - + VAM 1.0beta3 Virtual Analog for MusE @@ -13971,7 +15666,7 @@ file_patterns - + Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) @@ -14129,13 +15824,21 @@ - Wave/Binary (*.wav *.ogg *.bin) - Wave/Binär (*.wav *.ogg *.bin) + Wave/Binary (*.wav *.ogg *.flac *.bin) + + Wave (*.wav *.ogg *.flac) + + + + Wave/Binary (*.wav *.ogg *.bin) + Wave/Binär (*.wav *.ogg *.bin) + + Wave (*.wav *.ogg) - Wave (*.wav *.ogg) + Wave (*.wav *.ogg) @@ -14437,9 +16140,35 @@ + midiWarnInitPendingBase + + + Instrument initialization + + + + + MusE should now send some Instrument Initialization Sequences. +The sequences (usually System Exclusive messages) are defined + by the selected instruments in the Settings -> Midi Ports dialog, + such as the GM (default), GS, or XG instruments. + +Typically you should answer yes here. +You can always do it manually from the Midi menu. + +Continue? + + + + + Don't ask me again + + + + shortcuts - + Transport: Start playback from current location Transport: Abspielen ab momentaner Position @@ -14465,6 +16194,11 @@ + Transport: Restart recording + + + + Transport: Goto left marker Transport: Zum linken Marker gehen @@ -14494,7 +16228,7 @@ Vollbild umschalten - + Edit: Copy Bearbeiten: Kopieren @@ -14525,6 +16259,21 @@ + Edit: Paste to selected track + + + + + Edit: Paste clone + + + + + Edit: Paste clone to selected track + + + + Edit: Paste (with dialog) Bearbeiten: Einfügen (Dialog zeigen) @@ -14734,12 +16483,12 @@ Einstellungen: Globale Konfiguration - + Settings: Configure shortcuts Einstellungen: Tastenkürzel einstellen - + Settings: Configure metronome Einstellungen: Metronom einstellen @@ -14884,14 +16633,17 @@ Bearbeiten: Leeren Takt einfügen - Edit: Paste as clones - Bearbeiten: Als Klone einfügen + Bearbeiten: Als Klone einfügen - Edit: Paste as clones (with dialog) - Bearbeiten: Als Klone einfügen (Dialog zeigen) + Bearbeiten: Als Klone einfügen (Dialog zeigen) + + + + Edit: Duplicate track + @@ -14904,7 +16656,12 @@ Nächste Spur auswählen - + + Edit selected track name + + + + Midi: Transpose Midi: Transponieren @@ -15010,6 +16767,31 @@ Zeigen: Nach links scrollen + + Transport: Step record + + + + + Transport: Midi input + + + + + Transport: Play events + + + + + Edit: Increase velocity + + + + + Edit: Decrease velocity + + + Edit: Set Fixed Length on Midi Events Bearbeiten: feste Länge für Midi-Events setzen @@ -15085,6 +16867,11 @@ Bearbeiten: Eventfarbe ändern + + Move: Move to selected note + + + Tool: Pointer Werkzeug: Zeiger @@ -15100,6 +16887,16 @@ Werkzeug: Radierer + + Tool: Pan + + + + + Tool: Zoom + + + Tool: Line Draw Werkzeug: Linie @@ -15151,6 +16948,11 @@ + Tool: Range + + + + Tool: Scissor Werkzeug: Schere @@ -15305,7 +17107,7 @@ Tonart einfügen - + Goto Next Marker Zum nächsten Marker gehen @@ -15314,5 +17116,28 @@ Goto Prev Marker Zum vorherigen Marker gehen + + + Normalize + + + + + warnBadTimingBase + + + Bad timing + Schlechter Taktgeber + + + + Message here + + + + + Don't warn me again + + diff -Nru muse-2.1.2/share/locale/muse_en.ts muse-3.0.2+ds1/share/locale/muse_en.ts --- muse-2.1.2/share/locale/muse_en.ts 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/muse_en.ts 2017-12-04 21:01:19.000000000 +0000 @@ -1,118 +1,33 @@ - + @default - - Add Midi Track - - - - - - Add Drum Track - - - - - Add Old Style Drum Track - - - - - Add New Style Drum Track - - - - - Add Wave Track - - - - - Add Audio Output - - - - - Add Audio Group - - - - - Add Audio Input - - - - - Add Aux Send - - - - - Add Synth - - - - - Select project directory + + Route - - Route + + channel="%1" - + dest - + name="%1"/ - - Warning: No output devices! - - - - - Open midi config... - - - - - Empty ports - - - - + <none> - - - channelMask="%1" - - - - - Bad timing - - - - - Timing source frequency is %1hz, which is below the recommended minimum: 500hz! -This could lead to audible timing problems for MIDI. -Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages. - - - AboutBox @@ -122,13 +37,13 @@ - - Version 2 + + Version info (replaced programmatically) - - (C) Copyright 1999-2012 Werner Schweer and others. + + (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. @@ -136,12 +51,22 @@ - + + System information: + + + + + TextLabel + + + + &Keep On Rocking! - + Alt+K @@ -229,242 +154,257 @@ - + add - + remove - + clear - + show snap grid - + Colors - + Items - + Palette - + add to palette - + B - + S - + H - + V - + G - + R - + Color name: - + Global opacity - + Style/Fonts - - QT Theme - - - - + Windows - + MusE - + Metal - + Norwegian Wood - + Platinum - + CDE - + Motif - + Motif Plus - + May require restarting MusE for best results - + Style Sheet: - - - - - - - - - + + + + + + + + + ... - + + MusE color scheme + + + + + current settings + + + + + Change + + + + Fonts - + Family - + Size - + Font 1 - + Font 2 - + Font 3 - + Font 0 - - - - - - - + + + + + + + Bold - - - - - - - + + + + + + + Italic - + Font 4 - + Font 5 - + + Themes + + + + Font 6 - + Apply - + Ok - + Cancel @@ -498,100 +438,60 @@ - - Control7 - - - - - Control14 - - - - - RPN - - - - - NRPN - - - - - RPN14 - - - - - NRPN14 - - - - - Pitch - - - - - Program - - - - + H-Ctrl - - + + Midi controller number high byte - + L-Ctrl - - + + Midi controller number low byte - + * wild card - + affect CCs at - + begin of song - + current position - + &Add - + &Delete - + Done @@ -618,6 +518,40 @@ + ChooseSysexBase + + + Dialog + + + + + Hex: + + + + + + TextLabel + + + + + Comment: + + + + + &OK + + + + + &Cancel + + + + ClipListEditorBase @@ -696,117 +630,190 @@ - + Split tracks into &parts - + Alt+P - + Split tracks into parts, or one single part - + + Default instrument: + + + + + Device Name metas trump Port metas if both exist + + + + + Instrument Name metas trump Mode sysexes if both exist + + + + Use new-style drum tracks - + Use old-style drum tracks - + Export: - + 96 - + 192 - + 384 - + Enable extended smf format (currently not implemented) - + Use &2-byte time signatures instead of standard 4 - + Alt+2 - + + Mode sysexes + + + + + Instrument name metas + + + + + + Both + + + + + Port metas + + + + + Device name metas + + + + + Export a Port/Device meta for format 0 + + + + Copyright: - + Format: - + Note: Format 0 uses the FIRST midi track's name/comment in the arranger - + Division: - + Save space by replacing note-offs with &zero velocity note-ons - + Alt+Z - + 0 (single track) - + 1 (multiple tracks) - + &OK - + &Cancel + CopyOnWriteDialogBase + + + Copy Wave Files + + + + + Some sound files will be copied to the Project Directory, +either because they are not writable or because more +than one independent Wave Event shares them. +(If you prefer instead that the Wave Events are + inter-dependent, try using Clone Parts.) + +Multiple copies will be made in some cases. + +If no Project has been created yet, you will be asked to, +giving another chance to cancel. + + + + + These files will be copied to the Project Directory: + + + + CrescendoBase @@ -880,311 +887,311 @@ - - - - - - - - + + + + + + + + Critical Error - - - - + + + + Cannot open file %1 - - - - + + + + Parsing error for file %1 - + Load category dialog - + Load set dialog - + Save set dialog - + New category - - + + Delete category - + Load category - + Save category - + Load set - + Save set - + Delete set - + New subcategory - - + + Delete subcategory - + Load subcategory - + Save subcategory - + New preset - - + + Delete preset - + Load preset - + Save preset - + No more category supported - + You can not add more categories - - - + + + Do you really want to delete %1 ? - - - + + + &Yes - - - + + + &No - - + + No category selected - - + + You must first select a category. - - - + + + Replace or add - + %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? - - - + + + &Replace - - - + + + &Add - - - + + + Download error - + There is no more free category slot. - + Save category dialog - + No more subcategory supported - + You can not add more subcategories - - + + No subcategory selected - - + + You must first select a subcategory. - + Load subcategory dialog - + %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? - + There is no more free subcategory slot. - + Save subcategory dialog - + No more preset supported - + You can not add more presets - - - + + + No preset selected - - - + + + You must first select a preset. - + Load preset dialog - + %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? - + There is no more free preset slot. - + Save preset dialog - + Browse set dialog - + Browse image dialog @@ -1534,7 +1541,7 @@ Release Rate - + @@ -1542,7 +1549,7 @@ 2° Decay Rate - + @@ -1550,7 +1557,7 @@ 1° Decay Level - + @@ -2614,17 +2621,17 @@ - + Don't show on startup - + Next tip - + Close @@ -2668,26 +2675,26 @@ - Copy standard controllers (vol, pan) + Copy standard (vol, pan) and synth controllers - + Copy effects rack plugins - + Copy plugin controllers - + Ok - + Cancel @@ -2695,73 +2702,78 @@ EditCtrlBase - + MusE: Edit Controller Event - + Time Position - + Available Controller: - + Create New Controller - + textLabel3 - + Value - + Controller - + + Note + + + + H-Bank - + L-Bank - + Program - - + + off - + pushButton4 - + &OK - + &Cancel @@ -2863,14 +2875,14 @@ - + Properties - - + + Name: @@ -2933,247 +2945,196 @@ - - Drum patch - - - - - If set, the patch is available only for drum channels. - - - - + + + Drum - + GM patch - + If set, the patch is available in a 'GM' or 'NO' midi song type. - + GM - + GS patch - + If set, the patch is available in a 'GS' or 'NO' midi song type. - + GS - + XG patch - + If set, the patch is available in an 'XG' or 'NO' midi song type. - + XG - + + + Show in tracks: + + + + + + + Midi + + + + Delete group or patch - - - + + + + &Delete - - - + + + Alt+D - + New patch - + New &Patch - + Alt+P - + New group - + New &Group - + Alt+G - - Contro&ller + + List of defined controllers - - Common: + + List of defined controllers. - - This is a list of commonly used midi controllers. -Note that in MusE pitch and program changes are -handled like normal controllers. + + Name - - List of defined controllers - - - - - List of defined controllers. - - - - - Name - - - - + Type - - + + H-Ctrl - - + + L-Ctrl - + Min - + Max - + Def - + Midi controller name - + Type: - - + + Midi controller type - - Control7 - - - - - Control14 - - - - - RPN - - - - - NRPN - - - - - RPN14 - - - - - NRPN14 - - - - - Pitch - - - - - Program - - - - - + + Midi controller number high byte - + Midi controller number low byte (* means drum controller) - + Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and @@ -3186,28 +3147,28 @@ - + * wild card - + Range: - + Min - + Minimum value. If negative, auto-translate. - + Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. @@ -3230,57 +3191,62 @@ - + Max - - + + Maximum value - + Default: - + L-Bank - - - - - + + + + + off - + Progr. - + ??? - + H-Bank - + + Drum&maps + + + + Default value. Off: No default. - + Default (initial) value. Off means no default. If a default value is chosen, the value will be sent @@ -3299,60 +3265,54 @@ - + off dont care - + Add common controller - - + &Add - + Alt+A - + Delete controller - + Create a new controller - + New &Controller - + Alt+C - - Null Param Hi: - - - - + Null parameter number High byte - - + + If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events @@ -3362,188 +3322,228 @@ - + Lo: - + Null parameter number Low byte - + S&ysEx - + SysEx List: - + Hex Entry: - + New SysE&x - + Alt+X - - Drummaps - - - - + Patch Collections: - + &Copy - + &Remove - + &Up - + &Down - + Patch: - - - + + + from - - - + + + to - + Bank Hi: - + Bank Lo: - + + Contro&llers + + + + + Null Parameters: Hi: + + + + + &Add Common... + + + + + W + + + + + Comment: + + + + + &Initialization + + + + + Instrument initialization sequence: + + + + + &Add... + + + + + &Change... + + + + Tools - + &File - + &Help - + &New - + New - + Ctrl+N - + &Open... - + Open - + Ctrl+O - + &Save - + Save - + Ctrl+S - + Save &As... - + Save As - - E&xit + + &Close - - Exit + + Close - - + + new item - - + + What's this? @@ -3605,56 +3605,203 @@ + Name: + + + + Comment: - - OK + + &Select... - - Cancel + + &OK + + + + + &Cancel + + + + + FLUIDSynthGuiBase + + + FLUID Synth + + + + + Load + + + + + Delete + + + + + Dump Info + + + + + ID + + + + + Fontname + + + + + Chnl + + + + + Soundfont + + + + + Drum Chnl + + + + + + Level + + + + + Width + + + + + Damping + + + + + Room Size + + + + + Reverb + + + + + CHANNEL SETUP + + + + + Sine + + + + + Triangle + + + + + Type + + + + + Number + + + + + Speed + + + + + Depth + + + + + Chorus + + + + + Gain + + + + + LOADED SOUNDFONTS FileDialogButtons - + fdialogbuttons - + + Home + + + + Global - + User - + Project - + read Midi Port Configuration - + write window states + FluidSynthGui + + + Choose soundfont + + + + GateTimeBase @@ -3735,242 +3882,252 @@ - + Project directory - + Projects: - - - + + + ... - + + start &with last song + + + + + start with &template + + + + + sta&rt with song + + + + Views - - - - - + + + + + y-pos - - - - + + + + show - - - - - + + + + + x-pos - - - - + + + + height - - - - + + + + width - + Mixer A - + Mixer B - + Big Time - + Main Window - + Transport - - - - - + + + + + set current values - - - - - + + + + + Cur - + Start Muse - + Start song - - start with last song - - - - + Choose start song or template - + Reset to default - - start with template - - - - - start with song - - - - + On Launch - + show splash screen - + show "Did you know?" dialog - + Start template or song: - + Read MIDI Ports configuration from file, or else automatically configure - + Read MIDI Ports configuration - - Audio + + Warn if opening file versions different than current version + Auto save (every 5 minutes if not playing/recording) + + + + + Audio + + + + Mixer - - + + dB - + min. Meter Value - + min. Slider Val - + Try to use Jack Freewheel - + Speeds bounce operations - + Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. - + Enable denormal protection - + Enable output limiter - + VST in-place - + Enable VST in-place processing (restart required) - + Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to @@ -3978,18 +4135,18 @@ - + Minimum control period - + Minimum audio controller process period (samples). - + Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth @@ -3998,290 +4155,340 @@ - + 1 - + 2 - + 4 - + 8 - - + + 16 - - + + 32 - - + + 64 - - + + 128 - - + + 256 - - + + 512 - - - + + + 1024 - - - + + + 2048 - + External Waveditor - + External Waveditor command - + Note: External editor opened from the internal editor. - + Dummy Audio Driver (settings require restart) - + Sample rate - + Hz - + Period size (Frames per period): - + Shorter periods give better midi playback resolution. - + Midi - - Ticks + + &Record all instruments - - RTC Resolution -(Ticks/Sec) + + &Don't record hidden instruments - - 4096 + + Don'&t record muted instruments - - 8192 + + Don't record &hidden or muted instruments - - 16384 + + Instrument initialization - - 32768 + + Send instrument initialization sequences - - Midi Resolution -(Ticks/Quarternote) + + Warn if instrument initialization sequences pending - - - 48 + + Send instrument controller default values if none in song, at init or rewind - - + + Ticks + + + + + RTC Resolution +(Ticks/Sec) + + + + + 4096 + + + + + 8192 + + + + + 16384 + + + + + 32768 + + + + + Midi Resolution +(Ticks/Quarternote) + + + + + &Only offer old-style drumtracks + + + + + Only offer new-style &drumtracks + + + + + &Prefer old-style drumtracks + + + + + Prefer &new-style drumtracks + + + + + + 48 + + + + + 96 - - + + 192 - - + + 384 - - + + 768 - - + + 1536 - - + + 3072 - - + + 6144 - - + + 12288 - + Displayed Resolution (Ticks/Quarternote) - + Record new style drum tracks - - Record all instruments - - - - - Don't record hidden instruments - - - - - Don't record muted instruments + + Warn if timer frequency is inadequate - - Don't record hidden or muted instruments + + GUI Behaviour - - GUI Behaviour + + Behavior - - Behavior + + Track height - + GUI Refresh Rate - + /sec - + Use old-style stop shortcut: - + Move single armed track with selection - + Use project save dialog - + Some popup menus stay open (else hold Ctrl) - + Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. - + In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the @@ -4289,104 +4496,121 @@ - + Use left mouse button for decreasing values - + Shift + Right click sets left range marker - + Allow adding hidden tracks in track list menu - + Unhide tracks when adding hidden tracks - - + + Smart focus - + After editing, controls will return focus to their respective canvas - - Drum tracks + + Show newly created midi velocity graphs per-note + + + + + px + + + + + Enable borderless mouse. +For certain functions like zoom/pan. +Disable to use an alternate standard + method. + - - Only offer old-style drumtracks + + Enable borderless mouse. +For certain functions like zoom. +Disable to use an alternate standard + method. - - Only offer new-style drumtracks + + Borderless zoom/pan mouse (else use alternate method) - - Prefer old-style drumtracks + + Scrollable submenus - - Prefer new-style drumtracks + + Drum tracks - + GUI Style - + MDI-subwindowness and sharing menus - + Presets: - + traditional MusE SDI - + Cakewalk-like MDI - + Borland-/Mac-like MDI - + &Apply - + &Ok - + &Cancel @@ -4566,216 +4790,225 @@ - + Metronome - + Audio Beep - + Choose outputs... - + + + + + 50 - - % Audio volume - - - - + MIDI Click - + Midi Channel - + Measure Note - + Measure Velocity - + Beat Velocity - + Beat Note - + Midi Port - + + Disabled since jack does not support it + + + + Precount - + enable - + Bars - + From Mastertrack - + / - + Signature - + Prerecord - + Preroll - + Hint: Enable metronome in Transportpanel - - &Apply + + Two samples (old samples) - - Alt+A + + Four samples (new samples, with accents) - - &OK + + Volume - - Alt+O + + Audio master - - &Cancel + + Meas - - Alt+C + + Beat - - - MidiAudioControlBase - - Midi control + + Accent1 - - Port: + + Accent2 - - Channel: + + Sample - - Control type: + + &Apply - - Control7 + + Alt+A - - Control14 + + &OK - - RPN + + Alt+O - - NRPN + + &Cancel - - RPN14 + + Alt+C + + + MidiAudioControlBase - - NRPN14 + + Midi control - - Pitch + + Port: - - Program + + Channel: - - Hi: + + Control type: - - Lo: + + &Learn + + + + + Hi: - - Learn + + Lo: @@ -4948,19 +5181,19 @@ - - - - + + + + Equal - - - - + + + + Unequal @@ -4971,84 +5204,82 @@ - + Poly Pressure - + Control Change - + Aftertouch - + Pitch Bend - + NRPN - + RPN - - - + + + Program + + + + + + Value 2 - - - - - + + + + + Value 1 - - + + Event Type - - - - + + + + Ignore - - - - - Higher - - - - Lower + Higher @@ -5056,7 +5287,7 @@ - Inside + Lower @@ -5064,202 +5295,215 @@ + Inside + + + + + + + Outside - - + + Channel - - + + Port - + Processing - - - - - + + + + + Keep - - - - - + + + + + Fix - - - - + + + + Plus - - - - + + + + Minus - - - - + + + + Multiply - - - - + + + + Divide - - - - + + + + Invert - + ScaleMap - + Flip - - - - + + + + Dyn - - - - + + + + Random - + + Toggle + + + + Modules - + 1 - + 2 - + 3 - + 4 - + enable modul 1 - + enable modul 2 - + enable modul 3 - + enable modul 4 - + Preset - + Name: - + Comment: - + Function - + create new preset - + &New - + delete preset - + &Delete - + &Dismiss - + PresetList @@ -5513,200 +5757,231 @@ - + output port - + output channel - + all midi events are sent to this output channel - + Out ch - + input routing - + output routing - + input detect - + Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. - + W - + Midi thru - + Pass input events through ('thru') to output. - - - - - - - + + + + + + + off - + Transp. - + Delay - - + + % - + Length - + Velocity - + Compr. - + Channel Info - + Select instrument patch - + + <unknown> - + Rec: - + Add all settings to song - + All - + Bank Select MSB. Ctrl-double-click on/off. - + Bank Select LSB. Ctrl-double-click on/off. - + Program. Ctrl-double-click on/off. - + Volume. Ctrl-double-click on/off. - - + + Change stereo position. Ctrl-double-click on/off. - + H-Bank - - L-Bank + + Change note length in percent of actual length - - Add bank + prog settings to song + + Offset playback of notes before or after actual note - + + Transpose notes up or down + + + + + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + + + + + Compress the notes velocity range, in percent of actual velocity + + + + + L-Bank + + + + + Add bank + prog settings to song + + + + Prog - + Add vol setting to song - + Vol - + Add pan setting to song - + Pan + + + Select instrument + + MidiTransformDialogBase @@ -6176,14 +6451,33 @@ + MusECore::AudioPreviewDialog + + + Auto play + + + + + + Stop + + + + + Play + + + + MusECore::Song - + Jack shutdown! - + Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: @@ -6202,101 +6496,126 @@ - - + + Automation: - + previous event - + next event - - + + set event - - + + add event - - + + erase event - + erase range - + clear automation - + Midi control - + Assign - + Clear - + Clear all controller events? - + &Ok - + &Cancel - + MusE: Tempo list - + External tempo changes were recorded. Transfer them to master tempo list? - + + Do you want to process ALL or only selected events? + + + + + &Selected + + + + + &All + + + + MusE - external script failed - + MusE was unable to launch the script, error message: %1 + + + Und&o + + + + + Re&do + + MusEDialog::PasteDialog @@ -6396,22 +6715,37 @@ - + Keep Qt system style - + + Do you really want to reset colors to theme default? + + + + + &Ok + + + + + &Cancel + + + + MusE: load image - + Select style sheet - + Qt style sheets (*.qss) @@ -6419,62 +6753,62 @@ MusEGui::Arranger - + Enable Recording - + Mute/Off Indicator - + Solo Indicator - + Track Type - + Track Name - + Midi output channel number or audio channels - + Midi output port or synth midi port - + Time Lock - + Automation parameter selection - + Notation clef - + Enable recording. Click to toggle. - + Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. @@ -6482,33 +6816,33 @@ - + Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. - + Track type. Right-click to change midi and drum track types. - + Track name. Double-click to edit. Right-click for more options. - + Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. - + Midi/drum track: Output port. Synth track: Assigned midi port. Left-click to change. @@ -6516,465 +6850,492 @@ - + Time lock - + Notation clef. Select this tracks notation clef. - + Arranger - + Cursor - + Off - + Bar - + Snap - + Len - - + + song length - bars - - Type + + Pitch - - NO + + midi pitch - - GM + + global midi pitch shift - - GS + + Tempo - - XG + + + midi tempo - - - midi song type + + N - - Pitch + + TrackInfo - - midi pitch + + R - - global midi pitch shift + + M - - Tempo + + S - - - midi tempo + + C - - N + + Track - - TrackInfo + + Port + + + + + Ch - R + T - M + Automation - S + Clef + + + MusEGui::ArrangerColumns - - C + + Control7 - - Track + + Control14 - - Port + + RPN - - Ch + + NPRN - - T + + RPN14 - - Automation + + NRPN14 - - Clef + + Pitch + + + + + Program + + + + + Aftertouch MusEGui::ArrangerView - + MusE: Arranger - + + D&elete + + + + C&ut - + &Copy - + Copy in range - + &Paste - - Paste (show dialog) + + Paste c&lone - - Paste c&lone + + Paste to selected &track - - Paste clone (show dialog) + + Paste clone to selected trac&k - + + Paste (show dialo&g) + + + + &Insert Empty Measure - + Delete Selected Tracks - + Duplicate Selected Tracks - + Shrink selected parts - + Expand selected parts - + Purge hidden events from selected parts - + Add Track - + Select - + Select &All - + &Deselect All - + Invert &Selection - + &Inside Loop - + &Outside Loop - + All &Parts on Track - + Score - + all tracks in one staff - + one staff per track - + New score window - + Pianoroll - + Drums - - + + List - + Wave - + Mastertrack - + Graphic - + Midi &Transform - + Global Cut - + Global Insert - + Global Split - + Global Cut - selected tracks - + Global Insert - selected tracks - + Global Split - selected tracks - + &Edit - + &Structure - + Functions - + &Quantize Notes - + Change note &length - + Change note &velocity - + Crescendo/Decrescendo - + Transpose - + Erase Events (Not Parts) - + Move Events (Not Parts) - + Set Fixed Note Length - + Delete Overlapping Notes - + Legato - + Window &Config - + Configure &custom columns - + Remove track(s) - + Are you sure you want to remove this track(s)? - - + + New - + Changed Settings - + Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. @@ -7048,98 +7409,98 @@ MusEGui::AudioStrip - + panorama - + aux send level - - Pan + + calibration gain - + 1/2 channel - + Pre - + pre fader - post fader - + dB - + record - + mute - + record downmix - - + + solo mode - + off - + input routing - + output routing - + Off - + Read - + Touch - + Write - + automation type @@ -7147,54 +7508,62 @@ MusEGui::BigTime - + format display - + bar - + beat - - + + tick - + minute - + second - - + + frame - + subframe - + MusE: Bigtime + MusEGui::Canvas + + + Tools: + + + + MusEGui::ClipListEdit @@ -7220,18 +7589,18 @@ MusEGui::CtrlCanvas - - + + Make the current part's track match the selected drumlist entry - + Drawing hint: Hold Ctrl to affect only existing events - + Use pencil or line tool to draw new events @@ -7239,93 +7608,60 @@ MusEGui::CtrlPanel - + S - + select controller - + X - + remove panel - + manual adjust - + ctrl-double-click on/off - + off - - - Velocity - - - - - add new ... - - - - - - Instrument-defined - - - - - - Add ... - - - - - Others - - - - - Edit instrument ... - - - - - Common Controls + + all/per-note velocity mode MusEGui::DList - + hide this instrument - + show this instrument - + this turns a grayed out eye into a blue eye @@ -7333,47 +7669,47 @@ MusEGui::DrumCanvas - + Moving items failed - + The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. - + Creating event failed - + Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. - - + + Recording event failed - - + + Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. - + Internal error - + Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. @@ -7381,656 +7717,700 @@ MusEGui::DrumEdit - - + + mute instrument - - - sound name + + + sound name - - + + volume percent - - + + quantisation - - + + this input note triggers the sound - - + + note length - - + + this is the note which is played - - output channel (hold ctl to affect all rows) - - - - - output port (hold ctl to affect all rows) - - - - - - shift + control key: draw velocity level 1 - - - - - - control key: draw velocity level 2 - - - - - - shift key: draw velocity level 3 - - - - - - draw velocity level 4 - - - - - output channel (ctl: affect all rows) - - - - - output port (ctl: affect all rows) - - - - + Load Map - + Save Map - + Reset GM Map - + &Edit - + Cut - + Copy - + Copy events in range - + Paste - + Paste (with Dialog) - + Delete Events - + &Select - + Select All - + Select None - + Invert - + Inside Loop - + Outside Loop - + Previous Part - + Next Part - + Fu&nctions - + Set Fixed Length - + Modify Velocity - + Crescendo/Decrescendo - + Quantize - + Erase Event - + Move Notes - + Delete Overlaps - + &Plugins - + Window &Config - + Drum tools - + Load Drummap - - + + hide instrument - + + override track output channel (hold ctl to affect all rows) + + + + + override track output port (hold ctl to affect all rows) + + + + + + control + meta keys: draw velocity level 1 + + + + + + meta key: draw velocity level 2 + + + + + + draw default velocity level 3 + + + + + + meta + alt keys: draw velocity level 4 + + + + + override track output channel (ctl: affect all rows) + + + + + override track output port (ctl: affect all rows) + + + + Re-order map - + Group - + Don't group - + Group by channel - + Group maximally - + Show/Hide - + Also show hidden instruments - + Show all instruments - + Hide all instruments - + Only show used instruments - + Only show instruments with non-empty name or used instruments - + Drum map tools - + Store Drummap - + Step Record - + Midi Input - + + Play Events + + + + cursor tools - + Cursor step: - + Set step size for cursor edit - + ctrl - + Add Controller View - + H - + M - + Sound - + Vol - + QNT - + E-Note - + Len - + A-Note - + Ch - + Port - + LV1 - + LV2 - + LV3 - + LV4 - + Muse: Load Drum Map - + MusE: Store Drum Map - + Drum map - + Reset the drum map with GM defaults? - + Not all parts are displayed - + You selected both old-style-drumtracks and others (that is: new-style or midi tracks), but they cannot displayed in the same drum edit. I'll only display the old-style drumtracks in this editor, dropping the others. - MusEGui::EditCAfterDialog + MusEGui::EditEventDialog - - MusE: Enter Channel Aftertouch + + Ok - - Time Position + + Cancel + + + + + MusEGui::EditInstrument + + + Control7 - - Pressure + + Control14 - - - MusEGui::EditEventDialog - - Ok + + RPN - - Cancel + + NPRN + + + + + RPN14 + + + + + NRPN14 + + + + + Pitch + + + + + Program - - - MusEGui::EditInstrument - + + PolyAftertouch + + + + + Aftertouch + + + + + Name - + Vol - + Quant - + E-Note - + + Len - + A-Note - + LV1 - + LV2 - + LV3 - + LV4 - - + + Tick + + + + + Data + + + + + MusE: Create file failed - + MusE: Write File failed - - + + MusE: Save Instrument Definition - - + + Instrument Definition (*.idf) - - + + MusE: Save instrument as - + Enter a new unique instrument name: - + The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? - + MusE: Bad instrument name - + Please choose a unique instrument name. (The name might be used by a hidden instrument.) - + MusE: Bad patch name - + Please choose a unique patch name - + MusE: Bad patchgroup name - + Please choose a unique patchgroup name - + MusE: Bad controller name - + Please choose a unique controller name - - - MusE: Cannot add common controller - - - - - A controller named '%1' already exists. + + New controller: Error - - A controller number %1 already exists. + + Error! All control numbers are taken up! +Clean up the instrument! - - + + MusE - - + + The current Instrument contains unsaved data Save Current Instrument? - - + + &Save - - + + &Nosave - + &Abort @@ -8038,53 +8418,30 @@ MusEGui::EditMetaDialog - + MusE: Enter Meta Event - + Time Position - + Meta Type - + Enter Hex - MusEGui::EditPAfterDialog - - - MusE: Enter Poly Aftertouch - - - - - Time Position - - - - - Pitch - - - - - Pressure - - - - MusEGui::EditToolBar - + select Pointer Tool: with the pointer tool you can: select parts @@ -8093,7 +8450,7 @@ - + select Pencil Tool: with the pencil tool you can: create new parts @@ -8101,113 +8458,147 @@ - + select Delete Tool: with the delete tool you can delete parts - + select Cut Tool: with the cut tool you can split a part - + select Glue Tool: with the glue tool you can glue two parts - + select Score Tool: - + select Quantize Tool: insert display quantize event - + select Drawing Tool - + select Muting Tool: click on part to mute/unmute - - Manipulate automation - - - - Cursor tool + Manipulate automation - + pointer - + pencil - + eraser - + cutter - + score - + glue - + + select Cursor (tracker mode) tool: +with the cursor tool you can: + navigate with arrow keys + use VBNM to place notes + change step with 0 and 9 + + + + + select Range Tool + + + + + select Panning Tool + + + + + select Zoom Tool + + + + + range + + + + + pan + + + + + zoom + + + + quantize - + draw - + mute parts - + edit automation - + cursor - + Edit Tools @@ -8215,67 +8606,72 @@ MusEGui::EffectRack - + effect rack - + new - + change - + move up - + move down - + remove - + bypass - + show gui - + show native gui - + save preset - + + Presets + + + + MusE: Save Preset - + Replace effect - + Do you really want to replace the effect %1? @@ -8283,7 +8679,7 @@ MusEGui::GlobalSettingsConfig - + MusE: Choose start template or song @@ -8422,127 +8818,117 @@ MusEGui::ListEdit - + insert Note - + insert SysEx - + insert Ctrl - + insert Meta - - insert Channel Aftertouch - - - - - insert Poly Aftertouch - - - - + &Edit - + Cut - + Copy - + Paste - + Delete Events - + Increase Tick - + Decrease Tick - + Window &Config - + Insert tools - + Tick - + Bar - + Type - + Ch - + Val A - + Val B - + Val C - + Len - + Comment - + MusE: List Editor @@ -8550,278 +8936,341 @@ MusEGui::MPConfig - - + Default input connections - - + + Are you sure you want to apply to all existing midi tracks now? - - + Default output connections - - - Setting will apply to new midi tracks. -Do you want to apply to all existing midi tracks now? - - - - + MusE: bad device name - + please choose a unique device name - - + + in - - + + out - - Show first aliases - - - - - Show second aliases + + + Toggle all - - - Toggle all + + + Change all tracks now - - - Change all tracks now + + Remove - + Create Jack device - - + + Port Number - + + Enable gui - + + Enable reading - + + Enable writing - + Port instrument - + Midi device name. Click to edit (Jack) - + Connections from Jack Midi outputs - + Connections to Jack Midi inputs - + Auto-connect these channels to new midi tracks - + Auto-connect new midi tracks to these channels - + Auto-connect new midi tracks to this channel + Device state - + Enable gui for device - + + Enable reading from device - + + Enable writing to device - + Name of the midi device associated with this port number. Click to edit Jack midi name. - + Instrument connected to port - + Connections from Jack Midi output ports - + Connections to Jack Midi input ports - + Auto-connect these channels, on this port, to new midi tracks. - + Connect new midi tracks to these channels, on this port. - + Connect new midi tracks to this channel, on this port. - + State: result of opening the device - + Port - + + GUI - + + I - + + O - + Instrument - + + Device Name - - In routes + + + Midi device name - - Out routes + + + Midi device type - - Def in ch + + Connections from Jack Midi - - Def out ch + + Connections to Jack Midi - - State + + Result of opening the device: +OK: Assigned to a port and in use +Closed: Unassigned to a port, or closed +R/W Error: Unable to open for read or write +Unavailable: USB midi unplugged, or external + application not running, or synth plugin + not installed etc. +(Jack Midi devices have 'unavailable ports' + in the routes columns.) +Unavailable devices or ports can be purged + with 'Remove' or with the advanced router. - - <unknown> + + Enable Graphical User Interface for device - - - <none> + + Connections from Jack Midi ports - - - MusEGui::MTScale - - bar scale + + Connections to Jack Midi ports - - - MusEGui::MTScaleFlo - - bar scale + + In routes - + + + Out routes + + + + + Def in ch + + + + + Def out ch + + + + + + State + + + + + Type + + + + + In + + + + + Out + + + + + <unknown> + + + + + <none> + + + + + MusEGui::MTScale + + + bar scale + + + + + MusEGui::MTScaleFlo + + + bar scale + + + MusEGui::MarkerView @@ -8959,10 +9408,58 @@ + MusEGui::MidiAudioControl + + + Control7 + + + + + Control14 + + + + + RPN + + + + + NPRN + + + + + RPN14 + + + + + NRPN14 + + + + + Pitch + + + + + Program + + + + + Aftertouch + + + + MusEGui::MidiInputTransformDialog - - + + New @@ -9405,8 +9902,10 @@ MusEGui::MidiTrackInfo - - + + + + <unknown> @@ -9414,8 +9913,8 @@ MusEGui::MidiTransformerDialog - - + + New @@ -9431,671 +9930,696 @@ MusEGui::MusE - - + + Failed to start audio! - + Was not able to start audio, check if jack is running. - + Timeout waiting for audio to run. Check if jack is running. - + Und&o - + Re&do - + undo last change to song - + redo last undo - + Loop - + loop between left mark and right mark - + Punchin - + record starts at left mark - + Punchout - + record stops at right mark - + Start - + rewind to start position - + Rewind - + rewind current position - + Forward - + move current position - + Stop - + stop sequencer - + Play - + start sequencer play - + Record - + to record press record and then play - - + + Panic - + send note off to all midi channels - + + turn on/off metronome + + + + &New - - + + Create New Song - + &Open - - + + Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. - + Open &Recent - - - + + + &Save - - + + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. - + Save &As - + Import Midifile - + Export Midifile - + Import Part - - - Import Wave File + + + Import Audio File - + Find unused wave files - + &Quit - + Song Info - + Transport Panel - + Bigtime Window - + Mixer A - + Mixer B - + Cliplist - + Marker View - + Arranger View - + Fullscreen - + &Plugins - + Edit Instrument - + Input Plugins - + Transpose - + Midi Input Transform - + Midi Input Filter - + Midi Remote Control - + Rhythm Generator - + Reset Instr. - + Init Instr. - + Local Off - + Bounce to Track - + Bounce to File - + Restart Audio - + Mixer Automation - + Take Snapshot - + Clear Automation Data - + Cascade - + Tile - + In rows - + In columns - + Global Settings - + Configure Shortcuts - + Follow Song - + Don't Follow Song - + Follow Page - + Follow Continuous - + + MusE: Song: + + + + + + Metronome - + Midi Sync - + Midi File Import/Export - + Appearance Settings - + Midi Ports / Soft Synth - + &Manual - + &MusE Homepage - + &Report Bug... - + &About MusE - + Song Position - + Tempo - + Signature - + File Buttons - + Undo/Redo - + Transport - + + Cpu load + + + + + Measured CPU load + + + + + No CPU load data + + + + &File - + &View - + &Midi - + &Audio - + A&utomation - + &Windows - + MusE Se&ttings - + &Help - + About &Qt - + Cannot read template - + File open error - + File read error - + Unknown File Format: %1 - - - + MusE: Song: %1 - + MusE: load project - + MusE: load template - + MusE: Write File failed - + The current Project contains unsaved data Save Current Project? - - + + S&kip - + &Cancel - + MusE: Save As - - + + Nothing to edit - - - - - + + + + + MusE: Bounce to Track - + No wave tracks found - - + + No audio output tracks found - + Select one audio output track, and one target wave track - + Select one target wave track - + Select one target wave track, and one audio output track - - + + MusE: Bounce to File - + Select one audio output track - + MusE: Bounce - + set left/right marker for bounce range - + The current Project contains unsaved data Load overwrites current Project: Save Current Project? - - + + &Abort - + This will clear all automation data on all audio tracks! Proceed? - + This takes an automation snapshot of all controllers on all audio tracks, at the current position. @@ -10103,81 +10627,95 @@ - + + MusE: Warning + + + + + The song uses multiple ports but export format 0 (single track) is set. +The first track's port will be used. Playback will likely be wrong + unless the channels used in one port are different from all other ports. +Canceling and setting a different export format would be better. +Continue? + + + + MusE: Export Midi - + no help found at: - + MusE: Open Help - + Unable to launch help - + For some reason MusE has to launch the default browser on your machine. - + MusE: Import Midi - + Add midi file to current project? - + &Add to Project - + &Replace - + reading midifile - + failed: - + Import part is only valid for midi and wave tracks! - + MusE: load part - + No track selected for import - + %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. @@ -10188,7 +10726,7 @@ - + %n part(s) could not be imported. Likely the selected track is the wrong type. @@ -10199,32 +10737,95 @@ - + to import an audio file you have first to selecta wave track - + Import Wavefile - + This wave file has a samplerate of %1, as opposed to current setting %2. +File will be resampled from %1 to %2 Hz. Do you still want to import it? - + + + + Wave import error + + + + + There are too many wave files +of the same base name as imported wave file +Can not continue. + + + + + Can't create new wav file in project folder! + + + + + + Failed to initialize sample rate converter! + + + + + Cancel + + + + + Resampling wave file +"%1" +from %2 to %3 Hz... + + + + + Output has clipped +Resampling again and normalizing wave file +"%1" +Try %2 of %3... + + + + &Yes - + &No + + + File version is %1.%2 +Current version is %3.%4 +Conversions may be applied if file is saved! + + + + + Opening file + + + + + Do not warn again + + MusEGui::NoteInfo @@ -10267,100 +10868,115 @@ MusEGui::PartCanvas - + Cannot copy/move/clone to different Track-Type - + + Part: + + + + C&ut - + &Copy - + s&elect - + clones - + rename - + color - + delete - + split - + glue - + super glue (merge selection) - + de-clone - - - + + + save part to disk - + wave edit - + file info - + + Normalize + + + + MusE: save part - + Part name: %1 Files: - + + Automation: + + + + Remove selected - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -10371,7 +10987,7 @@ - + %n part(s) could not be pasted. Likely the selected track is the wrong type. @@ -10382,32 +10998,32 @@ - + Cannot paste: multiple tracks selected - + Cannot paste: no track selected - + Can only paste to midi/drum track - + Can only paste to wave track - + Can only paste to midi or wave track - + Cannot paste: wrong data type @@ -10461,192 +11077,192 @@ MusEGui::PianoRoll - + &Edit - + C&ut - + &Copy - + Copy events in range - + &Paste - + Paste (with dialog) - + Delete &Events - + &Select - + Select &All - + &Deselect All - + Invert &Selection - + &Inside Loop - + &Outside Loop - + &Previous Part - + &Next Part - + Fu&nctions - + Quantize - + Modify Note Length - + Modify Velocity - + Crescendo/Decrescendo - + Transpose - + Erase Events - + Move Notes - + Set Fixed Length - + Delete Overlaps - + Legato - + &Plugins - + Window &Config - + &Event Color - + &Blue - + &Pitch colors - + &Velocity colors - + Pianoroll tools - + Step Record - + Midi Input - + Play Events - + ctrl - + Add Controller View @@ -10654,201 +11270,228 @@ MusEGui::PluginDialog - + MusE: select plugin - + + Plugin categories. +Right-click on tabs to manage. +Right-click on plugins to add/remove from a category. + + + + Type - + Lib - + Label - + Name - + AI - + AO - + CI - + CO - + IP - + id - + Maker - + Copyright - + Audio inputs - + Audio outputs - + Control inputs - + Control outputs - + In-place capable - + ID number - - Ok + + &create new group - - Cancel + + &delete currently selected group - - Show plugs: + + re&name currently selected group - - Mono and Stereo + + Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. - - Stereo + + Associated categories - - Mono + + You need to define some categories first. - - Show All + + + new group - - Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. + + + Enter the new group name + + + + + Wine VST - + dssi synth - + dssi effect - - ladspa + + LV2 synth + + + + + LV2 effect - - Search in 'Label' and 'Name': + + ladspa MusEGui::PluginGui - + File Buttons - + Load Preset - + Save Preset - - + + bypass plugin - + MusE: load preset - + Error reading preset. Might not be right type for this plugin - + MusE: save preset + MusEGui::PopupMenu + + + <More...> %1 + + + + MusEGui::ProjectCreateImpl @@ -10857,110 +11500,251 @@ - MusEGui::RoutePopupMenu + MusEGui::RouteDialog - - - - - - - - + + Normal + + + + + Alias 1 + + + + + Alias 2 + + + + + + Source + + + + + + Destination + + + + + <none> + + + + + MusEGui::RoutePopupMenu + + Channel - - - + + + + Soloing chain - - + + + Audio returns - + Warning: No input devices! - + + Channel grouping: + + + + + Mono + + + + + Stereo + + + + + + + + + + + + + Channels + + + + + + Midi ports/devices + + + + + + + Omni + + + + + Show aliases: + + + + + First + + + + + Second + + + + + Show names + + + + + Show first aliases + + + + + Show second aliases + + + + + + Jack ports + + + + + + Connect + + + + + Unavailable + + + + + Open advanced router... + + + + + Output routes: + + + + + Input routes: + + + + Open midi config... - - - - + <none> - - Toggle all + + Midi sends - + + Sources: + + + + More... - - Audio sends + + + Tracks - - Midi port sends + + Destinations: + + + + + Audio sends MusEGui::ScoreCanvas - + Treble - + Bass - + Grand Staff - + Remove staff - + Ambiguous part - + There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. - + No part - + There are no parts you could add the note to. @@ -10968,246 +11752,246 @@ MusEGui::ScoreEdit - + Step recording tools - + Step Record - + Note settings - + Note length: - + last - - - + + + Apply to new notes: - - + + Apply to selected notes: - + Velocity: - + Off-Velocity: - + Quantisation settings - + Quantisation: - + Pixels per whole: - + &Edit - + C&ut - + &Copy - + Copy events in range - + &Paste - + Paste (with dialog) - + Delete &Events - + &Select - + Select &All - + &Deselect All - + Invert &Selection - + &Inside Loop - + &Outside Loop - + Fu&nctions - + &Quantize - + Change note &length - + Change note &velocity - + Crescendo/Decrescendo - + Transpose - + Erase Events - + Move Notes - + Set Fixed Length - + Delete Overlaps - + Legato - + Window &Config - + Note head &colors - + &Black - + &Velocity - + &Part - + Set up &preamble - + Display &key signature - + Display &time signature - + Set Score &name - - + + Enter the new score title - + Error - + Changing score title failed: the selected title is not unique @@ -11216,17 +12000,17 @@ MusEGui::ScrollScale - + next page - + previous page - + current page number @@ -11234,27 +12018,67 @@ MusEGui::ShortcutCaptureDialog - + Ok - + Cancel - + Shortcut conflicts with %1 - + Undefined + MusEGui::ShortcutConfig + + + Save printable text file + + + + + Text files (*.txt);;All files (*) + + + + + + Error + + + + + Error opening file for saving + + + + + Shortcuts for selected category: + + + + + Legend: + + + + + + An error occurred while saving + + + + MusEGui::SigScale @@ -11278,7 +12102,7 @@ MusEGui::Strip - + Remove track? @@ -11286,237 +12110,257 @@ MusEGui::TList - + <none> - + visible - + no clef - + Treble - + Bass - + Grand - - + + off - + <unknown> - + MusE: bad trackname - + please choose a unique track name - + Unused Devices - - - Update drummap? + + + show gui - - Do you want to use same port for all instruments in the drummap? + + + show native gui - - - &Yes + + + Presets - - - &No + + Clear all controller events? - - - show gui + + &Ok - - - show native gui + + &Cancel + + + + + Change color - + Midi control - + Assign - + Clear - + + Other + + + + + clear automation + + + + Treble clef - + Bass clef - + Grand Staff - + Viewable automation - + Internal - + Synth - + Delete Track - + Track Comment - + Save track's drumlist - + Save track's drumlist differences to initial state - + Load track's drumlist - + Reset track's drumlist - + Reset track's drumlist-ordering - + Copy track's drumlist to all selected tracks - + Copy track's drumlist's differences to all selected tracks - + Insert Track - - + + Drum map - + Reset the track's drum map with instrument defaults? - + Reset the track's drum map ordering? - + Muse: Load Track's Drum Map - + + Drummap + + + + + This drummap was created with a previous version of MusE, +it is being read but the format has changed slightly so some +adjustments may be necessary. + + + + MusE: Store Track's Drum Map - + Midi - + Drum - + New style drum - - - Do you want to use same port and channel for all instruments in the drummap? - - MusEGui::TempoSig @@ -11549,17 +12393,17 @@ - + Solo - + Cursor - + Snap @@ -11567,102 +12411,107 @@ MusEGui::TopWin - + As subwindow - + Shares tools and menu - + Fullscreen - + Undo/Redo tools - + Panic - + + Metronome + + + + Transport - + Song Position - + Tempo - + Signature - + Piano roll - + List editor - + Drum editor - + Master track editor - + Master track list editor - + Wave editor - + Clip list - + Marker view - + Score editor - + Arranger - + <unknown toplevel type> @@ -11918,134 +12767,194 @@ + MusEGui::WaveCanvas + + + Adjust Wave Offset + + + + + Wave offset (frames) + + + + + Part creation failed + + + + + Left and right position markers must be placed inside the current part. + + + + + Part created + + + + + The selected region has been copied to the clipboard and can be pasted in the arranger. + + + + + MusE - external editor failed + + + + + MusE was unable to launch the external editor +check if the editor setting in: +Global Settings->Audio:External Waveditor +is set to a valid editor. + + + + + MusE - file size changed + + + + + When editing in external editor - you should not change the filesize +since it must fit the selected region. + +Missing data is muted + + + + MusEGui::WaveEdit - + &Edit - + Func&tions - + &Gain - + Other - + &Copy - + + &Create Part from Region + + + + C&ut - + &Paste - + Edit in E&xternal Editor - + Mute Selection - + Normalize Selection - + Fade In Selection - + Fade Out Selection - + Reverse Selection - + Select - + Select &All - + &Deselect All - - Window &Config + + &Previous Part - - WaveEdit tools + + &Next Part - - Solo + + Window &Config - - Cursor + + &Event Color - - - MusEGui::WaveView - - MusE - external editor failed + + &Part colors - - MusE was unable to launch the external editor -check if the editor setting in: -Global Settings->Audio:External Waveditor -is set to a valid editor. + + &Gray - - MusE - file size changed + + WaveEdit tools - - When editing in external editor - you should not change the filesize -since it must fit the selected region. - -Missing data is muted + + Solo + + + + + Cursor @@ -12334,6 +13243,59 @@ + PluginDialogBase + + + Dialog + + + + + Ports: + + + + + Mono + + + + + Mono + Stereo + + + + + Stereo + + + + + All + + + + + Plugin type: + + + + + Filter: + + + + + &OK + + + + + &Cancel + + + + ProjectCreate @@ -12347,8 +13309,8 @@ - - + + ... @@ -12379,11 +13341,11 @@ - Create project folder (recommended for audio projects) + &Create project folder (recommended for audio projects) - + Song information: @@ -12402,44 +13364,153 @@ Please first select the range for crescendo with the loop markers. + + + Controller ! + + + + + Other ! + + + + + Select gui type + + + + + Preset actions + + + + + Save preset... + + + + + Update list + + + + + Saved presets + + + + + No presets found + + + + + Enter new preset name + + + + + Midi programs + + + + + Presets + + + + + Switch on General Midi Level 1 mode + + + + + Switch on General Midi Level 2 mode + + + + + Switch off General Midi Level 1 or 2 + + + + + Switch on Roland GS mode + + + + + Switch on Yamaha XG mode + + + + + Tracks: + + + + + Midi ports: + + + + + Midi devices: + + + + + Jack: + + + + + Jack midi: + + QWidget - - + + Cannot convert sysex string - - + Hex String too long (2048 bytes limit) - + generic midi - + new - - + + None - + create peakfile for - + + No wave events selected. + + + + No selection. Ignoring @@ -12467,29 +13538,60 @@ - + File %1 exists. Overwrite? - + Open File %1 failed: %2 - + MusE: write - + MusE: Open File + + + Instrument-defined + + + + + + Edit instrument ... + + + + + Add + + + + + Others + + + + + Common Controls + + + + + Velocity + + QuantBase @@ -12710,52 +13812,107 @@ - - Add Route + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). - - Source: + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. - - Destination: + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). - - connect source to destination + + Itemized list of current connections. - - Connect + + + Show only selected sources - - Current Routes + + + Show only destination routes for selected source - - Source + + + Show only selected destinations - - Destination + + + Show only source routes for selected destination + + + + + + Show all Midi Ports + + + + + Auto adjust column size + + + + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + + + + + + Preferred route name or alias - - remove selected route + + + Connect source to destination - + + + Remove selected route + + + + + Connect + + + + Remove @@ -12816,22 +13973,22 @@ SS_PluginFront - + Clear and unload effect - + Load effect - + Toggle display of effect parameters - + Turn effect on/off @@ -12985,6 +14142,26 @@ Alt+A + + + &Printable file... + + + + + Alt+P + + + + + &Ok + + + + + Alt+O + + SimpleDrumsGuiBase @@ -12997,17 +14174,32 @@ SimpleSynthGui - + + Mix + + + + + Chn + + + + + Channel routing + + + + &Load setup - + &Save setup - + Load sample dialog @@ -13048,75 +14240,98 @@ SynthConfigBase - - Midi Port and Soft Synth Configuration + + Name - - Instances + + Type - - - Name + + Configure midi devices, midi ports, and synthesizers - - - Type + + Available soft synthesizers + + + + + list of available software synthesizers - Midi Port + File - Remove Instance + Inst - - Midi connections + + Version - - Soft Synthesizer + + Description - - Add Instance + + Useable devices - - list of available software synthesizers + + Add: + + + + + Synth - File + ALSA - - Inst + + JACK - - Version + + Rename - - Description + + Remove + + + + + Device port assignments + + + + + &Apply + + + + + &OK @@ -13211,13 +14426,13 @@ VAMGui - + MusE: Load VAM Presets - - + + MusE: Save VAM Presets @@ -13407,7 +14622,7 @@ On - + VAM 1.0beta3 Virtual Analog for MusE @@ -13492,174 +14707,174 @@ file_patterns - + Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) - + Midi (*.mid *.MID *.mid.gz *.mid.bz2) - + Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) - - - - - - - - - - - - + + + + + + + + + + + + All Files (*) - + Midi (*.mid) - + Karaoke (*.kar) - + all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) - + med Files (*.med *.med.gz *.med.bz2) - - - + + + Uncompressed med Files (*.med) - - - + + + gzip compressed med Files (*.med.gz) - - - + + + bzip2 compressed med Files (*.med.bz2) - + mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) - + (*.jpg *.gif *.png) - + (*.jpg) - + (*.gif) - + (*.png) - + part Files (*.mpt *.mpt.gz *.mpt.bz2) - + part Files (*.mpt) - + gzip compressed part Files (*.mpt.gz) - + bzip2 compressed part Files (*.mpt.bz2) - + Presets (*.pre *.pre.gz *.pre.bz2) - + Presets (*.pre) - + gzip compressed presets (*.pre.gz) - + bzip2 compressed presets (*.pre.bz2) - + Presets (*.map *.map.gz *.map.bz2) - + Presets (*.map) - + gzip compressed presets (*.map.gz) - + bzip2 compressed presets (*.map.bz2) - - Wave/Binary (*.wav *.ogg *.bin) + + Wave/Binary (*.wav *.ogg *.flac *.bin) - - Wave (*.wav *.ogg) + + Wave (*.wav *.ogg *.flac) - + Binary (*.bin) @@ -13954,29 +15169,55 @@ - shortcuts + midiWarnInitPendingBase - - Transport: Start playback from current location + + Instrument initialization - + + MusE should now send some Instrument Initialization Sequences. +The sequences (usually System Exclusive messages) are defined + by the selected instruments in the Settings -> Midi Ports dialog, + such as the GM (default), GS, or XG instruments. + +Typically you should answer yes here. +You can always do it manually from the Midi menu. + +Continue? + + + + + Don't ask me again + + + + + shortcuts + + + Transport: Start playback from current location + + + + Transport: Toggle metronome - + Transport: Stop Playback - + Transport: Goto Start - + Transport: Play, Stop, Rewind @@ -14011,825 +15252,913 @@ - + Edit: Copy - + Edit: Copy in range - + Edit: Undo - + Edit: Redo - + Edit: Cut - + Edit: Paste - + + Edit: Paste to selected track + + + + + Edit: Paste clone + + + + + Edit: Paste clone to selected track + + + + Edit: Paste (with dialog) - + Edit: Delete - + File: New project - + File: Open from disk - + File: Save project - + File: Open recent file - + File: Save as - + File: Load template - + File: Import midi file - + File: Export midi file - + File: Import midi part - + File: Import audio file - + File: Quit MusE - + Edit: Select parts on track - + Open pianoroll - + Open drumeditor - + Open listeditor - + Open waveeditor - + Open graphical mastertrack editor - + Open list mastertrack editor - + Open midi transformer - + Add midi track - + Add drum track - + Add new style drum track - + Add wave track - + Add audio output - + Add audio group - + Add audio input - + Add audio aux - + Structure: Global cut - + Structure: Global insert - + Structure: Global split - + Structure: Cut events - + View: Open mixer #1 window - + View: Open mixer #2 window - + View: Toggle transport window - + View: Toggle bigtime window - + View: Open marker window - + Settings: Follow song by page - + Settings: Follow song off - + Settings: Follow song continuous - + Settings: Global configuration - + Settings: Configure shortcuts - + + Transport: Restart recording + + + + Settings: Configure metronome - + Settings: Midi sync configuration - + Settings: Midi file import/export configuration - + Settings: Appearance settings - + Settings: Midi ports / Soft Synth - + Settings: Audio subsystem configuration - + Midi: Edit midi instruments - + Midi: Open midi input transform - + Midi: Open midi input filter - + Midi: Midi input transpose - + Midi: Midi remote control - + Midi: Random rhythm generator - + Midi: Reset midi - + Midi: Init midi - + Midi: Midi local off - + Audio: Bounce audio to track - + Audio: Bounce audio to file - + Audio: Restart audio - + Automation: Mixer automation - + Automation: Take mixer snapshot - + Automation: Clear mixer automation - + Help: Open Manual - + Help: Toggle whatsthis mode - + Edit: Edit selected part - + Edit: Select nearest part on track above - + Edit: Add nearest part on track above - + Edit: Select nearest part on track below - + Edit: Add nearest part on track below - + Edit: Insert empty measure - - Edit: Paste as clones + + Edit: Duplicate track - - Edit: Paste as clones (with dialog) + + Select track above - - Select track above + + Select track below - - Select track below + + Edit selected track name - + Midi: Transpose - + Edit: Select all - + Edit: Select none - + Edit: Invert Selection - + Edit: Select events/parts inside locators - + Edit: Select events/parts outside locators - + Edit: Select previous part - + Edit: Select next part - + Edit: Select nearest part/event to the left or move cursor - + Edit: Add nearest part/event to the left to selection - + Edit: Select nearest part/event to the right or move cursor - + Edit: Add nearest part/event to the right to selection - + Edit: Set locators to selection - + Edit: Increase pitch - + Edit: Decrease pitch - + Edit: Increase event position - + Edit: Decrease event position - + View: Zoom in - + View: Zoom out - + View: Goto Current Position - - + + View: Scroll left + Transport: Step record + + + + + Transport: Midi input + + + + + Transport: Play events + + + + + Edit: Increase velocity + + + + + Edit: Decrease velocity + + + + Edit: Set Fixed Length on Midi Events - + Quantize - + Modify Note Length - + Modify Velocity - + Edit: Crescendo - + Edit: Thin Out - + Edit: Erase Event - + Edit: Delete Overlaps - + Edit: Note Shift - + Edit: Move Clock - + Edit: Copy Measure - + Edit: Erase Measure - + Edit: Delete Measure - + Edit: Create Measure - + Edit: Change Event Color - + + Move: Move to selected note + + + + Tool: Pointer - + Tool: Pencil - + Tool: Eraser - + + Tool: Pan + + + + + Tool: Zoom + + + + Tool: Line Draw - + Tool: Cursor - + Add note velocity 1 - + Add note velocity 2 - + Add note velocity 3 - + Add note velocity 4 - + Cursor step size: larger - + Cursor step size: smaller - + Instrument/Cursor up - + Instrument/Cursor down - + + Tool: Range + + + + Tool: Scissor - + Tool: Glue - + Tool: Mute - + Transport: Increase current position - + Transport: Decrease current position - + Transport: Increase current position, no snap - + Transport: Decrease current position, no snap - + Quantize: Set quantize to 1/1 note - + Quantize: Set quantize to 1/2 note - + Quantize: Set quantize to 1/4 note - + Quantize: Set quantize to 1/8 note - + Quantize: Set quantize to 1/16 note - + Quantize: Set quantize to 1/32 note - + Quantize: Set quantize to 1/64 note - + Quantize: Toggle triol quantization - + Quantize: Toggle punctuation quantization - + Quantize: Toggle punctuation quantization (2) - + Edit: Insert at location - + Edit: Increase length - + Edit: Decrease length - + Insert Note - + Insert SysEx - + Insert Ctrl - + Insert Meta - + Insert Channel Aftertouch - + Insert Key Aftertouch - + Insert Tempo - + Insert Signature - + Change Event Position - + Edit Event Value - + Insert Key - + Goto Next Marker - + Goto Prev Marker + + + Normalize + + + + + warnBadTimingBase + + + Bad timing + + + + + Message here + + + + + Don't warn me again + + diff -Nru muse-2.1.2/share/locale/muse_es.ts muse-3.0.2+ds1/share/locale/muse_es.ts --- muse-2.1.2/share/locale/muse_es.ts 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/muse_es.ts 2017-12-04 21:01:19.000000000 +0000 @@ -1,71 +1,52 @@ - + @default - Add Midi Track Agregar pista MIDI - - Add Drum Track Agregar pista de percusión - - Add Old Style Drum Track - - - - - Add New Style Drum Track - - - - Add Wave Track Agregar pista de audio - Add Audio Output Agregar salida de audio - Add Audio Group Agregar grupo de audio - Add Audio Input Agregar entrada de audio - Add Aux Send Agregar envio auxiliar - Add Synth Agregar sintetizador - - Select project directory - - - - + Route Ruta - + + channel="%1" + + + + dest dest @@ -75,44 +56,26 @@ nombre="%1"/ - Warning: No output devices! Atencion: No hay salidas disponibles - Open midi config... Configurar MIDI... - Empty ports Puertos no asignados - + <none> <ninguno> - channelMask="%1" channelMask="%1" - - - Bad timing - - - - - Timing source frequency is %1hz, which is below the recommended minimum: 500hz! -This could lead to audible timing problems for MIDI. -Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages. - - - AboutBox @@ -136,23 +99,53 @@ y mas información. - - Version 2 + (C) Copyright 1999-2012 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Derechos de autor 1999-2010 Werner Schweer y otros. +Ver http://www.muse-sequencer.org por nuevas versiones +y mas información. {1999-2012 ?} + + + (C) Copyright 1999-2014 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Derechos de autor 1999-2010 Werner Schweer y otros. +Ver http://www.muse-sequencer.org por nuevas versiones +y mas información. {1999-2012 ?} {1999-2014 ?} + + + + Version info (replaced programmatically) - (C) Copyright 1999-2012 Werner Schweer and others. + (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Derechos de autor 1999-2010 Werner Schweer y otros. Ver http://www.muse-sequencer.org por nuevas versiones -y mas información. {1999-2012 ?} +y mas información. {1999-2012 ?} {1999-2014 ?} {1999-2015 ?} - + + System information: + + + + + TextLabel + + + + &Keep On Rocking! Volver a la sesión @@ -165,7 +158,7 @@ AppearanceDialogBase - + Apply Aplicar @@ -180,7 +173,7 @@ Cancelar - + Arranger Arreglador @@ -255,7 +248,7 @@ Imagen de fondo - + show snap grid muestra la rejilla de captura @@ -270,17 +263,16 @@ Objetos - + Style/Fonts Estilo/Tipografía - QT Theme - tema QT + tema QT - + Windows Ventanas @@ -325,77 +317,97 @@ - + + MusE color scheme + + + + + current settings + + + + + Change + + + + Fonts Tipografía - + Family Familia - + Size Tamaño - + Font 1 Tipografía 1 - + + Themes + + + + Font 2 Tipografía 2 - + Font 3 Tipografía 3 - + Font 0 Tipografía 0 - - - - - - - + + + + + + + Bold Negrita - - + - - - + + + + Italic Cursiva - + - - - - - - + + + + + + ... ... - + Color name: Nombre de color @@ -405,27 +417,27 @@ Opacidad global - + Font 4 Tipografía 4 - + Font 5 Tipografía 5 - + Palette Paleta - + MusE: Appearance settings MusE: Configuración de apariencia - + add Agregar @@ -435,7 +447,7 @@ quitar - + add to palette Agregar a la paleta @@ -470,17 +482,17 @@ Rojo - + clear Limpiar - + Style Sheet: Hoja de estilo - + Font 6 Tipografía 6 @@ -514,47 +526,35 @@ Tipo de controlador MIDI - Control7 - Control7 + Control7 - Control14 - Control14 + Control14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - RPN14 - RPN14 + RPN14 - NRPN14 - NRPN14 - - - - Pitch - + NRPN14 - Program - Programa + Programa - + H-Ctrl Control-H @@ -634,6 +634,40 @@ + ChooseSysexBase + + + Dialog + Dialogo + + + + Hex: + + + + + + TextLabel + + + + + Comment: + Comentario: + + + + &OK + + + + + &Cancel + &Cancelar + + + ClipListEditorBase @@ -702,7 +736,7 @@ ConfigMidiFileBase - + &OK &OK @@ -712,7 +746,7 @@ &Cancelar - + 0 (single track) 0 (una sola pista) @@ -722,12 +756,12 @@ 1 (Pistas multiples) - + Format: Formato: - + 96 96 @@ -742,17 +776,17 @@ 384 - + Division: División: - + Copyright: Derecho de autor - + MusE: Config Midi File Import/Export MusE: Configuración de la importacion/exportación MIDI @@ -762,7 +796,7 @@ Importado - + Split tracks into &parts Dividir en pistas y regiones @@ -777,22 +811,37 @@ Dividir en pistas y regiones o una sola región. - + + Default instrument: + + + + + Device Name metas trump Port metas if both exist + + + + + Instrument Name metas trump Mode sysexes if both exist + + + + Use new-style drum tracks - + Use old-style drum tracks - + Export: Exportado: - + Enable extended smf format (currently not implemented) Habilitar formato extendido smf (no implementado) @@ -807,12 +856,43 @@ Alt+2 - + + Mode sysexes + + + + + Instrument name metas + + + + + + Both + + + + + Port metas + + + + + Device name metas + + + + + Export a Port/Device meta for format 0 + + + + Note: Format 0 uses the FIRST midi track's name/comment in the arranger Nota: Formato 0 utiliza el nombre de la primera pista midi como comentario en el arreglista - + Save space by replacing note-offs with &zero velocity note-ons Ahorro de espacio por la sustitución de "note-off" con cero y la velocidad de "note-on" @@ -823,6 +903,33 @@ + CopyOnWriteDialogBase + + + Copy Wave Files + + + + + Some sound files will be copied to the Project Directory, +either because they are not writable or because more +than one independent Wave Event shares them. +(If you prefer instead that the Wave Events are + inter-dependent, try using Clone Parts.) + +Multiple copies will be made in some cases. + +If no Project has been created yet, you will be asked to, +giving another chance to cancel. + + + + + These files will be copied to the Project Directory: + + + + CrescendoBase @@ -896,7 +1003,7 @@ - + @@ -908,8 +1015,8 @@ Error critico - - + + @@ -917,8 +1024,8 @@ No se puede abrir el archivo %1 - - + + @@ -926,8 +1033,8 @@ Error de análisis del archivo %1 - - + + Load category dialog Cargar diálogo de la categoría @@ -1560,7 +1667,7 @@ Release Rate Release Rate - + @@ -1568,7 +1675,7 @@ 2° Decay Rate 2° Decay Rate - + @@ -1576,7 +1683,7 @@ 1° Decay Level 1° Decay Level - + @@ -2647,7 +2754,7 @@ ¿Sabia usted que? - + Don't show on startup No volver a mostrar @@ -2701,11 +2808,11 @@ - Copy standard controllers (vol, pan) + Copy standard (vol, pan) and synth controllers - + Copy effects rack plugins @@ -2728,7 +2835,7 @@ EditCtrlBase - + MusE: Edit Controller Event MusE:-Editar eventos de control @@ -2748,22 +2855,27 @@ Crear nuevo control - + textLabel3 textLabel3 - + Value Valor - + Controller Controlador - + + Note + Nota + + + H-Bank Banco-H @@ -2880,26 +2992,29 @@ Programa: - - - + + + + &Delete &Borrar - - - + + + Alt+D Alt+D - + + + Drum Percusión - + GM GM @@ -2914,89 +3029,87 @@ XG - This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. - Lista de controladores MIDI de uso común + Lista de controladores MIDI de uso común Tenga en cuenta que en Muse cambios de tono y el programa se manejan como los controladores normales. - - + + Properties Propiedades - Control7 - Control7 + Control7 - Control14 - Control14 + Control14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - Pitch - Tono + Tono - Program - Programa + Programa - - + + H-Ctrl Control-H - - + + L-Ctrl Control-L - + Min Minimo - + Max Máximo - + Name Nombre - + + Drum&maps + + + + Type Tipo - + Hex Entry: Hexadecimal: - + &File Archivo @@ -3061,17 +3174,15 @@ Guardar &Como... - Exit - Salir + Salir - E&xit - &Salir + &Salir - + Instrument Name: Nombre de instrumento: @@ -3102,13 +3213,13 @@ - - + + Name: Nombre: - + Group or patch name Nombre de grupo o sección @@ -3151,17 +3262,15 @@ Número de parte de programa - Drum patch - Parte de percusiòn + Parte de percusiòn - If set, the patch is available only for drum channels. - Si se establece, el grupo estará disponible únicamente para los canales de percusión. + Si se establece, el grupo estará disponible únicamente para los canales de percusión. - + GM patch Grupo GM @@ -3191,7 +3300,20 @@ Si se establece, estará disponible el grupo "XG" para la sesión. - + + + Show in tracks: + + + + + + + Midi + MIDI + + + Delete group or patch Borrar grupo @@ -3226,17 +3348,15 @@ Alt+G - Contro&ller - Contro&lador + Contro&lador - Common: - Comunes: + Comunes: - + List of defined controllers Lista de controladores definidos @@ -3246,54 +3366,52 @@ Lista de controladores definidos. - + Min Mínimo - + Max Máximo - + Def Def - + Midi controller name Nombre de controlador MIDI - + Type: Tipo: - + Midi controller type Tipo de controlador MIDI - RPN14 - RPN14 + RPN14 - NRPN14 - NRPN14 + NRPN14 - + Midi controller number high byte Controlar número alto del byte MIDI - + Midi controller number low byte (* means drum controller) Controlador MIDI byte bajo (* significa que controla la percusión) @@ -3320,12 +3438,12 @@ * - + Range: Rango: - + Minimum value. If negative, auto-translate. Valor mínimo. Si es negativo,se traduce automaticamente. @@ -3364,47 +3482,47 @@ True range: Min: -8192 Max: 8191 (bias 0) - + Maximum value Valor máximo - + Default: Por defecto: - + L-Bank Banco-L - - - - - + + + + + off apagado - + Progr. Programa - + ??? ??? - + H-Bank Banco-H - + Default value. Off: No default. El valor por defecto. Apagado: No hay valor por defecto. @@ -3442,28 +3560,27 @@ apagado - + Add common controller Agregar controlador común - - + &Add &Agregar - + Alt+A Alt+A - + Delete controller Borrar controlador - + Create a new controller Crear un nuevo controlador @@ -3478,18 +3595,17 @@ Alt+C - Null Param Hi: - Byte alto no válido: + Byte alto no válido: - + Null parameter number High byte El número de byte alto no es válido. - + If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events @@ -3504,17 +3620,17 @@ número de controlador RPN / NRPN. - + Lo: Inferior: - + Null parameter number Low byte El parametro inferior es nulo - + S&ysEx S&ysEx @@ -3524,7 +3640,7 @@ Lista se Sistema exclusivo (SysEx) - + New SysE&x Nuevo SysE&x @@ -3534,12 +3650,7 @@ Alt+X - - Drummaps - - - - + Patch Collections: @@ -3593,12 +3704,67 @@ - + + Contro&llers + + + + + Null Parameters: Hi: + + + + + &Add Common... + + + + + W + W + + + + Comment: + Comentario: + + + + &Initialization + + + + + Instrument initialization sequence: + + + + + &Add... + + + + + &Change... + + + + &Help A&yuda - + + &Close + + + + + Close + Cerrar + + + new item Nuevo ítem @@ -3667,18 +3833,36 @@ + Name: + Nombre: + + + Comment: Comentario: - - OK - Aceptar + + &Select... + + + + + &OK + + &Cancel + &Cancelar + + + OK + Aceptar + + Cancel - Cancelar + Cancelar @@ -3706,106 +3890,136 @@ FLUIDSynthGuiBase + FLUID Synth - Fluid Synth + Fluid Synth + Load - Cargar + Cargar + Delete - Borrar + Borrar + Dump Info - Volcar información + Volcar información + ID - ID + ID + Fontname - Nombre de fuente + Nombre de fuente + Chnl - Canal + Canal + Soundfont - Fuente de sonido + Fuente de sonido + Drum Chnl - Canal de percusión + Canal de percusión + + Level - Nivel + Nivel + Width - Anchura + Anchura + Damping - Amortiguamiento + Amortiguamiento + Room Size - Tamaño sala + Tamaño sala + Reverb - Reverberación + Reverberación + CHANNEL SETUP - CONFIGURACIÓN DE CANAL + CONFIGURACIÓN DE CANAL + Sine - Seno + Seno + Triangle - Triángulo + Triángulo + Type - Tipo + Tipo + Number - Número + Número + Speed - Velocidad + Velocidad + Depth - Profundidad + Profundidad + Chorus - Coro ;-) + Coro ;-) + Gain - Ganancia + Ganancia + LOADED SOUNDFONTS - SOUNDFONTS CARGADOS + SOUNDFONTS CARGADOS FileDialogButtons - + + Home + + + + Global Global @@ -3820,19 +4034,19 @@ Sesión - + read Midi Port Configuration - + write window states - + fdialogbuttons Dialogo de botones @@ -3840,8 +4054,9 @@ FluidSynthGui + Choose soundfont - Elegir soundfont + Elegir soundfont @@ -3920,7 +4135,7 @@ MusE: Configuración global globales - + Audio Audio @@ -3946,21 +4161,21 @@ MIDI - + Ticks Tics - + - + 1024 1024 - + - + 2048 2048 @@ -3970,75 +4185,75 @@ 4096 - + Displayed Resolution (Ticks/Quarternote) Resolución mostrada (tics/negra) - - + + 48 48 - - + + 96 96 - - + + 192 192 - - + + 384 384 - - + + 768 768 - - + + 1536 1536 - - + + 3072 3072 - - + + 6144 6144 - - + + 12288 12288 - + RTC Resolution (Ticks/Sec) Resolución del reloj (Tics/segundo) - + /sec /seg @@ -4048,17 +4263,27 @@ Velocidad de refresco de la interfáz - + + Warn if timer frequency is inadequate + + + + GUI Behaviour - + + Track height + + + + Use project save dialog Usar diálogo de guardado - + Some popup menus stay open (else hold Ctrl) Menús emergentes (si no mantenga presionado Ctrl) @@ -4087,59 +4312,76 @@ Botón izquierdo del ratón reduce el valor - + Shift + Right click sets left range marker Ctrl + clic derecho establece marcador de la izquierda. - + Allow adding hidden tracks in track list menu - + Unhide tracks when adding hidden tracks - - + + Smart focus - + After editing, controls will return focus to their respective canvas - - Drum tracks + + Show newly created midi velocity graphs per-note - - Only offer old-style drumtracks + + px - - Only offer new-style drumtracks + + Enable borderless mouse. +For certain functions like zoom/pan. +Disable to use an alternate standard + method. + - Prefer old-style drumtracks + Enable borderless mouse. +For certain functions like zoom. +Disable to use an alternate standard + method. - - Prefer new-style drumtracks + + Borderless zoom/pan mouse (else use alternate method) - + + Scrollable submenus + + + + + Drum tracks + + + + GUI Style @@ -4184,27 +4426,25 @@ &Cancelar - + Application Aplicación - + Start Muse Inicio de MusE - start with last song Abrir la última sesión - start with song Abrir la sesión - + Views Vistas @@ -4279,12 +4519,12 @@ Establecer valores actuales - + show splash screen Mostrar pantalla de inicio - + Mixer A Mezcaldora A @@ -4294,7 +4534,7 @@ Mezcladora B - + show "Did you know?" dialog Ver dialogo ¿Sabia usted que? @@ -4304,7 +4544,22 @@ Iniciar Sesión - + + start &with last song + + + + + start with &template + + + + + sta&rt with song + + + + min. Slider Val Valor mínimo del potensiómetro @@ -4355,7 +4610,7 @@ 16 - + Project directory @@ -4365,19 +4620,19 @@ - - + + ... ... - + Main Window Ventana principal - + Choose start song or template @@ -4387,12 +4642,7 @@ - - start with template - - - - + Start template or song: @@ -4408,7 +4658,17 @@ - + + Warn if opening file versions different than current version + + + + + Auto save (every 5 minutes if not playing/recording) + + + + Try to use Jack Freewheel Habilitar Jack para juego libre @@ -4529,7 +4789,47 @@ Períodos más cortos dan una mejor resolución de la reproducción midi. - + + &Record all instruments + + + + + &Don't record hidden instruments + + + + + Don'&t record muted instruments + + + + + Don't record &hidden or muted instruments + + + + + Instrument initialization + + + + + Send instrument initialization sequences + + + + + Warn if instrument initialization sequences pending + + + + + Send instrument controller default values if none in song, at init or rewind + + + + 8192 5 1/3' {8192?} @@ -4544,54 +4844,54 @@ 5 1/3' {32768?} - + Midi Resolution (Ticks/Quarternote) Resolución MIDI (Tics/Negra) - - Record new style drum tracks + + &Only offer old-style drumtracks - - Record all instruments + + Only offer new-style &drumtracks - Don't record hidden instruments + &Prefer old-style drumtracks - Don't record muted instruments + Prefer &new-style drumtracks - - Don't record hidden or muted instruments + + Record new style drum tracks - + Use old-style stop shortcut: Atajo antiguo para detener - + Move single armed track with selection Armar la pista con la selección (Grabar) - + On Launch Lanzar al inicio - + Behavior Comportamiento @@ -4771,22 +5071,67 @@ MusE: Configuración del metrónomo - + Metronome Metrónomo - + Audio Beep Bip de audio - + + Two samples (old samples) + + + + + Four samples (new samples, with accents) + + + + + Volume + Volume + + + + Audio master + + + + + Meas + + + + + Beat + + + + + Accent1 + + + + + Accent2 + + + + + Sample + + + + MIDI Click Clic MIDI - + Midi Channel Canal MIDI @@ -4816,17 +5161,22 @@ Puerto MIDI - + + Disabled since jack does not support it + + + + Precount Cuenta atrás - + enable habilitada - + Bars Compases @@ -4836,12 +5186,12 @@ desde la pista Mestra - + / / - + Signature Compás @@ -4856,7 +5206,7 @@ Pre-escucha - + &Apply &Aplicar @@ -4871,22 +5221,25 @@ &Cancelar - + Choose outputs... Seleccionar salidas... - + + + + + 50 50 - % Audio volume - % De volúmen de audio + % De volúmen de audio - + Hint: Enable metronome in Transportpanel Habilitar metrónomo en el panel de transporte @@ -4929,47 +5282,40 @@ - + + &Learn + + + Control7 - Control7 + Control7 - Control14 - Control14 + Control14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - RPN14 - RPN14 + RPN14 - NRPN14 - NRPN14 + NRPN14 - - Pitch - - - - Program - Programa + Programa - + Hi: @@ -4978,11 +5324,6 @@ Lo: - - - Learn - - MidiFilterConfigBase @@ -5153,7 +5494,7 @@ - + @@ -5161,8 +5502,8 @@ Igual - - + + @@ -5170,64 +5511,70 @@ Diferente - + Note Nota - + Poly Pressure Presión polifóica - - + + Control Change Cambio de control - - + + Aftertouch AfterTouch - - + + Pitch Bend Rueda de modulación - - + + NRPN NRPN - - + + RPN RPN - - + + + Program + Programa + + + + Value 2 Valor 2 - - + + - + Value 1 Valor 1 - + Event Type Tipo de Evento @@ -5274,81 +5621,81 @@ - + Channel Canal - - + + Port Puerto - + Processing Procesando - + - + Keep Mantener - - + + - + Fix Arreglar - + - + Plus Más - + - + Minus Menos - + - + Multiply Multiplicar - + - + Divide Dividir - + - + Invert Invertir - + ScaleMap Mapa de escalado @@ -5360,21 +5707,26 @@ - + Dyn Dinámico - + - + Random Aleatorio - + + Toggle + + + + Modules Módulos @@ -5729,49 +6081,64 @@ MusE: Información de pista - + output channel canal de salida - - + + % % - + output port puerto de salida - - - - + + + + off apagado - + Transp. Transporte - + Channel Info Información de Canal - + Rec: Grabar - + + Change note length in percent of actual length + + + + + Offset playback of notes before or after actual note + + + + + Transpose notes up or down + + + + Bank Select MSB. Ctrl-double-click on/off. @@ -5782,6 +6149,16 @@ + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + + + + + Compress the notes velocity range, in percent of actual velocity + + + + Program. Ctrl-double-click on/off. @@ -5812,47 +6189,52 @@ Panorama - + + Select instrument + + + + Delay Retardo - + H-Bank Banco-superior - + Compr. Compresión - + L-Bank Banco-inferior - + Velocity Velocidad - + Length Duración - + all midi events are sent to this output channel Todos los eventos MIDI se envían a este canal de salida - + Out ch Canal de salida - + input routing Ruteo de entrada @@ -5892,17 +6274,18 @@ Pasar los eventos de entrada a la salida (Thru). - + Select instrument patch Seleccione el instrumento + <unknown> <desconocido> - + Add all settings to song Agregar todos los ajustes a la sesión @@ -5912,7 +6295,7 @@ Todo - + Add bank + prog settings to song Agregar ajustes de banco y programa a la sesión @@ -6395,9 +6778,28 @@ + MusECore::AudioPreviewDialog + + + Auto play + + + + + + Stop + Parar + + + + Play + Reproducir + + + MusECore::Song - + Jack shutdown! Detener Jack @@ -6434,13 +6836,13 @@ haga clic en el botón Reiniciar. - - + + Automation: Automatización - + previous event evento anterior @@ -6451,24 +6853,24 @@ - + set event - - + + add event agregar evento - - + + erase event borrar evento - + erase range borrar rango @@ -6508,7 +6910,7 @@ &Cancelar - + MusE: Tempo list @@ -6519,7 +6921,22 @@ - + + Do you want to process ALL or only selected events? + + + + + &Selected + + + + + &All + + + + MusE - external script failed MusE - fallo de orden externa @@ -6529,6 +6946,16 @@ %1 + + + Und&o + Deshacer + + + + Re&do + &Rehacer + MusEGui @@ -6706,17 +7133,32 @@ Personalizado - + Keep Qt system style - + + Do you really want to reset colors to theme default? + + + + + &Ok + + + + + &Cancel + &Cancelar + + + MusE: load image MusE: carga imagen - + Select style sheet Seleccionar hoja de estilo @@ -6729,7 +7171,7 @@ MusEGui::Arranger - + Enable Recording Habilita captura @@ -6857,7 +7299,7 @@ Cursor - + Off Apagado @@ -6867,7 +7309,7 @@ Compás - + Snap Snap @@ -6883,38 +7325,31 @@ Duración de compás - Type - Tipo + Tipo - NO - No + No - GM - GM + GM - GS - GS + GS - XG - XG + XG - - midi song type - tipo de canción midi + tipo de canción midi - + Pitch Tono @@ -7001,14 +7436,67 @@ + MusEGui::ArrangerColumns + + + Control7 + Control7 + + + + Control14 + Control14 + + + + RPN + RPN + + + + NPRN + + + + + RPN14 + RPN14 + + + + NRPN14 + NRPN14 + + + + Pitch + + + + + Program + Programa + + + + Aftertouch + AfterTouch + + + MusEGui::ArrangerView - + MusE: Arranger Ordenar - + + D&elete + + + + C&ut C&ortar @@ -7028,9 +7516,8 @@ &Pegar - Paste (show dialog) - Pegar (ver dialogo) + Pegar (ver dialogo) @@ -7038,12 +7525,11 @@ Pegar c&lon - Paste clone (show dialog) - Pergar clon (ver dialogo) + Pergar clon (ver dialogo) - + &Insert Empty Measure &Insertar compás vacío @@ -7072,7 +7558,22 @@ Limpiar regiones seleccionadas + + Paste to selected &track + + + + + Paste clone to selected trac&k + + + + Paste (show dialo&g) + + + + Purge hidden events from selected parts @@ -7208,7 +7709,7 @@ &Editar - + &Structure E&structura @@ -7278,7 +7779,7 @@ - + Remove track(s) @@ -7288,13 +7789,13 @@ - + New Nuevo - + Changed Settings @@ -7373,22 +7874,26 @@ MusEGui::AudioStrip - + panorama panorama - + aux send level volumen de envio auxiliar - Pan - Paneo + Paneo - + + calibration gain + + + + 1/2 channel Canales 1/2 @@ -7403,7 +7908,7 @@ pre fader - port fader - + dB dB @@ -7472,7 +7977,7 @@ MusEGui::BigTime - + format display Formato de vista @@ -7514,12 +8019,20 @@ sub-frame - + MusE: Bigtime MusE: Big Time + MusEGui::Canvas + + + Tools: + + + + MusEGui::ClipListEdit @@ -7545,7 +8058,7 @@ MusEGui::CtrlCanvas - + Make the current part's track match the selected drumlist entry @@ -7564,7 +8077,7 @@ MusEGui::CtrlPanel - + S S @@ -7599,48 +8112,43 @@ apagado - - + + all/per-note velocity mode + + + Velocity - Velocidad + Velocidad - add new ... - Agregar nuevo... + Agregar nuevo... - - Instrument-defined - Definición de instrumento + Definición de instrumento - - Add ... - Agregar... + Agregar... - Others - Otros + Otros - Edit instrument ... - Editar instrumento... + Editar instrumento... - Common Controls - Controles comunes + Controles comunes MusEGui::DList - + hide this instrument @@ -7658,7 +8166,7 @@ MusEGui::DrumCanvas - + Moving items failed @@ -7669,7 +8177,7 @@ - + Creating event failed @@ -7680,14 +8188,14 @@ - - + + Recording event failed - - + + Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. @@ -7706,7 +8214,7 @@ MusEGui::DrumEdit - + mute instrument silenciar instrumento @@ -7748,55 +8256,43 @@ Esta es la nota que se toca - output channel (hold ctl to affect all rows) - canal de salida (mantener presionado Ctrl a afectar a todas las pistas) + canal de salida (mantener presionado Ctrl a afectar a todas las pistas) - output port (hold ctl to affect all rows) - Puerto de salida (Mantener presionado CTRL a afecta todas las pistas) + Puerto de salida (Mantener presionado CTRL a afecta todas las pistas) - - shift + control key: draw velocity level 1 - Shift + CTRL: Dibuja la velocidad 1 + Shift + CTRL: Dibuja la velocidad 1 - - control key: draw velocity level 2 - CTRL: Dibuja la velocidad 2 + CTRL: Dibuja la velocidad 2 - - shift key: draw velocity level 3 - Shift: Dibuja la velocidad 3 + Shift: Dibuja la velocidad 3 - - draw velocity level 4 - Dibuja la velocidad 4 + Dibuja la velocidad 4 - output channel (ctl: affect all rows) - Canal de salida. (CTRL afecta todas las pistas) + Canal de salida. (CTRL afecta todas las pistas) - output port (ctl: affect all rows) - Puerto de salida (CTRL afecta todas las pistas) + Puerto de salida (CTRL afecta todas las pistas) &File Archivo - + Load Map Cargar Mapa @@ -7950,19 +8446,63 @@ Cargar mapa de percusión - + hide instrument - - Re-order map + + override track output channel (hold ctl to affect all rows) - - Group + + override track output port (hold ctl to affect all rows) + + + + + + control + meta keys: draw velocity level 1 + + + + + + meta key: draw velocity level 2 + + + + + + draw default velocity level 3 + + + + + + meta + alt keys: draw velocity level 4 + + + + + override track output channel (ctl: affect all rows) + + + + + override track output port (ctl: affect all rows) + + + + + Re-order map + + + + + Group @@ -8031,7 +8571,12 @@ Entrada MIDI - + + Play Events + Reproducir eventos + + + cursor tools Herramientas de cursor @@ -8126,7 +8671,7 @@ VL4 - + Muse: Load Drum Map MusE: Cargar mapa de percusión @@ -8146,7 +8691,7 @@ ¿Resetear en mapa de percusión con el banco GM? - + Not all parts are displayed @@ -8160,25 +8705,22 @@ MusEGui::EditCAfterDialog - MusE: Enter Channel Aftertouch - MusE: Introduzca el AfterTouch del canal + MusE: Introduzca el AfterTouch del canal - Time Position - Posición de tiempo + Posición de tiempo - Pressure - Presión + Presión MusEGui::EditEventDialog - + Ok Aceptar @@ -8191,12 +8733,63 @@ MusEGui::EditInstrument - + + Control7 + Control7 + + + + Control14 + Control14 + + + + RPN + RPN + + + + NPRN + + + + + RPN14 + RPN14 + + + + NRPN14 + NRPN14 + + + + Pitch + + + + + Program + Programa + + + + PolyAftertouch + + + + + Aftertouch + AfterTouch + + + + Name Nombre - + Vol @@ -8212,11 +8805,12 @@ + Len - + A-Note Nota-A @@ -8241,8 +8835,18 @@ VL4 - - + + Tick + Tic + + + + Data + Datos + + + + MusE: Create file failed MusE: Fallo la creacion del archivo @@ -8252,19 +8856,19 @@ MusE: La captura del archivo ha fallado - - + + MusE: Save Instrument Definition MusE: Guardar definicion de instrumento - - + + Instrument Definition (*.idf) Definicion de instrumento (*.idf) - + MusE: Save instrument as MusE: Guardar instrumento como @@ -8281,7 +8885,7 @@ - + MusE: Bad instrument name MusE: Nombre de instrumento invalido @@ -8313,7 +8917,7 @@ Seleccione un nombre único al grupo - + MusE: Bad controller name MusE: Nombre del controlador no válido. @@ -8323,23 +8927,30 @@ El nombre del controlador ya existe - - + + New controller: Error + + + + + Error! All control numbers are taken up! +Clean up the instrument! + + + MusE: Cannot add common controller - MusE: No se puede agregar un controlador comun. + MusE: No se puede agregar un controlador comun. - A controller named '%1' already exists. - El nombre de controlador '%1' ya existe. + El nombre de controlador '%1' ya existe. - A controller number %1 already exists. - El número de controlador %1 ya existe. + El número de controlador %1 ya existe. - + MusE MusE @@ -8373,7 +8984,7 @@ MusEGui::EditMetaDialog - + MusE: Enter Meta Event MusE: Introduzca evento Meta @@ -8396,30 +9007,26 @@ MusEGui::EditPAfterDialog - MusE: Enter Poly Aftertouch - Muse: Escribe el aftertouch polifónico + Muse: Escribe el aftertouch polifónico - Time Position - Posición de tiempo + Posición de tiempo - Pitch - Tono + Tono - Pressure - Presión + Presión MusEGui::EditToolBar - + select Pointer Tool: with the pointer tool you can: select parts @@ -8495,12 +9102,11 @@ Manipular automatización - Cursor tool - Herramientas de cursor + Herramientas de cursor - + pointer puntero @@ -8520,17 +9126,56 @@ cortador - + score partitura - + glue Pegador + + select Cursor (tracker mode) tool: +with the cursor tool you can: + navigate with arrow keys + use VBNM to place notes + change step with 0 and 9 + + + + + select Range Tool + + + + + select Panning Tool + + + + + select Zoom Tool + + + + + range + + + + + pan + + + + zoom + + + + quantize cuantizador @@ -8555,7 +9200,7 @@ Cursor - + Edit Tools Editar herramientas @@ -8563,7 +9208,7 @@ MusEGui::EffectRack - + effect rack rack de efectos @@ -8613,12 +9258,17 @@ Guardar preseteo - + + Presets + Plantillas + + + MusE: Save Preset MusE: Guardar preseteo - + Replace effect Reemplazar efecto @@ -8631,7 +9281,7 @@ MusEGui::GlobalSettingsConfig - + MusE: Choose start template or song @@ -8770,7 +9420,7 @@ MusEGui::ListEdit - + insert Note insertar Nota @@ -8790,17 +9440,15 @@ insertar Meta - insert Channel Aftertouch - insertar AfterTouch de canal + insertar AfterTouch de canal - insert Poly Aftertouch - insertar AfterTouch Polifónico + insertar AfterTouch Polifónico - + &Edit &Editar @@ -8898,33 +9546,29 @@ MusEGui::MPConfig - - + Default input connections Conexión de entrada predeterminada - - + + Are you sure you want to apply to all existing midi tracks now? ¿Seguro que desea aplicar a todas las pistas MIDI existentes? - Default output connections Conexión de salida predeterminada - - Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? - El sjuste se aplicará a las nuevas pistas MIDI. + El sjuste se aplicará a las nuevas pistas MIDI. ¿Usted desea aplicar a todas las pistas MIDI existentes? - + MusE: bad device name MusE: Nombre del dispositivo incorrecto @@ -8934,67 +9578,73 @@ El nombre del dispositivo ya existe - - + + in Entrada - - + + out Salida - Show first aliases Mostrar primero los álias - Show second aliases Mostrsr después los álias - - + + Toggle all Cambiar todos - - + + Change all tracks now Cambiar todas las pistas ahora - + + Remove + Eliminar + + + Create Jack device Crear conexión Jack - - + + Port Number Número de puerto - + + Enable gui Habilitar visualización - + + Enable reading Habilitar lectura - + + Enable writing Habilitar escritura - + Port instrument Puerto de instrumento @@ -9004,7 +9654,7 @@ Nombre del dispositivo MIDI, Clic para editar (Jack) - + Connections from Jack Midi outputs Conexiones de salidas MIDI de Jack @@ -9014,42 +9664,45 @@ Conexiones de entrada MIDI de Jack - + Auto-connect these channels to new midi tracks Conectar automaticamente a estos canales las nuevas pistas midi - + Auto-connect new midi tracks to these channels Conectar nuevas pistas MIDI a estos canales - + Auto-connect new midi tracks to this channel Autoconectar las nuevas pistas MIDI a este canal - + + Device state Estado del dispositivo - + Enable gui for device Habilitar visualización para el dispositivo + Enable reading from device Habilitar la lectura desde el dispositivo - + + Enable writing to device Habilitar la escritura en el dispositivo - + Name of the midi device associated with this port number. Click to edit Jack midi name. Nombre del dispositivo MIDI asociado con este puerto. Haga clic para cambiar el nombre del dispositivo MIDI de Jack. @@ -9059,7 +9712,7 @@ Instrumento conectado al puerto - + Connections from Jack Midi output ports Conexiones de los puertos de salida MIDI de Jack @@ -9069,67 +9722,123 @@ Conexiones de los puertos de entrada MIDI de Jack - + Auto-connect these channels, on this port, to new midi tracks. Conexión automática de estos canales, en este puerto, a las nuevas pistas MIDI. - + Connect new midi tracks to these channels, on this port. Conecte las nuevas pistas MIDI a estos canales, en este puerto. - + Connect new midi tracks to this channel, on this port. Conecte las nuevas pistas MIDI a este canal, en este puerto. - + State: result of opening the device Estado: resultado de abrir el puerto - + Port Puerto - + + GUI GUI - + + I - + + O - + Instrument Instrumento - + + Device Name Nombre de dispositivo - - In routes - Rutas de entrada + + + Midi device name + - - Out routes - Rutas de salida + + + Midi device type + + + + + Connections from Jack Midi + + + + + Connections to Jack Midi + + + + + Result of opening the device: +OK: Assigned to a port and in use +Closed: Unassigned to a port, or closed +R/W Error: Unable to open for read or write +Unavailable: USB midi unplugged, or external + application not running, or synth plugin + not installed etc. +(Jack Midi devices have 'unavailable ports' + in the routes columns.) +Unavailable devices or ports can be purged + with 'Remove' or with the advanced router. + + + + + Enable Graphical User Interface for device + + + + + Connections from Jack Midi ports + + + + + Connections to Jack Midi ports + + + + + In routes + Rutas de entrada + Out routes + Rutas de salida + + + Def in ch Definir canales de entrada @@ -9139,18 +9848,33 @@ Definir canales de salida - + + State Estado - + + Type + Tipo + + + + In + + + + + Out + + + + <unknown> <desconocido> - - + <none> <ningúno> @@ -9158,7 +9882,7 @@ MusEGui::MTScale - + bar scale escala de compases @@ -9166,7 +9890,7 @@ MusEGui::MTScaleFlo - + bar scale escala de compases @@ -9308,9 +10032,57 @@ + MusEGui::MidiAudioControl + + + Control7 + Control7 + + + + Control14 + Control14 + + + + RPN + RPN + + + + NPRN + + + + + RPN14 + RPN14 + + + + NRPN14 + NRPN14 + + + + Pitch + + + + + Program + Programa + + + + Aftertouch + AfterTouch + + + MusEGui::MidiInputTransformDialog - + New Nuevo @@ -9784,8 +10556,10 @@ MusEGui::MidiTrackInfo - - + + + + <unknown> <desconocido> @@ -9793,7 +10567,7 @@ MusEGui::MidiTransformerDialog - + New Nuevo @@ -9810,7 +10584,7 @@ MusEGui::MusE - + Failed to start audio! Fallo al iniciar el audio @@ -9919,7 +10693,7 @@ para el secuenciador - + Play Reproducir @@ -9929,7 +10703,7 @@ inicia el secuenciador - + Record Grabar @@ -9939,18 +10713,23 @@ para grabar pulsa grabar y después reproducir - - + + Panic Pánico - + send note off to all midi channels envia un apagado de nota (note off) a todos los canales midi - + + turn on/off metronome + + + + &New &Nueva sesión @@ -9978,13 +10757,13 @@ - - + + &Save &Guardar - + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. @@ -10012,13 +10791,13 @@ Importar región. - - - Import Wave File + + + Import Audio File Importar archivo de audio - + Find unused wave files Buscar archivos de audio sin uso @@ -10208,12 +10987,19 @@ Seguir contínuamente - + + MusE: Song: + + + + + + Metronome Metrónomo - + Midi Sync Sincronización MIDI @@ -10283,7 +11069,22 @@ Transporte - + + Cpu load + + + + + Measured CPU load + + + + + No CPU load data + + + + &File Sesión @@ -10328,12 +11129,12 @@ - + Cannot read template No se puede leer la plantilla - + File open error Error al abrir el archivo @@ -10348,29 +11149,27 @@ Formato de archivo desconocido: %1 - - - + MusE: Song: %1 Sesión de MusE: %1 - + MusE: load project MusE: Carga sesión - + MusE: load template MusE: cargar plantilla - + MusE: Write File failed MusE: La captura del archivo ha fallado - + The current Project contains unsaved data Save Current Project? La sesión actual contiene datos sin guardar @@ -10378,28 +11177,28 @@ - + S&kip Ignorar - + &Cancel &Cancelar - + MusE: Save As MusE: Guardar como - + Nothing to edit Nada para editar - + @@ -10459,7 +11258,7 @@ Fije marcadores derecho e izquierdo para el rango de rebote. - + The current Project contains unsaved data Load overwrites current Project: Save Current Project? @@ -10468,13 +11267,13 @@ Guardar el proyecto actual? - - + + &Abort &Abortar - + This will clear all automation data on all audio tracks! Proceed? @@ -10489,12 +11288,26 @@ - + + MusE: Warning + + + + + The song uses multiple ports but export format 0 (single track) is set. +The first track's port will be used. Playback will likely be wrong + unless the channels used in one port are different from all other ports. +Canceling and setting a different export format would be better. +Continue? + + + + MusE: Export Midi MusE: Exportar MIDI - + no help found at: no se ha encontrado ayuda en: @@ -10504,7 +11317,7 @@ MusE: Abre ayuda - + Unable to launch help No es posible enlazar la ayuda. @@ -10551,7 +11364,7 @@ falló: - + Import part is only valid for midi and wave tracks! Importar regiones es solo para pistas MIDI o de audio @@ -10566,7 +11379,7 @@ No hay pistas seleccionadas para la importaciòn - + %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. @@ -10586,12 +11399,12 @@ - + to import an audio file you have first to selecta wave track Para importar un archivo de audio seleccione la pista de audio - + Import Wavefile Importar archivo de audio @@ -10599,13 +11412,65 @@ This wave file has a samplerate of %1, as opposed to current setting %2. +File will be resampled from %1 to %2 Hz. +Do you still want to import it? + + + + + + + Wave import error + + + + + There are too many wave files +of the same base name as imported wave file +Can not continue. + + + + + Can't create new wav file in project folder! + + + + + + Failed to initialize sample rate converter! + + + + + Cancel + Cancelar + + + + Resampling wave file +"%1" +from %2 to %3 Hz... + + + + + Output has clipped +Resampling again and normalizing wave file +"%1" +Try %2 of %3... + + + + This wave file has a samplerate of %1, +as opposed to current setting %2. Do you still want to import it? Este archivo tiene una frecuencia de muestreo de %1, El valor actual de la sesión es %2. ¿Desea importarlo de todos modos? - + &Yes Aceptar @@ -10614,6 +11479,23 @@ &No Ca&ncelar + + + File version is %1.%2 +Current version is %3.%4 +Conversions may be applied if file is saved! + + + + + Opening file + + + + + Do not warn again + + MusEGui::NoteInfo @@ -10656,12 +11538,17 @@ MusEGui::PartCanvas - + Cannot copy/move/clone to different Track-Type No se puede copiar/mover/clonar a un tipo diferente de pista - + + Part: + + + + C&ut C&ortar @@ -10733,24 +11620,34 @@ Información de archivo - + + Normalize + + + + MusE: save part MusE: Guardar región. - + Part name: %1 Files: Nombre de región: %1 Archivos: - + + Automation: + Automatización + + + Remove selected - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -10849,7 +11746,7 @@ MusEGui::PianoRoll - + &Edit &Editar @@ -11029,7 +11926,7 @@ Reproducir eventos - + ctrl control @@ -11042,11 +11939,18 @@ MusEGui::PluginDialog - + MusE: select plugin MusE: Selecciona el plugin + + Plugin categories. +Right-click on tabs to manage. +Right-click on plugins to add/remove from a category. + + + Type Tipo @@ -11137,39 +12041,43 @@ - - Ok - Aceptar + + &create new group + - - Cancel - Cancelar + + &delete currently selected group + - - Show plugs: + + re&name currently selected group - + Ok + Aceptar + + + Cancel + Cancelar + + Mono and Stereo - Mono y estereo + Mono y estereo - Stereo - Estéreo + Estéreo - Mono - Mono + Mono - Show All - Ver todo + Ver todo @@ -11177,7 +12085,34 @@ - + + Associated categories + + + + + You need to define some categories first. + + + + + + new group + + + + + + Enter the new group name + + + + + Wine VST + + + + dssi synth @@ -11187,20 +12122,29 @@ - + + LV2 synth + + + + + LV2 effect + + + + ladspa - Search in 'Label' and 'Name': - Buscar en nombre y etiqueta + Buscar en nombre y etiqueta MusEGui::PluginGui - + File Buttons Botón de archivos @@ -11221,7 +12165,7 @@ saltar plugin - + MusE: load preset MusE: Cargar plantilla @@ -11237,6 +12181,14 @@ + MusEGui::PopupMenu + + + <More...> %1 + + + + MusEGui::ProjectCreateImpl @@ -11245,67 +12197,216 @@ + MusEGui::RouteDialog + + + Normal + Normal + + + + Alias 1 + + + + + Alias 2 + + + + + + Source + Fuente + + + + + Destination + Destino + + + + <none> + + + + MusEGui::RoutePopupMenu - - - - - - - - + Channel Canal - - - + + + + Soloing chain Cadena de solos - - + + + Audio returns Retornos de audio - + Warning: No input devices! Atención: no hay entradas + + Channel grouping: + + + + + Mono + + + + + Stereo + Estéreo + + + + + + + + + + + + Channels + + + + + + Midi ports/devices + + + + + + + Omni + + + + + Show aliases: + + + + + First + + + + + Second + + + + + Show names + + + + Show first aliases + Mostrar primero los álias + + + + Show second aliases + Mostrsr después los álias + + + + + Jack ports + + + + + + Connect + Conectar + + + + Unavailable + + + + + Open advanced router... + + + + + Output routes: + + + + + Input routes: + + + + Open midi config... Abrir la configuraciòn MIDI - - - - + <none> <ningúno> - + + Midi sends + + + + + Sources: + + + Toggle all Cambiar todos - + More... Mas... - + + + Tracks + + + + + Destinations: + + + + Audio sends Envios de audio - Midi port sends Puertos de envio MIDI @@ -11313,7 +12414,7 @@ MusEGui::ScoreCanvas - + Treble Agudos @@ -11333,7 +12434,7 @@ Remover partitura - + Ambiguous part Parte ambígua @@ -11356,7 +12457,7 @@ MusEGui::ScoreEdit - + Step recording tools Herramienta de grabación por pasos @@ -11605,7 +12706,7 @@ MusEGui::ScrollScale - + next page página siguiente @@ -11623,7 +12724,7 @@ MusEGui::ShortcutCaptureDialog - + Ok Aceptar @@ -11644,6 +12745,46 @@ + MusEGui::ShortcutConfig + + + Save printable text file + + + + + Text files (*.txt);;All files (*) + + + + + + Error + Error + + + + Error opening file for saving + + + + + Shortcuts for selected category: + + + + + Legend: + + + + + + An error occurred while saving + + + + MusEGui::SigScale @@ -11667,7 +12808,7 @@ MusEGui::Strip - + Remove track? @@ -11675,12 +12816,12 @@ MusEGui::TList - + <none> <ningúno> - + visible @@ -11706,12 +12847,12 @@ - + off - + <unknown> <desconocido> @@ -11726,47 +12867,66 @@ Error: El nombre de pista ya existe - + Unused Devices - - Update drummap? - ¿Actualizár mapa de percusión? + ¿Actualizár mapa de percusión? - Do you want to use same port for all instruments in the drummap? - ¿Desea utilizar el mismo puerto para todos los instrumentos en el mapa de percusión? + ¿Desea utilizar el mismo puerto para todos los instrumentos en el mapa de percusión? - - &Yes - Aceptar + Aceptar - - &No - Cancelar + Cancelar - - + + show gui Ver interfáz - - + + show native gui Ver interfáz nativa - + + + Presets + Plantillas + + + + Clear all controller events? + Limpiar todos los eventos de control + + + + &Ok + + + + + &Cancel + &Cancelar + + + + Change color + + + + Midi control @@ -11781,7 +12941,17 @@ - + + Other + Otros + + + + clear automation + limpiar automatizacion + + + Treble clef Partitura de agudos @@ -11811,7 +12981,7 @@ - + Delete Track Borrar pista @@ -11861,7 +13031,7 @@ Insertar pista - + Drum map Mapa de percusiòn @@ -11877,17 +13047,29 @@ - + Muse: Load Track's Drum Map - + + Drummap + + + + + This drummap was created with a previous version of MusE, +it is being read but the format has changed slightly so some +adjustments may be necessary. + + + + MusE: Store Track's Drum Map - + Midi MIDI @@ -11902,9 +13084,8 @@ - Do you want to use same port and channel for all instruments in the drummap? - ¿Desea utilizar el mismo puerto y canal para todos los instrumentos del mapa de percusión? + ¿Desea utilizar el mismo puerto y canal para todos los instrumentos del mapa de percusión? @@ -11938,17 +13119,17 @@ Apagado - + Solo Solo - + Cursor Cursor - + Snap Chasquido @@ -11956,7 +13137,7 @@ MusEGui::TopWin - + As subwindow Como sub ventana @@ -11982,11 +13163,16 @@ + Metronome + Metrónomo + + + Transport Transporte - + Song Position @@ -12001,7 +13187,7 @@ Compás - + Piano roll Editor de matríz @@ -12307,9 +13493,74 @@ + MusEGui::WaveCanvas + + + Adjust Wave Offset + + + + + Wave offset (frames) + + + + + Part creation failed + + + + + Left and right position markers must be placed inside the current part. + + + + + Part created + + + + + The selected region has been copied to the clipboard and can be pasted in the arranger. + + + + + MusE - external editor failed + MusE: Falla del editor de audio externo + + + + MusE was unable to launch the external editor +check if the editor setting in: +Global Settings->Audio:External Waveditor +is set to a valid editor. + Muse no pudo lanzar el editor externo +Compruebe esta configuraciòn en: +Configuración global-> Audio: Editor de audio externo +que el comando del editor sea válido. + + + + MusE - file size changed + MusE: Cambió el tamaño del archivo + + + + When editing in external editor - you should not change the filesize +since it must fit the selected region. + +Missing data is muted + Al editar en el editor externo no se debe cambiar el tamaño del archivo +ya que debe ajustarse a la región seleccionada. + +Los datos faltantes se silenciarán. + + + MusEGui::WaveEdit - + &Edit &Editar @@ -12335,6 +13586,11 @@ + &Create Part from Region + + + + C&ut Cortar @@ -12349,7 +13605,7 @@ Abrir con editor externo - + Mute Selection Silenciar selección @@ -12374,27 +13630,52 @@ Invertir selección - - Select - Seleccionar + + Select + Seleccionar + + + + Select &All + Seleccion&ar todo + + + + &Deselect All + &Deseleccionar todo + + + + &Previous Part + Región previa + + + + &Next Part + Siguiente región + + + + Window &Config + &Comportamiento de ventana - Select &All - Seleccion&ar todo + &Event Color + Color de eventos - - &Deselect All - &Deseleccionar todo + + &Part colors + - - Window &Config - &Comportamiento de ventana + + &Gray + - + WaveEdit tools Herramientas de edición de audio @@ -12412,33 +13693,29 @@ MusEGui::WaveView - MusE - external editor failed - MusE: Falla del editor de audio externo + MusE: Falla del editor de audio externo - MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. - Muse no pudo lanzar el editor externo + Muse no pudo lanzar el editor externo Compruebe esta configuraciòn en: Configuración global-> Audio: Editor de audio externo que el comando del editor sea válido. - MusE - file size changed - MusE: Cambió el tamaño del archivo + MusE: Cambió el tamaño del archivo - When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted - Al editar en el editor externo no se debe cambiar el tamaño del archivo + Al editar en el editor externo no se debe cambiar el tamaño del archivo ya que debe ajustarse a la región seleccionada. Los datos faltantes se silenciarán. @@ -12712,6 +13989,59 @@ + PluginDialogBase + + + Dialog + Dialogo + + + + Ports: + + + + + Mono + Mono + + + + Mono + Stereo + + + + + Stereo + Estéreo + + + + All + Todo + + + + Plugin type: + + + + + Filter: + + + + + &OK + + + + + &Cancel + &Cancelar + + + ProjectCreate @@ -12725,13 +14055,13 @@ - + ... ... - + Project Name: Nombre del la sesión: @@ -12757,11 +14087,15 @@ + &Create project folder (recommended for audio projects) + + + Create project folder (recommended for audio projects) - Crear carpeta para la sesión (recomendado para sesiones de audio) + Crear carpeta para la sesión (recomendado para sesiones de audio) - + Song information: Información de la sesión @@ -12780,23 +14114,127 @@ Please first select the range for crescendo with the loop markers. Por favor, seleccione el rango de crescendo con los marcadores de bucle. + + + Controller ! + + + + + Other ! + + + + + Select gui type + + + + + Preset actions + + + + + Save preset... + + + + + Update list + + + + + Saved presets + + + + + No presets found + + + + + Enter new preset name + + + + + Midi programs + + + + + Presets + Plantillas + + + + Switch on General Midi Level 1 mode + + + + + Switch on General Midi Level 2 mode + + + + + Switch off General Midi Level 1 or 2 + + + + + Switch on Roland GS mode + + + + + Switch on Yamaha XG mode + + + + + Tracks: + + + + + Midi ports: + + + + + Midi devices: + + + + + Jack: + + + + + Jack midi: + + QWidget - - + + Cannot convert sysex string No se puede convertir el paquete de sistema exclusivo - - + Hex String too long (2048 bytes limit) Cadena hexadecimal demasiado larga (límite de 2048 bytes) - + generic midi MIDI genérico @@ -12806,7 +14244,7 @@ Nueva sesión - + create peakfile for Crear archivo de picos @@ -12837,7 +14275,7 @@ falló la creación de carpeta - + File %1 exists. Overwrite? @@ -12865,16 +14303,52 @@ MusE: Abrir archivo - - + + None Ningúno - + + No wave events selected. + + + + No selection. Ignoring No hay nada seleccionado! + + + Instrument-defined + Definición de instrumento + + + + + Edit instrument ... + Editar instrumento... + + + + Add + + + + + Others + Otros + + + + Common Controls + Controles comunes + + + + Velocity + + QuantBase @@ -13064,93 +14538,180 @@ Velocidad - - Length - Duración + + Length + Duración + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> + + + + + OK + Aceptar + + + + Cancel + Cancelar + + + + RhythmGenerator + + + + + + + RouteDialogBase + + + MusE: Routing + Muse: Ruteado + + + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. + + + + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + + Itemized list of current connections. + + + + + + Show only selected sources + + + + + + Show only destination routes for selected source + + + + + + Show only selected destinations + + + + + + Show only source routes for selected destination + + + + + + Show all Midi Ports + - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> - + + Auto adjust column size + - - OK - Aceptar + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + - - Cancel - Cancelar + + + Preferred route name or alias + - - - RhythmGenerator - - + + + Connect source to destination + - - - RouteDialogBase - - MusE: Routing - Muse: Ruteado + + + Remove selected route + - Add Route Agregar ruta - Source: Fuente: - Destination: Destino: - + Connect Conectar - connect source to destination Conectar fuente a destino - Current Routes Rutas actuales - Source Fuente - Destination Destino - + Remove Eliminar - remove selected route Eliminar ruta seleccionada @@ -13211,7 +14772,7 @@ SS_PluginFront - + Clear and unload effect Limpiar y descargar efecto @@ -13380,6 +14941,26 @@ Alt+A Alt+A + + + &Printable file... + + + + + Alt+P + Alt+P + + + + &Ok + + + + + Alt+O + Alt+O + SimpleDrumsGuiBase @@ -13392,7 +14973,22 @@ SimpleSynthGui - + + Mix + Mezcla + + + + Chn + + + + + Channel routing + + + + &Load setup Cargar configuración @@ -13402,7 +14998,7 @@ Guardar configuración - + Load sample dialog Cargar diálogo de muestra @@ -13443,23 +15039,20 @@ SynthConfigBase - Soft Synthesizer Sintetizador Virtual - + File Archivo - Instances Copias - - + Name Nombre @@ -13469,38 +15062,38 @@ lista de sintetizadores de software dispoibles - Add Instance Agregar copia - Remove Instance Quitar copia - Midi Port Puerto MIDI - - Midi Port and Soft Synth Configuration - - - - - + Type Tipo - Midi connections Conexiones de MIDI - + + Configure midi devices, midi ports, and synthesizers + + + + + Available soft synthesizers + + + + Inst Instrumento @@ -13514,6 +15107,56 @@ Description Descripción + + + Useable devices + + + + + Add: + + + + + Synth + + + + + ALSA + + + + + JACK + + + + + Rename + + + + + Remove + Eliminar + + + + Device port assignments + + + + + &Apply + &Aplicar + + + + &OK + + TransposeBase @@ -13607,7 +15250,7 @@ VAMGui - + MusE: Load VAM Presets MusE: Cargar preseteos de VAM @@ -13803,7 +15446,7 @@ On Encendido - + VAM 1.0beta3 Virtual Analog for MusE @@ -13896,7 +15539,7 @@ file_patterns - + Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) @@ -14054,12 +15697,12 @@ - Wave/Binary (*.wav *.ogg *.bin) + Wave/Binary (*.wav *.ogg *.flac *.bin) - Wave (*.wav *.ogg) + Wave (*.wav *.ogg *.flac) @@ -14358,9 +16001,35 @@ + midiWarnInitPendingBase + + + Instrument initialization + + + + + MusE should now send some Instrument Initialization Sequences. +The sequences (usually System Exclusive messages) are defined + by the selected instruments in the Settings -> Midi Ports dialog, + such as the GM (default), GS, or XG instruments. + +Typically you should answer yes here. +You can always do it manually from the Midi menu. + +Continue? + + + + + Don't ask me again + + + + shortcuts - + Transport: Start playback from current location Transporte: Iniciar reproducción desde la ubicación actual @@ -14386,6 +16055,11 @@ + Transport: Restart recording + + + + Transport: Goto left marker Transporte: Ir a la marca izquierda @@ -14415,7 +16089,7 @@ Pantalla completa - + Edit: Copy Editar:Copiar @@ -14446,6 +16120,21 @@ + Edit: Paste to selected track + + + + + Edit: Paste clone + + + + + Edit: Paste clone to selected track + + + + Edit: Paste (with dialog) Editar: Pegar (con diálogo) @@ -14655,12 +16344,12 @@ Configuraciones: Configuración global - + Settings: Configure shortcuts Configuraciones: Atajos de teclado - + Settings: Configure metronome Configuraciones: Metrónomo @@ -14805,14 +16494,17 @@ Editar: Insertar compás vacío - Edit: Paste as clones - Editar: Pegar como clon + Editar: Pegar como clon - Edit: Paste as clones (with dialog) - Editar: Pegar como clon (con diálogo) + Editar: Pegar como clon (con diálogo) + + + + Edit: Duplicate track + @@ -14825,7 +16517,12 @@ Seleccionar la pista siguiente - + + Edit selected track name + + + + Midi: Transpose MIDI: Transportar @@ -14931,6 +16628,31 @@ Ver: Mover a la izquierda + + Transport: Step record + + + + + Transport: Midi input + + + + + Transport: Play events + + + + + Edit: Increase velocity + + + + + Edit: Decrease velocity + + + Edit: Set Fixed Length on Midi Events Editar Igualar la duración de las notas MIDI @@ -15006,6 +16728,11 @@ Editar: Cambiar el color de evento + + Move: Move to selected note + + + Tool: Pointer Herramientas: Apuntador @@ -15021,6 +16748,16 @@ Herramientas: Borrador + + Tool: Pan + + + + + Tool: Zoom + + + Tool: Line Draw Herramientas: Linea de dibujo @@ -15072,6 +16809,11 @@ + Tool: Range + + + + Tool: Scissor Herramientas: Tijeta @@ -15226,7 +16968,7 @@ Insertar clave - + Goto Next Marker Ir a la siguiente marca @@ -15235,5 +16977,28 @@ Goto Prev Marker Ir a la marca anterior + + + Normalize + + + + + warnBadTimingBase + + + Bad timing + + + + + Message here + + + + + Don't warn me again + + diff -Nru muse-2.1.2/share/locale/muse_fr.ts muse-3.0.2+ds1/share/locale/muse_fr.ts --- muse-2.1.2/share/locale/muse_fr.ts 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/muse_fr.ts 2017-12-04 21:01:19.000000000 +0000 @@ -1,71 +1,64 @@ - + @default - Add Midi Track Ajouter une Piste Midi - - Add Drum Track Ajouter une Piste de Batterie - Add Old Style Drum Track Ajouter une Piste de Batterie (Ancien Style) - Add New Style Drum Track Ajouter une Piste de Batterie (Nouveau Style) - Add Wave Track Ajouter une Piste Audio - Add Audio Output Ajouter une Sortie Audio - Add Audio Group Ajouter un Groupe Audio - Add Audio Input Ajouter une Entrée Audio - Add Aux Send Ajouter un Envoie Aux - Add Synth Ajouter un Synthétiseur - Select project directory Sélectionner le répertoire du projet - + Route Route - + + channel="%1" + + + + dest dest @@ -75,38 +68,31 @@ nom ="%1"/ - Warning: No output devices! Attention: Pas de périphériques de sortie! - Open midi config... Ouvrir la config midi... - - Output port/device Port/périphérique de sortie - default défaut - Empty ports Ports vides - + <none> <rien> - channelMask="%1" masqueCanal="%1" @@ -115,7 +101,6 @@ Mauvais timing - Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. @@ -140,25 +125,71 @@ Version 2 - Version 2.1.1 - Version 2.1.1 + Version 2.1.1 + + + Version 2.2beta1 + Version 2.2beta1 - (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License - (C) Copyright 1999-2012 Werner Schweer et autres. + (C) Copyright 1999-2012 Werner Schweer et autres. Voir http://www.muse-sequencer.org pour de nouvelles versions et pour plus d'information. Publié sous la licence publique GNU - + Version 2.2beta2 + Version 2.2beta2 {2.2b?} + + + (C) Copyright 1999-2014 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer et autres. +Voir http://www.muse-sequencer.org pour de nouvelles versions et +pour plus d'information. + +Publié sous la licence publique GNU {1999-2014 ?} + + + + Version info (replaced programmatically) + + + + + (C) Copyright 1999-2015 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer et autres. +Voir http://www.muse-sequencer.org pour de nouvelles versions et +pour plus d'information. + +Publié sous la licence publique GNU {1999-2014 ?} {1999-2015 ?} + + + + System information: + + + + + TextLabel + + + + &Keep On Rocking! &Keep On Rocking! @@ -171,7 +202,7 @@ AppearanceDialogBase - + Apply Appliquer @@ -186,7 +217,7 @@ Annuler - + Arranger Arrangeur @@ -258,7 +289,7 @@ Background picture - image de fond + Image de fond @@ -276,17 +307,16 @@ Eléments - + Style/Fonts Styles/Polices - QT Theme - Thème QT + Thème QT - + Windows Windows @@ -336,104 +366,102 @@ Schéma de couleur MusE - + current settings réglages courants - light theme (changing overrides all other settings) - thème clair (le changement écrasera tous les autres réglages) + thème clair (le changement écrasera tous les autres réglages) - dark theme (changing overrides all other settings) - thème foncé (le changement écrasera tous les autres réglages) + thème foncé (le changement écrasera tous les autres réglages) - + Fonts Polices - + Family Famille - + Size Taille - + Font 1 Police 1 - + Font 2 Police 2 - + Font 3 Police 3 - + Font 0 Police 0 - - - - - - - + + + + + + + Bold Gras - + - - - - + + + + Italic Italique - + - - - - - - + + + + + + ... ... - + Font 4 Police 4 - + Font 5 Police 5 - + clear - vider + nettoyer @@ -456,7 +484,7 @@ Palette - + add to palette ajouter à la palette @@ -501,12 +529,22 @@ Opacité globale - + + Themes + Thèmes + + + Style Sheet: Feuille de style: - + + Change + Changer + + + Font 6 Police 6 @@ -516,28 +554,28 @@ Configure arranger columns - Configurer les colonnes de l'arrangeur + Configurer les colonnes de l'arrangeur Columns: - Colonnes: + Colonnes: Name: - Nom: + Nom: Controller type: - Type de contrôleur: + Type de contrôleur: Midi controller type - Type de contrôleur midi + Type de contrôleur midi Control7 @@ -574,61 +612,61 @@ H-Ctrl - H-Ctrl + H-Ctrl Midi controller number high byte - Contrôleur midi - numéro de byte haut + Contrôleur midi - numéro de byte haut L-Ctrl - L-Ctrl + L-Ctrl Midi controller number low byte - Contrôleur midi - numéro de byte bas + Contrôleur midi - numéro de byte bas * wild card - * + * affect CCs at - affecte les CCs à + affecte les CCs à begin of song - début du + début du morceau current position - position courante + position courante &Add - &Ajouter + &Ajouter &Delete - &Supprimer + &Supprimer Done - Fait + Fait @@ -653,6 +691,40 @@ + ChooseSysexBase + + + Dialog + Dialogue + + + + Hex: + + + + + + TextLabel + + + + + Comment: + + + + + &OK + + + + + &Cancel + + + + ClipListEditorBase @@ -705,7 +777,7 @@ Form1 - Forme1 + Form1 @@ -753,14 +825,14 @@ Device Name metas trump Port metas if both exist - Le Nom de Périphérique metas trompe le Port metas - si les deux existent + Le Nom de Périphérique métas prend le dessus sur le Port +métas si les deux existent Instrument Name metas trump Mode sysexes if both exist - Le Nom d'Instrument metas trompe le Mode sysexes - si les deux existent + Le Nom d'Instrument métas prend le dessus sur le Mode + sysexes si les deux existent @@ -815,7 +887,7 @@ Instrument name metas - Nom d'instrument metas + Nom d'instrument métas @@ -826,17 +898,17 @@ Port metas - Port metas + Port métas Device name metas - Nom de périphérique metas + Nom de périphérique métas Export a Port/Device meta for format 0 - Exporte un Port/Périphérique meta + Exporte un Port/Périphérique méta pour le format 0 @@ -895,7 +967,7 @@ Copy Wave Files - Copier les Fichiers Wave + Copier les Fichiers Wave @@ -909,7 +981,7 @@ If no Project has been created yet, you will be asked to, giving another chance to cancel. - Certains fichiers son seront copiés dans le répertoire projet, + Certains fichiers son seront copiés dans le répertoire projet, soit parce qu'ils non pas le droit d'écriture ou parce que plus d'un évènement Wave indépendant les partages. (Si vous préférez qu'à la place les Evènements Wave soient @@ -923,7 +995,7 @@ These files will be copied to the Project Directory: - Ces fichiers seront copiés dans le répertoire projet: + Ces fichiers seront copiés dans le répertoire projet: @@ -946,7 +1018,7 @@ Selected Looped - Sélectionnée bouclée + Sélectionné bouclé @@ -1027,7 +1099,7 @@ Parsing error for file %1 - Erreur de parsing for le fichier %1 + Erreur d'analyse pour le fichier %1 @@ -1288,7 +1360,7 @@ %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? - %1 est supposé être affecté au prog numéro %2, mais il y a en a déjà une dans cet emplacement. + %1 est supposé être affecté au prog numéro %2, mais il y a en a déjà un dans cet emplacement. Voulez-vous le remplacer ou l'ajouter dans le prochain emplacement libre ? @@ -1664,7 +1736,7 @@ Release Rate Vitesse de relachement - + @@ -1672,7 +1744,7 @@ 2° Decay Rate 2nd Vitesse de décroissance - + @@ -1680,7 +1752,7 @@ 1° Decay Level 1er niveau de décroissance - + @@ -2459,13 +2531,13 @@ Select LADSPA plugin - Sélectionner le gréffon LADSPA + Sélectionner le greffon LADSPA Change plugin - Changer le gréffon + Changer le greffon @@ -2751,7 +2823,7 @@ Le saviez vous? - + Don't show on startup Ne pas afficher au démarrage @@ -2771,68 +2843,72 @@ Duplicate tracks - Dupliquer les pistes + Dupliquer les pistes Number of copies - Nombre de copies + Nombre de copies Copy all routes - Copier toutes les routes + Copier toutes les routes Default routing - Routage par défaut + Routage par défaut No routes - Pas de routes + Pas de routes Copy parts - Copier des parties + Copier des parties Copy drumlist - Copier des listes de batterie + Copier des listes de batterie + Copy standard (vol, pan) and synth controllers + + + Copy standard controllers (vol, pan) - Copier les controlleurs standards (vol, pan) + Copier les controlleurs standards (vol, pan) - + Copy effects rack plugins - Copier les racks de gréffons d'effets + Copier les racks de gréffons d'effets Copy plugin controllers - Copier les contrôleurs de gréffon + Copier les contrôleurs de gréffon Ok - Ok + Ok Cancel - Annuler + Annuler EditCtrlBase - + MusE: Edit Controller Event MusE: Editer les évènements du contrôleur @@ -3005,19 +3081,19 @@ - + Properties Propriétés - - + + Name: Nom: - + Group or patch name Groupe ou nom de patch @@ -3075,24 +3151,22 @@ Numéro de programme de patch - Drum patch - Patch de batterie + Patch de batterie - If set, the patch is available only for drum channels. - Si activé, le patch n'est disponible que pour les canaux de batterie. + Si activé, le patch n'est disponible que pour les canaux de batterie. - - + + Drum Batterie - + GM patch Patch GM @@ -3137,7 +3211,7 @@ XG - + Delete group or patch Supprimer un groupe ou un patch @@ -3145,11 +3219,12 @@ + &Delete &Supprimer - + Alt+D @@ -3186,7 +3261,27 @@ Alt+G - + + &Initialization + + + + + Instrument initialization sequence: + + + + + &Add... + + + + + &Change... + + + + &Close &Fermer @@ -3212,7 +3307,7 @@ gérés comme des contrôleurs normaux. - + List of defined controllers Liste de contrôleurs définis @@ -3658,7 +3753,8 @@ Paramètres Null: Hi: - + + Midi Midi @@ -3674,7 +3770,8 @@ W - + + Show in tracks: Afficher dans les pistes: @@ -3684,7 +3781,7 @@ Commentaire: - + Tools Outils @@ -3779,7 +3876,7 @@ MusE: Enter Note - Mus2: Entrer la Note + MusE: Entrer la Note @@ -3831,44 +3928,58 @@ + Name: + Nom: + + + Comment: Commentaire: - - OK - OK + + &Select... + + + + + &OK + + &Cancel + + + + OK + OK + + Cancel - Annuler + Annuler FLUIDGui - FLUID: open Soundfile - FLUID: ouvrir un fichier son + FLUID: ouvrir un fichier son FLUIDGuiBase - Form1 - Form1 + Form1 - Soundfont - Soundfont + Soundfont - Load - Charger + Charger @@ -3998,7 +4109,12 @@ FileDialogButtons - + + Home + + + + Global Global @@ -4013,7 +4129,7 @@ Projet - + read Midi Port Configuration Lire le port de @@ -4021,14 +4137,14 @@ Midi - + write window states écrire les états de la fenêtre - + fdialogbuttons fdialogbuttons @@ -4036,7 +4152,7 @@ FluidSynthGui - + Choose soundfont Choisir la soundfont @@ -4117,7 +4233,7 @@ MusE: Paramètres Globaux - + Audio Audio @@ -4235,7 +4351,7 @@ (Coches par sec.) - + /sec /sec @@ -4246,7 +4362,7 @@ l'interface - + Warn if timer frequency is inadequate Avertir si la fréquence du timer n'est pas adaptée @@ -4256,12 +4372,12 @@ Hauteur de piste - + Use project save dialog Utiliser le dialogue de sauvegarde de projet - + Some popup menus stay open (else hold Ctrl) Certains menus contextuels restent ouvert (autrement appuyer sur Ctrl) @@ -4280,7 +4396,7 @@ middle mouse button can select this option to make the left button behave like the middle button in such areas. Dans certaines zones, le bouton du milieu décrémente -la valeur, tandis que le bouton de droite l'incrémente. Les utilisateur sous un +la valeur, tandis que le bouton de droite l'incrémente. Les utilisateur sans un bouton du milieu peuvent sélectionner cette option pour rendre le comportement du bouton de gauche identique au bouton droit dans ces zones. @@ -4290,7 +4406,7 @@ Utilisez le bouton de gauche pour décrémenter les valeurs - + Shift + Right click sets left range marker Shift + clic droit règle le marqueur gauche de la plage @@ -4300,18 +4416,18 @@ Autorise l'ajout de pistes cachées dans le menu liste des pistes - + Unhide tracks when adding hidden tracks Découvre les pistes quand on ajoute des pistes cachées - - + + Smart focus Focus intelligent - + After editing, controls will return focus to their respective canvas Après édition, les contrôles rendront @@ -4323,12 +4439,12 @@ Afficher les graphes nouvellement créés de vélocité midi par note - + px px - + Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard @@ -4351,42 +4467,43 @@ standard. - + Borderless zoom/pan mouse (else use alternate method) Souris zoom/panoramique sans bord (sinon utilise une méthode alternative) - + + Scrollable submenus + + + + Drum tracks Pistes de batterie - Only offer old-style drumtracks Propose seulement les pistes de batterie ancien style - Only offer new-style drumtracks Propose seulement les pistes de batterie nouveau style - Prefer old-style drumtracks Préfère les pistes de batterie ancien style - Prefer new-style drumtracks Préfère les pistes de batterie nouveau style - + GUI Style Style du GUI @@ -4434,12 +4551,27 @@ &Annuler - + Application Application - + + start &with last song + + + + + start with &template + + + + + sta&rt with song + + + + Views Vues @@ -4524,7 +4656,7 @@ Cour - + Start Muse Démarrer MusE @@ -4544,19 +4676,17 @@ Démarrer la chanson - start with last song Démarrer avec la dernière chanson - start with song Démarrer avec la chanson - + min. Slider Val Val min. de l'ascenceur @@ -4607,7 +4737,7 @@ 16 - + Project directory Répertoire project @@ -4617,19 +4747,19 @@ Projets: - - + + ... ... - + Main Window Fenêtre Principale - + Choose start song or template Choisir une chanson de départ ou un modèle @@ -4639,12 +4769,11 @@ Revenir aux valeurs par défaut - start with template Démarrer avec un modèle - + Start template or song: Modèle ou chanson de démarrage: @@ -4661,7 +4790,17 @@ Lire la configuration des Ports MIDI - + + Warn if opening file versions different than current version + + + + + Auto save (every 5 minutes if not playing/recording) + Sauvegarde automatique (toute les 5 minutes si pas de lecture/enregistrement) + + + Try to use Jack Freewheel Essaye d'utiliser Jack FreeWheel @@ -4786,7 +4925,27 @@ Des périodes plus courtes donne une meilleure résolution de rendu midi. - + + &Record all instruments + + + + + &Don't record hidden instruments + + + + + Don'&t record muted instruments + + + + + Don't record &hidden or muted instruments + + + + Instrument initialization Initialisation de l'Instrument @@ -4829,52 +4988,68 @@ (coches/Noire) - + + &Only offer old-style drumtracks + + + + + Only offer new-style &drumtracks + + + + + &Prefer old-style drumtracks + + + + + Prefer &new-style drumtracks + + + + Record new style drum tracks Enregistre des pistes de batterie dans le nouveau style - Record all instruments Enregistre tous les instruments - Don't record hidden instruments N'enregistre pas les instruments cachés - Don't record muted instruments N'enregistre pas les instruments rendus muets - Don't record hidden or muted instruments N'enregistre pas les instruments cachés ou rendus muets - + GUI Behaviour Comportement du GUI - + Use old-style stop shortcut: Utilise l'ancien style de raccourci d'arrêt: - + Move single armed track with selection Déplacer la piste armée avec la sélection - + On Launch Au Démarrage - + Behavior Comportement @@ -4909,7 +5084,7 @@ Selected Looped - Sélectionné bouclé + Sélectionné Bouclé @@ -5059,12 +5234,12 @@ MusE: Configuration du Métronome - + Metronome Métronome - + Audio Beep Bip audio @@ -5080,37 +5255,32 @@ échantillons, avec accents) - Audio volume - Volume Audio + Volume Audio - Meas volume - Volume Mesure + Volume Mesure - Beat volume - Volume Battement + Volume Battement - Accent1 volume - Volume Accent 1 + Volume Accent 1 - Accent2 volume - Volume Accent 2 + Volume Accent 2 - + MIDI Click Clic MIDI - + Midi Channel Canal Midi @@ -5140,17 +5310,22 @@ Port MIDI - + + Disabled since jack does not support it + + + + Precount Pré-compte - + enable activer - + Bars Mesures @@ -5160,12 +5335,12 @@ de la Piste Principale - + / / - + Signature Signature @@ -5180,12 +5355,47 @@ PreRoll - + &Apply &Appliquer - + + Volume + Volume + + + + Audio master + + + + + Meas + + + + + Beat + + + + + Accent1 + + + + + Accent2 + + + + + Sample + + + + &OK &Ok @@ -5195,14 +5405,14 @@ &Annuler - + Choose outputs... Choisir les sorties... - - - + + + 50 @@ -5213,7 +5423,7 @@ % de volume audio - + Hint: Enable metronome in Transportpanel Astuce: Activer le métronome dans le panneau de transport @@ -5238,27 +5448,27 @@ Midi control - Contrôle Midi + Contrôle Midi Port: - Port: + Port: Channel: - Canal: + Canal: Control type: - Type de contrôle: + Type de contrôle: &Learn - &Apprendre + &Apprendre Control7 @@ -5295,12 +5505,12 @@ Hi: - Hi: + Hi: Lo: - Lo: + Lo: Learn @@ -5308,6 +5518,167 @@ + MidiControllerEditDialogBase + + MusE: Define Midi Controller + MusE: Définir le Contrôleur Midi + + + &Add + &Ajouter + + + Alt+A + Alt+A + + + create new entry + créer une nouvelle entrée + + + pressing the New button you create a new entry +in the MusE list of defined controllers + en pressant le bouton Nouveau, vous créez une nouvelle entrée +dans la liste MusE des contrôleurs définis + + + &Delete + &Supprimer + + + delete selected entry + supprimer l'entrée sélectionnée + + + &OK + &OK + + + A&pply + A&ppliquer + + + &Cancel + A&nnuler + + + Predefined Controller: + Contrôleur prédéfini: + + + Name + Nom + + + Type + Type + + + H-Ctrl + H-Ctrl + + + L-Ctrl + L-Ctrl + + + Min Val + Val Min + + + Max Val + Val Max + + + list of defined controllers + liste des contrôleurs définis + + + This is the MusE list of defined controllers. + Ceci est la liste MusE des contrôleurs définis. + + + Managed Controller for Port + Contrôleur géré pour le Port + + + Channel + Canal + + + This is a list of commonly used midi controllers. +Note that in MusE pitch and program changes are +handled like normal controllers. + Ceci est une liste de contrôleurs communément utilisés. +Notez que dans MusE le ton et les changements de programme sont +gérés comme des contrôleurs normaux. + + + Properties + Propriétés + + + Name + Nom + + + Min Value + Valeur Min + + + Max Value + Valeur Max + + + Type + Type + + + Control7 + Control7 + + + Control14 + Control14 + + + RPN + RPN + + + NRPN + NRPN + + + RPN14 + RPN14 + + + NRPN14 + NRPN14 + + + Pitch + Ton + + + Program + Programme + + + Midi Controller Number High Byte + Contrôleur Midi - Numéro de Byte Haut + + + Midi Controller Number Low Byte + Contrôleur Midi - Numéro de Byte Bas + + + Range + Plage + + + MidiFilterConfigBase @@ -5476,7 +5847,7 @@ - + @@ -5484,8 +5855,8 @@ Egal - - + + @@ -5493,64 +5864,70 @@ Différent de - + Note Note - + Poly Pressure Pression Poly - - + + Control Change Contrôle de Changement - - + + Aftertouch Pression - - + + Pitch Bend Molette de modulation - - + + NRPN NRPN - - + + RPN RPN - - + + + Program + Programme + + + + Value 2 Valeur 2 - - + + - + Value 1 Valeur 1 - + Event Type Type d'Evènement @@ -5597,81 +5974,81 @@ - + Channel Canal - - + + Port Port - + Processing Travail - + - + Keep Garder - - + + - + Fix Fixer - + - + Plus Plus - + - + Minus Moins - + - + Multiply Multiplier - + - + Divide Diviser - + - + Invert Inverser - + ScaleMap Echelle @@ -5683,21 +6060,26 @@ - + Dyn Dyn - + - + Random Aléatoire - + + Toggle + + + + Modules Modules @@ -6081,34 +6463,34 @@ MusE: Info de Piste - + output channel canal de sortie - - + + % % - + output port port de sortie - - - - + + + + off off - + Transpose notes up or down Transposer les notes vers le haut ou le bas @@ -6118,37 +6500,37 @@ Transp. - + Offset playback of notes before or after actual note Décale le jeu des notes avant ou après la note actuelle - + Change note length in percent of actual length Change la longueur de note en pourcentage de la longueur actuelle - + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> <html><head/><body><p>Ajoute ou soustrait la vélocité des notes sur la piste.</p><p><span style=" font-style:italic;">Puisque la plage de note midi est 0-127 ceci <br/>signifie que les notes n'atteindront pas <br/>la vélocité combinée, note + Vélocité.</span></p></body></html> - + Compress the notes velocity range, in percent of actual velocity Compresse la plage de vélocité des notes, en pourcentage de la vélocité actuelle - + Channel Info Info canal - + Rec: Enr: - + Bank Select MSB. Ctrl-double-click on/off. Sélection de banque MSB. Ctrl-double-clic on/off. @@ -6158,7 +6540,7 @@ Sélection de banque LSB. Ctrl-double-clic on/off. - + Program. Ctrl-double-click on/off. Programme. Ctrl-double-clic on/off. @@ -6189,47 +6571,52 @@ Pan - + + Select instrument + + + + Delay Délai - + H-Bank H-Bank - + Compr. Compr. - + L-Bank L-Bank - + Velocity Vélocité - + Length Longueur - + all midi events are sent to this output channel tous les évènements midi sont envoyés vers ce canal de sortie - + Out ch Ch. sortie - + input routing routage d'entrée @@ -6268,17 +6655,18 @@ Fait transiter les évènements d'entrée vers ('thru') la sortie. - + Select instrument patch Sélectionne le patch d'instrument + <unknown> <inconnu> - + Add all settings to song Appliquer tous les réglages à la chanson @@ -6288,7 +6676,7 @@ Tous - + Add bank + prog settings to song Applique les réglages de banque et de programme à la chanson @@ -6771,9 +7159,28 @@ + MusECore::AudioPreviewDialog + + + Auto play + + + + + + Stop + Stop + + + + Play + Jouer + + + MusECore::Song - + Jack shutdown! Arrêt de Jack! @@ -6811,7 +7218,7 @@ cliquez sur le bouton Redémarrer. - + Automation: Automation: @@ -6885,7 +7292,7 @@ &Annuler - + MusE: Tempo list MusE: Liste de tempo @@ -6897,7 +7304,22 @@ Les transférer vers la liste principale de tempo? - + + Do you want to process ALL or only selected events? + + + + + &Selected + + + + + &All + + + + MusE - external script failed MusE - échec d'un script externe @@ -6909,7 +7331,7 @@ %1 - + Und&o &Annuler @@ -7077,17 +7499,32 @@ Personnalisé - + Keep Qt system style Conserver le style système Qt - + + Do you really want to reset colors to theme default? + Voulez-vous réellement réinitialiser les couleurs à celles du thème par défaut? + + + + &Ok + &Ok + + + + &Cancel + &Annuler + + + MusE: load image MusE: charger une image - + Select style sheet Sélectionner la feuille de style @@ -7162,7 +7599,7 @@ On/Off is not! Indicateur muet. Cliquer pour basculer. Clic-droit pour basculer la piste entre on et off. -Muet estconçu pour une action rapide et répétée. +Muet est conçu pour une action rapide et répétée. On/Off ne l'est pas! @@ -7370,47 +7807,47 @@ Control7 - Control7 + Control7 Control14 - Control14 + Control14 RPN - RPN + RPN NPRN - NPRN + NPRN RPN14 - RPN14 + RPN14 NRPN14 - NRPN14 + NRPN14 Pitch - Ton + Ton Program - Programme + Programme Aftertouch - Pression Aftertouch + Pression Aftertouch @@ -7421,7 +7858,12 @@ MusE: Arrangeur - + + D&elete + + + + C&ut Co&uper @@ -7511,7 +7953,7 @@ Select &All - Selectionner &Tout + Sélectionner &Tout @@ -7630,7 +8072,7 @@ &Editer - + &Structure &Structure @@ -7700,7 +8142,7 @@ Configure les &colonnes supplémentaires - + Remove track(s) Supprime le(s) piste(s) @@ -7716,7 +8158,7 @@ Nouveau - + Changed Settings Paramètres Modifiés @@ -7727,7 +8169,7 @@ To apply the changes, please restart MusE. Sorry. (we'll try to fix that) Malheureusement, la modification des réglages des colonnes de l'arrangeur -ne peut pas être appliqué tant que MusE s'exécute. +ne peut pas être appliquée tant que MusE s'exécute. Pour appliquer ces changements, veuillez redémarrer MusE. Désolé. (nous essayerons de réparer cela) @@ -7752,7 +8194,7 @@ Show Midi Tracks - Afficher les Pites Midi + Afficher les Pistes Midi @@ -7798,7 +8240,7 @@ MusEGui::AudioStrip - + panorama panorama @@ -7817,7 +8259,7 @@ gain de calibration - + 1/2 channel 1/2 canal @@ -7901,7 +8343,7 @@ MusEGui::BigTime - + format display format d'affichage @@ -7919,7 +8361,7 @@ tick - marque + coche @@ -7943,7 +8385,7 @@ sous-trame - + MusE: Bigtime MusE: Grande Horloge @@ -7951,7 +8393,7 @@ MusEGui::Canvas - + Tools: Outils: @@ -7982,7 +8424,7 @@ MusEGui::CtrlCanvas - + Make the current part's track match the selected drumlist entry Faire que la partie courante de la piste corresponde à l'entrée sélectionnée de la liste de batterie @@ -8114,14 +8556,14 @@ Sélectionnez la partie de destination et essayez à nouveau. - - + + Recording event failed Echec de l'enregistrement d'un évènement - - + + Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Impossible d'enregistrer l'évènement, parce que la partie courante sélectionnée n'est pas la même piste, et l'instrument qui doit être enregistré peut être soit sur aucune soit sur plusieurs parties, ce qui est ambigu. @@ -8141,7 +8583,7 @@ MusEGui::DrumEdit - + mute instrument rendre muet l'instrument @@ -8351,7 +8793,7 @@ &Plugins - &Gréffons + &Greffons @@ -8366,7 +8808,7 @@ Load Drummap - Charger le Set de Batterie + Charger la Carte de Batterie @@ -8481,7 +8923,7 @@ Store Drummap - Enregistrer le Set de Batterie + Enregistrer la Carte de Batterie @@ -8594,9 +9036,9 @@ LV4 - + Muse: Load Drum Map - MusE: Charger la Carete de Batterie + MusE: Charger la Carte de Batterie @@ -8614,7 +9056,7 @@ Réinitialiser la carte de batterie avec les réglages GM par défaut? - + Not all parts are displayed Toutes les parties ne sont pas affichées @@ -8644,7 +9086,7 @@ MusEGui::EditEventDialog - + Ok Ok @@ -8657,7 +9099,7 @@ MusEGui::EditInstrument - + Control7 Control7 @@ -8707,12 +9149,13 @@ Pression Aftertouch - + + Name Nom - + Vol Vol @@ -8728,11 +9171,12 @@ + Len Long - + A-Note A-Note @@ -8757,7 +9201,17 @@ LV4 - + + Tick + Coche + + + + Data + Donnée + + + MusE: Create file failed MusE: Echec de la création du fichier @@ -8769,18 +9223,18 @@ - + MusE: Save Instrument Definition MusE: Sauvegarder la définition de l'instrument - - + + Instrument Definition (*.idf) Définition de l'instrument (*.idf) - + MusE: Save instrument as MusE: Sauvegarder l'instrument sous @@ -8798,7 +9252,7 @@ Etes-vous sur? - + MusE: Bad instrument name MusE: Mauvais nom d'instrument @@ -8830,7 +9284,7 @@ Veuillez choisir un nom de groupe de patch unique - + MusE: Bad controller name MusE: Mauvais nom de contrôleur @@ -8848,7 +9302,7 @@ Error! All control numbers are taken up! Clean up the instrument! - Error! Tous les numéros de contrôle sont pris! + Erreur! Tous les numéros de contrôle sont pris! Nettoyez l'instrument! @@ -8898,7 +9352,7 @@ MusEGui::EditMetaDialog - + MusE: Enter Meta Event MusE: Entrez un Méta-évènement @@ -8940,7 +9394,7 @@ MusEGui::EditToolBar - + select Pointer Tool: with the pointer tool you can: select parts @@ -9105,7 +9559,7 @@ mute parts - rend silencieux des parties + rend silencieuces des parties @@ -9118,7 +9572,7 @@ curseur - + Edit Tools Outils d'Edition @@ -9126,7 +9580,7 @@ MusEGui::EffectRack - + effect rack rack d'effets @@ -9176,12 +9630,17 @@ sauvegarder les pré-réglages - + + Presets + Pré-réglages + + + MusE: Save Preset MusE: Sauvegarder les pré-réglages - + Replace effect Remplacer l'effet @@ -9194,7 +9653,7 @@ MusEGui::GlobalSettingsConfig - + MusE: Choose start template or song MusE: Choisir le modèle de départ ou la chanson @@ -9333,14 +9792,14 @@ MusEGui::ListEdit - + insert Note - insére une note + insère une note insert SysEx - insére une commande midi SysEx + insère une commande midi SysEx @@ -9350,7 +9809,7 @@ insert Meta - insére une balise Meta + insére une balise Méta insert Channel Aftertouch @@ -9460,33 +9919,29 @@ MusEGui::MPConfig - - + Default input connections Connections d'entrée par défaut - - + + Are you sure you want to apply to all existing midi tracks now? Etes-vous sur de vouloir appliquer ces changements à toutes les pistes midi maintenant? - Default output connections Connections de sortie par défaut - - Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? - Les réglages s'appliqueront aux nouvelles pistes midi. + Les réglages s'appliqueront aux nouvelles pistes midi. Voulez-vous appliquer ces réglages à toutes les pistes midi existantes maintenant? - + MusE: bad device name MusE: mauvais nom de périphérique @@ -9496,67 +9951,73 @@ veuillez choisir un nom de périphérique unique - - + + in entrée - - + + out sortie - Show first aliases Montrer les premiers alias - Show second aliases Montrer les seconds alias - - + + Toggle all Basculer tout - - + + Change all tracks now Modifier toutes les pistes maintenant - + + Remove + Supprimer + + + Create Jack device Créer un périphérique Jack - - + + Port Number Numéro de port - + + Enable gui Activer le gui - + + Enable reading Activer la lecture - + + Enable writing Activer l'écriture - + Port instrument Port d'instrument @@ -9566,7 +10027,7 @@ Nom de périphérique midi. Cliquer pour éditer (Jack) - + Connections from Jack Midi outputs Connections à partir des sorties Midi Jack @@ -9576,42 +10037,45 @@ Connections à des entrées Midi Jack - + Auto-connect these channels to new midi tracks Auto-connecter ces canaux aux nouvelles pistes midi - + Auto-connect new midi tracks to these channels - Auto-connecter les nouvelles pistes midi à cas canaux + Auto-connecter les nouvelles pistes midi à ces canaux - + Auto-connect new midi tracks to this channel Auto-connecter les nouvelles pistes midi à ce canal - + + Device state Etat du périphérique - + Enable gui for device Activer le gui pour le périphérique + Enable reading from device Activer la lecture à partir du périphérique - + + Enable writing to device - Activer l'écriture vers le device + Activer l'écriture vers le périphérique - + Name of the midi device associated with this port number. Click to edit Jack midi name. Nom du périphérique midi associé à ce numéro de port. Cliquer pour éditer le nom midi Jack. @@ -9621,7 +10085,7 @@ Instrument connecté au port - + Connections from Jack Midi output ports Connections à partir des ports de sortie Midi Jack @@ -9631,57 +10095,113 @@ Connections aux ports d'entrée Midi Jack - + Auto-connect these channels, on this port, to new midi tracks. Auto-connecter ces canaux, sur ce port, vers les nouvelles pistes midi. - + Connect new midi tracks to these channels, on this port. Connecter les nouvelles pistes midi vers ces canaux, sur ce port. - + Connect new midi tracks to this channel, on this port. Connecter les nouvelles pistes midi vers ce canal, sur ce port. - + State: result of opening the device Etat: résultat de l'ouverture du périphérique - + Port Port - + + GUI GUI - + + I E - + + O S - + Instrument Instrument - + + Device Name Nom du Périphérique + + + Midi device name + + + + + + Midi device type + + + + + Connections from Jack Midi + + + + + Connections to Jack Midi + + + + + Result of opening the device: +OK: Assigned to a port and in use +Closed: Unassigned to a port, or closed +R/W Error: Unable to open for read or write +Unavailable: USB midi unplugged, or external + application not running, or synth plugin + not installed etc. +(Jack Midi devices have 'unavailable ports' + in the routes columns.) +Unavailable devices or ports can be purged + with 'Remove' or with the advanced router. + + + + + Enable Graphical User Interface for device + + + + Connections from Jack Midi ports + + + + + Connections to Jack Midi ports + + + + In routes Routes d'entrée @@ -9691,7 +10211,7 @@ Routes de sortie - + Def in ch Déf can entr @@ -9701,18 +10221,33 @@ Déf can sort - + + State Etat - + + Type + Type + + + + In + + + + + Out + + + + <unknown> <inconnu> - - + <none> <rien> @@ -9768,7 +10303,7 @@ Bar:Beat:Tick - Mesure:Résolution(Beat):Tic + Mesure:Résolution(Beat):Coche @@ -9806,7 +10341,7 @@ Enable master - Activer maitre + Activer maître @@ -9874,53 +10409,53 @@ Control7 - Control7 + Control7 Control14 - Control14 + Control14 RPN - RPN + RPN NPRN - NPRN + NPRN RPN14 - RPN14 + RPN14 NRPN14 - NRPN14 + NRPN14 Pitch - Ton + Ton Program - Programme + Programme Aftertouch - Pression Aftertouch + Pression Aftertouch MusEGui::MidiInputTransformDialog - + New Nouveau @@ -10085,7 +10620,7 @@ Receive start rewinds before playing - La réception de démarre rembobine avant de jouer + La réception de 'démarre' rembobine avant de jouer @@ -10095,12 +10630,12 @@ Send midi clock output - Envoie l'horloge de sortie midi + Envoie la sortie horloge midi Send midi realtime output - Envoie la sortie midi temps réelle + Envoie la sortie midi temps réel @@ -10119,7 +10654,7 @@ Midi clock input detected. Current port actually used is red. Click to force a port to be used. - Horloge d'entrée mici détectée. + Horloge d'entrée midi détectée. Le port courant actuellement utilisé est rouge. Cliquer pour forcer un port à être utilisé. @@ -10165,7 +10700,7 @@ Accepte les entrées horloge midi. Seulement une entrée est utilisée pour l'horloge. Auto-aquisition: Si deux entrées temps réelles ou plus sont activées, la première horloge détectée est utilisée, jusqu'à ce que l'horloge soit perdue, - alors une autre horloge prend le relai. Meilleur si chaque entrée éteint son horloge + alors une autre horloge prend le relai. Meilleurs si chaque entrée éteint son horloge à l'arrêt, de façon à ce que MusE puisse ré-aquérir l'horloge à partir d'un autre port. Cliquer sur l'indicateur de détection pour forcer l'utilisation d'une autre horloge. @@ -10183,7 +10718,7 @@ Les évènements hors horloge (démarre,stop etc) sont acceptés par TOUS les ports activés. Ceci veut dire que vous pouvez avoir plusieurs périphériques - maitres connectés, et muse acceptera les entrées de leur + maîtres connectés, et MusE acceptera les entrées de leur part. @@ -10203,8 +10738,8 @@ When start is received, rewind before playing. Note: It may be impossible to rewind fast enough to synchronize with the external device. - Quand démarre est reçu, rembobinne avant de jouer. -Note: Il peut être possible de rembobinner suffisamment + Quand 'démarre' est reçu, rembobine avant de jouer. +Note: Il peut être possible de rembobiner suffisamment rapidement pour synchroniser avec un périphérique externe. @@ -10227,9 +10762,9 @@ other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. - Envoi la sortie temps réelle midi, incluant démarre/stop/continue, + Envoi la sortie midi temps réel, incluant démarre/stop/continue, et la position dans la chanson. Si 'Esclave d'une synchro externe' est choisi, - muse peut re-transmettre les messages de l'entrée temps réelle à chacun + MusE peut re-transmettre les messages de l'entrée temps réelle à chacun des autres ports choisis. Ceci veut dire que vous pouvez avoir plusieurs périphériques esclaves connectés, et muse peut re-transmettre les messages temps réels à chacun d'eux. @@ -10390,8 +10925,10 @@ MusEGui::MidiTrackInfo - - + + + + <unknown> <inconnu> @@ -10399,7 +10936,7 @@ MusEGui::MidiTransformerDialog - + New Nouveau @@ -10416,7 +10953,7 @@ MusEGui::MusE - + Failed to start audio! Echec du démarrage de l'audio! @@ -10436,7 +10973,7 @@ - + Und&o &Annuler @@ -10528,7 +11065,7 @@ arrêter le séquenceur - + Play Jouer @@ -10538,7 +11075,7 @@ démarrer la lecture du séquenceur - + Record Enregistrer @@ -10548,18 +11085,23 @@ pour enregistrer, presser Enregistrement puis Lecture - - + + Panic Panique - + send note off to all midi channels envoyer 'note off' à tous les canaux midi - + + turn on/off metronome + activer / désactiver le métronome + + + &New &Nouveau @@ -10587,13 +11129,13 @@ - - + + &Save &Sauvegarder - + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. @@ -10621,13 +11163,13 @@ Importer une Partie - - - Import Wave File - Importer fichier WAV + + + Import Audio File + Importer fichier Audio - + Find unused wave files Trouver les fichiers wave non utilisés @@ -10649,7 +11191,7 @@ Bigtime Window - Fenêtre GrandeHorloge + Fenêtre Grande Horloge @@ -10684,7 +11226,7 @@ &Plugins - &Gréffons + &Greffons @@ -10694,7 +11236,7 @@ Input Plugins - Gréffons d'Entrée + Greffons d'Entrée @@ -10817,12 +11359,19 @@ Suivi Continu - + + MusE: Song: + MusE: Chanson: + + + + + Metronome Métronome - + Midi Sync Synchro MIDI @@ -10892,7 +11441,22 @@ Transport - + + Cpu load + + + + + Measured CPU load + + + + + No CPU load data + + + + &File &Fichier @@ -10937,7 +11501,7 @@ A propos de &Qt - + Cannot read template Impossible de lire le modèle @@ -10957,14 +11521,12 @@ Format de fichier inconnu: %1 - - - + MusE: Song: %1 MusE: Chanson: %1 - + MusE: load project MusE: charger le projet @@ -10974,12 +11536,12 @@ MusE: charger le modèle - + MusE: Write File failed - MusE: échec de l'écriture de fichier + MusE: échec de l'écriture du fichier - + The current Project contains unsaved data Save Current Project? Le Projet en cours contient des données non encore sauvegardées @@ -10987,17 +11549,17 @@ - + S&kip Sau&ter - + &Cancel &Annuler - + MusE: Save As MusE: Enregistrer Sous @@ -11008,7 +11570,7 @@ Rien à éditer - + @@ -11077,13 +11639,13 @@ Sauvegarder le Projet Courant? - + &Abort &Annuler - + This will clear all automation data on all audio tracks! Proceed? @@ -11126,7 +11688,7 @@ MusE: Export Midi - + no help found at: pas d'aide trouvée ici: @@ -11136,7 +11698,7 @@ MusE: Ouvrir l'Aide - + Unable to launch help Impossible de charger l'aide @@ -11156,7 +11718,8 @@ Add midi file to current project? - Ajouter le fichier midi au projet courant? + Ajouter le fichier midi au projet courant? + @@ -11183,7 +11746,7 @@ échec: - + Import part is only valid for midi and wave tracks! Importer une partie n'est valide que pour les pistes midi et wave! @@ -11218,12 +11781,12 @@ - + to import an audio file you have first to selecta wave track pour importer un fichier audio, vous devez d'abord sélectionner une piste wave - + Import Wavefile Importer un fichier Wave @@ -11231,13 +11794,65 @@ This wave file has a samplerate of %1, as opposed to current setting %2. +File will be resampled from %1 to %2 Hz. +Do you still want to import it? + + + + + + + Wave import error + + + + + There are too many wave files +of the same base name as imported wave file +Can not continue. + + + + + Can't create new wav file in project folder! + + + + + + Failed to initialize sample rate converter! + + + + + Cancel + Annuler + + + + Resampling wave file +"%1" +from %2 to %3 Hz... + + + + + Output has clipped +Resampling again and normalizing wave file +"%1" +Try %2 of %3... + + + + This wave file has a samplerate of %1, +as opposed to current setting %2. Do you still want to import it? Ce fichier wave a une période d'échantillonnage de %1, contrairement au réglage courant %2. Voulez-vous quand même l'importer? - + &Yes &Oui @@ -11246,6 +11861,23 @@ &No &Non + + + File version is %1.%2 +Current version is %3.%4 +Conversions may be applied if file is saved! + + + + + Opening file + + + + + Do not warn again + + MusEGui::NoteInfo @@ -11288,13 +11920,13 @@ MusEGui::PartCanvas - + Cannot copy/move/clone to different Track-Type Ne peut copier/déplacer vers une type de Piste différent - + Part: Partie: @@ -11371,19 +12003,24 @@ information du fichier - + + Normalize + + + + MusE: save part MusE: sauvegarder une partie - + Part name: %1 Files: Nom de partie: %1 Fichiers: - + Automation: Automation: @@ -11393,7 +12030,7 @@ Supprimer les sélectionnés - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. @@ -11435,7 +12072,7 @@ Can only paste to midi or wave track - Ne peut uniqument coller que sur une piste midi ou wave + Ne peut uniquement coller que sur une piste midi ou wave @@ -11492,7 +12129,7 @@ MusEGui::PianoRoll - + &Edit &Editer @@ -11624,7 +12261,7 @@ &Plugins - &Gréffons + &Greffons @@ -11685,16 +12322,16 @@ MusEGui::PluginDialog - + MusE: select plugin - MusE: choisir le gréffon + MusE: choisir le greffon Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. - Catégories de gréffon. + Catégories de greffon. Clic-droit sur les onglets pour gérer. Clic-droit sur les gréffons pour ajouter/supprimer d'une catégorie. @@ -11789,42 +12426,35 @@ Numéro d'ID - Ok - Ok + Ok - Cancel - Annuler + Annuler - Show plugs: - Afficher les prises: + Afficher les prises: - Mono and Stereo - Mono et Stéréo + Mono et Stéréo - Stereo - Stéréo + Stéréo - Mono - Mono + Mono - Show All - Afficher Tout + Afficher Tout - + &create new group &créer un nouveau groupe @@ -11841,10 +12471,10 @@ Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. - Selectionnez les types de gréffons qui devraient être visible dans la liste.<br>Notez qu'utiliser des gréffons mono sur des pistes mono n'est pas un problème, deux seront utilisés en parallèle.<br>Attention aussi car l'alternative 'tout' inclues des gréffons qui peuvent ne pas être utiles dans un rack d'effets. + Selectionnez les types de greffons qui devraient être visible dans la liste.<br>Notez qu'utiliser des greffons mono sur des pistes mono n'est pas un problème, deux seront utilisés en parallèle.<br>Attention aussi car l'alternative 'tout' inclues des greffons qui peuvent ne pas être utiles dans un rack d'effets. - + Associated categories Catégories associées @@ -11866,7 +12496,12 @@ Entrer le nouveau nom de groupe - + + Wine VST + + + + dssi synth synthé dssi @@ -11876,20 +12511,29 @@ effet dssi - + + LV2 synth + + + + + LV2 effect + + + + ladspa ladspa - Search in 'Label' and 'Name': - Chercher dans 'Label' et 'Nom': + Chercher dans 'Label' et 'Nom': MusEGui::PluginGui - + File Buttons Boutons Fichier @@ -11907,17 +12551,17 @@ bypass plugin - Gréffon bypassé + Greffon bypassé - + MusE: load preset MusE: charger les pré-réglages Error reading preset. Might not be right type for this plugin - Erreur lors de la lecture du pré-réglage. Peut ne pas être le bon type pour ce gréffon + Erreur lors de la lecture du pré-réglage. Peut ne pas être le bon type pour ce greffon @@ -11928,7 +12572,7 @@ MusEGui::PopupMenu - + <More...> %1 <Plus...> %1 @@ -11942,67 +12586,216 @@ + MusEGui::RouteDialog + + + Normal + Normal + + + + Alias 1 + + + + + Alias 2 + + + + + + Source + Source + + + + + Destination + Destination + + + + <none> + + + + MusEGui::RoutePopupMenu - - - - - - - - + Channel Canal - - - + + + + Soloing chain chaîne solo - - + + + Audio returns Retours audio - + Warning: No input devices! Attention: pas de périphériques d'entrée! + + Channel grouping: + + + + + Mono + + + + + Stereo + Stéréo + + + + + + + + + + + + Channels + + + + + + Midi ports/devices + + + + + + + Omni + + + + + Show aliases: + + + + + First + + + + + Second + + + + + Show names + + + + + Show first aliases + Montrer les premiers alias + + + Show second aliases + Montrer les seconds alias + + + + + Jack ports + + + + + + Connect + Connecter + + + + Unavailable + + + + + Open advanced router... + + + + + Output routes: + + + + + Input routes: + + + + Open midi config... Ouvrir la configuration midi... - - - - + <none> <rien> - + + Midi sends + + + + + Sources: + + + Toggle all Basculer tout - + More... Plus... - + + + Tracks + + + + + Destinations: + + + + Audio sends Envoies audio - Midi port sends Envoies de port midi @@ -12010,7 +12803,7 @@ MusEGui::ScoreCanvas - + Treble Aigus @@ -12030,7 +12823,7 @@ Supprimer la portée - + Ambiguous part Partie ambigue @@ -12053,7 +12846,7 @@ MusEGui::ScoreEdit - + Step recording tools Outils d'enregistrement pas à pas @@ -12302,7 +13095,7 @@ MusEGui::ScrollScale - + next page page suivante @@ -12320,7 +13113,7 @@ MusEGui::ShortcutCaptureDialog - + Ok Ok @@ -12394,18 +13187,18 @@ time signature at current position - signature temporelle à la position en cours + signature temporelle à la position en cours Signature: - Signature: + Signature: MusEGui::Strip - + Remove track? Supprimer la piste? @@ -12413,12 +13206,12 @@ MusEGui::TList - + <none> <rien> - + visible visible @@ -12444,12 +13237,12 @@ - + off off - + <unknown> <inconnu> @@ -12464,7 +13257,7 @@ veuillez choisir un nom unique de piste - + Unused Devices Périphériques non utilisés @@ -12485,23 +13278,29 @@ &Non - - + + show gui afficher l'interface - - + + show native gui afficher l'interface native - - Clear all controller events? - Nettoyer tous les contrôleurs d'évènements? - - + + + Presets + Pré-réglages + + + + Clear all controller events? + Nettoyer tous les contrôleurs d'évènements? + + &Ok &Ok @@ -12572,7 +13371,7 @@ Synthé - + Delete Track Supprimer la piste @@ -12622,7 +13421,7 @@ Insérer une Piste - + Drum map Carte de batterie @@ -12638,17 +13437,31 @@ Réinitialiser l'organisation de la carte de batterie de la piste? - + Muse: Load Track's Drum Map MusE: Charger la Carte de Batterie de la Piste - + + Drummap + Carte de batterie + + + + This drummap was created with a previous version of MusE, +it is being read but the format has changed slightly so some +adjustments may be necessary. + Cette carte de batterie a été créée avec une version précédente de MusE, +elle sera lue mais le format a légèrement changé donc quelques +ajustement seront nécessaires. + + + MusE: Store Track's Drum Map MusE: Stocker la Carte de Batterie de la Piste - + Midi Midi @@ -12680,12 +13493,12 @@ tempo at current position - tempo à la position courante + tempo à la position courante Tempo: - Tempo: + Tempo: @@ -12716,7 +13529,7 @@ MusEGui::TopWin - + As subwindow Comme sous-fenêtre @@ -12742,11 +13555,16 @@ + Metronome + Métronome + + + Transport Transport - + Song Position Position de chanson @@ -12761,7 +13579,7 @@ Signature - + Piano roll Vue piano @@ -13008,7 +13826,7 @@ Master - Maitre + Maître @@ -13067,41 +13885,48 @@ + MusEGui::WTScale + + bar scale + taille de la mesure + + + MusEGui::WaveCanvas - + Adjust Wave Offset - Ajuster le décalage Wave + Ajuster le décalage Wave Wave offset (frames) - Décalage Wave (trames) + Décalage Wave (trames) Part creation failed - Echec de la création de la partie + Echec de la création de la partie Left and right position markers must be placed inside the current part. - La position des marqueurs gauche et droite doit être placé à l'intérieur de la partie courante. + La position des marqueurs gauche et droite doit être placé à l'intérieur de la partie courante. Part created - Partie créée + Partie créée The selected region has been copied to the clipboard and can be pasted in the arranger. - La région sélectionnée a été copiée dans le presse-papier et peut être collée dans l'arrangeur. + La région sélectionnée a été copiée dans le presse-papier et peut être collée dans l'arrangeur. - + MusE - external editor failed - MusE - échec de lancement de l'éditeur externe + MusE - échec de lancement de l'éditeur externe @@ -13109,7 +13934,7 @@ check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. - MusE n'a pas été capable de lancer l'éditeur externe + MusE n'a pas été capable de lancer l'éditeur externe vérifier que le réglage de l'éditeur dans: Réglages Globaux->Audio:Editeur Wave Externe est réglé sur un éditeur valide. @@ -13117,7 +13942,7 @@ MusE - file size changed - MusE - la taille du fichier a changé + MusE - la taille du fichier a changé @@ -13125,7 +13950,7 @@ since it must fit the selected region. Missing data is muted - Quand on édite via l'éditeur externe - vous ne devriez pas changer la taille du fichier + Quand on édite via l'éditeur externe - vous ne devriez pas changer la taille du fichier car il doit tenir dans la région sélectionnée. Les données manquante sont rendues muettes @@ -13179,12 +14004,11 @@ Editer dans un éditeur e&xterne - Adjust wave offset... - Ajuster le décalage wave... + Ajuster le décalage wave... - + Mute Selection Rendre la sélection muette @@ -13272,33 +14096,29 @@ MusEGui::WaveView - MusE - external editor failed - MusE - échec de lancement de l'éditeur externe + MusE - échec de lancement de l'éditeur externe - MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. - MusE n'a pas été capable de lancer l'éditeur externe + MusE n'a pas été capable de lancer l'éditeur externe vérifier que le réglage de l'éditeur dans: Réglages Globaux->Audio:Editeur Wave Externe est réglé sur un éditeur valide. - MusE - file size changed - MusE - la taille du fichier a changé + MusE - la taille du fichier a changé - When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted - Quand on édite via l'éditeur externe - vous ne devriez pas changer la taille du fichier + Quand on édite via l'éditeur externe - vous ne devriez pas changer la taille du fichier car il doit tenir dans la région sélectionnée. Les données manquante sont rendues muettes @@ -13577,6 +14397,59 @@ + PluginDialogBase + + + Dialog + Dialogue + + + + Ports: + + + + + Mono + Mono + + + + Mono + Stereo + + + + + Stereo + Stéréo + + + + All + Tous + + + + Plugin type: + + + + + Filter: + + + + + &OK + + + + + &Cancel + + + + ProjectCreate @@ -13651,17 +14524,122 @@ Please first select the range for crescendo with the loop markers. Veuillez sélectionner une plage pour le crescendo avec les marqueurs de boucle. + + + Controller ! + + + + + Other ! + + + + + Select gui type + + + + + Preset actions + + + + + Save preset... + + + + + Update list + + + + + Saved presets + + + + + No presets found + + + + + Enter new preset name + + + + + Midi programs + + + + + Presets + Pré-réglages + + + + Switch on General Midi Level 1 mode + + + + + Switch on General Midi Level 2 mode + + + + + Switch off General Midi Level 1 or 2 + + + + + Switch on Roland GS mode + + + + + Switch on Yamaha XG mode + + + + + Tracks: + + + + + Midi ports: + + + + + Midi devices: + + + + + Jack: + + + + + Jack midi: + + QWidget - - + + Cannot convert sysex string Ne peut convertir le code sysex - + Hex String too long (2048 bytes limit) Code Hexa trop long (limite: 2048 octets) @@ -13676,7 +14654,7 @@ nouveau - + create peakfile for créer fichier de crêtes pour @@ -13707,7 +14685,7 @@ échec de la création du répertoire - + File %1 exists. Overwrite? @@ -13735,24 +14713,23 @@ MusE: Ouvrir un Fichier - - + + None Rien - + No wave events selected. - Pas d'évènements wave sélectionnés. + Pas d'évènements wave sélectionnés. - No selection. Ignoring Pas de sélection. On ignore - + Instrument-defined Instrument-défini @@ -13841,223 +14818,708 @@ Quantifier Longueur - - Raster - Rendre + + Raster + Rendre + + + + Whole + Entièrement + + + + Half + Moitié + + + + 4th + Noire + + + + 4th Triplet + Triolet de Noire + + + + 8th + Croche + + + + 8th Triplet + Triolet de croche + + + + 16th + Double croche + + + + 16th Triplet + Triolet de double croche + + + + 32th + Triple croche + + + + 32th Triplet + Triolet de triple croche + + + + Swing: + Swing: + + + + If the proposed change in tick or length is smaller than threshold, nothing is done. +If swing=0, this is normal +If swing is 33, you get a 2:1-rhythm. +If swing is -33, you get a 1:2-rhythm. + + Si le changement proposé en coche ou longueur est plus petit que le seuil, rien n'est fait. +Si swing=0, c'est normal +Si swing vaut 33, vous obtenez un rythme 2:1. +Si swing vaut -33, vous obtenez un rythme 1:2. + + + + + OK + OK + + + + Cancel + Annuler + + + + RemoveBase + + + MusE: Erase Notes + MusE: Effaçage de Notes + + + + Range + Plage + + + + All Events + Tous les Evènements + + + + Selected Events + Evènements choisis + + + + Looped Events + Evènements bouclés + + + + Selected Looped + Sélectionné Bouclé + + + + Thresholds + Seuils + + + + ticks + coches + + + + Velocity + Vélocité + + + + Length + Longueur + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si rien n'est coché, tout sera supprimé.</p> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si la vélocité est cochée, seul les notes ayant velo &lt; seuil seront supprimées.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si tous est coché, les notes ayant velo &lt; seuil OU ayant une longueur &lt; seuil seront supprimées.</p></body></html> + + + + OK + OK + + + + Cancel + Annuler + + + + RhythmBase + + MusE: Random Rhythm Generator + MusE: Générateur de Rythme Aléatoire + + + Instrument Properties + Propriétés d'Instrument + + + counts/bar + décomptes/mesure + + + steps/count + pas/décompte + + + # bars + # mesures + + + test + teste + + + contrib + contribution + + + randomize + randomiser + + + Group 1 + Groupe 1 + + + Group 2 + Groupe 2 + + + Group 3 + Groupe 3 + + + Group 4 + Groupe 4 + + + Group 5 + Groupe 5 + + + listen + écouter + + + Instrument + Instrument + + + Group + Groupe + + + create new entry + créer une nouvelle entrée + + + pressing the New button you create a new entry +in the MusE list of defined controllers + en pressant le bouton Nouveau, vous créez une nouvelle entrée +dans la liste MusE des contrôleurs définis + + + &New + &Nouveau + + + delete selected entry + supprimer l'entrée sélectionnée + + + &Delete + &Supprimer + + + Up + Haut + + + Down + Bas + + + list of defined controllers + liste des contrôleurs définis + + + This is the MusE list of defined controllers. + Ceci est la liste MusE des contrôleurs définis. + + + Instrument + Instrument + + + steps/count + pas/décompte + + + <b>Notice!</b><br> +Random Rhythm Generator is not enabled yet! + <b>Remarque!</b><br> +Le Générateur de Rythme Aléatoire n'est pas encore activé! + + + Tools + Outils + + + &File + &Fichier + + + &Edit + &Editer + + + &Help + &Aide + + + New + Nouveau + + + Ctrl+N + Ctrl+N + + + &Open... + &Ouvrir... + + + Open + Ouvrir + + + Ctrl+O + Ctrl+O + + + &Save + &Sauvegarder + + + Save + Sauvegarder + + + Ctrl+S + Ctrl+S + + + Save &As... + Sauvegarder &sous... + + + Save As + Sauvegarder sous + + + &Print... + Im&primer... + + + Print + Imprimer + + + Ctrl+P + Ctrl+P + + + E&xit + &Quitter + + + Exit + Quitter + + + &Undo + Ann&uler + + + Undo + Annuler + + + Ctrl+Z + Ctrl+Z + + + &Redo + &Refaire + + + Redo + Refaire + + + Ctrl+Y + Ctrl+Y + + + &Cut + &Couper + + + Cut + Couper + + + Ctrl+X + Ctrl+X + + + C&opy + C&opier + + + Copy + Copier + + + Ctrl+C + Ctrl+C + + + &Paste + Co&ller + + + Paste + Coller + + + Ctrl+V + Ctrl+V + + + &Find... + C&hercher... + + + Find + Chercher + + + Ctrl+F + Ctrl+H + + + &Contents... + &Contenus... + + + Contents + Contenus + + + &Index... + &Index... + + + Index + Index + + + &About... + &A propos... + + + About + A propos + + + + RhythmGenerator + + Form3 + Form3 + + + Instrument Settings: + Réglages d'instrument: + + + Instrument + Instrument + + + Hi-Hat + Hi-Hat + + + add + ajouter + + + delete + supprimer + + + steps/count + pas/décompte + + + count/bar + décompte/mesure + + + # bars + # mesures + + + Group Settings: + Réglages de Groupe: + + + Group + Groupe + + + Group 1 + Groupe 1 - - Whole - Entièrement + Group 2 + Groupe 2 - - Half - Moitié + Group 3 + Groupe 3 - - 4th - Noire + Group 4 + Groupe 4 - - 4th Triplet - Triolet de Noire + Group 5 + Groupe 5 - - 8th - Croche + contrib + contribution - - 8th Triplet - Triolet de croche + listen + écouter - - 16th - Double croche + Randomize + Randomiser - - 16th Triplet - Triolet de double croche + Rhythm Style: + Style de Rythme: - - 32th - Triple croche + Clear + Nettoyer - - 32th Triplet - Triolet de triple croche + Open... + Ouvrir... - - Swing: - Swing: + Save + Sauvegarder - - If the proposed change in tick or length is smaller than threshold, nothing is done. -If swing=0, this is normal -If swing is 33, you get a 2:1-rhythm. -If swing is -33, you get a 1:2-rhythm. - - Si le changement proposé en coche ou longueur est plus petit que le seuil, rien n'est fait. -Si swing=0, c'est normal -Si swing vaut 33, vous obtenez un rythme 2:1. -Si swing vaut -33, vous obtenez un rythme 1:2. - + Save as... + Sauvegarder sous... - - OK - OK + Generate + Générer - - Cancel - Annuler + Close + Fermer - RemoveBase + RouteDialogBase - - MusE: Erase Notes - MusE: Effaçage de Notes + + MusE: Routing + MusE: Routage - - Range - Plage + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + - - All Events - Tous les Evènements + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. + - - Selected Events - Evènements choisis + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + - - Looped Events - Evènements bouclés + + Itemized list of current connections. + - - Selected Looped - Sélectionné Bouclé + + + Show only selected sources + - Thresholds - Seuils + + Show only destination routes for selected source + - - ticks - coches + + + Show only selected destinations + - - Velocity - Vélocité + + + Show only source routes for selected destination + - - Length - Longueur + + + Show all Midi Ports + - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si rien n'est coché, tout sera supprimé.</p> -<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si la vélocité est cochée, seul les notes ayant velo &lt; seuil seront supprimées.</p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si tous est coché, les notes ayant velo &lt; seuil OU ayant une longueur &lt; seuil seront supprimées.</p></body></html> + + Auto adjust column size + - - OK - OK + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + - - Cancel - Annuler + + + Preferred route name or alias + - - - RouteDialogBase - - MusE: Routing - MusE: Routage + + + Connect source to destination + + + + + + Remove selected route + - Add Route Ajouter une route - Source: Source: - Destination: Destination: - connect source to destination connecter la source à la destination - + Connect Connecter - Current Routes Routes courantes - Source Source - Destination Destination - remove selected route supprimer la route sélectionnée - + Remove Supprimer @@ -14067,7 +15529,7 @@ SimpleDrums - Ladspa Plugin Chooser - BatterieSimple - Sélecteur de Gréffon Ladspa + BatterieSimple - Sélecteur de Greffon Ladspa @@ -14320,7 +15782,22 @@ SimpleSynthGui - + + Mix + Mix + + + + Chn + + + + + Channel routing + + + + &Load setup &Charger la configuration @@ -14330,7 +15807,7 @@ &Sauvegarder la configuration - + Load sample dialog Dialogue de chargement d'échantillon @@ -14371,65 +15848,66 @@ SynthConfigBase - Soft Synthesizer Synthétiseur Soft - + File Fichier - Instances Instances - - + Name Nom - + list of available software synthesizers liste des synthétiseurs softs disponibles - Add Instance Ajouter une Instance - Remove Instance Retirer une Instance - Midi Port Port MIDI - Midi Port and Soft Synth Configuration Port Midi et Configuration du Synthé Soft - - + Type Type - Midi connections Connections midi - + + Configure midi devices, midi ports, and synthesizers + + + + + Available soft synthesizers + + + + Inst Inst @@ -14444,7 +15922,47 @@ Description - + + Useable devices + + + + + Add: + + + + + Synth + Synthé + + + + ALSA + + + + + JACK + + + + + Rename + + + + + Remove + Supprimer + + + + Device port assignments + + + + &Apply &Appliquer @@ -14455,6 +15973,17 @@ + TimeCanvas + + Ctrl + Ctrl + + + Add Controller View + Ajouter une Vue Contrôleur + + + TransposeBase @@ -14746,7 +16275,7 @@ On On - + VAM 1.0beta3 Virtual Analog for MusE @@ -14839,7 +16368,7 @@ file_patterns - + Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) @@ -15160,12 +16689,12 @@ Plate (Small) - Plaque (Petit) + Plaque (Petite) Plate (Medium) - Plaque (Moyen) + Plaque (Moyenne) @@ -15175,7 +16704,7 @@ Plate (Large) - HD - Plaque (Grand) - HD + Plaque (Grande) - HD @@ -15313,7 +16842,7 @@ Instrument initialization - Initialisation de l'instrument + Initialisation de l'instrument @@ -15326,7 +16855,7 @@ You can always do it manually from the Midi menu. Continue? - MusE devrait maintenant envoyer des Séquences d'Initialisation d'Instrument. + MusE devrait maintenant envoyer des Séquences d'Initialisation d'Instrument. Les séquences (habituellement des Messages Systèmes exclusifs) sont définis via les instruments sélectionnés dans Réglages -> Dialogue Ports Midi Ports, comme les instruments GM (défaut), GS, ou XG. @@ -15339,7 +16868,7 @@ Don't ask me again - Ne plus me redemander + Ne plus me redemander @@ -15370,7 +16899,7 @@ Transport: Jouer, Stop, Rembobiner - + Transport: Goto left marker Transport: Aller au marqueur gauche @@ -15660,7 +17189,12 @@ Réglages: Configuration des raccourcis - + + Transport: Restart recording + + + + Settings: Configure metronome Réglages: Configuration du métronome @@ -15805,6 +17339,11 @@ Editer: Ajouter une mesure vide + + Edit: Duplicate track + + + Select track above Sélectionner la piste du dessus @@ -15815,7 +17354,12 @@ Sélectionner la piste du dessous - + + Edit selected track name + + + + Midi: Transpose Midi: Transposer @@ -15921,6 +17465,31 @@ Vue: Faire défiler à gauche + + Transport: Step record + + + + + Transport: Midi input + + + + + Transport: Play events + + + + + Edit: Increase velocity + + + + + Edit: Decrease velocity + + + Edit: Set Fixed Length on Midi Events Editer: Régler des longueurs fixées sur des Evènements Midi @@ -15996,6 +17565,11 @@ Editer: Changer la Couleur de l'Evènement + + Move: Move to selected note + + + Tool: Pointer Outil: Pointeur @@ -16240,23 +17814,28 @@ Goto Prev Marker Aller au Marqueur Précédent + + + Normalize + + warnBadTimingBase Bad timing - Mauvais timing + Mauvais timing Message here - Message ici + Message ici Don't warn me again - Ne plus me prévenir + Ne plus me prévenir diff -Nru muse-2.1.2/share/locale/muse_it.ts muse-3.0.2+ds1/share/locale/muse_it.ts --- muse-2.1.2/share/locale/muse_it.ts 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/muse_it.ts 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,12709 @@ + + + + + @default + + Add Midi Track + Aggiungi traccia Midi + + + Add Drum Track + Aggiungi traccia batteria + + + Add Wave Track + Aggiungi traccia audio + + + Add Audio Output + Aggiungi uscita audio + + + Add Audio Group + Aggiungi gruppo audio + + + Add Audio Input + Aggiungi ingresso audio + + + Add Aux Send + Aggiungi mandata aux + + + Add Synth + Aggiungi Synth + + + Select project directory + Scegli directory per il progetto + + + Route + + + + dest + dest + + + name="%1"/ + + + + Warning: No output devices! + Avviso: Non ci sono dispositivi d'uscita! + + + Open midi config... + Apri configurazione Midi... + + + Empty ports + Porte vuote + + + <none> + <nulla> + + + Bad timing + Timing difettoso + + + channel="%1" + + + + + AboutBox + + AboutBox + su MusE + + + Version 2 + Versione 2 + + + (C) Copyright 1999-2012 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer e altri. +Visita http://www.muse-sequencer.org a proposito +di nuove versioni e informationi aggiuntive. + +Published under the GNU Public License + + + &Keep On Rocking! + &Keep On Rocking! + + + Alt+K + Alt+K + + + (C) Copyright 1999-2014 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer e altri. +Visita http://www.muse-sequencer.org a proposito +di nuove versioni e informationi aggiuntive. + +Published under the GNU Public License {1999-2014 ?} + + + Version info (replaced programmatically) + + + + (C) Copyright 1999-2015 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer e altri. +Visita http://www.muse-sequencer.org a proposito +di nuove versioni e informationi aggiuntive. + +Published under the GNU Public License {1999-2014 ?} {1999-2015 ?} + + + System information: + + + + TextLabel + + + + + AppearanceDialogBase + + MusE: Appearance settings + MusE: Impostazioni relative all'aspetto + + + Arranger + Arrangiatore + + + Parts + Parti + + + show names + mostra nomi + + + show events + mostra eventi + + + show Cakewalk Style + mostra stile Cakewalk + + + y-stretch + + + + Events + Eventi + + + note on + nota attiva + + + poly pressure + + + + controller + + + + aftertouch + aftertouch + + + pitch bend + + + + program change + + + + special + speciale + + + Background picture + Immagine di sfondo + + + add + aggiungi + + + remove + rimuovi + + + clear + cancella + + + show snap grid + mostra griglia di allineamento + + + Colors + Colori + + + Items + Oggetti + + + Palette + Tavolozza dei colori + + + add to palette + aggiungi a tavolozza + + + + B + B + + + S + S + + + H + H + + + V + V + + + G + G + + + R + R + + + Color name: + Nome colore: + + + Global opacity + Opacità globale + + + Style/Fonts + Stile/Caratteri + + + QT Theme + Tema QT + + + Windows + Windows + + + MusE + MusE + + + Metal + Metallo + + + Norwegian Wood + Legno norvegese + + + Platinum + Platino + + + CDE + CDE + + + Motif + Motif + + + Motif Plus + Motif Plus + + + May require restarting MusE for best results + Per ottenere risultati ottimali, potrebbe richiedere il riavvio di MusE + + + Style Sheet: + Foglio di stile: + + + ... + ... + + + Fonts + Caratteri + + + Family + Famiglia + + + Size + Dimensione + + + Font 1 + Tipo di carattere 1 + + + Font 2 + Tipo di carattere 2 + + + Font 3 + Tipo di carattere 3 + + + Font 0 + Tipo di carattere 0 + + + Bold + grassetto + + + Italic + corsivo + + + Font 4 + Tipo di carattere 4 + + + Font 5 + Tipo di carattere 5 + + + Font 6 + Tipo di carattere 6 + + + Apply + Applica + + + Ok + OK + + + Cancel + Annulla + + + Themes + + + + MusE color scheme + + + + current settings + + + + Change + + + + + ArrangerColumnsBase + + Configure arranger columns + Configura colonne arrangiatore + + + Columns: + Colonne: + + + Name: + Nome: + + + Controller type: + Tip controllo: + + + Midi controller type + Tipo controllo Midi + + + Control7 + Control7 + + + Control14 + Control14 + + + RPN + RPN + + + NRPN + NRPN + + + RPN14 + RPN14 + + + NRPN14 + NRPN14 + + + Pitch + Tono + + + Program + Programma + + + H-Ctrl + H-Ctrl + + + Midi controller number high byte + Numero controller Midi (byte alto) + + + L-Ctrl + L-Ctrl + + + Midi controller number low byte + Numero controller Midi (byte basso) + + + * + wild card + * + + + affect CCs at + influenza CC + + + begin of song + all'inizio della canzone + + + current position + alla posizione corrente + + + &Add + &Aggiungi + + + &Delete + &Cancella + + + Done + Fatto + + + + Awl::MidiVolEntry + + off + spento + + + db + db + + + + Awl::VolEntry + + off + spento + + + + ChooseSysexBase + + Dialog + + + + Hex: + + + + TextLabel + + + + Comment: + + + + &OK + + + + &Cancel + A&nnulla + + + + ClipListEditorBase + + MusE: ClipList + MusE: Lista clip + + + Name + Nome + + + Refs + Rif + + + Samplerate + Frequenza di campionamento + + + Len + Lun + + + Data + Dati + + + Clip Properties + Proprietà Clip + + + Pos: + Pos: + + + Len: + Lun: + + + + CommentBase + + Form1 + Modulo1 + + + Track Comment + Commento traccia + + + Track 1 + Traccia 1 + + + + ConfigMidiFileBase + + MusE: Config Midi File Import/Export + + + + Import: + Importa: + + + Split tracks into &parts + Dividi tracce in &parti + + + Alt+P + Alt+P + + + Split tracks into parts, or one single part + Dividi tracce in parti, o un'unica parte + + + Export: + Esporta: + + + 96 + 96 + + + 192 + 192 + + + 384 + 384 + + + Enable extended smf format (currently not implemented) + + + + Use &2-byte time signatures instead of standard 4 + + + + Alt+2 + Alt+2 + + + Copyright: + Copyright: + + + Format: + Formato: + + + Note: Format 0 uses the FIRST midi track's name/comment in the arranger + + + + Division: + + + + Save space by replacing note-offs with &zero velocity note-ons + + + + Alt+Z + + + + 0 (single track) + + + + 1 (multiple tracks) + + + + &OK + + + + &Cancel + A&nnulla + + + Default instrument: + + + + Device Name metas trump Port metas if both exist + + + + Instrument Name metas trump Mode sysexes if both exist + + + + Use new-style drum tracks + + + + Use old-style drum tracks + + + + Mode sysexes + + + + Instrument name metas + + + + Both + + + + Port metas + + + + Device name metas + + + + Export a Port/Device meta for format 0 + + + + + CopyOnWriteDialogBase + + Copy Wave Files + + + + Some sound files will be copied to the Project Directory, +either because they are not writable or because more +than one independent Wave Event shares them. +(If you prefer instead that the Wave Events are + inter-dependent, try using Clone Parts.) + +Multiple copies will be made in some cases. + +If no Project has been created yet, you will be asked to, +giving another chance to cancel. + + + + These files will be copied to the Project Directory: + + + + + CrescendoBase + + MusE: Crescendo/Decrescendo + MusE: Crescendo/Decrescendo + + + Range + Intervallo + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + Values + Valori + + + Start velocity + Velocity di partenza + + + % + % + + + End velocity + Velocity finale + + + Absolute + Assoluti + + + Relative + Relativi + + + OK + OK + + + Cancel + Annulla + + + + DeicsOnzeGui + + Save configuration + + + + Critical Error + + + + Cannot open file %1 + + + + Parsing error for file %1 + + + + Load category dialog + + + + Load set dialog + + + + Save set dialog + + + + New category + + + + Delete category + + + + Load category + + + + Save category + + + + Load set + + + + Save set + + + + Delete set + + + + New subcategory + + + + Delete subcategory + + + + Load subcategory + + + + Save subcategory + + + + New preset + + + + Delete preset + + + + Load preset + + + + Save preset + + + + No more category supported + + + + You can not add more categories + + + + Do you really want to delete %1 ? + + + + &Yes + + + + &No + + + + No category selected + + + + You must first select a category. + + + + Replace or add + + + + %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. + Do you want to replace it or to add it in the next free slot ? + + + + &Replace + + + + &Add + &Aggiungi + + + Download error + + + + There is no more free category slot. + + + + Save category dialog + + + + No more subcategory supported + + + + You can not add more subcategories + + + + No subcategory selected + + + + You must first select a subcategory. + + + + Load subcategory dialog + + + + %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. + Do you want to replace it or to add it in the next free slot ? + + + + There is no more free subcategory slot. + + + + Save subcategory dialog + + + + No more preset supported + + + + You can not add more presets + + + + No preset selected + + + + You must first select a preset. + + + + Load preset dialog + + + + %1 is supposed to be affected to the prog number %2, but there is already one on this slot. + Do you want to replace it or to add it in the next free slot ? + + + + There is no more free preset slot. + + + + Save preset dialog + + + + Browse set dialog + + + + Browse image dialog + + + + + DeicsOnzeGuiBase + + DeicsOnze + + + + &Preset + + + + Program numerous + + + + INITVOICE + + + + LBank + + + + Subcategory + + + + Bank numerous + + + + NONE + + + + HBank + + + + Category + + + + Prog + + + + Preset + + + + DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. + + + + &Global + + + + Pitch Envelope + + + + PL3 + + + + PL2 + + + + PL1 + + + + PR1 + + + + PR2 + + + + PR3 + + + + Pitch modulation depth + + + + LFO + + + + LFO Sync + + + + Pitch modulation sensitivity + + + + Pitch Modulation Sensitivity + + + + LFO Delay + + + + LFO delay + + + + LFO speed + + + + Amplitude modulation depth + + + + Amplitude modulation sensitivity + + + + Amplitude Modulation Sensitivity + + + + AMS + + + + LFO Waveform + + + + Pitch Modulation Depth + + + + PMD + + + + LFO Speed + + + + AMD + + + + Speed + + + + Delay + + + + PMS + + + + Modulation Matrix + + + + <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> +<b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> +<b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> +<b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> +<b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> +<b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> +<b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> +<b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> + + + + Op4 Feedback + + + + Feedback level of the operator 4 + + + + Transpose + + + + Op &1 + + + + Scaling 1 + + + + LS1 + + + + RS1 + + + + Rate Scaling + + + + Attack Rate of the operator 1 + + + + Level Scaling + + + + Amplitude Envelope 1 + + + + RR1 + + + + D1R1 + + + + D1L1 + + + + D2R1 + + + + Release Rate + + + + 2° Decay Rate + + + + 1° Decay Level + + + + 1° Decay Rate + + + + Attack Rate + + + + AR1 + + + + Detune, OSCWave, EGShift 1 + + + + DET1 + + + + Detune + + + + EG Shift + + + + 96dB + + + + 48dB + + + + 24dB + + + + 12dB + + + + Wave form + + + + Wave form 1 = <i>sin(<b>t</b>)</i><br> +Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> +Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> +Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> +Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> +Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> +Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> +Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> + + + + Sensitivity 1 + + + + KVS1 + + + + Amplitude Modulation Enable + + + + AME1 + + + + Keyboard Velocity Sensitivity + + + + Key Velocity Sensitivity + + + + EBS1 + + + + EG Bias Sensitivity + + + + Eg Bias Sensitivity + + + + Frequency 1 + + + + Coarse 1 + + + + Coarse Ratio + + + + Fine 1 + + + + Fine Ratio + + + + Freq 1 + + + + Fixed Frequency + + + + Toggle Fix Frequency + + + + FIX + + + + OUT 1 + + + + Output Volume + + + + Volume + + + + Op &2 + + + + Amplitude Envelope 2 + + + + D1R2 + + + + D1L2 + + + + D2R2 + + + + RR2 + + + + AR2 + + + + Frequency 2 + + + + Coarse 2 + + + + Fine 2 + + + + Freq 2 + + + + Scaling 2 + + + + LS2 + + + + RS2 + + + + OUT 2 + + + + Detune, OSCWave, EGShift 2 + + + + DET2 + + + + Sensitivity 2 + + + + EBS2 + + + + KVS2 + + + + AME2 + + + + Op &3 + + + + Amplitude Envelope 3 + + + + D1R3 + + + + D1L3 + + + + D2R3 + + + + RR3 + + + + AR3 + + + + Scaling 3 + + + + LS3 + + + + RS3 + + + + OUT 3 + + + + Frequency 3 + + + + Coarse 3 + + + + Fine 3 + + + + Freq 3 + + + + Detune, OSCWave, EGShift 3 + + + + DET3 + + + + Sensitivity 3 + + + + EBS3 + + + + KVS3 + + + + AME3 + + + + Op &4 + + + + amplitude Envelope 4 + + + + AR4 + + + + D1R4 + + + + D1L4 + + + + D2R4 + + + + RR4 + + + + Frequency 4 + + + + Coarse 4 + + + + Fine 4 + + + + Freq 4 + + + + Scaling 4 + + + + LS4 + + + + RS4 + + + + OUT 4 + + + + Detune, OSCWave, EGShift 4 + + + + DET4 + + + + Sensitivity 4 + + + + EBS4 + + + + KVS4 + + + + AME4 + + + + &Func + + + + Delay Pan Depth + + + + Delay Pan LFO Freq + + + + Delay Ch Send Level + + + + Channel Chorus + + + + Delay Feedback + + + + Delay On/Off, Return Level + + + + On + acceso + + + Delay Beat Ratio + + + + Delay BPM + + + + Foot Control + + + + Pitch Bend Range + + + + Pitch + Tono + + + Amplitude + + + + Modulation Wheel + + + + Breath Control + + + + Pitch Bias + + + + Envelope Bias + + + + After Touch + + + + Phony Mode + + + + POLY + + + + MONO + + + + Potamento + + + + Portamento Mode + + + + FINGER + + + + FULL + + + + PT + + + + Portamento Time + + + + C&horus + + + + Chorus Parameters + + + + Channel send level + + + + On/Off and Return level + + + + Select LADSPA plugin + + + + Change plugin + + + + &Reverb + + + + Reverb Parameters + + + + &Config + + + + Font Size + + + + Quality + + + + High + + + + Middle + + + + Low + + + + Ultra low + + + + Filter + + + + Save Mode (into the song) + + + + Save only the used presets + + + + Save the entire set + + + + Save the configuration + + + + Configuration File + + + + Save... + + + + Load... + + + + Save as default + + + + Colors + Colori + + + Text + + + + Background + + + + Edit Text + + + + Edit Background + + + + Red + + + + Blue + + + + Green + + + + Set Path + + + + Image in the background : + + + + Browse... + + + + Load the set at the initialization : + + + + Set Brightness, Detune, Attack and Release of the current channel to default + + + + Res. Ctrl + + + + Cut all notes off + + + + Panic! + + + + Number of Voices + + + + Number of voices + + + + Enable + + + + Channel + + + + Vol + Vol + + + Channel Ctrl + + + + Release + + + + Attack + + + + Brightness + + + + Modulation + + + + Pan + + + + + DelOverlapsBase + + MusE: Delete Overlaps + MusE: Cancella sovrapposizioni + + + Range + Intervallo + + + All Events + Tutti gli eventi + + + Selected Events + Eventi selezionati + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + OK + OK + + + Cancel + Annulla + + + + DidYouKnow + + Did you know? + Sapevi che...? + + + Don't show on startup + Non mostrare all'avvio + + + Next tip + Suggerimento successivo + + + Close + Chiudi + + + + DuplicateTracksBase + + Duplicate tracks + + + + Number of copies + Numero di copie + + + Copy all routes + + + + Default routing + + + + No routes + + + + Copy parts + + + + Copy effects rack plugins + + + + Copy plugin controllers + + + + Ok + OK + + + Cancel + Annulla + + + Copy drumlist + + + + Copy standard (vol, pan) and synth controllers + + + + + EditCtrlBase + + MusE: Edit Controller Event + + + + Time Position + + + + Available Controller: + + + + Create New Controller + + + + textLabel3 + + + + Value + Valore + + + Controller + + + + H-Bank + + + + L-Bank + + + + Program + Programma + + + off + spento + + + pushButton4 + + + + &OK + + + + &Cancel + A&nnulla + + + Note + + + + + EditGainBase + + MusE: Modify gain + + + + Gain + + + + 200% + 200% + + + 100% + 100% + + + 0% + 0% + + + &Reset + + + + Alt+R + + + + &Apply + &Applica + + + Alt+A + + + + &Cancel + A&nnulla + + + Alt+C + + + + + EditInstrumentBase + + MusE: Instrument Editor + + + + Instrument Name: + + + + Selected instrument name. + + + + List of defined instruments. + + + + Pa&tches + + + + List of groups and patches. + + + + Group/Patch + + + + Properties + + + + Name: + Nome: + + + Group or patch name + + + + High Bank: + + + + Patch high bank number + + + + Patch high bank number. --- means don't care. + + + + --- + dont care + + + + Low Bank: + + + + Patch low bank number + + + + Patch low bank number. --- means don't care. + + + + --- + + + + Program: + + + + Patch program number + + + + Drum + + + + GM patch + + + + If set, the patch is available in a 'GM' or 'NO' midi song type. + + + + GM + + + + GS patch + + + + If set, the patch is available in a 'GS' or 'NO' midi song type. + + + + GS + + + + XG patch + + + + If set, the patch is available in an 'XG' or 'NO' midi song type. + + + + XG + + + + Delete group or patch + + + + &Delete + &Cancella + + + Alt+D + + + + New patch + + + + New &Patch + + + + Alt+P + Alt+P + + + New group + + + + New &Group + + + + Alt+G + + + + List of defined controllers + + + + List of defined controllers. + + + + Name + + + + Type + + + + H-Ctrl + H-Ctrl + + + L-Ctrl + L-Ctrl + + + Min + + + + Max + + + + Def + + + + Midi controller name + + + + Type: + + + + Midi controller type + Tipo controllo Midi + + + Midi controller number high byte + Numero controller Midi (byte alto) + + + Midi controller number low byte (* means drum controller) + + + + Midi controller number low byte. +If low byte is * then the controller is a + 'drum controller'. For drum tracks and + GS/XG type songs and instruments. +Allows controllers for each instrument in + Muse's drum map. The low byte will be + replaced by the 'ANote' in the drum map. +Examples: The GS and XG instruments' + Drum controllers. + + + + * + wild card + * + + + Range: + + + + Min + + + + Minimum value. If negative, auto-translate. + + + + Minimum value. If the minimum value + is negative, the range will automatically + be translated to a positive range. + +Useful for controllers which should be + displayed with zero bias. For example, +'Pan': Minimum: -64 Maximum: 63 +True range: Min: 0 Max: 127 (bias = 64) +'CoarseTuning': Min: -24 Max: 23 +True range: Min: 40 Max: 87 (bias = 64) + +Bias is determined from controller type: +7-bit Controller7 / RPN: Bias = 64 +14-bit Controller14 / RPN14: Bias = 8192 + +Type 'Pitch' is the exception. It is biased + at zero, even with a negative minimum: +'Pitch': Min: -8192 Max: 8191 +True range: Min: -8192 Max: 8191 (bias 0) + + + + Max + + + + Maximum value + + + + Default: + + + + L-Bank + + + + off + spento + + + Progr. + + + + ??? + + + + H-Bank + + + + Default value. Off: No default. + + + + Default (initial) value. Off means no default. + +If a default value is chosen, the value will be sent + to the controller when the controller is added to + the song (in piano roll or event editor). When + the song is re-loaded, the value is sent again. +Otherwise the controller remains at its last value. +Controllers are also automatically added to a + song upon reception of a midi controller event. + +Caution! Watch out for controllers such as + 'Sustain' and 'ResetAllController' with default + values. You should probably turn 'off' their + default (in piano roll or drum edit, and + instrument editor). + + + + off + dont care + spento + + + Add common controller + + + + &Add + &Aggiungi + + + Alt+A + + + + Delete controller + + + + Create a new controller + + + + New &Controller + + + + Alt+C + + + + Null parameter number High byte + + + + If set, these 'null' parameter numbers will + be sent after each RPN/NRPN event. +This prevents subsequent 'data' events + from corrupting the RPN/NRPN controller. +Typically, set to 127/127, or an unused + RPN/NRPN controller number. + + + + Lo: + + + + Null parameter number Low byte + + + + S&ysEx + + + + SysEx List: + + + + Hex Entry: + + + + New SysE&x + + + + Alt+X + + + + Tools + + + + &File + + + + &Help + + + + &New + + + + New + Nuovo + + + Ctrl+N + + + + &Open... + + + + Open + + + + Ctrl+O + + + + &Save + + + + Save + + + + Ctrl+S + + + + Save &As... + + + + Save As + + + + new item + + + + What's this? + + + + Show in tracks: + + + + Midi + Midi + + + Drum&maps + + + + Patch Collections: + + + + &Copy + + + + &Remove + + + + &Up + + + + &Down + + + + Patch: + + + + from + + + + to + + + + Bank Hi: + + + + Bank Lo: + + + + Contro&llers + + + + Null Parameters: Hi: + + + + &Add Common... + + + + W + + + + Comment: + + + + &Initialization + + + + Instrument initialization sequence: + + + + &Add... + + + + &Change... + + + + &Close + + + + Close + Chiudi + + + + EditNoteDialogBase + + MusE: Enter Note + MusE: immetti nota + + + OK + OK + + + Cancel + Annulla + + + Length: + Lunghezza: + + + Time Position: + Posizione nel tempo: + + + Pitch: + Tono: + + + Velocity On: + Velocity On: + + + Velocity Off: + Velocity Off: + + + + EditSysexDialogBase + + MusE: Enter SysEx + + + + TimePosition: + + + + Comment: + + + + Name: + Nome: + + + &Select... + + + + &OK + + + + &Cancel + A&nnulla + + + + FLUIDGui + + FLUID: open Soundfile + FLUID: apri Soundfile + + + + FLUIDGuiBase + + Soundfont + Soundfont + + + + FLUIDSynthGuiBase + + FLUID Synth + + + + Load + + + + Delete + + + + Dump Info + + + + ID + + + + Fontname + + + + Chnl + + + + Soundfont + Soundfont + + + Drum Chnl + + + + Level + + + + Width + + + + Damping + + + + Room Size + + + + Reverb + + + + CHANNEL SETUP + + + + Sine + + + + Triangle + + + + Type + + + + Number + + + + Speed + + + + Depth + + + + Chorus + + + + Gain + + + + LOADED SOUNDFONTS + + + + + FileDialogButtons + + fdialogbuttons + + + + Global + + + + User + + + + Project + + + + read Midi Port +Configuration + + + + write window +states + + + + Home + + + + + FluidSynthGui + + Choose soundfont + Scegli un Soundfont + + + + GateTimeBase + + MusE: Modify Note Length + MusE: modifica lunghezza note + + + Range + Intervallo + + + All Events + Tutti gli eventi + + + Selected Events + Eventi selezionati + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + Values + Valori + + + Rate: + Rata: + + + Offset: + Offset: + + + % + % + + + lenNew = (lenOld * rate) + offset + lenNew = (lenOld * rate) + offset + + + OK + OK + + + Cancel + Annulla + + + + GlobalSettingsDialogBase + + MusE: Global Settings + MusE: Impostazioni globali + + + Application + Applicazione + + + Project directory + Directory progetti + + + Projects: + Progetti: + + + ... + ... + + + Views + Finestre + + + y-pos + pos-y + + + show + mostra + + + x-pos + pos-x + + + height + altezza + + + width + larghezza + + + Mixer A + Mixer A + + + Mixer B + Mixer B + + + Big Time + Timer gigante + + + Main Window + Finestra principale + + + Transport + Trasporto + + + set current values + imposta valori attuali + + + Cur + corrente + + + Start Muse + Avviamento di Muse + + + Start song + Canzone iniziale + + + start with last song + ultima canzone + + + Choose start song or template + Seleziona canzone iniziale o modello + + + Reset to default + Ripristina valori predefiniti + + + start with template + modello + + + start with song + canzone specifica + + + On Launch + All'avvio + + + show splash screen + mostra schermata iniziale + + + show "Did you know?" dialog + mostra finestra "Sapevi che...?" + + + Start template or song: + Modello o canzone di partenza: + + + Read MIDI Ports configuration from file, + or else automatically configure + Reimposta configurazione porte Midi dal file, +o altrimenti configura automaticamente + + + Read MIDI Ports configuration + Reimposta configurazione porte Midi + + + Audio + Audio + + + Mixer + Mixer + + + dB + dB + + + min. Meter Value + valore minimo dell'indicatore + + + min. Slider Val + valore minimo dello slider + + + Try to use Jack Freewheel + Tenta di utilizzare Jack Freewheel + + + Speeds bounce operations + Accelera operazioni di bounce + + + Use Jack Freewheel mode if possible. +This dramatically speeds bounce operations. + Usa la modalità Jack Freewheel se possibile. +Cio velocizza notevolmente operazioni di bounce. + + + Enable denormal protection + Abilita protezione da denormalizzazione + + + Enable output limiter + Attiva limitatore output + + + VST in-place + VST in-place + + + Enable VST in-place processing (restart required) + Attiva elaborazione VST in-place (richiede restart) + + + Enable VST in-place processing. Turn this off if + VST Ladspa effect rack plugins do not work or + feedback loudly, even if they are supposed to + be in-place capable. Setting requires a restart. + + + + Minimum control period + Periodo di controllo minimo + + + Minimum audio controller process period (samples). + + + + + Minimum audio controller process period (samples). +Adjusts responsiveness of audio controls and + controller graphs. Set a low value for fast, smooth + control. If it causes performance problems, set a + higher value. + + + + 1 + 1 + + + 2 + 2 + + + 4 + 4 + + + 8 + 8 + + + 16 + 16 + + + 32 + 32 + + + 64 + 64 + + + 128 + 128 + + + 256 + 256 + + + 512 + 512 + + + 1024 + 1024 + + + 2048 + 2048 + + + External Waveditor + Editor externo suoni + + + External Waveditor command + Comando per lanciare un editor esterno di suoni + + + Note: External editor opened from the internal editor. + Nota: l'editor esterno viene lanciato dall'editor interno. + + + Dummy Audio Driver (settings require restart) + Driver audio fittizio (modifiche a queste impostazioni richiedono il riavvio) + + + Sample rate + Frequenza di campionamento + + + Hz + Hz + + + Period size (Frames per period): + Dimensione periodo (fotogrammi per periodo): + + + Shorter periods give better midi playback resolution. + Periodi più brevi migliorano la risoluzione della riproduzione Midi. + + + Midi + Midi + + + Ticks + + + + RTC Resolution +(Ticks/Sec) + + + + 4096 + 4096 + + + 8192 + 8192 + + + 16384 + 16384 + + + 32768 + 32768 + + + Midi Resolution +(Ticks/Quarternote) + + + + 48 + 48 + + + 96 + 96 + + + 192 + 192 + + + 384 + 384 + + + 768 + 768 + + + 1536 + 1536 + + + 3072 + 3072 + + + 6144 + 6144 + + + 12288 + 12288 + + + Displayed Resolution +(Ticks/Quarternote) + + + + GUI Behaviour + Comportamento interfaccia grafica + + + Behavior + Comportamento + + + GUI Refresh Rate + frequenza aggiornamento interfaccia grafica + + + /sec + /sec + + + Use old-style stop shortcut: + Utilizza tasto stop diretto classico: + + + Move single armed track with selection + Sposta traccia singola con selezione + + + Use project save dialog + Utilizza finestra dialogo per salvataggio progetto + + + Some popup menus stay open (else hold Ctrl) + Tieni aperti alcuni menu popup (in alternativa premi Ctrl) + + + Allows some popup menus to stay open. +Otherwise, hold Ctrl to keep them open. + + + + In some areas, the middle mouse button decreases +values, while the right button increases. Users without a +middle mouse button can select this option to make the +left button behave like the middle button in such areas. + + + + Use left mouse button for decreasing values + Utilizza pulsante mouse sinistro per decrementare i valori + + + Shift + Right click sets left range marker + Shift + click destro imposta il demarcatore di sinistra dell'intervallo + + + Allow adding hidden tracks in track list menu + Permetti l'aggiunta di trace nascoste nel menu lista tracce + + + Unhide tracks when adding hidden tracks + Scopri tracce quando aggiungi trace nascoste + + + Smart focus + Focus intelligente + + + After editing, controls will return + focus to their respective canvas + Dopo le modifiche, i controlli ripassano +lo stato attivo al loro canvas + + + GUI Style + Stile interfaccia grafica + + + MDI-subwindowness and sharing menus + + + + Presets: + Impostazioni predefinite: + + + traditional MusE SDI + SDI classica MusE + + + Cakewalk-like MDI + MDI stile Cakewalk + + + Borland-/Mac-like MDI + MDI stile Borland/Mac + + + &Apply + &Applica + + + &Ok + &OK + + + &Cancel + A&nnulla + + + Auto save (every 5 minutes if not playing/recording) + + + + Record new style drum tracks + + + + Instrument initialization + + + + Send instrument initialization sequences + + + + Warn if instrument initialization sequences pending + + + + Send instrument controller default values if none in song, at init or rewind + + + + Warn if timer frequency is inadequate + + + + Track height + + + + Show newly created midi velocity graphs per-note + + + + px + + + + Enable borderless mouse. +For certain functions like zoom/pan. +Disable to use an alternate standard + method. + + + + + Enable borderless mouse. +For certain functions like zoom. +Disable to use an alternate standard + method. + + + + Borderless zoom/pan mouse (else use alternate method) + + + + Drum tracks + + + + start &with last song + + + + start with &template + + + + sta&rt with song + + + + &Record all instruments + + + + &Don't record hidden instruments + + + + Don'&t record muted instruments + + + + Don't record &hidden or muted instruments + + + + Scrollable submenus + + + + &Only offer old-style drumtracks + + + + Only offer new-style &drumtracks + + + + &Prefer old-style drumtracks + + + + Prefer &new-style drumtracks + + + + Warn if opening file versions different than current version + + + + + LegatoBase + + MusE: Legato + + + + Range + Intervallo + + + All Events + Tutti gli eventi + + + Selected Events + Eventi selezionati + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + Settings + + + + ticks + + + + Minimum Length + + + + Allow shortening notes + + + + OK + OK + + + Cancel + Annulla + + + + MITTransposeBase + + MusE: Midi Input Plugin: Transpose + + + + On + acceso + + + TriggerKey + + + + Transpose: + + + + +0 + + + + + MRConfigBase + + MusE: Midi Input Plugin: Remote Control + MusE: Midi Input Plugin: Controllo Remoto + + + Activate + Attiva + + + On + acceso + + + Actions + Azioni + + + Stop + Stop + + + Record + Registra + + + Goto Left Mark + Vai al marcatore di sinistra + + + Play + Riproduci + + + Insert rest (step rec) + Inserisci pausa (step rec) + + + + MdiSettingsBase + + Form + + + + GroupBox + + + + MDI subwin + + + + Shares menu when subwin + + + + Shares menu when free + + + + + MetronomeConfigBase + + MusE: Metronome Config + + + + Metronome + + + + Audio Beep + + + + Choose outputs... + + + + 50 + 50 + + + MIDI Click + + + + Midi Channel + + + + Measure Note + + + + Measure Velocity + + + + Beat Velocity + + + + Beat Note + + + + Midi Port + + + + Precount + + + + enable + + + + Bars + + + + From Mastertrack + + + + / + + + + Signature + + + + Prerecord + + + + Preroll + + + + Hint: Enable metronome in Transportpanel + + + + &Apply + &Applica + + + Alt+A + + + + &OK + + + + Alt+O + + + + &Cancel + A&nnulla + + + Alt+C + + + + Two samples (old samples) + + + + Four samples (new samples, with accents) + + + + Volume + + + + Audio master + + + + Meas + + + + Beat + + + + Accent1 + + + + Accent2 + + + + Sample + + + + Disabled since jack does not support it + + + + + MidiAudioControlBase + + Midi control + + + + Port: + + + + Channel: + + + + Control type: + + + + Hi: + + + + Lo: + + + + &Learn + + + + + MidiFilterConfigBase + + MusE: Midi Input Filter + + + + Record Filter + + + + Note On + + + + Poly Pressure + + + + Controller + + + + Program Change + + + + After Touch + + + + Pitch Bend + + + + Sysex + + + + Thru Filter + + + + Controller Filter + + + + Channel Filter + + + + 14 + 14 + + + 10 + 10 + + + 6 + 6 + + + 12 + 12 + + + 4 + 4 + + + 2 + 2 + + + 9 + 9 + + + 8 + 8 + + + 3 + 3 + + + 13 + 13 + + + 15 + 15 + + + 16 + 16 + + + 7 + 7 + + + 11 + 11 + + + 5 + 5 + + + 1 + 1 + + + + MidiInputTransformDialogBase + + MusE: Midi Input Transformator + + + + Filter + + + + All + + + + Equal + + + + Unequal + + + + Note + + + + Poly Pressure + + + + Control Change + + + + Aftertouch + + + + Pitch Bend + + + + NRPN + NRPN + + + RPN + RPN + + + Value 2 + + + + Value 1 + + + + Event Type + + + + Ignore + + + + Higher + + + + Lower + + + + Inside + + + + Outside + + + + Channel + + + + Port + Porta + + + Processing + + + + Keep + + + + Fix + + + + Plus + + + + Minus + + + + Multiply + + + + Divide + + + + Invert + Inverti selezione + + + ScaleMap + + + + Flip + + + + Dyn + + + + Random + + + + Modules + + + + 1 + 1 + + + 2 + 2 + + + 3 + 3 + + + 4 + 4 + + + enable modul 1 + + + + enable modul 2 + + + + enable modul 3 + + + + enable modul 4 + + + + Preset + + + + Name: + Nome: + + + Comment: + + + + Function + + + + create new preset + + + + &New + + + + delete preset + + + + &Delete + &Cancella + + + &Dismiss + + + + PresetList + + + + Program + Programma + + + Toggle + + + + + MidiSyncConfigBase + + MusE: Midi Sync + + + + Apply + Applica + + + Ok + OK + + + Cancel + Annulla + + + MTC + + + + Type: + + + + 24 + 24 + + + 25 + 25 + + + 30D + 30D + + + 30N + 30N + + + Offset: + Offset: + + + hour + + + + h + + + + minute + minuto + + + m + + + + second + secondo + + + s + + + + frame + fotogramma + + + f + + + + subframe + frazione di fotogramma + + + Sync receiving and sending + + + + Send and receive Jack transport + + + + Send and receive Jack transport information, + including stop, start and position. + + + + Use Jack transport + + + + Make MusE the Jack transport Timebase Master + + + + Make MusE the Jack transport Timebase Master. +Allows Jack to show time as + MusE Bars, Beats, and Ticks. +MusE will try to become master, but other + Jack clients can also take over later. +You can always click here again for Master. + + + + Jack transport Timebase Master + + + + Control MusE timing by external midi clock or MTC sync + + + + When in slave mode, tempo is + controlled externally. +MusE can sync to midi clock, or MTC quarter frame sync. +Enabled inputs in the list will + be in effect (RMC, RMMC, RMTC). + + + + Slave to external sync + + + + Averaging applied to recorded external tempo changes. + + + + 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. + + + + Tempo record averaging + + + + bpm + + + + Tempo record quantization + + + + Send start to first clock delay + + + + Allows 'slow sync' devices time + to synchronize to MusE. This value is the + delay from sending start to sending + the first clock. + + + + ms + + + + Send sync delay + + + + Note: Sync delay and MTC sync currently not fully implemented + + + + + MidiTrackInfoBase + + MusE: TrackInfo + + + + output port + + + + output channel + + + + all midi events are sent to this output channel + + + + Out ch + + + + input routing + + + + output routing + + + + input detect + + + + Input detect indicator. Detects all note on-off, controller, aftertouch, + program change, and pitchbend (but not sysex or realtime) events + on the selected channels, on the selected midi ports. + + + + W + + + + Midi thru + + + + Pass input events through ('thru') to output. + + + + off + spento + + + Transp. + + + + Delay + + + + % + % + + + Length + + + + Velocity + + + + Compr. + + + + Channel Info + + + + Select instrument patch + + + + <unknown> + <sconosciuto> + + + Rec: + + + + Add all settings to song + + + + All + + + + Bank Select MSB. Ctrl-double-click on/off. + + + + Bank Select LSB. Ctrl-double-click on/off. + + + + Program. Ctrl-double-click on/off. + + + + Volume. Ctrl-double-click on/off. + + + + Change stereo position. Ctrl-double-click on/off. + + + + H-Bank + + + + L-Bank + + + + Add bank + prog settings to song + + + + Prog + + + + Add vol setting to song + + + + Vol + Vol + + + Add pan setting to song + + + + Pan + + + + Change note length in percent of actual length + + + + Offset playback of notes before or after actual note + + + + Transpose notes up or down + + + + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + + + + Compress the notes velocity range, in percent of actual velocity + + + + Select instrument + + + + + MidiTransformDialogBase + + MusE: Midi Transformator + + + + &New + + + + &Delete + &Cancella + + + &Apply + &Applica + + + &OK + + + + &Cancel + A&nnulla + + + PresetList + + + + Processing + + + + Event Type + + + + Keep + + + + Fix + + + + Note + + + + Poly Pressure + + + + Control Change + + + + Aftertouch + + + + Pitch Bend + + + + NRPN + NRPN + + + RPN + RPN + + + Plus + + + + Minus + + + + Multiply + + + + Divide + + + + Value 2 + + + + Invert + Inverti selezione + + + ScaleMap + + + + Flip + + + + Dyn + + + + Random + + + + Value 1 + + + + Length + + + + Position + + + + Filter + + + + All + + + + Equal + + + + Unequal + + + + Ignore + + + + Higher + + + + Lower + + + + Inside + + + + Outside + + + + Bar Range + + + + Preset + + + + Name: + Nome: + + + Comment: + + + + Range + Intervallo + + + process all events + + + + selected tracks + + + + inside loop + + + + Function + + + + Select + + + + Quantize + Quantizza + + + Delete + + + + Transform + + + + Insert + + + + Copy + Copia + + + Extract + + + + Quantize Value + + + + + MixdownFileDialogBase + + MusE: Set Mixdown Wavefile + + + + &OK + + + + &Cancel + A&nnulla + + + File Path + + + + Channel + + + + Stereo + + + + Mono + + + + 5.1 + 5.1 + + + wav,16 Bit + + + + wav, 24 Bit + + + + wav, 32 Bit (float) + + + + Format + + + + + MoveBase + + MusE: Move Notes + MusE: Sposta note + + + Range + Intervallo + + + All Events + Tutti gli eventi + + + Selected Events + Eventi selezionati + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + Value + Valore + + + Move by + Sposta di + + + ticks + + + + OK + OK + + + Cancel + Annulla + + + + MusECore::AudioPreviewDialog + + Auto play + + + + Stop + Stop + + + Play + Riproduci + + + + MusECore::Song + + Jack shutdown! + + + + Jack has detected a performance problem which has lead to +MusE being disconnected. +This could happen due to a number of reasons: +- a performance issue with your particular setup. +- a bug in MusE (or possibly in another connected software). +- a random hiccup which might never occur again. +- jack was voluntary stopped by you or someone else +- jack crashed +If there is a persisting problem you are much welcome to discuss it +on the MusE mailinglist. +(there is information about joining the mailinglist on the MusE + homepage which is available through the help menu) + +To proceed check the status of Jack and try to restart it and then . +click on the Restart button. + + + + Automation: + + + + previous event + + + + next event + + + + set event + + + + add event + + + + erase event + + + + erase range + + + + clear automation + + + + Midi control + + + + Assign + + + + Clear + + + + Clear all controller events? + + + + &Ok + &OK + + + &Cancel + A&nnulla + + + MusE: Tempo list + + + + External tempo changes were recorded. +Transfer them to master tempo list? + + + + MusE - external script failed + + + + MusE was unable to launch the script, error message: +%1 + + + + Do you want to process ALL or only selected events? + + + + &Selected + + + + &All + + + + Und&o + + + + Re&do + + + + + MusEDialog::PasteDialog + + %n quarter(s) + %n quarters + + + + quarter + quarter + + + quarters + quarters + + + + MusEDialog::PasteEventsDialog + + %n quarter(s) + %n quarters + + + + quarter + quarter + + + quarters + quarters + + + + MusEGui::Appearance + + Main application font, and default font for any + controls not defined here. + + + + Mixer strips and effects racks. Midi track info panel. +Midi control panel entry box. + + + + Transport controls. + + + + Time scale upper, and time signature. +Controller graph and S/X buttons. + + + + Time scale lower, and arranger part name overlay. + + + + Tempo scale, and markers. + + + + Mixer labels. Auto-font-sizing up to chosen font size. +Word-breaking but only with spaces. + + + + Maximum mixer label auto-font-sizing font size. + + + + Global opacity (opposite of transparency). + + + + Standard + + + + Custom + + + + Keep Qt system style + + + + MusE: load image + + + + Select style sheet + + + + Qt style sheets (*.qss) + + + + Do you really want to reset colors to theme default? + + + + &Ok + &OK + + + &Cancel + A&nnulla + + + + MusEGui::Arranger + + Enable Recording + + + + Mute/Off Indicator + + + + Solo Indicator + + + + Track Type + Tipo di traccia + + + Track Name + Nome traccia + + + Midi output channel number or audio channels + Numero canali Midi d'uscita o canali audio + + + Midi output port or synth midi port + Porta Midi d'uscita o porta Midi sintetizzatore + + + Time Lock + + + + Automation parameter selection + Selezione parametri automazione + + + Notation clef + Chiave notazione + + + Enable recording. Click to toggle. + Abilita registrazione. + + + Mute indicator. Click to toggle. +Right-click to toggle track on/off. +Mute is designed for rapid, repeated action. +On/Off is not! + + + + Solo indicator. Click to toggle. +Connected tracks are also 'phantom' soloed, + indicated by a dark square. + + + + Track type. Right-click to change + midi and drum track types. + + + + Track name. Double-click to edit. +Right-click for more options. + + + + Midi/drum track: Output channel number. +Audio track: Channels. +Mid/right-click to change. + + + + Midi/drum track: Output port. +Synth track: Assigned midi port. +Left-click to change. +Right-click to show GUI. + + + + Time lock + + + + Notation clef. Select this tracks notation clef. + + + + Arranger + Arrangiatore + + + Cursor + + + + Off + spento + + + Bar + + + + Snap + + + + Len + Lun + + + song length - bars + + + + Pitch + Tono + + + midi pitch + + + + global midi pitch shift + + + + Tempo + + + + midi tempo + + + + N + + + + TrackInfo + + + + R + R + + + M + M + + + S + S + + + C + + + + Track + + + + Port + Porta + + + Ch + Ch + + + T + + + + Automation + + + + Clef + + + + + MusEGui::ArrangerColumns + + Control7 + Control7 + + + Control14 + Control14 + + + RPN + RPN + + + NPRN + + + + RPN14 + RPN14 + + + NRPN14 + NRPN14 + + + Pitch + Tono + + + Program + Programma + + + Aftertouch + + + + + MusEGui::ArrangerView + + MusE: Arranger + + + + C&ut + + + + &Copy + + + + Copy in range + + + + &Paste + + + + Paste c&lone + + + + &Insert Empty Measure + + + + Delete Selected Tracks + + + + Duplicate Selected Tracks + + + + Shrink selected parts + + + + Expand selected parts + + + + Add Track + + + + Select + + + + Select &All + + + + &Deselect All + + + + Invert &Selection + + + + &Inside Loop + + + + &Outside Loop + + + + All &Parts on Track + + + + Score + + + + all tracks in one staff + + + + one staff per track + + + + New score window + + + + Pianoroll + + + + Drums + + + + List + + + + Wave + + + + Mastertrack + + + + Graphic + + + + Midi &Transform + + + + Global Cut + + + + Global Insert + + + + Global Split + + + + Global Cut - selected tracks + + + + Global Insert - selected tracks + + + + Global Split - selected tracks + + + + &Edit + &Modifica + + + &Structure + + + + Functions + + + + &Quantize Notes + + + + Change note &length + + + + Change note &velocity + + + + Crescendo/Decrescendo + Crescendo/Decrescendo + + + Transpose + + + + Erase Events (Not Parts) + + + + Move Events (Not Parts) + + + + Set Fixed Note Length + + + + Delete Overlapping Notes + + + + Legato + + + + Window &Config + &Configura finestra + + + Configure &custom columns + + + + New + Nuovo + + + Changed Settings + + + + Unfortunately, the changed arranger column settings +cannot be applied while MusE is running. +To apply the changes, please restart MusE. Sorry. +(we'll try to fix that) + + + + D&elete + + + + Paste to selected &track + + + + Paste clone to selected trac&k + + + + Paste (show dialo&g) + + + + Purge hidden events from selected parts + + + + Remove track(s) + + + + Are you sure you want to remove this track(s)? + + + + + MusEGui::AudioMixerApp + + &Create + + + + &View + + + + Routing + + + + Show Midi Tracks + + + + Show Drum Tracks + + + + Show Wave Tracks + + + + Show Inputs + + + + Show Outputs + + + + Show Groups + + + + Show Auxs + + + + Show Synthesizers + + + + Show New Style Drum Tracks + + + + + MusEGui::AudioStrip + + panorama + + + + aux send level + + + + 1/2 channel + + + + Pre + + + + pre fader - post fader + + + + dB + dB + + + record + + + + mute + + + + record downmix + + + + solo mode + + + + off + spento + + + input routing + + + + output routing + + + + Off + spento + + + Read + + + + Touch + + + + Write + + + + automation type + + + + calibration gain + + + + + MusEGui::BigTime + + format display + formato display + + + bar + misura + + + beat + battuta + + + tick + frazione + + + minute + minuto + + + second + secondo + + + frame + fotogramma + + + subframe + frazione di fotogramma + + + MusE: Bigtime + MusE: Timer gigante + + + + MusEGui::Canvas + + Tools: + + + + + MusEGui::ClipListEdit + + MusE: Clip List Editor + + + + Window &Config + &Configura finestra + + + + MusEGui::ComboQuant + + Off + spento + + + + MusEGui::CtrlCanvas + + Drawing hint: Hold Ctrl to affect only existing events + + + + Make the current part's track match the selected drumlist entry + + + + Use pencil or line tool to draw new events + + + + + MusEGui::CtrlPanel + + S + S + + + select controller + + + + X + + + + remove panel + + + + manual adjust + + + + ctrl-double-click on/off + + + + off + spento + + + all/per-note velocity mode + + + + + MusEGui::DList + + hide this instrument + + + + show this instrument + + + + this turns a grayed out eye into a blue eye + + + + + MusEGui::DrumCanvas + + Moving items failed + + + + The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. +Changing the current part with ALT+LEFT/RIGHT may help. + + + + Creating event failed + + + + Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. +Select the destination part, then try again. + + + + Recording event failed + + + + Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. +Select the destination part, then try again. + + + + Internal error + + + + Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. + + + + + MusEGui::DrumEdit + + mute instrument + disattiva audio strumento + + + sound name + nome suono + + + volume percent + percentuale volume + + + quantisation + quantizzazione + + + this input note triggers the sound + questa nota d'ingresso attiva il suono + + + note length + lunghezza della nota + + + this is the note which is played + questa è la nota che viene suonata + + + output channel (hold ctl to affect all rows) + canale di output (premi ctl per modificare tutte le righe) + + + output port (hold ctl to affect all rows) + porta di output (premi ctl per modificare tutte le righe) + + + Load Map + Carica mappa + + + Save Map + Salva mappa + + + Reset GM Map + Reimposta mappa GM + + + &Edit + &Modifica + + + Cut + Taglia + + + Copy + Copia + + + Copy events in range + Copia eventi nell'intervallo + + + Paste + Incolla + + + Paste (with Dialog) + Incolla (con dialogo) + + + Delete Events + Cancella eventi + + + &Select + &Seleziona + + + Select All + Seleziona tutto + + + Select None + Annulla selezione + + + Invert + Inverti selezione + + + Inside Loop + All'interno del loop + + + Outside Loop + All'esterno del loop + + + Previous Part + Parte precedente + + + Next Part + Parte successiva + + + Fu&nctions + Fu&nzioni + + + Re-order list + Riordina lista + + + Set Fixed Length + Imposta lunghezza fissa + + + Modify Velocity + Modifica velocity + + + Crescendo/Decrescendo + Crescendo/Decrescendo + + + Quantize + Quantizza + + + Erase Event + Cancella evento + + + Move Notes + Sposta note + + + Delete Overlaps + Cancella sovrapposizioni + + + &Plugins + &Plugin + + + Window &Config + &Configura finestra + + + Drum tools + Strumenti batteria + + + Load Drummap + Carica mappa batteria + + + Store Drummap + Salva mappa batteria + + + Step Record + Registra passo per passo + + + Midi Input + Ingresso Midi + + + cursor tools + strumenti cursore + + + Set step size for cursor edit + Imposta dimensione passo per editing con il cursore + + + ctrl + ctrl + + + Add Controller View + Aggiungi visualizzazione controller + + + M + M + + + Sound + Suono + + + Vol + Vol + + + QNT + QNT + + + E-Note + E-Note + + + Len + Lun + + + A-Note + A-Note + + + Ch + Ch + + + Port + Porta + + + LV1 + LV1 + + + LV2 + LV2 + + + LV3 + LV3 + + + LV4 + LV4 + + + Muse: Load Drum Map + Muse: Carica mappa batteria + + + MusE: Store Drum Map + Muse: Salva mappa batteria + + + Drum map + Mappa batteria + + + Reset the drum map with GM defaults? + Reimposta mappa batteria predefinita GM? + + + hide instrument + + + + override track output channel (hold ctl to affect all rows) + + + + override track output port (hold ctl to affect all rows) + + + + control + meta keys: draw velocity level 1 + + + + meta key: draw velocity level 2 + + + + draw default velocity level 3 + + + + meta + alt keys: draw velocity level 4 + + + + override track output channel (ctl: affect all rows) + + + + override track output port (ctl: affect all rows) + + + + Re-order map + + + + Group + + + + Don't group + + + + Group by channel + + + + Group maximally + + + + Show/Hide + + + + Also show hidden instruments + + + + Show all instruments + + + + Hide all instruments + + + + Only show used instruments + + + + Only show instruments with non-empty name or used instruments + + + + Drum map tools + + + + Play Events + + + + Cursor step: + + + + H + H + + + Not all parts are displayed + + + + You selected both old-style-drumtracks and others (that is: new-style or midi tracks), but they cannot displayed in the same drum edit. +I'll only display the old-style drumtracks in this editor, dropping the others. + + + + + MusEGui::EditEventDialog + + Ok + OK + + + Cancel + Annulla + + + + MusEGui::EditInstrument + + MusE: Create file failed + + + + MusE: Write File failed + + + + MusE: Save Instrument Definition + + + + Instrument Definition (*.idf) + + + + MusE: Save instrument as + + + + Enter a new unique instrument name: + + + + The user instrument '%1' already exists. This will overwrite its .idf instrument file. +Are you sure? + + + + MusE: Bad instrument name + + + + Please choose a unique instrument name. +(The name might be used by a hidden instrument.) + + + + MusE: Bad patch name + + + + Please choose a unique patch name + + + + MusE: Bad patchgroup name + + + + Please choose a unique patchgroup name + + + + MusE: Bad controller name + + + + Please choose a unique controller name + + + + MusE + MusE + + + The current Instrument contains unsaved data +Save Current Instrument? + + + + &Save + + + + &Nosave + + + + &Abort + + + + Control7 + Control7 + + + Control14 + Control14 + + + RPN + RPN + + + NPRN + + + + RPN14 + RPN14 + + + NRPN14 + NRPN14 + + + Pitch + Tono + + + Program + Programma + + + PolyAftertouch + + + + Aftertouch + + + + Name + Nome + + + Vol + Vol + + + Quant + + + + E-Note + E-Note + + + Len + Lun + + + A-Note + A-Note + + + LV1 + LV1 + + + LV2 + LV2 + + + LV3 + LV3 + + + LV4 + LV4 + + + Tick + + + + Data + Dati + + + New controller: Error + + + + Error! All control numbers are taken up! +Clean up the instrument! + + + + + MusEGui::EditMetaDialog + + MusE: Enter Meta Event + + + + Time Position + + + + Meta Type + + + + Enter Hex + + + + + MusEGui::EditToolBar + + select Pointer Tool: +with the pointer tool you can: + select parts + move parts + copy parts + + + + select Pencil Tool: +with the pencil tool you can: + create new parts + modify length of parts + + + + select Delete Tool: +with the delete tool you can delete parts + + + + select Cut Tool: +with the cut tool you can split a part + + + + select Glue Tool: +with the glue tool you can glue two parts + + + + select Score Tool: + + + + + select Quantize Tool: +insert display quantize event + + + + select Drawing Tool + + + + select Muting Tool: +click on part to mute/unmute + + + + Manipulate automation + + + + pointer + + + + pencil + + + + eraser + + + + cutter + + + + score + + + + glue + + + + quantize + + + + draw + + + + mute parts + + + + edit automation + + + + cursor + + + + Edit Tools + + + + select Cursor (tracker mode) tool: +with the cursor tool you can: + navigate with arrow keys + use VBNM to place notes + change step with 0 and 9 + + + + select Range Tool + + + + select Panning Tool + + + + select Zoom Tool + + + + range + + + + pan + + + + zoom + + + + + MusEGui::EffectRack + + effect rack + + + + new + + + + change + + + + move up + + + + move down + + + + remove + rimuovi + + + bypass + + + + show gui + + + + show native gui + + + + save preset + + + + MusE: Save Preset + + + + Replace effect + + + + Do you really want to replace the effect %1? + + + + Presets + + + + + MusEGui::GlobalSettingsConfig + + MusE: Choose start template or song + MusE: Scegli un modello o una canzone di partenza + + + + MusEGui::Header + + Track Info Columns + Colonne Info Tracce + + + + MusEGui::LMaster + + MusE: Mastertrack + + + + &Edit + &Modifica + + + Insert Tempo + + + + Insert Signature + + + + Insert Key + + + + Edit Positon + + + + Edit Value + + + + Delete Event + + + + Window &Config + &Configura finestra + + + Edit tools + + + + Tempo + + + + Timesig + + + + Key + + + + new tempo + + + + new signature + + + + new key + + + + Meter + + + + Time + + + + Type + + + + Value + Valore + + + Reposition of the initial tempo and signature events is not allowed + + + + MusE: List Editor + + + + Input error, conversion not OK or value out of range + + + + Reposition of tempo and signature events to start position is not allowed! + + + + + MusEGui::ListEdit + + insert Note + + + + insert SysEx + + + + insert Ctrl + + + + insert Meta + + + + &Edit + &Modifica + + + Cut + Taglia + + + Copy + Copia + + + Paste + Incolla + + + Delete Events + Cancella eventi + + + Window &Config + &Configura finestra + + + Insert tools + + + + Tick + + + + Bar + + + + Type + + + + Ch + Ch + + + Val A + + + + Val B + + + + Val C + + + + Len + Lun + + + Comment + + + + Increase Tick + + + + Decrease Tick + + + + MusE: List Editor + + + + + MusEGui::MPConfig + + Default input connections + + + + Are you sure you want to apply to all existing midi tracks now? + + + + Default output connections + + + + MusE: bad device name + + + + please choose a unique device name + + + + in + + + + out + + + + Toggle all + + + + Change all tracks now + + + + Create Jack device + + + + Port Number + + + + Enable gui + + + + Enable reading + + + + Enable writing + + + + Port instrument + + + + Midi device name. Click to edit (Jack) + + + + Connections from Jack Midi outputs + + + + Connections to Jack Midi inputs + + + + Auto-connect these channels to new midi tracks + + + + Auto-connect new midi tracks to these channels + + + + Auto-connect new midi tracks to this channel + + + + Device state + + + + Enable gui for device + + + + Enable reading from device + + + + Enable writing to device + + + + Name of the midi device associated with this port number. Click to edit Jack midi name. + + + + Instrument connected to port + + + + Connections from Jack Midi output ports + + + + Connections to Jack Midi input ports + + + + Auto-connect these channels, on this port, to new midi tracks. + + + + Connect new midi tracks to these channels, on this port. + + + + Connect new midi tracks to this channel, on this port. + + + + State: result of opening the device + + + + Port + Porta + + + GUI + + + + I + + + + O + + + + Instrument + + + + Device Name + + + + In routes + + + + Out routes + + + + Def in ch + + + + Def out ch + + + + State + + + + <unknown> + <sconosciuto> + + + <none> + <nulla> + + + Remove + + + + Midi device name + + + + Midi device type + + + + Connections from Jack Midi + + + + Connections to Jack Midi + + + + Result of opening the device: +OK: Assigned to a port and in use +Closed: Unassigned to a port, or closed +R/W Error: Unable to open for read or write +Unavailable: USB midi unplugged, or external + application not running, or synth plugin + not installed etc. +(Jack Midi devices have 'unavailable ports' + in the routes columns.) +Unavailable devices or ports can be purged + with 'Remove' or with the advanced router. + + + + Enable Graphical User Interface for device + + + + Connections from Jack Midi ports + + + + Connections to Jack Midi ports + + + + Type + + + + In + + + + Out + + + + + MusEGui::MTScale + + bar scale + scala barre + + + + MusEGui::MTScaleFlo + + bar scale + scala barre + + + + MusEGui::MarkerView + + MusE: Marker + + + + add marker + + + + delete marker + + + + &Edit + &Modifica + + + Window &Config + &Configura finestra + + + edit tools + + + + Bar:Beat:Tick + + + + Hr:Mn:Sc:Fr:Sf + + + + Lock + + + + Text + + + + Marker Properties + + + + + MusEGui::MasterEdit + + MusE: Mastertrack + + + + Window &Config + &Configura finestra + + + Enable master + + + + Enable + + + + Enable usage of master track + + + + Info + + + + Cursor + + + + time at cursor position + + + + tempo at cursor position + + + + Off + spento + + + Bar + + + + Snap + + + + CurPos + + + + tempo at current position + + + + time signature at current position + + + + + MusEGui::MidiAudioControl + + Control7 + Control7 + + + Control14 + Control14 + + + RPN + RPN + + + NPRN + + + + RPN14 + RPN14 + + + NRPN14 + NRPN14 + + + Pitch + Tono + + + Program + Programma + + + Aftertouch + + + + + MusEGui::MidiInputTransformDialog + + New + Nuovo + + + + MusEGui::MidiStrip + + off + spento + + + ctrl-double-click on/off + + + + VariationSend + + + + Var + + + + ReverbSend + + + + Rev + + + + ChorusSend + + + + Cho + + + + dB + dB + + + Pan/Balance + + + + Pan + + + + record + + + + mute + + + + solo mode + + + + input routing + + + + output routing + + + + + MusEGui::MidiSyncConfig + + Port Number + + + + Name of the midi device associated with this port number + + + + Midi clock input detected + + + + Midi tick input detected + + + + Midi real time input detected + + + + MMC input detected + + + + MTC input detected + + + + Detected SMPTE format + + + + Receive id number. 127 = Global. Double click to edit. + + + + Accept midi clock input + + + + Accept midi real time input + + + + Accept MMC input + + + + Accept MTC input + + + + Receive start rewinds before playing + + + + Transmit id number. 127 = Global. Double click to edit. + + + + Send midi clock output + + + + Send midi realtime output + + + + Send MMC output + + + + Send MTC output + + + + Midi clock input detected. +Current port actually used is red. +Click to force a port to be used. + + + + Midi realtime input detected, including + start/stop/continue, and song position. + + + + MMC input detected, including stop/play/deferred play, and locate. + + + + MTC input detected, including forward quarter-frame sync and full-frame locate. +Current port actually used is red. Click to force a port to be current. + + + + Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop +Detects format of MTC quarter and full frame, and MMC locate. + + + + Receive id number. 127 = global receive all, even if not global. + + + + Accept midi clock input. Only one input is used for clock. +Auto-acquire: If two or more port realtime inputs are enabled, + the first clock detected is used, until clock is lost, + then another can take over. Best if each turns off its clock + at stop, so MusE can re-acquire the clock from another port. +Click on detect indicator to force another. + + + + Accept midi realtime input, including + start/stop/continue, and song position. +Non-clock events (start,stop etc) are + accepted by ALL enabled ports. +This means you may have several master + devices connected, and muse will accept + input from them. + + + + Accept MMC input, including stop/play/deferred play, and locate. + + + + Accept MTC input, including forward quarter-frame sync and full-frame locate. +See 'rc' column for more help. + + + + When start is received, rewind before playing. +Note: It may be impossible to rewind fast + enough to synchronize with the external device. + + + + Transmit id number. 127 = global transmit to all. + + + + Send midi clock output. If 'Slave to External Sync' is chosen, + muse can re-transmit clock to any other chosen ports. + + + + Send midi realtime output, including start/stop/continue, + and song position. If 'Slave to external sync' is chosen, + muse can re-transmit midi realtime input messages to any + other chosen ports. This means you may have several slave + devices connected, and muse can re-send realtime messages + to any or all of them. + + + + Port + Porta + + + Device Name + + + + c + + + + k + + + + r + + + + m + + + + t + + + + type + + + + rid + + + + rc + + + + rr + + + + rm + + + + rt + + + + rw + + + + tid + + + + tc + + + + tr + + + + tm + + + + tt + + + + None + + + + Tiny + + + + Small + + + + Large + + + + Large with pre-detect + + + + MusE + MusE + + + Settings have changed +Apply sync settings? + + + + &Apply + &Applica + + + &No + + + + &Abort + + + + <none> + <nulla> + + + + MusEGui::MidiTrackInfo + + <unknown> + <sconosciuto> + + + + MusEGui::MidiTransformerDialog + + New + Nuovo + + + + MusEGui::MixdownFileDialog + + Wave Files (*.wav);;All Files (*) + File Wave (*.wav);;Tutti i File (*) + + + + MusEGui::MusE + + Failed to start audio! + + + + Was not able to start audio, check if jack is running. + + + + + Timeout waiting for audio to run. Check if jack is running. + + + + + Und&o + + + + Re&do + + + + undo last change to song + + + + redo last undo + + + + Loop + + + + loop between left mark and right mark + + + + Punchin + + + + record starts at left mark + + + + Punchout + + + + record stops at right mark + + + + Start + + + + rewind to start position + + + + Rewind + + + + rewind current position + + + + Forward + + + + move current position + + + + Stop + Stop + + + stop sequencer + + + + Play + Riproduci + + + start sequencer play + + + + Record + Registra + + + to record press record and then play + + + + Panic + + + + send note off to all midi channels + + + + &New + + + + Create New Song + + + + &Open + + + + Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. + + + + Open &Recent + + + + &Save + + + + Click this button to save the song you are editing. You will be prompted for a file name. +You can also select the Save command from the File menu. + + + + Save &As + + + + Import Midifile + + + + Export Midifile + + + + Import Part + + + + Import Audio File + + + + Find unused wave files + + + + &Quit + + + + Song Info + + + + Transport Panel + + + + Bigtime Window + + + + Mixer A + Mixer A + + + Mixer B + Mixer B + + + Cliplist + + + + Marker View + + + + Arranger View + + + + Fullscreen + + + + &Plugins + &Plugin + + + Edit Instrument + + + + Input Plugins + + + + Transpose + + + + Midi Input Transform + + + + Midi Input Filter + + + + Midi Remote Control + + + + Rhythm Generator + + + + Reset Instr. + + + + Init Instr. + + + + Local Off + + + + Bounce to Track + + + + Bounce to File + + + + Restart Audio + + + + Mixer Automation + + + + Take Snapshot + + + + Clear Automation Data + + + + Cascade + + + + Tile + + + + In rows + + + + In columns + + + + Global Settings + + + + Configure Shortcuts + + + + Follow Song + + + + Don't Follow Song + + + + Follow Page + + + + Follow Continuous + + + + Metronome + + + + Midi Sync + + + + Midi File Import/Export + + + + Appearance Settings + + + + Midi Ports / Soft Synth + + + + &Manual + + + + &MusE Homepage + + + + &Report Bug... + + + + &About MusE + + + + Song Position + + + + Tempo + + + + Signature + + + + File Buttons + Pulsanti file + + + Undo/Redo + + + + Transport + Trasporto + + + &File + + + + &View + + + + &Midi + + + + &Audio + + + + A&utomation + + + + &Windows + + + + MusE Se&ttings + + + + &Help + + + + About &Qt + + + + Cannot read template + + + + File open error + + + + File read error + + + + Unknown File Format: %1 + + + + MusE: Song: %1 + + + + MusE: load project + + + + MusE: load template + + + + MusE: Write File failed + + + + The current Project contains unsaved data +Save Current Project? + + + + S&kip + + + + &Cancel + A&nnulla + + + MusE: Save As + + + + Nothing to edit + + + + MusE: Bounce to Track + + + + No wave tracks found + + + + No audio output tracks found + + + + Select one audio output track, +and one target wave track + + + + Select one target wave track + + + + Select one target wave track, +and one audio output track + + + + MusE: Bounce to File + + + + Select one audio output track + + + + MusE: Bounce + + + + set left/right marker for bounce range + + + + The current Project contains unsaved data +Load overwrites current Project: +Save Current Project? + + + + &Abort + + + + This will clear all automation data on + all audio tracks! +Proceed? + + + + This takes an automation snapshot of + all controllers on all audio tracks, + at the current position. +Proceed? + + + + MusE: Export Midi + + + + no help found at: + + + + MusE: Open Help + + + + Unable to launch help + + + + For some reason MusE has to launch the default +browser on your machine. + + + + MusE: Import Midi + + + + Add midi file to current project? + + + + + &Add to Project + + + + &Replace + + + + reading midifile + + + + + +failed: + + + + Import part is only valid for midi and wave tracks! + + + + MusE: load part + + + + No track selected for import + + + + %n part(s) out of %1 could not be imported. +Likely the selected track is the wrong type. + + %n parts out of %1 could not be imported. +Likely the selected track is the wrong type. + + + + + %n part(s) could not be imported. +Likely the selected track is the wrong type. + + %n parts could not be imported. +Likely the selected track is the wrong type. + + + + + to import an audio file you have first to selecta wave track + + + + Import Wavefile + + + + &Yes + + + + &No + + + + turn on/off metronome + + + + MusE: Song: + + + + MusE: Warning + + + + The song uses multiple ports but export format 0 (single track) is set. +The first track's port will be used. Playback will likely be wrong + unless the channels used in one port are different from all other ports. +Canceling and setting a different export format would be better. +Continue? + + + + This wave file has a samplerate of %1, +as opposed to current setting %2. +File will be resampled from %1 to %2 Hz. +Do you still want to import it? + + + + Wave import error + + + + There are too many wave files +of the same base name as imported wave file +Can not continue. + + + + Can't create new wav file in project folder! + + + + + Failed to initialize sample rate converter! + + + + Cancel + Annulla + + + Resampling wave file +"%1" +from %2 to %3 Hz... + + + + Output has clipped +Resampling again and normalizing wave file +"%1" +Try %2 of %3... + + + + File version is %1.%2 +Current version is %3.%4 +Conversions may be applied if file is saved! + + + + Opening file + + + + Do not warn again + + + + Cpu load + + + + Measured CPU load + + + + No CPU load data + + + + + MusEGui::NoteInfo + + Note Info + + + + delta/absolute mode + + + + Start + + + + Len + Lun + + + Pitch + Tono + + + Velo On + + + + Velo Off + + + + + MusEGui::PartCanvas + + Cannot copy/move/clone to different Track-Type + + + + C&ut + + + + &Copy + + + + s&elect + + + + clones + + + + rename + + + + color + + + + delete + + + + split + + + + glue + + + + super glue (merge selection) + + + + de-clone + + + + save part to disk + + + + wave edit + + + + file info + + + + MusE: save part + + + + Part name: %1 +Files: + + + + Remove selected + + + + %n part(s) out of %1 could not be pasted. +Likely the selected track is the wrong type. + + %n parts out of %1 could not be pasted. +Likely the selected track is the wrong type. + + + + + %n part(s) could not be pasted. +Likely the selected track is the wrong type. + + %n parts could not be pasted. +Likely the selected track is the wrong type. + + + + + Cannot paste: multiple tracks selected + + + + Cannot paste: no track selected + + + + Can only paste to midi/drum track + + + + Can only paste to wave track + + + + Can only paste to midi or wave track + + + + Cannot paste: wrong data type + + + + Part: + + + + Automation: + + + + Normalize + + + + + MusEGui::PasteDialog + + %n quarter(s) + + %n quarters + + + + + %1 quarter + for floating-point arguments like 1.5 + + + + %1 quarters + for floating-point arguments like 1.5 + + + + + MusEGui::PasteEventsDialog + + %n quarter(s) + + %n quarters + + + + + %1 quarter + for floating-point arguments like 1.5 + + + + %1 quarters + for floating-point arguments like 1.5 + + + + + MusEGui::PianoRoll + + &Edit + &Modifica + + + C&ut + + + + &Copy + + + + Copy events in range + Copia eventi nell'intervallo + + + &Paste + + + + Paste (with dialog) + + + + Delete &Events + + + + &Select + &Seleziona + + + Select &All + + + + &Deselect All + + + + Invert &Selection + + + + &Inside Loop + + + + &Outside Loop + + + + &Previous Part + + + + &Next Part + + + + Fu&nctions + Fu&nzioni + + + Quantize + Quantizza + + + Modify Note Length + + + + Modify Velocity + Modifica velocity + + + Crescendo/Decrescendo + Crescendo/Decrescendo + + + Transpose + + + + Erase Events + + + + Move Notes + Sposta note + + + Set Fixed Length + Imposta lunghezza fissa + + + Delete Overlaps + Cancella sovrapposizioni + + + Legato + + + + &Plugins + &Plugin + + + Window &Config + &Configura finestra + + + &Event Color + + + + &Blue + + + + &Pitch colors + + + + &Velocity colors + + + + Pianoroll tools + + + + Step Record + Registra passo per passo + + + Midi Input + Ingresso Midi + + + Play Events + + + + ctrl + ctrl + + + Add Controller View + Aggiungi visualizzazione controller + + + + MusEGui::PluginDialog + + MusE: select plugin + + + + Type + + + + Lib + + + + Label + + + + Name + Nome + + + AI + + + + AO + + + + CI + + + + CO + + + + IP + + + + id + + + + Maker + + + + Copyright + + + + Audio inputs + + + + Audio outputs + + + + Control inputs + + + + Control outputs + + + + In-place capable + + + + ID number + + + + Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. + + + + dssi synth + + + + dssi effect + + + + ladspa + + + + Plugin categories. +Right-click on tabs to manage. +Right-click on plugins to add/remove from a category. + + + + &create new group + + + + &delete currently selected group + + + + re&name currently selected group + + + + Associated categories + + + + You need to define some categories first. + + + + new group + + + + Enter the new group name + + + + Wine VST + + + + LV2 synth + + + + LV2 effect + + + + + MusEGui::PluginGui + + File Buttons + Pulsanti file + + + Load Preset + Carica preimpostazioni + + + Save Preset + Salva preimpostazioni + + + bypass plugin + bypassa plugin + + + MusE: load preset + MusE: carica preimpostazioni + + + Error reading preset. Might not be right type for this plugin + Errore di lettura preset. Forse non è adatto a questo plugin + + + MusE: save preset + MusE: salva preimpostazioni + + + + MusEGui::PopupMenu + + <More...> %1 + + + + + MusEGui::ProjectCreateImpl + + Select directory + Seleziona directory + + + + MusEGui::RouteDialog + + Normal + + + + Alias 1 + + + + Alias 2 + + + + Source + + + + Destination + + + + <none> + <nulla> + + + + MusEGui::RoutePopupMenu + + Channel + + + + Soloing chain + + + + Audio returns + + + + Warning: No input devices! + + + + Open midi config... + Apri configurazione Midi... + + + <none> + <nulla> + + + More... + + + + Audio sends + + + + Channel grouping: + + + + Mono + + + + Stereo + + + + Channels + + + + Midi ports/devices + + + + Omni + + + + Show aliases: + + + + First + + + + Second + + + + Show names + + + + Show first aliases + + + + Show second aliases + + + + Jack ports + + + + Connect + + + + Unavailable + + + + Open advanced router... + + + + Output routes: + + + + Input routes: + + + + Tracks + + + + Destinations: + + + + Midi sends + + + + Sources: + + + + + MusEGui::ScoreCanvas + + Treble + Alti + + + Bass + Bassi + + + Grand Staff + Pentagramma doppio + + + Remove staff + Rimuovi pentagramma + + + Ambiguous part + Parte ambigua + + + There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. + Ci sono due o più parti a cui potresti aggiungere la nota, ma nessuna coincide con la parte scelta. Scegli la parte di destinazione clickando su una nota della parte e riprovaci di nuovo o aggiungi un nuovo pentagramma contente la parte di destinazione. + + + No part + Nessuna parte + + + There are no parts you could add the note to. + Non ci sono parti a cui aggiungere la nota. + + + + MusEGui::ScoreEdit + + Step recording tools + + + + Step Record + Registra passo per passo + + + Note settings + + + + Note length: + + + + last + + + + Apply to new notes: + + + + Apply to selected notes: + + + + Velocity: + + + + Off-Velocity: + + + + Quantisation settings + + + + Quantisation: + + + + Pixels per whole: + + + + &Edit + &Modifica + + + C&ut + + + + &Copy + + + + Copy events in range + Copia eventi nell'intervallo + + + &Paste + + + + Paste (with dialog) + + + + Delete &Events + + + + &Select + &Seleziona + + + Select &All + + + + &Deselect All + + + + Invert &Selection + + + + &Inside Loop + + + + &Outside Loop + + + + Fu&nctions + Fu&nzioni + + + &Quantize + + + + Change note &length + + + + Change note &velocity + + + + Crescendo/Decrescendo + Crescendo/Decrescendo + + + Transpose + + + + Erase Events + + + + Move Notes + Sposta note + + + Set Fixed Length + Imposta lunghezza fissa + + + Delete Overlaps + Cancella sovrapposizioni + + + Legato + + + + Window &Config + &Configura finestra + + + Note head &colors + + + + &Black + + + + &Velocity + + + + &Part + + + + Set up &preamble + + + + Display &key signature + + + + Display &time signature + + + + Set Score &name + + + + Enter the new score title + + + + Error + + + + Changing score title failed: +the selected title is not unique + + + + + MusEGui::ScrollScale + + next page + + + + previous page + + + + current page number + + + + + MusEGui::ShortcutCaptureDialog + + Ok + OK + + + Cancel + Annulla + + + Shortcut conflicts with %1 + + + + Undefined + Non definito + + + + MusEGui::ShortcutConfig + + Save printable text file + + + + Text files (*.txt);;All files (*) + + + + Error + + + + Error opening file for saving + + + + Shortcuts for selected category: + + + + Legend: + + + + + An error occurred while saving + + + + + MusEGui::SigScale + + signature scale + + + + + MusEGui::SigToolbarWidget + + time signature at current position + + + + Signature: + + + + + MusEGui::Strip + + Remove track? + Rimovi traccia? + + + + MusEGui::TList + + <none> + <nulla> + + + visible + + + + no clef + + + + Treble + Alti + + + Bass + Bassi + + + Grand + + + + off + spento + + + <unknown> + <sconosciuto> + + + MusE: bad trackname + + + + please choose a unique track name + + + + Unused Devices + + + + show gui + + + + show native gui + + + + Midi control + + + + Assign + + + + Clear + + + + Treble clef + + + + Bass clef + + + + Grand Staff + Pentagramma doppio + + + Viewable automation + + + + Internal + + + + Synth + + + + Delete Track + + + + Track Comment + Commento traccia + + + Insert Track + + + + Midi + Midi + + + Drum + + + + Presets + + + + Clear all controller events? + + + + &Ok + &OK + + + &Cancel + A&nnulla + + + Change color + + + + Other + + + + clear automation + + + + Save track's drumlist + + + + Save track's drumlist differences to initial state + + + + Load track's drumlist + + + + Reset track's drumlist + + + + Reset track's drumlist-ordering + + + + Copy track's drumlist to all selected tracks + + + + Copy track's drumlist's differences to all selected tracks + + + + Drum map + Mappa batteria + + + Reset the track's drum map with instrument defaults? + + + + Reset the track's drum map ordering? + + + + Muse: Load Track's Drum Map + + + + Drummap + + + + This drummap was created with a previous version of MusE, +it is being read but the format has changed slightly so some +adjustments may be necessary. + + + + MusE: Store Track's Drum Map + + + + New style drum + + + + + MusEGui::TempoSig + + Tempo/Sig + + + + + MusEGui::TempoToolbarWidget + + tempo at current position + + + + Tempo: + + + + + MusEGui::Toolbar1 + + Off + spento + + + Solo + + + + Cursor + + + + Snap + + + + + MusEGui::TopWin + + As subwindow + + + + Shares tools and menu + + + + Fullscreen + + + + Undo/Redo tools + + + + Panic + + + + Transport + Trasporto + + + Song Position + + + + Tempo + + + + Signature + + + + Piano roll + + + + List editor + + + + Drum editor + + + + Master track editor + + + + Master track list editor + + + + Wave editor + + + + Clip list + + + + Marker view + Vista marcatori + + + Score editor + + + + Arranger + Arrangiatore + + + <unknown toplevel type> + + + + Metronome + + + + + MusEGui::TrackComment + + MusE: Track Comment + + + + Track Comment: + + + + + MusEGui::Transport + + Overdub + + + + Replace + + + + Rec Mode + + + + Normal + + + + Mix + + + + Cycle Rec + + + + punchin + + + + loop + + + + punchout + + + + Punch In + + + + Loop + + + + Punch Out + + + + Left Mark + marcatore di sinistra + + + Right Mark + marcatore di destra + + + rewind to start + + + + Click this button to rewind to start position + + + + rewind + + + + Click this button to rewind + + + + forward + + + + Click this button to forward current play position + + + + stop + + + + Click this button to stop playback + + + + play + + + + Click this button to start playback + + + + record + + + + Click this button to enable recording + + + + AC + + + + quantize during record + + + + Click + + + + metronom click on/off + + + + Sync + + + + external sync on/off + + + + Jack + + + + Jack transport sync on/off + + + + Master + + + + use master track + + + + + MusEGui::VisibleTracks + + Show wave tracks + Mostra tracce audio + + + Show group tracks + Mostra tracce di gruppo + + + Show aux tracks + Mostra tracce aux + + + Show input tracks + Mostra tracce input + + + Show output tracks + Mostra tracce output + + + Show midi tracks + Mostra tracce Midi + + + Show synth tracks + Mostra tracce synth + + + Visible track types + Tipi di traccia visibili + + + + MusEGui::WaveCanvas + + Adjust Wave Offset + + + + Wave offset (frames) + + + + Part creation failed + + + + Left and right position markers must be placed inside the current part. + + + + Part created + + + + The selected region has been copied to the clipboard and can be pasted in the arranger. + + + + MusE - external editor failed + + + + MusE was unable to launch the external editor +check if the editor setting in: +Global Settings->Audio:External Waveditor +is set to a valid editor. + + + + MusE - file size changed + + + + When editing in external editor - you should not change the filesize +since it must fit the selected region. + +Missing data is muted + + + + + MusEGui::WaveEdit + + &Edit + &Modifica + + + Func&tions + + + + &Gain + + + + Other + + + + &Copy + + + + C&ut + + + + &Paste + + + + Edit in E&xternal Editor + + + + Mute Selection + + + + Normalize Selection + + + + Fade In Selection + + + + Fade Out Selection + + + + Reverse Selection + + + + Select + + + + Select &All + + + + &Deselect All + + + + Window &Config + &Configura finestra + + + WaveEdit tools + + + + Solo + + + + Cursor + + + + &Create Part from Region + + + + &Previous Part + + + + &Next Part + + + + &Event Color + + + + &Part colors + + + + &Gray + + + + + OrganGuiBase + + MusE: Organ + + + + Drawbars + + + + 16' + 16' + + + 4' + 4' + + + 2 2/3' + 2 2/3' + + + 2' + 2' + + + 5 1/3' + 5 1/3' + + + 8' + 8' + + + Envelope Hi + + + + Release + + + + Sustain + + + + Decay + + + + Attack + + + + ms + + + + cB + + + + Envelope Lo + + + + O-1 + + + + Oscillator + + + + Brass + + + + Reed + + + + Flute + + + + Velocity + + + + + PasteDialog + + %n quarter(s) + %n quarters + + + + quarter + quarter + + + quarters + quarters + + + + PasteDialogBase + + MusE: Paste Parts + + + + Number and raster + + + + insert + + + + times + + + + raster + + + + ticks + + + + Move, Merge, Clone + + + + Move everything to the right + + + + Move only affected parts to the right + + + + Put everything into a single track + + + + Merge with existing parts + + + + Insert as clones (where possible) + + + + OK + OK + + + Cancel + Annulla + + + + PasteEventsDialogBase + + MusE: Paste Events + + + + Number and raster + + + + insert + + + + times + + + + raster + + + + ticks + + + + Paste options + + + + Always into existing parts + + + + Never into existing parts + + + + Into existing parts if part has not +to be expanded by more than + + + + Put everything into the (selected) part + + + + OK + OK + + + Cancel + Annulla + + + + PluginDialogBase + + Dialog + + + + Ports: + + + + Mono + + + + Mono + Stereo + + + + Stereo + + + + All + + + + Plugin type: + + + + Filter: + + + + &OK + + + + &Cancel + A&nnulla + + + + ProjectCreate + + Create Project + + + + Projects folder: + + + + ... + ... + + + Project Name: + + + + Project is a Template + + + + Write window state + + + + Project song file type: + + + + Project Path to song file: + + + + Song information: + + + + &Create project folder (recommended for audio projects) + + + + + QObject + + Error + + + + Please first select the range for crescendo with the loop markers. + + + + Controller ! + + + + Other ! + + + + No presets found + + + + Switch on General Midi Level 1 mode + + + + Switch on General Midi Level 2 mode + + + + Switch off General Midi Level 1 or 2 + + + + Switch on Roland GS mode + + + + Switch on Yamaha XG mode + + + + Select gui type + + + + Preset actions + + + + Save preset... + + + + Update list + + + + Saved presets + + + + Enter new preset name + + + + Midi programs + + + + Presets + + + + Tracks: + + + + Midi ports: + + + + Midi devices: + + + + Jack: + + + + Jack midi: + + + + + QWidget + + Cannot convert sysex string + + + + Hex String too long (2048 bytes limit) + + + + generic midi + + + + new + + + + None + + + + create peakfile for + + + + No selection. Ignoring + + + + MusE: get file name + + + + The directory +%1 +does not exist. +Create it? + + + + MusE: create directory + + + + creating dir failed + + + + File +%1 +exists. Overwrite? + + + + Open File +%1 +failed: %2 + + + + MusE: write + + + + MusE: Open File + + + + Instrument-defined + + + + Edit instrument ... + + + + Add + + + + Others + + + + Common Controls + + + + Velocity + + + + No wave events selected. + + + + + QuantBase + + MusE: Quantize + + + + Range + Intervallo + + + All Events + Tutti gli eventi + + + Selected Events + Eventi selezionati + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + Values + Valori + + + Strength: + + + + % + % + + + Threshold (ticks): + + + + Quantize Len + + + + Raster + + + + Whole + + + + Half + + + + 4th + + + + 4th Triplet + + + + 8th + + + + 8th Triplet + + + + 16th + + + + 16th Triplet + + + + 32th + + + + 32th Triplet + + + + Swing: + + + + If the proposed change in tick or length is smaller than threshold, nothing is done. +If swing=0, this is normal +If swing is 33, you get a 2:1-rhythm. +If swing is -33, you get a 1:2-rhythm. + + + + + OK + OK + + + Cancel + Annulla + + + + RemoveBase + + MusE: Erase Notes + + + + Range + Intervallo + + + All Events + Tutti gli eventi + + + Selected Events + Eventi selezionati + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + Thresholds + + + + ticks + + + + Velocity + + + + Length + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> + + + + OK + OK + + + Cancel + Annulla + + + + RouteDialogBase + + MusE: Routing + + + + Connect + + + + Remove + + + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. + + + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + Itemized list of current connections. + + + + Show only selected sources + + + + Show only destination routes for selected source + + + + Show only selected destinations + + + + Show only source routes for selected destination + + + + Show all Midi Ports + + + + Auto adjust column size + + + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + + + + Preferred route name or alias + + + + Connect source to destination + + + + Remove selected route + + + + + SS_PluginChooserBase + + SimpleDrums - Ladspa Plugin Chooser + + + + Name + Nome + + + Label + + + + Inports + + + + Outports + + + + Creator + + + + &Cancel + A&nnulla + + + Alt+C + + + + &OK + + + + Alt+O + + + + + SS_PluginFront + + Clear and unload effect + + + + Load effect + + + + Toggle display of effect parameters + + + + Turn effect on/off + + + + + SetlenBase + + MusE: Set Note Length + + + + Range + Intervallo + + + All Events + Tutti gli eventi + + + Selected Events + Eventi selezionati + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + Value + Valore + + + New length + + + + ticks + + + + OK + OK + + + Cancel + Annulla + + + + ShortcutCaptureDialogBase + + Enter shortcut sequence + Immetti sequenza scelta rapida + + + Press keys to enter shortcut sequence! + Premi dei tasti per immettere la sequenza di scelta rapida! + + + Old shortcut: + Scelta rapida precedente: + + + Undefined + Non definito + + + New shortcut: + Nuova scelta rapida: + + + OK + OK + + + Cancel + Annulla + + + + ShortcutConfigBase + + Configure Keyboard Shortcuts + + + + Shortcut Category + + + + Shortcut + + + + Description + + + + &Clear + + + + Alt+C + + + + &Define + + + + Alt+D + + + + &Apply + &Applica + + + Alt+A + + + + &Printable file... + + + + Alt+P + Alt+P + + + &Ok + &OK + + + Alt+O + + + + + SimpleDrumsGuiBase + + DrumSynth 0.1 + DrumSynth 0.1 + + + + SimpleSynthGui + + &Load setup + &carica impostazioni + + + &Save setup + &salva impostazioni + + + Load sample dialog + Finestra caricamento campioni + + + Mix + + + + Chn + + + + Channel routing + + + + + SongInfo + + Song Information + + + + Show on song load + + + + &Cancel + A&nnulla + + + Alt+C + + + + &Ok + &OK + + + Alt+O + + + + + SynthConfigBase + + Name + Nome + + + Type + + + + list of available software synthesizers + + + + File + + + + Inst + + + + Version + + + + Description + + + + &Apply + &Applica + + + &OK + + + + Configure midi devices, midi ports, and synthesizers + + + + Available soft synthesizers + + + + Useable devices + + + + Add: + + + + Synth + + + + ALSA + + + + JACK + + + + Rename + + + + Remove + + + + Device port assignments + + + + + TransposeBase + + MusE: Transpose + + + + Range + Intervallo + + + All Events + Tutti gli eventi + + + Selected Events + Eventi selezionati + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + Value + Valore + + + Halftone-steps + + + + OK + OK + + + Cancel + Annulla + + + + UnusedWaveFiles + + Dialog + + + + List of unused audio files in current project directory: + + + + Current project + + + + All .med files +in current + directory + + + + Move files to 'unused' subdir + + + + Cancel + Annulla + + + + VAMGui + + MusE: Load VAM Presets + + + + MusE: Save VAM Presets + + + + + VAMGuiBase + + Virtual Analogue for MusE + + + + LFO + + + + Freq + + + + Waveform + + + + Sine + + + + Pulse + + + + Saw + + + + Triangle + + + + Filter + + + + EnvMod + + + + Attack + + + + Decay + + + + Sustain + + + + Release + + + + Cutoff + + + + Resonance + + + + Invert + Inverti selezione + + + KeyTrack + + + + Presets + + + + Set + + + + load preset list + + + + save preset list + + + + save preset list to a new file + + + + delete preset + + + + DCO 1 + + + + Pitch + Tono + + + Detune + + + + PWM + + + + FM + + + + PW + + + + DCO 2 + + + + On + acceso + + + VAM 1.0beta3 +Virtual Analog for MusE +Released under GPL. +Copyright(C) 2002 +Jotsif Lindman Hörnlund +( jotsif@linux.nu ) +Copyright(C) 2005 +Robert Jonsson +(rj@spamatica.se) + + + + + VelocityBase + + MusE: Modify Velocity + + + + Range + Intervallo + + + All Events + Tutti gli eventi + + + Selected Events + Eventi selezionati + + + Looped Events + Eventi del loop + + + Selected Looped + Eventi del loop selezionati + + + Values + Valori + + + Rate: + Rata: + + + % + % + + + Offset: + Offset: + + + veloNew = (veloOld * rate) + offset + + + + OK + OK + + + Cancel + Annulla + + + + file_patterns + + Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) + + + + Midi (*.mid *.MID *.mid.gz *.mid.bz2) + + + + Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) + + + + All Files (*) + + + + Midi (*.mid) + + + + Karaoke (*.kar) + + + + all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) + + + + med Files (*.med *.med.gz *.med.bz2) + + + + Uncompressed med Files (*.med) + + + + gzip compressed med Files (*.med.gz) + + + + bzip2 compressed med Files (*.med.bz2) + + + + mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) + + + + (*.jpg *.gif *.png) + + + + (*.jpg) + + + + (*.gif) + + + + (*.png) + + + + part Files (*.mpt *.mpt.gz *.mpt.bz2) + + + + part Files (*.mpt) + + + + gzip compressed part Files (*.mpt.gz) + + + + bzip2 compressed part Files (*.mpt.bz2) + + + + Presets (*.pre *.pre.gz *.pre.bz2) + + + + Presets (*.pre) + + + + gzip compressed presets (*.pre.gz) + + + + bzip2 compressed presets (*.pre.bz2) + + + + Presets (*.map *.map.gz *.map.bz2) + + + + Presets (*.map) + + + + gzip compressed presets (*.map.gz) + + + + bzip2 compressed presets (*.map.bz2) + + + + Binary (*.bin) + + + + Wave/Binary (*.wav *.ogg *.flac *.bin) + + + + Wave (*.wav *.ogg *.flac) + + + + + freeverb + + FreeVerb + + + + Room Size + + + + Damping + + + + Wet Level + + + + Tap-Reverberator + + + + Decay [ms] + + + + dB + dB + + + Dry Level [dB] + + + + Wet Level [dB] + + + + Preset: + + + + AfterBurn + + + + AfterBurn (Long) + + + + Ambience + + + + Ambience (Thick) + + + + Ambience (Thick) - HD + + + + Cathedral + + + + Cathedral - HD + + + + Drum Chamber + + + + Garage + + + + Garage (Bright) + + + + Gymnasium + + + + Gymnasium (Bright) + + + + Gymnasium (Bright) - HD + + + + Hall (Small) + + + + Hall (Medium) + + + + Hall (Large) + + + + Hall (Large) - HD + + + + Plate (Small) + + + + Plate (Medium) + + + + Plate (Large) + + + + Plate (Large) - HD + + + + Pulse Chamber + + + + Pulse Chamber (Reverse) + + + + Resonator (96 ms) + + + + Resonator (152 ms) + + + + Resonator (208 ms) + + + + Room (Small) + + + + Room (Medium) + + + + Room (Large) + + + + Room (Large) - HD + + + + Slap Chamber + + + + Slap Chamber - HD + + + + Slap Chamber (Bright) + + + + Slap Chamber (Bright) HD + + + + Smooth Hall (Small) + + + + Smooth Hall (Medium) + + + + Smooth Hall (Large) + + + + Smooth Hall (Large) - HD + + + + Vocal Plate + + + + Vocal Plate - HD + + + + Warble Chamber + + + + Warehoouse + + + + Warehouse - HD + + + + Comb Filters + + + + Allpass Filters + + + + Bandpass Filters + + + + Enhanced Stereo + + + + + midiWarnInitPendingBase + + Instrument initialization + + + + MusE should now send some Instrument Initialization Sequences. +The sequences (usually System Exclusive messages) are defined + by the selected instruments in the Settings -> Midi Ports dialog, + such as the GM (default), GS, or XG instruments. + +Typically you should answer yes here. +You can always do it manually from the Midi menu. + +Continue? + + + + Don't ask me again + + + + + shortcuts + + Transport: Start playback from current location + Trasporto: inizia riproduzione dalla posizione corrente + + + Transport: Toggle metronome + Trasporto: attiva/disattiva metronomo + + + Transport: Stop Playback + Trasporto: arresta riproduzione in corso + + + Transport: Goto Start + Trasporto: vai all'inizio + + + Transport: Play, Stop, Rewind + Trasporto: riproduzione, arresto, riavvolgimento + + + Transport: Goto left marker + Trasporto: vai al marcatore di sinistra + + + Transport: Goto right marker + Trasporto: vai al marcatore di destra + + + Transport: Toggle Loop section + Trasporto: attiva/disattiva selezione loop + + + Transport: Toggle Record + Trasporto: attiva/disattiva registrazione + + + Transport: Clear all rec enabled tracks + Trasporto: cancella tutte le tracce abilitate alla registrazione + + + Toggle fullscreen + attiva/disattiva visualizzazione a tutto schermo + + + Edit: Copy + Modifica: copia + + + Edit: Copy in range + Modifica: copia nell'intervallo + + + Edit: Undo + Modifica: annulla + + + Edit: Redo + Modifica: ripristina + + + Edit: Cut + Modifica: taglia + + + Edit: Paste + Modifica: incolla + + + Edit: Paste (with dialog) + Modifica: incolla (con dialogo) + + + Edit: Delete + Modifica: cancella + + + File: New project + File: nuovo progetto + + + File: Open from disk + File: apri da disco + + + File: Save project + File: salva progetto + + + File: Open recent file + File: apri file recente + + + File: Save as + File: salva come + + + File: Load template + File: carica modello + + + File: Import midi file + file: importa file Midi + + + File: Export midi file + File: esporta file Midi + + + File: Import midi part + File: importa parte Midi + + + File: Import audio file + File: importa file audio + + + File: Quit MusE + File: chiudi MusE + + + Edit: Select parts on track + Modifica: seleziona parti sulla traccia + + + Open pianoroll + Apri pianoroll + + + Open drumeditor + Apri editor batteria + + + Open listeditor + Apri editor liste + + + Open waveeditor + Apri editor audio + + + Open graphical mastertrack editor + Apri editor grafico traccia master + + + Open list mastertrack editor + Apri editor lista tracce master + + + Open midi transformer + Apri trasformatore Midi + + + Add midi track + Aggiungi traccia Midi + + + Add drum track + Aggiungi traccia batteria + + + Add wave track + Aggiungi traccia audio + + + Add audio output + Aggiungi uscita audio + + + Add audio group + Aggiungi gruppo audio + + + Add audio input + Aggiungi ingresso audio + + + Add audio aux + Aggiungi audio aux + + + Structure: Global cut + Struttura: taglio globale + + + Structure: Global insert + Struttura: inserimento globale + + + Structure: Global split + Struttura: divisione globale + + + Structure: Cut events + Struttura: taglio eventi + + + View: Open mixer #1 window + + + + View: Open mixer #2 window + + + + View: Toggle transport window + + + + View: Toggle bigtime window + + + + View: Open marker window + + + + Settings: Follow song by page + + + + Settings: Follow song off + + + + Settings: Follow song continuous + + + + Settings: Global configuration + + + + Settings: Configure shortcuts + + + + Settings: Configure metronome + + + + Settings: Midi sync configuration + + + + Settings: Midi file import/export configuration + + + + Settings: Appearance settings + + + + Settings: Midi ports / Soft Synth + + + + Settings: Audio subsystem configuration + + + + Midi: Edit midi instruments + + + + Midi: Open midi input transform + + + + Midi: Open midi input filter + + + + Midi: Midi input transpose + + + + Midi: Midi remote control + + + + Midi: Random rhythm generator + + + + Midi: Reset midi + + + + Midi: Init midi + + + + Midi: Midi local off + + + + Audio: Bounce audio to track + + + + Audio: Bounce audio to file + + + + Audio: Restart audio + + + + Automation: Mixer automation + + + + Automation: Take mixer snapshot + + + + Automation: Clear mixer automation + + + + Help: Open Manual + + + + Help: Toggle whatsthis mode + + + + Edit: Edit selected part + + + + Edit: Select nearest part on track above + + + + Edit: Add nearest part on track above + + + + Edit: Select nearest part on track below + + + + Edit: Add nearest part on track below + + + + Edit: Insert empty measure + + + + Select track above + + + + Select track below + + + + Midi: Transpose + + + + Edit: Select all + + + + Edit: Select none + + + + Edit: Invert Selection + + + + Edit: Select events/parts inside locators + + + + Edit: Select events/parts outside locators + + + + Edit: Select previous part + + + + Edit: Select next part + + + + Edit: Select nearest part/event to the left or move cursor + + + + Edit: Add nearest part/event to the left to selection + + + + Edit: Select nearest part/event to the right or move cursor + + + + Edit: Add nearest part/event to the right to selection + + + + Edit: Set locators to selection + + + + Edit: Increase pitch + + + + Edit: Decrease pitch + + + + Edit: Increase event position + + + + Edit: Decrease event position + + + + View: Zoom in + + + + View: Zoom out + + + + View: Goto Current Position + + + + View: Scroll left + + + + Edit: Set Fixed Length on Midi Events + + + + Quantize + Quantizza + + + Modify Note Length + + + + Modify Velocity + Modifica velocity + + + Edit: Crescendo + + + + Edit: Thin Out + + + + Edit: Erase Event + + + + Edit: Delete Overlaps + + + + Edit: Note Shift + + + + Edit: Move Clock + + + + Edit: Copy Measure + + + + Edit: Erase Measure + + + + Edit: Delete Measure + + + + Edit: Create Measure + + + + Edit: Change Event Color + + + + Tool: Pointer + + + + Tool: Pencil + + + + Tool: Eraser + + + + Tool: Line Draw + + + + Tool: Cursor + + + + Add note velocity 1 + + + + Add note velocity 2 + + + + Add note velocity 3 + + + + Add note velocity 4 + + + + Cursor step size: larger + + + + Cursor step size: smaller + + + + Instrument/Cursor up + + + + Instrument/Cursor down + + + + Tool: Scissor + + + + Tool: Glue + + + + Tool: Mute + + + + Transport: Increase current position + + + + Transport: Decrease current position + + + + Transport: Increase current position, no snap + + + + Transport: Decrease current position, no snap + + + + Quantize: Set quantize to 1/1 note + + + + Quantize: Set quantize to 1/2 note + + + + Quantize: Set quantize to 1/4 note + + + + Quantize: Set quantize to 1/8 note + + + + Quantize: Set quantize to 1/16 note + + + + Quantize: Set quantize to 1/32 note + + + + Quantize: Set quantize to 1/64 note + + + + Quantize: Toggle triol quantization + + + + Quantize: Toggle punctuation quantization + + + + Quantize: Toggle punctuation quantization (2) + + + + Edit: Insert at location + + + + Edit: Increase length + + + + Edit: Decrease length + + + + Insert Note + + + + Insert SysEx + + + + Insert Ctrl + + + + Insert Meta + + + + Insert Channel Aftertouch + + + + Insert Key Aftertouch + + + + Insert Tempo + + + + Insert Signature + + + + Change Event Position + + + + Edit Event Value + + + + Insert Key + + + + Goto Next Marker + + + + Goto Prev Marker + + + + Edit: Paste to selected track + + + + Edit: Paste clone + + + + Edit: Paste clone to selected track + + + + Add new style drum track + + + + Transport: Step record + + + + Transport: Midi input + + + + Transport: Play events + + + + Edit: Increase velocity + + + + Edit: Decrease velocity + + + + Move: Move to selected note + + + + Tool: Pan + + + + Tool: Zoom + + + + Tool: Range + + + + Edit: Duplicate track + + + + Transport: Restart recording + + + + Edit selected track name + + + + Normalize + + + + + warnBadTimingBase + + Bad timing + Timing difettoso + + + Message here + + + + Don't warn me again + + + + diff -Nru muse-2.1.2/share/locale/muse_pl.ts muse-3.0.2+ds1/share/locale/muse_pl.ts --- muse-2.1.2/share/locale/muse_pl.ts 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/muse_pl.ts 2017-12-04 21:01:19.000000000 +0000 @@ -1,117 +1,60 @@ - + @default - Add Midi Track - Dodaj Åšlad Midi + Dodaj Åšlad Midi - - Add Drum Track - Dodaj Åšlad Perkusyjny - - - - Add Old Style Drum Track - - - - - Add New Style Drum Track - + Dodaj Åšlad Perkusyjny - Add Wave Track - Dodaj Åšlad Audio + Dodaj Åšlad Audio - Add Audio Output - Dodaj WyjÅ›cie Audio + Dodaj WyjÅ›cie Audio - Add Audio Group - Dodaj GrupÄ™(SzynÄ™) Audio + Dodaj GrupÄ™(SzynÄ™) Audio - Add Audio Input - Dodaj WejÅ›cie Audio + Dodaj WejÅ›cie Audio - Add Aux Send - Dodaj WysyÅ‚kÄ™ Audio - - - - Select project directory - + Dodaj WysyÅ‚kÄ™ Audio - - Add Synth - + + Route + PoÅ‚Ä…czenie - - Route - PoÅ‚Ä…cznie + + channel="%1" + kanaÅ‚="%1" - + dest - + cel name="%1"/ - - - - - Warning: No output devices! - - - - - Open midi config... - - - - - Empty ports - + nazwa="%1"/ - + <none> - - - - - channelMask="%1" - - - - - Bad timing - - - - - Timing source frequency is %1hz, which is below the recommended minimum: 500hz! -This could lead to audible timing problems for MIDI. -Please see the FAQ on http://muse-sequencer.org for remedies. -Also please check console output for any further error messages. - - + <brak> @@ -119,37 +62,50 @@ AboutBox - O Programie + O programie - - Version 2 - + + Version info (replaced programmatically) + - (C) Copyright 1999-2012 Werner Schweer and others. + (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License - + (C) Copyright 1999-2015 Werner Schweer i inni. +Nowe wersje oraz wiÄ™cej informacji na stronie: http://www.muse-sequencer.org. + +Program wydany na licencji GNU Public License - + + System information: + Informacja o systemie: + + + + TextLabel + + + + &Keep On Rocking! - + Alt+K - + AppearanceDialogBase - + Apply Zastosuj @@ -164,47 +120,45 @@ Anuluj - + Arranger Główne Okno MusE: Appearance settings - + MusE: ustawienia wyglÄ…du - + Parts Klocki - + show names - pokaż nazwy + Pokaż nazwy - show events - pokaż elementy midi + pokaż zdarzenia MIDI - show Cakewalk Style - pokaż w stylu Cakewalk + pokaż w stylu Cakewalk - + y-stretch - + RozciÄ…gnij w pionie Events - Elementy midi + Elementy MIDI - + note on DźwiÄ™ki @@ -216,12 +170,12 @@ controller - Kontrolery midi + Kontrolery MIDI aftertouch - Nacisk pod uderzeniu (aftertouch) + Docisk (Aftertouch) @@ -236,15 +190,30 @@ special - specjalne + Specjalne + Wave Drawing in Parts + Wykres audio w klockach + + + + Only Outline + Tylko obrys + + + + RMS/PEAK(Traditional) + Tradycyjny (RMS/PEAK) + + + Background picture Obrazek w tle - + show snap grid pokaż siatkÄ™ przyciÄ…gania @@ -259,14 +228,14 @@ SkÅ‚adniki - + Color name: - + Nazwa koloru: Global opacity - + Globalna przeźroczystość @@ -274,19 +243,18 @@ Style/Czcionki - QT Theme - Styl Qt + Styl Qt - + Windows Windows MusE - MuzA + MusE @@ -321,105 +289,135 @@ May require restarting MusE for best results - + Zrestartuj MusE dla lepszego efektu - + + MusE color scheme + Zestaw kolorów MusE + + + + current settings + Bieżące ustawienia + + + + Change + ZmieÅ„ + + + Fonts Czcionki - + Family Rodzina Czcionek - + Size Rozmiar - + Font 1 Czcionka 1 - + + Themes + Style + + + Font 2 Czcionka 2 - + Font 3 Czcionka 3 - + Font 0 Czcionka 0 - - - - - - - + + + + + + + Bold - Pogrubionie + Pogrubienie - - - + - - + + + + Italic Kursywa - + - - - - - - + + + + + + ... ... - + Font 4 Czcionka 4 - + Font 5 Czcionka 5 - + Palette Paleta kolorów add - Zufgen + dodaj + + + + show e&vents + Pokaż &zdarzenia MIDI + show Ca&kewalk Style + Pokaż w stylu Ca&kewalk + + + remove - usuÅ„ + usuÅ„ - + add to palette dodaj do palety @@ -454,19 +452,19 @@ R - + clear - + wyczyść - + Style Sheet: - + Arkusz stylów: - + Font 6 - Czcionka 6 + Czcionka 6 @@ -474,126 +472,110 @@ Configure arranger columns - + Konfiguracja kolumn w oknie głównym Columns: - + Kolumny: Name: - Nazwa: + Nazwa: Controller type: - + Typ kontrolera MIDI: Midi controller type - + Rodzaj kontrolera MIDI - Control7 - Kontroler7 + Kontroler7 - Control14 - Kontroler14 + Kontroler14 - RPN - RPN + RPN - NRPN - NRPN - - - - RPN14 - - - - - NRPN14 - + NRPN - Pitch - Transpozycja + Transpozycja - Program - Program + Program - + H-Ctrl - H-Ctrl + H-Ctrl Midi controller number high byte - + Numer kontrolera MIDI: starszy bajt L-Ctrl - L-Ctrl + L-Ctrl Midi controller number low byte - + Numer kontrolera MIDI: mÅ‚odszy bajt * wild card - + affect CCs at - + wyÅ›lij komunikaty CC na begin of song - + poczÄ…tku utworu current position - + bieżącej pozycji &Add - &Dodaj + &Dodaj &Delete - &Kasuj + &Kasuj Done - + Gotowe @@ -601,12 +583,12 @@ off - + wyÅ‚. db - + db @@ -614,7 +596,41 @@ off - + wyÅ‚. + + + + ChooseSysexBase + + + Dialog + + + + + Hex: + + + + + + TextLabel + + + + + Comment: + Opis: + + + + &OK + &Akceptuj + + + + &Cancel + &Anuluj @@ -622,7 +638,7 @@ MusE: ClipList - MuzA: Lista Klipów + MusE: Lista Klipów @@ -632,12 +648,12 @@ Refs - Refs + Odn. Samplerate - + CzÄ™st. próbkowania @@ -686,7 +702,7 @@ ConfigMidiFileBase - + &OK &Akceptuj @@ -696,7 +712,7 @@ &Anuluj - + 0 (single track) 0 (pojedynczy Å›lad) @@ -706,12 +722,12 @@ 1 (wiele Å›ladów) - + Format: Format: - + 96 96 @@ -726,84 +742,217 @@ 384 - + Division: Rozdzielczość: - + Copyright: Prawa autorskie: - + MusE: Config Midi File Import/Export - + MusE: konfiguracja importu/eksportu plików MIDI Import: - + Import: - + Split tracks into &parts - + &Podziel Å›lady na klocki Alt+P - + Alt+P Split tracks into parts, or one single part - + Podziel Å›lady MIDI na poszczególne klocki - - Use new-style drum tracks - + + Default instrument: + DomyÅ›lny instrument: - - Use old-style drum tracks - + + Device Name metas trump Port metas if both exist + May be incorrect + W przypadku konfliktu, meta-nazwy urzÄ…dzenia przykrywajÄ… meta-nazwy portu - - Export: - + + Instrument Name metas trump Mode sysexes if both exist + May be incorrect + W przypadku konfliktu, meta-nazwy instrumentu przykrywajÄ… komunikaty Mode SysEx - + + Use new-style drum tracks + Åšlady perkusyjne nowego typu + + + + Use old-style drum tracks + Åšlady perkusyjne starego typu + + + + Export: + Eksport: + + + Enable extended smf format (currently not implemented) - + Użyj rozszerzonego formatu SMF (nie zaimplementowano) + + + + Running Status saves space by not + repeating event status bytes. +If this is off every event will have a + status byte, increasing file size. + + Tryb Running Status pozwala na + oszczÄ™dzanie miejsca poprzez + unikanie powtarzania bajtów + statusu zdarzenia. +JeÅ›li opcja jest wyÅ‚Ä…czona, + każdemu zdarzeniu bÄ™dzie + towarzyszyÅ‚ bajt statusu, + co spowoduje zwiÄ™kszenie + rozmiaru pliku. + + + + Use Running &Status + Tryb Running &Status + + + + Alt+S + + + + + To turn a note off some devices can use + note-offs or zero-velocity note-ons or + both. When used with Running Status + this setting saves space. It is safe to + leave this off. + By wyÅ‚Ä…czyć dźwiÄ™k, urzÄ…dzenia mogÄ… + używać komunikatów Note Off, Note On + z zerowÄ… wartoÅ›ciÄ… parametru velociy + bÄ…dź obu. W przypadku korzystania z + trybu Running Status ustawienie to + pozwala na zredukowanie liczby + przesyÅ‚anych komunikatów MIDI i + oszczÄ™dność miejsca. Bezpieczniej jest + pozostawić tÄ™ opcjÄ™ wyÅ‚Ä…czonÄ…. + Replace note-offs with &zero velocity note-ons + Zamieniaj Note Off na Note On z &zerowym velocity + + + Use &2-byte time signatures instead of standard 4 - + Używaj &2-bajtowego metrum, zamiast standardowego 4-bajtowego Alt+2 - + - + + Export instrument or mode: + Eksport instrumentu/trybu: + + + + Mode sysexes + Mode SysEx + + + + Export port or device metas: + Eksport meta portu lub urzÄ…dzenia (Port/Device metas): + + + + Instrument name metas + Meta-nazwy instrumentu + + + Both + Oba + + + + Port metas + Meta-nazwy portu + + + + Device name metas + Meta-nazwy urzÄ…dzenia + + + + Export a Port/Device meta for format 0 + Eksportuj Port/Device meta dla formatu 0 + + + Note: Format 0 uses the FIRST midi track's name/comment in the arranger - + Uwaga: dla formatu 0 użyte zostanÄ… nazwy i komentarze z pierwszego Å›ladu MIDI - Save space by replacing note-offs with &zero velocity note-ons - + OszczÄ™dzanie miejsca poprzez zamianÄ™ zdarzeÅ„ note-off na zdarzenia note-on z &zerowÄ… wartoÅ›ciÄ… parametru velocity - + Alt+Z - + + + + + CopyOnWriteDialogBase + + + Copy Wave Files + Kopiowanie plików audio + + + + Some sound files will be copied to the Project Directory, +either because they are not writable or because more +than one independent Wave Event shares them. +(If you prefer instead that the Wave Events are + inter-dependent, try using Clone Parts.) + +Multiple copies will be made in some cases. + +If no Project has been created yet, you will be asked to, +giving another chance to cancel. + Niektóre pliki audio zostanÄ… skopiowane do katalogu projektu, ponieważ sÄ… one niezapisywalne lub sÄ… dzielone pomiÄ™dzy kilka niezależnych zdarzeÅ„ audio (należy użyć klonowania klocków, jeÅ›li zdarzenia majÄ… być współzależne). +W niektórych przypadkach pliki mogÄ… być skopiowane wielokrotnie. +Jeżeli żaden projekt nie zostaÅ‚ jeszcze utworzony, pojawi siÄ™ proÅ›ba o jego stworzenie, co da kolejnÄ… szansÄ™ wycofania siÄ™. + + + + These files will be copied to the Project Directory: + NastÄ™pujÄ…ce pliki zostanÄ… skopiowane do katalogu projektu: @@ -811,63 +960,63 @@ MusE: Crescendo/Decrescendo - + MusE: crescendo/decrescendo Range - Zakres + Zakres Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami Values - Wartość + WartoÅ›ci Start velocity - + PoczÄ…tkowa velocity % - + End velocity - + KoÅ„cowa velocity Absolute - + BezwzglÄ™dna Relative - + WzglÄ™dna OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj @@ -875,12 +1024,12 @@ Save configuration - + Zapisz konfiguracjÄ™ - + @@ -889,304 +1038,307 @@ Critical Error - + BÅ‚Ä…d krytyczny - - + + Cannot open file %1 - + Nie można otworzyć pliku %1 - - + + Parsing error for file %1 - + BÅ‚Ä…d parsowania pliku %1 - - + + Load category dialog - + Åaduj kategoriÄ™ Load set dialog - + Åaduj zestaw Save set dialog - + Zapisz zestaw New category - + Nowa kategoria Delete category - + Kasuj kategoriÄ™ Load category - + Åaduj kategoriÄ™ Save category - + Zapisz kategoriÄ™ Load set - + Åaduj zestaw Save set - + Zapisz zestaw Delete set - + Kasuj zestaw New subcategory - + Nowa podkategoria Delete subcategory - + Kasuj podkategoriÄ™ Load subcategory - + Åaduj podkategoriÄ™ Save subcategory - + Zapisz podkategoriÄ™ New preset - + Nowe ustawienie Delete preset - + Kasuj ustawienie Load preset - + Åaduj ustawienie Save preset - + Zapisz ustawienie No more category supported - + Nie można użyć wiÄ™kszej liczby kategorii You can not add more categories - + Nie można dodać wiÄ™kszej liczby kategorii Do you really want to delete %1 ? - + Na pewno chcesz skasować %1? &Yes - &Tak + &Tak &No - &Nie + &Nie No category selected - + Nie wybrano żadnej kategorii You must first select a category. - + Musisz najpierw wybrać kategoriÄ™. Replace or add - + ZamieÅ„ lub dodaj %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? - + Kategoria %1 ma zostać przyporzÄ…dkowana do banku (HBank) nr %2, jednak jest on zajÄ™ty. + Nadpisać, czy dodać do kolejnego wolnego slotu? &Replace - &ZastÄ…p + &ZastÄ…p &Add - &Dodaj + &Dodaj Download error - + BÅ‚Ä…d pobierania There is no more free category slot. - + Brak wolnych miejsc na kategoriÄ™. Save category dialog - + Zapisz kategoriÄ™ No more subcategory supported - + Nie można użyć wiÄ™kszej liczby podkategorii You can not add more subcategories - + Nie można dodać wiÄ™kszej liczby podkategorii No subcategory selected - + Nie wybrano żadnej podkategorii You must first select a subcategory. - + Musisz najpierw wybrać podkategoriÄ™. Load subcategory dialog - + Åaduj podkategoriÄ™ %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? - + Podkategoria %1 ma zostać przyporzÄ…dkowana do banku (LBank) nr %2, jednak jest on zajÄ™ty. + Nadpisać, czy dodać do kolejnego wolnego slotu? There is no more free subcategory slot. - + Brak wolnych miejsc na podkategoriÄ™. Save subcategory dialog - + Zapisz podkategoriÄ™ No more preset supported - + Nie można użyć wiÄ™kszej liczby ustawieÅ„ You can not add more presets - + Nie można dodać wiÄ™kszej liczby ustawieÅ„ No preset selected - + Nie wybrano żadnego ustawienia You must first select a preset. - + Musisz najpierw wybrać ustawienie. Load preset dialog - + Åaduj ustawienie %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? - + Ustawienie %1 ma zostać przyporzÄ…dkowane do programu (Prog) nr %2, jednak jest on zajÄ™ty. + Nadpisać, czy dodać do kolejnego wolnego slotu? There is no more free preset slot. - + Brak wolnych miejsc na ustawienia. Save preset dialog - + Zapisz ustawienie Browse set dialog - + PrzeglÄ…danie zestawów Browse image dialog - + PrzeglÄ…danie obrazów @@ -1194,74 +1346,74 @@ DeicsOnze - + &Preset - + &Ustawienie Program numerous - + Numer programu INITVOICE - + LBank - + Subcategory - + Podkategoria Bank numerous - + Numer banku NONE - + BRAK HBank - + Category - + Kategoria Prog - + Preset - Ustawienie + Ustawienie DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. - + DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Wydany na licencji GPL. &Global - + &Globalnie @@ -1409,7 +1561,7 @@ Modulation Matrix - + Matryca modulacji @@ -1421,7 +1573,14 @@ <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> - + <b>Algorytm 1</b> : <i>Op 1</i> modulowany przez <i>Op 2</i> modulowany przez <i>Op 3</i> modulowany przez <i>Op 4</i><br> +<b>Algorytm 2</b> : <i>Op 1</i> modulowany przez <i>Op 2</i> modulowany przez both <i>Op 3</i> i <i>Op 4</i><br> +<b>Algorytm 3</b> : <i>Op 1</i> modulowany przez <i>Op 4</i> i <i>Op 2</i> modulowany przez <i>Op 3</i><br> +<b>Algorytm 4</b> : <i>Op 1</i> modulowany przez <i>Op 2</i> i <i>Op 3</i> modulowany przez <i>Op 4</i><br> +<b>Algorytm 5</b> : (<i>Op 1</i> modulowany przez <i>Op 2</i>) dodaj do (<i>Op 3</i> modulowany przez <i>Op 4</i>) <br> +<b>Algorytm 6</b> : dodanie trzech <i>Op 1, 2, 3</i> wszystkie modulowane przez <i>Op 4</i><br> +<b>Algorytm 7</b> : dodanie trzech <i>Op 1, 2, 3</i> z <i>Op 3</i> modulowane przez <i>Op 4</i><br> +<b>Algorytm 8</b> : dodanie czterech <i>Op 1, 2, 3, 4</i> @@ -1534,7 +1693,7 @@ Release Rate - + @@ -1542,7 +1701,7 @@ 2° Decay Rate - + @@ -1550,7 +1709,7 @@ 1° Decay Level - + @@ -1655,7 +1814,14 @@ Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> - + Wave form 1 = <i>sin(<b>t</b>)</i><br> +Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> +Wave form 3 = <i>if <b>t</b>&#060 pi wtedy sin(<b>t</b>) w przeciwnym razie 0</i><br> +Wave form 4 = <i>if <b>t</b>&#060 pi wtedy sin(<b>t</b>)*abs(sin(<b>t</b>)) w przeciwnym razie 0</i><br> +Wave form 5 = <i>if <b>t</b>&#060 pi wtedy sin(2*<b>t</b>) w przeciwnym razie 0</i><br> +Wave form 6 = <i>if <b>t</b>&#060 pi wtedy sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) w przeciwnym razie 0</i><br> +Wave form 7 = <i>if <b>t</b>&#060 pi wtedy abs(sin(2*<b>t</b>)) w przeciwnym razie 0</i><br> +Wave form 8 = <i>if <b>t</b>&#060 pi wtedy sin(2*<b>t</b>)*sin(2*<b>t</b>) w przeciwnym razie 0</i> @@ -2213,7 +2379,7 @@ Pitch - Transpozycja + @@ -2248,7 +2414,7 @@ After Touch - Nacisk pod uderzeniu (aftertouch) + Docisk (Aftertouch) @@ -2343,199 +2509,199 @@ &Config - &Konfiguracja + &Konfiguracja Font Size - + Rozmiar czcionki Quality - + Jakość High - + Wysoka Middle - + Åšrednia Low - + Niska Ultra low - + Bardzo niska Filter - + Filtr Save Mode (into the song) - + Tryb zapisywania (w utworze) Save only the used presets - + Zapisz tylko wykorzystane ustawienia Save the entire set - + Zapisz caÅ‚y zestaw Save the configuration - + Zapisz konfiguracjÄ™ Configuration File - + Plik konfiguracji Save... - + Zapisz... Load... - + Åaduj... Save as default - + Zapisz jako domyÅ›lne Colors - Kolory + Kolory Text - Tekst + Tekst Background - + TÅ‚o Edit Text - + Element edytowany Edit Background - + TÅ‚o elementu edytowanego Red - + Czerwony Blue - + Niebieski Green - + Zielony Set Path - + Ustaw Å›cieżkÄ™ Image in the background : - + Obraz w tle: Browse... - + PrzeglÄ…daj... Load the set at the initialization : - + Åaduj zestaw podczas inicjalizacji: Set Brightness, Detune, Attack and Release of the current channel to default - + Ustaw domyÅ›lne wartoÅ›ci dla bieżącego kanaÅ‚u Res. Ctrl - + Res. kontr. Cut all notes off - + WyÅ‚Ä…cz wszystkie dźwiÄ™ki Panic! - + Panika! Number of Voices - + Liczba gÅ‚osów Number of voices - + Liczba gÅ‚osów Enable - WÅ‚Ä…cz + WÅ‚Ä…cz Channel - + KanaÅ‚ Vol - + GÅ‚. Channel Ctrl - + Kontr. kan. @@ -2568,42 +2734,42 @@ MusE: Delete Overlaps - + MusE: kasowanie elementów nakÅ‚adajÄ…cych siÄ™ Range - Zakres + Zakres All Events - Wszytkie Elementy + Wszystkie elementy Selected Events - Zaznaczone Elementy + Zaznaczone elementy Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj @@ -2611,22 +2777,22 @@ Did you know? - + Czy wiesz, że...? - + Don't show on startup - + Nie pokazuj przy starcie Next tip - + NastÄ™pna porada Close - + Zamknij @@ -2634,75 +2800,75 @@ Duplicate tracks - + Kopiuj Å›lady Number of copies - + Liczba kopii Copy all routes - + Kopiuj wszystkie poÅ‚Ä…czenia Default routing - + DomyÅ›lne poÅ‚Ä…czenia No routes - + Brak poÅ‚Ä…czeÅ„ Copy parts - + Kopiuj klocki Copy drumlist - + Kopiuj listÄ™ instrumentów perkusyjnych - Copy standard controllers (vol, pan) - + Copy standard (vol, pan) and synth controllers + Kopiuj standardowe kontrolery (gÅ‚., pan.) oraz kontrolery instrumentów - + Copy effects rack plugins - + Kopiuj wtyczki z racka efektów Copy plugin controllers - + Kopiuj kontrolery wtyczek Ok - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj EditCtrlBase - + MusE: Edit Controller Event - MuzA: Edytuj zdarzenia kontrolera + MusE: edytuj zdarzenia kontrolera Time Position - Pozycja: + Pozycja @@ -2715,22 +2881,27 @@ Ustaw nowy kontroler - + textLabel3 Etykieta3 - + Value Wartość - + Controller Kontroler - + + Note + DźwiÄ™k + + + H-Bank H-Bank @@ -2771,57 +2942,57 @@ MusE: Modify gain - + MusE: modyfikuj wzmocnienie Gain - + Wzmocnienie 200% - 200% + 200% 100% - 100% + 100% 0% - 0% + 0% &Reset - + Alt+R - + &Apply - &Zastosuj + &Zastosuj Alt+A - Alt+A + Alt+Z &Cancel - + A&nuluj Alt+C - Alt+C + Alt+N @@ -2829,10 +3000,10 @@ MusE: Instrument Editor - MuzA: Edytor Instrumentów + MusE: Edytor Instrumentów - + High Bank: Górny Bank: @@ -2847,122 +3018,120 @@ Program: - - - + + + + &Delete &Kasuj - - - + + + Alt+D - Alt+D + Alt+K - + + + Drum Perkusja - GM - GM + GM - GS - GS + GS - XG - XG + XG - This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. - To jest lista najczęściej używanych kontorlerów midi. + To jest lista najczęściej używanych kontrolerów MIDI. Zauważ, że "pitch" i "zmiana programu (program change)" sÄ… traktowane w MuzA jako zwykÅ‚e kontrolery. - - + + Properties WÅ‚aÅ›ciwoÅ›ci - Control7 - Kontroler7 + Kontroler7 - Control14 - Kontroler14 + Kontroler14 - RPN - RPN + RPN - NRPN - NRPN + NRPN - Pitch - Transpozycja + Transpozycja - Program - Program + Program - - + + H-Ctrl - H-Ctrl + H-Ctrl - - + + L-Ctrl - L-Ctrl + L-Ctrl - + Min Min. - + Max - Max + Maks. - + Name - nazwa + Nazwa - + + Drum&maps + &Zestaw perkusyjny + + + Type Typ - + Hex Entry: Wartość Heks. - + &File &Plik @@ -3027,241 +3196,192 @@ Zapisz &Jako... - Exit - ZakoÅ„cz + ZakoÅ„cz - E&xit - &ZakoÅ„cz + &ZakoÅ„cz - + Instrument Name: - + Nazwa instrumentu: Selected instrument name. - + Nazwa wybranego instrumentu. List of defined instruments. - + Lista zdefiniowanych instrumentów. - + Pa&tches - + &Brzmienia List of groups and patches. - + Lista brzmieÅ„ w grupach. Group/Patch - + Grupa/Brzmienie - - + + Name: - Nazwa: + Nazwa: - + Group or patch name - + Nazwa grupy bÄ…dź brzmienia. Patch high bank number - + Numer górnego banku dla danej barwy Patch high bank number. --- means don't care. - + Numer górnego banku dla danej barwy, --- – bez znaczenia. --- dont care - + --- Patch low bank number - + Numer dolnego banku dla danej barwy Patch low bank number. --- means don't care. - + Numer dolnego banku dla danej barwy, --- – bez znaczenia. --- - + --- Patch program number - + Nr programu danego brzmienia - Drum patch - - - - - If set, the patch is available only for drum channels. - - - - - GM patch - - - - - If set, the patch is available in a 'GM' or 'NO' midi song type. - - - - - GS patch - - - - - If set, the patch is available in a 'GS' or 'NO' midi song type. - - - - - XG patch - + + Show in tracks: + Pokaż na Å›cieżkach: - - If set, the patch is available in an 'XG' or 'NO' midi song type. - + + + + Midi + MIDI - + Delete group or patch - + Kasuj grupÄ™ lub brzmienie New patch - + Nowe brzmienie New &Patch - + Nowe b&rzmienie Alt+P - + Alt+R New group - + Nowa grupa New &Group - + Nowa &grupa Alt+G - - - - - Contro&ller - - - - - Common: - + - + List of defined controllers - + Lista zdefiniowanych kontrolerów List of defined controllers. - + Lista zdefiniowanych kontrolerów. - + Min - + Min. - + Max - + Maks. - + Def - + DomyÅ›lnie - + Midi controller name - + Nazwa kontrolera MIDI - + Type: - Typ: + Typ: - + Midi controller type - - - - - RPN14 - + Typ kontrolera MIDI - - NRPN14 - - - - + Midi controller number high byte - + Numer kontrolera MIDI: starszy bajt - + Midi controller number low byte (* means drum controller) - + Numer kontrolera MIDI: mÅ‚odszy bajt (* oznacza kontroler perkusyjny) @@ -3274,23 +3394,25 @@ replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. - + MÅ‚odszy bajt numeru kontrolera MIDI. + +Wartość * oznacza kontroler perkusyjny dla Å›ladów perkusyjnych i instrumentów w standardzie GS/XG. Umożliwia przyporzÄ…dkowanie kontrolerów dla każdego instrumentu w zestawie perkusyjnym MusE. MÅ‚odszy bajt zostanie zastÄ…piony przez „ANote†w przypadku zestawu perkusyjnego. PrzykÅ‚ady: instrumenty GS/XG, kontrolery perkusyjne. * wild card - + * - + Range: - + Zakres: - + Minimum value. If negative, auto-translate. - + Wartość minimalna. JeÅ›li ujemna, użyta zostanie konwersja automatyczna. @@ -3313,52 +3435,57 @@ at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) - + Wartość minimalna. Jeżeli jest mniejsza od zera, zakres zostanie automatycznie przeliczony do wartoÅ›ci dodatnich. + +Jest to rozwiÄ…zanie przydatne dla kontrolerów, które prezentowane sÄ… z przesuniÄ™ciem wzglÄ™dem zera (zero bias). Dla przykÅ‚adu: „Panâ€: min.: -64, maks.: 63. Rzeczywisty zakres: min. 0, maks: 127 (bias = 64). „CoarseTuningâ€: min.: -24, maks.: 23. Rzeczywisty zakres: min.:40, maks.: 87 (bias = 64). +Wartość „bias†okreÅ›lana jest na podstawie typu kontrolera: 7-bitowy Controller7/RPN: bias = 64, 14-bitowy Controller14/RPN14: bias = 8192. + +Typ „Pitch†(odchylenie stroju) jest wyjÄ…tkiem. Dla niego wartość przesuniÄ™cia wynosi 0, mimo iż zawiera wartoÅ›ci ujemne. „Pitchâ€: min.: -8192, maks.: 8191. Rzeczywisty zakres: min.: -8192, maks.: 8191 (bias 0). - + Maximum value - + Wartość maksymalna - + Default: - + DomyÅ›lnie: - + L-Bank - L-Bank + L-Bank - - - - - + + + + + off - + - + Progr. - Nr programu + Nr programu - + ??? - ??? + ??? - + H-Bank - H-Bank + H-Bank - + Default value. Off: No default. - + DomyÅ›lna wartość. WyÅ‚.: brak wartoÅ›ci domyÅ›lnej. @@ -3377,189 +3504,264 @@ values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). - + DomyÅ›lna (poczÄ…tkowa) wartość. WyÅ‚. oznacza brak wartoÅ›ci domyÅ›lnej. + +Jeżeli podano wartość domyÅ›lnÄ…, zostanie ona wysÅ‚ana do danego kontrolera, w chwili jego dodania (w edytorze Pianoroll bÄ…dź w Edytorze Listy). Wartość zostanie wysÅ‚ana ponownie po ponownym wczytaniu utworu. +W każdym innym przypadku, kontroler pozostaje ustawiony zgodnie ze swojÄ… ostatniÄ… wartoÅ›ciÄ…. Kontrolery dodawane sÄ… także do utworu w momencie wystÄ…pienia zwiÄ…zanego z kontrolerem zdarzenia MIDI. + +Ważne! Należy zwrócić szczególnÄ… uwagÄ™ na domyÅ›lne wartoÅ›ci takich kontrolerów jak „Sustainâ€, czy „ResetAllControllersâ€. Najprawdopodobniej najlepszym rozwiÄ…zaniem jest ustawienie ich wartoÅ›ci domyÅ›lnych na „wyÅ‚.†(w edytorze Pianoroll bÄ…dź Edytorze Listy). off dont care - + wyÅ‚. - + Add common controller - + Dodaj typowy kontroler - - + &Add - &Dodaj + &Dodaj - + Alt+A - Alt+A + Alt+D - + Delete controller - + Kasuj kontroler - + Create a new controller - + Utwórz nowy kontroler New &Controller - + Nowy k&ontroler Alt+C - Alt+C - - - - Null Param Hi: - + Alt+O - + Null parameter number High byte - + Starszy bajt numeru pustego (null) kontrolera dodatkowego - + If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events from corrupting the RPN/NRPN controller. Typically, set to 127/127, or an unused RPN/NRPN controller number. - + JeÅ›li ustawiono, „puste†(null) numery kontrolerów dodatkowych zastanÄ… wysÅ‚ane po każdym zdarzeniu RPN/NRPN. Zapobiega to psuciu ustawionych wartoÅ›ci RPN/NRPN przez wystÄ™pujÄ…ce w miÄ™dzyczasie zdarzenia typu „dataâ€. +Zazwyczaj ustawiane na wartość 127/127 bÄ…dź numer nieużywanego kontrolera dodatkowego (RPN/NRPN). - + Lo: - + MÅ‚.: - + Null parameter number Low byte - + MÅ‚odszy bajt numeru pustego (null) kontrolera dodatkowego - + S&ysEx - + SysEx List: - + Lista SysEx: - + New SysE&x - + Nowy SysE&x Alt+X - + - - Drummaps - + + Patch Collections: + Zbiór brzmieÅ„: - - Patch Collections: - + + Note off mode: + Tryb Note off: - + + Note off mode + Tryb Note off + + + + Selects how to handle note off events. +The instrument can use note off events, + or not at all, or convert them to + zero-velocity note on events which helps + save midi interface bandwidth. +Refer to the instrument manufacturer's + midi implementation chart for details. + Pozwala na wybranie sposobu obsÅ‚ugi zdarzeÅ„ „note offâ€. + +Dany instrument może, lecz nie musi korzystać ze zdarzeÅ„ „note offâ€. Może też przedstawiać je, jako nuty o zerowej wartoÅ›ci parametru velocity (co pozwala na oszczÄ™dzanie przepustowoÅ›ci interfejsu MIDI). + +Szczegółowe informacje odnoÅ›nie konkretnego instrumentu, powinny być zawarte w tabeli implementacji MIDI dostarczanej przez producenta. + + + &Copy - &Kopiuj + &Kopiuj &Remove - + &UsuÅ„ &Up - + &Góra &Down - + D&ół Patch: - + Brzmienie: from - + od to - + do Bank Hi: - + Górny bank: Bank Lo: - + Dolny bank: - - &Help - P&omoc + + Contro&llers + Kontro&lery - - - new item - + + Null Parameters: Hi: + Bajt kontrolera pustego (null): St.: - - - What's this? - + + &Add Common... + &Dodaj typowy... - - - EditNoteDialogBase - - MusE: Enter Note - MuzA: Wstaw NutÄ™ + + W + - - OK - Akceptuj + + Comment: + Opis: + + + + &Initialization + &Inicjalizacja + + + + Instrument initialization sequence: + Sekwencja inicjalizacji instrumentu: + + + + &Add... + &Dodaj... + + + + &Change... + Z&mieÅ„... + + + + &Help + P&omoc + + + + &Close + Z&amknij + + + + Close + Zamknij + + + + + new item + nowy + + + + + What's this? + Co to jest? + + + + EditNoteDialogBase + + + MusE: Enter Note + MusE: wstaw nutÄ™ + + + + OK + Akceptuj @@ -3579,17 +3781,17 @@ Pitch: - Wysokość dźwiÄ™ku + Wysokość dźwiÄ™ku: Velocity On: - Predkość uderzenia (velocity) WÅ‚.: + PrÄ™dkość uderzenia (velocity) wÅ‚.: Velocity Off: - Predkość uderzenia (velocity) WyÅ‚.: + PrÄ™dkość uderzenia (velocity) wyÅ‚.: @@ -3597,7 +3799,7 @@ MusE: Enter SysEx - MuzA: Wstaw komunikat midi SysEx + MusE: wstaw komunikat MIDI SysEx @@ -3606,18 +3808,36 @@ + Name: + Nazwa: + + + Comment: Opis: - - OK - Akceptuj + + &Select... + &Wybierz... + + + + &OK + &Akceptuj + &Cancel + A&nuluj + + + OK + Akceptuj + + Cancel - Anuluj + Anuluj @@ -3630,18 +3850,137 @@ FLUIDSynthGuiBase + + FLUID Synth + + + + + Load + Åaduj + + + Delete - Kasuj + Kasuj + + + + Dump Info + Informacja + + ID + + + + + Fontname + Nazwa + + + + Chnl + Kan. + + + + Soundfont + + + + + Drum Chnl + Kan. perk. + + + + + Level + + + + + Width + + + + + Damping + + + + + Room Size + Rozmiar +pomieszczenia + + + + Reverb + + + + + CHANNEL SETUP + KONFIGURACJA KANAÅU + + + + Sine + + + + + Triangle + + + + Type - Typ + Typ + + + + Number + Numer + + + + Speed + + + + + Depth + + + + + Chorus + + + + + Gain + Wzmocnienie + + + + LOADED SOUNDFONTS + ZAÅADOWANE SOUNDFONTY FileDialogButtons - + + Home + Katalog domowy + + + Global Globalnie @@ -3656,21 +3995,29 @@ Katalog projektu - + read Midi Port Configuration - + Åaduj konfiguracjÄ™ portów MIDI - + write window states - + Zapisuj pozycje okien - + fdialogbuttons - + + + + + FluidSynthGui + + + Choose soundfont + Wybierz soundfont @@ -3678,67 +4025,67 @@ Range - Zakres + Zakres All Events - Wszytkie Elementy + Wszystkie elementy Selected Events - Zaznaczone Elementy + Zaznaczone elementy Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami MusE: Modify Note Length - + MusE: modyfikuj dÅ‚ugość nut Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami Values - Wartość + Parametry Rate: - CzÄ™stotliwość: + CzÄ™stotliwość: Offset: - Offset: + Offset: % - % + % lenNew = (lenOld * rate) + offset - + dÅ‚Nowa = (dÅ‚Stara * czÄ™st.) + offset OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj @@ -3746,10 +4093,10 @@ MusE: Global Settings - MuzA: Globalne Ustawienia + MusE: ustawienia globalne - + Audio Audio @@ -3767,29 +4114,29 @@ min. Meter Value - min. wartość miernika sygnaÅ‚u + Min. wartość miernika sygnaÅ‚u Midi - Midi + MIDI - + Ticks - Tykanie + Ticks - + - + 1024 1024 - + - + 2048 2048 @@ -3799,194 +4146,237 @@ 4096 - + Displayed Resolution (Ticks/Quarternote) - WyÅ›wietlana Rozdzielczość -(Tykanie/Ćwiartka) + WyÅ›wietlana rozdzielczość +(Ticks/Ćwiartka) - - + + 48 48 - - + + 96 96 - - + + 192 192 - - + + 384 384 - - + + 768 768 - - + + 1536 1536 - - + + 3072 3072 - - + + 6144 6144 - - + + 12288 12288 - + RTC Resolution (Ticks/Sec) RTC (Rzeczywista Rozdzielczość Zegara) -(Tykanie/Sekunda) +(Ticks/Sekunda) - + /sec /sek. - + GUI Refresh Rate CzÄ™stotliwość odÅ›wieżania interfejsu użytkownika - + + Warn if timer frequency is inadequate + Ostrzegaj, jeÅ›li czÄ™stotliwość zegara jest nieodpowiednia + + + + Track height + Wysokość Å›ladu + + + Use project save dialog - + Używaj okna dialogowego „Zapisz projekt†- + Some popup menus stay open (else hold Ctrl) - + Niektóre wyskakujÄ…ce menu pozostajÄ… otwarte (jeÅ›li nie, użyj Ctrl) - + Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. - + Pozwala, by niektóre wyskakujÄ…ce menu pozostaÅ‚y otwarte. +Jeżeli opcja jest nieaktywna, należy przytrzymać Ctrl, by menu nie byÅ‚o zamykane. - + In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. - + Przy niektórych elementach, Å›rodkowy klawisz myszy zwiÄ™ksza +wartoÅ›ci, a prawy je zmniejsza. Użytkownicy myszki bez +Å›rodkowego przycisku mogÄ… wybrać tÄ™ opcjÄ™, aby lewy klawisz +zachowywaÅ‚ siÄ™ jak klawisz Å›rodkowy. Use left mouse button for decreasing values - + Używaj lewego przycisku myszy do zmniejszania wartoÅ›ci - + Shift + Right click sets left range marker - + Shift + prawy klawisz ustawia lewy znacznik zakresu Allow adding hidden tracks in track list menu - + Pozwól na dodawanie ukrytych Å›ladów w menu „lista Å›ladów†- + Unhide tracks when adding hidden tracks - + Podczas dodawania ukrytych Å›ladów, spraw by byÅ‚y widoczne - - + + Smart focus - + - + After editing, controls will return focus to their respective canvas - + Probably should be more informative + Po edycji wartoÅ›ci, zostanie + aktywowany poprzednio + wybrany element - - Drum tracks - + + Show newly created midi velocity graphs per-note + DomyÅ›lnie pokazuj wykres velocity tylko dla nut o tej samej wysokoÅ›ci - - Only offer old-style drumtracks - + + px + - - Only offer new-style drumtracks - + + Enable borderless mouse. +For certain functions like zoom/pan. +Disable to use an alternate standard + method. + + WÅ‚Ä…cz mysz nieograniczonÄ… ramami + ekranu. DziaÅ‚a dla takich funkcji, jak + przesuwanie/powiÄ™kszanie. +WyÅ‚Ä…czenie tej opcji implikuje + użycie metody alternatywnej. - Prefer old-style drumtracks - + Enable borderless mouse. +For certain functions like zoom. +Disable to use an alternate standard + method. + WÅ‚Ä…cz mysz nieograniczonÄ… ramami + ekranu. DziaÅ‚a dla takich funkcji, jak + przesuwanie/powiÄ™kszanie. +WyÅ‚Ä…czenie tej opcji implikuje + użycie metody alternatywnej. - - Prefer new-style drumtracks - + + Borderless zoom/pan mouse (else use alternate method) + Mysz pozbawiona ograniczeÅ„ przy powiÄ™kszaniu/przesuwaniu + (w przeciwnym razie użyj metody alternatywnej) - + + Scrollable submenus + Przewijane podmenu + + + + Drum tracks + Åšlady perkusyjne + + + GUI Style - + Styl GUI MDI-subwindowness and sharing menus - + Podokna MDI i współdzielone menu Presets: - + Ustawienia: traditional MusE SDI - + Tradycyjne MDI MusE Cakewalk-like MDI - + MDI w stylu Cakewalk Borland-/Mac-like MDI - + MDI w stylu Borland/Mac @@ -4004,27 +4394,25 @@ A&nuluj - + Application - Zastosowanie + Aplikacja - + Start Muse - Uruchamianie MuzA + Uruchamianie MusE - start with last song - Otwórz ostatnio otwierany utwór + Otwórz ostatnio otwierany utwór - start with song - Åaduj przy uruchamianiu utwór + Åaduj przy uruchamianiu utwór - + Views Widoki @@ -4099,159 +4487,181 @@ Ustaw aktualnÄ… wartość - + show splash screen Pokaż okno o programie przy starcie - + Mixer A - + Mikser A Mixer B - + Mikser B - + show "Did you know?" dialog - + Pokaż okno „Czy wiesz, że...?†Start song - + Utwór przy uruchamianiu + + + + start &with last song + Åa&duj ostatni utwór + + + + start with &template + Åaduj &szablon + + + + sta&rt with song + Z&acznij nowy utwór - + min. Slider Val - + Min. wartość tÅ‚umika sygnaÅ‚u Enable denormal protection - + WÅ‚Ä…cz ochronÄ™ przed liczbami zdenormalizowanymi Enable output limiter - + WÅ‚Ä…cz limiter na wyjÅ›ciu External Waveditor - + ZewnÄ™trzny edytor audio External Waveditor command - + WywoÅ‚anie zewnÄ™trznego edytora Note: External editor opened from the internal editor. - + Uwaga: zewnÄ™trzny edytor otwiera siÄ™ z poziomu edytora wewnÄ™trznego. Dummy Audio Driver (settings require restart) - + Atrapa sterownika audio (ustawienia wymagajÄ… restartu) Hz - + Period size (Frames per period): - + Not sure if it is a correct translation + Rozmiar okresu (ramki na sekundÄ™) 16 - 16 + 16 - + Project directory - + Katalog projektu Projects: - + Projekty: - - + + ... - ... + ... - + Main Window - + Główne okno - + Choose start song or template - + Wybierz utwór uruchamiany przy starcie bÄ…dź szablon Reset to default - - - - - start with template - + Wróć do ustawieÅ„ domyÅ›lnych - + Start template or song: - + Szablon bÄ…dź utwór uruchamiany przy starcie: Read MIDI Ports configuration from file, or else automatically configure - + Åaduj plik z konfiguracjÄ… portów MIDI; +w przeciwnym razie konfiguruj automatycznie Read MIDI Ports configuration - + Åaduj konfiguracjÄ™ portów MIDI - + + Warn if opening file versions different than current version + Ostrzegaj, jeżeli otwierany jest plik zapisany w innej wersji programu + + + + Auto save (every 5 minutes if not playing/recording) + Automatyczne zapisywanie (co 5 minut, jeÅ›li nie jest uruchomione odtwarzanie/nagrywanie) + + + Try to use Jack Freewheel - + Staraj siÄ™ używać Jack Freewheel Speeds bounce operations - + PrzyÅ›piesza operacje zgrywania (bounce) Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. - + Jeżli to możliwe używaj trybu Jack Freewheel, który znaczÄ…co przyÅ›piesza operacje zgrywania (bounce). VST in-place - + Enable VST in-place processing (restart required) - + WÅ‚Ä…cz tryb przetwarzania „in-place†dla VST (wymagany restart) @@ -4259,18 +4669,25 @@ VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. - + WÅ‚Ä…cz tryb przetwarzania „in-place†dla VST. Należy +je wyÅ‚Ä…czyć, jeÅ›li wtyczki VST Ladspa nie dziaÅ‚ajÄ… +poprawnie bÄ…dź sÅ‚yszalne jest gÅ‚oÅ›ne sprzężenie +(nawet w przypadku, gdy wtyczki majÄ… możliwość pracy +w tym trybie). Zmiana tego ustawienia wymaga +restartu. Minimum control period - + Not sure. + Minimalny okres sterowania Minimum audio controller process period (samples). - + Not sure. + Minimalny okres dla procesu sterowania audio (próbki). @@ -4279,138 +4696,209 @@ controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. - + Minimalny okres dla procesu sterowania audio (próbki). +WpÅ‚ywa na czuÅ‚ość nastaw audio i szczegółowość + wykresów kontrolerów. Niska wartość zapewnia + szybkie i pÅ‚ynne sterowanie. W przypadku problemów + z wydajnoÅ›ciÄ…, należy zwiÄ™kszyć tÄ… wartość. 1 - 1 + 1 2 - 2 + 2 4 - 4 + 4 8 - 8 + 8 32 - 32 + 32 64 - 64 + 64 128 - 128 + 128 256 - 256 + 256 512 - 512 + 512 Sample rate - + CzÄ™stotliwość próbkowania Shorter periods give better midi playback resolution. - + Krótszy okres pozwala na wyższÄ… rozdzielczość podczas odtwarzania. - + + &Record all instruments + Nag&rywaj wszystkie instrumenty + + + + &Don't record hidden instruments + &Nie nagrywaj ukrytych instrumentów + + + + Don'&t record muted instruments + Nie nagrywaj &wyciszonych instrumentów + + + + Don't record &hidden or muted instruments + Nie nagrywaj &ukrytych oraz wyciszonych instrumentów + + + + Instrument initialization + Inicjalizacja instrumentów + + + + Send instrument initialization sequences + WysyÅ‚aj sekwencje inicjalizacji instrumentów + + + + Warn if instrument initialization sequences pending + Ostrzegaj, jeżeli inicjalizacja instrumentu nie zostaÅ‚a wykonana + + + + Send instrument controller default values if none in song, at init or rewind + Podczas startu lub przewijania wysyÅ‚aj domyÅ›lne wartoÅ›ci kontrolerów, +jeżeli nie wystÄ™pujÄ… w utworze + + + 8192 - 8192 + 8192 16384 - 16384 + 16384 32768 - 32768 + 32768 - + Midi Resolution (Ticks/Quarternote) - + Rozdzielczość MIDI +(Ticks/Ćwiartka) - - Record new style drum tracks - + + Live update wave parts while recording + OdÅ›wieżanie klocków audio podczas nagrywania - - Record all instruments - + + LV2 UI Open behavior + Sposób otwierania UI LV2 + + + + Use first available + Wybierz pierwsze dostÄ™pne + + + + Ask once + Zapytaj raz + + + + Ask always + Pytaj zawsze + + + + &Only offer old-style drumtracks + &Używaj wyÅ‚Ä…cznie Å›ladów perkusyjnych starego typu - Don't record hidden instruments - + Only offer new-style &drumtracks + Używaj wyÅ‚Ä…cznie Å›la&dów perkusyjnych nowego typu - Don't record muted instruments - + &Prefer old-style drumtracks + &Preferuj Å›lady perkusyjne starego typu - Don't record hidden or muted instruments - + Prefer &new-style drumtracks + Preferuj Å›lady perkusyjne &nowego typu - + + Record new style drum tracks + Nagrywaj Å›lady perkusyjne nowego typu + + + GUI Behaviour - + Zachowanie GUI - + Use old-style stop shortcut: - + Używaj starego skrótu dla funkcji stop Move single armed track with selection - + PrzesuÅ„ status nagrywania na Å›ladzie wraz z zaznaczeniem - + On Launch - + Przy starcie - + Behavior - + Zachowanie @@ -4418,62 +4906,62 @@ MusE: Legato - + Range - Zakres + Zakres All Events - Wszytkie Elementy + Wszystkie elementy Selected Events - Zaznaczone Elementy + Zaznaczone elementy Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami Settings - &Ustawienia + Ustawienia ticks - + ticks Minimum Length - + DÅ‚ugość minimalna Allow shortening notes - + Pozwalaj na skracanie nut OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj @@ -4481,7 +4969,7 @@ MusE: Midi Input Plugin: Transpose - MuzA: Wtyczka WejÅ›cia Midi: Transponuj + MusE: wtyczka wejÅ›cia MIDI: transponuj @@ -4509,7 +4997,7 @@ MusE: Midi Input Plugin: Remote Control - MuzA: Wtyczka WejÅ›cia Midi: Zdalne Sterowanie + MusE: wtyczka wejÅ›cia MIDI: zdalne sterowanie @@ -4549,7 +5037,7 @@ Insert rest (step rec) - + Wstaw pauzÄ™ (nagr. krok.) @@ -4567,17 +5055,17 @@ MDI subwin - + MDI podokno Shares menu when subwin - + Współdzieli menu w podoknie Shares menu when free - + Współdzieli menu jeÅ›li wolne @@ -4585,27 +5073,72 @@ MusE: Metronome Config - MuzA: Konfiguracja Metronomu + MusE: konfiguracja metronomu - + Metronome Metronom - + Audio Beep Audio Beep - + + Two samples (old samples) + Dwie próbki (stare sample) + + + + Four samples (new samples, with accents) + Cztery próbki (nowe sample z akcentami) + + + + Volume + GÅ‚oÅ›ność + + + + Audio master + + + + + Meas + Takt + + + + Beat + Rytm + + + + Accent1 + Akcent1 + + + + Accent2 + Akcent2 + + + + Sample + Sampel + + + MIDI Click MIDI Klik - + Midi Channel - KanaÅ‚ Midi + KanaÅ‚ MIDI @@ -4630,20 +5163,25 @@ Midi Port - Numer Portu Midi + Numer portu MIDI - + + Disabled since jack does not support it + WyÅ‚Ä…czone, ponieważ nie jest wspierane przez Jack + + + Precount Nabicie tempa - + enable - wÅ‚Ä…cz + WÅ‚Ä…cz - + Bars Takty @@ -4653,12 +5191,12 @@ Ze Å›ladu tempo/metrum (Mastertrack) - + / / - + Signature Metrum @@ -4673,7 +5211,7 @@ Preroll - + &Apply &Zastosuj @@ -4685,42 +5223,41 @@ &Cancel - &Anuluj + A&nuluj - + Choose outputs... - + Wybierz wyjÅ›cia... - + + + + + 50 - 50 - - - - % Audio volume - + 50 - + Hint: Enable metronome in Transportpanel - + Podpowiedź: wÅ‚Ä…cz metronom na Panelu Transportu Alt+A - Alt+A + Alt+Z Alt+O - + Alt+A Alt+C - Alt+C + Alt+N @@ -4728,77 +5265,61 @@ Midi control - + Sterowanie MIDI Port: - + Channel: - + KanaÅ‚: Control type: - + Typ kontrolera: + + + + &Learn + &Ucz siÄ™ - Control7 - Kontroler7 + Kontroler7 - Control14 - Kontroler14 + Kontroler14 - RPN - RPN + RPN - NRPN - NRPN - - - - RPN14 - - - - - NRPN14 - + NRPN - Pitch - Transpozycja + Transpozycja - Program - Program + Program - + Hi: - + St.: Lo: - - - - - Learn - + MÅ‚.: @@ -4806,24 +5327,25 @@ MusE: Midi Input Filter - Filtr WejÅ›cia Midi + MusE: filtr wejÅ›cia MIDI Record Filter - Filtr Komunikatów Przych. (Midi In) + Filtr Komunikatów Przych. (MIDI In) Note On - DźwiÄ™ki + DźwiÄ™ki (Note On) Poly Pressure - Åšredni nacisk po uderzeniu + Polifoniczny nacisk po uderzeniu +(Poly Pressure) @@ -4835,13 +5357,13 @@ Program Change - Zmiana programu (program change) + Zmiana programu (Program Change) After Touch - Nacisk pod uderzeniu (aftertouch) + Docisk (Aftertouch) @@ -4956,7 +5478,7 @@ MusE: Midi Input Transformator - MuzA: Transformator WejÅ›cia Midi + MusE: przeksztaÅ‚canie wejÅ›cia MIDI @@ -4970,7 +5492,7 @@ - + @@ -4978,8 +5500,8 @@ Równe - - + + @@ -4987,67 +5509,73 @@ Nierówne - + Note DźwiÄ™k - + Poly Pressure - Åšredni nacisk po uderzeniu (Poly Pressure) + Polifoniczny nacisk po uderzeniu (Poly Pressure) - - + + Control Change - Control Change + Control Change - - + + Aftertouch - Nacisk pod uderzeniu (aftertouch) + Docisk (Aftertouch) - - + + Pitch Bend Odchylenie stroju (Pitch Bend) - - + + NRPN - NRPN + NRPN - - + + RPN - RPN + RPN + + + + + Program + Program - - + + Value 2 Wartość 2 - - + + - + Value 1 Wartość 1 - + Event Type - Rodzaje komunikatów midi + Rodzaje komunikatów MIDI @@ -5091,83 +5619,83 @@ - + Channel KanaÅ‚ - - + + Port Port - + Processing Przetwarzanie - + - + Keep Pozostaw - - + + - + Fix Ustal - + - + Plus Plus - + - + Minus Minus - + - + Multiply Przemnóż - + - + Divide Podziel - + - + Invert Odwróć - + ScaleMap - Zakres Skali + Zakres skali @@ -5177,21 +5705,26 @@ - + Dyn Dyn - + - + Random Losowo - + + Toggle + PrzeÅ‚Ä…cz + + + Modules ModuÅ‚y @@ -5273,7 +5806,7 @@ delete preset - kasuj ustawienie + Kasuj ustawienie @@ -5283,7 +5816,7 @@ PresetList - Lista UstawieÅ„ + Lista ustawieÅ„ @@ -5291,7 +5824,7 @@ MusE: Midi Sync - MuzA: Synchronizacja Midi + MusE: synchronizacja MIDI @@ -5351,10 +5884,25 @@ subframe - subramka + podramka + + + + When in slave mode, tempo is + controlled externally. +MusE can sync to midi clock, or MTC quarter frame sync. +Enabled inputs in the list will + be in effect (RMC, RMMC, RMTC). + W trybie slave tempo jest kontrolowane + zewnÄ™trznie. +MusE może siÄ™ zsynchronizować do zegara + MIDI lub do ćwiartki ramki + synchronizacji MTC. +ObowiÄ…zujÄ… aktywne wejÅ›cia z listy + (RMC, RMMC, RMTC). - + 24 24 @@ -5366,12 +5914,12 @@ 30D - 30D + 30D 30N - 30N + 30N @@ -5391,28 +5939,29 @@ Sync receiving and sending - + WysyÅ‚anie i odbiór komunikatów synchronizacji Send and receive Jack transport - + WysyÅ‚aj i odbieraj transport Jack Send and receive Jack transport information, including stop, start and position. - + WysyÅ‚aj i odbieraj transport Jack, +wÅ‚Ä…czajÄ…c w to pozycje start i stop. Use Jack transport - + Używaj transportu Jack Make MusE the Jack transport Timebase Master - + Ustaw MusE jako Timebase Master dla transportu Jack @@ -5422,36 +5971,50 @@ MusE will try to become master, but other Jack clients can also take over later. You can always click here again for Master. - + Ustaw MusE jako Timebase Master dla transportu Jack. + +Umożliwia to serwerowi Jack pokazywanie czasu w formacie + MusE (takty, uderzenia i ticks). +MusE spróbuje dziaÅ‚ać w trybie Master, jednak + pozostali klienci Jack w każdej chwili mogÄ… + przejąć jego rolÄ™. +W takiej sytuacji, klikniÄ™cie w tym miejscu + pozwala na ponowne przejÄ™cie roli Master. Jack transport Timebase Master - + Timebase Master dla transportu Jack Control MusE timing by external midi clock or MTC sync - + Kontroluj czas MusE zewnÄ™trznym zegarem MIDI bÄ…dź za pomocÄ… synchronizacji MTC - When in slave mode, tempo is controlled externally. -MusE can sync to midi clock, or MTC quarter frame sync. +MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). - + W trybie slave tempo jest kontrolowane + zewnÄ™trznie. +MusE może siÄ™ zsynchronizować do zegara + MIDI lub do ćwiartki ramki + synchronizacji MTC. +ObowiÄ…zujÄ… aktywne wejÅ›cia z listy + (RMC, RMMC, RMTC). + W trybie slave tempo - + Slave to external sync - + Synchronizacja zewnÄ™trzna (tryb slave) Averaging applied to recorded external tempo changes. - + Użyj uÅ›redniania podczas nagrywania zmian tempa z zewnÄ…trz. @@ -5481,27 +6044,61 @@ audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. - + ZewnÄ™trzny zegar MIDI może być bardzo + niestabilny. Na jego podstawie wyznaczane + i nagrywane jest tempo utworu. + Wskazane jest wiÄ™c jego uÅ›rednianie i tym + samym ograniczenie liczby zapisywanych + zmian tempa. + +Najmniejsze: 2 sekcje 4/4 = 8 etapów. +UÅ›rednianie do nuty 1/8T, może powodować + niestabilność. + +MaÅ‚e: 3 sekcje 12/8/4 = 24 etapy. +UÅ›rednianie do nuty 1/4, nadal może + powodować niestabilność. + +Åšrednie: 3 sekcje 28/12/8 = 48 etapów. +UÅ›rednianie do nuty 1/2. Mniejsza + niestabilność. + +Duże: 4 sekcje 48/48/48/48 = 192 etapy. + Rekomendowane, gdy utwór ma tylko jedno tempo. + MogÄ… być użyte bardzo niskie wartoÅ›ci + kwantyzacji. + +Duże pre-detect: 4 sekcje 8/48/48/48 = 152 + etapy + „large step pre-detector†w + pierwszym etapie. + Rekomendowane, jeżeli istnieje możliwość + wystÄ…pienia nagÅ‚ych i znaczÄ…cych zmian tempa. + +Brak: używane jedynie w przypadku koniecznoÅ›ci + synchronizacji odtwarzania audio. Uwaga: może + zapisywać tysiÄ…ce zmian tempa na minute. Może + znaczÄ…co spowolnić MusE, rozmiar utworu może + być bardzo duży. Tempo record averaging - + UÅ›rednianie tempa podczas nagrywania bpm - + Tempo record quantization - + Kwantyzacja tempa podczas nagrywania Send start to first clock delay - + Opóźnienie pomiÄ™dzy start, a pierwszym komunikatem zegara (sync delay) @@ -5509,22 +6106,24 @@ to synchronize to MusE. This value is the delay from sending start to sending the first clock. - + Pozwala „powolnym†urzÄ…dzeniom na zsynchronizowanie + siÄ™ do MusE. Wartość ta stanowi opóźnienie miÄ™dzy + wysÅ‚aniem start, a pierwszym komunikatem zegara (sync delay). ms - + Send sync delay - + Opóźnienie przy wysyÅ‚aniu synchronizacji Note: Sync delay and MTC sync currently not fully implemented - + Uwaga: opóźnienie synchronizacji i synchronizacja MTC nie sÄ… w tej chwili caÅ‚kowicie zaimplementowane @@ -5535,82 +6134,107 @@ Info o Å›ladzie - + output channel kanaÅ‚ wyjÅ›ciowy - - + + % % - + output port port wyjÅ›ciowy - - - - + + + + off - WyÅ‚. + wyÅ‚. - + Transp. Transpozycja - + Channel Info Info o kanale - + Rec: - + Nagr.: - + + Change note length in percent of actual length + Zmienia dÅ‚ugość nut w stosunku procentowym do obecnej dÅ‚ugoÅ›ci + + + + Offset playback of notes before or after actual note + Opóźnienie/wyprzedzenie przy odtwarzaniu zapisanych na Å›cieżce dźwiÄ™ków + + + + Transpose notes up or down + Transpozycja nut (w górÄ™ bÄ…dź w dół) + + + Bank Select MSB. Ctrl-double-click on/off. - + Wybór banku MSB (Bank Select MSB). Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza. Bank Select LSB. Ctrl-double-click on/off. - + Wybór banku LSB (Bank Select LSB). Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza. + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + <html><head/><body><p>Dodaj lub odejmij prÄ™dkość uderzenia (velocity) do/od nut zapisanych na Å›ladzie.</p><p><span style=" font-style:italic;">Zakres velocity nut to 0-127, może siÄ™ wiÄ™c <br />zdarzyć, że nie bÄ™dzie możliwe <br />przyporzÄ…dkowanie w caÅ‚oÅ›ci tak obliczonej <br />wartoÅ›ci (note velocity + velocity), gdyż <br />znajdzie siÄ™ ona poza dopuszczalnym <br />zakresem.</span></p></body></html> + + + + Compress the notes velocity range, in percent of actual velocity + Kompresuj zakres prÄ™dkoÅ›ci uderzenia (Note Velocity), o procent w stosunku do wartoÅ›ci obecnej + + + Program. Ctrl-double-click on/off. - + Program. Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza. Prog - + Program Volume. Ctrl-double-click on/off. - + GÅ‚oÅ›ność. Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza. Vol - + GÅ‚oÅ›n. (vol.) Change stereo position. Ctrl-double-click on/off. - + ZmieÅ„ pozycjÄ™ w panoramie stereo. Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza. @@ -5618,116 +6242,127 @@ Panorama - + + Select instrument + Wybierz instrument + + + Delay Opóźnienie - + H-Bank - H-Bank + H-Bank - + Compr. Kompr. - + L-Bank - L-Bank + L-Bank - + Velocity - PrÄ™dk. uderz. (vel) + PrÄ™dk. uderz. (vel.) - + Length DÅ‚ugość - + all midi events are sent to this output channel - + Wszystkie zdarzenia MIDI wysyÅ‚ane sÄ… do tego kanaÅ‚u - + Out ch - + KanaÅ‚ wyj. - + input routing - + PoÅ‚Ä…czenia wejÅ›ciowe output routing - poÅ‚Ä…czenie WyjÅ›cia + PoÅ‚Ä…czenia wyjÅ›ciowe input detect - + Zdarzenie wejÅ›ciowe Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. - + Kontrolka zdarzeÅ„ wejÅ›ciowych. Sygnalizuje + nastÄ™pujÄ…ce zdarzenia (na wybranych kanaÅ‚ach + i wybranych portach MIDI): wszystkie zdarzenia + note on-off, zdarzenia kontrolerów, aftertouch, + program change, pitch bend. Nie sygnalizuje + komunikatów SysEx i zdarzeÅ„ typu realtime. W - + Midi thru - + MIDI thru Pass input events through ('thru') to output. - + Przekazuje zdarzenia wejÅ›ciowe do wyjÅ›cia (through - „thruâ€). - + Select instrument patch - + Wybierz brzmienie (patch) instrumentu + <unknown> - + <nieznany> - + Add all settings to song - + Dodaj do utworu wszystkie poniższe ustawienia All - Wszystko + Wszystko - + Add bank + prog settings to song - + Dodaj do utworu ustawienia banków i programów Add vol setting to song - + Dodaj do utworu ustawienia gÅ‚oÅ›noÅ›ci (volume) Add pan setting to song - + Dodaj do utworu ustawienia panoramy (pan) @@ -5735,7 +6370,7 @@ MusE: Midi Transformator - MuzA: Midi Transformator + MusE: przeksztaÅ‚canie komunikatów MIDI @@ -5776,7 +6411,7 @@ Event Type - Rodzaje komunikatów midi + Rodzaje komunikatów MIDI @@ -5805,37 +6440,37 @@ Poly Pressure - Åšredni nacisk po uderzeniu + Polifoniczny nacisk po uderzeniu Control Change - Control Change + Control Change Aftertouch - Nacisk pod uderzeniu (aftertouch) + Docisk (Aftertouch) Pitch Bend - Odchylenie stroju (Pitch Bend) + Odchylenie stroju (Pitch Bend) NRPN - NRPN + NRPN RPN - RPN + RPN @@ -5885,7 +6520,7 @@ ScaleMap - Zakres Skali + Zakres skali @@ -5896,7 +6531,7 @@ Dyn - Dyn. + Dyn. @@ -5948,7 +6583,7 @@ Unequal - Nnierówne + Nierówne @@ -6081,7 +6716,7 @@ MusE: Set Mixdown Wavefile - MuzA: Ustaw miks do zgrania do pliku Wavefile + MusE: ustaw parametry zgrania miksu do pliku audio @@ -6144,65 +6779,84 @@ MusE: Move Notes - + MusE: przesuÅ„ nuty Range - Zakres + Zakres All Events - Wszytkie Elementy + Wszystkie elementy Selected Events - Zaznaczone Elementy + Zaznaczone elementy Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami Value - Wartość + Wartość Move by - + PrzesuÅ„ o ticks - + OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj + + + + MusECore::AudioPreviewDialog + + + Auto play + Odtwarzanie automatycznie + + + + + Stop + Stop + + + + Play + Odtwarzanie MusECore::Song - + Jack shutdown! - + WyÅ‚Ä…czenie serwera Jack! @@ -6221,103 +6875,144 @@ To proceed check the status of Jack and try to restart it and then . click on the Restart button. - + Jack wykryÅ‚ problem z wydajnoÅ›ciÄ…, co spowodowaÅ‚o odÅ‚Ä…czenie +MusE. +MogÅ‚o tak siÄ™ zdarzyć z kilku powodów. Np.: +- problem z wydajnoÅ›ciÄ… systemu, +- bÅ‚Ä…d w MusE (lub innej podÅ‚Ä…czonej aplikacji), +- losowy problem, który w przyszÅ‚oÅ›ci może siÄ™ nie pojawić, +- Jack zostaÅ‚ zatrzymany intencjonalnie, +- Jack ulegÅ‚ uszkodzeniu i zostaÅ‚ zatrzymany. +Jeżeli problem powtarza siÄ™, zapraszamy do przedstawienia go +na liÅ›cie mailowej MusE (informacja o subskrypcji znajduje +siÄ™ na stronie domowej MusE dostÄ™pnej w menu Pomoc). + +Aby kontynuować, należy sprawdzić status serwera Jack +i spróbować go zrestartować. NastÄ™pnie należy kliknąć na +przycisk Restart. - - + + Automation: - + Automatyka: - + previous event - + poprzedni element next event - + nastÄ™pne zdarzenie - + set event - + ustaw zdarzenie - - + + add event - + dodaj zdarzenie - - + + erase event - + skasuj zdarzenie - + erase range - + skasuj zakres clear automation - + wyczyść automatykÄ™ Midi control - + Sterowanie MIDI Assign - + PrzyporzÄ…dkuj Clear - + Wyczyść Clear all controller events? - + WyczyÅ›cić wszystkie zdarzenia kontrolerów? &Ok - &Akceptuj + &Akceptuj &Cancel - + A&nuluj - + MusE: Tempo list - + MusE: Lista Tempa External tempo changes were recorded. Transfer them to master tempo list? - + ZostaÅ‚y nagrane zewnÄ™trzne zmiany tempa. +Czy przenieść je do głównej listy tempa? + + + + Do you want to process ALL or only selected events? + Przetwarzać WSZYSTKIE, czy wybrane elementy? + + + + &Selected + &Wybrane - + + &All + W&szystkie + + + MusE - external script failed - + MusE - wystÄ…piÅ‚ bÅ‚Ä…d w skrypcie zewnÄ™trznym MusE was unable to launch the script, error message: %1 - + MusE: nie można uruchomić skryptu, informacja o bÅ‚Ä™dzie: +%1 + + + + Und&o + &Cofnij + + + + Re&do + &Przywróć @@ -6329,7 +7024,7 @@ move parts copy parts Kliknij na strzaÅ‚kÄ™. -StrzaÅ‚kÄ… możesz zaznaczać, przesuwać i kopiować klocki +StrzaÅ‚kÄ… możesz zaznaczać, przesuwać i kopiować klocki. select Pencil Tool: @@ -6366,7 +7061,7 @@ select Quantize Tool: insert display quantize event Ustaw Kwantyzator -(ustawia kwantyzacjÄ™ wstawiania komunikatów midi). +(ustawia kwantyzacjÄ™ wstawiania komunikatów MIDI). select Drawing Tool @@ -6417,138 +7112,159 @@ Main application font, and default font for any controls not defined here. - + Główna czcionka aplikacji i domyÅ›lna czcionka dla +elementów, które nie sÄ… tu wymienione. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. - + Not sure what is 'control panel entry box'. + Sekcje kanałów miksera i racki efektów. +Panel informacyjny Å›ladów MIDI. +Okno dialogowe panelu kontrolnego MIDI. Transport controls. - + Tekst na panelu transportu. Time scale upper, and time signature. Controller graph and S/X buttons. - + Górna linijka czasu i metrum. +Wykres kontrolera i przyciski S/X. Time scale lower, and arranger part name overlay. - + Dolna linijka czasu i nazwy klocków w oknie głównym. Tempo scale, and markers. - + Linijka tempa i znaczniki. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. - + Etykiety miksera. Elementy sÄ… automatycznie powiÄ™kszkane do wybranego rozmiaru czcionki. +Linie Å‚amane sÄ… tylko w miejscu spacji. Maximum mixer label auto-font-sizing font size. - + Maksymalny rozmiar czcionki etykiet miksera. Global opacity (opposite of transparency). - + Globalna przeźroczystość (wÅ‚aÅ›ciwie poziom nieprzeźroczystoÅ›ci). Standard - + Standardowe Custom - + Użytkownika - + Keep Qt system style - + Zachowaj systemowy temat Qt + + + + Do you really want to reset colors to theme default? + Czy naprawdÄ™ chcesz powrócić do domyÅ›lnych kolorów tematu? + + + + &Ok + &Akceptuj + + + + &Cancel + A&nuluj - + MusE: load image - Muza: ZaÅ‚aduj obrazek. + MusE: zaÅ‚aduj obrazek - + Select style sheet - + Wybierz arkusz stylów Qt style sheets (*.qss) - + Arkusze stylów Qt (*.qss) MusEGui::Arranger - + Enable Recording - UdostÄ™pnij nagrywanie + Aktywuj nagrywanie Mute/Off Indicator - + Kontrolka wyciszenia (mute) Solo Indicator - Solo Wzkaźnik + Kontrolka trybu solo Track Type - Typ Å›ladu + Typ Å›ladu Track Name - + Nazwa Å›ladu Midi output channel number or audio channels - + Numer wyjÅ›ciowego kanaÅ‚u MIDI bÄ…dź kanałów audio Midi output port or synth midi port - + Port wyjÅ›ciowy MIDI bÄ…dź port MIDI syntezatora programowego Time Lock - Zablokuj Åšlad + Zablokuj Å›lad (Time Lock) Automation parameter selection - + Wybór parametrów automatyki Notation clef - + Klucz zapisu nutowego Enable recording. Click to toggle. - + Aktywuj nagrywanie. Kliknij, aby przeÅ‚Ä…czyć. @@ -6556,33 +7272,40 @@ Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! - + Can't uderstand the meaning of the last sentences. So, not translating it now. + Kontrolka wyciszenia (mute). Kliknij, aby przeÅ‚Ä…czyć. +Prawy klawisz wÅ‚Ä…cza/wyÅ‚Ä…cza Å›lad. Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. - + Kontrolka trybu solo. Kliknij, aby przeÅ‚Ä…czyć. +Åšlady podÅ‚Ä…czone do modyfikowanego przejdÄ… +w „poÅ›redni†tryb solo, co jest zaznaczone +ciemnym kwadratem. Track type. Right-click to change midi and drum track types. - + Typ Å›ladu. Prawy klawisz zmienia typ Å›ladu perkusyjnego i MIDI. Track name. Double-click to edit. Right-click for more options. - + Nazwa Å›ladu. Podwójny klik - edycja. Prawy klawisz pojazuje wiÄ™cej opcji. Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. - + Åšlad MIDI/perkusyjny: numer kanaÅ‚u wyjÅ›ciowego. +Åšlad audio: kanaÅ‚y. +Åšrodkowy/prawy klawisz zmienia wartość. @@ -6590,110 +7313,106 @@ Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. - + Åšlad MIDI/perkusyjny: port wyjÅ›ciowy. +Åšlad syntezatora software'owego: przyporzÄ…dkowane porty MIDI. +Åšrodkowy/prawy klawisz zmienia wartość. +Prawy klawisz pokazuje GUI syntezatora. Time lock - + Zablokuj Å›lad (Time Lock) Notation clef. Select this tracks notation clef. - + Klucz zapisu nutowego. Pozwala na wybranie klucza zapisu nutowego. Arranger - Główne Okno + Główne Okno Cursor - + Kursor - + Off - WyÅ‚. + WyÅ‚. Bar - + Takt - + Snap - + PrzyciÄ…ganie Len - DÅ‚ugość + DÅ‚ugość song length - bars - + dÅ‚ugość utworu - takty - Type - Typ + Typ - NO - Brak + Brak - GM - GM + GM - GS - GS + GS - XG - XG + XG - - midi song type - Utwór Midi + Utwór Midi - + Pitch - Transpozycja + Transpozycja midi pitch - transpozycja midi + Transpozycja MIDI global midi pitch shift - globalna transpozycja midi + Globalna transpozycja MIDI Tempo - Tempo + Tempo midi tempo - Tempo Midi + Tempo MIDI @@ -6703,352 +7422,410 @@ TrackInfo - Info o Å›ladzie + Info o Å›ladzie R - R + R M - + S - S + S C - T + T Track - Åšlad + Åšlad Port - Port + Port Ch - + Kan. T - B + B Automation - Au&tomatyka + Automatyka Clef - + Klucz + + + + MusEGui::ArrangerColumns + + + Control7 + Kontroler7 + + + + Control14 + Kontroler14 + + + + RPN + RPN + + + + NPRN + + + + + RPN14 + + + + + NRPN14 + + + + + Pitch + Odchylenie (Pitch bend) + + + + Program + Program + + + + Aftertouch + Docisk (Aftertouch) MusEGui::ArrangerView - + MusE: Arranger - + MusE: Główne Okno - + + D&elete + &Kasuj + + + C&ut - + &Wytnij &Copy - &Kopiuj + K&opiuj Copy in range - + Kopiuj zakres &Paste - &Wklej + &Wklej - Paste (show dialog) - + Paste c&lone + Wklej klo&n - Paste c&lone - + Paste to selected &track + Wkle&j do zaznaczonego Å›ladu - Paste clone (show dialog) - + Paste clone to selected trac&k + Wklej klon do zaz&naczonego Å›ladu + + + + Paste (show dialo&g) + Wklej (pokaż okno dialo&gowe) &Insert Empty Measure - + &Wstaw pusty takt Delete Selected Tracks - Skasuj Zaznaczone Åšlady + Skasuj zaznaczone Å›lady Duplicate Selected Tracks - + Kopiuj zaznaczone Å›lady Shrink selected parts - + Skróć zaznaczone klocki Expand selected parts - + Rozszerz zaznaczone klocki Purge hidden events from selected parts - + UsuÅ„ ukryte elementy z zaznaczonych klocków Add Track - Dodaj Åšlad + Dodaj Å›lad Select - Wybierz + Wybierz Select &All - Zaznacz &wszystko + Zaznacz &wszystko &Deselect All - &Odznacz wszystko + &Odznacz wszystko Invert &Selection - Odwróć &zaznaczenie + Odw&róć zaznaczenie &Inside Loop - &PomiÄ™dzy lokatorami. + &PomiÄ™dzy lokatorami &Outside Loop - &Poza obrÄ™bem lokatorów + &Poza obrÄ™bem lokatorów All &Parts on Track - Wszystkie &klocki na Å›ladzie + Wszystkie &klocki na Å›ladzie Score - + Zapis nutowy all tracks in one staff - + Wszystkie Å›lady na jednej piÄ™ciolinii one staff per track - + Jedna piÄ™ciolinia na Å›lad New score window - + Nowe okno z zapisem nutowym Pianoroll - Pianoroll + Pianoroll Drums - Edytor Perkusji + Edytor Perkusji List - Edytor Lista + Edytor Lista Wave - + Edytor Audio Mastertrack - Åšlad Tempo/Metrum + Åšlad Tempo/Metrum Graphic - Edytor Graficzny Tempo/Metrum + Edytor Graficzny Tempo/Metrum Midi &Transform - &PrzeksztaÅ‚canie Midi + &PrzeksztaÅ‚canie MIDI Global Cut - Globalne WyciÄ™cie + Globalne wyciÄ™cie Global Insert - Globalne Wstawienie + Globalne wstawienie Global Split - Globalne Podzielenie + Globalne podzielenie Global Cut - selected tracks - + Globalne wyciÄ™cie - zaznaczone Å›lady Global Insert - selected tracks - + Globalne wstawienie - zaznaczone Å›lady Global Split - selected tracks - + Globalne podzielenie - zaznaczone Å›lady &Edit - + &Edycja - + &Structure - &Globalne + &Globalne Functions - Opcje + DziaÅ‚ania &Quantize Notes - + &Kwantyzuj nuty Change note &length - + Modyfikuj &dÅ‚ugość nut Change note &velocity - + Modyfikuj &prÄ™dkość uderzenia (velocity) Crescendo/Decrescendo - + Crescendo/decrescendo Transpose - Transponuj + Transponuj Erase Events (Not Parts) - + Kasuj zdarzenia (pozostaw klocki) Move Events (Not Parts) - + PrzesuÅ„ nuty (pozostaw klocki) Set Fixed Note Length - + Ustaw staÅ‚Ä… dÅ‚ugość nut Delete Overlapping Notes - + Kasuj nakÅ‚adajÄ…ce siÄ™ nuty Legato - + Window &Config - + &Konfiguracja okna Configure &custom columns - + Konfiguruj &kolumny użytkownika - + Remove track(s) - + UsuÅ„ Å›lad(y) Are you sure you want to remove this track(s)? - + Czy na pewno usunąć te Å›lad(y)? - + New - Nowy + Nowy - + Changed Settings - + Zmienione ustawienia @@ -7056,1122 +7833,1319 @@ cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. (we'll try to fix that) - + Niestety, zmiana ustawieÅ„ kolumn w Oknie Głównym +nie może być zastosowana podczas dziaÅ‚ania MusE. +Wymagany jest restart MusE. Przepraszamy. +(spróbujemy poprawić tÄ™ niedogodność) MusEGui::AudioMixerApp - + &Create - + &Utwórz &View - &PrzeglÄ…daj + &PrzeglÄ…daj + Strips + Sekcje kanałów + + + Routing - Konfiguracja poÅ‚Ä…czeÅ„ + Konfiguracja poÅ‚Ä…czeÅ„ Show Midi Tracks - + Pokaż Å›lady MIDI Show Drum Tracks - + Pokaż Å›lady perkusyjne Show New Style Drum Tracks - + Pokaż Å›lady perkusyjne nowego typu Show Wave Tracks - + Pokaż Å›lady audio Show Inputs - + Pokaż wejÅ›cia Show Outputs - + Pokaż wyjÅ›cia Show Groups - + Pokaż grupy Show Auxs - + Pokaż szyny (aux) Show Synthesizers - + Pokaż syntezatory + + + + Traditional order + Kolejność tradycyjna + + + + Arranger order + Kolejność z Głównego Okna + + + + User order + Kolejność użytkownika + + + + Show all hidden strips + Pokaż wszystkie ukryte kanaÅ‚y + + + + Unhide strip: + Pokaż ukryty kanaÅ‚: + + + + (no hidden strips) + (brak ukrytych kanałów) MusEGui::AudioStrip - + panorama - Panorama + panorama - aux send level - Poziom syngaÅ‚u na wysyÅ‚ce + poziom sygnaÅ‚u na wysyÅ‚ce - + Pan - Panorama + Panorama - + + calibration gain + kalibracja wzmocnienia + + + + aux send level (dB) + poziom sygnaÅ‚u na wysyÅ‚ce (dB) + + + 1/2 channel - kanaÅ‚ m/s + kanaÅ‚ m/s Pre - Przed + Przed pre fader - post fader - przed/ za tÅ‚umikiem + przed/za tÅ‚umikiem - + + Gain + Wzm. + + + + Volume/gain + GÅ‚oÅ›ność/wzmocnienie + + + + dB - dB + dB - + record - + nagrywaj mute - wycisz + wycisz record downmix - zgraj miks + zgraj miks solo mode - + tryb solo off - + wyÅ‚. input routing - + poÅ‚Ä…czenia wejÅ›ciowe output routing - poÅ‚Ä…czenie WyjÅ›cia + poÅ‚Ä…czenia wyjÅ›ciowe Off - WyÅ‚. + WyÅ‚. Read - Czytaj + Czytaj Touch - Dotknij + Dotknij Write - Zapisz + Zapisz automation type - typ automatyki + typ automatyki + + + + L meter peak/clip + Lewy miernik poziomu szczytowego/przesterowania + + + + R meter peak/clip + Prawy miernik poziomu szczytowego/przesterowania MusEGui::BigTime - + format display - + format wyÅ›wietlania bar - + takt beat - + uderzenie tick - + minute - minuta + minuta second - sekunda + sekunda frame - ramka + ramka subframe - subramka + podramka - + MusE: Bigtime - MuzA: Duży zegar + MusE: Duży Zegar + + + + MusEGui::Canvas + + + Tools: + NarzÄ™dzia: + + + + MusEGui::ClipListEdit + + + MusE: Clip List Editor + MusE: Edytor Lista Klipów + + + + Window &Config + Konfigura&cja okna + + + + MusEGui::ComboQuant + + + + + Off + WyÅ‚. + + + + MusEGui::CompactPatchEdit + + + Hi + + + + + Lo + + + + + Prg + + + + + Patch name + Nazwa brzmienia + + + + Patch high-bank number +(Ctrl-double-click on/off) + Numer górnego banku dla danej barwy +(Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza) + + + + Patch low-bank number +(Ctrl-double-click on/off) + Numer dolnego banku dla danej barwy +(Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza) + + + + Patch program +(Ctrl-double-click on/off) + Nr programu brzmienia +(Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza) + + + Patch high-bank number + Numer górnego banku dla danej barwy - - - MusEGui::ClipListEdit - - MusE: Clip List Editor - + Patch low-bank number + Numer dolnego banku dla danej barwy - - Window &Config - + Patch program + Nr programu brzmienia - MusEGui::ComboQuant + MusEGui::CompactSlider - - - - Off - WyÅ‚. + + off + wyÅ‚. MusEGui::CtrlCanvas - + Make the current part's track match the selected drumlist entry - + Is it correct? + Dopasuj Å›lad bieżącego klocka do wybranej pozycji listy instrumentów perkusyjnych Drawing hint: Hold Ctrl to affect only existing events - + Wskazówka odnoÅ›nie rysowania: przytrzymaj ctrl aby zmienić tylko istniejÄ…ce elementy Use pencil or line tool to draw new events - + Użyj ołówka bÄ…dź narzÄ™dzia linii aby dodać nowe elementy MusEGui::CtrlPanel - + S - S + S select controller - Wybierz kontroler + wybierz kontroler X - + X remove panel - UsuÅ„ panel + usuÅ„ panel manual adjust - + rÄ™czne dopasowywanie ctrl-double-click on/off - + ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza off - - - - - - Velocity - PrÄ™dk. uderz. (vel) - - - - add new ... - dodaj nowe ... - - - - - Instrument-defined - - - - - - Add ... - + wyÅ‚. - - Others - + + all/per-note velocity mode + tryb wyÅ›wietlania. velocity: wszystkie/per nuta - - Edit instrument ... - + Velocity + PrÄ™dk. uderz. (vel) - - Common Controls - + add new ... + dodaj nowe ... MusEGui::DList - + hide this instrument - + ukryj ten instrument show this instrument - + pokaż ten instrument this turns a grayed out eye into a blue eye + ? MusEGui::DrumCanvas - + Moving items failed - + PrzesuniÄ™cie elementu nie powiodÅ‚o siÄ™ The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. - + Wybrane elementy nie mogÄ… zostać przeniesione, ponieważ przynajmniej jedna nuta zostanie przeniesiona na Å›lad, który jest inny, niż Å›lad oryginalny i bieżący Å›lad klocka. +Pomóc może zmiana bieżacego klocka za pomocÄ… klawiszy ALT+LEWO/PRAWO. - + Creating event failed - + Utworzenie elementu nie powiodÅ‚o siÄ™ Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. - + Not sure I understand the text correctly. Probably should be fixed. + Nie można utworzyć elementu, ponieważ aktualnie wybrany klocek nie jest na tym samym Å›ladzie i wybrany instrument może być na żadnym bÄ…dź wielu Å›ladach, co jest dwuznaczne. +Wybierz klocek docelowy i spróbuj ponownie. - - + + Recording event failed - + Nagranie zdarzenia nie powiodÅ‚o siÄ™ - - + + Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. - + Not sure I understand the text correctly. Probably should be fixed. + Nie można nagrać zdarzenia, ponieważ aktualnie wybrany klocek nie jest na tym samym Å›ladzie i wybrany instrument może być na żadnym bÄ…dź wielu Å›ladach, co jest dwuznaczne. +Wybierz klocek docelowy i spróbuj ponownie. Internal error - + BÅ‚Ä…d wewnÄ™trzny Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. - + WTF? WystÄ…piÅ‚ dziwny bÅ‚Ä…d, który nie miaÅ‚ prawa wystÄ…pić. Sprawdź informacje na konsoli. Nic nie zostaÅ‚o nagrane. MusEGui::DrumEdit - + mute instrument - wyÅ‚Ä…cz instrument + wycisz instrument sound name - nazwa brzmienia + nazwa brzmienia volume percent - + procent gÅ‚oÅ›noÅ›ci quantisation - kwantyzacja + kwantyzacja this input note triggers the sound - ta wysokość uruchamia brzmienie + ten dźwiÄ™k odgrywa brzmienie note length - dÅ‚ugość dźwiÄ™ku + dÅ‚ugość dźwiÄ™ku this is the note which is played - - - - - output channel (hold ctl to affect all rows) - - - - - output port (hold ctl to affect all rows) - - - - - - shift + control key: draw velocity level 1 - - - - - - control key: draw velocity level 2 - - - - - - shift key: draw velocity level 3 - - - - - - draw velocity level 4 - - - - - output channel (ctl: affect all rows) - - - - - output port (ctl: affect all rows) - + to jest dźwiÄ™k, który jest odgrywany &File &Plik - + Load Map - ZaÅ‚aduj zestaw + ZaÅ‚aduj zestaw Save Map - Zapisz zestaw + Zapisz zestaw Reset GM Map - + Resetuj zestaw GM &Edit - + &Edycja Cut - Wytnij + Wytnij Copy - Kopiuj + Kopiuj Copy events in range - + Kopiuj elementy z zakresu Paste - Wklej + Wklej Paste (with Dialog) - + Wklej (pokaż okno dialogowe) Delete Events - Kasuj Elementy + Kasuj elementy &Select - &Zaznacz + &Zaznacz Select All - Zaznacz wszystko + Zaznacz wszystko Select None - Bez zaznaczenia + Bez zaznaczenia Invert - Odwróć + Odwróć Inside Loop - + PomiÄ™dzy lokatorami Outside Loop - Poza obrÄ™bem lokatorów + Poza obrÄ™bem lokatorów Previous Part - + Poprzedni klocek Next Part - + NastÄ™pny klocek Fu&nctions - + DziaÅ‚ania Set Fixed Length - Ustaw staÅ‚Ä… dÅ‚ugość nut + Ustaw staÅ‚Ä… dÅ‚ugość nut Modify Velocity - + Modyfikuj prÄ™dkość uderzenia (velocity) Crescendo/Decrescendo - + Crescendo/decrescendo Quantize - Kwantyzuj + Kwantyzuj Erase Event - + Kasuj zdarzenia Move Notes - + PrzesuÅ„ nuty Delete Overlaps - + Kasuj nakÅ‚adajÄ…ce siÄ™ nuty &Plugins - + &Wtyczki Window &Config - + &Konfiguracja okna Drum tools - + NarzÄ™dzia perkusyjne Load Drummap - ZaÅ‚aduj zestaw perkusyjny + ZaÅ‚aduj zestaw perkusyjny - + hide instrument - + ukryj instrument - + + override track output channel (hold ctl to affect all rows) + nadpisz kanaÅ‚ wyjÅ›ciowy Å›ladu (wciÅ›nij ctrl aby zmienić we wszystkich rzÄ™dach) + + + + override track output port (hold ctl to affect all rows) + nadpisz wyjÅ›ciowy port Å›ladu (wciÅ›nij ctrl aby zmienić we wszystkich rzÄ™dach) + + + + + control + meta keys: draw velocity level 1 + ctrl + klawisze meta: rysuj velocity poziom 1 + + + + + meta key: draw velocity level 2 + klawisz meta: rysuj velocity poziom 2 + + + + + draw default velocity level 3 + rysuj domyÅ›lne velocity poziom 3 + + + + + meta + alt keys: draw velocity level 4 + alt + klawisze meta: rysuj velocity poziom 4 + + + + override track output channel (ctl: affect all rows) + nadpisz kanaÅ‚ wyjÅ›ciowy Å›ladu (wciÅ›nij ctrl aby zmienić we wszystkich rzÄ™dach) + + + + override track output port (ctl: affect all rows) + nadpisz wyjÅ›ciowy port Å›ladu (wciÅ›nij ctrl aby zmienić we wszystkich rzÄ™dach) + + + Re-order map - + Przesortuj zestaw perkusyjny Group - + Grupuj Don't group - + Nie grupuj Group by channel - + Grupuj po kanale Group maximally - + Grupuj maksymalnie Show/Hide - + Pokaż/ukryj Also show hidden instruments - + Pokaż także ukryte instrumenty Show all instruments - + Pokaż wszystkie instrumenty Hide all instruments - + Ukryj wszystkie instrumenty Only show used instruments - + Pokaż tylko używane instrumenty Only show instruments with non-empty name or used instruments - + Pokaż tylko używane instrumenty bÄ…dź te, których nazwa nie jest pusta Drum map tools - + NarzÄ™dzia zestawu perkusyjnego Store Drummap - Zapisz zestaw perkusyjny + Zapisz zestaw perkusyjny Step Record - + Nagrywanie krokowe Midi Input - + WejÅ›cie MIDI - + + Play Events + Odgrywaj nuty + + + cursor tools - + narzÄ™dzia kursora Cursor step: - + Krok kursora: Set step size for cursor edit - + Ustaw rozmiar kroku dla edycji kursorem ctrl - + Add Controller View - + Dodaj widok kontrolera H - H + H M - + Sound - Brzmienie + Brzmienie Vol - + GÅ‚oÅ›n. QNT - Kwant. + Kwant. E-Note - U-Wys + U-dźwiÄ™k Len - DÅ‚ugość + DÅ‚ugość A-Note - W-Wys + W-dźwiÄ™k Ch - + Kan. Port - Port + Port LV1 - PV1 + PV1 LV2 - PV2 + PV2 LV3 - PV3 + PV3 LV4 - PV4 + PV4 - + Muse: Load Drum Map - MuzA: ZaÅ‚aduj zestaw perkusyjny + MusE: zaÅ‚aduj zestaw perkusyjny MusE: Store Drum Map - MuzA: Zapisz zestaw perkusyjny + MusE: zapisz zestaw perkusyjny Drum map - + Zestaw perkusyjny Reset the drum map with GM defaults? - + Nadpisać zestaw perkusyjny instrumentami GM? - + Not all parts are displayed - + Nie wszystkie klocki sÄ… wyÅ›wietlane You selected both old-style-drumtracks and others (that is: new-style or midi tracks), but they cannot displayed in the same drum edit. I'll only display the old-style drumtracks in this editor, dropping the others. - + ZostaÅ‚y wybrane Å›lady perkusyjne starego typu, jak również inne Å›lady (czyli perkusja nowego typu lub Å›lady MIDI), jednak nie mogÄ… być wyÅ›wietlone w tym samym oknie edycyjnym. +WyÅ›wietlam jedynie perkusjÄ™ starego typu, pomijajÄ…c resztÄ™. MusEGui::EditCAfterDialog - MusE: Enter Channel Aftertouch - MuzA: Wstaw zmianÄ™ Å›redniego nacisku pod uderzeniu (channel aftertouch) + MuzA: Wstaw zmianÄ™ Å›redniego nacisku pod uderzeniu (channel aftertouch) - Time Position - Pozycja: + Pozycja: - Pressure - Nacisk + Nacisk MusEGui::EditEventDialog - + Ok - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj MusEGui::EditInstrument - - Name - Nazwa + + Use note offs + Używaj Note Off + + + + No note offs + Nie używaj Note Off + + + + Convert to 0-vel note ons + Przekształć w Note On z Velocity 0 + + + + Control7 + Kontroler7 + + + + Control14 + Kontroler14 + + + + RPN + RPN + + + + NPRN + + + + + RPN14 + + + + + NRPN14 + + + + + Pitch + Odchylenie od stroju (Pitch bend) + + + + Program + Program + + + + PolyAftertouch + Docisk polifoniczny (Poly Aftertouch) + Aftertouch + Docisk (Aftertouch) + + + + + Name + Nazwa + + + Vol - + GÅ‚oÅ›n. Quant - + Kwant. E-Note - U-Wys + U-dźwiÄ™k + Len - DÅ‚ugość + DÅ‚ugość - + A-Note - W-Wys + W-dźwiÄ™k LV1 - PV1 + PV1 LV2 - PV2 + PV2 LV3 - PV3 + PV3 LV4 - PV4 + PV4 - - + + Tick + Tick (impuls sygnaÅ‚u czasu) + + + + Data + Dane + + + + MusE: Create file failed - + MusE: utworzenie pliku nie powiodÅ‚o siÄ™ MusE: Write File failed - MuzA: Zapis pliku nie powiódÅ‚ siÄ™ + MusE: zapis pliku nie powiódÅ‚ siÄ™ - - + + MusE: Save Instrument Definition - + MusE: zapis definicji instrumentu - - + + Instrument Definition (*.idf) - + Definicja instrumentu (*.idf) - + MusE: Save instrument as - + MusE: zapisz instrument jako Enter a new unique instrument name: - + Wpisz nowÄ… unikalnÄ… nazwÄ™ instrumentu The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? - + Instrument użytkownika „%1†już istnieje. Plik .idf zostanie nadpisany. +JesteÅ› pewien? - + MusE: Bad instrument name - + MusE: niewÅ‚aÅ›ciwa nazwa instrumentu Please choose a unique instrument name. (The name might be used by a hidden instrument.) - + ProszÄ™ wybrać unikalnÄ… nazwÄ™ dla instrumentu +(Nazwa może być używana przez ukryty instrument.) - + MusE: Bad patch name - + MusE: niewÅ‚aÅ›ciwa nazwa brzmienia Please choose a unique patch name - + ProszÄ™ wybrać unikalnÄ… nazwÄ™ brzmienia MusE: Bad patchgroup name - + MusE: zÅ‚a nazwa grupy brzmieÅ„ Please choose a unique patchgroup name - + ProszÄ™ wybrać unikalnÄ… nazwÄ™ grupy brzmieÅ„ - + MusE: Bad controller name - + MusE: niewÅ‚aÅ›ciwa nazwa kontrolera Please choose a unique controller name - - - - - - MusE: Cannot add common controller - + ProszÄ™ wybrać unikalnÄ… nazwÄ™ kontrolera - - A controller named '%1' already exists. - + + New controller: Error + Nowy kontroler: bÅ‚Ä…d - - A controller number %1 already exists. - + + Error! All control numbers are taken up! +Clean up the instrument! + BÅ‚Ä…d! Wszystkie wartoÅ›ci kontrolerów zostaÅ‚y użyte. +ProszÄ™ wyczyÅ›cić definicjÄ™ instrumentu! - + MusE - MuzA + MusE The current Instrument contains unsaved data Save Current Instrument? - + Bieżący instrument zawiera niezapisane dane. +Zapisać bieżący instrument? &Save - &Zapisz + &Zapisz &Nosave - &Nie zapisuj + &Nie zapisuj &Abort - &Anuluj + &Anuluj MusEGui::EditMetaDialog - + MusE: Enter Meta Event - + MusE: wstaw meta-zdarzenie Time Position - Pozycja: + Pozycja Meta Type - Meta Typ + Meta typ Enter Hex - Wstaw Heks. + Wstaw heks. MusEGui::EditPAfterDialog - MusE: Enter Poly Aftertouch - MuzA: Wstaw zmianÄ™ Å›redniego nacisku pod uderzeniu (channel aftertouch) + MuzA: Wstaw zmianÄ™ Å›redniego nacisku pod uderzeniu (channel aftertouch) - Time Position - Pozycja: + Pozycja: - Pitch - Transpozycja + Transpozycja - Pressure - Nacisk + Nacisk MusEGui::EditToolBar - + select Pointer Tool: with the pointer tool you can: select parts move parts copy parts - Kliknij na strzaÅ‚kÄ™. + Kliknij na strzaÅ‚kÄ™. StrzaÅ‚kÄ… możesz zaznaczać, przesuwać i kopiować klocki @@ -8180,200 +9154,244 @@ with the pencil tool you can: create new parts modify length of parts - Kliknij na ołówek. + Kliknij na ołówek. Ołówkiem możesz tworzyć klocki oraz zmieniać ich dÅ‚ugość. select Delete Tool: with the delete tool you can delete parts - Kliknij na GumkÄ™. + Kliknij na gumkÄ™. GumkÄ… można kasować klocki. select Cut Tool: with the cut tool you can split a part - Kliknij na Nożyczki. + Kliknij na nożyczki. Nożyczkami można ciąć klocki. select Glue Tool: with the glue tool you can glue two parts - Kliknij na Klej. -Klejem możesz sklejać dwa klocki + Kliknij na klej. +Klejem możesz sklejać dwa klocki. select Score Tool: - Wybierz PodglÄ…d Nutowy. + Wybierz zapis nutowy select Quantize Tool: insert display quantize event - Ustaw Kwantyzator -(ustawia kwantyzacjÄ™ wstawiania komunikatów midi). + Ustaw kwantyzator. +Ustawia kwantyzacjÄ™ wstawiania komunikatów MIDI. select Drawing Tool - wybierz Wykres + Wybierz wykres select Muting Tool: click on part to mute/unmute - Kliknij na WyÅ‚Ä…cz. + Kliknij na wyÅ‚Ä…cz. Kliknij na pojedynczy klocek aby go wyÅ‚Ä…czyć z odtwarzania. Manipulate automation - - - - - Cursor tool - + Modyfikuj automatykÄ™ - + pointer - StrzaÅ‚ka + strzaÅ‚ka pencil - Ołówek + ołówek eraser - Gumka + gumka cutter - Nożyczki + nożyczki - + score - PoglÄ…d nutowy + zapis nutowy - + glue - + klej + + + + select Cursor (tracker mode) tool: +with the cursor tool you can: + navigate with arrow keys + use VBNM to place notes + change step with 0 and 9 + Wybierz kursor (tryb trackera). +Z narzÄ™dziem kursora możesz: + - poruszać za pomocÄ… klawiszy kursora, + - użyć klawiszy VBNM do rysowania nut, + - zmienić krok w zakresie 0 - 9. + + + + + select Range Tool + Wybierz zakres + + + + select Panning Tool + Wybierz przesuwanie + + + + select Zoom Tool + Wybierz powiÄ™kszenie + + + + range + zakres + + + + pan + przesuwanie + zoom + powiÄ™kszanie + + + quantize - Kwantyzator + kwantyzacja draw - Wykres + wykres mute parts - WyÅ‚Ä…cz klocki z odtwarzania + wyÅ‚Ä…czenie klocków z odtwarzania edit automation - + edycja automatyki cursor - + kursor - + Edit Tools - NarzÄ™dzia Edycyjne + NarzÄ™dzia edycyjne MusEGui::EffectRack - + effect rack - skrzynia efektów + rack efektów - + new - nowy + nowy change - zmieÅ„ + zmieÅ„ move up - przesuÅ„ w górÄ™ + przesuÅ„ w górÄ™ move down - przesuÅ„ w dół + przesuÅ„ w dół remove - usuÅ„ + usuÅ„ bypass - omiÅ„ (bypass) + omiÅ„ (bypass) show gui - pokaż interfejs użytkownika + pokaż interfejs użytkownika show native gui - + pokaż natywny interfejs użytkownika save preset - + zapisz ustawienie - + + Presets + Ustawienia + + + MusE: Save Preset - + MusE: zapisz ustawienie - + Replace effect - + ZamieÅ„ efekt Do you really want to replace the effect %1? - + Czy naprawdÄ™ chcesz zamienić efekt %1? MusEGui::GlobalSettingsConfig - + MusE: Choose start template or song - + MusE: wybierz szablon poczÄ…tkowy bÄ…dź utwór @@ -8381,7 +9399,7 @@ Track Info Columns - + Kolumny informacji o Å›ladzie @@ -8389,525 +9407,596 @@ MusE: Mastertrack - MuzA: Åšlad Tempo/Metrum + MusE: Åšlad Tempo/Metrum &Edit - + &Edycja Insert Tempo - + Wstaw tempo Insert Signature - + Wstaw metrum Insert Key - + Wstaw tonacjÄ™ Edit Positon - + Edytuj pozycjÄ™ Edit Value - + Edytuj wartość Delete Event - + Skasuj zdarzenie Window &Config - + &Konfiguracja okna Edit tools - + NarzÄ™dzia edycyjne Tempo - Tempo + Tempo Timesig - Metrum + Metrum Key - + Tonacja new tempo - nowe tempo + nowe tempo new signature - nowe metrum + nowe metrum new key - + nowa tonacja Meter - Miernik + Pozycja Time - + Czas Type - Typ + Typ Value - Wartość + Wartość Reposition of the initial tempo and signature events is not allowed - + Zmiana pozycji poczÄ…tkowego tempa i metrum jest niedozwolona MusE: List Editor - + MusE: Edytor Lista Input error, conversion not OK or value out of range - + BÅ‚Ä…d wprowadzanej wartoÅ›ci: niewÅ‚aÅ›ciwy format bÄ…dź wartość poza zakresem Reposition of tempo and signature events to start position is not allowed! - + Zmiana tempa i metrum do pozycji poczÄ…tkowej jest niedozwolona! MusEGui::ListEdit - + insert Note - Wstaw nutÄ™ + Wstaw nutÄ™ insert SysEx - wstaw SysEx + Wstaw SysEx insert Ctrl - wstaw + Wstaw kontroler insert Meta - Wstaw Meta + Wstaw meta - insert Channel Aftertouch - Wstaw zmianÄ™ Å›redniego nacisku pod uderzeniu (channel aftertouch) + Wstaw zmianÄ™ Å›redniego nacisku pod uderzeniu (channel aftertouch) - insert Poly Aftertouch - Wstaw zmianÄ™ Å›redniego nacisku pod uderzeniu (Poly Aftertouch) + Wstaw zmianÄ™ Å›redniego nacisku pod uderzeniu (Poly Aftertouch) - + &Edit - + &Edycja Cut - Wytnij + Wytnij Copy - Kopiuj + Kopiuj Paste - Wklej + Wklej Delete Events - Kasuj Elementy + Kasuj zdarzenia Increase Tick - + ZwiÄ™ksz tick Decrease Tick - + Zmniejsz tick Window &Config - + &Konfiguracja okna Insert tools - + NarzÄ™dzia wstawiania Tick - Takt (impuls sygnaÅ‚u czasu) + Tick Bar - + Pozycja Type - Typ + Typ Ch - + Kan. Val A - Wart. A + Wart. A Val B - Wart. B + Wart. B Val C - Wart. C + Wart. C Len - DÅ‚ugość + DÅ‚ugość Comment - Opis + Opis MusE: List Editor - + MusE: Edytor Lista MusEGui::MPConfig - - + Default input connections - + DomyÅ›lne poÅ‚Ä…czenia wejÅ›ciowe - - + + Are you sure you want to apply to all existing midi tracks now? - + Czy na pewno teraz chcesz zastosować dla wszystkich Å›ladów MIDI? - Default output connections - - - - - - Setting will apply to new midi tracks. -Do you want to apply to all existing midi tracks now? - + DomyÅ›lne poÅ‚Ä…czenia wyjÅ›ciowe - + MusE: bad device name - + MusE: niewÅ‚aÅ›ciwa nazwa urzÄ…dzenia please choose a unique device name - + proszÄ™ wybrać unikalnÄ… nazwÄ™ urzÄ…dzenia - - + + in - + wej. - - + + out - - - - - Show first aliases - + wyj. - - Show second aliases - - - - + Toggle all - + PrzeÅ‚Ä…cz wszystkie Change all tracks now - + ZmieÅ„ na wszystkich Å›ladach - + + Remove + UsuÅ„ + + + Create Jack device - + Utwórz urzÄ…dzenie Jack - - + + Port Number - Numer portu + Numer portu - + + Enable gui - + WÅ‚Ä…cz GUI - + + Enable reading - + WÅ‚Ä…cz wejÅ›cie - + + Enable writing - + WÅ‚Ä…cz wyjÅ›cie - + Port instrument - + Numer portu Midi device name. Click to edit (Jack) - + Nazwa urzÄ…dzenia MIDI. Kliknij, aby edytować (Jack) - + Connections from Jack Midi outputs - + PoÅ‚Ä…czenia z wyjść MIDI Jack Connections to Jack Midi inputs - + PoÅ‚Ä…czenia do wejść MIDI Jack - + Auto-connect these channels to new midi tracks - + Automatycznie podÅ‚Ä…czaj te kanaÅ‚y do nowych Å›ladów MIDI - + Auto-connect new midi tracks to these channels - + Automatycznie podÅ‚Ä…czaj nowe Å›lady MIDI do tych kanałów - + Auto-connect new midi tracks to this channel - + Automatycznie podÅ‚Ä…czaj nowe Å›lady MIDI do tego kanaÅ‚u - + + Device state - + Status urzÄ…dzenia - + Enable gui for device - + WÅ‚Ä…cz graficzny interfejs użytkownika (GUI) dla tego urzÄ…dzenia + Enable reading from device - + WÅ‚Ä…cz odczyt z tego urzÄ…dzenia (wejÅ›cie) - + + Enable writing to device - + WÅ‚Ä…cz zapis do tego urzÄ…dzenia (wyjÅ›cie) - + Name of the midi device associated with this port number. Click to edit Jack midi name. - + Nazwa urzÄ…dzenia MIDI powiÄ…zanego z tym numerem portu. Kliknij, aby edytować nazwÄ™ urzÄ…dzenia Jack. Instrument connected to port - Instrument poÅ‚Ä…czony do portu + Instrument poÅ‚Ä…czony do portu - + Connections from Jack Midi output ports - + PoÅ‚Ä…czenia z portów wyjÅ›ciowych Jack MIDI Connections to Jack Midi input ports - + PoÅ‚Ä…czenia do portów wejÅ›ciowych Jack MIDI + + + + Auto-connect these channels, on this port, to new midi tracks. + Automatycznie podÅ‚Ä…czaj te kanaÅ‚y (na tym porcie) do nowych Å›ladów MIDI. + + + + Connect new midi tracks to these channels, on this port. + PodÅ‚Ä…cz nowe Å›lady MIDI do tych kanałów (na tym porcie). + + + + Connect new midi tracks to this channel, on this port. + PodÅ‚Ä…cz nowe Å›lady MIDI do tego kanaÅ‚u (na tym porcie). + + + + State: result of opening the device + Status: status otwarcia urzÄ…dzenia + + + + Port + Port + + + + + GUI + GUI + + + + + I + Wej. - - Auto-connect these channels, on this port, to new midi tracks. - + + + O + Wyj. - - Connect new midi tracks to these channels, on this port. - + + Instrument + - - Connect new midi tracks to this channel, on this port. - + + + Device Name + Nazwa urzÄ…dzenia - - State: result of opening the device - Status: rezultat otworzenia urzÄ…dzenia + + + Midi device name + Nazwa urzÄ…dzenia MIDI - - Port - Port + + + Midi device type + Typ urzÄ…dzenia MIDI - - GUI - + + Connections from Jack Midi + PoÅ‚Ä…czenia z Jack MIDI - I - Wej. + Connections to Jack Midi + PoÅ‚Ä…czenia do Jack MIDI - - O - Wyj. + + Result of opening the device: +OK: Assigned to a port and in use +Closed: Unassigned to a port, or closed +R/W Error: Unable to open for read or write +Unavailable: USB midi unplugged, or external + application not running, or synth plugin + not installed etc. +(Jack Midi devices have 'unavailable ports' + in the routes columns.) +Unavailable devices or ports can be purged + with 'Remove' or with the advanced router. + Status otwarcia urzÄ…dzenia: +OK: przyporzÄ…dkowane, w użyciu, +Closed: nieprzyporzÄ…dkowane do portu + bÄ…dź zamkniÄ™te, +R/W Error: bÅ‚Ä…d otwarcia do odczytu lub zapisu, +Unavailable: urzÄ…dzenie MIDI USB odÅ‚Ä…czone, + zewnÄ™trzna aplikacja nie pracuje, wtyczka + syntezatora nie zainstalowana etc., +UrzÄ…dzenia lub porty ze statusem „unavailable†+ mogÄ… być skasowane za pomocÄ… przycisku UsuÅ„ + bÄ…dź w oknie Konfiguracja PoÅ‚Ä…czeÅ„. - - Instrument - + + Enable Graphical User Interface for device + WÅ‚Ä…cz graficzny interfejs użytkownika (GUI) dla urzÄ…dzenia - Device Name - Nazwa UrzÄ…dzenia + Connections from Jack Midi ports + PoÅ‚Ä…czenia z portów Jack MIDI + Connections to Jack Midi ports + PoÅ‚Ä…czenia do portów Jack MIDI + + + In routes - + PoÅ‚. wej. Out routes - + PoÅ‚. wyj. - + Def in ch - + DomyÅ›lny kan. wej. Def out ch - + DomyÅ›lny kan. wyj. - + + State - Status + Status - + + Type + Typ + + + + In + JWej. + + + + Out + JWyj. + + + <unknown> - + <nieznany> - - + <none> - + <brak> MusEGui::MTScale - + bar scale - + linijka taktów MusEGui::MTScaleFlo - + bar scale - + linijka taktów @@ -8915,37 +10004,37 @@ MusE: Marker - MuzA: Znacznik + MusE: Edytor Znaczników add marker - Dodaj znacznik + Dodaj znacznik delete marker - Kasuj znacznik + Kasuj znacznik &Edit - + &Edycja Window &Config - + &Konfiguracja okna edit tools - NarzÄ™dzia Edycyjne + NarzÄ™dzia Edycyjne Bar:Beat:Tick - Takt:Puls:Takt + Takt:Uderzenie:Tick @@ -8955,17 +10044,17 @@ Lock - Zablokuj + Zablokuj Text - Tekst + Opis Marker Properties - WÅ‚aÅ›ciwoÅ›ci znacznika + WÅ‚aÅ›ciwoÅ›ci znacznika @@ -8973,172 +10062,334 @@ MusE: Mastertrack - MuzA: Åšlad Tempo/Metrum + MusE: Edytor Graficzny Tempo/Metrum Window &Config - + &Konfiguracja okna Enable master - + WÅ‚Ä…cz Åšlad tempo/Metrum Enable - WÅ‚Ä…cz + WÅ‚Ä…cz Enable usage of master track - Aktywuj Åšlad Tempo/Metrum + Aktywuj Åšlad tempo/Metrum Info - Info + Informacja Cursor - + Kursor time at cursor position - Wstaw metrum w aktualnej pozycji kursora + Wstaw metrum w aktualnej pozycji kursora tempo at cursor position - Wstaw tempo w aktualnej pozycji kursora + Wstaw tempo w aktualnej pozycji kursora Off - WyÅ‚. + WyÅ‚. Bar - + Takt Snap - + PrzyciÄ…ganie CurPos - Akt. Poz. Kursora: + Akt. poz. kursora tempo at current position - Wstaw tempo w aktualnej pozycji kursora + Wstaw tempo w aktualnej pozycji kursora time signature at current position - Wstaw tempo w aktualnej pozycji kursora + Wstaw metrum w aktualnej pozycji kursora + + + + MusEGui::MidiAudioControl + + + Control7 + Kontroler7 + + + + Control14 + Kontroler14 + + + + RPN + RPN + + + + NPRN + + + + + RPN14 + RPN14 + + + + NRPN14 + NRPN14 + + + + Pitch + Odchylenie (Pitch bend) + + + + Program + Program + + + + Aftertouch + Docisk (Aftertouch) MusEGui::MidiInputTransformDialog - + New - Nowy + Nowy MusEGui::MidiStrip - - - + + off - + wyÅ‚. - - ctrl-double-click on/off - + ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza - VariationSend - VariationSend + VariationSend + + + + Transpose notes up or down + Transpozycja nut (w górÄ™ bÄ…dź w dół) + + + + Tran + Tran. + + + + Offset playback of notes before or after actual note + Opóźnienie/wyprzedzenie przy odtwarzaniu zapisanych na Å›cieżce dźwiÄ™ków + + + + Dly + Opóź. + + + + Change note length in percent of actual length + Zmienia dÅ‚ugość nut w stosunku procentowym do obecnej dÅ‚ugoÅ›ci + + + + Len + DÅ‚. + + + + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + <html><head/><body><p>Dodaj lub odejmij prÄ™dkość uderzenia (velocity) do/od nut zapisanych na Å›ladzie.</p><p><span style=" font-style:italic;">Zakres velocity nut to 0-127, może siÄ™ wiÄ™c <br />zdarzyć, że nie bÄ™dzie możliwe <br />przyporzÄ…dkowanie w caÅ‚oÅ›ci tak obliczonej <br />wartoÅ›ci (note velocity + velocity), gdyż <br />znajdzie siÄ™ ona poza dopuszczalnym <br />zakresem.</span></p></body></html> + + + + Vel + Vel. + + + + Compress the notes velocity range, in percent of actual velocity + Kompresuj zakres prÄ™dkoÅ›ci uderzenia (Note Velocity), o procent w stosunku do wartoÅ›ci obecnej + + + + Cmp + Kompr. + + + + Instrument + Instrument + + + + + + <unknown> + <nieznany> + + + + Program + Program + Pro + Prg. + + + + VariationSend +(Ctrl-double-click on/off) + VariationSend +(Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza) + + + + ReverbSend +(Ctrl-double-click on/off) + WysyÅ‚ka pogÅ‚osu +(Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza) + + + + Volume/gain +(Ctrl-double-click on/off) + GÅ‚oÅ›ność/wzmocnienie. +(Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza) + + + Prg + Prg. + + + Var - Var + Var. - ReverbSend - WysyÅ‚ka PogÅ‚osu + WysyÅ‚ka pogÅ‚osu - + Rev - PogÅ‚. + PogÅ‚. - ChorusSend - WysyÅ‚ka Chorusa + WysyÅ‚ka chorusa - + Cho - Chorus + Chorus - + + ChorusSend +(Ctrl-double-click on/off) + WysyÅ‚ka chorusa +(Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza) + + + dB - dB + dB - Pan/Balance - Pan/Równowaga + Pan/Balans - + Pan - Panorama + Panorama - + + Pan/Balance +(Ctrl-double-click on/off) + Panorama/balans +(Ctrl + podwójny klik wÅ‚Ä…cza/wyÅ‚Ä…cza) + + + record - + Nagrywanie - + mute - wycisz + Wycisz - + solo mode - + Tryb solo - + input routing - + PoÅ‚Ä…czenia wejÅ›ciowe - + output routing - poÅ‚Ä…czenie WyjÅ›cia + PoÅ‚Ä…czenia wyjÅ›ciowe + + + + midi thru + MIDI thru + + + + Pass input events through ('thru') to output + Przekazuje zdarzenia wejÅ›ciowe do wyjÅ›cia (through - „thruâ€). @@ -9147,136 +10398,148 @@ Port Number - Numer portu + Numer portu Name of the midi device associated with this port number - Nazwa urzÄ…dzenia midi skojarzona z tym numerem portu + Nazwa urzÄ…dzenia MIDI skojarzona z tym numerem portu Midi clock input detected - + Wykrycie na wejÅ›ciu zegara MIDI Midi tick input detected - + Wykrycie na wejÅ›ciu MIDI tick Midi real time input detected - + Wykrycie na wejÅ›ciu komunikatów MIDI czasu rzeczywistego (realtime) MMC input detected - + Wykrycie na wejÅ›ciu komunikatów MMC MTC input detected - + Wykrycie na wejÅ›ciu komunikatów MTC Detected SMPTE format - + Wykryty format SMPTE Receive id number. 127 = Global. Double click to edit. - + Numer identyfikacyjny (odbiór). 127 = globalny. Podwójny klik - edycja. Accept midi clock input - + Odbieraj na wejÅ›ciu zegar MIDI Accept midi real time input - + Odbieraj na wejÅ›ciu komunikaty MIDI czasu rzeczywistego (realtime) Accept MMC input - + Odbieraj na wejÅ›ciu komunikaty MMC Accept MTC input - + Odbieraj na wejÅ›ciu komunikaty MTC Receive start rewinds before playing - + Odbieraj przewijanie do poczÄ…tku przed odtwarzaniem Transmit id number. 127 = Global. Double click to edit. - + Numer identyfikacyjny (nadawanie). 127 = globalny. Podwójny klik - edycja. Send midi clock output - + WysyÅ‚aj komunikaty zegara MIDI Send midi realtime output - + WysyÅ‚aj komunikaty MIDI czasu rzeczywistego (realtime) Send MMC output - + WysyÅ‚aj komunikaty MMC Send MTC output - + WysyÅ‚aj komunikaty MTC Midi clock input detected. Current port actually used is red. Click to force a port to be used. - + Wykrycie na wejÅ›ciu zegara MIDI. +Aktualnie używany port zaznaczony jest na czerwono. +Kliknij, aby wymusić używanie danego portu. Midi realtime input detected, including start/stop/continue, and song position. - + Wykrycie na wejÅ›ciu komunikatów + MIDI czasu rzeczywistego (realtime), + w tym komunikatów: start/stop/continue + oraz pozycja utworu. MMC input detected, including stop/play/deferred play, and locate. - + Wykrycie na wejÅ›ciu komunikatów MMC, w tym + komunikatów: stop/play/deferred play/locate. MTC input detected, including forward quarter-frame sync and full-frame locate. Current port actually used is red. Click to force a port to be current. - + Wykrycie na wejÅ›ciu komunikatów MTC, w tym: + forward quarter-frame sync, full-frame locate. +Aktualnie używany port zaznaczony jest na + czerwono. Kliknij, aby wymusić używanie + danego portu. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. - + Wykrywane formaty SMPTE: 24fps, 25fps, 30fps drop frame, 30fps non-drop. +Wykrywane sÄ… formaty MTC quarter i full frame, a także MMC locate. Receive id number. 127 = global receive all, even if not global. - + But what means: 'even if not global' in this context? + Numer identyfikacyjny (odbiór). 127 = globalny odbiór wszystkiego. @@ -9286,7 +10549,19 @@ then another can take over. Best if each turns off its clock at stop, so MusE can re-acquire the clock from another port. Click on detect indicator to force another. - + Odbieraj na wejÅ›ciu zegar MIDI. Dla zegara używane + jest tylko jedno wejÅ›cie. +Automatyczne przejmowanie: w przypadku, gdy + wybrany jest wiÄ™cej niż jeden port, zostaje użyty + ten, na którym zegar pojawiÅ‚ siÄ™ jako pierwszy. + JeÅ›li zegar zniknie, wykorzystany zostanie + nastÄ™pny port. RozwiÄ…zanie to dziaÅ‚a najlepiej, kiedy + urzÄ…dzenia zaprzestajÄ… wysyÅ‚ania zegara przy + zatrzymaniu odtwarzania. MusE może wtedy + skorzystać z zegara odbieranego na innym porcie. +Kliknij na kontrolce wejÅ›cia, aby wymusić używanie + konkretnego portu. + @@ -9297,36 +10572,56 @@ This means you may have several master devices connected, and muse will accept input from them. - + Odbieraj komunikaty czasu rzeczywistego + (realtime). W tym komunikaty: + start/stop/continue oraz pozycjÄ™ utworu. +Zdarzenia nie zwiÄ…zane z zegarem (start, + stop etc.) sÄ… odbierane przez WSZYSTKIE + aktywne porty. +Oznacza to, że istnieć może kilka aktywnych + urzÄ…dzeÅ„ „masterâ€. MusE odbierze + komunikaty z każdego z nich. Accept MMC input, including stop/play/deferred play, and locate. - + Odbieraj komunikaty MMC, w tym komunikaty: + stop/play/deferred play/locate. Accept MTC input, including forward quarter-frame sync and full-frame locate. See 'rc' column for more help. - + Odbieraj komunikaty MTC, w tym: forward + quarter-frame sync, full-frame locate. +Pomoc dla kolumny „rc†zawiera wiÄ™cej + informacji. When start is received, rewind before playing. Note: It may be impossible to rewind fast enough to synchronize with the external device. - + Jeżeli odebrano komunikat „startâ€, przewiÅ„ + przed odtwarzaniem. +Uwaga: szybkość przewijania może okazać siÄ™ + niewystarczajÄ…ca do poprawnej synchronizacji + z urzÄ…dzeniem zewnÄ™trznym. Transmit id number. 127 = global transmit to all. - + WysyÅ‚any numer identyfikacyjny. 127 = globalne wysyÅ‚anie do wszystkich. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. - + WysyÅ‚aj zegar MIDI. Jeżeli wybrano opcjÄ™ + „Synchronizacja zewnÄ™trzna (tryb slave)â€, + MusE może przekazywać odebrane + komunikaty zegara do innego wybranego + portu. @@ -9336,176 +10631,188 @@ other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. - + WysyÅ‚aj komunikaty MIDI czasu rzeczywistego + (realtime), w tym start/stop/continue oraz + pozycjÄ™ utworu. Jeżeli wybrano opcjÄ™ + „Synchronizacja zewnÄ™trzna (tryb slave)â€, + MusE może przekazywać odebrane komunikaty + realtime do innego wybranego portu. Oznacza + to, że podÅ‚Ä…czonych może być kilka urzÄ…dzeÅ„ + „slave†i MusE bÄ™dzie do każdego z nich + przekazywać uprzednio odebrane komunikaty. Port - Port + Port Device Name - Nazwa UrzÄ…dzenia + Nazwa urzÄ…dzenia c - + c k - + k r - + r m - min. + m t - + t type - + typ rid - + rid rc - + rc rr - + rr rm - + rm rt - + rt rw - + rw tid - + tid tc - + tc tr - + tr tm - + tm tt - + tt None - Brak + Brak Tiny - + Najmniejsze Small - + MaÅ‚e Large - + Duże Large with pre-detect - + Duże + pre-detect MusE - MuzA + MusE Settings have changed Apply sync settings? - + Ustawienia zmieniÅ‚y siÄ™. +Użyć nowych ustawieÅ„ +synchronizacji? &Apply - &Zastosuj + &Zastosuj &No - &Nie + &Nie &Abort - &Anuluj + &Anuluj <none> - + <brak> MusEGui::MidiTrackInfo - - + + + + <unknown> - + <nieznany> MusEGui::MidiTransformerDialog - + New - Nowy + Nowy @@ -9513,681 +10820,721 @@ Wave Files (*.wav);;All Files (*) - Pliki Wave (*.wav);;Wszystkie Pliki (*) + Pliki wave (*.wav);;Wszystkie pliki (*) MusEGui::MusE - + Failed to start audio! - + Nie można uruchomić dźwiÄ™ku! Was not able to start audio, check if jack is running. - + Nie można uruchomić dźwiÄ™ku, sprawdź dziaÅ‚anie serwera Jack. Timeout waiting for audio to run. Check if jack is running. - + UpÅ‚ynÄ…Å‚ czas oczekiwania na uruchomienie dźwiÄ™ku. Sprawdź dziaÅ‚anie serwera Jack. - + Und&o - Co&fnij + Co&fnij Re&do - Co&fnij + &Przywróć undo last change to song - cofnij ostatniÄ… zmianÄ™ w utworze + cofnij ostatniÄ… zmianÄ™ w utworze redo last undo - ponów ostanie cofnij + przywróć ostatnio cofniÄ™tÄ… operacjÄ™ Loop - PÄ™tla + PÄ™tla loop between left mark and right mark - ZapÄ™tl pomiÄ™dzy lokatorami + ZapÄ™tl pomiÄ™dzy lokatorami Punchin - WÅ‚Ä…cz wcinki (punch In) + WÅ‚Ä…cz wcinki (punch In) record starts at left mark - Nagrywaj od lewego lokatora. + Nagrywaj od lewego lokatora. Punchout - WyÅ‚. wcinki (punch out) + WyÅ‚. wcinki (punch out) record stops at right mark - Nagrywaj od prawego lokatora. + Nagrywaj od prawego lokatora. Start - + Start rewind to start position - PrzewiÅ„ do poczÄ…tku. + PrzewiÅ„ do poczÄ…tku. Rewind - PrzewiÅ„ do tyÅ‚u + PrzewiÅ„ do tyÅ‚u rewind current position - Przewijaj kursor do tyÅ‚u. + PrzewiÅ„ kursor do tyÅ‚u. Forward - PrzewiÅ„ do przodu + PrzewiÅ„ do przodu move current position - Przewijaj kursor do przodu. + Przewijaj kursor do przodu. Stop - Stop + Stop stop sequencer - Zatrzymaj sekwencer. + Zatrzymaj sekwencer. Play - Odtwarzanie + Odtwarzanie - + + Restart rec + Ponów nagrywanie + + + start sequencer play - Uruchom odtwarzanie sekwencera. + Uruchom odtwarzanie sekwencera. - + Record - Nagrywanie + Nagrywanie to record press record and then play - Aby nagrywać najpierw naciÅ›nij nagrywanie, potem odtwarzanie. + Aby nagrywać najpierw naciÅ›nij nagrywanie, potem odtwarzanie. - - + + Panic - Zatrzymaj wszystkie komunikaty midi! + Zatrzymaj wszystkie komunikaty MIDI! - + send note off to all midi channels - Zatrzymaj komunikaty midi na wszystkich kanaÅ‚ach! + WyÅ›lij komunikaty Note Off do wszystkich kanałów. - + + turn on/off metronome + WÅ‚Ä…cz/wyÅ‚Ä…cz metronom. + + + &New - &Nowy + &Nowy Create New Song - Twórz nowy utwór. Można też nacisnąć skrót "Ctrl-N". + Twórz nowy utwór. Można też użyć kombinacji Ctrl-N. &Open - &Otwórz + &Otwórz Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. - Otwórz nowy utwór. Można też wybrać <b>Otwórz</b> z menu Plik. + Kliknij ten przycisk, aby otworzyć <em>nowy utwór</em>.<br>Można też wybrać opcjÄ™ <b>Otwórz</b> w menu Plik. Open &Recent - Otwórz O&statnie + Otwórz o&statnie - - + + &Save - &Zapisz + &Zapisz - + Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. - Zapisz edytowany utwór. Trzeba podać nazwÄ™ pliku. -Można też wybrać "Zapisz" z menu plik, lub "Ctrl-S". + Kliknij ten przycisk, aby zapisać edytowany utwór. Trzeba podać nazwÄ™ pliku. +Można też wybrać opcjÄ™ Zapisz w menu Plik lub użyć kombinacji Ctrl-S. Save &As - Zapisz &Jako + Zapisz &jako Import Midifile - &Import Pliku Midi + &Import pliku MIDI Export Midifile - &Export do pliku midi + &Eksport pliku MIDI Import Part - + Import klocka - - - Import Wave File - Import Pliku &Wave + + + Import Audio File + Import pliku audio - + Find unused wave files - + Znajdź nieużywane pliki audio &Quit - &ZakoÅ„cz + &ZakoÅ„cz Song Info - + Informacja o utworze Transport Panel - Panel transportu + Panel Transportu Bigtime Window - Duży Zegar + Duży Zegar Mixer A - + Mikser A Mixer B - + Mikser B Cliplist - + Lista Klipów Marker View - + Edytor Znaczników Arranger View - + Główne Okno Fullscreen - + Tryb peÅ‚noekranowy &Plugins - + &Wtyczki Edit Instrument - Edytuj Instrument + Edytuj instrument Input Plugins - Wtyczki wejÅ›cia + Wtyczki wejÅ›cia Transpose - Transponuj + Transponuj Midi Input Transform - PrzeksztaÅ‚canie WejÅ›cia Midi + PrzeksztaÅ‚canie wejÅ›cia MIDI Midi Input Filter - Filtr WejÅ›cia Midi + Filtr wejÅ›cia MIDI Midi Remote Control - Zdalne Sterowanie Midi + Zdalne Sterowanie MIDI Rhythm Generator - + Generator rytmu Reset Instr. - Reset Instr. + Reset instr. Init Instr. - Inicjalizacja Instr. + Inicjalizacja instr. Local Off - Local Off + Local Off Bounce to Track - -Zgrywanie Å›ladu (bounce) + Zgrywanie Å›ladu (bounce) Bounce to File - Zgrywanie do Pliku (bounce) + Zgrywanie do pliku (bounce) Restart Audio - Zrestartuj Audio + Zrestartuj audio Mixer Automation - Automatyka Miksera + Automatyka miksera Take Snapshot - Zrób zrzut ekranu + Zrzut ustawieÅ„ miksera Clear Automation Data - Wyczyść automatykÄ™ + Wyczyść automatykÄ™ Cascade - + Kaskadowo Tile - + SÄ…siadujÄ…co In rows - + W rzÄ™dach In columns - + W kolumnach Global Settings - Globalne Ustawienia + Ustawienia globalne Configure Shortcuts - Konfiguracja skrótów + Konfiguracja skrótów Follow Song - podążanie kursora za utworem + Podążanie kursora za utworem Don't Follow Song - nie podążaj za utworem + Nie podążaj za utworem Follow Page - kursor siÄ™ przesuwa + Kursor siÄ™ przesuwa Follow Continuous - takty siÄ™ przesuwajÄ… (kursor stoi) + Takty siÄ™ przesuwajÄ… (kursor stoi) - + + &Did you know? + &Czy wiesz, że...? + + + + MusE: Song: + MusE: utwór: + + + + + Metronome - Metronom + Metronom - + Midi Sync - Synchronizacja Midi + Synchronizacja MIDI Midi File Import/Export - + Konfiguracja importu/eksportu plików MIDI Appearance Settings - Ustawienia wyglÄ…du + Ustawienia wyglÄ…du Midi Ports / Soft Synth - Porty Midi i Syntezatory Softowe + Porty MIDI i syntezatory programowe &Manual - &Instrukcja ObÅ‚sugi + &Instrukcja obsÅ‚ugi &MusE Homepage - strona domowa &MuzA + Strona domowa &MusE - + &Report Bug... - &ZgÅ‚oÅ› BÅ‚Ä…d... + &ZgÅ‚oÅ› bÅ‚Ä…d... &About MusE - &Informacje o programie + I&nformacje o programie - + Song Position - + Pozycja utworu Tempo - Tempo + Tempo Signature - Metrum + Metrum File Buttons - Przyciski Menu Plik + Przyciski Menu Plik Undo/Redo - + Cofnij/Przywróć Transport - Panel transportu + Panel transportu - + + Cpu load + Obciążenie CPU + + + + Measured CPU load + Aktualne obciążenie CPU + + + + No CPU load data + Brak danych o obciążeniu CPU + + + &File - &Plik + &Plik &View - &PrzeglÄ…daj + &Widok &Midi - &Midi + &MIDI &Audio - &Audio + &Audio A&utomation - + A&utomatyka &Windows - + &Okna MusE Se&ttings - + Us&tawienia MusE &Help - P&omoc + Pomo&c - + About &Qt - + Informacje o &Qt - + Cannot read template - Nie można odczytać pliku szablonu + Nie można odczytać pliku szablonu - + File open error - BÅ‚Ä…d odczytu pliku + BÅ‚Ä…d odczytu pliku File read error - bÅ‚Ä…d odczytu pliku + BÅ‚Ä…d odczytu pliku Unknown File Format: %1 - + Nieznany format pliku: %1 - - - + MusE: Song: %1 - + MusE: utwór: %1 - + MusE: load project - MuzA: Å‚aduj utwór + MusE: Å‚aduj utwór - + MusE: load template - MuzA: Å‚aduj szablon + MusE: Å‚aduj szablon - + MusE: Write File failed - MuzA: Zapis pliku nie powiódÅ‚ siÄ™ + MusE: zapis pliku nie powiódÅ‚ siÄ™ - + The current Project contains unsaved data Save Current Project? - Otwarty utwór zawiera niezapisane dane + Bieżący utwór zawiera niezapisane dane. Zapisać otwarty utwór? - + S&kip - + &PomiÅ„ - + &Cancel - + &Anuluj - + MusE: Save As - MuzA: Zapisz Jako + MusE: zapisz Jako - + Nothing to edit - Nie ma nic do edytowania + Nie ma nic do edytowania - + MusE: Bounce to Track - MuzA: Zgrywanie (bounce) do Åšladu + MusE: zgrywanie (bounce) do Å›ladu No wave tracks found - + Nie znaleziono Å›ladów audio No audio output tracks found - + Nie znaleziono Å›ladów wyjÅ›cia Select one audio output track, and one target wave track - + Wybierz jeden Å›lad wyjÅ›cia +oraz jeden docelowy Å›lad +audio + Select one target wave track - + Wybierz jeden docelowy Å›lad audio Select one target wave track, and one audio output track - + Wybierz jeden docelowy Å›lad +audio oraz jeden Å›lad wyjÅ›cia MusE: Bounce to File - MuzA: Zgrywanie (bounce) do Pliku + MusE: zgrywanie (bounce) do pliku Select one audio output track - + Wybierz jeden Å›lad wyjÅ›cia audio MusE: Bounce - MuzA: Zgrywanie (bounce) + MusE: zgrywanie (bounce) set left/right marker for bounce range - ustaw lokatory do zgrania (bounce) danego zakresu + ustaw lokatory do zgrania (bounce) danego zakresu - + The current Project contains unsaved data Load overwrites current Project: Save Current Project? - Otwarty utwór zawiera niezapisane dane. -ZaÅ‚adowanie nadpisze otwarty utwór: -Zapisać otwarty utwór? + Bieżący projekt zawiera niezapisane dane. +Wybrane dziaÅ‚anie spowoduje jego nadpisanie. +Zapisać bieżący projekt? - - + + &Abort - &Anuluj + &Anuluj - + This will clear all automation data on all audio tracks! Proceed? - + Dane automatyki dla wszystkich + Å›ladów zostanÄ… wyczyszczone! +Kontynuować? @@ -10195,124 +11542,234 @@ all controllers on all audio tracks, at the current position. Proceed? - + Zostanie wykonany automatyczny zrzut +wszystkich kontrolerów dla wszystkich +Å›ladów audio na bieżącej pozycji. +Kontynuować? + + + + MusE: Warning + MusE: ostrzeżenie + + + + The song uses multiple ports but export format 0 (single track) is set. +The first track's port will be used. Playback will likely be wrong + unless the channels used in one port are different from all other ports. +Canceling and setting a different export format would be better. +Continue? + W utworze użyto wielu portów, jednak ustawiony jest eksport do formatu +0 (tylko jeden Å›lad). Zostanie wiÄ™c wykorzystany jedynie port z pierwszego +Å›ladu, co sprawi, że utwór najprawdopodobniej bÄ™dzie odtwarzany +niepoprawnie. Lepszym rozwiÄ…zaniem bÄ™dzie anulowanie tej czynnoÅ›ci +i zmiana formatu eksportowanego pliku. +Kontynuować? - + MusE: Export Midi - MuzA: Exportuj Midi + MusE: eksportuj do pliku MIDI - + no help found at: - nie znalezniono pomocy + nie znaleziono pomocy przy: MusE: Open Help - MuzA: Otwórz Pomoc + MusE: otwórz pomoc - + Unable to launch help - + Nie można uruchomić pomocy For some reason MusE has to launch the default browser on your machine. - + MusE musi otworzyć domyÅ›lnÄ… przeglÄ…darkÄ™ +na Twoim komputerze. MusE: Import Midi - MuzA: Importuj Midi + MusE: importuj plik MIDI Add midi file to current project? - Dodać plik midi do otwartego utworu? + Dodać plik MIDI do bieżącego projektu? &Add to Project - &Dodaj do utworu + &Dodaj do projektu &Replace - &ZastÄ…p + &ZastÄ…p reading midifile - czytanie pliku midi + czytanie pliku MIDI failed: - + niepowodzenie: - + Import part is only valid for midi and wave tracks! - + Importowanie klocków jest możliwe jedynie dla Å›ladów MIDI i audio! MusE: load part - + MusE: importuj klocek No track selected for import - + Nie wybrano Å›ladu do importu - + %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. - - + + %n klocek z %1 nie mogÅ‚ zostać zaimportowany. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. + %n klocki z %1 nie mogÅ‚y zostać zaimportowane. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. + %n klocków z %1 nie mogÅ‚o zostać zaimportowanych. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. %n part(s) could not be imported. Likely the selected track is the wrong type. - - + + %n klocek nie mogÅ‚ zostać zaimportowany. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. + %n klocki nie mogÅ‚y zostać zaimportowane. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. + %n klocków nie mogÅ‚o zostać zaimportowanych. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. - + to import an audio file you have first to selecta wave track - + aby zaimportować plik dźwiÄ™kowy musisz najpierw wybrać Å›lad audio - + Import Wavefile - + Import pliku audio This wave file has a samplerate of %1, as opposed to current setting %2. +File will be resampled from %1 to %2 Hz. Do you still want to import it? - + CzÄ™stotliwość próbkowania tego pliku audio to %1, +jednak bieżące ustawienie to %2. +Plik zostanie przekonwertowany z czÄ™stotliwoÅ›ci +%1 do %2 Hz. +Czy nadal chcesz importować plik? + + + + + + + Wave import error + BÅ‚Ä…d importu pliku audio + + + + There are too many wave files +of the same base name as imported wave file +Can not continue. + Jest zbyt dużo plików audio + o tej samej nazwie bazowej. +Nie można kontynuować. + + + + Can't create new wav file in project folder! + + Nie można utworzyć nowego pliku audio w katalogu projektu! + + + + Failed to initialize sample rate converter! + BÅ‚Ä…d inicjalizacji konwertera czÄ™stotliwoÅ›ci próbkowania! + + + + Cancel + Anuluj + Resampling wave file +"%1" +from %2 to %3 Hz... + Konwersja pliku audio +„%1†+z %2 do %3 Hz... + + + + Output has clipped +Resampling again and normalizing wave file +"%1" +Try %2 of %3... + Plik wyjÅ›ciowy jest przesterowany. +Ponowna konwersja i normalizacja pliku audio +„%1†+Próba %2 z %3... + + + &Yes - &Tak + &Tak &No - &Nie + &Nie + + + + File version is %1.%2 +Current version is %3.%4 +Conversions may be applied if file is saved! + Plik zapisany jest w wersji %1.%2. +Bieżąca wersja to %3.%4. +Przy zapisywaniu pliku może być wykonana konwersja! + + + + Opening file + Otwieranie pliku + + + + Do not warn again + Nie ostrzegaj ponownie @@ -10320,179 +11777,205 @@ Note Info - Info o dźwiÄ™ku + Informacja o nutach delta/absolute mode - + Tryb delta/bezwzglÄ™dny Start - + Len - DÅ‚ugość + DÅ‚ugość Pitch - Transpozycja + Wysokość Velo On - PrÄ™dk. uderz. + PrÄ™dk. uderz. wÅ‚. - + Velo Off - PrÄ™dk. puszcz. + PrÄ™dk. uderz. wyÅ‚. MusEGui::PartCanvas - + Cannot copy/move/clone to different Track-Type - Nie można skopiować/przenieść/sklonować do innego typu Å›ladu + Nie można skopiować/przenieść/sklonować do Å›ladu innego typu + + + + Part: + Klocek: - + C&ut - + &Wytnij &Copy - &Kopiuj + &Kopiuj s&elect - + W&ybierz clones - + klony rename - zmieÅ„ nazwÄ™ + ZmieÅ„ nazwÄ™ color - kolor + Kolor delete - + Kasuj split - podziel + Podziel glue - + Klej super glue (merge selection) - + Super klej (scalanie wybranych klocków) de-clone - sklonuj + Odklonuj save part to disk - + Zapisz klocek na dysk wave edit - edycja audio + Edycja audio file info - + Informacja o plikach - + + Normalize + Normalizacja + + + MusE: save part - + MusE: zapisz klocek - + Part name: %1 Files: - + Nazwa klocka: %1 +Pliki: - + + Automation: + Automatyka: + + + Remove selected - + UsuÅ„ zaznaczone elementy - + %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. - - + + %n klocek z %1 nie mogÅ‚ zostać wklejony. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. + %n klocki z %1 nie mogÅ‚y zostać wklejone. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. + %n klocków z %1 nie mogÅ‚o zostać wklejonych. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. %n part(s) could not be pasted. Likely the selected track is the wrong type. - - + + %n klocek nie mogÅ‚ zostać wklejony. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. + %n klocki nie mogÅ‚y zostać wklejone. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. + %n klocków nie mogÅ‚o zostać wklejonych. +Najprawdopodobniej wybrany Å›lad ma niewÅ‚aÅ›ciwy typ. Cannot paste: multiple tracks selected - Nie można wkleić: zaznaczono kilka Å›ladów + Nie można wkleić: zaznaczono kilka Å›ladów Cannot paste: no track selected - Nie można wkleić: nie zaznaczono żadnych Å›ladów + Nie można wkleić: nie zaznaczono żadnych Å›ladów Can only paste to midi/drum track - Można tylko wkleić do Å›ladu midi, lub perkusji + Można wkleić tylko do Å›ladu MIDI lub perkusji Can only paste to wave track - Można wkleić tylko do Å›ladu audio + Można wkleić tylko do Å›ladu audio Can only paste to midi or wave track - + Można wkleić tylko do Å›ladu MIDI lub audio Cannot paste: wrong data type - Nie można wkleić: nieprawidÅ‚owy typ danych + Nie można wkleić: nieprawidÅ‚owy typ danych @@ -10500,21 +11983,23 @@ %n quarter(s) - - + + %n ćwiartka + %n ćwiartki + %n ćwiartek %1 quarter for floating-point arguments like 1.5 - + %1 ćwiartki %1 quarters for floating-point arguments like 1.5 - + %1 ćwiartki @@ -10522,411 +12007,470 @@ %n quarter(s) - - + + %n ćwiartka + %n ćwiartki + %n ćwiartek %1 quarter for floating-point arguments like 1.5 - + %1 ćwiartki %1 quarters for floating-point arguments like 1.5 - + %1 ćwiartki MusEGui::PianoRoll - + &Edit - + &Edycja C&ut - + &Wytnij &Copy - &Kopiuj + &Kopiuj Copy events in range - + Kopiuj elementy z zakresu &Paste - &Wklej + &Wklej Paste (with dialog) - + Wklej (pokaż okno dialogowe) Delete &Events - + Kasuj &elementy &Select - &Zaznacz + &Zaznacz Select &All - Zaznacz &wszystko + Zaznacz &wszystko &Deselect All - &Odznacz wszystko + &Odznacz wszystko Invert &Selection - Odwróć &zaznaczenie + Odwróć &zaznaczenie &Inside Loop - &PomiÄ™dzy lokatorami. + &PomiÄ™dzy lokatorami &Outside Loop - &Poza obrÄ™bem lokatorów + &Poza obrÄ™bem lokatorów &Previous Part - + &Poprzedni klocek &Next Part - + &NastÄ™pny klocek Fu&nctions - + D&ziaÅ‚ania Quantize - Kwantyzuj + Kwantyzuj Modify Note Length - + Modyfikuj dÅ‚ugość nut Modify Velocity - + Modyfikuj prÄ™dkość uderzenia (velocity) Crescendo/Decrescendo - + Crescendo/decrescendo Transpose - Transponuj + Transponuj Erase Events - + Kasuj zdarzenia Move Notes - + PrzesuÅ„ nuty Set Fixed Length - Ustaw staÅ‚Ä… dÅ‚ugość nut + Ustaw staÅ‚Ä… dÅ‚ugość nut Delete Overlaps - + Kasuj nakÅ‚adajÄ…ce siÄ™ nuty Legato - + Legato &Plugins - + &Wtyczki Window &Config - + &Konfiguracja okna &Event Color - + &Kolor nut &Blue - + &Niebieski &Pitch colors - + K&oloruj po wysokoÅ›ci &Velocity colors - + Koloruj &po prÄ™dkoÅ›ci uderzenia (velocity) Pianoroll tools - + NarzÄ™dzia Pianoroll Step Record - + Nagrywanie krokowe Midi Input - + WejÅ›cie MIDI Play Events - PodglÄ…d midi + Odgrywaj nuty - + ctrl - + kontr. Add Controller View - + Dodaj widok kontrolera MusEGui::PluginDialog - + MusE: select plugin - MuzA: wybierz wtyczkÄ™ + MusE: wybierz wtyczkÄ™ + + + + Plugin categories. +Right-click on tabs to manage. +Right-click on plugins to add/remove from a category. + Kategorie wtyczek. +Kliknij prawym klawiszem na zakÅ‚adkach, aby zarzÄ…dzać. +Kliknij prawym klawiszem na wtyczkach, aby dodać/usunąć + je z kategorii. Type - Typ + Typ Lib - Bibl. + Bibl. Label - Etykieta + Etykieta Name - Nazwa + Nazwa AI - AI + AWej. AO - AO + AWyj. CI - CI + KWej. CO - CO + KWyj. IP - IP + IP id - numer + Ident. Maker - Znacznik + Autor Copyright - Prawa autorskie + Prawa autorskie Audio inputs - + WejÅ›cia audio Audio outputs - + WyjÅ›cia audio Control inputs - + WejÅ›cia kontrolne Control outputs - + WyjÅ›cia kontrolne In-place capable - + Możliwość pracy w trybie „in-place†ID number - + Numer identyfikacyjny - - Ok - Akceptuj + + &create new group + &Twórz nowÄ… grupÄ™ - - Cancel - Anuluj + + &delete currently selected group + &UsuÅ„ zaznaczonÄ… grupÄ™ - - Show plugs: - + + re&name currently selected group + ZmieÅ„ &nazwÄ™ zaznaczonej grupy - - Mono and Stereo - + Ok + Akceptuj - - Stereo - Stereo + Cancel + Anuluj - - Mono - Mono + Stereo + Stereo - - Show All - + Mono + Mono Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. - + But where the All tab is? + Zaznacz typy wtyczek, które powinny być widoczne na liÅ›cie.<br>Zwróć uwagÄ™, że dopuszczalne jest umieszczanie wtyczek mono na Å›ladach stereofonicznych. W takim przypadku użyte zostanÄ… równolegle dwie.<br>Zwróć też uwagÄ™, że w zakÅ‚adce grupujÄ…cej wszystkie wtyczki, mogÄ… znaleźć siÄ™ też takie, które nie bÄ™dÄ… przydatne w racku efektów. + + + + Associated categories + PrzyporzÄ…dkowane kategorie + + + + You need to define some categories first. + Musisz najpierw zdefiniować jakieÅ› kategorie. + + + + + new group + Nowa grupa + + + + + Enter the new group name + Wpisz nazwÄ™ nowej grupy + + + + Wine VST + Wine VST - + dssi synth - + synt. DSSI dssi effect - + efekt DSSI - - ladspa - + + LV2 synth + synt. LV2 - - Search in 'Label' and 'Name': - + + LV2 effect + efekt LV2 + + + + VST synth + synt.VST + + + + VST effect + efekt VST + + + + ladspa + ladspa MusEGui::PluginGui - + File Buttons - Przyciski Menu Plik + Przyciski Menu Plik Load Preset - ZaÅ‚aduj Ustawienie + ZaÅ‚aduj ustawienie Save Preset - Zapisz ustawienie + Zapisz ustawienie bypass plugin - omiÅ„ wtyczkÄ™ (bypass) + omiÅ„ wtyczkÄ™ (bypass) - + MusE: load preset - MuzA: zaÅ‚aduj ustawienie + MusE: zaÅ‚aduj ustawienie Error reading preset. Might not be right type for this plugin - + BÅ‚Ä…d podczas czytania ustawieÅ„. Ich rodzaj może nie być wÅ‚aÅ›ciwy dla tej wtyczki MusE: save preset - MuzA: zapisz ustawienie + MusE: zapisz ustawienie + + + + MusEGui::PopupMenu + + + <More...> %1 + <WiÄ™cej...> %1 @@ -10934,405 +12478,589 @@ Select directory - + Wybierz katalog + + + + MusEGui::RouteDialog + + + Normal + Normalny + + + + Alias 1 + Alias 1 + + + + Alias 2 + Alias 2 + + + + + Source + Urz. źródÅ‚owe + + + + + Destination + Urz. docelowe + + + + <none> + <brak> MusEGui::RoutePopupMenu - - - - - - - - + Channel - + KanaÅ‚ - - - + + + + Soloing chain - + ÅaÅ„cuch trybu solo - - + + + Audio returns - + Powroty audio - + Warning: No input devices! - + Uwaga: brak urzÄ…dzeÅ„ wejÅ›ciowych! + + + + Channel grouping: + Grupowanie kanałów: + + + + Mono + Mono + + + + Stereo + Stereo + + + + + + + + + + + + Channels + KanaÅ‚y + + + + + Midi ports/devices + Porty/urzÄ…dzenia MIDI + + + + + + Omni + Omni + + + + Show aliases: + Pokaż aliasy: + + + + First + Pierwszy + + + + Second + Drugi + + + + Show names + Pokaż nazwy + + + + Show first aliases + Pokaż pierwsze aliasy + Show second aliases + Pokaż drugie aliasy + + + + + Jack ports + Porty Jack + + + + + Connect + PoÅ‚Ä…cz + + + + Unavailable + NiedostÄ™pny + + + + Open advanced router... + Otwórz KonfiguracjÄ™ PoÅ‚Ä…czeÅ„... + + + + Output routes: + PoÅ‚Ä…czenia wyjÅ›ciowe: + + + + Input routes: + PoÅ‚Ä…czenia wejÅ›ciowe: + + + Open midi config... - + Otwórz konfiguracjÄ™ MIDI... - - - - + <none> - + <brak> - - Toggle all - + + Midi sends + WysyÅ‚ki MIDI - + + Sources: + ŹródÅ‚a: + + + More... - + WiÄ™cej... - - Audio sends - + + + Tracks + Åšlady - - Midi port sends - + + Destinations: + Urz. docelowe: + + + + Audio sends + WysyÅ‚ki audio MusEGui::ScoreCanvas - + Treble - + Klucz wiolinowy Bass - + Klucz basowy Grand Staff - + Akolada Remove staff - + UsuÅ„ piÄ™cioliniÄ™ - + Ambiguous part - + Klocek jest niejednoznaczny There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. - + IstniejÄ… klocki, do których można dodać nutÄ™, jednak żaden z nich nie odpowiada tym wybranym. ProszÄ™ wybrać klocek docelowy klikajÄ…c na dowolnej należącej do niego nucie, nastÄ™pnie spróbować ponownie. Można też dodać nowÄ… piÄ™cioliniÄ™ zawierajÄ…cÄ… jedynie klocek docelowy. No part - + Brak klocka There are no parts you could add the note to. - + Nie ma klocków, do których można dodać nutÄ™. MusEGui::ScoreEdit - + Step recording tools - + NarzÄ™dzia nagrywania krokowego Step Record - + Nagrywanie krokowe Note settings - + Ustawienia nut Note length: - + DÅ‚ugość nuty: last - + ost. Apply to new notes: - + Zastosuj dla nowych nut: Apply to selected notes: - + Zastosuj dla wybranych nut: Velocity: - + PrÄ™dk. uderz. (velocity): Off-Velocity: - + Predk. uderz. (velocity) wyÅ‚.: Quantisation settings - + Ustawienia kwantyzacji Quantisation: - + Kwantyzacja: Pixels per whole: - + :-/ + Rozmiar: &Edit - + &Edycja C&ut - + &Wytnij &Copy - &Kopiuj + &Kopiuj Copy events in range - + Kopiuj elementy z zakresu &Paste - &Wklej + &Wklej Paste (with dialog) - + Wklej (pokaż okno dialogowe) Delete &Events - + Kasuj &elementy &Select - &Zaznacz + &Zaznacz Select &All - Zaznacz &wszystko + Zaznacz &wszystko &Deselect All - &Odznacz wszystko + &Odznacz wszystko Invert &Selection - Odwróć &zaznaczenie + Odwróć &zaznaczenie &Inside Loop - &PomiÄ™dzy lokatorami. + &PomiÄ™dzy lokatorami &Outside Loop - &Poza obrÄ™bem lokatorów + Poz&a obrÄ™bem lokatorów Fu&nctions - + D&ziaÅ‚ania &Quantize - + &Kwantyzuj Change note &length - + Modyfikuj &dÅ‚ugość nut Change note &velocity - + ZmieÅ„ &prÄ™dkość uderzenia (velocity) Crescendo/Decrescendo - + Crescendo/decrescendo Transpose - Transponuj + Transponuj Erase Events - + Kasuj zdarzenia Move Notes - + PrzesuÅ„ nuty Set Fixed Length - Ustaw staÅ‚Ä… dÅ‚ugość nut + Ustaw staÅ‚Ä… dÅ‚ugość nut Delete Overlaps - + Kasuj nakÅ‚adajÄ…ce siÄ™ nuty Legato - + Legato Window &Config - + &Konfiguracja okna Note head &colors - + Kolory &główki nuty &Black - + &Czarny &Velocity - + Koloruj &po prÄ™dkoÅ›ci uderzenia (velocity) &Part - + Koloruj po &klocku Set up &preamble - + Ko&nfiguracja notacji Display &key signature - + WyÅ›wietlaj &tonacjÄ™ Display &time signature - + WyÅ›wietlaj &metrum Set Score &name - + Ustaw nazwÄ™ arkusza nutowego Enter the new score title - + Wpisz nowy tytuÅ‚ arkusza Error - + BÅ‚Ä…d Changing score title failed: the selected title is not unique - + Zmiana nazwy arkusza nutowego nie powiodÅ‚a siÄ™: +podany tytuÅ‚ nie jest unikalny MusEGui::ScrollScale - + next page - nastÄ™pna strona + nastÄ™pna strona previous page - poprzednia strona + poprzednia strona current page number - aktualny numer strony + aktualny numer strony MusEGui::ShortcutCaptureDialog - + Ok - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj Shortcut conflicts with %1 - + Konflikt skrótu z %1 Undefined - Niezdefiniowany + Niezdefiniowany + + + + MusEGui::ShortcutConfig + + + Save printable text file + Zapisz jako plik tekstowy + + + + Text files (*.txt);;All files (*) + Pliki tekstowe (*.txt);;Wszystkie pliki (*) + + + + + Error + BÅ‚Ä…d + + + + Error opening file for saving + Nie można otworzyć pliku do zapisu + + + + Shortcuts for selected category: + Skróty dla wybranej kategorii: + + + + Legend: + + Legenda: + + + + + An error occurred while saving + WystÄ…piÅ‚ bÅ‚Ä…d podczas zapisu @@ -11340,263 +13068,320 @@ signature scale - oÅ› metrum + oÅ› metrum MusEGui::SigToolbarWidget - + time signature at current position - Wstaw tempo w aktualnej pozycji kursora + Wstaw metrum w aktualnej pozycji kursora Signature: - + Metrum: MusEGui::Strip - Remove track? - + Usunąć Å›lad? + + + + Remove track + UsuÅ„ Å›lad + + + + Hide strip + Ukryj kanaÅ‚ MusEGui::TList - + <none> - + <brak> - + visible - + widoczna no clef - + brak klucza Treble - + Kl. wiol. Bass - + Kl. bas. Grand - + Akolada - + off - + wyÅ‚. - + <unknown> - + <nieznany> MusE: bad trackname - MuzA: zÅ‚a nazwa Å›ladu + MusE: bÅ‚Ä™dna nazwa Å›ladu please choose a unique track name - proszÄ™ wybrać niepowtarzalnÄ… nazwÄ™ dla Å›ladu + ProszÄ™ wybrać unikalnÄ… nazwÄ™ Å›ladu - + Unused Devices - + Nieużywane urzÄ…dzenia - - Update drummap? - Zaktualizować zestaw perkusyjny? + Zaktualizować zestaw perkusyjny? - Do you want to use same port for all instruments in the drummap? - Czy chcesz używać jednego portu midi dla wszystkich instrumentów w zestawie perkusyjnym? + Czy chcesz używać jednego portu MIDI dla wszystkich instrumentów w zestawie perkusyjnym? - - &Yes - &Tak + &Tak - - &No - &Nie + &Nie - - + + show gui - pokaż interfejs użytkownika + pokaż interfejs użytkownika - - + + show native gui - + pokaż natywny interfejs użytkownika + + + + + Presets + Ustawienia + + + + Clear all controller events? + WyczyÅ›cić wszystkie zdarzenia kontrolerów? + + + + &Ok + &Akceptuj + + + + &Cancel + &Anuluj + + + + Change color + ZmieÅ„ kolor - + Midi control - + Sterowanie MIDI Assign - + PrzyporzÄ…dkuj Clear - + Wyczyść - + + Other + PozostaÅ‚e + + + + clear automation + wyczyść automatykÄ™ + + + Treble clef - + Klucz wiolinowy Bass clef - + Klucz basowy Grand Staff - + Akolada Viewable automation - + Widoczna automatyka Internal - + WewnÄ™trzne Synth - + Syntezator - + Delete Track - + Skasuj Å›lad - + + Delete Selected Tracks + Skasuj zaznaczone Å›lady + + + Track Comment - Opis Å›ladu + Opis Å›ladu Save track's drumlist - + Zapisz zestaw perkusyjny Å›ladu Save track's drumlist differences to initial state - + Unsure + Zapisz różnice w zestawie perkusyjnym Å›ladu w stosunku do stanu poczÄ…tkowego Load track's drumlist - + Åaduj zestaw perkusyjny Å›ladu Reset track's drumlist - + Przywróć zestaw perkusyjny Reset track's drumlist-ordering - + Przywróć kolejność zestawu perkusyjnego Copy track's drumlist to all selected tracks - + Kopiuj zestaw perkusyjny Å›ladu do zaznaczonych Å›ladów Copy track's drumlist's differences to all selected tracks - + Kopiuj różnice w zestawie perkusyjnym Å›ladu do zaznaczonych Å›ladów Insert Track - + Dodaj Å›lad - + Drum map - + Zestaw perkusyjny Reset the track's drum map with instrument defaults? - + Przywrócić zestaw perkusyjny Å›ladu do wartoÅ›ci domyÅ›lnych? Reset the track's drum map ordering? - + Przywrócić kolejność zestawu perkusyjnego Å›ladu? - + Muse: Load Track's Drum Map - + MusE: zaÅ‚aduj zestaw perkusyjny - + + Drummap + Zestaw perkusyjny + + + + This drummap was created with a previous version of MusE, +it is being read but the format has changed slightly so some +adjustments may be necessary. + Ten zestaw perkusyjny zostaÅ‚ stworzony za pomocÄ… poprzedniej +wersji MusE. ZostaÅ‚ przeczytany, jednak format pliku nieco siÄ™ +zmieniÅ‚. Pewne zmiany mogÄ… siÄ™ okazać konieczne. + + + MusE: Store Track's Drum Map - + MusE: zapisz zestaw perkusyjny - + Midi - Midi + MIDI Drum - Perkusja + Perkusja New style drum - + Perkusja nowego typu - Do you want to use same port and channel for all instruments in the drummap? - Czy chcesz używać jednego portu i jednego kanaÅ‚u dla wszystkich instrumentów w zestawie perkusyjnym? + Czy chcesz używać jednego portu i jednego kanaÅ‚u dla wszystkich instrumentów w zestawie perkusyjnym? @@ -11604,20 +13389,25 @@ Tempo/Sig - Tempo/Metrum + Tempo/Metrum MusEGui::TempoToolbarWidget - + tempo at current position - Wstaw tempo w aktualnej pozycji kursora + Wstaw tempo w aktualnej pozycji kursora Tempo: - + Tempo: + + + + TAP + TAP @@ -11627,125 +13417,130 @@ Off - WyÅ‚. + WyÅ‚. - + Solo - Solo + Solo - + Cursor - + Kursor - + Snap - + PrzyciÄ…ganie MusEGui::TopWin - + As subwindow - + Jako podokno Shares tools and menu - + Współdzieli narzÄ™dzia oraz menu Fullscreen - + Tryb peÅ‚noekranowy - + Undo/Redo tools - + NarzÄ™dzia Cofnij/Przywróć Panic - Zatrzymaj wszystkie komunikaty midi! + Zatrzymaj wszystkie komunikaty MIDI! + + + + Metronome + Metronom Transport - Panel transportu + Panel transportu - + Song Position - + Pozycja utworu Tempo - Tempo + Tempo Signature - Metrum + Metrum - + Piano roll - + Pianoroll List editor - + Edytor Lista Drum editor - + Edytor Perkusji Master track editor - + Edytor graficzny Tempo/Metrum Master track list editor - + Edytor Lista Tempo/Metrum Wave editor - + Edytor Audio Clip list - + Lista Klipów Marker view - + Edytor Znaczników Score editor - + Edytor Zapisu Nutowego Arranger - Główne Okno + Główne Okno <unknown toplevel type> - + <nieznany typ najwyższego poziomu> @@ -11753,12 +13548,12 @@ MusE: Track Comment - MuzA: Opis Åšladu + MusE: opis Å›ladu Track Comment: - Opis Åšladu + Opis Å›ladu: @@ -11766,186 +13561,186 @@ Overdub - Dogrywanie + Dogrywanie Replace - ZastÄ™powania + ZastÄ™powanie Rec Mode - Typ Nagrywania + Tryb nagrywania Normal - Normalny + Normalny Mix - Miks + Miks Cycle Rec - ZapÄ™tl nagrywanie + ZapÄ™tl nagrywanie punchin - wÅ‚Ä…cz wcinki (punch In) + wÅ‚Ä…cz wcinki (punch In) loop - pÄ™tla + pÄ™tla punchout - wyÅ‚. wcinki (punch out)punchout + wyÅ‚. wcinki (punch out) Punch In - WÅ‚Ä…cz wcinki (punch In) + WÅ‚Ä…cz wcinki (punch In) Loop - PÄ™tla + PÄ™tla Punch Out - WyÅ‚. wcinki (punch out) + WyÅ‚. wcinki (punch out) Left Mark - Lewy Lokator + Lewy lokator Right Mark - Prawy Lokator + Prawy lokator rewind to start - przewiÅ„ do poczÄ…tku + Przewijanie do poczÄ…tku Click this button to rewind to start position - PrzewiÅ„ do poczÄ…tku. + WciÅ›nij ten przycisk, aby przewinąć do poczÄ…tku rewind - + Przewijanie do tyÅ‚u Click this button to rewind - Przewijaj do tyÅ‚u. + WciÅ›nij ten przycisk, aby przewinąć do tyÅ‚u forward - + Przewijanie do przodu Click this button to forward current play position - Przewijaj do przodu. + WciÅ›nij ten przycisk, aby przewinąć do poczÄ…tku stop - + Stop Click this button to stop playback - Zatrzymaj odtwarzanie. + WciÅ›nij ten przycisk, aby zatrzymać odtwarzanie play - + Odtwarzanie Click this button to start playback - Rozpocznij odtwarzanie. + Kliknij ten przycisk, aby rozpocząć odtwarzanie record - + Nagrywanie Click this button to enable recording - NaciÅ›nij ten przycisk, aby pozwolić na nagrywanie + NaciÅ›nij ten przycisk, aby pozwolić na nagrywanie AC - Aut. Kw. + Aut. kwant. quantize during record - kwantyzuj podczas nagrywania + Kwantyzuj podczas nagrywania Click - Metronom + Metronom metronom click on/off - wÅ‚Ä…cz/wyÅ‚Ä…cz metronom + WÅ‚Ä…cz/wyÅ‚Ä…cz metronom Sync - Synch. + Synch. external sync on/off - synchronizowanie do zewn. urz. + Synchronizacja do zewnÄ™trznego urzÄ…dzenia (wÅ‚./wyÅ‚.) Jack - + Jack Jack transport sync on/off - + Synchronizacja transportu Jack (wÅ‚./wyÅ‚.) Master - + Master use master track - Używaj Å›ladu tempo/metrum + Używaj Å›ladu tempo/metrum (master track) @@ -11954,180 +13749,248 @@ Show wave tracks - + Pokaż Å›lady audio Show group tracks - + Pokaż Å›lady grup Show aux tracks - + Pokaż szyny (aux) Show input tracks - + Pokaż Å›lady wejÅ›cia Show output tracks - + Pokaż Å›lady wyjÅ›cia Show midi tracks - + Pokaż Å›lady MIDI Show synth tracks - + Pokaż Å›lady syntezatorów Visible track types - + Pokazywane Å›lady + + + + MusEGui::WaveCanvas + + + Adjust Wave Offset + Dopasuj przesuniÄ™cie (offset) audio + + + + Wave offset (frames) + PrzesuniÄ™cie audio (ramki) + + + + Part creation failed + Utworzenie klocka nie powiodÅ‚o siÄ™ + + + + Left and right position markers must be placed inside the current part. + Lewy i prawy znacznik musi być umieszczony wewnÄ…trz bieżącego klocka. + + + + Part created + Utworzono klocek + + + + The selected region has been copied to the clipboard and can be pasted in the arranger. + Wybrany zakres zostaÅ‚ skopiowany do schowka i może być wklejony w Głównym Oknie. + + + + MusE - external editor failed + MusE - wystÄ…piÅ‚ bÅ‚Ä…d w zewnÄ™trznym edytorze + + + + MusE was unable to launch the external editor +check if the editor setting in: +Global Settings->Audio:External Waveditor +is set to a valid editor. + MusE nie mógÅ‚ uruchomić zewnÄ™trznego edytora. +Sprawdź, czy w okienku: +Ustawienia MusE->Ustawienia globalne-> + Audio:WywoÅ‚anie zewnÄ™trznego edytora +znajduje siÄ™ poprawne wywoÅ‚anie edytora. + + + + MusE - file size changed + MusE - zmieniÅ‚ siÄ™ rozmiar pliku + + + + When editing in external editor - you should not change the filesize +since it must fit the selected region. + +Missing data is muted + Podczas edycji w zewnÄ™trznym edytorze nie powinno siÄ™ zmieniać +rozmiaru edytowanego pliku, ponieważ musi on pasować do +wybranego zakresu. + +BrakujÄ…ce fragmenty zostanÄ… wyciszone. MusEGui::WaveEdit - + &Edit - + &Edycja Func&tions - + D&ziaÅ‚ania &Gain - + &Wzmocnienie Other - + Inne &Copy - &Kopiuj + &Kopiuj + + + + &Create Part from Region + &Utwórz klocek z zakresu C&ut - + &Wytnij &Paste - &Wklej + &Wklej Edit in E&xternal Editor - + Edytuj w &zewnÄ™trznym edytorze audio - + Mute Selection - + Wycisz zaznaczenie Normalize Selection - + Normalizuj zaznaczenie Fade In Selection - + ZgÅ‚aÅ›niaj zaznaczenie (fade in) Fade Out Selection - + Åšciszaj zaznaczenie (fade out) Reverse Selection - + Odwróć zaznaczenie Select - Wybierz + Wybierz Select &All - Zaznacz &wszystko + Zaznacz &wszystko &Deselect All - &Odznacz wszystko + &Odznacz wszystko - - Window &Config - + + &Previous Part + &Poprzedni klocek - - WaveEdit tools - + + &Next Part + &NastÄ™pny klocek - Solo - Solo + Window &Config + &Konfiguracja okna - - Cursor - + + &Event Color + &Kolor audio - - - MusEGui::WaveView - - MusE - external editor failed - + + &Part colors + &Kolory klocka - - MusE was unable to launch the external editor -check if the editor setting in: -Global Settings->Audio:External Waveditor -is set to a valid editor. - + + &Gray + &Szary - - MusE - file size changed - + + WaveEdit tools + NarzÄ™dzia edycyjne audio - - When editing in external editor - you should not change the filesize -since it must fit the selected region. - -Missing data is muted - + + Solo + Solo + + + + Cursor + Kursor @@ -12258,72 +14121,72 @@ MusE: Paste Parts - + MusE: wklej klocki Number and raster - + Liczba i raster insert - + wstaw times - + raz(y) raster - + raster ticks - + ticks Move, Merge, Clone - + Przesuwanie, scalanie, klonowanie Move everything to the right - + PrzesuÅ„ wszystko do prawej Move only affected parts to the right - + PrzesuÅ„ do prawej tylko zmodyfikowane klocki Put everything into a single track - + Umieść wszystko na jednym Å›ladzie Merge with existing parts - + Scal z istniejÄ…cymi klockami Insert as clones (where possible) - + Wstaw jako klony (jeÅ›li to możliwe) OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj @@ -12331,69 +14194,123 @@ MusE: Paste Events - + MusE: wklej elementy Number and raster - + Liczba i raster insert - + wstaw times - + raz(y) raster - + raster ticks - + ticks Paste options - + Opcje wklejania Always into existing parts - + Zawsze do istniejÄ…cych klocków Never into existing parts - + Nigdy do istniejÄ…cych klocków Into existing parts if part has not to be expanded by more than - + Do istniejÄ…cych klocków, jeÅ›li klocek +nie zostanie rozszerzony bardziej, niż o: Put everything into the (selected) part - + Umieść wszystko w zaznaczonym Å›ladzie OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj + + + + PluginDialogBase + + + Dialog + + + + + Ports: + Porty: + + + + Mono + Mono + + + + Mono + Stereo + + + + + Stereo + Stereo + + + + All + Wszystko + + + + Plugin type: + Rodzaj wtyczki: + + + + Filter: + Filtr: + + + + &OK + &Akceptuj + + + + &Cancel + A&nuluj @@ -12401,54 +14318,55 @@ Create Project - + Twórz projekt Projects folder: - + Katalog projektu: - + ... - ... + ... - + Project Name: - + Nazwa projektu: Project is a Template - + Projekt jest szablonem Write window state - + Zapisuj pozycje okien Project song file type: - + Typ pliku utworu: Project Path to song file: - + Åšcieżka do utworu: - Create project folder (recommended for audio projects) - + &Create project folder (recommended for audio projects) + &Utwórz katalog projektu (rekomendowane w przypadku +projektów z plikami audio) - + Song information: - + Informacja o utworze: @@ -12457,28 +14375,132 @@ Error - + BÅ‚Ä…d Please first select the range for crescendo with the loop markers. - + ProszÄ™ najpierw zaznaczyć zakres crescendo za pomocÄ… lokatorów pÄ™tli. + + + + Controller ! + Kontroler ! + + + + Other ! + Inny ! + + + + Select gui type + Wybierz typ GUI + + + + Preset actions + DziaÅ‚ania na ustawieniach + + + + Save preset... + Zapisz ustawienie... + + + + Update list + OdÅ›wież listÄ™ + + + + Saved presets + Zapisane ustawienia + + + + No presets found + Nie znaleziono żadnego ustawienia + + + + Enter new preset name + Wpisz nowÄ… nazwÄ™ ustawienia + + + + Midi programs + Programy MIDI + + + + Presets + Ustawienia + + + + Switch on General Midi Level 1 mode + WÅ‚Ä…cz tryb General MIDI Level 1 + + + + Switch on General Midi Level 2 mode + WÅ‚Ä…cz tryb General MIDI Level 2 + + + + Switch off General Midi Level 1 or 2 + WÅ‚Ä…cz tryb General MIDI Level 1 lub 2 + + + + Switch on Roland GS mode + WÅ‚Ä…cz tryb Roland GS + + + + Switch on Yamaha XG mode + WÅ‚Ä…cz tryb Yamaha XG + + + + Tracks: + Åšlady: + + + + Midi ports: + Porty MIDI: + + + + Midi devices: + UrzÄ…dzenia MIDI: + + + + Jack: + Jack: + + + + Jack midi: + Jack MIDI: QWidget - - + + Cannot convert sysex string - Nie można przekonwertować Å‚aÅ„cucha sysex + Nie można przekonwertować Å‚aÅ„cucha SysEx - - + Hex String too long (2048 bytes limit) - ÅaÅ„cuch Hex za dÅ‚ugi (limit 2048 bajtów) + ÅaÅ„cuch hex za dÅ‚ugi (limit 2048 bajtów) @@ -12486,14 +14508,14 @@ nowy - + create peakfile for - stwórz plik szczytu dla + stwórz plik szczytu dla MusE: get file name - MuzA: weź nazwÄ™ pliku + MusE: pobierz nazwÄ™ pliku @@ -12501,12 +14523,15 @@ %1 does not exist. Create it? - + Katalog +%1 +nie istnieje. +Utworzyć? MusE: create directory - MuzA: utwórz katalog + MusE: utwórz katalog @@ -12514,44 +14539,84 @@ tworzenie katalogu nie powiodÅ‚o siÄ™ - + File %1 exists. Overwrite? - + Plik +%1 +istnieje. Nadpisać? Open File %1 failed: %2 - + Otwarcie pliku +%1 +nie powiodÅ‚o siÄ™: %2 MusE: write - MuzA: zapis: + MusE: zapis: MusE: Open File - MuzA: Otwórz plik + MusE: otwórz plik - - + + None Brak - + generic midi - generyczne midi + Ogólny instrument MIDI - + + No wave events selected. + Nie wybrano zdarzeÅ„ audio + + + No selection. Ignoring - + Niczego nie zaznaczono. Pomijam. + + + + Instrument-defined + Zdefiniowane w instrumencie + + + + + Edit instrument ... + Edytuj instrument... + + + + Add + Dodaj + + + + Others + Inne + + + + Common Controls + Typowe kontrolery + + + + Velocity + PrÄ™dk. uderz. (vel) @@ -12559,117 +14624,117 @@ MusE: Quantize - + MusE: kwantyzacja Range - Zakres + Zakres All Events - Wszytkie Elementy + Wszystkie elementy Selected Events - Zaznaczone Elementy + Zaznaczone elementy Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami Values - Wartość + WartoÅ›ci Strength: - + SiÅ‚a: % - % + % Threshold (ticks): - + Próg (ticks): Quantize Len - + Kwantyzuj dÅ‚ugość Raster - + Raster Whole - + CaÅ‚a nuta (1) Half - + Półnuta (2) 4th - + Ćwierćnuta (4) 4th Triplet - + Triola ćwierćnutowa (4T) 8th - + Ósemka (8) 8th Triplet - + Triola ósemkowa (8T) 16th - + Szesnastka (16) 16th Triplet - + Triola szesnastkowa (16T) 32th - + Trzydziestodwójka (32) 32th Triplet - + Triola trzydziestodwójkowa (32T) Swing: - + Swing: @@ -12678,17 +14743,22 @@ If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. - + JeÅ›li zmiana pozycji bÄ…dź dÅ‚ugoÅ›ci nut jest mniejsza od progu, nic nie zostanie zrobione. + +Jeżeli swing bÄ™dzie ustawiony na 0, rytm siÄ™ nie zmieni. +jeżeli swing bÄ™dzie ustawiony na 33, powstanie rytm 2:1. +jeżeli swing bÄ™dzie ustawiony na -33, powstanie rytm 1:2. + OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj @@ -12696,52 +14766,52 @@ MusE: Erase Notes - + MusE: kasuj zdarzenia Range - Zakres + Zakres All Events - Wszytkie Elementy + Wszystkie elementy Selected Events - Zaznaczone Elementy + Zaznaczone elementy Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami Thresholds - + Progi ticks - + ticks Velocity - PrÄ™dk. uderz. (vel) + PrÄ™dk. uderz. (vel) Length - + DÅ‚ugość @@ -12752,17 +14822,23 @@ <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Jeżeli nic nie zaznaczono, usuwane sÄ… wszystkie zdarzenia..</p> +<p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Jeżeli zaznaczono „PrÄ™dk. uderz. (vel)â€, usuwane sÄ… jedynie nuty z wartoÅ›ciÄ… velocity &lt; progu.</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Jeżali zaznaczono obie opcje, usuwane sÄ… nuty z wartoÅ›ciÄ… velocity &lt; progu LUB nuty o dÅ‚ugoÅ›ci &lt; od progu.</p></body></html> OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj @@ -12777,57 +14853,169 @@ MusE: Routing - MuzA: Konfiguracja poÅ‚Ä…czeÅ„ + MusE: Konfiguracja PoÅ‚Ä…czeÅ„ + + + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + Lista dostÄ™pnych urzÄ…dzeÅ„ + źródÅ‚owych. +PoÅ‚Ä…cz źródÅ‚a sygnaÅ‚u z urzÄ…dzeniami + docelowymi. +W przypadków pozycji z listÄ… kanałów, + poÅ‚Ä…czenia można tworzyć w trybie + Omni Mode (pozycja tekstowa) bÄ…dź + w trybie Channel Mode (poÅ‚Ä…czenia + pomiÄ™dzy poszczególnymi kanaÅ‚ami). + + + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. + Okno z widokiem poÅ‚Ä…czeÅ„. +Pokazuje bieżące poÅ‚Ä…czenia. +Grube linie oznaczajÄ… + poÅ‚Ä…czenia Omni Mode. +Cienkie linie oznaczajÄ… + poÅ‚Ä…czenia Channel Mode. + + + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + Lista dostÄ™pnych urzÄ…dzeÅ„ docelowych. +PoÅ‚Ä…cz źródÅ‚a z urzÄ…dzeniami + docelowymi. +W przypadków pozycji z listÄ… kanałów, + poÅ‚Ä…czenia można tworzyć w trybie + Omni Mode (pozycja tekstowa) bÄ…dź + w trybie Channel Mode (poÅ‚Ä…czenia + pomiÄ™dzy poszczególnymi kanaÅ‚ami). + + + + Itemized list of current connections. + Lista bieżących poÅ‚Ä…czeÅ„. + + + + + Show only selected sources + Pokaż tylko wybrane urzÄ…dzenia źródÅ‚owe + + + + + Show only destination routes for selected source + Pokaż tylko docelowe poÅ‚Ä…czenia dla wybranego źródÅ‚a + + + + + Show only selected destinations + Pokaż tylko wybrane urzÄ…dzenia docelowe + + + + + Show only source routes for selected destination + Pokaż tylko źródÅ‚owe poÅ‚Ä…czenia dla wybranego urzÄ…dzenia docelowego + + + + + Show all Midi Ports + Pokaż wszystkie porty MIDI + + + + Auto adjust column size + Automatycznie dopasuj rozmiary kolumn + + + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + Automatycznie dopasowuje szerokość drzew + urzÄ…dzeÅ„ źródÅ‚owych i docelowych przy + zmianie rozmiaru ich okien. +Opcja ta wÅ‚Ä…cza także zawijanie tekstu, co + może spowolnić wyÅ›wietlanie przy dÅ‚uższych + listach. + + + + + Preferred route name or alias + Preferowana nazwa poÅ‚Ä…czenia bÄ…dź alias + + + + + Connect source to destination + ÅÄ…czy urzÄ…dzenie źródÅ‚owe z docelowym + + + + + Remove selected route + Usuwa wybrane poÅ‚Ä…czenie - Add Route - Dodaj poÅ‚Ä…czenie + Dodaj poÅ‚Ä…czenie - Source: - ŹródÅ‚o: + ŹródÅ‚o: - Destination: - Odbiornik: + Odbiornik: - + Connect PoÅ‚Ä…cz - connect source to destination - poÅ‚Ä…cz źródÅ‚o do odbiornika + poÅ‚Ä…cz źródÅ‚o do odbiornika - Current Routes - Aktualne poÅ‚Ä…czenia + Aktualne poÅ‚Ä…czenia - Source - ŹródÅ‚o + ŹródÅ‚o - Destination - Odbiornik + Odbiornik - + Remove UsuÅ„ - remove selected route - UsuÅ„ wybrane poÅ‚Ä…czenie + UsuÅ„ wybrane poÅ‚Ä…czenie @@ -12835,75 +15023,75 @@ SimpleDrums - Ladspa Plugin Chooser - + Simple Drums - lista wtyczek Ladspa Name - Nazwa + Nazwa Label - Etykieta + Etykieta Inports - + Porty wej. Outports - + Porty wyj. Creator - + Autor &Cancel - + A&nuluj Alt+C - Alt+C + Alt+N &OK - &Akceptuj + &Akceptuj Alt+O - + Alt+A SS_PluginFront - + Clear and unload effect - + Wyczyść i usuÅ„ efekt Load effect - + Åaduj efekt Toggle display of effect parameters - + WÅ‚Ä…cz/wyÅ‚Ä…cz wyÅ›wietlanie parametrów efektów Turn effect on/off - + WÅ‚Ä…cz/wyÅ‚Ä…cz efekt @@ -12911,57 +15099,57 @@ MusE: Set Note Length - + MusE: ustaw staÅ‚Ä… dÅ‚ugość nut Range - Zakres + Zakres All Events - Wszytkie Elementy + Wszystkie elementy Selected Events - Zaznaczone Elementy + Zaznaczone elementy Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami Value - Wartość + Wartość New length - + Nowa dÅ‚ugość ticks - + ticks OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj @@ -12969,7 +15157,7 @@ Enter shortcut sequence - Wprowadź skrót - kominacjÄ™ klawiszy + Wprowadź skrót - kombinacjÄ™ klawiszy @@ -13008,12 +15196,12 @@ Configure Keyboard Shortcuts - Konfiguruj Skróty Klawiaturowe + Konfiguruj skróty klawiaturowe Shortcut Category - Kategoria Skrótu + Kategoria skrótu @@ -13033,12 +15221,12 @@ Alt+C - Alt+C + Alt+W &Define - &Zdefiniuj + Z&definiuj @@ -13048,38 +15236,73 @@ &Apply - &Zastosuj + Z&astosuj Alt+A Alt+A + + + &Printable file... + &Plik tekstowy... + + + + Alt+P + + + + + &Ok + A&kceptuj + + + + Alt+O + Alt+K + SimpleDrumsGuiBase DrumSynth 0.1 - + SimpleSynthGui - + + Mix + Miks + + + + Chn + Kan. + + + + Channel routing + PoÅ‚Ä…czenia kanałów + + + &Load setup - + &ZaÅ‚aduj konfiguracjÄ™ &Save setup - + Z&apisz konfiguracjÄ™ - + Load sample dialog - + Okno zaÅ‚aduj sample @@ -13087,54 +15310,51 @@ Song Information - + Informacja o utworze Show on song load - + Pokaż po zaÅ‚adowaniu utworu &Cancel - + &Anuluj Alt+C - Alt+C + Alt+A &Ok - &Akceptuj + A&kceptuj Alt+O - + Alt+K SynthConfigBase - Soft Synthesizer - Syntezator Softowy + Syntezator Softowy - + File Plik - Instances - UrzÄ…dzenia + UrzÄ…dzenia - - + Name Nazwa @@ -13144,38 +15364,38 @@ lista wszystkich dostÄ™pnych syntezatorów softowych - Add Instance - Dodaj urzÄ…dzenie + Dodaj urzÄ…dzenie - Remove Instance - UsuÅ„ urzÄ…dzenie + UsuÅ„ urzÄ…dzenie - Midi Port - Port Midi - - - - Midi Port and Soft Synth Configuration - + Port Midi - - + Type - Typ + Typ - Midi connections - PoÅ‚Ä…czenia midi + PoÅ‚Ä…czenia MIDI - + + Configure midi devices, midi ports, and synthesizers + Konfiguruj urzÄ…dzenia MIDI, porty MIDI i syntezatory + + + + Available soft synthesizers + DostÄ™pne syntezatory programowe + + + Inst Instr. @@ -13189,58 +15409,108 @@ Description Opis + + + Useable devices + Gotowe urzÄ…dzenia + + + + Add: + Dodaj: + + + + Synth + Synt. + + + + ALSA + + + + + JACK + + + + + Rename + ZmieÅ„ nazwÄ™ + + + + Remove + UsuÅ„ + + + + Device port assignments + PrzyporzÄ…dkowanie urzÄ…dzeÅ„ do portów + + + + &Apply + &Zastosuj + + + + &OK + &Akceptuj + TransposeBase MusE: Transpose - + MusE: transponuj Range - Zakres + Zakres All Events - Wszytkie Elementy + Wszystkie elementy Selected Events - Zaznaczone Elementy + Zaznaczone elementy Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami Value - Wartość + Wartość Halftone-steps - + Liczba półtonów OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj @@ -13248,48 +15518,51 @@ Dialog - + Okno dialogowe List of unused audio files in current project directory: - + Lista nieużywanych plików audio w katalogu projektu: Current project - + Bieżący projekt All .med files in current directory - + Wszystkie pliki +.med w +bieżącym +katalogu Move files to 'unused' subdir - + PrzenieÅ› pliki do podkatalogu „unused†Cancel - Anuluj + Anuluj VAMGui - + MusE: Load VAM Presets - + MusE: Å‚aduj ustawienia VAM MusE: Save VAM Presets - + MusE: zapisz ustawienia VAM @@ -13405,7 +15678,7 @@ Presets - + Ustawienia @@ -13441,7 +15714,7 @@ Pitch - Transpozycja + @@ -13477,7 +15750,7 @@ On - + VAM 1.0beta3 Virtual Analog for MusE @@ -13496,85 +15769,85 @@ MusE: Modify Velocity - MuzA: Modyfikuj predkość uderzenia (velocity) + MusE: modyfikuj prÄ™dkość uderzenia (velocity) Range - Zakres + Zakres All Events - Wszytkie Elementy + Wszystkie elementy Selected Events - Zaznaczone Elementy + Zaznaczone elementy Looped Events - PomiÄ™dzy lokatorami + PomiÄ™dzy lokatorami Values - Wartość + WartoÅ›ci Rate: - CzÄ™stotliwość: + CzÄ™stotliwość: Offset: - Offset: + Offset: % - % + % Selected Looped - + Zaznaczone pomiÄ™dzy lokatorami veloNew = (veloOld * rate) + offset - + dÅ‚Nowa = (dÅ‚Stara * czÄ™st.) + offset OK - Akceptuj + Akceptuj Cancel - Anuluj + Anuluj file_patterns - + Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) - + Midi (*.mid *.MID *.mid.gz *.mid.bz2) - + Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) - + @@ -13590,148 +15863,148 @@ All Files (*) - Wszystkie Pliki (*) + Wszystkie Pliki (*) Midi (*.mid) - + Karaoke (*.kar) - + all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) - + wszystkie znane pliki (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) - + Pliki med (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) - + Nieskompresowane pliki med (*.med) gzip compressed med Files (*.med.gz) - + Pliki med skompresowane gzip (*.med.gz) bzip2 compressed med Files (*.med.bz2) - + Pliki med skompresowane bzip2 (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) - + Pliki mid (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) - + (*.jpg) - + (*.gif) - + (*.png) - + part Files (*.mpt *.mpt.gz *.mpt.bz2) - + Wszystkie pliki klocków (*.mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) - + Pliki klocków (*.mpt) gzip compressed part Files (*.mpt.gz) - + Pliki klocków skompresowane gzip (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) - + Pliki klocków skompresowane bzip2 (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) - + Wszystkie ustawienia (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) - + Ustawienia (*.pre) gzip compressed presets (*.pre.gz) - + Ustawienia skompresowane gzip (*.pre.gz) bzip2 compressed presets (*.pre.bz2) - + Ustawienia skompresowane bzip2 (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) - + Wszystkie ustawienia (*.map *.map.gz *.map.bz2) Presets (*.map) - + Ustawienia (*.map) gzip compressed presets (*.map.gz) - + Ustawienia skompresowane gzip (*.map.gz) bzip2 compressed presets (*.map.bz2) - + Ustawienia skompresowane bzip2 (*.map.bz2) - Wave/Binary (*.wav *.ogg *.bin) - + Wave/Binary (*.wav *.ogg *.flac *.bin) + - Wave (*.wav *.ogg) - + Wave (*.wav *.ogg *.flac) + Binary (*.bin) - + @@ -14024,882 +16297,1015 @@ + midiWarnInitPendingBase + + + Instrument initialization + Inicjalizacja instrumentów + + + + MusE should now send some Instrument Initialization Sequences. +The sequences (usually System Exclusive messages) are defined + by the selected instruments in the Settings -> Midi Ports dialog, + such as the GM (default), GS, or XG instruments. + +Typically you should answer yes here. +You can always do it manually from the Midi menu. + +Continue? + MusE powinien w tej chwili wysÅ‚ać pewne sekwencje inicjalizacji instrumentów. +Sekwencje te (najczęściej komunikaty System Exclusive) przyporzÄ…dkowane sÄ… +na podstawie wybranego typu instrumentu w oknie: + Ustawienia MusE -> Porty MIDI i syntezatory programowe +Np. instrumenty GM (domyÅ›lne), GS lub XG. + +Zazwyczaj należy potwierdzić tÄ™ operacjÄ™. +W każdej chwili można wysÅ‚ać sekwencje rÄ™cznie korzystajÄ…c z menu MIDI. + +Kontynuować? + + + + Don't ask me again + Nie pytaj ponownie + + + shortcuts - + Transport: Start playback from current location - + Transport: odtwarzaj od bieżącej pozycji Transport: Toggle metronome - + Transport: wÅ‚Ä…cz/wyÅ‚Ä…cz metronom Transport: Stop Playback - + Transport: zatrzymaj odtwarzanie Transport: Goto Start - + Transport: wróć do poczÄ…tku Transport: Play, Stop, Rewind - + Transport: odtwarzaj, zatrzymaj, przewiÅ„ - + Transport: Goto left marker - + Transport: idź do lewego znacznika Transport: Goto right marker - + Transport: idź do prawego znacznika Transport: Toggle Loop section - + Transport: wÅ‚Ä…cz/wyÅ‚Ä…cz pÄ™tlÄ™ Transport: Toggle Record - + Transport: wÅ‚Ä…cz/wyÅ‚Ä…cz nagrywanie Transport: Clear all rec enabled tracks - + Transport: wyczyść tryb nagrywania dla wszystkich Å›ladów Toggle fullscreen - + WÅ‚Ä…cz/wyÅ‚Ä…cz tryb peÅ‚noekranowy - + Edit: Copy - + Edycja: kopiuj Edit: Copy in range - + Edycja: kopiuj zakres Edit: Undo - + Edycja: cofnij Edit: Redo - + Edycja: przywróć Edit: Cut - + Edycja: wytnij Edit: Paste - + Edycja: wklej + + + + Edit: Paste to selected track + Edycja: wklej do zaznaczonego Å›ladu + + + + Edit: Paste clone + Edycja: wklej klon + + + + Edit: Paste clone to selected track + Edycja: wklej klon do zaznaczonego Å›ladu Edit: Paste (with dialog) - + Edycja: wklej (pokaż okno dialogowe) Edit: Delete - + Edycja: kasuj File: New project - + Plik: nowy File: Open from disk - + Plik: otwórz File: Save project - + Plik: zapisz File: Open recent file - + Plik: otwórz ostatnie File: Save as - + Plik: zapisz jako File: Load template - + Plik: Å‚aduj szablon File: Import midi file - + Plik: import pliku MIDI File: Export midi file - + Plik: eksport pliku MIDI File: Import midi part - + Plik: import klocka File: Import audio file - + Plik: import pliku audio File: Quit MusE - + Plik: zakoÅ„cz Edit: Select parts on track - + Edycja: wybierz wszystkie klocki na Å›ladzie Open pianoroll - + Otwórz Pianoroll Open drumeditor - + Otwórz Edytor Perkusji Open listeditor - + Otwórz Edytor Lista Open waveeditor - + Otwórz Edytor Audio Open graphical mastertrack editor - + Otwórz Edytor Graficzny Tempo/Metrum Open list mastertrack editor - + Otwórz Edytor Lista Tempo/Metrum Open midi transformer - + Otwórz PrzeksztaÅ‚canie wejÅ›cia MIDI Add midi track - + Dodaj Å›lad MIDI Add drum track - + Dodaj Å›lad perkusyjny Add new style drum track - + Dodaj Å›lad perkusyjny nowego typu Add wave track - + Dodaj Å›lad audio Add audio output - + Dodaj wyjÅ›cie audio Add audio group - + Dodaj grupÄ™ audio Add audio input - + Dodaj wejÅ›cie audio Add audio aux - + Dodaj szynÄ™ (aux) audio Structure: Global cut - + Globalne: globalne wyciÄ™cie Structure: Global insert - + Globalne: globalne wstawienie Structure: Global split - + Globalne: globalne podzielenie Structure: Cut events - + Not implemented + Globalne: wytnij elementy View: Open mixer #1 window - + Widok: mikser A View: Open mixer #2 window - + Widok: mikser B View: Toggle transport window - + Widok: wÅ‚Ä…cz/wyÅ‚Ä…cz Panel Transportu View: Toggle bigtime window - + Widok: wÅ‚Ä…cz/wyÅ‚Ä…cz Duży Zegar View: Open marker window - + Widok: otwórz Edytor Znaczników Settings: Follow song by page - + Ustawienia: kursor siÄ™ przesuwa Settings: Follow song off - + Ustawienia: nie podążaj za utworem Settings: Follow song continuous - + Ustawienia: takty siÄ™ przesuwajÄ… (kursor stoi) Settings: Global configuration - + Ustawienia: ustawienia globalne - + Settings: Configure shortcuts - + Ustawienia: konfiguruj skróty klawiaturowe + + + + Transport: Restart recording + Transport: ponów nagrywanie + Transport: Restart recording (multi take) + Transport: ponów nagrywanie (tryb multi take) + + + Settings: Configure metronome - + Ustawienia: metronom Settings: Midi sync configuration - + Ustawienia: synchronizacja MIDI Settings: Midi file import/export configuration - + Ustawienia: konfiguracja importu/eksportu plików MIDI Settings: Appearance settings - + Ustawienia: ustawienia wyglÄ…du Settings: Midi ports / Soft Synth - + Ustawienia: porty MIDI i syntezatory programowe Settings: Audio subsystem configuration - + Not implemented? + Ustawienia: konfiguracja podsystemu audio Midi: Edit midi instruments - + MIDI: Edytor Instrumentów Midi: Open midi input transform - + MIDI: Otwórz przeksztaÅ‚canie wejÅ›cia MIDI Midi: Open midi input filter - + MIDI: otwórz filtr wejÅ›cia MIDI Midi: Midi input transpose - + MIDI: wtyczka wejÅ›cia MIDI: transponuj Midi: Midi remote control - + MIDI: wtyczka wejÅ›cia MIDI: zdalne sterowanie Midi: Random rhythm generator - + MIDI: generator losowego rytmu Midi: Reset midi - + MIDI: reset instr. Midi: Init midi - + MIDI: inicjalizacja instr. Midi: Midi local off - + MIDI: local off Audio: Bounce audio to track - + Audio: zgrywanie (bounce) do Å›ladu Audio: Bounce audio to file - + Audio: zgrywanie (bounce) do pliku Audio: Restart audio - + Audio: zrestartuj audio Automation: Mixer automation - + Automatyka: automatyka miksera Automation: Take mixer snapshot - + Automatyka: zrzut ustawieÅ„ miksera Automation: Clear mixer automation - + Automatyka: wyczyść automatykÄ™ Help: Open Manual - + Pomoc: instrukcja obsÅ‚ugi Help: Toggle whatsthis mode - + Pomoc: wÅ‚Ä…cz/wyÅ‚Ä…cz tryb „Co to jest?†Edit: Edit selected part - + Edycja: edytuj zaznaczony klocek Edit: Select nearest part on track above - + Edycja: zaznacz najbliższy klocek na górnym Å›ladzie Edit: Add nearest part on track above - + Edycja: dodaj do zaznaczenia najbliższy klocek na górnym Å›ladzie Edit: Select nearest part on track below - + Edycja: zaznacz najbliższy klocek na dolnym Å›ladzie Edit: Add nearest part on track below - + Edycja: dodaj do zaznaczenia najbliższy klocek na dolnym Å›ladzie Edit: Insert empty measure - - - - - Edit: Paste as clones - + Edycja: wstaw pusty takt - Edit: Paste as clones (with dialog) - + Edit: Duplicate track + Edycja: kopiuj Å›lad Select track above - + Zaznacz górny Å›lad Select track below - + Zaznacz dolny Å›lad - + + Edit selected track name + Edytuj nazwÄ™ wybranego Å›ladu + + + Midi: Transpose - + MIDI: transponuj Edit: Select all - + Edycja: zaznacz wszystko Edit: Select none - + Edycja: odznacz wszystko Edit: Invert Selection - + Edycja: odwróć zaznaczenie Edit: Select events/parts inside locators - + Edycja: zaznacz elementy/klocki pomiÄ™dzy lokatorami Edit: Select events/parts outside locators - + Edycja: zaznacz elementy/klocki poza lokatorami Edit: Select previous part - + Edycja: zaznacz poprzedni klocek Edit: Select next part - + Edycja: zaznacz nastÄ™pny klocek Edit: Select nearest part/event to the left or move cursor - + Edycja: zaznacz najbliższy element/klocek na lewo bÄ…dź przesuÅ„ kursor Edit: Add nearest part/event to the left to selection - + Edycja: dodaj do zaznaczenia najbliższy element/klocek na lewo Edit: Select nearest part/event to the right or move cursor - + Edycja: zaznacz najbliższy element/klocek na prawo bÄ…dź przesuÅ„ kursor Edit: Add nearest part/event to the right to selection - + Edycja: dodaj do zaznaczenia najbliższy element/klocek na prawo Edit: Set locators to selection - + Edycja: ustaw lokatory w miejscu zaznaczenia Edit: Increase pitch - + Edycja:zwiÄ™ksz wysokość dźwiÄ™ku Edit: Decrease pitch - + Edycja:zmniejsz wysokość dźwiÄ™ku Edit: Increase event position - + Edycja: przesuÅ„ element w prawo Edit: Decrease event position - + Edycja: przesuÅ„ element w lewo View: Zoom in - + Widok: powiÄ™ksz View: Zoom out - + Widok: pomniejsz View: Goto Current Position - + Widok: idź do bieżącej pozycji View: Scroll left - + Widok: przesuÅ„ w lewo + + + + Transport: Step record + Transport: nagrywanie krokowe + + + + Transport: Midi input + Transport: wejÅ›cie MIDI + + + + Transport: Play events + Transport: odgrywaj nuty + + + + Edit: Increase velocity + Edycja: zwiÄ™ksz prÄ™dkość uderzenia (velocity) + + + + Edit: Decrease velocity + Edycja: zmniejsz prÄ™dkość uderzenia (velocity) Edit: Set Fixed Length on Midi Events - + Edycja: ustaw staÅ‚Ä… dÅ‚ugość nut Quantize - Kwantyzuj + Kwantyzuj Modify Note Length - + Modyfikuj dÅ‚ugość nut Modify Velocity - + Modyfikuj prÄ™dkość uderzenia (velocity) Edit: Crescendo - + Edycja: crescendo/decrescendo Edit: Thin Out - + Not impemented? + Edycja: thin out Edit: Erase Event - + Edycja: kasuj zdarzenie Edit: Delete Overlaps - + Edycja: kasuj elementy nakÅ‚adajÄ…ce siÄ™ Edit: Note Shift - + Edycja: przesuÅ„ nuty Edit: Move Clock - + Edycja: przesuÅ„ zegar Edit: Copy Measure - + Edycja: kopiuj takt Edit: Erase Measure - + Not implemented, ambiguous + Edycja: wyczyść takt Edit: Delete Measure - + Not implemented, ambiguous + Edycja: kasuj takt Edit: Create Measure - + Edycja: usuÅ„ takt Edit: Change Event Color - + Edycja: zmieÅ„ kolor elementu + + + + Move: Move to selected note + Przesuwanie: przesuÅ„ kursor do wybranej nuty Tool: Pointer - + NarzÄ™dzie: strzaÅ‚ka Tool: Pencil - + NarzÄ™dzie: ołówek Tool: Eraser - + NarzÄ™dzie: gumka + + + + Tool: Pan + NarzÄ™dzie: przesuwanie + + + + Tool: Zoom + NarzÄ™dzie: powiÄ™kszanie Tool: Line Draw - + NarzÄ™dzie: wykres Tool: Cursor - + NarzÄ™dzie: kursor Add note velocity 1 - + Dodaj nutÄ™ z poziomem velocity 1 Add note velocity 2 - + Dodaj nutÄ™ z poziomem velocity 2 Add note velocity 3 - + Dodaj nutÄ™ z poziomem velocity 3 Add note velocity 4 - + Dodaj nutÄ™ z poziomem velocity 4 Cursor step size: larger - + Rozmiar kroku kursora: wiÄ™kszy Cursor step size: smaller - + Rozmiar kroku kursora: mniejszy Instrument/Cursor up - + Instrument/kursor do góry Instrument/Cursor down - + Instrument/kursor do doÅ‚u + + + + Tool: Range + NarzÄ™dzie: zakres Tool: Scissor - + NarzÄ™dzie: nożyczki Tool: Glue - + NarzÄ™dzie: klej Tool: Mute - + NarzÄ™dzie: wycisz Transport: Increase current position - + Transport: przesuÅ„ kursor w prawo Transport: Decrease current position - + Transport: przesuÅ„ kursor w lewo Transport: Increase current position, no snap - + Transport: przesuÅ„ kursor w prawo (bez przyciÄ…gania) Transport: Decrease current position, no snap - + Transport: przesuÅ„ kursor w lewo (bez przyciÄ…gania) Quantize: Set quantize to 1/1 note - + Kwantyzacja: ustaw kwantyzacjÄ™ do 1/1 caÅ‚ej nuty Quantize: Set quantize to 1/2 note - + Kwantyzacja: ustaw kwantyzacjÄ™ do 1/2 caÅ‚ej nuty Quantize: Set quantize to 1/4 note - + Kwantyzacja: ustaw kwantyzacjÄ™ do 1/4 caÅ‚ej nuty Quantize: Set quantize to 1/8 note - + Kwantyzacja: ustaw kwantyzacjÄ™ do 1/8 caÅ‚ej nuty Quantize: Set quantize to 1/16 note - + Kwantyzacja: ustaw kwantyzacjÄ™ do 1/16 caÅ‚ej nuty Quantize: Set quantize to 1/32 note - + Kwantyzacja: ustaw kwantyzacjÄ™ do 1/32 caÅ‚ej nuty Quantize: Set quantize to 1/64 note - + Kwantyzacja: ustaw kwantyzacjÄ™ do 1/64 caÅ‚ej nuty Quantize: Toggle triol quantization - + Kwantyzacja: ustaw kwantyzacjÄ™ do trioli Quantize: Toggle punctuation quantization - + Kwantyzacja: ustaw kwantyzacjÄ™ do kropek Quantize: Toggle punctuation quantization (2) - + Kwantyzacja: ustaw kwantyzacjÄ™ do kropek (2) Edit: Insert at location - + Edycja: wstaw odstÄ™p w pozycji kursora Edit: Increase length - + Edycja:zwiÄ™ksz dÅ‚ugość elementu Edit: Decrease length - + Edycja:zmniejsz dÅ‚ugość elementu Insert Note - Wstaw nutÄ™ + Wstaw nutÄ™ Insert SysEx - wstaw SysEx + Wstaw SysEx Insert Ctrl - Wstaw kontroler + Wstaw kontroler Insert Meta - Wstaw Meta + Wstaw meta Insert Channel Aftertouch - Wstaw zmianÄ™ Å›redniego nacisku pod uderzeniu (channel aftertouch) + Wstaw zmianÄ™ docisku kanaÅ‚owego (Channel Aftertouch) Insert Key Aftertouch - Wstaw pojedynczÄ… zmiane nacisku pod uderzeniu + Wstaw zmianÄ™ docisku klawisza (Key Aftertouch) Insert Tempo - + Wstaw tempo Insert Signature - + Wstaw metrum Change Event Position - + ZmieÅ„ pozycjÄ™ elementu Edit Event Value - + Edytuj wartość elementu Insert Key - + Wstaw tonacjÄ™ - + Goto Next Marker - + Idź do nastÄ™pnego znacznika Goto Prev Marker - + Idź do poprzedniego znacznika + + + + Normalize + Normalizacja + + + + warnBadTimingBase + + + Bad timing + ZÅ‚y timing + + + + Message here + + + + + Don't warn me again + Nie ostrzegaj ponownie diff -Nru muse-2.1.2/share/locale/muse_ru.ts muse-3.0.2+ds1/share/locale/muse_ru.ts --- muse-2.1.2/share/locale/muse_ru.ts 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/muse_ru.ts 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - + @default @@ -13,7 +13,7 @@ Add Wave Track - Добавить Wave-трек + Добавить Ðудио-трек Add Audio Output @@ -73,7 +73,7 @@ Bad timing - + Плохой тайминг Timing source frequency is %1hz, which is below the recommended minimum: 500hz! @@ -81,15 +81,22 @@ Please see the FAQ on http://muse-sequencer.org for remedies. Also please check console output for any further error messages. - + ЧаÑтота равна %1гц, что ниже рекомендованного минимума:500гц! +Ñто может привеÑти к Ñлышимым проблемам при воÑпроизведении MIDI. +Смотрите FAQ на http://muse-sequencer.org Ð´Ð»Ñ Ð¸ÑправлениÑ. +Также проверьте вывод конÑоли Ð´Ð»Ñ Ð²Ñ‹ÑÐ²Ð»ÐµÐ½Ð¸Ñ Ñообщений об ошибках. Add Old Style Drum Track - + Добавить трек ударных в Ñтаром Ñтиле Add New Style Drum Track - + Добавить трек ударных в новом Ñтиле + + + channel="%1" + канал="%1" @@ -124,7 +131,7 @@ Version 2 - + ВерÑиÑ-2 (C) Copyright 1999-2012 Werner Schweer and others. @@ -132,12 +139,48 @@ more information. Published under the GNU Public License - (C) Copyright 1999-2010 Werner Schweer и другие. + (C) Copyright 1999-2010 Werner Schweer и другие. Ðа http://www.muse-sequencer.org Ñмотрите новые верÑии и дополнительную информацию. Опубликовано на уÑловиÑÑ… GNU Public License {1999-2012 ?} + + (C) Copyright 1999-2014 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2010 Werner Schweer и другие. +Ðа http://www.muse-sequencer.org Ñмотрите новые верÑии и +дополнительную информацию. + +Опубликовано на уÑловиÑÑ… GNU Public License {1999-2012 ?} {1999-2014 ?} + + + Version info (replaced programmatically) + + + + (C) Copyright 1999-2015 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2010 Werner Schweer и другие. +Ðа http://www.muse-sequencer.org Ñмотрите новые верÑии и +дополнительную информацию. + +Опубликовано на уÑловиÑÑ… GNU Public License {1999-2012 ?} {1999-2014 ?} {1999-2015 ?} + + + System information: + + + + TextLabel + + AppearanceDialogBase @@ -243,7 +286,7 @@ QT Theme - Тема QT + Тема QT Windows @@ -379,109 +422,125 @@ May require restarting MusE for best results - + Может потребоватьÑÑ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿ÑƒÑк MusE + + + Themes + Темы + + + MusE color scheme + Ð¦Ð²ÐµÑ‚Ð¾Ð²Ð°Ñ Ñхема MusE + + + current settings + текущие наÑтройки + + + Change + Изменить ArrangerColumnsBase Configure arranger columns - + ÐаÑтроить колонки компоновщика Columns: - + Колонки: Name: - ИмÑ: + ИмÑ: Controller type: - + Тип контроллера: Midi controller type - Тип midi-контроллера + Тип midi-контроллера Control7 - Контрл7 + Контрл7 Control14 - Контрл14 + Контрл14 RPN - RPN + RPN NRPN - NRPN + NRPN RPN14 - RPN14 + RPN14 NRPN14 - NRPN14 + NRPN14 Pitch - Ð’Ñ‹Ñота тона + Ð’Ñ‹Ñота тона Program - Программа + Программа H-Ctrl - H-Ктрл + H-Ктрл Midi controller number high byte - Старший байт midi-контроллера + Старший байт midi-контроллера L-Ctrl - L-Ктрл + L-Ктрл Midi controller number low byte - + Младший байт Midi контроллера * wild card - * + * универÑальный шаблон affect CCs at - + влиÑет на контроллеры begin of song - + начало Ñонга current position - + Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð¿Ð¾Ð·Ð¸Ñ†Ð¸Ñ &Add - &Добавить + &Добавить &Delete - + &Удалить Done - + Готово @@ -503,6 +562,33 @@ + ChooseSysexBase + + Dialog + Диалог + + + Hex: + + + + TextLabel + + + + Comment: + Комментарий: + + + &OK + + + + &Cancel + + + + ClipListEditorBase MusE: ClipList @@ -538,7 +624,7 @@ Samplerate - + ЧаÑтота диÑкретизации @@ -608,7 +694,7 @@ Split tracks into &parts - Разделить треки на &партии + РазделÑÑ‚ÑŒ треки на &партии Alt+P @@ -648,10 +734,70 @@ Use new-style drum tracks - + ИÑпользовать треки ударных в новом Ñтиле Use old-style drum tracks + ИÑпользовать треки ударных в Ñтаром Ñтиле + + + Default instrument: + + + + Device Name metas trump Port metas if both exist + + + + Instrument Name metas trump Mode sysexes if both exist + + + + Mode sysexes + + + + Instrument name metas + + + + Both + + + + Port metas + + + + Device name metas + + + + Export a Port/Device meta for format 0 + + + + + CopyOnWriteDialogBase + + Copy Wave Files + + + + Some sound files will be copied to the Project Directory, +either because they are not writable or because more +than one independent Wave Event shares them. +(If you prefer instead that the Wave Events are + inter-dependent, try using Clone Parts.) + +Multiple copies will be made in some cases. + +If no Project has been created yet, you will be asked to, +giving another chance to cancel. + + + + These files will be copied to the Project Directory: @@ -935,191 +1081,191 @@ DeicsOnzeGuiBase DeicsOnze - DeicsOnze + DeicsOnze Subcategory - ÐŸÐ¾Ð´ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ + ÐŸÐ¾Ð´ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ NONE - ÐИЧЕГО + ÐИЧЕГО Category - ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ + ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Preset - ПреÑет + ПреÑет Bank numerous - ÐеÑколько банков + ÐеÑколько банков Prog - Прогр + Прогр Program numerous - ÐеÑколько программ + ÐеÑколько программ &Global - &Глобальный + &Общий Coarse Ratio - Ð“Ñ€ÑƒÐ±Ð°Ñ Ð¿Ñ€Ð¾Ð¿Ð¾Ñ€Ñ†Ð¸Ñ + Ð“Ñ€ÑƒÐ±Ð°Ñ Ð¿Ñ€Ð¾Ð¿Ð¾Ñ€Ñ†Ð¸Ñ POLY - ПОЛИ + ПОЛИ MONO - МОÐО + МОÐО Pitch Bend Range - Диапозон Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²Ñ‹Ñоты тона + Диапозон Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²Ñ‹Ñоты тона LFO - LFO + LFO PMS - PMS + PMS PMD - PMD + PMD AMD - AMD + AMD Speed - СкороÑÑ‚ÑŒ + СкороÑÑ‚ÑŒ Delay - Задержка + Задержка AMS - AMS + AMS LFO Waveform - Форма волны LFO + Форма волны LFO LFO Sync - Синхр. LFO + Синхр. LFO Amplitude Modulation Sensitivity - ЧувÑтвительноÑÑ‚ÑŒ Ðмплитудной МодулÑции + ЧувÑтвительноÑÑ‚ÑŒ Ðмплитудной МодулÑции Pitch Modulation Sensitivity - ЧувÑтвительноÑÑ‚ÑŒ ЧаÑтотной МодулÑции + ЧувÑтвительноÑÑ‚ÑŒ ЧаÑтотной МодулÑции Pitch Modulation Depth - Глубина ЧаÑтотной МодулÑции + Глубина ЧаÑтотной МодулÑции LFO Speed - СкороÑÑ‚ÑŒ LFO + СкороÑÑ‚ÑŒ LFO LFO Delay - Задержка LFO + Задержка LFO Transpose - ТранÑпонировать + ТранÑпонировать Modulation Matrix - Матрица модулÑции + Матрица модулÑции Volume - ГромкоÑÑ‚ÑŒ + ГромкоÑÑ‚ÑŒ Detune - РаÑÑтройка + РаÑÑтройка &Preset - + &ПреÑет INITVOICE - + INITVOICE LBank - + LБанк HBank - + HБанк DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. - + DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Опубликован под лицензией GPL. Pitch Envelope - + Развертка выÑоты тона PL3 - + PL3 PL2 - + PL2 PL1 - + PR1 - + PR1 PR2 - + PR3 - + PR3 Pitch modulation depth - + Глубина модулÑции Pitch modulation sensitivity - + ЧувÑтвительноÑÑ‚ÑŒ модулÑции LFO delay - + Задержка LFO LFO speed - + СкороÑÑ‚ÑŒ LFO Amplitude modulation depth @@ -1200,15 +1346,15 @@ Release Rate - + 2° Decay Rate - + 1° Decay Level - + 1° Decay Rate @@ -1954,50 +2100,54 @@ DuplicateTracksBase Duplicate tracks - + Дублировать треки Number of copies - + КоличеÑтво копий Copy all routes - + Скопировать вÑе маршруты Default routing - + Маршрут по умолчанию No routes - + Ðет маршрутов Copy parts - + Скопировать партии Copy standard controllers (vol, pan) - + Копировать Ñтандартные контроллеры (громк, пан) Copy effects rack plugins - + Копировать плагины Ñ€Ñка Ñффектов Copy plugin controllers - + Копировать контроллеры плагинов Ok - + ОК Cancel - Отмена + Отмена Copy drumlist + Копировать ÑпиÑок ударных + + + Copy standard (vol, pan) and synth controllers @@ -2059,6 +2209,10 @@ &Cancel О&тмена + + Note + Ðота + EditGainBase @@ -2153,7 +2307,7 @@ This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. - Это ÑпиÑок общеупотребительных midi-контроллеров. + Это ÑпиÑок общеупотребительных midi-контроллеров. Обратите внимание, что изменение программ и звуковыÑотноÑти в MusE, такое же, как и управление обычными контроллерами. @@ -2164,27 +2318,27 @@ Control7 - Контрл7 + Контрл7 Control14 - Контрл14 + Контрл14 RPN - RPN + RPN NRPN - NRPN + NRPN Pitch - Ð’Ñ‹Ñота тона + Ð’Ñ‹Ñота тона Program - Программа + Программа H-Ctrl @@ -2268,11 +2422,11 @@ Exit - Выйти + Выйти E&xit - Ð’&ыйти + Ð’&ыйти Instrument Name: @@ -2339,11 +2493,11 @@ Drum patch - Патч ударных + Патч ударных If set, the patch is available only for drum channels. - ЕÑли уÑтановлено, то Ñтот патч может иÑпользоватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в каналах ударных. + ЕÑли уÑтановлено, то Ñтот патч может иÑпользоватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ в каналах ударных. GM patch @@ -2399,11 +2553,11 @@ Contro&ller - Контро&ллер + Контро&ллер Common: - Обычный: + Обычный: List of defined controllers @@ -2439,11 +2593,11 @@ RPN14 - RPN14 + RPN14 NRPN14 - NRPN14 + NRPN14 Midi controller number high byte @@ -2623,7 +2777,7 @@ Null Param Hi: - Ðулевой Парам Ст: + Ðулевой Парам Ст: Null parameter number High byte @@ -2680,48 +2834,104 @@ Drummaps - + Карты ударных Patch Collections: - + Коллекции патчей: &Copy - С&копировать + С&копировать &Remove - + &Удалить &Up - + &Вверх &Down - + &Вниз Patch: - + Патч: from - + из to - + в Bank Hi: - + Банк Hi: Bank Lo: + Банк Lo: + + + Show in tracks: + + + + Midi + Midi + + + Drum&maps + + + + Contro&llers + + + + Null Parameters: Hi: + + + + &Add Common... + + + + W + W + + + Comment: + Комментарий: + + + &Initialization + + + + Instrument initialization sequence: + + + + &Add... + + + + &Change... + + + + &Close + + Close + Закрыть + EditNoteDialogBase @@ -2774,11 +2984,27 @@ OK - ОК + ОК Cancel - Отмена + Отмена + + + Name: + ИмÑ: + + + &Select... + + + + &OK + + + + &Cancel + @@ -2807,99 +3033,99 @@ FLUIDSynthGuiBase FLUID Synth - Синтезатор FLUID + Синтезатор FLUID Gain - УÑиление + УÑиление Level - Уровень + Уровень Width - Ширина + Ширина Damping - Демпфирование + Демпфирование Room Size - Размер Ð¿Ð¾Ð¼ÐµÑ‰ÐµÐ½Ð¸Ñ + Размер Ð¿Ð¾Ð¼ÐµÑ‰ÐµÐ½Ð¸Ñ Reverb - Реверб + Реверб Delete - Удалить + Удалить Load - Загрузить + Загрузить Sine - СинуÑоида + СинуÑоида Triangle - Ð¢Ñ€ÐµÑƒÐ³Ð¾Ð»ÑŒÐ½Ð°Ñ + Ð¢Ñ€ÐµÑƒÐ³Ð¾Ð»ÑŒÐ½Ð°Ñ Type - Тип + Тип Number - ЧиÑло + ЧиÑло Speed - СкороÑÑ‚ÑŒ + СкороÑÑ‚ÑŒ Depth - Глубина + Глубина Chorus - Ð¥Ð¾Ñ€ÑƒÑ + Ð¥Ð¾Ñ€ÑƒÑ CHANNEL SETUP - ÐÐСТРОЙКРКÐÐÐЛР+ ÐÐСТРОЙКРКÐÐÐЛРID - ID + ID Fontname - Ðазв. SF-банка + Ðазв. SF-банка Chnl - Канал + Канал Soundfont - Банк + Банк LOADED SOUNDFONTS - ЗÐГРУЖЕÐÐЫЕ БÐÐКИ + ЗÐГРУЖЕÐÐЫЕ БÐÐКИ Dump Info - Дамп Инфо + Дамп Инфо Drum Chnl - Канал ударных + Канал ударных @@ -2930,6 +3156,10 @@ write window states + запоминать ÑоÑтоÑние окон + + + Home @@ -2937,7 +3167,7 @@ FluidSynthGui Choose soundfont - Выберите soundfont + Выберите soundfont @@ -3007,7 +3237,7 @@ Some popup menus stay open (else hold Ctrl) - Ðекоторые вÑплыв. меню оÑтанутÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼Ð¸ (или удерживайте Ctrl) + ИÑпользовать клавишу Ctrl Ð´Ð»Ñ ÑƒÐ´ÐµÑ€Ð¶Ð°Ð½Ð¸Ñ Ð²Ñплывающих меню Allows some popup menus to stay open. @@ -3027,7 +3257,7 @@ Use left mouse button for decreasing values - ИÑпользуйте левую кнопку мыши Ð´Ð»Ñ ÑƒÐ¼ÐµÐ½ÑŒÑˆÐµÐ½Ð¸Ñ + ИÑпользовать левую кнопку мыши Ð´Ð»Ñ ÑƒÐ¼ÐµÐ½ÑŒÑˆÐµÐ½Ð¸Ñ Shift + Right click sets left range marker @@ -3035,7 +3265,7 @@ Allow adding hidden tracks in track list menu - Разрешает добавление Ñкрытых треков в меню ÑпиÑка треков + Разрешить добавление Ñкрытых треков в меню ÑпиÑка треков Unhide tracks when adding hidden tracks @@ -3099,7 +3329,7 @@ Ticks - Тики + Тики 1024 @@ -3179,15 +3409,15 @@ start with last song - запуÑтить Ñ Ð¿Ð¾Ñледним проектом + Ñ Ð¿Ð¾Ñледним проектом start with song - запуÑтить Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð¾Ð¼ + Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð¾Ð¼ Views - Виды + ÐаÑтройки окон y-pos @@ -3303,7 +3533,7 @@ Try to use Jack Freewheel - Попробуйте иÑпользавать режим "Jack Freewheel" + ИÑпользовать режим "Jack Freewheel" Speeds bounce operations @@ -3336,7 +3566,7 @@ Minimum control period - Минимальный контрольный период + Минимальный размер буфера Minimum audio controller process period (samples). @@ -3393,7 +3623,7 @@ Sample rate - + ЧаÑтота диÑкретизации Shorter periods give better midi playback resolution. @@ -3439,76 +3669,185 @@ Choose start song or template - + Ðачать Ñ Ñонга или шаблона Reset to default - + СброÑить к иÑходным start with template - + Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¾Ð¼ Start template or song: - + запуÑтить шаблон или Ñонг: Read MIDI Ports configuration from file, or else automatically configure - + Читать конфигурацию midi портов из файла, +или наÑтроить автоматичеÑки Read MIDI Ports configuration - + Читать конфигурацию midi-портов Smart focus - + Умное фокуÑирование After editing, controls will return focus to their respective canvas - + ПоÑле Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ€ÐµÐ³ÑƒÐ»Ñторы +вернутÑÑ Ð² ÑоответÑтвующие позиции Record new style drum tracks - + ЗапиÑÑŒ треков ударных в новом Ñтиле Record all instruments - + ЗапиÑывать вÑе инÑтрументы Don't record hidden instruments - + Ðе запиÑывать Ñкрытые инÑтрументы Don't record muted instruments - + Ðе запиÑывать инÑтрументы "без звука" Don't record hidden or muted instruments - + Ðе запиÑывать инÑтрументы "без звука" и Ñкрытые Drum tracks - + Треки ударных Only offer old-style drumtracks - + Предлагать только треки ударных в Ñтаром Ñтиле Only offer new-style drumtracks - + Предлагать только треки ударных в новом Ñтиле Prefer old-style drumtracks - + Предпочитать Ñтарые Prefer new-style drumtracks + Предпочитать новые + + + Auto save (every 5 minutes if not playing/recording) + + + + Instrument initialization + + + + Send instrument initialization sequences + + + + Warn if instrument initialization sequences pending + + + + Send instrument controller default values if none in song, at init or rewind + + + + Warn if timer frequency is inadequate + + + + Track height + + + + Show newly created midi velocity graphs per-note + + + + px + + + + Enable borderless mouse. +For certain functions like zoom/pan. +Disable to use an alternate standard + method. + + + + + Enable borderless mouse. +For certain functions like zoom. +Disable to use an alternate standard + method. + + + + Borderless zoom/pan mouse (else use alternate method) + + + + start &with last song + + + + start with &template + + + + sta&rt with song + + + + &Record all instruments + + + + &Don't record hidden instruments + + + + Don'&t record muted instruments + + + + Don't record &hidden or muted instruments + + + + Scrollable submenus + Прокручиваемые подменю + + + &Only offer old-style drumtracks + + + + Only offer new-style &drumtracks + + + + &Prefer old-style drumtracks + + + + Prefer &new-style drumtracks + + + + Warn if opening file versions different than current version @@ -3660,11 +3999,11 @@ Audio Beep - Ðудио щелчок + Ðудио клик MIDI Click - MIDI щелчок + MIDI клик Midi Channel @@ -3744,7 +4083,7 @@ % Audio volume - % ГромкоÑÑ‚ÑŒ аудио + % ГромкоÑÑ‚ÑŒ аудио Hint: Enable metronome in Transportpanel @@ -3762,67 +4101,111 @@ Alt+C Alt+C + + Two samples (old samples) + + + + Four samples (new samples, with accents) + + + + Volume + ГромкоÑÑ‚ÑŒ + + + Audio master + + + + Meas + + + + Beat + + + + Accent1 + + + + Accent2 + + + + Sample + + + + Disabled since jack does not support it + + MidiAudioControlBase Midi control - + Midi контроль Port: - + Channel: - + Канал: Control type: - + Тип контролÑ: Control7 - Контрл7 + Контрл7 Control14 - Контрл14 + Контрл14 RPN - RPN + RPN NRPN - NRPN + NRPN RPN14 - RPN14 + RPN14 NRPN14 - NRPN14 + NRPN14 Pitch - Ð’Ñ‹Ñота тона + Ð’Ñ‹Ñота тона Program - Программа + Программа Hi: - + Hi: Lo: - + Lo: Learn + Узнать + + + &Learn @@ -4155,6 +4538,14 @@ PresetList СпиÑок преÑетов + + Program + Программа + + + Toggle + + MidiSyncConfigBase @@ -4320,11 +4711,11 @@ Note: Sync delay and MTC sync currently not fully implemented - + Примечание: Задержка Ñинхр. и MTC применена не полноÑтью Averaging applied to recorded external tempo changes. - + Ð”Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи изменений внешнего темпа применено уÑреднение. External midi clock can be very jittery. @@ -4353,19 +4744,42 @@ audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. - + Внешнее midi Ð²Ñ€ÐµÐ¼Ñ Ð¼Ð¾Ð¶ÐµÑ‚ быть очень неровным. +Темп беретÑÑ Ð¸ запиÑываетÑÑ Ð¸Ð· него. +Обычно нужно уÑреднÑÑ‚ÑŒ его и +ограничивать количеÑтво запиÑываемых изменений. + +Очень маленькое: 2 Ñекции 4/4 = 8 ÑтупенÑм. +УÑреднение до1/8T ноты, могут быть Ñкачки. +Маленькое: 3 Ñекции 12/8/4 = 24 ÑтупенÑм. +УÑреднение до1/4 ноты, но вÑе равно могут быть Ñкачки. + +Среднее: 3 Ñекции 28/12/8 = 48 ÑтупенÑм. +УÑреднение до1/2 ноты, меньше Ñкачков. + +Большое: 4 Ñекции 48/48/48/48 = 192 ÑтупенÑм. +ИÑпользуйте его, еÑли в Ñонге только один темп. +Может быть иÑпользовано очень низкое значение квантованиÑ. + +Большое Ñ Ð¿Ñ€ÐµÐ´-обнаружением: 4 Ñекции 8/48/48/48 = 152 + ÑтупенÑм. + большой шаг первой Ñтупени - пред-обнаружитель. +ИÑпользуйте еÑли ожидаете большие резкие Ñкачки темпа. + +Ðет: ИÑпользуйте , еÑли нужна Ð±Ð¾Ð»ÑŒÑˆÐ°Ñ Ñ‚Ð¾Ñ‡Ð½Ð¾ÑÑ‚ÑŒ воÑпроизведениÑ. +Предупреждение: ЗапиÑываютÑÑ Ñ‚Ñ‹ÑÑчи изменений темпа в минуту. +Это может замедлить работу MusE и увеличить размер файла. Tempo record averaging - + УÑреднение запиÑи темпа bpm - + bpm Tempo record quantization - + Квантование запиÑи темпа @@ -4507,22 +4921,46 @@ Bank Select MSB. Ctrl-double-click on/off. - + Выбор банка MSB. Ctrl-двойной щелчок вкл/выкл. Bank Select LSB. Ctrl-double-click on/off. - + Выбор банка LSB. Ctrl-двойной щелчок вкл/выкл. Program. Ctrl-double-click on/off. - + Программа. Ctrl-двойной щелчок вкл/выкл. Volume. Ctrl-double-click on/off. - + ГромкоÑÑ‚ÑŒ. Ctrl-двойной щелчок вкл/выкл. Change stereo position. Ctrl-double-click on/off. + Изменение Ñтерео позиции. Ctrl-двойной щелчок вкл/выкл. + + + Change note length in percent of actual length + + + + Offset playback of notes before or after actual note + + + + Transpose notes up or down + + + + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + + + + Compress the notes velocity range, in percent of actual velocity + + + + Select instrument @@ -4856,6 +5294,21 @@ + MusECore::AudioPreviewDialog + + Auto play + + + + Stop + ОÑтановить + + + Play + ВоÑпроизвеÑти + + + MusECore::Song Jack shutdown! @@ -4950,25 +5403,46 @@ Midi control - + Midi контроль Assign - + Ðазначить Clear - + ОчиÑтить MusE: Tempo list - + MusE: СпиÑок темпа External tempo changes were recorded. Transfer them to master tempo list? + Были запиÑаны Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ‚ÐµÐ¼Ð¿Ð°. +ПеренеÑти их в ÑпиÑок маÑтер-трека? + + + Do you want to process ALL or only selected events? + + &Selected + + + + &All + + + + Und&o + &Отменить + + + Re&do + &Повторить + MusEGui @@ -5156,8 +5630,20 @@ Keep Qt system style + Удерживать ÑиÑтемный Ñтиль Qt + + + Do you really want to reset colors to theme default? + + &Ok + &ОК + + + &Cancel + Отмена + MusEGui::Arranger @@ -5291,27 +5777,27 @@ Type - Тип + Тип NO - NO + NO GM - GM + GM GS - GS + GS XG - XG + XG midi song type - тип midi-проекта + тип midi-проекта Pitch @@ -5383,39 +5869,78 @@ - MusEGui::ArrangerView + MusEGui::ArrangerColumns - MusE: Arranger - MusE: Компоновщик + Control7 + Контрл7 - C&ut - &Вырезать + Control14 + Контрл14 - &Copy - С&копировать + RPN + RPN - Copy in range - Копировать диапазон + NPRN + - &Paste - &Ð’Ñтавить + RPN14 + RPN14 - Paste (show dialog) - Ð’Ñтавить (показать диалог) + NRPN14 + NRPN14 - Paste c&lone - Ð’Ñтавить к&лон + Pitch + Ð’Ñ‹Ñота тона - Paste clone (show dialog) - Ð’Ñтавить клон (показать диалог) - + Program + Программа + + + Aftertouch + ПоÑленажатие + + + + MusEGui::ArrangerView + + MusE: Arranger + MusE: Компоновщик + + + C&ut + &Вырезать + + + &Copy + С&копировать + + + Copy in range + Копировать диапазон + + + &Paste + &Ð’Ñтавить + + + Paste (show dialog) + Ð’Ñтавить (показать диалог) + + + Paste c&lone + Ð’Ñтавить к&лон + + + Paste clone (show dialog) + Ð’Ñтавить клон (показать диалог) + &Insert Empty Measure &Ð’Ñтавить пуÑтой такт @@ -5494,15 +6019,15 @@ Drums - Ударные + Редактор ударных List - СпиÑок + Редактор ÑпиÑка Wave - Wave + Редактор Ðудио Mastertrack @@ -5510,7 +6035,7 @@ Graphic - График + ГрафичеÑкий редактор Midi &Transform @@ -5530,15 +6055,15 @@ Global Cut - selected tracks - ÐžÐ±Ñ‰Ð°Ñ Ð²Ñ‹Ñ€ÐµÐ·ÐºÐ° - выбр. треки + ÐžÐ±Ñ‰Ð°Ñ Ð²Ñ‹Ñ€ÐµÐ·ÐºÐ° - выбранные треки Global Insert - selected tracks - ÐžÐ±Ñ‰Ð°Ñ Ð²Ñтавка - выбр. треки + ÐžÐ±Ñ‰Ð°Ñ Ð²Ñтавка - выбранные треки Global Split - selected tracks - Общее разделение - выбр. треки + Общее разделение - выбранные треки &Edit @@ -5582,7 +6107,7 @@ Set Fixed Note Length - УÑтановить фикÑ. длину ноты + УÑтановить длину ноты Delete Overlapping Notes @@ -5602,29 +6127,48 @@ Configure &custom columns - + ÐаÑтроить &колонки Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Changed Settings - + Измененные наÑтройки Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. (we'll try to fix that) - + К Ñожалению изменение колонок не может быть +применено во Ð²Ñ€ÐµÐ¼Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ MusE. +Чтобы применить изменениÑ, перезапуÑтите MusE. +(поÑтараемÑÑ Ñто иÑправить) Purge hidden events from selected parts - + Удалить Ñкрытые ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¸Ð· выбранных партий Remove track(s) - + Удалить трек(и) Are you sure you want to remove this track(s)? + Ð’Ñ‹ уверены, что хотите удалить трек(и)? + + + D&elete + + + + Paste to selected &track + + + + Paste clone to selected trac&k + + + + Paste (show dialo&g) @@ -5676,7 +6220,7 @@ Show New Style Drum Tracks - + Показывать треки ударных в новом Ñтиле @@ -5691,7 +6235,7 @@ Pan - Пан + Пан 1/2 channel @@ -5757,6 +6301,10 @@ automation type тип автоматизации + + calibration gain + + MusEGui::BigTime @@ -5798,6 +6346,13 @@ + MusEGui::Canvas + + Tools: + + + + MusEGui::ClipListEdit MusE: Clip List Editor @@ -5827,11 +6382,11 @@ Make the current part's track match the selected drumlist entry - + ПривеÑти текущий трек в ÑоответÑтвие Ñ Ð²Ð²Ð¾Ð´Ð¾Ð¼ ÑпиÑка ударных Use pencil or line tool to draw new events - + ИÑпользуйте карандаш или линию Ð´Ð»Ñ Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð½Ð¾Ð²Ñ‹Ñ… Ñобытий @@ -5862,34 +6417,38 @@ Velocity - СкороÑÑ‚ÑŒ Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ + СкороÑÑ‚ÑŒ Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ add new ... - Добавить новый... + Добавить новый... Instrument-defined - Определенный инÑтрументом + Определенный инÑтрументом Add ... - Добавить... + Добавить... Others - Другие + Другие Edit instrument ... - Редактировать инÑтрумент... + Редактировать инÑтрумент... Common Controls - Обычные кнопки + Обычные кнопки ctrl-double-click on/off + ctrl-двойной щелчок вкл/выкл + + + all/per-note velocity mode @@ -5897,53 +6456,57 @@ MusEGui::DList hide this instrument - + Ñкрыть Ñтот инÑтрумент show this instrument - + показать Ñтот инÑтрумент this turns a grayed out eye into a blue eye - + Ñто превратит Ñерый глаз в голубой MusEGui::DrumCanvas Moving items failed - + Передвижение не удалоÑÑŒ The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. - + Выбранное не может быть передвинуто, так как по крайней мере одна нота была бы +передвинута в трек не ÑоответÑтвующий ни оригинальному ни текущему. +Может помочь изменение данной партии Ñ ALT+ВЛЕВО/ВПРÐВО. Creating event failed - + Ðе удалоÑÑŒ Ñоздать Ñоытие Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. - + Ðе могу Ñоздать Ñобытие, так как Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð°Ñ Ð¿Ð°Ñ€Ñ‚Ð¸Ñ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚ÑÑ Ð½Ðµ в том же треке, и +выбранный инÑтрумент может быть или нигде, или во многих партиÑÑ…, что Ñомнительно. Выберите партию Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¸ попробуйте Ñнова. Recording event failed - + ЗапиÑÑŒ ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð½Ðµ удалаÑÑŒ Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. - + Ðе могу запиÑать Ñобытие, так как Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð°Ñ Ð¿Ð°Ñ€Ñ‚Ð¸Ñ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚ÑÑ Ð½Ðµ в том же треке, и +выбранный инÑтрумент может быть или нигде, или во многих партиÑÑ…, что Ñомнительно. Выберите партию Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¸ попробуйте Ñнова. Internal error - + ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. - + Бр-Ñ€, абÑолютно Ð½ÐµÐ²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð°Ñ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° вÑе же ÑлучилаÑÑŒ. Проверьте вывод конÑоли. Ðичего не запиÑалоÑÑŒ. @@ -5978,35 +6541,35 @@ output channel (hold ctl to affect all rows) - канал выхода (удерживайте ctrl, чтобы воздейÑтвовать на вÑе Ñ€Ñды) + канал выхода (удерживайте ctrl, чтобы воздейÑтвовать на вÑе Ñ€Ñды) output port (hold ctl to affect all rows) - порт выхода (удерживайте ctrl, чтобы воздейÑтвовать на вÑе Ñ€Ñды) + порт выхода (удерживайте ctrl, чтобы воздейÑтвовать на вÑе Ñ€Ñды) shift + control key: draw velocity level 1 - клавиши shift+control: риÑовать уровень 1 ÑкороÑти Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ + клавиши shift+control: риÑовать уровень 1 ÑкороÑти Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ control key: draw velocity level 2 - Клавиша control: риÑовать уровень 2 ÑкороÑти Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ + Клавиша control: риÑовать уровень 2 ÑкороÑти Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ shift key: draw velocity level 3 - Клавиша shift: риÑовать уровень 3 ÑкороÑти Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ + Клавиша shift: риÑовать уровень 3 ÑкороÑти Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ draw velocity level 4 - риÑовать уровень 4 ÑкороÑти Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ + риÑовать уровень 4 ÑкороÑти Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ output channel (ctl: affect all rows) - канал выхода (ctrl: дейÑтвует на вÑе Ñ€Ñды) + канал выхода (ctrl: дейÑтвует на вÑе Ñ€Ñды) output port (ctl: affect all rows) - порт выхода (ctrl: дейÑтвует на вÑе Ñ€Ñды) + порт выхода (ctrl: дейÑтвует на вÑе Ñ€Ñды) &File @@ -6234,87 +6797,123 @@ hide instrument - + Ñкрыть инÑтрумент Re-order map - + ПерепиÑать карту Group - + Группировать Don't group - + Ðе группировать Group by channel - + группировать по каналам Group maximally - + Группировать макÑимально Show/Hide - + Показать/Скрыть Also show hidden instruments - + Также показать Ñкрытые инÑтрументы Show all instruments - + Показать вÑе инÑтрументы Hide all instruments - + Скрыть вÑе инÑтрументы Only show used instruments - + Показать только иÑпользуемые инÑтрументы Only show instruments with non-empty name or used instruments - + Показать только инÑÑ‚Ñ€ÑƒÐ¼ÐµÐ½Ñ‚Ñ‹Ñ Ð½Ðµ пуÑтым именем или иÑпользуемые Drum map tools - + ИнÑтрументы карты барабанов Cursor step: - + Шаг курÑора: H - H + H Not all parts are displayed - + Показаны не вÑе партии You selected both old-style-drumtracks and others (that is: new-style or midi tracks), but they cannot displayed in the same drum edit. I'll only display the old-style drumtracks in this editor, dropping the others. + Ð’Ñ‹ выбрали треки в Ñтаром Ñтиле вмеÑте Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸ (в новом Ñтиле или midi треки), но они не могут быть показаны в той же редакции ударных. Показываю только треки в Ñтаром Ñтиле, оÑтальные опуÑкаю. + + + override track output channel (hold ctl to affect all rows) + + + + override track output port (hold ctl to affect all rows) + + + + control + meta keys: draw velocity level 1 + + + + meta key: draw velocity level 2 + + + + draw default velocity level 3 + + + + meta + alt keys: draw velocity level 4 + + + + override track output channel (ctl: affect all rows) + + + + override track output port (ctl: affect all rows) + + Play Events + ВоÑпроизвеÑти ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ + MusEGui::EditCAfterDialog MusE: Enter Channel Aftertouch - MusE: ВвеÑти Channel Aftertouch + MusE: ВвеÑти Channel Aftertouch Time Position - ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð¿Ð¾ времени + ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð¿Ð¾ времени Pressure - Давление + Давление @@ -6396,15 +6995,15 @@ MusE: Cannot add common controller - MusE: Ðевозможно добавить обычный контроллер + MusE: Ðевозможно добавить обычный контроллер A controller named '%1' already exists. - Контроллер '%1' уже ÑущеÑтвует. + Контроллер '%1' уже ÑущеÑтвует. A controller number %1 already exists. - Контроллер номер '%1' уже ÑущеÑтвует. + Контроллер номер '%1' уже ÑущеÑтвует. MusE @@ -6430,43 +7029,100 @@ Name - Ð˜Ð¼Ñ + Ð˜Ð¼Ñ Vol - Громк + Громк Quant - + Квант E-Note - E-Ðота + E-Ðота Len - Дл + Дл A-Note - A-Ðота + A-Ðота LV1 - УР1 + УР1 LV2 - УР2 + УР2 LV3 - УР3 + УР3 LV4 - УР4 + УР4 + + + Control7 + Контрл7 + + + Control14 + Контрл14 + + + RPN + RPN + + + NPRN + + + + RPN14 + RPN14 + + + NRPN14 + NRPN14 + + + Pitch + Ð’Ñ‹Ñота тона + + + Program + Программа + + + PolyAftertouch + + + + Aftertouch + ПоÑленажатие + + + Tick + Тик + + + Data + Данные + + + New controller: Error + + + + Error! All control numbers are taken up! +Clean up the instrument! + @@ -6492,19 +7148,19 @@ MusEGui::EditPAfterDialog MusE: Enter Poly Aftertouch - MusE: ВвеÑти полиф. поÑленажатие + MusE: ВвеÑти полиф. поÑленажатие Time Position - ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð¿Ð¾ времени + ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð¿Ð¾ времени Pitch - Ð’Ñ‹Ñота тона + Ð’Ñ‹Ñота тона Pressure - Давление + Давление @@ -6515,7 +7171,7 @@ select parts move parts copy parts - выбрать инÑтрумент "Указатель": + выбрать инÑтрумент "Указатель": при помощи Ð£ÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ñ Ð¼Ð¾Ð¶Ð½Ð¾: выделÑÑ‚ÑŒ партии перемещать партии @@ -6526,7 +7182,7 @@ with the pencil tool you can: create new parts modify length of parts - выбрать инÑтрумент "Карандаш": + выбрать инÑтрумент "Карандаш": инÑтрументом Карандаш вы можете: Ñоздавать новые партии изменÑÑ‚ÑŒ длину партий @@ -6534,100 +7190,131 @@ select Delete Tool: with the delete tool you can delete parts - выбрать инÑтрумент "ЛаÑтик": + выбрать инÑтрумент "ЛаÑтик": инÑтрументом "ЛаÑтик" вы можете удалÑÑ‚ÑŒ партии select Cut Tool: with the cut tool you can split a part - выбрать инÑтрумент "Ðожницы": + выбрать инÑтрумент "Ðожницы": инÑтрументом Ðожницы вы можете резать партию select Glue Tool: with the glue tool you can glue two parts - выбрать инÑтрумент "Клей": + выбрать инÑтрумент "Клей": инÑтрументом Клей вы можете ÑоединÑÑ‚ÑŒ партии select Score Tool: - выбрать инÑтрумент партитуры: - + выбрать инÑтрумент партитуры: select Quantize Tool: insert display quantize event - выбрать инÑтрумент "Квантование": + выбрать инÑтрумент "Квантование": квантует отображаемые ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ select Drawing Tool - выбрать инÑтрумент "РиÑование" + выбрать инÑтрумент "РиÑование" select Muting Tool: click on part to mute/unmute - выбрать инÑтрумент "Без звука": + выбрать инÑтрумент "Без звука": Щелкните по партии, чтоб переключать "Без зв./Звук" Manipulate automation - УправлÑÑ‚ÑŒ автоматизацией + УправлÑÑ‚ÑŒ автоматизацией Cursor tool - ИнÑтрумент КурÑор + ИнÑтрумент КурÑор pointer - указатель + указатель pencil - карандаш + карандаш eraser - лаÑтик + лаÑтик cutter - ножницы + ножницы score - партитура + партитура glue - клей + клей quantize - квантовать + квантовать draw - риÑовать + риÑовать mute parts - заглушить партию + заглушить партию edit automation - править автоматизацию + править автоматизацию cursor - курÑор + курÑор Edit Tools ИнÑтрументы правки + + select Cursor (tracker mode) tool: +with the cursor tool you can: + navigate with arrow keys + use VBNM to place notes + change step with 0 and 9 + + + + select Range Tool + + + + select Panning Tool + + + + select Zoom Tool + + + + range + + + + pan + + + + zoom + + MusEGui::EffectRack @@ -6683,12 +7370,16 @@ Do you really want to replace the effect %1? Ð’Ñ‹ дейÑтвительно хотите заменить Ñффект %1? + + Presets + ПреÑеты + MusEGui::GlobalSettingsConfig MusE: Choose start template or song - + MusE: Ðачать Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð° или Ñонга @@ -6817,11 +7508,11 @@ insert Channel Aftertouch - вÑтавить Channel поÑленажатие + вÑтавить Channel поÑленажатие insert Poly Aftertouch - вÑтавить Полиф. ПоÑленаж + вÑтавить Полиф. ПоÑленаж &Edit @@ -6889,15 +7580,15 @@ Increase Tick - + Увеличить Тик Decrease Tick - + Уменьшить Тик MusE: List Editor - MusE: Редактор ÑпиÑка + MusE: Редактор ÑпиÑка @@ -6917,7 +7608,7 @@ Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? - ÐаÑтройка будет применена к новым midi трекам. + ÐаÑтройка будет применена к новым midi трекам. Хотите применить её ко вÑем уже ÑущеÑтвующим midi трекам? @@ -6930,11 +7621,11 @@ in - в + in out - вне (из) + out Show first aliases @@ -7100,42 +7791,100 @@ <none> <ничего> - - - MusEGui::MTScale - bar scale - шкала тактов + Remove + Удалить - - - MusEGui::MTScaleFlo - bar scale - шкала тактов + Midi device name + - - - MusEGui::MarkerView - MusE: Marker - MusE: Маркер + Midi device type + - add marker - добавить маркер + Connections from Jack Midi + - delete marker - удалить маркер + Connections to Jack Midi + - &Edit - &Правка + Result of opening the device: +OK: Assigned to a port and in use +Closed: Unassigned to a port, or closed +R/W Error: Unable to open for read or write +Unavailable: USB midi unplugged, or external + application not running, or synth plugin + not installed etc. +(Jack Midi devices have 'unavailable ports' + in the routes columns.) +Unavailable devices or ports can be purged + with 'Remove' or with the advanced router. + - Window &Config - ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ &окна + Enable Graphical User Interface for device + + + + Connections from Jack Midi ports + + + + Connections to Jack Midi ports + + + + Type + Тип + + + In + + + + Out + + + + + MusEGui::MTScale + + bar scale + шкала тактов + + + + MusEGui::MTScaleFlo + + bar scale + шкала тактов + + + + MusEGui::MarkerView + + MusE: Marker + MusE: Маркер + + + add marker + добавить маркер + + + delete marker + удалить маркер + + + &Edit + &Правка + + + Window &Config + ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ &окна edit tools @@ -7226,6 +7975,45 @@ + MusEGui::MidiAudioControl + + Control7 + Контрл7 + + + Control14 + Контрл14 + + + RPN + RPN + + + NPRN + + + + RPN14 + RPN14 + + + NRPN14 + NRPN14 + + + Pitch + Ð’Ñ‹Ñота тона + + + Program + Программа + + + Aftertouch + ПоÑленажатие + + + MusEGui::MidiInputTransformDialog New @@ -7296,7 +8084,7 @@ ctrl-double-click on/off - + ctrl-двойной щелчок вкл/выкл @@ -7591,23 +8379,23 @@ None - Ðичего + Ðичего Tiny - + Очень маленький Small - + Маленький Large - + Большой Large with pre-detect - + Большой Ñ Ð¿Ñ€ÐµÐ´-обнаружением @@ -7792,12 +8580,12 @@ Импортировать Партию - Import Wave File - Импортировать Wave-файл + Import Audio File + Импортировать Ðудио-файл Find unused wave files - Ðайти не иÑпользуемые wave-файлы + Ðайти не иÑпользуемые аудио-файлы &Quit @@ -7873,15 +8661,15 @@ Reset Instr. - Перезагрузить ИнÑÑ‚Ñ€. + Перезагрузить инÑÑ‚Ñ€. Init Instr. - ЗапуÑтить ИнÑÑ‚Ñ€. + ЗапуÑтить инÑÑ‚Ñ€. Local Off - откл. локальное управление + Отключить локальное управление Bounce to Track @@ -8205,19 +8993,23 @@ %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. - + %n парти(Ñ, и, ий) из %1 не могут быть импортированы. Возможно выбранный трек неверного типа. + + %n part(s) could not be imported. Likely the selected track is the wrong type. - + %n парти(Ñ, и, ий) могут быть импортированы. Возможно выбранный трек неверного типа. + + @@ -8246,33 +9038,127 @@ Song Position - + ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð¡Ð¾Ð½Ð³Ð° Tempo - Темп + Темп Signature - Размер + Размер About &Qt - + О &Qt This will clear all automation data on all audio tracks! Proceed? - + УдалитÑÑ Ð²ÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð²Ð¾ вÑех +аудио треках! +Продолжить? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? + Будет Ñделан Ñнимок автоматизации +вÑех контроллеров во вÑех аудио +треках в текущей позиции. +Продолжить? + + + turn on/off metronome + + + + MusE: Song: + + + + MusE: Warning + + + + The song uses multiple ports but export format 0 (single track) is set. +The first track's port will be used. Playback will likely be wrong + unless the channels used in one port are different from all other ports. +Canceling and setting a different export format would be better. +Continue? + + + + This wave file has a samplerate of %1, +as opposed to current setting %2. +File will be resampled from %1 to %2 Hz. +Do you still want to import it? + + + + Wave import error + + + + There are too many wave files +of the same base name as imported wave file +Can not continue. + + + + Can't create new wav file in project folder! + + + + + Failed to initialize sample rate converter! + + + + Cancel + Отмена + + + Resampling wave file +"%1" +from %2 to %3 Hz... + + + + Output has clipped +Resampling again and normalizing wave file +"%1" +Try %2 of %3... + + File version is %1.%2 +Current version is %3.%4 +Conversions may be applied if file is saved! + + + + Opening file + + + + Do not warn again + + + + Cpu load + Загрузка ЦП + + + Measured CPU load + Загрузка ЦП + + + No CPU load data + Ðет данных о загрузке ЦП + MusEGui::NoteInfo @@ -8302,7 +9188,7 @@ delta/absolute mode - + отн./абÑолютный режим @@ -8374,25 +9260,29 @@ Part name: %1 Files: - Ð˜Ð¼Ñ Ð¿Ð°Ñ€Ñ‚Ð¸Ð¸: + Ð˜Ð¼Ñ Ð¿Ð°Ñ€Ñ‚Ð¸Ð¸:%1 Файлы: %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. - + %n парти(Ñ)(и)(й) из %1 не могут быть вÑтавлены. Ðеверный формат, выбранного трека. + + %n part(s) could not be pasted. Likely the selected track is the wrong type. - + %n парти(Ñ)(и)(й) не могут быть вÑтавлены. Ðеверный формат, выбранного трека. + + @@ -8421,6 +9311,18 @@ Remove selected + Удалить выбранное + + + Part: + + + + Automation: + ÐвтоматизациÑ: + + + Normalize @@ -8428,40 +9330,44 @@ MusEGui::PasteDialog %n quarter(s) - - %n четверт(и) + + %n четверть(и) + + %1 quarter for floating-point arguments like 1.5 - %n четверть + %1 четверть %1 quarters for floating-point arguments like 1.5 - %n четверти + %1 четверти MusEGui::PasteEventsDialog %n quarter(s) - - %n четверт(и) + + %n четверть(и) + + %1 quarter for floating-point arguments like 1.5 - %n четверть + %1 четверть %1 quarters for floating-point arguments like 1.5 - %n четверти + %1 четверти @@ -8671,78 +9577,125 @@ Ok - Ok + Ok Cancel - Отмена + Отмена Mono and Stereo - Моно и Стерео + Моно и Стерео Stereo - Стерео + Стерео Mono - Моно + Моно Show All - Показать Ð’Ñе + Показать Ð’Ñе Search in 'Label' and 'Name': - ИÑкать по "Меткам" и "Именам": + ИÑкать по "Меткам" и "Именам": Type - Тип + Тип Audio inputs - + Ðудио входы Audio outputs - + Ðудио выходы Control inputs - + Контрольные входы Control outputs - + Контрольные выходы In-place capable - + In-place capable ID number - + Ðомер ID Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. - + Выбирает типы плагинов, которые будут показаны в ÑпиÑке.<br>ИÑпользование моно плагинов на Ñтерео треках не проблема, +два пойдут в параллель.<br>Ð’ категории "all" могут вÑтретитьÑÑ Ð¿Ð»Ð°Ð³Ð¸Ð½Ñ‹ не Ð´Ð»Ñ Ñ€Ñка Ñффектов. dssi synth - + dssi Ñинт dssi effect - + dssi Ñффект + + + LV2 synth + LV2 Ñинт + + + LV2 effect + LV2 Ñффект ladspa - + ladspa Show plugs: + Показать плагины: + + + Plugin categories. +Right-click on tabs to manage. +Right-click on plugins to add/remove from a category. + + + + &create new group + + + + &delete currently selected group + + + + re&name currently selected group + + + + Associated categories + + + + You need to define some categories first. + + + + new group + + + + Enter the new group name + + + + Wine VST @@ -8778,6 +9731,13 @@ + MusEGui::PopupMenu + + <More...> %1 + + + + MusEGui::ProjectCreateImpl Select directory @@ -8785,6 +9745,33 @@ + MusEGui::RouteDialog + + Normal + Ðормальный + + + Alias 1 + + + + Alias 2 + + + + Source + ИÑточник + + + Destination + Ðазначение + + + <none> + <ничего> + + + MusEGui::RoutePopupMenu Channel @@ -8826,23 +9813,111 @@ Midi port sends ПоÑылы midi порта - - - MusEGui::ScoreCanvas - Treble - Скрипичный + Channel grouping: + - Bass - БаÑовый + Mono + - Grand Staff - Большой Ñтан + Stereo + Стерео - Remove staff + Channels + + + + Midi ports/devices + + + + Omni + + + + Show aliases: + + + + First + + + + Second + + + + Show names + + + + Show first aliases + Показать первые пÑевдонимы + + + Show second aliases + Показать вторые пÑевдонимы + + + Jack ports + + + + Connect + Соединить + + + Unavailable + + + + Open advanced router... + + + + Output routes: + + + + Input routes: + + + + Tracks + + + + Destinations: + + + + Midi sends + + + + Sources: + + + + + MusEGui::ScoreCanvas + + Treble + Скрипичный + + + Bass + БаÑовый + + + Grand Staff + Большой Ñтан + + + Remove staff Удалить Ñтан @@ -9094,6 +10169,38 @@ + MusEGui::ShortcutConfig + + Save printable text file + + + + Text files (*.txt);;All files (*) + + + + Error + Ошибка + + + Error opening file for saving + + + + Shortcuts for selected category: + + + + Legend: + + + + + An error occurred while saving + + + + MusEGui::SigScale signature scale @@ -9104,11 +10211,11 @@ MusEGui::SigToolbarWidget time signature at current position - размер в текущей позиции + размер в текущей позиции Signature: - + Размер: @@ -9158,19 +10265,19 @@ Update drummap? - Обновить карту ударных? + Обновить карту ударных? Do you want to use same port for all instruments in the drummap? - Ð’Ñ‹ хотите иÑпользовать один порт Ð´Ð»Ñ Ð²Ñех инÑтрументов в ÑпиÑке ударных? + Ð’Ñ‹ хотите иÑпользовать один порт Ð´Ð»Ñ Ð²Ñех инÑтрументов в ÑпиÑке ударных? &Yes - &Да + &Да &No - &Ðет + &Ðет show gui @@ -9218,86 +10325,124 @@ Do you want to use same port and channel for all instruments in the drummap? - Ð’Ñ‹ хотите иÑпользовать тот же порт и канал Ð´Ð»Ñ Ð²Ñех инÑтрументов в ÑпиÑке ударных? + Ð’Ñ‹ хотите иÑпользовать тот же порт и канал Ð´Ð»Ñ Ð²Ñех инÑтрументов в ÑпиÑке ударных? off - выкл + выкл <unknown> - <неизвеÑтно> + <неизвеÑтно> Midi control - + Midi контроль Assign - + Ðазначить Clear - + ОчиÑтить Internal - + Внутренний Synth - + Синт Save track's drumlist - + Сохранить ÑпиÑок ударных трека Save track's drumlist differences to initial state - + Сохранить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² ÑпиÑке ударных трека Load track's drumlist - + Загрузить ÑпиÑок ударных трека Reset track's drumlist - + СброÑить ÑпиÑок ударных трека в нач. ÑоÑтоÑние Reset track's drumlist-ordering - + СброÑить порÑдок ударных трека в нач. ÑоÑтоÑние Copy track's drumlist to all selected tracks - + Скопировать ÑпиÑок ударных трека во вÑе выбранные треки Copy track's drumlist's differences to all selected tracks - + Скопировать Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² ÑпиÑке ударных трека во вÑе выбранные треки Drum map - Карта ударных + Карта ударных Reset the track's drum map with instrument defaults? - + Заменить инÑтрументы в карте инÑтрументами по умолчанию? Reset the track's drum map ordering? - + ВоÑÑтановить в карте ударных порÑдок по умолчанию? Muse: Load Track's Drum Map - + Muse: Загрузить карту ударных трека MusE: Store Track's Drum Map - + Muse: Сохранить карту ударных трека New style drum + Барабан в новом Ñтиле + + + Presets + ПреÑеты + + + Clear all controller events? + ОчиÑтить вÑе ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»Ð»ÐµÑ€Ð¾Ð²? + + + &Ok + &ОК + + + &Cancel + + + + Change color + + + + Other + Другой + + + clear automation + очиÑтить автоматизацию + + + Drummap + + + + This drummap was created with a previous version of MusE, +it is being read but the format has changed slightly so some +adjustments may be necessary. @@ -9312,11 +10457,11 @@ MusEGui::TempoToolbarWidget tempo at current position - темп в текущей позиции + темп в текущей позиции Tempo: - + Темп: @@ -9398,27 +10543,31 @@ Undo/Redo tools - + ИнÑтрумент отменить/вернуть Panic - Паника + Паника Transport - ТранÑпорт + ТранÑпорт Song Position - + ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð¡Ð¾Ð½Ð³Ð° Tempo - Темп + Темп Signature - Размер + Размер + + + Metronome + Метроном @@ -9583,7 +10732,7 @@ MusEGui::VisibleTracks Show wave tracks - Показать wave-треки + Показать аудио-треки Show group tracks @@ -9615,6 +10764,61 @@ + MusEGui::WaveCanvas + + Adjust Wave Offset + + + + Wave offset (frames) + + + + Part creation failed + + + + Left and right position markers must be placed inside the current part. + + + + Part created + + + + The selected region has been copied to the clipboard and can be pasted in the arranger. + + + + MusE - external editor failed + MusE - ошибка запуÑка внешнего редактора + + + MusE was unable to launch the external editor +check if the editor setting in: +Global Settings->Audio:External Waveditor +is set to a valid editor. + "MusE" не может запуÑтить внешний редактор. +Проверьте, дейÑтвительно ли в: +Общие наÑтройки->Ðудио:Внешний редактор +выбран правильный редактор. + + + MusE - file size changed + MusE - размер файла изменилÑÑ + + + When editing in external editor - you should not change the filesize +since it must fit the selected region. + +Missing data is muted + При редактировании во внешнем редакторе - не изменÑйте размер файла +так как он должен ÑоответÑтвовать выбранной облаÑти. + +УтерÑнные данные заглушены + + + MusEGui::WaveEdit &Edit @@ -9696,33 +10900,57 @@ Cursor КурÑор + + &Create Part from Region + + + + &Previous Part + &ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Ð¿Ð°Ñ€Ñ‚Ð¸Ñ + + + &Next Part + &Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Ð¿Ð°Ñ€Ñ‚Ð¸Ñ + + + &Event Color + &Цвет ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ + + + &Part colors + + + + &Gray + + MusEGui::WaveView MusE - external editor failed - MusE - ошибка запуÑка внешнего редактора + MusE - ошибка запуÑка внешнего редактора MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. - "MusE" не может запуÑтить внешний редактор. + "MusE" не может запуÑтить внешний редактор. Проверьте, дейÑтвительно ли в: Общие наÑтройки->Ðудио:Внешний редактор выбран правильный редактор. MusE - file size changed - MusE - размер файла изменилÑÑ + MusE - размер файла изменилÑÑ When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted - При редактировании во внешнем редакторе - не изменÑйте размер файла + При редактировании во внешнем редакторе - не изменÑйте размер файла так как он должен ÑоответÑтвовать выбранной облаÑти. УтерÑнные данные заглушены @@ -9827,7 +11055,7 @@ Number and raster - Ðомер и раÑÑ‚Ñ€ + Ðомер и значение insert @@ -9839,7 +11067,7 @@ raster - раÑÑ‚Ñ€ + значение ticks @@ -9886,7 +11114,7 @@ Number and raster - Ðомер и раÑÑ‚Ñ€ + Ðомер и значение insert @@ -9898,7 +11126,7 @@ raster - раÑÑ‚Ñ€ + значение ticks @@ -9913,80 +11141,211 @@ Ð’Ñегда в ÑущеÑтвующие партии - Never into existing parts - Ðикогда в ÑущеÑтвующие партии + Never into existing parts + Ðикогда в ÑущеÑтвующие партии + + + Into existing parts if part has not +to be expanded by more than + Ð’ ÑущеÑтвующие партии, еÑли Ð¿Ð°Ñ€Ñ‚Ð¸Ñ +не будет раÑширена больше чем + + + Put everything into the (selected) part + ПомеÑтить вÑÑ‘ в (выбранную) партию + + + OK + OK + + + Cancel + Отмена + + + + PluginDialogBase + + Dialog + Диалог + + + Ports: + + + + Mono + Моно + + + Mono + Stereo + + + + Stereo + Стерео + + + All + Ð’Ñе + + + Plugin type: + + + + Filter: + + + + &OK + + + + &Cancel + + + + + ProjectCreate + + Create Project + Создать Проект + + + Projects folder: + Папка проекта: + + + ... + ... + + + Project Name: + Ðазвание проекта: + + + Project is a Template + Проект ÑвлÑетÑÑ Ð¨Ð°Ð±Ð»Ð¾Ð½Ð¾Ð¼ + + + Project song file type: + Тип файла Ñонга в проекте: + + + Project Path to song file: + Путь к файлу Ñонга в проекте: + + + Create project folder (recommended for audio projects) + Создать папку пректа (рекомендуетÑÑ Ð´Ð»Ñ Ð°ÑƒÐ´Ð¸Ð¾ проектов) + + + Song information: + Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ Ñонге: + + + Write window state + Запоминать ÑоÑтоÑние окон + + + &Create project folder (recommended for audio projects) + + + + + QObject + + Error + Ошибка + + + Please first select the range for crescendo with the loop markers. + Выберите Ñначала маркерами диапазон Ð´Ð»Ñ ÐºÑ€ÐµÑ‰ÐµÐ½Ð´Ð¾. + + + Controller ! + + + + Other ! + + + + No presets found + ПреÑеты не найдены + + + Switch on General Midi Level 1 mode + + + + Switch on General Midi Level 2 mode + - Into existing parts if part has not -to be expanded by more than - Ð’ ÑущеÑтвующие партии, еÑли Ð¿Ð°Ñ€Ñ‚Ð¸Ñ -не будет раÑширена больше чем + Switch off General Midi Level 1 or 2 + - Put everything into the (selected) part - ПомеÑтить вÑÑ‘ в (выбранную) партию + Switch on Roland GS mode + - OK - OK + Switch on Yamaha XG mode + - Cancel - Отмена + Select gui type + Выберите Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ - - - ProjectCreate - Create Project - Создать Проект + Preset actions + ДейÑÑ‚Ð²Ð¸Ñ Ð½Ð°Ð´ преÑетами - Projects folder: - Папка проекта: + Save preset... + Сохранить преÑет... - ... - ... + Update list + Обновить ÑпиÑок - Project Name: - Ðазвание проекта: + Saved presets + Сохраненные преÑеты - Project is a Template - Проект ÑвлÑетÑÑ Ð¨Ð°Ð±Ð»Ð¾Ð½Ð¾Ð¼ + Enter new preset name + Введите Ð¸Ð¼Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ преÑета - Project song file type: - Тип файла Ñонга в проекте: + Midi programs + Midi программы - Project Path to song file: - Путь к файлу Ñонга в проекте: + Presets + ПреÑеты - Create project folder (recommended for audio projects) - Создать папку пректа (рекомендуетÑÑ Ð´Ð»Ñ Ð°ÑƒÐ´Ð¸Ð¾ проектов) + Tracks: + - Song information: - Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ Ñонге: + Midi ports: + - Write window state + Midi devices: - - - QObject - Error - Ошибка + Jack: + - Please first select the range for crescendo with the loop markers. - Выберите Ñначала маркерами диапазон Ð´Ð»Ñ ÐºÑ€ÐµÑ‰ÐµÐ½Ð´Ð¾. + Jack midi: + @@ -10065,6 +11424,34 @@ No selection. Ignoring Ðе выделено. ИгнорируетÑÑ + + Instrument-defined + Определенный инÑтрументом + + + Edit instrument ... + Редактировать инÑтрумент... + + + Add + Добавить + + + Others + Другие + + + Common Controls + Обычные кнопки + + + Velocity + СкороÑÑ‚ÑŒ Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ + + + No wave events selected. + Ðе выбрано wave-Ñобытий + QuantBase @@ -10114,7 +11501,7 @@ Raster - РаÑÑ‚Ñ€ + Значение Whole @@ -10301,6 +11688,79 @@ remove selected route Удалить выбранный маршрут + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. + + + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + Itemized list of current connections. + + + + Show only selected sources + + + + Show only destination routes for selected source + + + + Show only selected destinations + + + + Show only source routes for selected destination + + + + Show all Midi Ports + + + + Auto adjust column size + + + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + + + + Preferred route name or alias + + + + Connect source to destination + + + + Remove selected route + + SS_PluginChooserBase @@ -10484,6 +11944,22 @@ Alt+A Alt+A + + &Printable file... + + + + Alt+P + Alt+P + + + &Ok + &ОК + + + Alt+O + + SimpleDrumsGuiBase @@ -10506,6 +11982,18 @@ Load sample dialog Загрузить диалог ÑÑмплов + + Mix + Смешать + + + Chn + + + + Channel routing + + SongInfo @@ -10592,6 +12080,54 @@ Description ОпиÑание + + &Apply + &Применить + + + &OK + + + + Configure midi devices, midi ports, and synthesizers + + + + Available soft synthesizers + + + + Useable devices + + + + Add: + + + + Synth + Синт + + + ALSA + + + + JACK + + + + Rename + + + + Remove + Удалить + + + Device port assignments + + TransposeBase @@ -10808,7 +12344,7 @@ On Вкл - + VAM 1.0beta3 Virtual Analog for MusE Released under GPL. @@ -11000,16 +12536,24 @@ Wave/Binary (*.wav *.ogg *.bin) - Wave/Бинарные (*.wav *.ogg *.bin) + Wave/Бинарные (*.wav *.ogg *.bin) Wave (*.wav *.ogg) - Wave (*.wav *.ogg) + Wave (*.wav *.ogg) Binary (*.bin) Бинарные (*.bin) + + Wave/Binary (*.wav *.ogg *.flac *.bin) + + + + Wave (*.wav *.ogg *.flac) + + freeverb @@ -11243,6 +12787,29 @@ + midiWarnInitPendingBase + + Instrument initialization + + + + MusE should now send some Instrument Initialization Sequences. +The sequences (usually System Exclusive messages) are defined + by the selected instruments in the Settings -> Midi Ports dialog, + such as the GM (default), GS, or XG instruments. + +Typically you should answer yes here. +You can always do it manually from the Midi menu. + +Continue? + + + + Don't ask me again + + + + shortcuts Transport: Start playback from current location @@ -11406,7 +12973,7 @@ Add wave track - Добавить wave-трек + Добавить аудио-трек Add audio output @@ -11486,7 +13053,7 @@ Settings: Midi sync configuration - ÐаÑтройки: ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ midi-Ñинхр + ÐаÑтройки: ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ midi-Ñинхронизации Settings: Midi file import/export configuration @@ -11598,11 +13165,11 @@ Edit: Paste as clones - Правка: Ð’Ñтавить как клон + Правка: Ð’Ñтавить как клон Edit: Paste as clones (with dialog) - Правка: Ð’Ñтавить как клон (Ñ Ð´Ð¸Ð°Ð»Ð¾Ð³Ð¾Ð¼) + Правка: Ð’Ñтавить как клон (Ñ Ð´Ð¸Ð°Ð»Ð¾Ð³Ð¾Ð¼) Select track above @@ -11942,6 +13509,85 @@ Add new style drum track + Добавить трек ударных в новом Ñтиле + + + Edit: Paste to selected track + + + + Edit: Paste clone + + + + Edit: Paste clone to selected track + + + + Transport: Step record + + + + Transport: Midi input + + + + Transport: Play events + + + + Edit: Increase velocity + + + + Edit: Decrease velocity + + + + Move: Move to selected note + + + + Tool: Pan + + + + Tool: Zoom + + + + Tool: Range + + + + Edit: Duplicate track + + + + Transport: Restart recording + ТранÑпорт: ПерезапуÑтить запиÑÑŒ + + + Edit selected track name + Редактировать Ð¸Ð¼Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ð¾Ð¹ дорожки + + + Normalize + Ðормализовать + + + + warnBadTimingBase + + Bad timing + Плохой тайминг + + + Message here + + + + Don't warn me again diff -Nru muse-2.1.2/share/locale/muse_sv_SE.ts muse-3.0.2+ds1/share/locale/muse_sv_SE.ts --- muse-2.1.2/share/locale/muse_sv_SE.ts 2013-03-28 15:15:47.000000000 +0000 +++ muse-3.0.2+ds1/share/locale/muse_sv_SE.ts 2017-12-04 21:01:19.000000000 +0000 @@ -1,6 +1,6 @@ - + @default @@ -73,7 +73,7 @@ Bad timing - DÃ¥lig timing + DÃ¥lig timing Timing source frequency is %1hz, which is below the recommended minimum: 500hz! @@ -94,6 +94,10 @@ Add New Style Drum Track Lägg till trumspÃ¥r av nya typen + + channel="%1" + + AboutBox @@ -110,21 +114,53 @@ - Version 2 - - - (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License - (C) Copyright 1999-2012 Werner Schweer och andra. + (C) Copyright 1999-2012 Werner Schweer och andra. Se http://www.muse-sequencer.org för nya versioner och mer information. Publicerad under GNU Public License + + (C) Copyright 1999-2014 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer och andra. +Se http://www.muse-sequencer.org för nya versioner och +mer information. + +Publicerad under GNU Public License {1999-2014 ?} + + + Version info (replaced programmatically) + + + + (C) Copyright 1999-2015 Werner Schweer and others. +See http://www.muse-sequencer.org for new versions and +more information. + +Published under the GNU Public License + (C) Copyright 1999-2012 Werner Schweer och andra. +Se http://www.muse-sequencer.org för nya versioner och +mer information. + +Publicerad under GNU Public License {1999-2014 ?} {1999-2015 ?} + + + System information: + + + + TextLabel + + AppearanceDialogBase @@ -218,7 +254,7 @@ QT Theme - QT-tema + QT-tema Windows @@ -372,6 +408,22 @@ May require restarting MusE for best results Kan kräva omstart av MusE för bästa resultat + + Themes + + + + MusE color scheme + + + + current settings + + + + Change + + ArrangerColumnsBase @@ -397,35 +449,15 @@ Control7 - Kontroll7 + Kontroll7 Control14 - Kontroll14 - - - RPN - - - - NRPN - - - - RPN14 - - - - NRPN14 - + Kontroll14 Pitch - Tonhöjd - - - Program - + Tonhöjd H-Ctrl @@ -492,6 +524,33 @@ + ChooseSysexBase + + Dialog + + + + Hex: + + + + TextLabel + + + + Comment: + Kommentar: + + + &OK + &Ok + + + &Cancel + &Avbryt + + + ClipListEditorBase MusE: ClipList @@ -647,6 +706,66 @@ Use old-style drum tracks Använd gamla typen trumspÃ¥r + + Default instrument: + + + + Device Name metas trump Port metas if both exist + + + + Instrument Name metas trump Mode sysexes if both exist + + + + Mode sysexes + + + + Instrument name metas + + + + Both + + + + Port metas + + + + Device name metas + + + + Export a Port/Device meta for format 0 + + + + + CopyOnWriteDialogBase + + Copy Wave Files + + + + Some sound files will be copied to the Project Directory, +either because they are not writable or because more +than one independent Wave Event shares them. +(If you prefer instead that the Wave Events are + inter-dependent, try using Clone Parts.) + +Multiple copies will be made in some cases. + +If no Project has been created yet, you will be asked to, +giving another chance to cancel. + + + + These files will be copied to the Project Directory: + + CrescendoBase @@ -1173,15 +1292,15 @@ Release Rate Utgivningsdatum - + 2° Decay Rate 2° Decay-hastighet - + 1° Decay Level 1° Decay-nivÃ¥ - + 1° Decay Rate 1° Decay-hastighet @@ -1971,7 +2090,7 @@ Copy standard controllers (vol, pan) - Kopiera standardkontroller (vol, pan) + Kopiera standardkontroller (vol, pan) Copy effects rack plugins @@ -1993,6 +2112,10 @@ Copy drumlist Kopiera trumlista + + Copy standard (vol, pan) and synth controllers + + EditCtrlBase @@ -2052,6 +2175,10 @@ &Cancel &Avbryt + + Note + Not + EditGainBase @@ -2146,7 +2273,7 @@ This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. - Detta är en lista med vanligt använda midi-kontroller + Detta är en lista med vanligt använda midi-kontroller Notera att i MusE hanteras tonhöjd och program change som vanliga kontroller. @@ -2156,27 +2283,15 @@ Control7 - Kontroll7 + Kontroll7 Control14 - Kontroll14 - - - RPN - - - - NRPN - + Kontroll14 Pitch - Tonhöjd - - - Program - + Tonhöjd H-Ctrl @@ -2260,11 +2375,11 @@ Exit - Stäng + Stäng E&xit - S&täng + S&täng Instrument Name: @@ -2324,11 +2439,11 @@ Drum patch - Trumpatch + Trumpatch If set, the patch is available only for drum channels. - Om satt sÃ¥ är patchen endast tillgänglig för trumkanaler. + Om satt sÃ¥ är patchen endast tillgänglig för trumkanaler. GM patch @@ -2384,11 +2499,11 @@ Contro&ller - Kontro&ll + Kontro&ll Common: - Vanlig: + Vanlig: List of defined controllers @@ -2423,14 +2538,6 @@ Midi kontrolltyp - RPN14 - - - - NRPN14 - - - Midi controller number high byte Midikontroll-nummer, hög byte @@ -2591,7 +2698,7 @@ Null Param Hi: - Nullparam. hög: + Nullparam. hög: Null parameter number High byte @@ -2664,7 +2771,7 @@ Drummaps - Trummappningar + Trummappningar Patch Collections: @@ -2706,6 +2813,62 @@ Bank Lo: LÃ¥g bank: + + Show in tracks: + + + + Midi + Midi + + + Drum&maps + + + + Contro&llers + + + + Null Parameters: Hi: + + + + &Add Common... + + + + W + + + + Comment: + Kommentar: + + + &Initialization + + + + Instrument initialization sequence: + + + + &Add... + + + + &Change... + + + + &Close + + + + Close + Stäng + EditNoteDialogBase @@ -2758,11 +2921,27 @@ OK - Ok + Ok Cancel - Avbryt + Avbryt + + + Name: + Namn: + + + &Select... + + + + &OK + &Ok + + + &Cancel + &Avbryt @@ -2783,71 +2962,99 @@ FLUIDSynthGuiBase Gain - Förstärkning + Förstärkning Room Size - Rumsstorlek + Rumsstorlek Damping - Dämpning + Dämpning Level - NivÃ¥ + NivÃ¥ Width - Bredd + Bredd Speed - Hastighet + Hastighet Depth - Djup + Djup Number - Nummer + Nummer Type - Typ + Typ Sine - Sinus + Sinus Triangle - Triangel + Triangel Load - Ladda + Ladda Delete - Ta bort + Ta bort Fontname - Fontnamn + Fontnamn Drum Chnl - Trumkanal + Trumkanal CHANNEL SETUP - Kanalkonfiguration + Kanalkonfiguration LOADED SOUNDFONTS - Laddade soundfonts + Laddade soundfonts + + + FLUID Synth + + + + Dump Info + + + + ID + + + + Chnl + + + + Soundfont + + + + Reverb + + + + Chorus + @@ -2878,12 +3085,16 @@ states skriv fönsterinställningar + + Home + + FluidSynthGui Choose soundfont - Välj soundfont + Välj soundfont @@ -3467,12 +3678,119 @@ Prefer new-style drumtracks Föredra nya typen trumspÃ¥r - - - LegatoBase - MusE: Legato - + Auto save (every 5 minutes if not playing/recording) + + + + Instrument initialization + + + + Send instrument initialization sequences + + + + Warn if instrument initialization sequences pending + + + + Send instrument controller default values if none in song, at init or rewind + + + + Warn if timer frequency is inadequate + + + + Track height + + + + Show newly created midi velocity graphs per-note + + + + px + + + + Enable borderless mouse. +For certain functions like zoom/pan. +Disable to use an alternate standard + method. + + + + + Enable borderless mouse. +For certain functions like zoom. +Disable to use an alternate standard + method. + + + + Borderless zoom/pan mouse (else use alternate method) + + + + start &with last song + + + + start with &template + + + + sta&rt with song + + + + &Record all instruments + + + + &Don't record hidden instruments + + + + Don'&t record muted instruments + + + + Don't record &hidden or muted instruments + + + + Scrollable submenus + + + + &Only offer old-style drumtracks + + + + Only offer new-style &drumtracks + + + + &Prefer old-style drumtracks + + + + Prefer &new-style drumtracks + + + + Warn if opening file versions different than current version + + + + + LegatoBase + + MusE: Legato + Range @@ -3700,7 +4018,7 @@ % Audio volume - % ljudvolym + % ljudvolym Hint: Enable metronome in Transportpanel @@ -3718,6 +4036,46 @@ Alt+C + + Two samples (old samples) + + + + Four samples (new samples, with accents) + + + + Volume + Volym + + + Audio master + + + + Meas + + + + Beat + + + + Accent1 + + + + Accent2 + + + + Sample + + + + Disabled since jack does not support it + + MidiAudioControlBase @@ -3739,35 +4097,15 @@ Control7 - Kontroll7 + Kontroll7 Control14 - Kontroll14 - - - RPN - - - - NRPN - - - - RPN14 - - - - NRPN14 - + Kontroll14 Pitch - Tonhöjd - - - Program - + Tonhöjd Hi: @@ -3779,7 +4117,11 @@ Learn - Lär + Lär + + + &Learn + @@ -4240,6 +4582,14 @@ PresetList Presetlista + + Program + + + + Toggle + + MidiSyncConfigBase @@ -4638,6 +4988,30 @@ Change stereo position. Ctrl-double-click on/off. Byt stereo position. Ctrl-dubbelklick av/pÃ¥. + + Change note length in percent of actual length + + + + Offset playback of notes before or after actual note + + + + Transpose notes up or down + + + + <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> + + + + Compress the notes velocity range, in percent of actual velocity + + + + Select instrument + + MidiTransformDialogBase @@ -4969,6 +5343,21 @@ + MusECore::AudioPreviewDialog + + Auto play + + + + Stop + Stopp + + + Play + Spela + + + MusECore::Song Jack shutdown! @@ -5079,6 +5468,26 @@ Externa tempoändringar har spelats in. För över dem till master-tempolistan? + + Do you want to process ALL or only selected events? + + + + &Selected + + + + &All + + + + Und&o + &Ã…ngra + + + Re&do + &Gör om + MusEGui @@ -5269,6 +5678,18 @@ Keep Qt system style BehÃ¥ll Qt systemstil + + Do you really want to reset colors to theme default? + + + + &Ok + &Ok + + + &Cancel + &Avbryt + MusEGui::Arranger @@ -5401,27 +5822,11 @@ Type - Typ - - - NO - - - - GM - - - - GS - - - - XG - + Typ midi song type - typ av midisÃ¥ng + typ av midisÃ¥ng Pitch @@ -5493,6 +5898,45 @@ + MusEGui::ArrangerColumns + + Control7 + Kontroll7 + + + Control14 + Kontroll14 + + + RPN + + + + NPRN + + + + RPN14 + + + + NRPN14 + + + + Pitch + Tonhöjd + + + Program + + + + Aftertouch + + + + MusEGui::ArrangerView MusE: Arranger @@ -5524,7 +5968,7 @@ Paste (show dialog) - Klistra in (visa dialog) + Klistra in (visa dialog) Paste c&lone @@ -5532,7 +5976,7 @@ Paste clone (show dialog) - Klistra in klon (visa dialog) + Klistra in klon (visa dialog) &Insert Empty Measure @@ -5748,16 +6192,32 @@ Are you sure you want to remove this track(s)? - - - MusEGui::AudioMixerApp - &Create - &Skapa + D&elete + - &View - &Vy + Paste to selected &track + + + + Paste clone to selected trac&k + + + + Paste (show dialo&g) + + + + + MusEGui::AudioMixerApp + + &Create + &Skapa + + + &View + &Vy Routing @@ -5811,10 +6271,6 @@ aux send nivÃ¥ - Pan - - - 1/2 channel 1/2 kanal @@ -5878,6 +6334,10 @@ automation type automationstyp + + calibration gain + + MusEGui::BigTime @@ -5919,6 +6379,13 @@ + MusEGui::Canvas + + Tools: + + + + MusEGui::ClipListEdit MusE: Clip List Editor @@ -5995,36 +6462,40 @@ Velocity - Hastighet + Hastighet add new ... - lägg till ny... + lägg till ny... Instrument-defined - Instrumentdefinierad + Instrumentdefinierad Add ... - Lägg till... + Lägg till... Others - Andra + Andra Edit instrument ... - Redigera instrument... + Redigera instrument... Common Controls - Vanliga kontroller + Vanliga kontroller ctrl-double-click on/off kontroll dubbelklick av/pÃ¥ + + all/per-note velocity mode + + MusEGui::DList @@ -6112,35 +6583,35 @@ output channel (hold ctl to affect all rows) - UtgÃ¥ngskanal (hÃ¥ll Ctrl för att ändra alla rader) + UtgÃ¥ngskanal (hÃ¥ll Ctrl för att ändra alla rader) output port (hold ctl to affect all rows) - UtgÃ¥ngsport (hÃ¥ll Ctrl för att ändra alla rader) + UtgÃ¥ngsport (hÃ¥ll Ctrl för att ändra alla rader) shift + control key: draw velocity level 1 - shift + kontrolltangent: rita anslag nivÃ¥ 1 + shift + kontrolltangent: rita anslag nivÃ¥ 1 control key: draw velocity level 2 - kontrolltangent: rita + kontrolltangent: rita shift key: draw velocity level 3 - Shift: Rita hastighet 3 + Shift: Rita hastighet 3 draw velocity level 4 - Rita hastighet 4 + Rita hastighet 4 output channel (ctl: affect all rows) - UtgÃ¥ngskanal. (CTRL pÃ¥verkar alla spÃ¥r) + UtgÃ¥ngskanal. (CTRL pÃ¥verkar alla spÃ¥r) output port (ctl: affect all rows) - UtgÃ¥ng (CTRL pÃ¥verkar alla spÃ¥r) + UtgÃ¥ng (CTRL pÃ¥verkar alla spÃ¥r) &File @@ -6443,20 +6914,56 @@ I'll only display the old-style drumtracks in this editor, dropping the others. + + override track output channel (hold ctl to affect all rows) + + + + override track output port (hold ctl to affect all rows) + + + + control + meta keys: draw velocity level 1 + + + + meta key: draw velocity level 2 + + + + draw default velocity level 3 + + + + meta + alt keys: draw velocity level 4 + + + + override track output channel (ctl: affect all rows) + + + + override track output port (ctl: affect all rows) + + + + Play Events + Spela händelser + MusEGui::EditCAfterDialog MusE: Enter Channel Aftertouch - Muse: Skriv in channel aftertouch + Muse: Skriv in channel aftertouch Time Position - Tidsposition + Tidsposition Pressure - Tryck + Tryck @@ -6538,15 +7045,15 @@ MusE: Cannot add common controller - Muse: Kan inte lägga till vanlig kontroll + Muse: Kan inte lägga till vanlig kontroll A controller named '%1' already exists. - Kontroll med namn %1 finns redan. + Kontroll med namn %1 finns redan. A controller number %1 already exists. - Kontroll med nummer %1 finns redan. + Kontroll med nummer %1 finns redan. MusE @@ -6610,6 +7117,63 @@ LV4 + + Control7 + Kontroll7 + + + Control14 + Kontroll14 + + + RPN + + + + NPRN + + + + RPN14 + + + + NRPN14 + + + + Pitch + Tonhöjd + + + Program + + + + PolyAftertouch + + + + Aftertouch + + + + Tick + kryssa + + + Data + Data + + + New controller: Error + + + + Error! All control numbers are taken up! +Clean up the instrument! + + MusEGui::EditMetaDialog @@ -6634,19 +7198,19 @@ MusEGui::EditPAfterDialog MusE: Enter Poly Aftertouch - MusE: Mata in Poly Aftertouch + MusE: Mata in Poly Aftertouch Time Position - Tidsposition + Tidsposition Pitch - Tonhöjd + Tonhöjd Pressure - Tryck + Tryck @@ -6721,7 +7285,7 @@ Cursor tool - Markören verktyg + Markören verktyg pointer @@ -6771,6 +7335,38 @@ Edit Tools Redigeringsverktyg + + select Cursor (tracker mode) tool: +with the cursor tool you can: + navigate with arrow keys + use VBNM to place notes + change step with 0 and 9 + + + + select Range Tool + + + + select Panning Tool + + + + select Zoom Tool + + + + range + + + + pan + + + + zoom + + MusEGui::EffectRack @@ -6826,6 +7422,10 @@ Do you really want to replace the effect %1? Vill du verkligen ersätta effekten %1? + + Presets + Preset + MusEGui::GlobalSettingsConfig @@ -6972,11 +7572,11 @@ insert Channel Aftertouch - Mata in Aftertouch för kanal + Mata in Aftertouch för kanal insert Poly Aftertouch - Mata in Poly Aftertouch + Mata in Poly Aftertouch &Edit @@ -7071,7 +7671,7 @@ MusEGui::MPConfig Default input connections - Standard ingÃ¥ng anslutning + Standard inputs Are you sure you want to apply to all existing midi tracks now? @@ -7079,41 +7679,41 @@ Default output connections - Standard utgÃ¥ngsanslutning + Standard outputs Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? - Den sjuste gäller nya MIDI-spÃ¥r. -Vill du att gälla för alla befintliga MIDI-spÃ¥r? + Inställningen gäller för nya MIDI-spÃ¥r. +Vill du ocksÃ¥ använda för alla befintliga MIDI-spÃ¥r? MusE: bad device name - Muse: Enhetsnamn fel + Muse: Felaktigt enhetsnamn please choose a unique device name - Enhetens namn finns redan + Välj ett unikt enhetsnamn in - Entry + in out - Utsignal + ut Show first aliases - Visa första alias + Visa först alias Show second aliases - Visa de alias efter + Visa andra alias Toggle all - ändra alla + Ändra alla Change all tracks now @@ -7121,7 +7721,7 @@ Create Jack device - Skapa anslutning Jack + Skapa Jackanslutning Port Number @@ -7129,19 +7729,19 @@ Enable gui - Aktivera visa + Aktivera gui Enable reading - Möjliggöra läsning + Aktivera läsning Enable writing - Aktivera skriva + Aktivera skrivning Port instrument - Redskapsöppning + Instrument-port Midi device name. Click to edit (Jack) @@ -7149,23 +7749,23 @@ Connections from Jack Midi outputs - MIDI utgÃ¥ngar av Jack + MIDI utgÃ¥ngar frÃ¥n Jack Connections to Jack Midi inputs - MIDI anslutningar av Jack + MIDI ingÃ¥ngar frÃ¥n Jack Auto-connect these channels to new midi tracks - Anslut automatiskt till dessa nya spÃ¥r midi-kanaler + Anslut automatiskt dessa kanaler till nya midi-spÃ¥r Auto-connect new midi tracks to these channels - Conectar nuevas pistas MIDI a estos canales + Anslut automatiskt dessa midi spÃ¥r till dessa kanaler Auto-connect new midi tracks to this channel - AutoConnect nya MIDI-spÃ¥r till den här kanalen + Koppla automatiskt nya MIDI-spÃ¥r till den här kanalen Device state @@ -7173,19 +7773,19 @@ Enable gui for device - Aktivera visa för enheten + Aktivera gui för enheten Enable reading from device - Möjliggöra läsning frÃ¥n anordningen + Aktivera läsning frÃ¥n enheten Enable writing to device - Aktivera skriva till enheten + Aktivera skrivning till enheten Name of the midi device associated with this port number. Click to edit Jack midi name. - MIDI enhetsnamn i samband med denna port. Klicka för att ändra MIDI enhetsnamnet Jack. + MIDI enhetsnamn associerat med detta portnummer. Klicka för att ändra MIDI enhetsnamnet i Jack. Instrument connected to port @@ -7193,47 +7793,47 @@ Connections from Jack Midi output ports - Anslutningar MIDI utgÃ¥ngsportar av Jack + Anslutningar frÃ¥n Jack MIDI utgÃ¥ngsportar Connections to Jack Midi input ports - Anslutningar MIDI ingÃ¥ngsportar av Jack + Anslutningar till Jack MIDI ingÃ¥ngsportar Auto-connect these channels, on this port, to new midi tracks. - Automatisk anslutning av dessa kanaler, i denna hamn, nya MIDI spÃ¥r. + Anslut automatisks dessa kanaler, pÃ¥ denna port, till nya MIDI spÃ¥r. Connect new midi tracks to these channels, on this port. - Anslut nya MIDI-spÃ¥r till dessa kanaler, i denna port. + Anslut nya MIDI-spÃ¥r till dessa kanaler, för denna port. Connect new midi tracks to this channel, on this port. - Anslut MIDI-spÃ¥r nya för den här kanalen pÃ¥ den här porten. + Anslut nya MIDI-spÃ¥r till denna kanal, pÃ¥ den här porten. State: result of opening the device - Status: resultat av öppning av enhet + Status: resultat av enhetsöppning Port - Porten + Port GUI - Gränssnitt + I - PÃ¥ + O - Av + Instrument - Instrument + Instrument Device Name @@ -7245,19 +7845,19 @@ Out routes - Utrymningsvägar + UtgÃ¥ngsvägar Def in ch - Definiera ingÃ¥ngskanaler + Std inkanal Def out ch - UtgÃ¥ngskanaler fastställa + Std utg kanal State - TillstÃ¥nd + TillstÃ¥nd <unknown> @@ -7267,6 +7867,64 @@ <none> <Inget> + + Remove + Ta bort + + + Midi device name + + + + Midi device type + + + + Connections from Jack Midi + + + + Connections to Jack Midi + + + + Result of opening the device: +OK: Assigned to a port and in use +Closed: Unassigned to a port, or closed +R/W Error: Unable to open for read or write +Unavailable: USB midi unplugged, or external + application not running, or synth plugin + not installed etc. +(Jack Midi devices have 'unavailable ports' + in the routes columns.) +Unavailable devices or ports can be purged + with 'Remove' or with the advanced router. + + + + Enable Graphical User Interface for device + + + + Connections from Jack Midi ports + + + + Connections to Jack Midi ports + + + + Type + Typ + + + In + + + + Out + + MusEGui::MTScale @@ -7417,6 +8075,45 @@ + MusEGui::MidiAudioControl + + Control7 + Kontroll7 + + + Control14 + Kontroll14 + + + RPN + + + + NPRN + + + + RPN14 + + + + NRPN14 + + + + Pitch + Tonhöjd + + + Program + + + + Aftertouch + + + + MusEGui::MidiInputTransformDialog New @@ -7980,7 +8677,7 @@ Importera Part - Import Wave File + Import Audio File Importera ljudfil @@ -8463,55 +9160,144 @@ nuvarande positionen. Försätt? - - - MusEGui::NoteInfo - Note Info - Notinfo + turn on/off metronome + - Start - Starta + MusE: Song: + - Len - Längd + MusE: Warning + - Pitch - Tonhöjd + The song uses multiple ports but export format 0 (single track) is set. +The first track's port will be used. Playback will likely be wrong + unless the channels used in one port are different from all other ports. +Canceling and setting a different export format would be better. +Continue? + - Velo On - pÃ¥ hastighet + This wave file has a samplerate of %1, +as opposed to current setting %2. +File will be resampled from %1 to %2 Hz. +Do you still want to import it? + - Velo Off - maximivarvtal + Wave import error + - delta/absolute mode - delta/absolut-läge + There are too many wave files +of the same base name as imported wave file +Can not continue. + - - - MusEGui::PartCanvas - Cannot copy/move/clone to different Track-Type - Kan inte kopiera/flytta/klona till annan SpÃ¥rtyp + Can't create new wav file in project folder! + + - C&ut - Klipp &ut + Failed to initialize sample rate converter! + - &Copy - &Kopiera + Cancel + Avbryt - s&elect - &Välj + Resampling wave file +"%1" +from %2 to %3 Hz... + + + + Output has clipped +Resampling again and normalizing wave file +"%1" +Try %2 of %3... + + + + File version is %1.%2 +Current version is %3.%4 +Conversions may be applied if file is saved! + + + + Opening file + + + + Do not warn again + + + + Cpu load + + + + Measured CPU load + + + + No CPU load data + + + + + MusEGui::NoteInfo + + Note Info + Notinfo + + + Start + Starta + + + Len + Längd + + + Pitch + Tonhöjd + + + Velo On + pÃ¥ hastighet + + + Velo Off + maximivarvtal + + + delta/absolute mode + delta/absolut-läge + + + + MusEGui::PartCanvas + + Cannot copy/move/clone to different Track-Type + Kan inte kopiera/flytta/klona till annan SpÃ¥rtyp + + + C&ut + Klipp &ut + + + &Copy + &Kopiera + + + s&elect + &Välj clones @@ -8613,6 +9399,18 @@ Remove selected Ta bort valt + + Part: + + + + Automation: + Automation: + + + Normalize + + MusEGui::PasteDialog @@ -8869,27 +9667,19 @@ Ok - Ok + Ok Cancel - Avbryt + Avbryt Mono and Stereo - Mono och stereo - - - Stereo - - - - Mono - + Mono och stereo Show All - Visa alla + Visa alla Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallell.<br>Also beware that the 'all' alternative includes plugins that probably not are usable by MusE. @@ -8897,7 +9687,7 @@ Search in 'Label' and 'Name': - Sök i 'Etikett' och 'Namn': + Sök i 'Etikett' och 'Namn': Type @@ -8945,7 +9735,53 @@ Show plugs: - Visa instickseffekter: + Visa instickseffekter: + + + Plugin categories. +Right-click on tabs to manage. +Right-click on plugins to add/remove from a category. + + + + &create new group + + + + &delete currently selected group + + + + re&name currently selected group + + + + Associated categories + + + + You need to define some categories first. + + + + new group + + + + Enter the new group name + + + + Wine VST + + + + LV2 synth + + + + LV2 effect + @@ -8980,6 +9816,13 @@ + MusEGui::PopupMenu + + <More...> %1 + + + + MusEGui::ProjectCreateImpl Select directory @@ -9086,6 +9929,33 @@ + MusEGui::RouteDialog + + Normal + + + + Alias 1 + + + + Alias 2 + + + + Source + Källa + + + Destination + + + + <none> + + + + MusEGui::RoutePopupMenu Channel @@ -9127,6 +9997,94 @@ Midi port sends Midiport-sänd + + Channel grouping: + + + + Mono + + + + Stereo + + + + Channels + + + + Midi ports/devices + + + + Omni + + + + Show aliases: + + + + First + + + + Second + + + + Show names + + + + Show first aliases + Visa först alias + + + Show second aliases + Visa andra alias + + + Jack ports + + + + Connect + Koppla + + + Unavailable + + + + Open advanced router... + + + + Output routes: + + + + Input routes: + + + + Tracks + + + + Destinations: + + + + Midi sends + + + + Sources: + + MusEGui::ScoreCanvas @@ -9407,6 +10365,38 @@ + MusEGui::ShortcutConfig + + Save printable text file + + + + Text files (*.txt);;All files (*) + + + + Error + Fel + + + Error opening file for saving + + + + Shortcuts for selected category: + + + + Legend: + + + + + An error occurred while saving + + + + MusEGui::SigScale signature scale @@ -9471,19 +10461,19 @@ Update drummap? - Updatera trum-map? + Updatera trum-map? Do you want to use same port for all instruments in the drummap? - Vill du använda samma port för alla instrument i trum-map:en? + Vill du använda samma port för alla instrument i trum-map:en? &Yes - &Ja + &Ja &No - &Nej + &Nej show gui @@ -9531,7 +10521,7 @@ Do you want to use same port and channel for all instruments in the drummap? - Vill du använda samma port och kanal för alla instrument i trummap:en? + Vill du använda samma port och kanal för alla instrument i trummap:en? off @@ -9613,6 +10603,44 @@ New style drum + + Presets + Preset + + + Clear all controller events? + Ta bort alla kontrollerhändelser? + + + &Ok + &Ok + + + &Cancel + &Avbryt + + + Change color + + + + Other + Andra + + + clear automation + rensa automation + + + Drummap + + + + This drummap was created with a previous version of MusE, +it is being read but the format has changed slightly so some +adjustments may be necessary. + + MusEGui::TempoSig @@ -9733,6 +10761,10 @@ Signature Signatur + + Metronome + Metronom + MusEGui::TrackComment @@ -9935,6 +10967,61 @@ + MusEGui::WaveCanvas + + Adjust Wave Offset + + + + Wave offset (frames) + + + + Part creation failed + + + + Left and right position markers must be placed inside the current part. + + + + Part created + + + + The selected region has been copied to the clipboard and can be pasted in the arranger. + + + + MusE - external editor failed + MusE - fel med extern redigerare + + + MusE was unable to launch the external editor +check if the editor setting in: +Global Settings->Audio:External Waveditor +is set to a valid editor. + MusE kunde inte starta extern redigerare +kontrollera om inställningarna i: +Globala inställningar->Ljud:Extern ljudredigerare +är satt till en giltig redigerarbinär. + + + MusE - file size changed + MusE - filstorleken har förändrats + + + When editing in external editor - you should not change the filesize +since it must fit the selected region. + +Missing data is muted + Vid editering i extern editor - bör du inte ändra filstorleken +eftersom den mÃ¥ste passa i regionen. + +Saknat data tystas + + + MusEGui::WaveEdit &Edit @@ -10024,33 +11111,57 @@ Cursor Markör + + &Create Part from Region + + + + &Previous Part + &FöregÃ¥ende part + + + &Next Part + &Nästa part + + + &Event Color + &Händelsefärg + + + &Part colors + + + + &Gray + + MusEGui::WaveView MusE - external editor failed - MusE - fel med extern redigerare + MusE - fel med extern redigerare MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. - MusE kunde inte starta extern redigerare + MusE kunde inte starta extern redigerare kontrollera om inställningarna i: Globala inställningar->Ljud:Extern ljudredigerare är satt till en giltig redigerarbinär. MusE - file size changed - MusE - filstorleken har förändrats + MusE - filstorleken har förändrats When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted - Vid editering i extern editor - bör du inte ändra filstorleken + Vid editering i extern editor - bör du inte ändra filstorleken eftersom den mÃ¥ste passa i regionen. Saknat data tystas @@ -10271,62 +11382,193 @@ 1 kvart - 1 quarter - 1 kvart + 1 quarter + 1 kvart + + + + PluginDialogBase + + Dialog + + + + Ports: + + + + Mono + + + + Mono + Stereo + + + + Stereo + + + + All + Alla + + + Plugin type: + + + + Filter: + + + + &OK + &Ok + + + &Cancel + &Avbryt + + + + ProjectCreate + + Create Project + Skapa projekt + + + Projects folder: + Projektkatalog: + + + ... + ... + + + Project Name: + Projektnamn: + + + Project is a Template + Projekt är en mall + + + Project song file type: + SÃ¥ng-filtyp för projekt: + + + Project Path to song file: + Projektsökväg till sÃ¥ngfil: + + + Create project folder (recommended for audio projects) + Skapa projektkatalog (rekommenderat för ljudprojekt) + + + Song information: + SÃ¥nginformation: + + + Write window state + Skriv fönsterinställningar + + + &Create project folder (recommended for audio projects) + + + + + QObject + + Error + Fel + + + Please first select the range for crescendo with the loop markers. + Välj omrÃ¥de för crescendo med loopmarkörerna. + + + Controller ! + + + + Other ! + + + + No presets found + + + + Switch on General Midi Level 1 mode + + + + Switch on General Midi Level 2 mode + + + + Switch off General Midi Level 1 or 2 + + + + Switch on Roland GS mode + + + + Switch on Yamaha XG mode + + + + Select gui type + - - - ProjectCreate - Create Project - Skapa projekt + Preset actions + - Projects folder: - Projektkatalog: + Save preset... + - ... - ... + Update list + - Project Name: - Projektnamn: + Saved presets + - Project is a Template - Projekt är en mall + Enter new preset name + - Project song file type: - SÃ¥ng-filtyp för projekt: + Midi programs + - Project Path to song file: - Projektsökväg till sÃ¥ngfil: + Presets + Preset - Create project folder (recommended for audio projects) - Skapa projektkatalog (rekommenderat för ljudprojekt) + Tracks: + - Song information: - SÃ¥nginformation: + Midi ports: + - Write window state - Skriv fönsterinställningar + Midi devices: + - - - QObject - Error - Fel + Jack: + - Please first select the range for crescendo with the loop markers. - Välj omrÃ¥de för crescendo med loopmarkörerna. + Jack midi: + @@ -10405,6 +11647,34 @@ No selection. Ignoring Inget valt, ignorerar + + Instrument-defined + Instrumentdefinierad + + + Edit instrument ... + Redigera instrument... + + + Add + + + + Others + Andra + + + Common Controls + Vanliga kontroller + + + Velocity + + + + No wave events selected. + + QuantBase @@ -10848,10 +12118,6 @@ Källa: - Destination: - - - Connect Koppla @@ -10868,10 +12134,6 @@ Källa - Destination - - - Remove Ta bort @@ -10879,6 +12141,79 @@ remove selected route Ta bort vald koppling + + List of available sources. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + Connections View window. +Shows all current connections. +Thick lines are Omni Routes. +Thin lines are Channel Routes. + + + + List of available destinations. +Connect a source to a destination. +For items having a channel bar, + connections can be Omni Mode + (the textual item) or Channel Mode + (the Channel bar channels). + + + + Itemized list of current connections. + + + + Show only selected sources + + + + Show only destination routes for selected source + + + + Show only selected destinations + + + + Show only source routes for selected destination + + + + Show all Midi Ports + + + + Auto adjust column size + + + + Automatically adjusts the source and destination + tree widths when the splitters are adjusted. +This also turns on text word wrap, which may + cause slower response with larger lists. + + + + + Preferred route name or alias + + + + Connect source to destination + + + + Remove selected route + + SS_PluginChooserBase @@ -11062,6 +12397,22 @@ Alt+A Alt+L + + &Printable file... + + + + Alt+P + Alt+P + + + &Ok + &Ok + + + Alt+O + + SimpleDrumsGuiBase @@ -11084,6 +12435,18 @@ Load sample dialog + + Mix + + + + Chn + + + + Channel routing + + SongInfo @@ -11170,6 +12533,54 @@ Description Beskrivning + + &Apply + + + + &OK + &Ok + + + Configure midi devices, midi ports, and synthesizers + + + + Available soft synthesizers + + + + Useable devices + + + + Add: + + + + Synth + Synt + + + ALSA + + + + JACK + + + + Rename + + + + Remove + Ta bort + + + Device port assignments + + TimeCanvas @@ -11393,7 +12804,7 @@ DCO 2 - + VAM 1.0beta3 Virtual Analog for MusE Released under GPL. @@ -11576,16 +12987,16 @@ bzip2-komprimerade presets (*.map.bz2) - Wave/Binary (*.wav *.ogg *.bin) + Binary (*.bin) - Wave (*.wav *.ogg) - + Wave/Binary (*.wav *.ogg *.flac *.bin) + - Binary (*.bin) - + Wave (*.wav *.ogg *.flac) + @@ -11820,6 +13231,29 @@ + midiWarnInitPendingBase + + Instrument initialization + + + + MusE should now send some Instrument Initialization Sequences. +The sequences (usually System Exclusive messages) are defined + by the selected instruments in the Settings -> Midi Ports dialog, + such as the GM (default), GS, or XG instruments. + +Typically you should answer yes here. +You can always do it manually from the Midi menu. + +Continue? + + + + Don't ask me again + + + + shortcuts Transport: Start playback from current location @@ -12175,11 +13609,11 @@ Edit: Paste as clones - Redigera: Klistra in som kloner + Redigera: Klistra in som kloner Edit: Paste as clones (with dialog) - Redigera: Klistra in som kloner (med dialog) + Redigera: Klistra in som kloner (med dialog) Select track above @@ -12521,5 +13955,84 @@ Add new style drum track + + Edit: Paste to selected track + + + + Edit: Paste clone + + + + Edit: Paste clone to selected track + + + + Transport: Step record + + + + Transport: Midi input + + + + Transport: Play events + + + + Edit: Increase velocity + + + + Edit: Decrease velocity + + + + Move: Move to selected note + + + + Tool: Pan + + + + Tool: Zoom + + + + Tool: Range + + + + Edit: Duplicate track + + + + Transport: Restart recording + + + + Edit selected track name + + + + Normalize + + + + + warnBadTimingBase + + Bad timing + DÃ¥lig timing + + + Message here + + + + Don't warn me again + + diff -Nru muse-2.1.2/share/metronome/CMakeLists.txt muse-3.0.2+ds1/share/metronome/CMakeLists.txt --- muse-2.1.2/share/metronome/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/metronome/CMakeLists.txt 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,29 @@ +#============================================================================= +# MusE +# Linux Music Editor +# $Id:$ +# +# Copyright (C) 1999-2014 by MusE dev team +# +# 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; either 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 +#============================================================================= + +file (GLOB metronome_files *.wav) + +install( FILES ${metronome_files} + DESTINATION ${MusE_SHARE_DIR}/metronome + ) + Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/metronome/klick1.wav and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/metronome/klick1.wav differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/metronome/klick2.wav and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/metronome/klick2.wav differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/metronome/klick3.wav and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/metronome/klick3.wav differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/metronome/klick4.wav and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/metronome/klick4.wav differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/metronome/klick5.wav and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/metronome/klick5.wav differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/metronome/klick6.wav and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/metronome/klick6.wav differ diff -Nru muse-2.1.2/share/scoreglyphs/feta-original/feta-din-code.mf muse-3.0.2+ds1/share/scoreglyphs/feta-original/feta-din-code.mf --- muse-2.1.2/share/scoreglyphs/feta-original/feta-din-code.mf 2013-03-28 15:15:49.000000000 +0000 +++ muse-3.0.2+ds1/share/scoreglyphs/feta-original/feta-din-code.mf 2018-01-26 21:59:38.000000000 +0000 @@ -40,7 +40,7 @@ % -% Couldn't find many z examples. This one is losely inspired +% Couldn't find many z examples. This one is loosely inspired % by a sfz from Mueller Etuden fuer Horn (Edition Hofmeister). % diff -Nru muse-2.1.2/share/scoreglyphs/feta-original/feta-params.mf muse-3.0.2+ds1/share/scoreglyphs/feta-original/feta-params.mf --- muse-2.1.2/share/scoreglyphs/feta-original/feta-params.mf 2013-03-28 15:15:49.000000000 +0000 +++ muse-3.0.2+ds1/share/scoreglyphs/feta-original/feta-params.mf 2018-01-26 21:59:38.000000000 +0000 @@ -151,7 +151,7 @@ % . Restore height and decrease `d' by 1. % % o The glyph is centered between two or four staff lines, and the origin is -% the middle of the whitespace. Assuming that the the whitespace consists +% the middle of the whitespace. Assuming that the whitespace consists % of an odd number of pixels, we have this: % % ----------- diff -Nru muse-2.1.2/share/scripts/CMakeLists.txt muse-3.0.2+ds1/share/scripts/CMakeLists.txt --- muse-2.1.2/share/scripts/CMakeLists.txt 2013-03-28 15:15:51.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/CMakeLists.txt 2018-01-22 16:43:28.000000000 +0000 @@ -28,7 +28,11 @@ ConstantLength SwingQuantize1 RandomQuantize1 + RandomPosition1 + RandomizeVelocityRelative + RemoveAftertouch Rhythm1 + TempoDelay ) install (PROGRAMS ${script_files} diff -Nru muse-2.1.2/share/scripts/ConstantLength muse-3.0.2+ds1/share/scripts/ConstantLength --- muse-2.1.2/share/scripts/ConstantLength 2013-03-28 15:15:51.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/ConstantLength 2017-12-04 21:01:19.000000000 +0000 @@ -57,18 +57,18 @@ button.setFocus() def execute(self): - testFile = file(sys.argv[1],"r") + testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() beatDiv = self.titleEdit.itemData(self.titleEdit.currentIndex()).toInt()[0] - print "beatDiv=",beatDiv + print ("beatDiv=",beatDiv) eventLen=0 #get beat length to calculate minimum length of event for line in inputEvents: if line.startswith('BEATLEN'): tag,tick = line.split(' ') - eventLen=int(tick)/beatDiv*4 + eventLen=int(tick/beatDiv*4) break outputEvents=[] @@ -85,7 +85,7 @@ else: outputEvents.append(line) - testFile = file(sys.argv[1],"w") + testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() diff -Nru muse-2.1.2/share/scripts/DoNothing muse-3.0.2+ds1/share/scripts/DoNothing --- muse-2.1.2/share/scripts/DoNothing 2013-03-28 15:15:51.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/DoNothing 2017-12-04 21:01:19.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- #============================================================================= # MusE @@ -24,7 +24,7 @@ #============================================================================= import sys -testFile = file(sys.argv[1],"r") +testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() @@ -33,6 +33,6 @@ for line in inputEvents: outputEvents.append(line) -testFile = file(sys.argv[1],"w") +testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() diff -Nru muse-2.1.2/share/scripts/DoubleSpeed muse-3.0.2+ds1/share/scripts/DoubleSpeed --- muse-2.1.2/share/scripts/DoubleSpeed 2013-03-28 15:15:51.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/DoubleSpeed 2017-12-04 21:01:19.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Mathias Gyllengahm 2009 @@ -27,7 +27,7 @@ #============================================================================= import sys,time -testFile = file(sys.argv[1],"r") +testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() @@ -37,10 +37,12 @@ if line.startswith('NOTE'): tag,tick,pitch,length,velocity = line.split(' ') - newline = tag + " " + str(int(tick)/2) + " " + pitch + " " + length + " " + velocity + print (line) + newline = tag + " " + str(int(int(tick)/2)) + " " + pitch + " " + length + " " + velocity + print (newline) outputEvents.append(newline) -testFile = file(sys.argv[1],"w") +testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() diff -Nru muse-2.1.2/share/scripts/RandomizeVelocityRelative muse-3.0.2+ds1/share/scripts/RandomizeVelocityRelative --- muse-2.1.2/share/scripts/RandomizeVelocityRelative 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/RandomizeVelocityRelative 2018-01-22 16:43:28.000000000 +0000 @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# MusE external midi processing script +# By: Michael Oswald 2018 +# Randomize the Velocity relative to it's current position +#============================================================================= +# MusE +# Linux Music Editor +# $Id:$ +# +# Copyright (C) 2002-2011 by Werner Schweer and others +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either 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 +#============================================================================= + + +import sys,time +from PyQt4 import QtGui, QtCore +import random + +class Quantize(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + + self.setWindowTitle('Randomize note velocity relative to it\'s current velocity') + + self.spreadEdit = QtGui.QLineEdit() + self.spreadEdit.setText('3') + + button = QtGui.QPushButton("Execute") + self.connect(button, QtCore.SIGNAL('clicked()'), self.execute) + + grid = QtGui.QGridLayout() + grid.setSpacing(3) + + grid.addWidget(QtGui.QLabel('Spread'), 2, 0) + grid.addWidget(self.spreadEdit, 2, 1) + grid.addWidget(button, 3, 1) + + self.setLayout(grid) + self.resize(300, 150) + button.setFocus() + + def execute(self): + testFile = open(sys.argv[1],"r") + inputEvents = testFile.readlines() + testFile.close() + + minSize=0 # fill in when we get the beat size value + outputEvents=[] + quantLen=0 + random.seed() + + #loop through events and quantize to the given beat + eventList=[] + for line in inputEvents: + if line.startswith('NOTE'): + tag,tick,pitch,length,velocity = line.split(' ') + + newVelo = int(velocity) + # apply random value to velocity + newVelo=int(random.gauss(newVelo,int(self.spreadEdit.text()))) + if newVelo < 0: + newVelo = 1 + if newVelo > 127: + newVelo = 127 + + newLine="NOTE "+ tick + " " + pitch + " "+ length + " " + str(newVelo) + "\n" + print ("oldVelo:", velocity) + print ("newLine:",newLine.strip()) + outputEvents.append(newLine) + else: + outputEvents.append(line) + + + + testFile = open(sys.argv[1],"w") + testFile.writelines(outputEvents) + testFile.close() + + quit() + + +app = QtGui.QApplication(sys.argv) +qb = Quantize() +qb.show() +sys.exit(app.exec_()) diff -Nru muse-2.1.2/share/scripts/RandomPosition1 muse-3.0.2+ds1/share/scripts/RandomPosition1 --- muse-2.1.2/share/scripts/RandomPosition1 2013-03-28 15:15:51.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/RandomPosition1 2018-01-22 16:43:28.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2013 @@ -55,7 +55,7 @@ button.setFocus() def execute(self): - testFile = file(sys.argv[1],"r") + testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() @@ -71,19 +71,21 @@ tag,tick,pitch,length,velocity = line.split(' ') newTick = int(tick) - # apply swing factor to every beat - newTick=int(random.gauss(newTick,self.spreadEdit.text().toInt()[0])) + # apply randomness to the note position + newTick=int(random.gauss(newTick,int(self.spreadEdit.text()))) + if newTick < 0: + newTick = 0 newLine="NOTE "+ str(newTick)+" " + pitch + " "+ length + " " + velocity - print "oldTick:", tick - print "newLine:",newLine.strip() + print ("oldTick:", tick) + print ("newLine:",newLine.strip()) outputEvents.append(newLine) else: outputEvents.append(line) - testFile = file(sys.argv[1],"w") + testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() diff -Nru muse-2.1.2/share/scripts/README.txt muse-3.0.2+ds1/share/scripts/README.txt --- muse-2.1.2/share/scripts/README.txt 2013-03-28 15:15:51.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/README.txt 2017-12-04 21:01:19.000000000 +0000 @@ -1,36 +1,91 @@ -MusE midi event scripting format 0.5 +MusE midi event scripting format 0.6 +==================================== -Some information for the budding script writer, here is some info -about the format currently used. - -Scripts can be put in two different dirs. -/share/muse/scripts -for scripts bundled -or $HOME/.muse/scripts -for user created scripts += INTRODUCTION +MusE supports a simple format + += SPECIFICATION + +The format of the scripts is just a set of text lines which deliberately +has been kept very brief and should be possible to handle from any programming +language, Python, Perl, C/C++, even shell script. Though for debugging +reasons an interpreted language is probably recommended. + There are two main requirements on scripts. -1. a script must have the executable flag set, that is, it must be considered +1. the actual script file must have the executable flag set, that is, it must be considered an executable from the perspective of the operating system. -2. a script shall take an input file as argument and will update this -file with the sought output. +2. a script takes it's input from a file given as argument and will send it's output +to the same file. The tags that may occur in the file sent to the script are: -PARTLEN -BEATLEN -QUANTLEN +TIMESIG +PARTLEN +BEATLEN +QUANTLEN NOTE CONTROLLER -PARTLEN, BEATLEN and QUANTLEN are there for informational purposes, to +TIMESIG, PARTLEN, BEATLEN and QUANTLEN are there for informational purposes, to make some transformations possible. e.g. quantization, beat delay. NOTE and CONTROLLER are the ones that are read back into MusE when the filter stops executing. These may be manipulated, removed or multiplied as seen fit by the filter. --- Note that it is a good idea to just pass on the lines your script is not -interested in, otherwise data may unintentionally be removed -- -A short example in pyton that does nothing but pass on output from input -to output is available in script DoNothing +* Note that it is generally a good idea to just pass on the lines your script +is not interested in, otherwise data may be unintentionally removed + += INSTALLATION + +Scripts can be put in two different dirs. +/share/muse/scripts +for scripts bundled +or $HOME/.config/MusE/scripts +for user created scripts + +Remember that a script must have the executable bit set. This means +that the script must be executable -on it's own-. For an interpreted +language that means the file should start with a hashbang (#!) and the path +to it's interpreter. +e.g +#!/usr/bin/python + += EXAMPLES + +There are a few existing scripts in this directory that can be used as +templates for creating more advanced scripts. + +- DoNothing, just reads the input file, loops through the lines and outputs + them again. +- DoubleSpeed, reads all notes and outputs them with all ticks divided + in half. +- RemoveShortEvents, a script with a GUI in PyQt which removes events + shorter then the set note length + +For the moment there are only scripts written in python in the standard +installation. + += DEBUGGING + +The process of debugging a script may seem daunting. There is no immediate way +to retrieve information about the internals of the script. + +Currently the standard output is suppressed from the scripts so while running +from MusE the script won't make a peep in the console. +if MusE is started with -D argument the contents of the data file is written +to standard out both before it enters the script and after it returns, this +can be used as a means for communication. +The file containing the script data is normally removed but when running +MusE with -D it will be kept. +So, when the file exists you can use it and run the script from a command +line, remember though that the file will be overwritten by the script. + +Running the script like this makes it a bit easier: +cp origData testData && MyTestScript testData +Then the data can be compared to the original and prints in the script can +be read on the commandline. + += END + diff -Nru muse-2.1.2/share/scripts/RemoveAftertouch muse-3.0.2+ds1/share/scripts/RemoveAftertouch --- muse-2.1.2/share/scripts/RemoveAftertouch 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/RemoveAftertouch 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +#============================================================================= +# MusE +# Linux Music Editor +# $Id:$ +# +# Copyright (C) 2002-2014 by Werner Schweer and others +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either 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 +#============================================================================= + +import sys +testFile = open(sys.argv[1],"r") +inputEvents = testFile.readlines() +testFile.close() + +outputEvents=[] +#loop through events +for line in inputEvents: + print (line.strip()) + if line.startswith("CONTROLLER")==True and int(line.split()[2]) == 262148: + print ("skipping ",line) + pass + else: + outputEvents.append(line) + +testFile = open(sys.argv[1],"w") +testFile.writelines(outputEvents) +testFile.close() diff -Nru muse-2.1.2/share/scripts/RemoveDoubles muse-3.0.2+ds1/share/scripts/RemoveDoubles --- muse-2.1.2/share/scripts/RemoveDoubles 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/RemoveDoubles 2018-01-26 21:59:38.000000000 +0000 @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# MusE external midi processing script +# By: Robert Jonsson (c) 2013 +# RemoveDoubles +#============================================================================= +# MusE +# Linux Music Editor +# $Id:$ +# +# Copyright (C) 2002-2013 by Werner Schweer and others +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either 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 +#============================================================================= + +import sys,time +testFile = open(sys.argv[1],"r") +inputEvents = testFile.readlines() +testFile.close() + +outputEvents=[] +#loop through events and omit doubles +for line in inputEvents: + if line.startswith('NOTE'): + tag,tick,pitch,length,velocity = line.split(' ') + found=False + for outline in outputEvents: + outtag,outtick,outpitch,outlength,outvelocity = outline.split(' ') + if tick == outtick and pitch == outpitch and outvelocity == outvelocity: + found=True + break + if not found: + newline = tag + " " + str(int(tick)) + " " + pitch + " " + length + " " + velocity + outputEvents.append(newline) + +testFile = open(sys.argv[1],"w") +testFile.writelines(outputEvents) +testFile.close() + diff -Nru muse-2.1.2/share/scripts/RemoveShortEvents muse-3.0.2+ds1/share/scripts/RemoveShortEvents --- muse-2.1.2/share/scripts/RemoveShortEvents 2013-03-28 15:15:51.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/RemoveShortEvents 2017-12-04 21:01:19.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2009 @@ -62,11 +62,12 @@ button.setFocus() def execute(self): - testFile = file(sys.argv[1],"r") + print ("Execute") + testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() - beatDiv = int(self.timeEdit.itemData(self.timeEdit.currentIndex()).toInt()[0]) + beatDiv = self.timeEdit.itemData(self.timeEdit.currentIndex()) minSize=0 # fill in when we get the beat size value outputEvents=[] @@ -74,20 +75,21 @@ for line in inputEvents: if line.startswith('BEATLEN'): tag,tick = line.split(' ') - minSize=int(tick)/beatDiv + minSize=int(tick)/beatDiv * 4 + print ("minSize = ",minSize) break #loop through events for line in inputEvents: if line.startswith('NOTE'): tag,tick,note,length,velocity = line.split(' ') - if int(length) > minSize: # only append long enough events + if int(length) >= minSize: # only append long enough events outputEvents.append(line) else: outputEvents.append(line) - testFile = file(sys.argv[1],"w") + testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() diff -Nru muse-2.1.2/share/scripts/Rhythm1 muse-3.0.2+ds1/share/scripts/Rhythm1 --- muse-2.1.2/share/scripts/Rhythm1 2013-03-28 15:15:51.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/Rhythm1 2017-12-04 21:01:19.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2012 @@ -27,7 +27,7 @@ #============================================================================= import sys,time -testFile = file(sys.argv[1],"r") +testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() @@ -77,7 +77,7 @@ newline = "NOTE " + str(ticking) + " " + hihatNote + " " + length + " " + velocity + "\n" outputEvents.append(newline) -testFile = file(sys.argv[1],"w") +testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() diff -Nru muse-2.1.2/share/scripts/SwingQuantize1 muse-3.0.2+ds1/share/scripts/SwingQuantize1 --- muse-2.1.2/share/scripts/SwingQuantize1 2013-03-28 15:15:51.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/SwingQuantize1 2018-01-06 20:31:35.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2009 @@ -66,12 +66,12 @@ button.setFocus() def execute(self): - testFile = file(sys.argv[1],"r") + testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() random.seed() - beatDiv = self.beatEdit.itemData(self.beatEdit.currentIndex()).toInt()[0] + beatDiv = self.beatEdit.itemData(self.beatEdit.currentIndex()) minSize=0 # fill in when we get the beat size value outputEvents=[] quantLen=0 @@ -82,8 +82,8 @@ tag,tick = line.split(' ') beatLen=int(tick) quantLen=int(tick)/beatDiv*4 - print "quantLen=%d beatDiv=%d"%(quantLen, beatDiv) - print line.strip() + print ("quantLen=%d beatDiv=%d"%(quantLen, beatDiv)) + print (line.strip()) #loop through events and quantize to the given beat eventList=[] for line in inputEvents: @@ -101,22 +101,22 @@ newTick=upperBound # apply swing factor to every other 8 beat - print "float =%f int = %d"%((float(newTick+beatLen)) / beatLen/2,((newTick+beatLen))/beatLen/2) + print ("float =%f int = %d"%((float(newTick+beatLen)) / beatLen/2,((newTick+beatLen))/beatLen/2)) if ((float(newTick+beatLen)) / beatLen/2 - ((newTick+beatLen))/beatLen/2) < 0.1: - print "adding swing to:",newTick - newTick=int(random.gauss(newTick,self.spreadEdit.text().toInt()[0])) + print ("adding swing to:",newTick) + newTick=int(random.gauss(newTick,int(self.spreadEdit.text()))) if (newTick < 0): newTick=0 newLine="NOTE "+ str(newTick)+" " + pitch + " "+ length + " " + velocity - print "newLine:",newLine.strip() + print ("newLine:",newLine.strip()) outputEvents.append(newLine) else: outputEvents.append(line) - testFile = file(sys.argv[1],"w") + testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() diff -Nru muse-2.1.2/share/scripts/TempoDelay muse-3.0.2+ds1/share/scripts/TempoDelay --- muse-2.1.2/share/scripts/TempoDelay 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/scripts/TempoDelay 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# MusE external midi processing script +# By: Robert Jonsson 2014 +# TEMPO delay +#============================================================================= +# MusE +# Linux Music Editor +# $Id:$ +# +# Copyright (C) 2002-2011 by Werner Schweer and others +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either 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 +#============================================================================= + + +import sys,time +from PyQt4 import QtGui, QtCore + +class TempoDelay(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + + self.setWindowTitle('Tempo delay') + + title = QtGui.QLabel('Delay time:') + self.timeEdit = QtGui.QComboBox() + self.timeEdit.addItem('1/1',1) + self.timeEdit.addItem('1/2',2) + self.timeEdit.addItem('1/4',4) + self.timeEdit.addItem('1/8',8) + self.timeEdit.addItem('1/16',16) + self.timeEdit.addItem('1/32',32) + self.timeEdit.setCurrentIndex(3) + + numLabel = QtGui.QLabel('Number of delays:') + self.numEntry = QtGui.QSpinBox() + self.numEntry.setMinimum(1) + self.numEntry.setValue(1) + + print ("setting execute") + button = QtGui.QPushButton("Execute") + self.connect(button, QtCore.SIGNAL('clicked()'), self.execute) + + grid = QtGui.QGridLayout() + grid.setSpacing(3) + + grid.addWidget(title, 1, 0) + grid.addWidget(self.timeEdit, 1, 1) + grid.addWidget(numLabel, 2, 0) + grid.addWidget(self.numEntry, 2, 1) + + grid.addWidget(button, 3, 1) + + self.setLayout(grid) + self.resize(200, 100) + button.setFocus() + + def execute(self): + + testFile = open(sys.argv[1],"r") + inputEvents = testFile.readlines() + testFile.close() + + numDelay = self.numEntry.value() + beatDiv = self.timeEdit.itemData(self.timeEdit.currentIndex()) + delayLength=0 # fill in when we get the beat size value + + outputEvents=[] + + #get beat length to calculate minimum length of event + for line in inputEvents: + if line.startswith('BEATLEN'): + tag,tick = line.split(' ') + delayLength=int(tick)/beatDiv + break + + #loop through events + for line in inputEvents: + if line.startswith('NOTE'): + tag,tick,note,length,velocity = line.split(' ') + outputEvents.append(line) + for v in range(numDelay): + outputEvents.append("%s %d %s %s %d\n"%(tag,int(tick)+delayLength*(v+1),note,length,int(velocity)/(v+2))) + + else: + outputEvents.append(line) + + + testFile = open(sys.argv[1],"w") + testFile.writelines(outputEvents) + testFile.close() + + + quit() + +app = QtGui.QApplication(sys.argv) +qb = TempoDelay() +qb.show() +sys.exit(app.exec_()) Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/splash.png and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/splash.png differ diff -Nru muse-2.1.2/share/templates/audio.med muse-3.0.2+ds1/share/templates/audio.med --- muse-2.1.2/share/templates/audio.med 2013-03-28 15:16:00.000000000 +0000 +++ muse-3.0.2+ds1/share/templates/audio.med 2017-12-04 21:01:19.000000000 +0000 @@ -1,123 +1,5 @@ - - - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 00:00:00:00:00 - 1 - 1 - 1 - 0 - 0 - 0 - - - 1 - 0 - 0 - 0 - - - 2 - 63 - 127 - 63 - 70 - 9 - 0 - 0 - 0 - 4 - 4 - 0 - 0 - 1 - 0 - 0.5 - - 0 - 28 - 31 - 33 - 29 - 36 - - - - 1 - 0 - 100 - - - 1 - 0 - 100 - - - 1 - 90 - 0 - 2 - 0 - 1 - - - 1 - 16 - 0 - 12 - 0 - - - 1 - - - 1 - 384 - - - 1 - 0 - - - 1 - 0 - - - 3 - 80 - 130 - 0 - - - 1 - 0 - 0 - - - 0 - 1 - 1536 - 0 - 0 - - - 1 - 384 - 0 - 0 - 3072 - 0 - - - + 1 @@ -350,14 +232,14 @@ - + - + diff -Nru muse-2.1.2/share/templates/default.med muse-3.0.2+ds1/share/templates/default.med --- muse-2.1.2/share/templates/default.med 2013-03-28 15:16:00.000000000 +0000 +++ muse-3.0.2+ds1/share/templates/default.med 2017-12-04 21:01:19.000000000 +0000 @@ -1,123 +1,5 @@ - - - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 00:00:00:00:00 - 1 - 1 - 1 - 0 - 0 - 0 - - - 1 - 0 - 0 - 0 - - - 2 - 63 - 127 - 63 - 70 - 9 - 0 - 0 - 0 - 4 - 4 - 0 - 0 - 1 - 0 - 0.5 - - 0 - 28 - 31 - 33 - 29 - 36 - - - - 1 - 0 - 100 - - - 1 - 0 - 100 - - - 1 - 90 - 0 - 2 - 0 - 1 - - - 1 - 16 - 0 - 12 - 0 - - - 1 - - - 1 - 384 - - - 1 - 0 - - - 1 - 0 - - - 3 - 80 - 130 - 0 - - - 1 - 0 - 0 - - - 0 - 1 - 1536 - 0 - 0 - - - 1 - 384 - 0 - 0 - 3072 - 0 - - - + 1 diff -Nru muse-2.1.2/share/templates/midiGM.med muse-3.0.2+ds1/share/templates/midiGM.med --- muse-2.1.2/share/templates/midiGM.med 2013-03-28 15:16:00.000000000 +0000 +++ muse-3.0.2+ds1/share/templates/midiGM.med 2017-12-04 21:01:19.000000000 +0000 @@ -1,123 +1,5 @@ - - - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 00:00:00:00:00 - 1 - 1 - 1 - 0 - 0 - 0 - - - 1 - 0 - 0 - 0 - - - 2 - 63 - 127 - 63 - 70 - 9 - 0 - 0 - 0 - 4 - 4 - 0 - 0 - 1 - 0 - 0.5 - - 0 - 28 - 31 - 33 - 29 - 36 - - - - 1 - 0 - 100 - - - 1 - 0 - 100 - - - 1 - 90 - 0 - 2 - 0 - 1 - - - 1 - 16 - 0 - 12 - 0 - - - 1 - - - 1 - 384 - - - 1 - 0 - - - 1 - 0 - - - 3 - 80 - 130 - 0 - - - 1 - 0 - 0 - - - 0 - 1 - 1536 - 0 - 0 - - - 1 - 384 - 0 - 0 - 3072 - 0 - - - + 1 diff -Nru muse-2.1.2/share/templates/monorecord.med muse-3.0.2+ds1/share/templates/monorecord.med --- muse-2.1.2/share/templates/monorecord.med 2013-03-28 15:16:00.000000000 +0000 +++ muse-3.0.2+ds1/share/templates/monorecord.med 2017-12-04 21:01:19.000000000 +0000 @@ -1,123 +1,5 @@ - - - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 00:00:00:00:00 - 1 - 1 - 1 - 0 - 0 - 0 - - - 1 - 0 - 0 - 0 - - - 2 - 63 - 127 - 63 - 70 - 9 - 0 - 0 - 0 - 4 - 4 - 0 - 0 - 1 - 0 - 0.5 - - 0 - 28 - 31 - 33 - 29 - 36 - - - - 1 - 0 - 100 - - - 1 - 0 - 100 - - - 1 - 90 - 0 - 2 - 0 - 1 - - - 1 - 16 - 0 - 12 - 0 - - - 1 - - - 1 - 384 - - - 1 - 0 - - - 1 - 0 - - - 3 - 80 - 130 - 0 - - - 1 - 0 - 0 - - - 0 - 1 - 1536 - 0 - 0 - - - 1 - 384 - 0 - 0 - 3072 - 0 - - - + 1 diff -Nru muse-2.1.2/share/templates/MusE.cfg muse-3.0.2+ds1/share/templates/MusE.cfg --- muse-2.1.2/share/templates/MusE.cfg 2013-03-28 15:16:00.000000000 +0000 +++ muse-3.0.2+ds1/share/templates/MusE.cfg 2017-12-04 21:01:19.000000000 +0000 @@ -1,12 +1,16 @@ - + + 0 384 1024 1 1 0 + 0 + 0 1 + 1 -60 -60 0 @@ -23,19 +27,20 @@ 384 1 + 1 0 1 1 1 1 - 0 + 3 1 - 0 + 3 GM 1 0 - + 3 1 1 0 @@ -46,7 +51,11 @@ 0 0 0 + 0 + 1 + 2 + 0 sweep 0 @@ -54,36 +63,23 @@ 0 0 0 - 1 - 1 - 1 - 1 - 0 - 0 - 1 - 0 - 0 - 0 1 1 0 1 1 + 1 1 - 0 - 0 + 1 + 1 1 - 0 - 0 - 0 + 1 + 1 + 1 20 - arial,10,-1,5,50,0,0,0,0,0 - arial,7,-1,5,50,0,0,0,0,0 - arial,10,-1,5,50,0,0,0,0,0 - arial,10,-1,5,75,0,0,0,0,0 - arial,8,-1,5,50,0,0,0,0,0 - arial,8,-1,5,75,0,0,0,0,0 - arial,8,-1,5,75,1,0,0,0,0 + 0 + 1 + 0 170 @@ -118,6 +114,7 @@ + Default Refrain Bridge @@ -135,10 +132,12 @@ Keyboard Piano Saxophone + + @@ -152,6 +151,7 @@ + @@ -173,24 +173,28 @@ 1 1 + 1 1 1 1 1 1 1 + -1004 Mixer B 1 1 + 1 1 1 1 1 1 1 + -1004 1 1 @@ -202,6 +206,30 @@ + + + + + + + + + + + + + + + + + + 8 + + + + + + 2 @@ -220,6 +248,15 @@ 1 0 0.5 + 1 + 1 + 0.1 + 0.1 + 0 + klick2.wav + klick1.wav + klick3.wav + klick4.wav 0 28 @@ -232,27 +269,10 @@ 96 132 788 + 0 934 488 - 000000ff00000000fd00000000000003a60000015a00000004000000040000000800000008fc0000000300000002000000050000001e -0055006e0064006f002f005200650064006f00200074006f006f006c00730100000000ffffffff000000000000000000000014004400720075006d0020007400 -6f006f006c00730100000049ffffffff000000000000000000000014004500640069007400200054006f006f006c007301000000f7ffffffff00000000000000 -0000000012007400720061006e00730070006f00720074010000019affffffff00000000000000000000000a00700061006e0069006301000002c0000000f800 -0000000000000000000002000000030000000c0063007500720073006f00720100000000ffffffff0000000000000000000000260050006f0073002f0053006e -00610070002f0053006f006c006f002d0074006f006f006c00730100000081ffffffff000000000000000000000012004e006f0074006500200049006e006600 -6f01000001b9ffffffff000000000000000000000002000000030000000a00540065006d0070006f0100000000ffffffff000000000000000000000012005300 -690067006e00610074007500720065010000008bffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e0100 -00013b0000026b0000000000000000 - 000000ff00000000fd00000000000004000000022900000004000000040000000800000008fc000000030000000200000006000 -0001800460069006c006500200042007500740074006f006e00730100000000ffffffff00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c00730100000085ffffffff000000000000000000000014004400720075006d00200074006f006f006c007301000000ceffffffff0 -00000000000000000000014004500640069007400200054006f006f006c0073010000017cffffffff000000000000000000000012007400720061006e0073007 -0006f00720074010000021fffffffff00000000000000000000000a00700061006e0069006301000003450000012000000000000000000000000200000003000 -0000c0063007500720073006f00720100000000ffffffff0000000000000000000000260050006f0073002f0053006e00610070002f0053006f006c006f002d0 -074006f006f006c00730100000081ffffffff000000000000000000000012004e006f0074006500200049006e0066006f01000001b9ffffffff0000000000000 -00000000002000000030000000a00540065006d0070006f0100000000ffffffff000000000000000000000012005300690067006e00610074007500720065010 -000008bffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e010000013b000002c50000000000000000 0 1 0 @@ -264,23 +284,6 @@ 852 460 - 000000ff00000000fd00000000000003540000013e00000004000000040000000800000008fc000000030000000200000005 -0000001e0055006e0064006f002f005200650064006f00200074006f006f006c00730100000000ffffffff00000000000000000000001e005000690061006e00 -6f0072006f006c006c00200074006f006f006c00730100000049ffffffff000000000000000000000014004500640069007400200054006f006f006c00730100 -0000b000000098000000000000000000000012007400720061006e00730070006f007200740100000148ffffffff00000000000000000000000a00700061006e -00690063010000026e000000f800000000000000000000000200000002000000260050006f0073002f0053006e00610070002f0053006f006c006f002d007400 -6f006f006c00730100000000ffffffff000000000000000000000012004e006f0074006500200049006e0066006f0100000162ffffffff000000000000000000 -000002000000030000000a00540065006d0070006f0100000000ffffffff000000000000000000000012005300690067006e0061007400750072006501000000 -8bffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e010000013b000002190000000000000000 - 000000ff00000000fd00000000000004000000022900000004000000040000000800000008fc000000030000000200000006000 -0001800460069006c006500200042007500740074006f006e00730100000000ffffffff00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c00730100000085ffffffff00000000000000000000001e005000690061006e006f0072006f006c006c00200074006f006f006c007 -301000000ceffffffff000000000000000000000014004500640069007400200054006f006f006c00730100000135ffffffff000000000000000000000012007 -400720061006e00730070006f0072007401000001baffffffff00000000000000000000000a00700061006e0069006301000002e000000120000000000000000 -00000000200000002000000260050006f0073002f0053006e00610070002f0053006f006c006f002d0074006f006f006c00730100000000ffffffff000000000 -000000000000012004e006f0074006500200049006e0066006f0100000162ffffffff000000000000000000000002000000030000000a00540065006d0070006 -f0100000000ffffffff000000000000000000000012005300690067006e00610074007500720065010000008bffffffff00000000000000000000001a0053006 -f006e006700200050006f0073006900740069006f006e010000013b000002c50000000000000000 0 1 0 @@ -298,25 +301,6 @@ 867 544 - 000000ff00000000fd0000000000000363000001a700000004000000040000000800000008fc000000030000000200000006 -0000001e0055006e0064006f002f005200650064006f00200074006f006f006c00730100000000ffffffff000000000000000000000028005300740065007000 -20007200650063006f007200640069006e006700200074006f006f006c00730100000049ffffffff000000000000000000000014004500640069007400200054 -006f006f006c00730100000074ffffffff00000000000000000000002a005100750061006e007400690073006100740069006f006e0020007300650074007400 -69006e0067007301000000dbffffffff000000000000000000000012007400720061006e00730070006f007200740100000224ffffffff000000000000000000 -00000a00700061006e006900630100000338000000420000000000000000000000020000000100000022004e006500770020006e006f00740065002000730065 -007400740069006e006700730100000000ffffffff000000000000000000000002000000030000000a00540065006d0070006f0100000000ffffffff00000000 -0000000000000012005300690067006e00610074007500720065010000008bffffffff00000000000000000000001a0053006f006e006700200050006f007300 -6900740069006f006e010000013b000002d80000000000000000 - 000000ff00000000fd00000000000004000000023d00000004000000040000000800000008fc000000030000000200000006000 -0001800460069006c006500200042007500740074006f006e007301000000000000008c00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c0073010000008cffffffff00000000000000000000002800530074006500700020007200650063006f007200640069006e0067002 -00074006f006f006c007301000000d5ffffffff000000000000000000000014004500640069007400200054006f006f006c00730100000100ffffffff0000000 -00000000000000012007400720061006e00730070006f007200740100000167ffffffff00000000000000000000000a00700061006e00690063010000028d000 -001730000000000000000000000020000000200000022004e006500770020006e006f00740065002000730065007400740069006e006700730100000000fffff -fff00000000000000000000002a005100750061006e007400690073006100740069006f006e002000730065007400740069006e00670073010000027a0000018 -6000000000000000000000002000000030000000a00540065006d0070006f0100000000ffffffff000000000000000000000012005300690067006e006100740 -07500720065010000008bffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e010000013b000002c500000 -00000000000 0 1 0 @@ -327,21 +311,6 @@ 784 544 - 000000ff00000000fd0000000000000310000001b200000004000000040000000800000008fc000000040000000200000005 -0000001e0055006e0064006f002f005200650064006f00200074006f006f006c00730100000000ffffffff000000000000000000000014004500640069007400 -200054006f006f006c00730100000049ffffffff000000000000000000000012007400720061006e00730070006f0072007401000000b0ffffffff0000000000 -0000000000000a00700061006e0069006301000001d6ffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e -01000002010000010f000000000000000000000002000000020000001a0045006e00610062006c00650020006d006100730074006500720100000000ffffffff -0000000000000000000000080049006e0066006f010000005dffffffff000000000000000000000002000000020000000a00540065006d0070006f0000000000 -ffffffff000000000000000000000012005300690067006e006100740075007200650000000000ffffffff00000000000000000000000200000000 - 000000ff00000000fd00000000000004000000024900000004000000040000000800000008fc000000040000000200000006000 -0001800460069006c006500200042007500740074006f006e007301000000000000008c00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c0073010000008cffffffff00000000000000000000001a0045006e00610062006c00650020006d006100730074006500720100000 -0d5ffffffff000000000000000000000014004500640069007400200054006f006f006c007301000001320000007100000000000000000000001200740072006 -1006e00730070006f0072007401000001a3ffffffff00000000000000000000000a00700061006e0069006301000002c9ffffffff00000000000000000000000 -200000002000000080049006e0066006f0100000000ffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e0 -10000025d000001a300000000000000000000000200000000000000020000000200000012005300690067006e006100740075007200650000000000ffffffff0 -0000000000000000000000a00540065006d0070006f00000000b0ffffffff0000000000000000 0 1 0 @@ -349,22 +318,11 @@ + 96 + 0 797 478 - 000000ff00000000fd000000000000031d0000018200000004000000040000000800000008fc000000020000000200000004 -0000001e0055006e0064006f002f005200650064006f00200074006f006f006c00730100000000ffffffff00000000000000000000001c005700610076006500 -4500640069007400200074006f006f006c007301000000490000010f000000000000000000000012007400720061006e00730070006f007200740100000158ff -ffffff00000000000000000000000a00700061006e00690063010000027e0000009f000000000000000000000002000000030000000a00540065006d0070006f -0100000000ffffffff000000000000000000000012005300690067006e00610074007500720065010000008bffffffff00000000000000000000001a0053006f -006e006700200050006f0073006900740069006f006e010000013b000001e20000000000000000 - 000000ff00000000fd00000000000004000000025b00000004000000040000000800000008fc000000020000000200000005000 -0001800460069006c006500200042007500740074006f006e00730100000000ffffffff00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c00730100000085ffffffff00000000000000000000001c0057006100760065004500640069007400200074006f006f006c0073010 -00000ceffffffff000000000000000000000012007400720061006e00730070006f0072007401000001d1ffffffff00000000000000000000000a00700061006 -e0069006301000002f700000109000000000000000000000002000000030000000a00540065006d0070006f0100000000ffffffff00000000000000000000001 -2005300690067006e00610074007500720065010000008bffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f0 -06e010000013b000002c50000000000000000 0 1 0 @@ -374,19 +332,6 @@ 756 364 - 000000ff00000000fd00000000000002f40000013000000004000000040000000800000008fc000000010000000200000007 -0000001e0055006e0064006f002f005200650064006f00200074006f006f006c00730100000000ffffffff00000000000000000000000a00700061006e006900 -630100000049ffffffff000000000000000000000012007400720061006e00730070006f007200740100000074ffffffff00000000000000000000001a005300 -6f006e006700200050006f0073006900740069006f006e0000000175ffffffff00000000000000000000000a00540065006d0070006f0000000182ffffffff00 -0000000000000000000012005300690067006e00610074007500720065000000019affffffff000000000000000000000022006c00690073007400200069006e -007300650072007400200074006f006f006c0073010000019affffffff0000000000000000 - 000000ff00000000fd00000000000004000000027b00000004000000040000000800000008fc000000010000000200000008000 -0001800460069006c006500200042007500740074006f006e007301000000000000008c00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c0073010000008cffffffff000000000000000000000022006c00690073007400200069006e007300650072007400200074006f006 -f006c007301000000d5ffffffff000000000000000000000012007400720061006e00730070006f007200740100000196ffffffff00000000000000000000000 -a00540065006d0070006f00000002c5ffffffff000000000000000000000012005300690067006e006100740075007200650000000350ffffffff00000000000 -000000000000a00700061006e0069006301000002bcffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e0 -1000002e7000001190000000000000000 0 1 0 @@ -396,17 +341,6 @@ 784 544 - 000000ff00000000fd0000000000000310000001e400000004000000040000000800000008fc000000010000000200000006 -0000001e0055006e0064006f002f005200650064006f00200074006f006f006c00730100000000ffffffff00000000000000000000000a00700061006e006900 -630100000049ffffffff000000000000000000000012007400720061006e00730070006f007200740100000074ffffffff00000000000000000000001a005300 -6f006e006700200050006f0073006900740069006f006e010000019affffffff00000000000000000000000a00540065006d0070006f01000001d5ffffffff00 -0000000000000000000012005300690067006e006100740075007200650100000260ffffffff0000000000000000 - 000000ff00000000fd00000000000004000000027b00000004000000040000000800000008fc000000010000000200000007000 -0001800460069006c006500200042007500740074006f006e007301000000000000008c00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c0073010000008cffffffff000000000000000000000012007400720061006e00730070006f0072007401000000d5ffffffff00000 -000000000000000000a00700061006e0069006301000001fbffffffff00000000000000000000000a00540065006d0070006f00000002c5ffffffff000000000 -000000000000012005300690067006e006100740075007200650000000350ffffffff00000000000000000000001a0053006f006e006700200050006f0073006 -900740069006f006e0100000226000001da0000000000000000 0 1 0 @@ -416,19 +350,6 @@ 612 360 - 000000ff00000000fd00000000000002640000012c00000004000000040000000800000008fc000000010000000200000007 -0000001e0055006e0064006f002f005200650064006f00200074006f006f006c0073010000000000000052000000000000000000000012007400720061006e00 -730070006f007200740100000052ffffffff00000000000000000000000a00700061006e006900630100000178ffffffff00000000000000000000001a005300 -6f006e006700200050006f0073006900740069006f006e00000001a30000006800000000000000000000000a00540065006d0070006f00000000a6ffffffff00 -0000000000000000000012005300690067006e006100740075007200650000000131ffffffff00000000000000000000002c004d006100730074006500720020 -004c0069007300740020004500640069007400200054006f006f006c007301000001a3000000920000000000000000 - 000000ff00000000fd00000000000004000000027b00000004000000040000000800000008fc000000020000000200000007000 -0001800460069006c006500200042007500740074006f006e007301000000000000008c00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c0073010000008cffffffff00000000000000000000002c004d006100730074006500720020004c006900730074002000450064006 -9007400200054006f006f006c007301000000d5ffffffff000000000000000000000012005300690067006e0061007400750072006500000002d9ffffffff000 -000000000000000000012007400720061006e00730070006f007200740100000182ffffffff00000000000000000000000a00700061006e0069006301000002a -8ffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e01000002d30000012d0000000000000000000000020 -00000010000000a00540065006d0070006f0000000000ffffffff0000000000000000 0 1 0 @@ -438,19 +359,6 @@ 784 544 - 000000ff00000000fd0000000000000310000001e400000004000000040000000800000008fc000000010000000200000007 -0000001e0055006e0064006f002f005200650064006f00200074006f006f006c00730100000000ffffffff00000000000000000000000a00700061006e006900 -630100000049ffffffff000000000000000000000012007400720061006e00730070006f007200740100000074ffffffff00000000000000000000001a005300 -6f006e006700200050006f0073006900740069006f006e0100000191ffffffff00000000000000000000000a00540065006d0070006f010000019effffffff00 -0000000000000000000012005300690067006e006100740075007200650100000229ffffffff000000000000000000000022006d00610072006b006500720020 -006500640069007400200074006f006f006c007301000002d9ffffffff0000000000000000 - 000000ff00000000fd00000000000004000000027b00000004000000040000000800000008fc000000010000000200000008000 -0001800460069006c006500200042007500740074006f006e007301000000000000008c00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c0073010000008cffffffff000000000000000000000022006d00610072006b006500720020006500640069007400200074006f006 -f006c007301000000d5ffffffff000000000000000000000012007400720061006e00730070006f00720074010000011effffffff00000000000000000000000 -a00700061006e006900630100000244ffffffff00000000000000000000000a00540065006d0070006f00000002c5ffffffff000000000000000000000012005 -300690067006e006100740075007200650000000329ffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e0 -10000026f000001910000000000000000 0 1 0 @@ -458,23 +366,8 @@ - 1024 - 553 - 000000ff00000000fd00000000000003ec0000008d00000004000000040000000800000008fc000000020000000200000005 -0000001e0055006e0064006f002f005200650064006f00200074006f006f006c00730100000000ffffffff00000000000000000000001a006100720072006100 -6e0067006500720054006f006f006c00730100000049ffffffff00000000000000000000002600560069007300690062006c006500200074007200610063006b -0020007400790070006500730100000128000000f0000000000000000000000012007400720061006e00730070006f007200740100000218ffffffff00000000 -000000000000000a00700061006e00690063010000033e000000b6000000000000000000000002000000010000001e0041007200720061006e00670065007200 -54006f006f006c0062006100720100000000ffffffff0000000000000000 - 000000ff00000000fd00000000000004000000022900000004000000040000000800000008fc000000030000000200000006000 -0001800460069006c006500200042007500740074006f006e007301000000000000008c00000000000000000000001e0055006e0064006f002f0052006500640 -06f00200074006f006f006c0073010000008cffffffff00000000000000000000001a0061007200720061006e0067006500720054006f006f006c00730100000 -0d5ffffffff00000000000000000000002600560069007300690062006c006500200074007200610063006b00200074007900700065007301000001b4fffffff -f000000000000000000000012007400720061006e00730070006f007200740100000293ffffffff00000000000000000000000a00700061006e0069006301000 -003b900000047000000000000000000000002000000010000001e0041007200720061006e0067006500720054006f006f006c0062006100720100000000fffff -fff000000000000000000000002000000030000000a00540065006d0070006f0100000000ffffffff000000000000000000000012005300690067006e0061007 -4007500720065010000008bffffffff00000000000000000000001a0053006f006e006700200050006f0073006900740069006f006e010000013b000002c5000 -0000000000000 + 1106 + 562 0 1 1 @@ -482,7 +375,6 @@ - 9 8 7 6 5 4 3 2 1 0 @@ -556,6 +448,8 @@ 16777221 32 + 67108896 + 33554464 16777222 87 16777233 @@ -627,6 +521,7 @@ 0 67108938 0 + 0 0 0 0 @@ -640,8 +535,10 @@ 16777237 50331669 100663375 + 100663364 83886101 83886099 + 16777265 67108929 100663361 67108937 @@ -657,6 +554,11 @@ 83886101 83886100 83886098 + 67108946 + 67108949 + 67108940 + 150994963 + 150994965 33554475 33554477 134217808 @@ -694,9 +596,12 @@ 46 44 69 + 67108941 65 68 82 + 80 + 90 70 85 86 @@ -707,6 +612,7 @@ 57 16777237 16777235 + 89 83 71 0 @@ -723,6 +629,7 @@ 67108939 16777269 16777268 + 134217806 diff -Nru muse-2.1.2/share/templates/synti.med muse-3.0.2+ds1/share/templates/synti.med --- muse-2.1.2/share/templates/synti.med 2013-03-28 15:16:00.000000000 +0000 +++ muse-3.0.2+ds1/share/templates/synti.med 2017-12-04 21:01:19.000000000 +0000 @@ -1,136 +1,5 @@ - - - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 - 1 - 00:00:00:00:00 - 1 - 1 - 1 - 0 - 0 - 0 - - - 1 - 0 - 0 - 0 - - - 2 - 63 - 127 - 63 - 70 - 9 - 0 - 0 - 0 - 4 - 4 - 0 - 0 - 1 - 0 - 0.5 - - 0 - 28 - 31 - 33 - 29 - 36 - - 1 - organ-1 - organ-1 - 2 - 1 - - - vam-1 - vam-1 - 2 - 1 - - - - - 1 - 0 - 100 - - - 1 - 0 - 100 - - - 1 - 90 - 0 - 2 - 0 - 1 - - - 1 - 16 - 0 - 12 - 0 - - - 1 - - - 1 - 384 - - - 1 - 0 - - - 1 - 0 - - - 3 - 80 - 130 - 0 - - - 1 - 0 - 0 - - - 0 - 1 - 1536 - 0 - 0 - - - 1 - 384 - 0 - 0 - 3072 - 0 - - - + 1 diff -Nru muse-2.1.2/share/themes/Ardour.cfg muse-3.0.2+ds1/share/themes/Ardour.cfg --- muse-2.1.2/share/themes/Ardour.cfg 2013-03-28 15:15:50.000000000 +0000 +++ muse-3.0.2+ds1/share/themes/Ardour.cfg 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + Oxygen 170 diff -Nru muse-2.1.2/share/themes/CMakeLists.txt muse-3.0.2+ds1/share/themes/CMakeLists.txt --- muse-2.1.2/share/themes/CMakeLists.txt 2013-03-28 15:15:50.000000000 +0000 +++ muse-3.0.2+ds1/share/themes/CMakeLists.txt 2017-12-04 21:01:19.000000000 +0000 @@ -21,7 +21,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= -file (GLOB theme_files *.cfg *.qss +file (GLOB theme_files *.cfg *.qss *.cfc ) install( FILES ${theme_files} diff -Nru "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/themes/Dark Theme.cfg" "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/themes/Dark Theme.cfg" --- "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/themes/Dark Theme.cfg" 2013-03-28 15:15:50.000000000 +0000 +++ "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/themes/Dark Theme.cfg" 2017-12-04 21:01:19.000000000 +0000 @@ -1,7 +1,6 @@ - + - Oxygen 170 diff -Nru muse-2.1.2/share/themes/default.cfc muse-3.0.2+ds1/share/themes/default.cfc --- muse-2.1.2/share/themes/default.cfc 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/themes/default.cfc 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/themes/Light Theme.cfg" "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/themes/Light Theme.cfg" --- "/tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/share/themes/Light Theme.cfg" 2013-03-28 15:15:50.000000000 +0000 +++ "/tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/share/themes/Light Theme.cfg" 2017-12-04 21:01:19.000000000 +0000 @@ -1,5 +1,5 @@ - + 170 diff -Nru muse-2.1.2/share/themes/pastel_colors.cfc muse-3.0.2+ds1/share/themes/pastel_colors.cfc --- muse-2.1.2/share/themes/pastel_colors.cfc 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/share/themes/pastel_colors.cfc 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru muse-2.1.2/svg/fast_forward.svg muse-3.0.2+ds1/svg/fast_forward.svg --- muse-2.1.2/svg/fast_forward.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/fast_forward.svg 2018-01-06 20:31:35.000000000 +0000 @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru muse-2.1.2/svg/headphones_off.svg muse-3.0.2+ds1/svg/headphones_off.svg --- muse-2.1.2/svg/headphones_off.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/headphones_off.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/headphones_on.svg muse-3.0.2+ds1/svg/headphones_on.svg --- muse-2.1.2/svg/headphones_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/headphones_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/monitor_off_default_col_round.svg muse-3.0.2+ds1/svg/monitor_off_default_col_round.svg --- muse-2.1.2/svg/monitor_off_default_col_round.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/monitor_off_default_col_round.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/monitor_off_default_col.svg muse-3.0.2+ds1/svg/monitor_off_default_col.svg --- muse-2.1.2/svg/monitor_off_default_col.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/monitor_off_default_col.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/monitor_off_round.svg muse-3.0.2+ds1/svg/monitor_off_round.svg --- muse-2.1.2/svg/monitor_off_round.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/monitor_off_round.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/monitor_off.svg muse-3.0.2+ds1/svg/monitor_off.svg --- muse-2.1.2/svg/monitor_off.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/monitor_off.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/monitor_on_round.svg muse-3.0.2+ds1/svg/monitor_on_round.svg --- muse-2.1.2/svg/monitor_on_round.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/monitor_on_round.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/monitor_on.svg muse-3.0.2+ds1/svg/monitor_on.svg --- muse-2.1.2/svg/monitor_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/monitor_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/mute_and_proxy_on.svg muse-3.0.2+ds1/svg/mute_and_proxy_on.svg --- muse-2.1.2/svg/mute_and_proxy_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/mute_and_proxy_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/mute_off.svg muse-3.0.2+ds1/svg/mute_off.svg --- muse-2.1.2/svg/mute_off.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/mute_off.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/mute_off_waves.svg muse-3.0.2+ds1/svg/mute_off_waves.svg --- muse-2.1.2/svg/mute_off_waves.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/mute_off_waves.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/mute_on.svg muse-3.0.2+ds1/svg/mute_on.svg --- muse-2.1.2/svg/mute_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/mute_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/mute_on_waves.svg muse-3.0.2+ds1/svg/mute_on_waves.svg --- muse-2.1.2/svg/mute_on_waves.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/mute_on_waves.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/mute_on_X.svg muse-3.0.2+ds1/svg/mute_on_X.svg --- muse-2.1.2/svg/mute_on_X.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/mute_on_X.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/mute_on_X_waves.svg muse-3.0.2+ds1/svg/mute_on_X_waves.svg --- muse-2.1.2/svg/mute_on_X_waves.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/mute_on_X_waves.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/mute_proxy_on.svg muse-3.0.2+ds1/svg/mute_proxy_on.svg --- muse-2.1.2/svg/mute_proxy_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/mute_proxy_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/play_off.svg muse-3.0.2+ds1/svg/play_off.svg --- muse-2.1.2/svg/play_off.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/play_off.svg 2018-01-06 20:31:35.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru muse-2.1.2/svg/play_on.svg muse-3.0.2+ds1/svg/play_on.svg --- muse-2.1.2/svg/play_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/play_on.svg 2018-01-06 20:31:35.000000000 +0000 @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru muse-2.1.2/svg/pre_fader_off.svg muse-3.0.2+ds1/svg/pre_fader_off.svg --- muse-2.1.2/svg/pre_fader_off.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/pre_fader_off.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/pre_fader_on.svg muse-3.0.2+ds1/svg/pre_fader_on.svg --- muse-2.1.2/svg/pre_fader_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/pre_fader_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/fast_forward.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/fast_forward.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/headphones_off.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/headphones_off.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/headphones_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/headphones_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/monitor_off_default_col.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/monitor_off_default_col.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/monitor_off_default_col_round.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/monitor_off_default_col_round.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/monitor_off.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/monitor_off.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/monitor_off_round.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/monitor_off_round.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/monitor_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/monitor_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/monitor_on_round.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/monitor_on_round.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/mute_and_proxy_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/mute_and_proxy_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/mute_off.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/mute_off.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/mute_off_waves.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/mute_off_waves.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/mute_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/mute_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/mute_on_waves.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/mute_on_waves.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/mute_on_X.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/mute_on_X.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/mute_on_X_waves.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/mute_on_X_waves.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/mute_proxy_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/mute_proxy_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/play_off.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/play_off.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/play_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/play_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/pre_fader_off.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/pre_fader_off.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/pre_fader_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/pre_fader_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/rec_arm_off_dark_red.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/rec_arm_off_dark_red.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/rec_arm_off_default_col.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/rec_arm_off_default_col.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/rec_arm_off.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/rec_arm_off.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/rec_arm_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/rec_arm_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/rewind.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/rewind.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/rewind_to_start.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/rewind_to_start.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_input_curved.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_input_curved.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_input.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_input.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_input_terminal_arrow.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_input_terminal_arrow.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_input_terminal.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_input_terminal.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_input_triangle.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_input_triangle.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_input_unconnected.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_input_unconnected.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_output_curved.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_output_curved.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_output.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_output.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_output_terminal_arrow.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_output_terminal_arrow.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_output_terminal.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_output_terminal.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/routing_output_unconnected.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/routing_output_unconnected.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/solo_and_proxy_spotlight_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/solo_and_proxy_spotlight_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/solo_proxy_spotlight_on_alone.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/solo_proxy_spotlight_on_alone.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/solo_proxy_spotlight_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/solo_proxy_spotlight_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/solo_spotlight_off.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/solo_spotlight_off.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/solo_spotlight_on_alone.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/solo_spotlight_on_alone.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/solo_spotlight_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/solo_spotlight_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/solo_spotlight_on_with_mic.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/solo_spotlight_on_with_mic.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/stereo_off.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/stereo_off.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/stereo_off_one_ring.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/stereo_off_one_ring.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/stereo_on_alt.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/stereo_on_alt.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/stereo_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/stereo_on.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/stop.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/stop.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/track_off.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/track_off.odg differ Binary files /tmp/tmp1L8Scc/LNqQbCsPPD/muse-2.1.2/svg/projects/track_on.odg and /tmp/tmp1L8Scc/IY5zq9KXmZ/muse-3.0.2+ds1/svg/projects/track_on.odg differ diff -Nru muse-2.1.2/svg/rec_arm_off_dark_red.svg muse-3.0.2+ds1/svg/rec_arm_off_dark_red.svg --- muse-2.1.2/svg/rec_arm_off_dark_red.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/rec_arm_off_dark_red.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/rec_arm_off_default_col.svg muse-3.0.2+ds1/svg/rec_arm_off_default_col.svg --- muse-2.1.2/svg/rec_arm_off_default_col.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/rec_arm_off_default_col.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/rec_arm_off.svg muse-3.0.2+ds1/svg/rec_arm_off.svg --- muse-2.1.2/svg/rec_arm_off.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/rec_arm_off.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/rec_arm_on.svg muse-3.0.2+ds1/svg/rec_arm_on.svg --- muse-2.1.2/svg/rec_arm_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/rec_arm_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/rewind.svg muse-3.0.2+ds1/svg/rewind.svg --- muse-2.1.2/svg/rewind.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/rewind.svg 2018-01-06 20:31:35.000000000 +0000 @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru muse-2.1.2/svg/rewind_to_start.svg muse-3.0.2+ds1/svg/rewind_to_start.svg --- muse-2.1.2/svg/rewind_to_start.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/rewind_to_start.svg 2018-01-06 20:31:35.000000000 +0000 @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru muse-2.1.2/svg/routing_input.svg muse-3.0.2+ds1/svg/routing_input.svg --- muse-2.1.2/svg/routing_input.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/routing_input.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/routing_input_unconnected.svg muse-3.0.2+ds1/svg/routing_input_unconnected.svg --- muse-2.1.2/svg/routing_input_unconnected.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/routing_input_unconnected.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/routing_output.svg muse-3.0.2+ds1/svg/routing_output.svg --- muse-2.1.2/svg/routing_output.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/routing_output.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/routing_output_unconnected.svg muse-3.0.2+ds1/svg/routing_output_unconnected.svg --- muse-2.1.2/svg/routing_output_unconnected.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/routing_output_unconnected.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/solo_and_proxy_spotlight_on.svg muse-3.0.2+ds1/svg/solo_and_proxy_spotlight_on.svg --- muse-2.1.2/svg/solo_and_proxy_spotlight_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/solo_and_proxy_spotlight_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/solo_proxy_spotlight_on_alone.svg muse-3.0.2+ds1/svg/solo_proxy_spotlight_on_alone.svg --- muse-2.1.2/svg/solo_proxy_spotlight_on_alone.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/solo_proxy_spotlight_on_alone.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/solo_proxy_spotlight_on.svg muse-3.0.2+ds1/svg/solo_proxy_spotlight_on.svg --- muse-2.1.2/svg/solo_proxy_spotlight_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/solo_proxy_spotlight_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/solo_spotlight_off.svg muse-3.0.2+ds1/svg/solo_spotlight_off.svg --- muse-2.1.2/svg/solo_spotlight_off.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/solo_spotlight_off.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/solo_spotlight_on_alone.svg muse-3.0.2+ds1/svg/solo_spotlight_on_alone.svg --- muse-2.1.2/svg/solo_spotlight_on_alone.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/solo_spotlight_on_alone.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/solo_spotlight_on.svg muse-3.0.2+ds1/svg/solo_spotlight_on.svg --- muse-2.1.2/svg/solo_spotlight_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/solo_spotlight_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/stereo_off.svg muse-3.0.2+ds1/svg/stereo_off.svg --- muse-2.1.2/svg/stereo_off.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/stereo_off.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/stereo_on.svg muse-3.0.2+ds1/svg/stereo_on.svg --- muse-2.1.2/svg/stereo_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/stereo_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/stop.svg muse-3.0.2+ds1/svg/stop.svg --- muse-2.1.2/svg/stop.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/stop.svg 2018-01-06 20:31:35.000000000 +0000 @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru muse-2.1.2/svg/track_off.svg muse-3.0.2+ds1/svg/track_off.svg --- muse-2.1.2/svg/track_off.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/track_off.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/svg/track_on.svg muse-3.0.2+ds1/svg/track_on.svg --- muse-2.1.2/svg/track_on.svg 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/svg/track_on.svg 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru muse-2.1.2/synti/CMakeLists.txt muse-3.0.2+ds1/synti/CMakeLists.txt --- muse-2.1.2/synti/CMakeLists.txt 2013-03-28 15:17:16.000000000 +0000 +++ muse-3.0.2+ds1/synti/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -28,12 +28,15 @@ ${PROJECT_SOURCE_DIR}/muse ${PROJECT_SOURCE_DIR}/muse/widgets ${PROJECT_SOURCE_DIR}/synti + # Make sure to include the uic generated headers paths. + ${CMAKE_CURRENT_BINARY_DIR}/libsimpleplugin ) set (SubDirs + libsimpleplugin + libsynti deicsonze # deicsonze2 - libsynti organ # simpledrums simpledrums2 diff -Nru muse-2.1.2/synti/deicsonze/ARCH_ALIN muse-3.0.2+ds1/synti/deicsonze/ARCH_ALIN --- muse-2.1.2/synti/deicsonze/ARCH_ALIN 2013-03-28 15:17:10.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/ARCH_ALIN 2018-01-26 21:59:38.000000000 +0000 @@ -11385,7 +11385,7 @@ ** Name: Tocc Organ ** Description: A pipe organ intended for Bach's Toccata & Fugue in D Minor -** (the piece the villian always plays in those horror movies) +** (the piece the villain always plays in those horror movies) ** Source: Art Hitzeman 12 1f 00 06 0f 3c 00 5e 04 03 10 1f 00 06 0f 32 00 60 08 05 17 1f 00 07 0f 32 00 5f 0d 06 0e 1f 00 06 0f 3c 00 5e 00 00 37 23 01 00 00 62 0c 02 04 00 diff -Nru muse-2.1.2/synti/deicsonze/CMakeLists.txt muse-3.0.2+ds1/synti/deicsonze/CMakeLists.txt --- muse-2.1.2/synti/deicsonze/CMakeLists.txt 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( deicsonze_mocs +QT5_WRAP_CPP ( deicsonze_mocs deicsonzegui.h ) @@ -34,12 +34,12 @@ file (GLOB deicsonze_ui_files deicsonzegui.ui ) -QT4_WRAP_UI ( deicsonze_uis ${deicsonze_ui_files} ) +QT5_WRAP_UI ( deicsonze_uis ${deicsonze_ui_files} ) ## ## Resource files ## -QT4_ADD_RESOURCES (deicsonze_qrc_files +QT5_ADD_RESOURCES (deicsonze_qrc_files deicsonze.qrc ) @@ -93,6 +93,9 @@ target_link_libraries(deicsonze al awl + mpevent_module + simpler_plugin + simpler_plugingui synti ${QT_LIBRARIES} ) diff -Nru muse-2.1.2/synti/deicsonze/common_defs.h muse-3.0.2+ds1/synti/deicsonze/common_defs.h --- muse-2.1.2/synti/deicsonze/common_defs.h 2013-03-28 15:17:10.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/common_defs.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,7 +23,8 @@ #ifndef __DEICSONZE_UNIQUE_ID_H #define __DEICSONZE_UNIQUE_ID_H -// Make sure this number is unique among all the MESS synths. +// Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. #define DEICSONZE_UNIQUE_ID 5 //#define DEICSONZE_DEBUG diff -Nru muse-2.1.2/synti/deicsonze/deicsonze.cpp muse-3.0.2+ds1/synti/deicsonze/deicsonze.cpp --- muse-2.1.2/synti/deicsonze/deicsonze.cpp 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/deicsonze.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -27,24 +27,16 @@ // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== -// #include #include -// #include - #include #include -#include "muse/midi.h" +#include "muse/midi_consts.h" #include "libsynti/mess.h" -//#include "common_defs.h" #include "deicsonze.h" - -#include "plugin.h" - -#include "muse/midictrl.h" -//#include "deicsonze.h" -#include "config.h" +#include "libsimpleplugin/simpler_plugin.h" +#include "muse/midictrl_consts.h" #define ABS(x) (x>=0?x:-x) @@ -52,11 +44,22 @@ float DeicsOnze::waveTable[NBRWAVES][RESOLUTION]; int DeicsOnze::useCount = 0; +QString DEI_configPath; +QString DEI_globalLibPath; +QString DEI_sharePath; +unsigned int DEI_segmentSize; +int DEI_sampleRate; +bool DEI_useDenormalBias; +float DEI_denormalBias; + //--------------------------------------------------------- // DeicsOnze //--------------------------------------------------------- DeicsOnze::DeicsOnze() : Mess(2) { + + MusESimplePlugin::SS_initPlugins(DEI_globalLibPath); + if (useCount++ == 0) { // create sinus wave table, W1 for(int i = 0; i < RESOLUTION; i++) @@ -125,8 +128,7 @@ _saveConfig = true; _isInitSet = true; //false if an initial bank must be download - QString sharePath(MusEGlobal::museGlobalShare); - _initSetPath = sharePath + QString("/presets/deicsonze/SutulaBank.dei"); + _initSetPath = DEI_sharePath + QString("/presets/deicsonze/SutulaBank.dei"); //TODO @@ -134,7 +136,7 @@ _isBackgroundPix = true; //false if an initial bank must be download //"/usr/local/share/muse-1.0pre1/wallpapers/abstractdeicsonze1.jpg"; - _backgroundPixPath = sharePath + QString("/wallpapers/paper2.jpg"); // Tim. + _backgroundPixPath = DEI_sharePath + QString("/wallpapers/paper2.jpg"); // Tim. //initialization GUI @@ -143,15 +145,15 @@ _gui->setWindowTitle(QString("DeicsOnze")); //FX - MusECore::Plugin* p; - p = MusEGlobal::plugins.find("freeverb", "freeverb1"); + MusESimplePlugin::Plugin* p; + p = MusESimplePlugin::plugins.find("freeverb", "freeverb1"); _pluginIReverb = NULL; if(p) initPluginReverb(p); _pluginIChorus = NULL; - p = MusEGlobal::plugins.find("doublechorus", "doublechorus1"); + p = MusESimplePlugin::plugins.find("doublechorus", "doublechorus1"); if(p) initPluginChorus(p); _pluginIDelay = NULL; - p = MusEGlobal::plugins.find("pandelay", "pandelay"); + p = MusESimplePlugin::plugins.find("pandelay", "pandelay"); if(p) initPluginDelay(p); //Filter @@ -160,7 +162,6 @@ _reverbFilter = new LowFilter(); _delayFilter = new LowFilter(); - // Moved here from below due to crash - _preset not initialized when loadConfiguration called. Tim. _initialPreset = new Preset(new Subcategory(new Category(NULL, "NONE", 0), "NONE", 0), 0); for(int c = 0; c < NBRCHANNELS; c++) { @@ -170,9 +171,9 @@ //Load configuration QString defaultConf = - (MusEGlobal::configPath + QString("/" DEICSONZESTR ".dco")); + DEI_configPath + QString("/" DEICSONZESTR ".dco"); FILE* f; - f = fopen(defaultConf.toAscii().data(), "r"); + f = fopen(defaultConf.toLatin1().data(), "r"); if(f) { fclose(f); loadConfiguration(defaultConf); @@ -184,14 +185,6 @@ //loadSutulaPresets(); - // Moved above due to crash - _preset not initialized when loadConfiguration called. Tim. - //_initialPreset = new - // Preset(new Subcategory(new Category(NULL, "NONE", 0), "NONE", 0), 0); - //for(int c = 0; c < NBRCHANNELS; c++) { - // _preset[c]=_initialPreset; - // setPreset(c); - //} - //update display gui //update mastervol unsigned char dataMasterVol[2]; @@ -337,56 +330,56 @@ void DeicsOnze::initCtrls() { int i=0; for(int k=0; ksensitivity.amplitude)); - //index is concidered on the half of the frequency of the LFO + //index is considered on the half of the frequency of the LFO _global.channel[c].lfoDelayMaxIndex = delay2Time(_preset[c]->lfo.delay)*_global.channel[c].lfoFreq*2; _global.channel[c].lfoDelayInct = @@ -1251,13 +1244,13 @@ QFile deicsonzeFile(fileName); if(!deicsonzeFile.open(QIODevice::ReadOnly)) { printf("Critical Error Cannot open file %s\n", - fileName.toAscii().data()); + fileName.toLatin1().data()); return; } QDomDocument domTree; if (!domTree.setContent(&deicsonzeFile )) { printf("Critical Error Parsing error for file %s\n", - fileName.toAscii().data()); + fileName.toLatin1().data()); deicsonzeFile.close(); return; } @@ -1602,10 +1595,10 @@ //--------------------------------------------------------- // lfoUpdate -// update the coefficent which multiplies the current inct +// update the coefficient which multiplies the current inct // in order to // get the right current frequency with respect to the lfo -// update the coefficent which multiplies the amplitude. +// update the coefficient which multiplies the amplitude. //--------------------------------------------------------- inline void lfoUpdate(Preset* p, Channel* p_c, float* wt) { double delayCoef; @@ -2068,7 +2061,7 @@ //nbrVoices //question? does the configurqtion has to save the number of //voices for each channel or not? - //temporarly or definitly under comments + //temporarly or definitely under comments /* if(qdEl.tagName()==NBRVOICESSTR) { setNbrVoices(qdEl.text().toInt()); @@ -2224,13 +2217,13 @@ QFile confFile(fileName); if(!confFile.open(QIODevice::ReadOnly)) { printf("Critical Error. Cannot open file %s\n", - fileName.toAscii().data()); + fileName.toLatin1().data()); return; } QDomDocument domTree; if (!domTree.setContent(&confFile )) { printf("Critical Error. Parsing error for file %s\n", - fileName.toAscii().data()); + fileName.toLatin1().data()); confFile.close(); return; } @@ -2324,8 +2317,8 @@ //save the set //*length = (version == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK *length = NUM_CONFIGLENGTH - + (_pluginIReverb?sizeof(float)*_pluginIReverb->plugin()->controlInPorts():0) - + (_pluginIChorus?sizeof(float)*_pluginIChorus->plugin()->controlInPorts():0) + + (_pluginIReverb?sizeof(float)*_pluginIReverb->plugin()->parameter():0) + + (_pluginIChorus?sizeof(float)*_pluginIChorus->plugin()->parameter():0) + baComp.size(); setupInitBuffer(*length); @@ -2393,7 +2386,7 @@ initBuffer[NUM_IS_REVERB_ON]=(unsigned char)_global.isReverbActivated; initBuffer[NUM_REVERB_RETURN]=(unsigned char)getReverbReturn(); initBuffer[NUM_REVERB_PARAM_NBR]= - (_pluginIReverb?(unsigned char)_pluginIReverb->plugin()->controlInPorts() : 0); + (_pluginIReverb?(unsigned char)_pluginIReverb->plugin()->parameter() : 0); strncpy((char*)&initBuffer[NUM_REVERB_LIB], (_pluginIReverb? _pluginIReverb->plugin()->lib().toLatin1().constData() : "\0"), @@ -2406,7 +2399,7 @@ initBuffer[NUM_IS_CHORUS_ON]=(unsigned char)_global.isChorusActivated; initBuffer[NUM_CHORUS_RETURN]=(unsigned char)getChorusReturn(); initBuffer[NUM_CHORUS_PARAM_NBR]= - (_pluginIChorus?(unsigned char)_pluginIChorus->plugin()->controlInPorts() : 0); + (_pluginIChorus?(unsigned char)_pluginIChorus->plugin()->parameter() : 0); strncpy((char*)&initBuffer[NUM_CHORUS_LIB], (_pluginIChorus? _pluginIChorus->plugin()->lib().toLatin1().constData() : "\0"), @@ -2650,17 +2643,17 @@ dataReverbRet[1]=(unsigned char)getReverbReturn(); MusECore::MidiPlayEvent evReverbRet(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataReverbRet, 2); _gui->writeEvent(evReverbRet); - MusECore::Plugin* p; - p = MusEGlobal::plugins.find((const char*)&data[NUM_REVERB_LIB], + MusESimplePlugin::Plugin* p; + p = MusESimplePlugin::plugins.find((const char*)&data[NUM_REVERB_LIB], (const char*)&data[NUM_REVERB_LABEL]); if(p) { initPluginReverb(p); - for(int i = 0; i < (int)_pluginIReverb->plugin()->controlInPorts(); i++) { + for(int i = 0; i < (int)_pluginIReverb->plugin()->parameter(); i++) { float val; //memcpy(&val, &data[(data[3] == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK memcpy(&val, &data[NUM_CONFIGLENGTH + sizeof(float)*i], sizeof(float)); - _pluginIReverb->putParam(i, val); + _pluginIReverb->setParam(i, val); } char dataBuildRev; dataBuildRev = SYSEX_BUILDGUIREVERB; @@ -2682,18 +2675,18 @@ dataChorusRet[1]=(unsigned char)getChorusReturn(); MusECore::MidiPlayEvent evChorusRet(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataChorusRet, 2); _gui->writeEvent(evChorusRet); - p = MusEGlobal::plugins.find((const char*)&data[NUM_CHORUS_LIB], + p = MusESimplePlugin::plugins.find((const char*)&data[NUM_CHORUS_LIB], (const char*)&data[NUM_CHORUS_LABEL]); if(p) { initPluginChorus(p); - for(int i = 0; i < (int)_pluginIChorus->plugin()->controlInPorts(); i++) { + for(int i = 0; i < (int)_pluginIChorus->plugin()->parameter(); i++) { float val; //memcpy(&val, &data[(data[3] == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK memcpy(&val, &data[NUM_CONFIGLENGTH + sizeof(float)*(int)data[NUM_REVERB_PARAM_NBR] + sizeof(float)*i], sizeof(float)); - _pluginIChorus->putParam(i, val); + _pluginIChorus->setParam(i, val); } char dataBuildCho; dataBuildCho = SYSEX_BUILDGUICHORUS; @@ -2719,7 +2712,7 @@ float delayfloat; memcpy(&delayfloat, &data[NUM_DELAY_BPM], sizeof(float)); //setDelayBPM(delayfloat); - if(_pluginIDelay) _pluginIDelay->putParam(0, delayfloat); + if(_pluginIDelay) _pluginIDelay->setParam(0, delayfloat); char dataDelayBPM[sizeof(float)+1]; dataDelayBPM[0] = SYSEX_DELAYBPM; memcpy(&dataDelayBPM[1], &delayfloat, sizeof(float)); @@ -2729,7 +2722,7 @@ _gui->writeEvent(evSysexDelayBPM); memcpy(&delayfloat, &data[NUM_DELAY_BEATRATIO], sizeof(float)); //setDelayBeatRatio(delayfloat); - if(_pluginIDelay) _pluginIDelay->putParam(1, delayfloat); + if(_pluginIDelay) _pluginIDelay->setParam(1, delayfloat); char dataDelayBeatRatio[sizeof(float)+1]; dataDelayBeatRatio[0] = SYSEX_DELAYBEATRATIO; memcpy(&dataDelayBeatRatio[1], &delayfloat, sizeof(float)); @@ -2739,7 +2732,7 @@ _gui->writeEvent(evSysexDelayBeatRatio); memcpy(&delayfloat, &data[NUM_DELAY_FEEDBACK], sizeof(float)); //setDelayFeedback(delayfloat); - if(_pluginIDelay) _pluginIDelay->putParam(2, delayfloat); + if(_pluginIDelay) _pluginIDelay->setParam(2, delayfloat); char dataDelayFeedback[sizeof(float)+1]; dataDelayFeedback[0] = SYSEX_DELAYFEEDBACK; memcpy(&dataDelayFeedback[1], &delayfloat, sizeof(float)); @@ -2749,7 +2742,7 @@ _gui->writeEvent(evSysexDelayFeedback); memcpy(&delayfloat, &data[NUM_DELAY_LFO_FREQ], sizeof(float)); //setDelayLFOFreq(delayfloat); - if(_pluginIDelay) _pluginIDelay->putParam(3, delayfloat); + if(_pluginIDelay) _pluginIDelay->setParam(3, delayfloat); char dataDelayLFOFreq[sizeof(float)+1]; dataDelayLFOFreq[0] = SYSEX_DELAYLFOFREQ; memcpy(&dataDelayLFOFreq[1], &delayfloat, sizeof(float)); @@ -2759,7 +2752,7 @@ _gui->writeEvent(evSysexDelayLFOFreq); memcpy(&delayfloat, &data[NUM_DELAY_LFO_DEPTH], sizeof(float)); //setDelayLFODepth(delayfloat); - if(_pluginIDelay) _pluginIDelay->putParam(4, delayfloat); + if(_pluginIDelay) _pluginIDelay->setParam(4, delayfloat); char dataDelayLFODepth[sizeof(float)+1]; dataDelayLFODepth[0] = SYSEX_DELAYLFODEPTH; memcpy(&dataDelayLFODepth[1], &delayfloat, sizeof(float)); @@ -3024,13 +3017,13 @@ } break; case SYSEX_SELECTREVERB: - MusECore::Plugin* pluginReverb; - memcpy(&pluginReverb, &d[1], sizeof(MusECore::Plugin*)); + MusESimplePlugin::Plugin* pluginReverb; + memcpy(&pluginReverb, &d[1], sizeof(MusESimplePlugin::Plugin*)); initPluginReverb(pluginReverb); break; case SYSEX_SELECTCHORUS: - MusECore::Plugin* pluginChorus; - memcpy(&pluginChorus, &d[1], sizeof(MusECore::Plugin*)); + MusESimplePlugin::Plugin* pluginChorus; + memcpy(&pluginChorus, &d[1], sizeof(MusESimplePlugin::Plugin*)); initPluginChorus(pluginChorus); break; case SYSEX_DELAYBPM: @@ -3698,7 +3691,7 @@ // getPatchName //--------------------------------------------------------- -QString DeicsOnze::getPatchName(int ch, int val, bool) const { +const char* DeicsOnze::getPatchName(int ch, int val, bool) const { if(_global.channel[ch].isEnable) { Preset* p_preset; int hbank = (val & 0xff0000) >> 16; @@ -3715,7 +3708,8 @@ if (p_preset) tempName=const_cast(p_preset->name.c_str()); return tempName; } - return " "; + //return " "; // ?? + return ""; } //--------------------------------------------------------- @@ -4096,7 +4090,7 @@ // write // synthesize n samples into buffer+offset //--------------------------------------------------------- -void DeicsOnze::process(float** buffer, int offset, int n) { +void DeicsOnze::process(unsigned pos, float** buffer, int offset, int n) { /* //Process messages from the gui while (_gui->fifoSize()) { @@ -4432,46 +4426,61 @@ //apply Filter if(_global.filter) _dryFilter->process(leftOutput, rightOutput, n); //Chorus - if(_pluginIChorus && _global.isChorusActivated) { - //apply Filter - if(_global.filter) _chorusFilter->process(tempOutputChorus[0], - tempOutputChorus[1], n); - //apply Chorus - _pluginIChorus->apply(n, 2, tempInputChorus, tempOutputChorus); - for(int i = 0; i < n; i++) { - leftOutput[i] += - tempOutputChorus[0][i] * _global.chorusReturn * _global.masterVolume; - rightOutput[i] += - tempOutputChorus[1][i] * _global.chorusReturn * _global.masterVolume; + if(_pluginIChorus) { + if(_global.isChorusActivated) + { + //apply Filter + if(_global.filter) _chorusFilter->process(tempOutputChorus[0], + tempOutputChorus[1], n); + //apply Chorus + _pluginIChorus->apply(pos, n, 2, tempInputChorus, tempOutputChorus); + for(int i = 0; i < n; i++) { + leftOutput[i] += + tempOutputChorus[0][i] * _global.chorusReturn * _global.masterVolume; + rightOutput[i] += + tempOutputChorus[1][i] * _global.chorusReturn * _global.masterVolume; + } } + else + _pluginIChorus->apply(pos, n, 0, 0, 0); // Just process controls only, not audio (do not 'run'). Tim. } //Reverb - if(_pluginIReverb && _global.isReverbActivated) { - //apply Filter - if(_global.filter) _reverbFilter->process(tempOutputReverb[0], - tempOutputReverb[1], n); - //apply Reverb - _pluginIReverb->apply(n, 2, tempInputReverb, tempOutputReverb); - for(int i = 0; i < n; i++) { - leftOutput[i] += - tempOutputReverb[0][i] * _global.reverbReturn * _global.masterVolume; - rightOutput[i] += - tempOutputReverb[1][i] * _global.reverbReturn * _global.masterVolume; + if(_pluginIReverb) { + if(_global.isReverbActivated) + { + //apply Filter + if(_global.filter) _reverbFilter->process(tempOutputReverb[0], + tempOutputReverb[1], n); + //apply Reverb + _pluginIReverb->apply(pos, n, 2, tempInputReverb, tempOutputReverb); + for(int i = 0; i < n; i++) { + leftOutput[i] += + tempOutputReverb[0][i] * _global.reverbReturn * _global.masterVolume; + rightOutput[i] += + tempOutputReverb[1][i] * _global.reverbReturn * _global.masterVolume; + } } + else + _pluginIReverb->apply(pos, n, 0, 0, 0); // Just process controls only, not audio (do not 'run'). Tim. } //Delay - if(_pluginIDelay && _global.isDelayActivated) { - //apply Filter - if(_global.filter) _delayFilter->process(tempOutputDelay[0], - tempOutputDelay[1], n); - //apply Delay - _pluginIDelay->apply(n, 2, tempInputDelay, tempOutputDelay); - for(int i = 0; i < n; i++) { - leftOutput[i] += - tempOutputDelay[0][i] * _global.delayReturn * _global.masterVolume; - rightOutput[i] += - tempOutputDelay[1][i] * _global.delayReturn * _global.masterVolume; + if(_pluginIDelay) { + if(_global.isDelayActivated) + { + //apply Filter + if(_global.filter) _delayFilter->process(tempOutputDelay[0], + tempOutputDelay[1], n); + //apply Delay + _pluginIDelay->apply(pos, n, 2, tempInputDelay, tempOutputDelay); + for(int i = 0; i < n; i++) { + leftOutput[i] += + tempOutputDelay[0][i] * _global.delayReturn * _global.masterVolume; + rightOutput[i] += + tempOutputDelay[1][i] * _global.delayReturn * _global.masterVolume; + } } + else + _pluginIDelay->apply(pos, n, 0, 0, 0); // Just process controls only, not audio (do not 'run'). Tim. } } @@ -4482,10 +4491,17 @@ class QWidget; -static Mess* instantiate(int sr, QWidget*, QString* /* projectPathPtr */, const char*) +static Mess* instantiate(unsigned long long /*parentWinId*/, const char* /*name*/, const MessConfig* config) { + DEI_configPath = QString(config->_configPath); + DEI_globalLibPath = QString(config->_globalLibPath); + DEI_sharePath = QString(config->_globalSharePath); + DEI_segmentSize = config->_segmentSize; + DEI_sampleRate = config->_sampleRate; + DEI_useDenormalBias = config->_useDenormalBias; + DEI_denormalBias = config->_denormalBias; DeicsOnze* deicsonze = new DeicsOnze(); - deicsonze->setSampleRate(sr); + deicsonze->setSampleRate(config->_sampleRate); return deicsonze; } diff -Nru muse-2.1.2/synti/deicsonze/deicsonzegui.cpp muse-3.0.2+ds1/synti/deicsonze/deicsonzegui.cpp --- muse-2.1.2/synti/deicsonze/deicsonzegui.cpp 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/deicsonzegui.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -34,20 +34,17 @@ #include #include #include -#include -#include "muse/midi.h" -#include "muse/midictrl.h" -#include "config.h" +#include "muse/midi_consts.h" +#include "muse/midictrl_consts.h" #include "common_defs.h" #include "deicsonzegui.h" -#include "plugin.h" -///#include "plugingui.h" -#include "plugins/pandelay/pandelaymodel.h" +#include "libsimpleplugin/simpler_plugin.h" +#include "libsimpleplugin/simpler_plugingui.h" -//#include "deicsonzegui.h" +//#include "plugins/pandelay/pandelaymodel.h" namespace MusEGui { class PluginDialog; @@ -416,14 +413,11 @@ this, SLOT(setPreset(QTreeWidgetItem*))); connect(presetListView, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(setPreset(QTreeWidgetItem*))); - //Connect socketnotifier to fifo - QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); - connect(s, SIGNAL(activated(int)), SLOT(readMessage(int))); - - QString sharePath(MusEGlobal::museGlobalShare); - // Tim. - updateInitSetPath(sharePath + QString("/presets/deicsonze/SutulaBank.dei")); // Tim. - updateBackgroundPixPath(sharePath + QString("/wallpapers/paper2.jpg")); // Tim. + + connect(this->getGuiSignal(),SIGNAL(wakeup()),this,SLOT(readMessage())); + + updateInitSetPath(DEI_sharePath + QString("/presets/deicsonze/SutulaBank.dei")); // Tim. + updateBackgroundPixPath(DEI_sharePath + QString("/wallpapers/paper2.jpg")); // Tim. updateBackgroundPixCheckBox(false); setTextColor(reinterpret_cast(*tColor)); @@ -557,7 +551,7 @@ // saveDefaultConfiguration //----------------------------------------------------------- void DeicsOnzeGui::saveDefaultConfiguration() { - QString filename = MusEGlobal::configPath + QString("/" DEICSONZESTR ".dco"); + QString filename = DEI_configPath + QString("/" DEICSONZESTR ".dco"); if(!filename.isEmpty()) { QFile f(filename); f.open(QIODevice::WriteOnly); @@ -1396,7 +1390,7 @@ // processEvent(const MidiEvent&); //----------------------------------------------------------- void DeicsOnzeGui::processEvent(const MusECore::MidiPlayEvent& ev) { - //Controler + //Controller if (ev.type() == MusECore::ME_CONTROLLER) { //printf("MusECore::ME_CONTROLLER\n"); int id=ev.dataA(); @@ -1719,9 +1713,9 @@ } /*! - \fn SimpleSynthGui::readMessage(int) + \fn SimpleSynthGui::readMessage() */ -void DeicsOnzeGui::readMessage(int) +void DeicsOnzeGui::readMessage() { MessGui::readMessage(); } @@ -2435,23 +2429,23 @@ QTreeWidgetItem* pre = presetListView->currentItem(); if(pre) { //TODO : must be changed with SysEx - _deicsOnze->_preset[_currentChannel]->name = n.toAscii().data(); - pre->setText(1,n.toAscii().data()); + _deicsOnze->_preset[_currentChannel]->name = n.toLatin1().data(); + pre->setText(1,n.toLatin1().data()); } } void DeicsOnzeGui::setSubcategoryName(const QString& s) { QTreeSubcategory* sub = (QTreeSubcategory*) subcategoryListView->currentItem(); if(sub) {//must be changed with SysEx - sub->_subcategory->_subcategoryName=s.toAscii().data(); - sub->setText(1, s.toAscii().data()); + sub->_subcategory->_subcategoryName=s.toLatin1().data(); + sub->setText(1, s.toLatin1().data()); } } void DeicsOnzeGui::setCategoryName(const QString& c) { QTreeCategory* cat = (QTreeCategory*) categoryListView->currentItem(); if(cat) {//must be changed with SysEx - cat->_category->_categoryName=c.toAscii().data(); - cat->setText(1, c.toAscii().data()); + cat->_category->_categoryName=c.toLatin1().data(); + cat->setText(1, c.toLatin1().data()); } } void DeicsOnzeGui::setHBank(int hb) { @@ -2525,7 +2519,7 @@ message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_INITSETPATH; - strncpy((char*)&message[3], s.toAscii().data(), MAXSTRLENGTHINITSETPATH); + strncpy((char*)&message[3], s.toLatin1().data(), MAXSTRLENGTHINITSETPATH); sendSysex(message, 3+MAXSTRLENGTHINITSETPATH); } void DeicsOnzeGui::setBrowseInitSetPath() { @@ -2563,7 +2557,7 @@ message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_BACKGROUNDPIXPATH; - strncpy((char*)&message[3], s.toAscii().data(), + strncpy((char*)&message[3], s.toLatin1().data(), MAXSTRLENGTHBACKGROUNDPIXPATH); sendSysex(message, 3+MAXSTRLENGTHBACKGROUNDPIXPATH); } @@ -2604,16 +2598,18 @@ sendSysex(message, 4); } void DeicsOnzeGui::setSelectChorusPlugin() { - MusECore::Plugin* pluginChorus = MusEGui::PluginDialog::getPlugin(this); + MusESimplePlugin::Plugin* pluginChorus = + MusESimplePlugin::SimplerPluginChooser::getPlugin(this); if(pluginChorus) { - unsigned char message[3+sizeof(MusECore::Plugin*)]; + unsigned char message[3+sizeof(MusESimplePlugin::Plugin*)]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_SELECTCHORUS; - memcpy(&message[3], &pluginChorus, sizeof(MusECore::Plugin*)); - sendSysex(message, 3+sizeof(MusECore::Plugin*)); + memcpy(&message[3], &pluginChorus, sizeof(MusESimplePlugin::Plugin*)); + sendSysex(message, 3+sizeof(MusESimplePlugin::Plugin*)); } } + /*void DeicsOnzeGui::setPanChorus1(double i) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; @@ -2682,14 +2678,14 @@ sendSysex(message, 4); } void DeicsOnzeGui::setSelectReverbPlugin() { - MusECore::Plugin* pluginReverb = MusEGui::PluginDialog::getPlugin(this); + MusESimplePlugin::Plugin* pluginReverb = MusESimplePlugin::SimplerPluginChooser::getPlugin(this); if(pluginReverb) { - unsigned char message[3+sizeof(MusECore::Plugin*)]; + unsigned char message[3+sizeof(MusESimplePlugin::Plugin*)]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_SELECTREVERB; - memcpy(&message[3], &pluginReverb, sizeof(MusECore::Plugin*)); - sendSysex(message, 3+sizeof(MusECore::Plugin*)); + memcpy(&message[3], &pluginReverb, sizeof(MusESimplePlugin::Plugin*)); + sendSysex(message, 3+sizeof(MusESimplePlugin::Plugin*)); } } @@ -3127,15 +3123,6 @@ void DeicsOnzeGui::setChannelDelay(int d) { sendController(_currentChannel, MusECore::CTRL_VARIATION_SEND, (unsigned char)d); } -//void DeicsOnzeGui::setDelayTime(int t) { -// unsigned char message[4]; -// message[0]=MUSE_SYNTH_SYSEX_MFG_ID; -// message[1]=DEICSONZE_UNIQUE_ID; -// message[2]=SYSEX_DELAYTIME; -// message[3]=(unsigned char)t; -// sendSysex(message, 4); -// updateDelayTime(t); -//} void DeicsOnzeGui::setDelayBPM(double t) { //int it = (int)(((t - MINDELAYTIME) / (MAXDELAYTIME - MINDELAYTIME))*255.0); unsigned char message[sizeof(float)+3]; @@ -3158,15 +3145,6 @@ message[3]=(unsigned char)f; sendSysex(message, sizeof(float)+3); } -//void DeicsOnzeGui::setDelayFeedback(int f) { -// unsigned char message[4]; -// message[0]=MUSE_SYNTH_SYSEX_MFG_ID; -// message[1]=DEICSONZE_UNIQUE_ID; -// message[2]=SYSEX_DELAYFEEDBACK; -// message[3]=(unsigned char)f; -// sendSysex(message, 4); -// updateDelayFeedback(f); -//} void DeicsOnzeGui::setDelayFeedback(double t) { //int idf = (int)(f*128.0+128.0); unsigned char message[sizeof(float)+3]; @@ -3178,15 +3156,6 @@ sendSysex(message, sizeof(float)+3); //updateDelayFeedback(idf); } -//void DeicsOnzeGui::setDelayPanLFOFreq(int pf) { -// unsigned char message[4]; -// message[0]=MUSE_SYNTH_SYSEX_MFG_ID; -// message[1]=DEICSONZE_UNIQUE_ID; -// message[2]=SYSEX_DELAYLFOFREQ; -// message[3]=(unsigned char)pf; -// sendSysex(message, 4); -// updateDelayPanLFOFreq(pf); -//} void DeicsOnzeGui::setDelayPanLFOFreq(double pf) { //int ipf = (int)(((pf - MINFREQ) / (MAXFREQ - MINFREQ))*255.0); unsigned char message[sizeof(float)+3]; @@ -3198,15 +3167,6 @@ sendSysex(message, sizeof(float)+3); //updateDelayPanLFOFreq(ipf); } -//void DeicsOnzeGui::setDelayPanLFODepth(int pd) { -// unsigned char message[4]; -// message[0]=MUSE_SYNTH_SYSEX_MFG_ID; -// message[1]=DEICSONZE_UNIQUE_ID; -// message[2]=SYSEX_DELAYLFODEPTH; -// message[3]=(unsigned char)pd; -// sendSysex(message, 4); -// updateDelayPanLFODepth(pd); -//} void DeicsOnzeGui::setDelayPanLFODepth(double pd) { //int ipd = (int)(pd*255.0); unsigned char message[sizeof(float)+3]; diff -Nru muse-2.1.2/synti/deicsonze/deicsonzegui.h muse-3.0.2+ds1/synti/deicsonze/deicsonzegui.h --- muse-2.1.2/synti/deicsonze/deicsonzegui.h 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/deicsonzegui.h 2018-01-06 20:31:35.000000000 +0000 @@ -139,7 +139,7 @@ QString lastDir; private slots: - void readMessage(int); + void readMessage(); void setEnabledChannel(bool); void setChangeChannel(int); void setPanic(); diff -Nru muse-2.1.2/synti/deicsonze/deicsonze.h muse-3.0.2+ds1/synti/deicsonze/deicsonze.h --- muse-2.1.2/synti/deicsonze/deicsonze.h 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/deicsonze.h 2018-01-29 20:07:03.000000000 +0000 @@ -39,7 +39,7 @@ #include "deicsonzeplugin.h" #include "deicsonzefilter.h" #include "libsynti/mess.h" -#include "plugin.h" +#include "libsimpleplugin/simpler_plugin.h" #define DEICSONZESTR "deicsonze" @@ -81,7 +81,7 @@ #define COEFMAXATTACK 7.5 #define COEFERRDECSUS 0.01 //for the transition between DECAY and SUSTAIN #define COEFERRSUSREL 0.001 //from SUSTAIN or RELEASE until no sound -//#define ERRPORTA 0.001 //dectection to stop portamento +//#define ERRPORTA 0.001 //detection to stop portamento #define COEFPORTA 0.01 //adjusted such that 10 second/octave with max porta #define COEFPITCHENV 0.00000025 //adjust according to a real DX11....??? #define COEFDECAY 1.0 @@ -335,8 +335,8 @@ //--------------------------------------------------------- struct Voice { - bool hasAttractor;//true iff the voice has an attractor (portamento occuring) - double attractor; //contains some coeficent for portamento TODO + bool hasAttractor;//true iff the voice has an attractor (portamento occurring) + double attractor; //contains some coefficient for portamento TODO PitchEnvState pitchEnvState; double pitchEnvCoefInct; double pitchEnvCoefInctPhase1; @@ -474,13 +474,13 @@ Preset* _initialPreset; //FX - MusECore::PluginI* _pluginIReverb; - MusECore::PluginI* _pluginIChorus; - MusECore::PluginI* _pluginIDelay; - - void initPluginReverb(MusECore::Plugin*); - void initPluginChorus(MusECore::Plugin*); - void initPluginDelay(MusECore::Plugin*); + MusESimplePlugin::PluginI* _pluginIReverb; + MusESimplePlugin::PluginI* _pluginIChorus; + MusESimplePlugin::PluginI* _pluginIDelay; + + void initPluginReverb(MusESimplePlugin::Plugin*); + void initPluginChorus(MusESimplePlugin::Plugin*); + void initPluginDelay(MusESimplePlugin::Plugin*); void setReverbParam(int i, float val); float getReverbParam(int i) const; @@ -592,24 +592,20 @@ bool sysex(int length, const unsigned char* data, bool fromGui); virtual bool sysex(int l, const unsigned char* d); - virtual QString getPatchName(int ch, int number, bool) const; + virtual const char* getPatchName(int ch, int number, bool) const; virtual const MidiPatch* getPatchInfo(int, const MidiPatch *) const; virtual int getControllerInfo(int arg1, const char** arg2, int* arg3, int* arg4, int* arg5, int* arg6) const; - ///virtual void getInitData(int* length, const unsigned char** data) const; virtual void getInitData(int* length, const unsigned char** data); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** data) const; virtual bool playNote(int channel, int pitch, int velo); virtual void processMessages(); - virtual void process(float** buffer, int offset, int n); + virtual void process(unsigned pos, float** buffer, int offset, int n); // GUI interface routines - //virtual bool hasGui() const { return true; } - //virtual bool guiVisible() const; - //virtual void showGui(bool); virtual bool hasNativeGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); @@ -620,5 +616,11 @@ virtual ~DeicsOnze(); }; +extern QString DEI_configPath; +extern QString DEI_sharePath; +extern unsigned int DEI_segmentSize; +extern int DEI_sampleRate; +extern bool DEI_useDenormalBias; +extern float DEI_denormalBias; #endif /* __DEICSONZE_H */ diff -Nru muse-2.1.2/synti/deicsonze/deicsonzeplugin.cpp muse-3.0.2+ds1/synti/deicsonze/deicsonzeplugin.cpp --- muse-2.1.2/synti/deicsonze/deicsonzeplugin.cpp 2013-03-28 15:17:10.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/deicsonzeplugin.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -29,11 +29,10 @@ #include "deicsonze.h" #include "deicsonzeplugin.h" -#include "plugin.h" -///#include "plugingui.h" +#include "libsimpleplugin/simpler_plugin.h" #include "ctrl.h" #include "fastlog.h" -#include "muse/midi.h" +#include "muse/midi_consts.h" #include "awl/floatentry.h" #include "awl/slider.h" #include "awl/checkbox.h" @@ -44,15 +43,14 @@ class PluginDialog; -void DeicsOnze::initPluginReverb(MusECore::Plugin* pluginReverb) { +void DeicsOnze::initPluginReverb(MusESimplePlugin::Plugin* pluginReverb) { //init plugin if(_pluginIReverb) delete(_pluginIReverb); - _pluginIReverb = new MusECore::PluginI(); + _pluginIReverb = new MusESimplePlugin::PluginI(); - _pluginIReverb->initPluginInstance(pluginReverb, 2); + _pluginIReverb->initPluginInstance( + pluginReverb, 2, sampleRate(), DEI_segmentSize, DEI_useDenormalBias, DEI_denormalBias); - //for(int i = 0; i < pluginReverb->parameter(); i++) { - //for(int i = 0; i < (int)pluginReverb->controlInPorts(); i++) { for(int i = 0; i < (int)_pluginIReverb->parameters(); i++) { @@ -70,7 +68,7 @@ //setReverbParam(i, pluginReverb->defaultValue(i)); //setReverbParam(i, _pluginIReverb->defaultValue(i)); - _pluginIReverb->putParam(i, _pluginIReverb->defaultValue(i)); + _pluginIReverb->setParam(i, _pluginIReverb->defaultValue(i)); } //send build gui to the gui @@ -80,11 +78,11 @@ _gui->writeEvent(evSysex); } -void DeicsOnze::initPluginChorus(MusECore::Plugin* pluginChorus) { +void DeicsOnze::initPluginChorus(MusESimplePlugin::Plugin* pluginChorus) { if(_pluginIChorus) delete(_pluginIChorus); - _pluginIChorus = new MusECore::PluginI(); - - _pluginIChorus->initPluginInstance(pluginChorus, 2); + _pluginIChorus = new MusESimplePlugin::PluginI(); + _pluginIChorus->initPluginInstance( + pluginChorus, 2, sampleRate(), DEI_segmentSize, DEI_useDenormalBias, DEI_denormalBias); //for(int i = 0; i < pluginChorus->parameter(); i++) { for(int i = 0; i < (int)_pluginIChorus->parameters(); i++) { @@ -98,7 +96,7 @@ //setChorusParam(i, pluginChorus->defaultValue(i)); //setChorusParam(i, _pluginIChorus->defaultValue(i)); - _pluginIChorus->putParam(i, _pluginIChorus->defaultValue(i)); + _pluginIChorus->setParam(i, _pluginIChorus->defaultValue(i)); } //send build gui to the gui @@ -108,13 +106,13 @@ _gui->writeEvent(evSysex); } -void DeicsOnze::initPluginDelay(MusECore::Plugin* pluginDelay) { +void DeicsOnze::initPluginDelay(MusESimplePlugin::Plugin* pluginDelay) { if(_pluginIDelay) delete(_pluginIDelay); - _pluginIDelay = new MusECore::PluginI(); - - _pluginIDelay->initPluginInstance(pluginDelay, 2); + _pluginIDelay = new MusESimplePlugin::PluginI(); - //for(int i = 0; i < pluginDelay->parameter(); i++) { + _pluginIDelay->initPluginInstance( + pluginDelay, 2, sampleRate(), DEI_segmentSize, DEI_useDenormalBias, DEI_denormalBias); + for(int i = 0; i < (int)_pluginIDelay->parameters(); i++) { // FIXME FIXME Tim @@ -125,11 +123,12 @@ */ //setChorusParam(i, pluginDelay->defaultValue(i)); - setDelayParam(i, _pluginIDelay->defaultValue(i)); + //setDelayParam(i, _pluginIDelay->defaultValue(i)); + _pluginIDelay->setParam(i, _pluginIDelay->defaultValue(i)); } //setDelayDryWet(1); - _pluginIDelay->putParam(5, 1.0); + _pluginIDelay->setParam(5, 1.0); float f; char dataDelayBPM[sizeof(float)+1]; @@ -313,7 +312,7 @@ void DeicsOnzeGui::buildGuiReverb() { if(!_deicsOnze->_pluginIReverb) return; - MusECore::PluginI* plugI = _deicsOnze->_pluginIReverb; + MusESimplePlugin::PluginI* plugI = _deicsOnze->_pluginIReverb; QString name = plugI->name(); name.resize(name.size()-2); updateLadspaReverbLineEdit(name); @@ -335,20 +334,19 @@ if(!_reverbFloatEntryVector.empty()) _reverbFloatEntryVector.clear(); if(!_reverbCheckBoxVector.empty()) _reverbCheckBoxVector.clear(); //build sliders - //for(int i = 0; i < plugI->plugin()->parameter(); i++) { - for(int i = 0; i < (int)plugI->plugin()->controlInPorts(); i++) { + for(int i = 0; i < (int)plugI->plugin()->parameter(); i++) { float min, max, val; plugI->range(i, &min, &max); val = _deicsOnze->getReverbParam(i); - if(plugI->ctrlValueType(i) == MusECore::VAL_BOOL) + if(plugI->isBool(i)) addPluginCheckBox(i, plugI->paramName(i), val > 0.0, _reverbSuperWidget, grid, true); - else if(plugI->ctrlValueType(i) == MusECore::VAL_INT) + else if(plugI->isInt(i)) addPluginIntSlider(i, plugI->paramName(i), rint(min), rint(max), rint(val), _reverbSuperWidget, grid, true); else - addPluginSlider(i, plugI->paramName(i), plugI->ctrlValueType(i) == MusECore::VAL_LOG, + addPluginSlider(i, plugI->paramName(i), plugI->isLog(i), min, max, val, _reverbSuperWidget, grid, true); } //update colors of the new sliders (and the whole gui actually) @@ -359,7 +357,7 @@ void DeicsOnzeGui::buildGuiChorus() { if(!_deicsOnze->_pluginIChorus) return; - MusECore::PluginI* plugI = _deicsOnze->_pluginIChorus; + MusESimplePlugin::PluginI* plugI = _deicsOnze->_pluginIChorus; QString name = plugI->name(); name.resize(name.size()-2); updateLadspaChorusLineEdit(name); @@ -381,20 +379,19 @@ if(!_chorusFloatEntryVector.empty()) _chorusFloatEntryVector.clear(); if(!_chorusCheckBoxVector.empty()) _chorusCheckBoxVector.clear(); //build sliders - //for(int i = 0; i < plugI->plugin()->parameter(); i++) { - for(int i = 0; i < (int)plugI->plugin()->controlInPorts(); i++) { + for(int i = 0; i < (int)plugI->plugin()->parameter(); i++) { float min, max, val; plugI->range(i, &min, &max); val = _deicsOnze->getChorusParam(i); - if(plugI->ctrlValueType(i) == MusECore::VAL_BOOL) + if(plugI->isBool(i)) addPluginCheckBox(i, plugI->paramName(i), val > 0.0, _chorusSuperWidget, grid, false); - else if(plugI->ctrlValueType(i) == MusECore::VAL_INT) + else if(plugI->isInt(i)) addPluginIntSlider(i, plugI->paramName(i), rint(min), rint(max), rint(val), _chorusSuperWidget, grid, false); else - addPluginSlider(i, plugI->paramName(i), plugI->ctrlValueType(i) == MusECore::VAL_LOG, + addPluginSlider(i, plugI->paramName(i), plugI->isLog(i), min, max, val, _chorusSuperWidget, grid, false); } //update colors of the new sliders (and the whole gui actually) @@ -406,42 +403,50 @@ //of the parameter because it sends a double and does not //change any thing void DeicsOnzeGui::setReverbCheckBox(double v, int i) { - if(i>=256) { - printf("setReverbCheckBox Error : controller index >= 256\n"); - return; - } - float f = (float)v; - unsigned char message[sizeof(float)+4]; - message[0]=MUSE_SYNTH_SYSEX_MFG_ID; - message[1]=DEICSONZE_UNIQUE_ID; - message[2]=SYSEX_REVERBPARAM; - message[3]=(unsigned char)i; - memcpy(&message[4], &f, sizeof(float)); - sendSysex(message, sizeof(float)+4); +// REMOVE Tim. Or keep. TESTING... +// if(i>=256) { +// printf("setReverbCheckBox Error : controller index >= 256\n"); +// return; +// } +// float f = (float)v; +// unsigned char message[sizeof(float)+4]; +// message[0]=MUSE_SYNTH_SYSEX_MFG_ID; +// message[1]=DEICSONZE_UNIQUE_ID; +// message[2]=SYSEX_REVERBPARAM; +// message[3]=(unsigned char)i; +// memcpy(&message[4], &f, sizeof(float)); +// sendSysex(message, sizeof(float)+4); + + // Putting directly to the control FIFO without SYSEX should be OK. Tim. + _deicsOnze->setReverbParam(i, v); } //setChorusCheckBox is used, by the way, to send the value //of the parameter because it sends a double and does not //change any thing void DeicsOnzeGui::setChorusCheckBox(double v, int i) { - if(i>=256) { - printf("setChorusCheckBox Error : controller index >= 256\n"); - return; - } - float f = (float)v; - unsigned char message[sizeof(float)+4]; - message[0]=MUSE_SYNTH_SYSEX_MFG_ID; - message[1]=DEICSONZE_UNIQUE_ID; - message[2]=SYSEX_CHORUSPARAM; - message[3]=(unsigned char)i; - memcpy(&message[4], &f, sizeof(float)); - sendSysex(message, sizeof(float)+4); +// REMOVE Tim. Or keep. TESTING... +// if(i>=256) { +// printf("setChorusCheckBox Error : controller index >= 256\n"); +// return; +// } +// float f = (float)v; +// unsigned char message[sizeof(float)+4]; +// message[0]=MUSE_SYNTH_SYSEX_MFG_ID; +// message[1]=DEICSONZE_UNIQUE_ID; +// message[2]=SYSEX_CHORUSPARAM; +// message[3]=(unsigned char)i; +// memcpy(&message[4], &f, sizeof(float)); +// sendSysex(message, sizeof(float)+4); + + // Putting directly to the control FIFO without SYSEX should be OK. Tim. + _deicsOnze->setChorusParam(i, v); } void DeicsOnzeGui::setReverbFloatEntry(double v, int i) { if(_deicsOnze->_pluginIReverb) { - - if(_deicsOnze->_pluginIReverb->ctrlValueType(i) == MusECore::VAL_INT) v = rint(v); + + if(_deicsOnze->_pluginIReverb->isInt(i)) v = rint(v); updateReverbFloatEntry(v, i); updateReverbSlider(v, i); setReverbCheckBox(v, i); //because this send the SYSEX @@ -451,7 +456,7 @@ void DeicsOnzeGui::setReverbSlider(double v, int i) { if(_deicsOnze->_pluginIReverb) { - if(_deicsOnze->_pluginIReverb->ctrlValueType(i) == MusECore::VAL_INT) v = rint(v); + if(_deicsOnze->_pluginIReverb->isInt(i)) v = rint(v); updateReverbFloatEntry(v, i); updateReverbSlider(v, i); setReverbCheckBox(v, i); //because this send the SYSEX @@ -460,8 +465,7 @@ } void DeicsOnzeGui::setChorusFloatEntry(double v, int i) { if(_deicsOnze->_pluginIReverb) { - - if(_deicsOnze->_pluginIChorus->ctrlValueType(i) == MusECore::VAL_INT) v = rint(v); + if(_deicsOnze->_pluginIChorus->isInt(i)) v = rint(v); updateChorusFloatEntry(v, i); updateChorusSlider(v, i); setChorusCheckBox(v, i); //because this send the SYSEX @@ -470,8 +474,7 @@ } void DeicsOnzeGui::setChorusSlider(double v, int i) { if(_deicsOnze->_pluginIReverb) { - - if(_deicsOnze->_pluginIChorus->ctrlValueType(i) == MusECore::VAL_INT) v = rint(v); + if(_deicsOnze->_pluginIChorus->isInt(i)) v = rint(v); updateChorusSlider(v, i); updateChorusFloatEntry(v, i); setChorusCheckBox(v, i); //because this send the SYSEX diff -Nru muse-2.1.2/synti/deicsonze/deicsonzeplugin.h muse-3.0.2+ds1/synti/deicsonze/deicsonzeplugin.h --- muse-2.1.2/synti/deicsonze/deicsonzeplugin.h 2013-03-28 15:17:10.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/deicsonzeplugin.h 2018-01-29 20:07:03.000000000 +0000 @@ -32,7 +32,7 @@ #include "deicsonze.h" #include "deicsonzegui.h" -#include "plugins/pandelay/pandelaymodel.h" +//#include "plugins/pandelay/pandelaymodel.h" class DeicsOnze; class DeicsOnzeGui; diff -Nru muse-2.1.2/synti/deicsonze/deicsonzepreset.cpp muse-3.0.2+ds1/synti/deicsonze/deicsonzepreset.cpp --- muse-2.1.2/synti/deicsonze/deicsonzepreset.cpp 2013-03-28 15:17:10.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/deicsonzepreset.cpp 2017-12-04 21:01:19.000000000 +0000 @@ -551,7 +551,7 @@ if (setEl.isNull()) continue; if (setEl.tagName() == "setName") - _setName=setEl.text().toAscii().data(); + _setName=setEl.text().toLatin1().data(); if (setEl.tagName() == "deicsOnzeCategory") { //load category QString version = setEl.attribute(QString("version")); @@ -586,7 +586,7 @@ if (catEl.isNull()) continue; if (catEl.tagName() == "categoryName") - _categoryName=catEl.text().toAscii().data(); + _categoryName=catEl.text().toLatin1().data(); if (catEl.tagName() == "hbank") _hbank=catEl.text().toInt(); if (catEl.tagName() == "deicsOnzeSubcategory") { @@ -625,7 +625,7 @@ if (subEl.isNull()) continue; if (subEl.tagName() == "subcategoryName") - _subcategoryName=subEl.text().toAscii().data(); + _subcategoryName=subEl.text().toLatin1().data(); if (subEl.tagName() == "lbank") _lbank=subEl.text().toInt(); if (subEl.tagName() == "deicsOnzePreset") { @@ -911,7 +911,7 @@ // globalDetune=presetEl.text().toInt(); //Names else if(presetEl.tagName()=="name") - name=presetEl.text().toAscii().data(); + name=presetEl.text().toLatin1().data(); //prog else if(presetEl.tagName()=="prog") prog=presetEl.text().toInt(); @@ -960,36 +960,36 @@ xml->tag(PMODSENSSTR, sensitivity.pitch); xml->tag(AMSSTR, sensitivity.amplitude); for(int i=0; itag(s, QString((sensitivity.ampOn[i]==true? "on":"off"))); } for(int i=0; itag(s, sensitivity.egBias[i]); } for(int i=0; itag(s, sensitivity.keyVelocity[i]); } xml->etag("sensitivity"); //frequency xml->stag("frequency"); for(int i=0; itag(s, frequency[i].ratio); } for(int i=0; itag(s, QString((frequency[i].isFix==true? "yes":"no"))); } for(int i=0; itag(s, frequency[i].freq); } xml->etag("frequency"); //oscWave for(int i=0; itag(s, QString((oscWave[i]==W1?"W1": (oscWave[i]==W2?"W2": (oscWave[i]==W3?"W3": @@ -1000,33 +1000,33 @@ } //detune for(int i=0; itag(s, detune[i]); } //eg xml->stag("eg"); for(int i=0; itag(s, eg[i].ar); } for(int i=0; itag(s, eg[i].d1r); } for(int i=0; itag(s, eg[i].d1l); } for(int i=0; itag(s, eg[i].d2r); } for(int i=0; itag(s, eg[i].rr); } for(int i=0; itag(s, QString((eg[i].egShift==VOF?"VOF": (eg[i].egShift==V48?"V48": (eg[i].egShift==V24?"V24":"V12"))))); @@ -1043,17 +1043,17 @@ xml->etag("pitchEg"); //outLevel for(int i=0; itag(s, outLevel[i]); } //scaling xml->stag("scaling"); for(int i=0; itag(s, scaling.rate[i]); } for(int i=0; itag(s, scaling.level[i]); } xml->etag("scaling"); @@ -1117,7 +1117,7 @@ for(int i=0; i #include #include "al/xml.h" +#include "muse/midictrl_consts.h" #define NBROP 4 //do not change -#define MAXCHARTAG 64 +#define MAXCHARTAG 256 #define PROG_NBR 128 #define LBANK_NBR 128 @@ -46,7 +47,8 @@ // number of ctrl // following the internal DX11 organization (c.f T81Z manual) //--------------------------------------------------------- -#define CTRLOFFSET 0x100 +//#define CTRLOFFSET 0x100 +#define CTRLOFFSET (MusECore::CTRL_NRPN14_OFFSET) #define DECAPAR1 13 #define ARSTR "AR" #define ARLONGSTR "AttackRate" diff -Nru muse-2.1.2/synti/deicsonze/README muse-3.0.2+ds1/synti/deicsonze/README --- muse-2.1.2/synti/deicsonze/README 2013-03-28 15:17:10.000000000 +0000 +++ muse-3.0.2+ds1/synti/deicsonze/README 2018-01-26 21:59:38.000000000 +0000 @@ -27,7 +27,7 @@ Not every function is implemented but it's already sounding like the original (even better). Pitch bend, modulation and some others are not implemented yet. -The files with the extention .fig in the directory pics have been made +The files with the extension .fig in the directory pics have been made with xfig. ----------- diff -Nru muse-2.1.2/synti/fluid/common_defs.h muse-3.0.2+ds1/synti/fluid/common_defs.h --- muse-2.1.2/synti/fluid/common_defs.h 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluid/common_defs.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,7 +23,8 @@ #ifndef __FLUID_UNIQUE_ID_H #define __FLUID_UNIQUE_ID_H -// Make sure this number is unique among all the MESS synths. +// Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. #define FLUID_UNIQUE_ID 0 //#define FLUID_DEBUG diff -Nru muse-2.1.2/synti/fluid/fluid.cpp muse-3.0.2+ds1/synti/fluid/fluid.cpp --- muse-2.1.2/synti/fluid/fluid.cpp 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluid/fluid.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -41,10 +41,10 @@ #include #include -#include "muse/midictrl.h" +#include "muse/midictrl_consts.h" //#include "common_defs.h" -#include "muse/midi.h" +#include "muse/midi_consts.h" #include "fluid.h" #include "fluidgui.h" @@ -54,10 +54,10 @@ class QWidget; -static Mess* instantiate(int sr, QWidget*, QString* /* projectPathPtr */, const char* name) +static Mess* instantiate(unsigned long long /*parentWinId*/, const char* name, const MessConfig* config) { ISynth* synth = new ISynth(); - synth->setSampleRate(sr); + synth->setSampleRate(config->_sampleRate); if (synth->init(name)) { delete synth; synth = 0; @@ -355,7 +355,7 @@ // Called from host, ONLY if output path is connected. //--------------------------------------------------------- -void ISynth::process(float** ports, int offset, int n) +void ISynth::process(unsigned /*pos*/, float** ports, int offset, int n) { if (!_busy) { /* @@ -391,10 +391,11 @@ return sysex(ev.len(), ev.data()); case MusECore::ME_PITCHBEND: setController(ev.channel(), MusECore::CTRL_PITCH, ev.dataA()); - break; - case MusECore::ME_PROGRAM: - setController(ev.channel(), MusECore::CTRL_PROGRAM, ev.dataA()); - break; + break; + // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore - only CTRL_PROGRAM. + //case MusECore::ME_PROGRAM: + // setController(ev.channel(), MusECore::CTRL_PROGRAM, ev.dataA()); + // break; default: break; } @@ -405,7 +406,7 @@ // getPatchName //--------------------------------------------------------- -QString ISynth::getPatchName(int /*ch*/, int val, bool /*drum*/) const +const char* ISynth::getPatchName(int /*ch*/, int val, bool /*drum*/) const { int prog = val & 0xff; if(val == MusECore::CTRL_VAL_UNKNOWN || prog == 0xff) diff -Nru muse-2.1.2/synti/fluid/fluidgui.cpp muse-3.0.2+ds1/synti/fluid/fluidgui.cpp --- muse-2.1.2/synti/fluid/fluidgui.cpp 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluid/fluidgui.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -34,7 +34,7 @@ #include "common_defs.h" #include "fluidgui.h" -#include "muse/midi.h" +#include "muse/midi_consts.h" #include "muse/mpevent.h" #include "muse/icons.h" @@ -53,15 +53,6 @@ fdialogButton->setIcon(QIcon(*MusEGui::openIcon)); connect(fdialogButton, SIGNAL(clicked()), SLOT(soundFontFileDialog())); connect(loadButton, SIGNAL(clicked()), SLOT(loadFont())); - - // work around for probable QT/WM interaction bug. - // for certain window managers, e.g xfce, this window is - // is displayed although not specifically set to show(); - // bug: 2811156 Softsynth GUI unclosable with XFCE4 (and a few others) - // Nov 21, 2012 Hey this causes the thing not to open at all, EVER, on Lubuntu and some others! - // And we had a request to remove this from a knowledgable tester. REMOVE Tim. - ///show(); - ///hide(); } //--------------------------------------------------------- diff -Nru muse-2.1.2/synti/fluid/fluid.h muse-3.0.2+ds1/synti/fluid/fluid.h --- muse-2.1.2/synti/fluid/fluid.h 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluid/fluid.h 2018-01-29 20:07:03.000000000 +0000 @@ -70,19 +70,16 @@ void resetAllController(int); virtual void processMessages(); - virtual void process(float**, int, int); + virtual void process(unsigned pos, float**, int, int); virtual bool playNote(int channel, int pitch, int velo); virtual bool setController(int, int, int); virtual bool sysex(int len, const unsigned char* p); virtual bool processEvent(const MusECore::MidiPlayEvent&); - virtual QString getPatchName (int, int, bool) const; + virtual const char* getPatchName (int, int, bool) const; virtual const MidiPatch* getPatchInfo(int, const MidiPatch *) const; virtual void getInitData(int*, const unsigned char**); - //virtual bool guiVisible() const; - //virtual void showGui(bool); - //virtual bool hasGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); virtual bool hasNativeGui() const { return true; } diff -Nru muse-2.1.2/synti/fluidsynth/CMakeLists.txt muse-3.0.2+ds1/synti/fluidsynth/CMakeLists.txt --- muse-2.1.2/synti/fluidsynth/CMakeLists.txt 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluidsynth/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -24,8 +24,9 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( fluidsynth_mocs +QT5_WRAP_CPP ( fluidsynth_mocs fluidsynthgui.h + fluidsynti.h ) ## @@ -34,12 +35,12 @@ file (GLOB fluidsynth_ui_files fluidsynthguibase.ui ) -QT4_WRAP_UI ( fluidsynth_uis ${fluidsynth_ui_files} ) +QT5_WRAP_UI ( fluidsynth_uis ${fluidsynth_ui_files} ) ## ## Resource files ## -QT4_ADD_RESOURCES (fluidsynth_qrc_files +QT5_ADD_RESOURCES (fluidsynth_qrc_files fluidsynth.qrc ) @@ -51,6 +52,9 @@ fluidsynthgui.cpp ) +include_directories(${INSTPATCH_INCLUDE_DIRS}) +include_directories(${FLUIDSYN_INCLUDE_DIRS}) + ## ## Define target ## @@ -90,6 +94,9 @@ synti ${QT_LIBRARIES} ${FLUIDSYN_LIBRARIES} + ${INSTPATCH_LIBRARIES} + mpevent_module + icons ) ## diff -Nru muse-2.1.2/synti/fluidsynth/common_defs.h muse-3.0.2+ds1/synti/fluidsynth/common_defs.h --- muse-2.1.2/synti/fluidsynth/common_defs.h 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluidsynth/common_defs.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,7 +23,8 @@ #ifndef __FLUIDSYNTH_UNIQUE_ID_H #define __FLUIDSYNTH_UNIQUE_ID_H -// Make sure this number is unique among all the MESS synths. +// Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. #define FLUIDSYNTH_UNIQUE_ID 3 #endif diff -Nru muse-2.1.2/synti/fluidsynth/fluidsynthgui.cpp muse-3.0.2+ds1/synti/fluidsynth/fluidsynthgui.cpp --- muse-2.1.2/synti/fluidsynth/fluidsynthgui.cpp 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluidsynth/fluidsynthgui.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -36,12 +36,13 @@ #include #include #include -#include #include #include #include -#include "muse/midi.h" +#include + +#include "muse/midi_consts.h" #include "icons.h" #include "common_defs.h" @@ -69,9 +70,7 @@ ChorusType->setItemIcon(0, QIcon(*MusEGui::sineIcon)); ChorusType->setItemIcon(1, QIcon(*MusEGui::sawIcon)); - //Connect socketnotifier to fifo - QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); - connect(s, SIGNAL(activated(int)), SLOT(readMessage(int))); + connect(this->getGuiSignal(),SIGNAL(wakeup()),this,SLOT(readMessage())); connect (Push, SIGNAL (clicked()), SLOT(loadClicked())); lastdir = ""; @@ -107,38 +106,10 @@ connect(ChorusSpeed, SIGNAL (valueChanged (int)), SLOT(changeChorusSpeed (int))); connect(ChorusDepth, SIGNAL (valueChanged (int)), SLOT(changeChorusDepth (int))); connect(ChorusLevel, SIGNAL (valueChanged (int)), SLOT(changeChorusLevel (int))); -/* - _notifier = new QSocketNotifier(0, QSocketNotifier::Read); - connect(_notifier, SIGNAL(activated(int)), SLOT(readData(int))); - - //Setup the ListView - sfListView->setColumnWidthMode(MUSE_FLUID_ID_COL,QListView::Maximum); - sfListView->setColumnWidthMode(MUSE_FLUID_SFNAME_COL,QListView::Maximum); - - sfListView->setColumnAlignment(MUSE_FLUID_ID_COL,AlignHCenter); - sfListView->setSorting(MUSE_FLUID_ID_COL,true); - channelListView->setColumnAlignment(MUSE_FLUID_CHANNEL_COL,AlignHCenter); - - _currentlySelectedFont = -1; //No selected font to start with - // The GUI-process is killed every time the window is shut, - // need to get all parameters from the synth - - requestAllParameters(); - - */ //Clear channels for (int i=0; ivisualItemRect(item).bottomLeft(); QTableWidget* listView = item->tableWidget(); - ppt += QPoint(listView->horizontalHeader()->sectionPosition(col), listView->horizontalHeader()->height()); + ppt += QPoint(40, listView->horizontalHeader()->height()); ppt = listView->mapToGlobal(ppt); int i = 0; + int lastindex = 0; for (std::list::reverse_iterator it = stack.rbegin(); it != stack.rend(); it++) { i++; /*byte* d = (byte*) it->name.toLatin1(); @@ -545,27 +547,28 @@ } printf("\n\n");*/ QAction* act1 = popup->addAction(it->name); - act1->setData(i); + act1->setData((int)it->id); + lastindex = std::max(lastindex, (int)it->id + 1); } - int lastindex = i+1; + QAction *lastaction = popup->addAction("unspecified"); lastaction->setData(lastindex); QAction * act = popup->exec(ppt, 0); if (act) { - int index = act->data().toInt(); - byte sfid; + int sfid = act->data().toInt(); QString fontname; - if (index == lastindex) { + if (sfid == lastindex) { sfid = FS_UNSPECIFIED_ID; fontname = "unspecified"; //Actually, it's not possible to reset fluid-channels as for now, } //so this is just a dummy that makes the synth block any events for the channel else { - sfid = getSoundFontId(act->text()); - fontname = getSoundFontName(sfid); + //sfid = getSoundFontId(act->text()); + fontname = getSoundFontName((byte)sfid); } //byte channel = atoi(item->text().toLatin1()) - 1; byte channel = row; sendChannelChange(sfid, channel); + sendUpdateDrumMaps(); item->setText(fontname); } delete popup; @@ -575,7 +578,7 @@ QMenu* popup = new QMenu(this); QPoint ppt = channelListView->visualItemRect(item).bottomLeft(); QTableWidget* listView = item->tableWidget(); - ppt += QPoint(listView->horizontalHeader()->sectionPosition(col), listView->horizontalHeader()->height()); + ppt += QPoint(40, listView->horizontalHeader()->height()); ppt = listView->mapToGlobal(ppt); QAction * yes = popup->addAction("Yes"); yes->setData(1); @@ -589,6 +592,7 @@ int index = act2->data().toInt(); if (index != drumchannels[channel]) { sendDrumChannelChange(index, channel); + sendUpdateDrumMaps(); drumchannels[channel] = index; item->setText(index == 0 ? "No" : "Yes" ); } @@ -650,14 +654,11 @@ void FluidSynthGui::popClicked() { - //byte data[2]; - byte data[4]; - data[0] = MUSE_SYNTH_SYSEX_MFG_ID; - data[1] = FLUIDSYNTH_UNIQUE_ID; - data[2] = FS_SOUNDFONT_POP; - data[3] = currentlySelectedFont; - //sendSysex(data,2); - sendSysex(data,4); + if(currentlySelectedFont >= 0) + { + sendPopFont(currentlySelectedFont); + sendUpdateDrumMaps(); + } } void FluidSynthGui::sfItemClicked(QTreeWidgetItem* item, int /*col*/) diff -Nru muse-2.1.2/synti/fluidsynth/fluidsynthgui.h muse-3.0.2+ds1/synti/fluidsynth/fluidsynthgui.h --- muse-2.1.2/synti/fluidsynth/fluidsynthgui.h 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluidsynth/fluidsynthgui.h 2018-01-06 20:31:35.000000000 +0000 @@ -41,19 +41,6 @@ struct FluidChannel; #define FS_DEBUG 0 //Turn on/off debug -/* -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "muse/debug.h" -*/ #define FS_MAX_NR_OF_CHANNELS 16 #define FS_UNSPECIFIED_FONT 126 @@ -180,6 +167,8 @@ virtual void processEvent(const MusECore::MidiPlayEvent& ev); void sendLastdir(QString); void sendLoadFont(QString); + void sendPopFont(int id); + void sendUpdateDrumMaps(); void sendChannelChange(byte font_id, byte channel); void sendDrumChannelChange(byte onoff, byte channel); void updateSoundfontListView(); @@ -213,7 +202,7 @@ */ private slots: void loadClicked(); - void readMessage(int); + void readMessage(); void changeGain(int); void dumpInfo(); void channelItemClicked(QTableWidgetItem* item); diff -Nru muse-2.1.2/synti/fluidsynth/fluidsynti.cpp muse-3.0.2+ds1/synti/fluidsynth/fluidsynti.cpp --- muse-2.1.2/synti/fluidsynth/fluidsynti.cpp 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluidsynth/fluidsynti.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: ./synti/fluidsynth/fluidsynti.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others +// (C) Copyright 2016 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 @@ -35,10 +36,31 @@ #include #include #include +#include +#include +#include //#include "common_defs.h" #include "fluidsynti.h" -#include "muse/midi.h" +#include "muse/midi_consts.h" + + +#ifdef HAVE_INSTPATCH +#include +typedef std::multimap < int /* note */, std::string > NoteSampleNameList_t; +typedef NoteSampleNameList_t::iterator iNoteSampleNameList_t; +typedef NoteSampleNameList_t::const_iterator ciNoteSampleNameList_t; +typedef std::pair NoteSampleNameInsertPair_t; +typedef NoteSampleNameList_t NoteSampleNameList; + +typedef std::map < int /*patch*/, NoteSampleNameList_t > PatchNoteSampleNameList_t; +typedef PatchNoteSampleNameList_t::iterator iPatchNoteSampleNameList_t; +typedef PatchNoteSampleNameList_t::const_iterator ciPatchNoteSampleNameList_t; +typedef std::pair PatchNoteSampleNameListResult_t; +typedef std::pair PatchNoteSampleNameInsertPair_t; +typedef PatchNoteSampleNameList_t PatchNoteSampleNameList; +#endif + FluidCtrl FluidSynth::fluidCtrl[] = { //{ "Expression", MusECore::CTRL_EXPRESSION, 0, 127 }, @@ -83,13 +105,13 @@ }; static int NUM_CONTROLLER = sizeof(FluidSynth::fluidCtrl)/sizeof(*(FluidSynth::fluidCtrl)); -static void* fontLoadThread(void* t); -QString *projPathPtr; +QString projPathPtr; + // // Fluidsynth // -FluidSynth::FluidSynth(int sr, pthread_mutex_t *_Globalsfloader_mutex) : Mess(2) +FluidSynth::FluidSynth(int sr, QMutex &_GlobalSfLoaderMutex) : Mess(2), _sfLoaderMutex(_GlobalSfLoaderMutex) { gui = 0; setSampleRate(sr); @@ -109,16 +131,20 @@ channels[i].preset = FS_UNSPECIFIED_PRESET; channels[i].drumchannel= false; } - //pthread_mutex_init(&_sfloader_mutex,NULL); - _sfloader_mutex = _Globalsfloader_mutex; initBuffer = 0; initLen = 0; + + QObject::connect(&fontWorker,SIGNAL(loadFontSignal(void*)),&fontWorker,SLOT(execLoadFont(void*))); + fontWorker.moveToThread(&fontLoadThread); + fontLoadThread.start(); } FluidSynth::~FluidSynth() { + fontLoadThread.exit(); + for (std::list::iterator it =stack.begin(); it !=stack.end(); it++) { if(it->intid == FS_UNSPECIFIED_FONT || it->intid == FS_UNSPECIFIED_ID) @@ -138,11 +164,7 @@ if (err == -1) { std::cerr << DEBUG_ARGS << "error while destroying synth: " << fluid_synth_error(fluidsynth) << std::endl; return; - } - //Destroy the mutex -/* if (pthread_mutex_destroy(&_sfloader_mutex) != 0) - std::cerr << DEBUG_ARGS << "Strange, mutex busy! Should not be!" << std::endl;*/ - + } } bool FluidSynth::init(const char* name) @@ -150,7 +172,6 @@ debug("FluidSynth::init\n"); gui = new FluidSynthGui(); - gui->show(); gui->setWindowTitle(name); lastdir= ""; @@ -220,7 +241,7 @@ // Called from host, ONLY if output path is connected. //--------------------------------------------------------- -void FluidSynth::process(float** ports, int offset, int len) +void FluidSynth::process(unsigned /*pos*/, float** ports, int offset, int len) { /* //Process messages from the gui @@ -253,10 +274,6 @@ //--------------------------------------------------------- void FluidSynth::getInitData(int* n, const unsigned char** data) { - - //printf("projPathPtr "); - //std::cout << *projPathPtr << std::endl; - // Data setup: // FS_INIT_DATA (1 byte) // FluidSynth version (2 bytes, x.y) @@ -279,10 +296,10 @@ for (std::list::const_iterator it = stack.begin(); it!=stack.end(); it++) { // if the soundfont is located under the projectPath we extract this from the filename - int fileLen = strlen(it->filename.c_str()); - if (QString(it->filename.c_str()).startsWith(*projPathPtr)) { - printf("project path found in filename, len %d shortened with %d\n",fileLen, projPathPtr->length()+1); - fileLen = fileLen - projPathPtr->length()-1; + int fileLen = it->file_name.size(); + if (it->file_name.startsWith(projPathPtr)) { + printf("project path found in filename, len %d shortened with %d\n",fileLen, projPathPtr.length()+1); + fileLen = fileLen - projPathPtr.length()-1; } len+=fileLen + 2; } @@ -325,13 +342,13 @@ // if the soundfont is located under the projectPath we extract this from the filename int offset=0; - if (QString(it->filename.c_str()).startsWith(*projPathPtr)) { - offset= projPathPtr->length()+1; + if (it->file_name.startsWith(projPathPtr)) { + offset= projPathPtr.length()+1; } - memcpy(chptr, it->filename.c_str()+offset, strlen(it->filename.c_str())-offset+1); + memcpy(chptr, it->file_name.toLatin1().constData()+offset, it->file_name.size()-offset+1); //printf("path name stored=%s\n", it->filename.c_str()+offset); - chptr = chptr + 1 + strlen(it->filename.c_str())-offset; + chptr = chptr + 1 + it->file_name.size()-offset; } //For each font again... @@ -372,7 +389,7 @@ void FluidSynth::parseInitData(int n, const byte* d) { printf("projPathPtr "); - std::cout << *projPathPtr->toAscii().data() << std::endl; + std::cout << projPathPtr.toLatin1().constData() << std::endl; bool load_drumchannels = true; // Introduced in initdata ver 0.3 bool handle_bankvalue = true; // Introduced in initdata ver 0.4 @@ -411,67 +428,72 @@ byte nr_of_fonts = d[3]; //byte nr_of_fonts = d[5]; nrOfSoundfonts = nr_of_fonts; //"Global" counter - const byte* chptr = (d + 4); + //byte* chptr = (unsigned char*)d + 4; + int arrayIndex = 4; //const byte* chptr = (d + FS_INIT_DATA_HEADER_SIZE); //Get lastdir: - lastdir = std::string((char*)chptr); + lastdir = std::string((char*)&d[arrayIndex]); sendLastdir(lastdir.c_str()); - chptr+=strlen(lastdir.c_str())+1; + arrayIndex+=strlen(lastdir.c_str())+1; + + printf("Number of soundfonts for this instance: %d\n", nr_of_fonts); FluidSoundFont* fonts = new FluidSoundFont[nrOfSoundfonts]; //Just a temp one //Fonts: for (int i=0; itoAscii(); + fonts[i].file_name = QString::fromLatin1((char*)&d[arrayIndex]); + arrayIndex+=fonts[i].file_name.size()+1; + QByteArray ba = projPathPtr.toLatin1(); - if (QFileInfo(fonts[i].filename.c_str()).isRelative()) { + if (QFileInfo(fonts[i].file_name).isRelative()) { printf("path is relative, we append full path!\n"); - fonts[i].filename = ba.constData() + std::string("/")+ fonts[i].filename; + fonts[i].file_name = QString(ba) + "/"+ fonts[i].file_name; } - std::cout << "SOUNDFONT FILENAME + PATH " << fonts[i].filename << std::endl; + std::cout << "SOUNDFONT FILENAME + PATH " << fonts[i].file_name.toLatin1().constData() << std::endl; } - if (*chptr != FS_INIT_CHANNEL_SECTION) { + if (d[arrayIndex] != FS_INIT_CHANNEL_SECTION) { + delete[] fonts; sendError("Init-data corrupt... Projectfile error. Initdata ignored.\n"); return; } - chptr++; + arrayIndex++; for (int i=0; ifptr = this; helper->id = extid; + QString fn = QString::fromLatin1(filename); - if (QFile::exists(filename)) + if (QFile::exists(fn)) { - helper->filename = filename; + helper->file_name = fn; } else { - //printf("current path: %s \nmuseProject %s\nfilename %s\n",QDir::currentPath().toLatin1().data(), MusEGlobal::museProject.toLatin1().data(), filename); - QFileInfo fi(filename); - if (QFile::exists(fi.fileName())) - helper->filename = QDir::currentPath().toStdString() + "/" + fi.fileName().toStdString(); + //printf("current path: %s \nmuseProject %s\nfilename %s\n",QDir::currentPath().toLatin1().constData(), MusEGlobal::museProject.toLatin1().constData(), filename); + QFileInfo fi(fn); + if (QFile::exists(fi.fileName())) // check if the file exists in current folder + helper->file_name = QDir::currentPath() + "/" + fi.fileName(); else { // TODO: Strings should be translated, this does // however require the class to be derived from qobject // tried in vain to make the call in the gui object // could'nt get it to work due to symbol missing in .so ... - QString newName = QFileDialog::getOpenFileName(0, - QString("Can't find soundfont: %1 - Choose soundfont").arg(filename), - filename, - QString("Soundfonts (*.sf2);;All files (*)")); - helper->filename = newName.toStdString(); +// helper->file_name = QFileDialog::getOpenFileName(0, +// QString("Can't find soundfont: %1 - Choose soundfont").arg(filename), +// fn, +// QString("Soundfonts (*.sf2);;All files (*)")); +// QMessageBox::warning(NULL,"No sound font found.","Could not open soundfont: " + fn, QMessageBox::Ok); + + fprintf(stderr, "Warning: Could not open soundfont: %s\n", fn.toLatin1().constData()); + + return false; } } - if (pthread_create(&fontThread, attributes, ::fontLoadThread, (void*) helper)) - perror("creating thread failed:"); + fontWorker.loadFont(helper); - pthread_attr_destroy(attributes); return true; } -//--------------------------------------------------------- -// fontLoadThread -// helper thread to load soundfont in the -// background -//--------------------------------------------------------- -static void* fontLoadThread(void* t) - { - //Init vars - FS_Helper* h = (FS_Helper*) t; - FluidSynth* fptr = h->fptr; - const char* filename = h->filename.c_str(); - pthread_mutex_t* sfloader_mutex = (fptr->_sfloader_mutex); +#ifdef HAVE_INSTPATCH +static void loadSf2NoteSampleNames(FluidSoundFont& font, IpatchSF2 *sf2) +{ + IpatchList *presets, *pZones, *iZones; + IpatchIter pIter, pZoneIter, iZoneIter; + IpatchItem *pset, *pZone, *inst, *iZone; + char *psetName, *instName, *sampName; + int bank, program, patch; + gboolean pRangeSet, iRangeSet; + IpatchSF2GenAmount pNoteRange, iNoteRange; + IpatchSF2Sample *samp; + + // Get preset children of SoundFont + presets = ipatch_container_get_children (IPATCH_CONTAINER (sf2), IPATCH_TYPE_SF2_PRESET); + ipatch_list_init_iter (presets, &pIter); + + // Iterate over presets in list + for (pset = ipatch_item_first (&pIter); pset != NULL; pset = ipatch_item_next (&pIter)) + { // Get name of preset, MIDI bank, and MIDI program number + g_object_get (pset, + "name", &psetName, + "bank", &bank, + "program", &program, + NULL); + + + //fprintf(stderr, "psetName:%s bank:%d program:%d\n", psetName, bank, program); + + // Compose a patch numer. Drums are on special bank 128. + patch = (bank << 16) | (0xff << 8) | (program & 0x7f); + PatchNoteSampleNameListResult_t res_pnsnl = font._noteSampleNameList.insert(PatchNoteSampleNameInsertPair_t(patch, NoteSampleNameList())); + iPatchNoteSampleNameList_t res_ipnsnl = res_pnsnl.first; + NoteSampleNameList& nsl = res_ipnsnl->second; + + // Get preset zones + pZones = ipatch_container_get_children (IPATCH_CONTAINER (pset), IPATCH_TYPE_SF2_ZONE); + ipatch_list_init_iter (pZones, &pZoneIter); + + // Iterate over preset zones + for (pZone = ipatch_item_first (&pZoneIter); pZone != NULL; pZone = ipatch_item_next (&pZoneIter)) + { // Get linked instrument and preset zone note range set flag + g_object_get (pZone, + "link-item", &inst, + "note-range-set", &pRangeSet, + NULL); + + // Get instrument name and global zone note range + g_object_get (inst, + "name", &instName, + NULL); + + //fprintf(stderr, "\tinstName:%s\n", instName); + + // Get instrument zones + iZones = ipatch_container_get_children (IPATCH_CONTAINER (inst), IPATCH_TYPE_SF2_ZONE); + ipatch_list_init_iter (iZones, &iZoneIter); + + for (iZone = ipatch_item_first (&iZoneIter); iZone != NULL; iZone = ipatch_item_next (&iZoneIter)) + { // Get sample and instrument zone note range set flag + g_object_get (iZone, + "note-range-set", &iRangeSet, + "link-item", &samp, + NULL); + + // Get instrument name and global zone note range + g_object_get (samp, + "name", &sampName, + NULL); + + if (pRangeSet) + ipatch_sf2_gen_item_get_amount (IPATCH_SF2_GEN_ITEM (pZone), IPATCH_SF2_GEN_NOTE_RANGE, &pNoteRange); + else ipatch_sf2_gen_item_get_amount (IPATCH_SF2_GEN_ITEM (pset), IPATCH_SF2_GEN_NOTE_RANGE, &pNoteRange); + + if (iRangeSet) + ipatch_sf2_gen_item_get_amount (IPATCH_SF2_GEN_ITEM (iZone), IPATCH_SF2_GEN_NOTE_RANGE, &iNoteRange); + else ipatch_sf2_gen_item_get_amount (IPATCH_SF2_GEN_ITEM (inst), IPATCH_SF2_GEN_NOTE_RANGE, &iNoteRange); + + if (ipatch_sf2_gen_range_intersect (&iNoteRange, &pNoteRange)) // Returns false if no common range + { + // Note range spans iNoteRange.range.low to iNoteRange.range.high + //fprintf(stderr, "\t\t%s\tiNoteRange h:%u l:%u pNoteRange h:%u l:%u\n", sampName, iNoteRange.range.high, iNoteRange.range.low, pNoteRange.range.high, pNoteRange.range.low); + + for(int iNote = iNoteRange.range.low; iNote <= iNoteRange.range.high; ++iNote) // Yes, that's less than or equal. + nsl.insert(NoteSampleNameInsertPair_t(iNote, std::string(sampName))); + } + + g_free (sampName); + g_object_unref (samp); + } + + g_object_unref (iZones); + g_free (instName); + g_object_unref (inst); + } - //Let only one loadThread have access to the fluidsynth-object at the time - pthread_mutex_lock(sfloader_mutex); - int rv = fluid_synth_sfload(fptr->fluidsynth, filename, 1); + g_object_unref (pZones); + g_free (psetName); + } - if (rv ==-1) { - fptr->sendError(fluid_synth_error(fptr->fluidsynth)); - if (FS_DEBUG) - std::cerr << DEBUG_ARGS << "error loading soundfont: " << fluid_synth_error(fptr->fluidsynth) << std::endl; - - //Unlock the mutex, or else we might be stuck here forever... - pthread_mutex_unlock(sfloader_mutex); - delete h; - pthread_exit(0); - } + g_object_unref (presets); +} - //Deal with internal and external id etc. - if (FS_DEBUG) - printf("Soundfont %s loaded, index %d\n", filename, rv); +//--------------------------------------------------------- +// loadNoteSampleNames +// Extracts all the note sample names +//--------------------------------------------------------- - FluidSoundFont font; - font.filename = h->filename;//strdup(filename); +static void loadNoteSampleNames(FluidSoundFont& font) +{ + IpatchSF2 *sf2; + IpatchSF2Reader* sf2_reader; + IpatchFileHandle *fhandle; + IpatchSF2File *sffile; + GError *err = NULL; + const QByteArray ba = font.file_name.toLatin1(); + const char* fname = ba.constData(); - font.intid = rv; - if (h->id == FS_UNSPECIFIED_ID) { - font.extid = fptr->getNextAvailableExternalId(); - if (FS_DEBUG) - printf("Font got extid %d\n",font.extid); - } - else - font.extid = h->id; - if (FS_DEBUG) - printf("Font has external id: %d int id:%d\n", font.extid, font.intid); + /* initialize libInstPatch */ + ipatch_init (); - //Strip off the filename - QString temp = QString(filename); - QString name = temp.right(temp.length() - temp.lastIndexOf('/',-1) - 1); - name = name.left(name.length()-4); //Strip off ".sf2" - font.name = name.toLatin1().constData(); - fptr->stack.push_front(font); - fptr->currentlyLoadedFonts++; + sffile = ipatch_sf2_file_new (); - //Cleanup & unlock: - pthread_mutex_unlock(sfloader_mutex); - delete h; + fhandle = ipatch_file_open(IPATCH_FILE(sffile), fname, "r", &err); + if(!fhandle) + { + fprintf (stderr, "Failed to identify file '%s': %s\n", fname, + ipatch_gerror_message (err)); + g_clear_error (&err); + g_object_unref (sffile); + return; + } - if (FS_DEBUG) - printf("Currently loaded fonts: %d Nr of soundfonts: %d\n",fptr->currentlyLoadedFonts, fptr->nrOfSoundfonts); - //Check whether this was the last font or not. If so, run initSynth(); - if (fptr->nrOfSoundfonts <= fptr->currentlyLoadedFonts) { - if (FS_DEBUG) - printf("This was the last font, rewriting channel settings...\n"); - fptr->rewriteChannelSettings(); - //Update data in GUI-window. - fptr->sendSoundFontData();; - fptr->sendChannelData(); - } + sf2_reader = ipatch_sf2_reader_new(fhandle); + sf2 = ipatch_sf2_reader_load(sf2_reader, NULL); - pthread_exit(0); - } + loadSf2NoteSampleNames(font, sf2); + + ipatch_file_close(fhandle); + g_object_unref (sf2); + //g_object_unref(sf2_reader); // Needed? + g_object_unref (sffile); +} +#endif //--------------------------------------------------------- // playNote @@ -748,7 +839,7 @@ //Calculate length in chars of all strings in the soundfontstack in one string for (std::list::iterator it = stack.begin(); it != stack.end(); it++) { - ndatalen += 1 + strlen(it->name.c_str()); + ndatalen += 1 + it->name.size(); ndatalen += FS_SFDATALEN; //unsigned char for ID } byte ndata[ndatalen]; @@ -764,9 +855,9 @@ //char* chunk_start = (char*)(ndata + 4); int chunk_len, name_len; for (std::list::iterator it = stack.begin(); it != stack.end(); ++it) { - name_len = strlen(it->name.c_str()) + 1; + name_len = it->name.size() + 1; chunk_len = name_len + FS_SFDATALEN; - memcpy(chunk_start, it->name.c_str(), name_len); //First, store the fontname + memcpy(chunk_start, it->name.toLatin1().constData(), name_len); //First, store the fontname *(chunk_start + name_len) = it->extid; //The GUI only needs to know about the external id, store that here chunk_start += chunk_len; } @@ -832,7 +923,7 @@ printf("\n"); for (std::list::iterator it = stack.begin(); it != stack.end(); it++) - printf("Font: %s\tintid: %d\textid %d\tfilename:%s\n", it->name.c_str(), it->intid, it->extid, it->filename.c_str()); + printf("Font: %s\tintid: %d\textid %d\tfilename:%s\n", it->name.toLatin1().constData(), it->intid, it->extid, it->file_name.toLatin1().constData()); printf("Reverb on: %d, width: %f, size: %f level: %f damp: %f\n",rev_on, rev_width, rev_size, rev_level, rev_damping); printf("-----------------------------------------------------\n"); } @@ -1102,11 +1193,51 @@ } if (FS_DEBUG) - //printf("FluidSynth::getControllerInfo() id: %d name: %s controller: %d min: %d max: %d\n",id,*name,*controller,*min,*max); - printf("FluidSynth::getControllerInfo() id: %d name: %s controller: %d min: %d max: %d initval: %d\n",id,*name,*controller,*min,*max,*initval); + printf("FluidSynth::getControllerInfo() id: %d name: %s controller: %d min: %d max: %d initval: %d\n", + id,*name,*controller,*min,*max,*initval); return ++id; } +#ifdef HAVE_INSTPATCH +bool FluidSynth::getNoteSampleName(bool drum, int channel, int patch, int note, const char** name) const +{ + if(!name || channel < 0 || channel >= FS_MAX_NR_OF_CHANNELS) + return false; + const FluidChannel& fc = channels[channel]; + if(fc.drumchannel != drum) + return false; + // Force the low bank to don't care, we don't use it in fluidsynth MESS). + patch |= 0xff00; + if(drum) + { + patch &= 0xffff; // Remove the high bank. + patch |= 0x800000; // Set high bank to 128 (special soundfont bank number meaning drums), and low bank to don't care. + } + + for(std::list::const_iterator it = stack.begin(); it != stack.end(); it++) + { + const FluidSoundFont& fsf = *it; + if(fsf.intid == fc.font_intid) // || fsf.extid == fc.font_extid) + { + ciPatchNoteSampleNameList_t ipnsnl = fsf._noteSampleNameList.find(patch); + if(ipnsnl != fsf._noteSampleNameList.end()) + { + const NoteSampleNameList& pnsnl = ipnsnl->second; + ciNoteSampleNameList_t insnl = pnsnl.find(note); + if(insnl != pnsnl.end()) + { + const std::string& str = insnl->second; + *name = str.c_str(); + return true; + } + } + } + } + + return false; +} +#endif + //--------------------------------------------------------- // sendError //--------------------------------------------------------- @@ -1227,7 +1358,7 @@ //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- -QString FluidSynth::getPatchName(int i, int, bool /*drum*/) const +const char* FluidSynth::getPatchName(int i, int, bool /*drum*/) const { if (channels[i].font_intid == FS_UNSPECIFIED_FONT || channels[i].font_intid == FS_UNSPECIFIED_ID) @@ -1415,27 +1546,100 @@ return success; } + +void LoadFontWorker::loadFont(void* h) +{ + emit loadFontSignal(h); +} + +//--------------------------------------------------------- +// execLoadFont +// helper function to load soundfont in the +// background. +//--------------------------------------------------------- +void LoadFontWorker::execLoadFont(void * t) +{ + FS_Helper *h = (FS_Helper*) t; + FluidSynth* fptr = h->fptr; + + QByteArray ba = h->file_name.toLocal8Bit(); + const char* filename = ba.constData(); + + if (FS_DEBUG) + printf("execLoadFont() font name %s\n", filename); + + //Let only one loadThread have access to the fluidsynth-object at the time + QMutexLocker ml(&fptr->_sfLoaderMutex); + int rv = fluid_synth_sfload(fptr->fluidsynth, filename, 1); + + if (rv ==-1) { + fptr->sendError(fluid_synth_error(fptr->fluidsynth)); + if (FS_DEBUG) + std::cerr << DEBUG_ARGS << "error loading soundfont: " << fluid_synth_error(fptr->fluidsynth) << std::endl; + + delete h; + return; + } + + //Deal with internal and external id etc. + if (FS_DEBUG) + printf("Soundfont %s loaded, index %d\n", filename, rv); + + FluidSoundFont font; + font.file_name = h->file_name; + + font.intid = rv; + if (h->id == FS_UNSPECIFIED_ID) { + font.extid = fptr->getNextAvailableExternalId(); + if (FS_DEBUG) + printf("Font got extid %d\n",font.extid); + } + else + font.extid = h->id; + if (FS_DEBUG) + printf("Font has external id: %d int id:%d\n", font.extid, font.intid); + + //Strip off the filename + QFileInfo fi(h->file_name); + font.name = fi.fileName(); + + #ifdef HAVE_INSTPATCH + loadNoteSampleNames(font); + #endif + + fptr->stack.push_front(font); + fptr->currentlyLoadedFonts++; + + if (FS_DEBUG) + printf("Currently loaded fonts: %d Nr of soundfonts: %d\n",fptr->currentlyLoadedFonts, fptr->nrOfSoundfonts); + //Check whether this was the last font or not. If so, run initSynth(); + if (fptr->nrOfSoundfonts <= fptr->currentlyLoadedFonts) { + if (FS_DEBUG) + printf("This was the last font, rewriting channel settings...\n"); + fptr->rewriteChannelSettings(); + //Update data in GUI-window. + fptr->sendSoundFontData();; + fptr->sendChannelData(); + } + + delete h; +} + //--------------------------------------------------------- // instantiate // construct a new synthesizer instance //--------------------------------------------------------- class QWidget; -static pthread_mutex_t globalMutex; -static bool mutexEnabled = false; +static QMutex globalFluidSynthMutex; -static Mess* instantiate(int sr, QWidget*, QString* projectPathPtr, const char* name) +static Mess* instantiate(unsigned long long /*parentWinId*/, const char* name, const MessConfig* config) { - printf("fluidsynth sampleRate %d\n", sr); - projPathPtr=projectPathPtr; - - if (!mutexEnabled) { - pthread_mutex_init(&globalMutex,NULL); - mutexEnabled = true; - } + printf("fluidsynth sampleRate %d\n", config->_sampleRate); + projPathPtr = QString(config->_projectPath); - FluidSynth* synth = new FluidSynth(sr, &globalMutex); + FluidSynth* synth = new FluidSynth(config->_sampleRate, globalFluidSynthMutex); if (synth->init(name)) { delete synth; synth = 0; diff -Nru muse-2.1.2/synti/fluidsynth/fluidsynti.h muse-3.0.2+ds1/synti/fluidsynth/fluidsynti.h --- muse-2.1.2/synti/fluidsynth/fluidsynti.h 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/fluidsynth/fluidsynti.h 2018-01-29 20:07:03.000000000 +0000 @@ -4,6 +4,7 @@ // $Id: ./synti/fluidsynth/fluidsynti.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others +// (C) Copyright 2016 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 @@ -36,23 +37,33 @@ #include #include +#include +#include #include "fluidsynthgui.h" #include "libsynti/mess.h" #include "muse/debug.h" -//#include "libsynti/mpevent.h" #include "muse/mpevent.h" -#include "muse/midictrl.h" +#include "muse/midictrl_consts.h" #include "common_defs.h" +// TODO: Try to not include this. Standalone build of plugin? +#include "config.h" +#ifdef HAVE_INSTPATCH +#include +#endif + #define FS_DEBUG_DATA 0 //Turn on/off debug print of midi data sent to fluidsynth typedef unsigned char byte; struct FluidSoundFont { - std::string filename; - std::string name; + QString file_name; + QString name; byte extid, intid; + #ifdef HAVE_INSTPATCH + std::map < int /*patch*/, std::multimap < int /* note */, std::string > > _noteSampleNameList; + #endif }; struct FluidCtrl { @@ -76,7 +87,6 @@ static const int FS_CHORUS_SPEED = 9 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_CHORUS_DEPTH = 10 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_CHORUS_LEVEL = 11 + MusECore::CTRL_NRPN14_OFFSET; -// Added by T356 static const int FS_PITCHWHEELSENS = 0 + MusECore::CTRL_RPN_OFFSET; // FluidChannel is used to map different soundfonts to different fluid-channels @@ -87,18 +97,26 @@ // can only have one soundfont, but one soundfont can have many channels) struct FluidChannel - { +{ byte font_extid, font_intid, preset, drumchannel; byte banknum; // hbank - //FluidSoundFont* font; - }; +}; -/*#include -#include -#include -*/ +class LoadFontWorker : public QObject +{ + Q_OBJECT + public: + LoadFontWorker() {} + void loadFont(void*); + signals: + void loadFontSignal(void*); + + private slots: + void execLoadFont(void*); +}; class FluidSynth : public Mess { + private: bool pushSoundfont (const char*, int); void sendSysex(int l, const unsigned char* d); @@ -116,7 +134,8 @@ FluidChannel channels[FS_MAX_NR_OF_CHANNELS]; std::string lastdir; - pthread_t fontThread; + QThread fontLoadThread; + LoadFontWorker fontWorker; const MidiPatch * getFirstPatch (int channel) const; const MidiPatch* getNextPatch (int, const MidiPatch *) const; @@ -126,26 +145,27 @@ int cho_num, cho_type; public: - FluidSynth(int sr, pthread_mutex_t *_Globalsfloader_mutex); + FluidSynth(int sr, QMutex &_GlobalSfLoaderMutex); virtual ~FluidSynth(); bool init(const char*); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** data) const; virtual void processMessages(); - virtual void process(float**, int, int); + virtual void process(unsigned pos, float**, int, int); virtual bool playNote(int channel, int pitch, int velo); virtual bool sysex(int, const unsigned char*); virtual bool setController(int, int, int); void setController(int, int , int, bool); virtual void getInitData(int*, const unsigned char**); - virtual QString getPatchName(int, int, bool) const; + virtual const char* getPatchName(int, int, bool) const; virtual const MidiPatch* getPatchInfo(int i, const MidiPatch* patch) const; virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const; virtual bool processEvent(const MusECore::MidiPlayEvent&); + #ifdef HAVE_INSTPATCH + // True if it found a name. + virtual bool getNoteSampleName(bool drum, int channel, int patch, int note, const char** name) const; + #endif - //virtual bool hasGui() const { return true; } - //virtual bool guiVisible() const; - //virtual void showGui(bool val); virtual bool hasNativeGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool val); @@ -160,7 +180,7 @@ fluid_synth_t* fluidsynth; FluidSynthGui* gui; - pthread_mutex_t *_sfloader_mutex; + QMutex& _sfLoaderMutex; int currentlyLoadedFonts; //To know whether or not to run the init-parameters std::list stack; int nrOfSoundfonts; @@ -174,7 +194,7 @@ struct FS_Helper //Only used to pass parameters when calling the loading thread { FluidSynth* fptr; - std::string filename; + QString file_name; int id; }; diff -Nru muse-2.1.2/synti/libsimpleplugin/CMakeLists.txt muse-3.0.2+ds1/synti/libsimpleplugin/CMakeLists.txt --- muse-2.1.2/synti/libsimpleplugin/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsimpleplugin/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,106 @@ +#============================================================================= +# MusE +# Linux Music Editor +# $Id:$ +# +# Copyright (C) 1999-2011 by Werner Schweer and others +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either 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(${PROJECT_SOURCE_DIR}/pch.txt) + +QT5_WRAP_CPP ( simpler_plugingui_mocs + simpler_plugingui.h + ) + +## +## UI files +## +file (GLOB simpler_plugingui_ui_files + simplepluginchooserbase.ui + ) +QT5_WRAP_UI ( simpler_plugingui_uis ${simpler_plugingui_ui_files} ) + +## +## List of source files to compile +## +file (GLOB simpler_plugingui_source_files + simpler_plugingui.cpp + ) +file (GLOB simpler_plugin_source_files + simpler_plugin.cpp + ) + +## +## Define target +## +add_library ( simpler_plugingui SHARED ## ${MODULES_BUILD} + ${PROJECT_BINARY_DIR}/all-pic.h + ${simpler_plugingui_source_files} + ${simpler_plugingui_mocs} + ${simpler_plugingui_uis} + ) +add_library ( simpler_plugin SHARED ## ${MODULES_BUILD} + ${PROJECT_BINARY_DIR}/all-pic.h + ${simpler_plugin_source_files} + ) + +## +## Append to the list of translations +## +set (FILES_TO_TRANSLATE + ${FILES_TO_TRANSLATE} + ${simpler_plugingui_source_files} + ${simpler_plugingui_ui_files} + ${simpler_plugin_source_files} + CACHE INTERNAL "" + ) + +## +## Compilation flags and target name +## +set_target_properties( simpler_plugingui + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all-pic.h" + OUTPUT_NAME muse_simpler_plugingui + ) +set_target_properties( simpler_plugin + PROPERTIES COMPILE_FLAGS "-include ${PROJECT_BINARY_DIR}/all-pic.h" + OUTPUT_NAME muse_simpler_plugin + ) + +## +## Linkage +## +target_link_libraries(simpler_plugingui + ${QT_LIBRARIES} + simpler_plugin + ) + +target_link_libraries(simpler_plugin + ${QT_LIBRARIES} + dl + ) + +## +## Install location +## +# if ( ${MODULES_BUILD} STREQUAL SHARED ) + install(TARGETS simpler_plugingui simpler_plugin + DESTINATION ${MusE_MODULES_DIR} + ) +# endif ( ${MODULES_BUILD} STREQUAL SHARED ) + diff -Nru muse-2.1.2/synti/libsimpleplugin/README muse-3.0.2+ds1/synti/libsimpleplugin/README --- muse-2.1.2/synti/libsimpleplugin/README 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsimpleplugin/README 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,3 @@ +This library is a simple LADSPA plugin loader and processor. +It is simplified but similar to the 'plugin.cpp/h' source code + in the main application, which is too complex for this use. diff -Nru muse-2.1.2/synti/libsimpleplugin/simplepluginchooserbase.ui muse-3.0.2+ds1/synti/libsimpleplugin/simplepluginchooserbase.ui --- muse-2.1.2/synti/libsimpleplugin/simplepluginchooserbase.ui 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsimpleplugin/simplepluginchooserbase.ui 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,121 @@ + + + SimplePluginChooserBase + + + + 0 + 0 + 777 + 681 + + + + SimpleDrums - Ladspa Plugin Chooser + + + + 6 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + + Name + + + + + Label + + + + + Inports + + + + + Outports + + + + + Creator + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 301 + 31 + + + + + + + + &Cancel + + + Alt+C + + + + + + + &OK + + + Alt+O + + + + + + + + + + + diff -Nru muse-2.1.2/synti/libsimpleplugin/simpler_plugin.cpp muse-3.0.2+ds1/synti/libsimpleplugin/simpler_plugin.cpp --- muse-2.1.2/synti/libsimpleplugin/simpler_plugin.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsimpleplugin/simpler_plugin.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,1334 @@ +// +// C++ Implementation: plugin +// +// Description: +// +// (C) Copyright 2000 Werner Schweer (ws@seh.de) +// +// Additions/modifications: Mathias Lundgren , (C) 2004 +// (C) Copyright 2011 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 +#include +#include +#include +#include +#include +#include +#include "simpler_plugin.h" + +#define SS_LOG_MAX 0 +#define SS_LOG_MIN -10 +#define SS_LOG_OFFSET SS_LOG_MIN + + +#define SP_TRACE_FUNC 0 +#define SP_DEBUG_LADSPA 0 + +#define SP_TRACE_IN if (SP_TRACE_FUNC) fprintf (stderr, "->%s:%d\n", __PRETTY_FUNCTION__, __LINE__); +#define SP_TRACE_OUT if (SP_TRACE_FUNC) fprintf (stderr, "<-%s:%d\n", __PRETTY_FUNCTION__, __LINE__); +#define SP_ERROR(string) fprintf(stderr, "SimplePlugin error: %s\n", string) +#define SP_DBG_LADSPA(string1) if (SP_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1); +#define SP_DBG_LADSPA2(string1, string2) if (SP_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, string2); + +// Turn on debugging messages. +//#define PLUGIN_DEBUGIN + +// Turn on constant stream of debugging messages. +//#define PLUGIN_DEBUGIN_PROCESS + +namespace MusESimplePlugin { + +PluginList plugins; + +// +// Map plugin parameter on domain [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] to domain [SS_LOG_MIN, SS_LOG_MAX] (log domain) +// +float SS_map_pluginparam2logdomain(int pluginparam_val) +{ + float scale = (float) (SS_LOG_MAX - SS_LOG_MIN)/ (float) SS_PLUGIN_PARAM_MAX; + float scaled = (float) pluginparam_val * scale; + float mapped = scaled + SS_LOG_OFFSET; + return mapped; +} +// +// Map plugin parameter on domain to domain [SS_LOG_MIN, SS_LOG_MAX] to [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] (from log-> [0,127]) +// (inverse func to the above) +int SS_map_logdomain2pluginparam(float pluginparam_log) +{ + float mapped = pluginparam_log - SS_LOG_OFFSET; + float scale = (float) SS_PLUGIN_PARAM_MAX / (float) (SS_LOG_MAX - SS_LOG_MIN); + int scaled = (int) round(mapped * scale); + return scaled; +} + +//--------------------------------------------------------- +// loadPluginLib +//--------------------------------------------------------- + +static void loadPluginLib(QFileInfo* fi) + { + SP_TRACE_IN + if (SP_DEBUG_LADSPA) { + fprintf(stderr, "loadPluginLib: %s\n", fi->fileName().toLatin1().constData()); + } + void* handle = dlopen(fi->filePath().toLatin1().constData(), RTLD_NOW); + if (handle == 0) { + fprintf(stderr, "dlopen(%s) failed: %s\n", + fi->filePath().toLatin1().constData(), dlerror()); + return; + } + LADSPA_Descriptor_Function ladspa = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor"); + + if (!ladspa) { + const char *txt = dlerror(); + if (txt) { + fprintf(stderr, + "Unable to find ladspa_descriptor() function in plugin " + "library file \"%s\": %s.\n" + "Are you sure this is a LADSPA plugin file?\n", + fi->filePath().toLatin1().constData(), + txt); + return;//exit(1); + } + } + const LADSPA_Descriptor* descr; + for (int i = 0;; ++i) { + descr = ladspa(i); + if (descr == NULL) + break; + + // Make sure it doesn't already exist. + if(plugins.find(fi->completeBaseName(), QString(descr->Label)) != 0) + continue; + + plugins.push_back(new LadspaPlugin(fi, ladspa, descr)); + } + SP_TRACE_OUT + } + +//--------------------------------------------------------- +// loadPluginDir +//--------------------------------------------------------- + +static void loadPluginDir(const QString& s) + { + SP_TRACE_IN + QDir pluginDir(s, QString("*.so"), 0, QDir::Files); + if (pluginDir.exists()) { + QFileInfoList list = pluginDir.entryInfoList(); + int n = list.size(); + for (int i = 0; i < n; ++i) { + QFileInfo fi = list.at(i); + loadPluginLib(&fi); + } + } + SP_TRACE_OUT + } + +//--------------------------------------------------------- +// initPlugins +// search for LADSPA plugins +//--------------------------------------------------------- + +void SS_initPlugins(const QString& globalLibPath) + { + SP_TRACE_IN + + loadPluginDir(globalLibPath + QString("/plugins")); + + std::string s; + const char* ladspaPath = getenv("LADSPA_PATH"); + if (ladspaPath == 0) + { + const char* home = getenv("HOME"); + s = std::string(home) + std::string("/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa"); + ladspaPath = s.c_str(); + } + const char* p = ladspaPath; + while (*p != '\0') { + const char* pe = p; + while (*pe != ':' && *pe != '\0') + pe++; + + int n = pe - p; + if (n) { + char* buffer = new char[n + 1]; + strncpy(buffer, p, n); + buffer[n] = '\0'; + loadPluginDir(QString(buffer)); + delete[] buffer; + } + p = pe; + if (*p == ':') + p++; + } + SP_TRACE_OUT + } + + +//--------------------------------------------------------- +// find +//--------------------------------------------------------- + +Plugin* PluginList::find(const QString& file, const QString& name) + { + SP_TRACE_IN + for (iPlugin i = begin(); i != end(); ++i) { + if ((file == (*i)->lib()) && (name == (*i)->label())) { + SP_TRACE_OUT + return *i; + } + } + //fprintf(stderr, "Plugin <%s> not found\n", name.toLatin1().constData()); + SP_TRACE_OUT + return 0; + } + +PluginList::~PluginList() +{ + //fprintf(stderr, "~PluginList\n"); + //Cleanup plugins: + for (iPlugin i = plugins.begin(); i != plugins.end(); ++i) + { + if((*i)->references() != 0) + { + fprintf(stderr, "~PluginList: Plugin <%s> reference count not zero! Cannot delete.\n", + (*i)->name().toLatin1().constData()); + continue; + } + //fprintf(stderr, "~PluginList: deleting plugin <%s>\n", + // (*i)->name().toLatin1().constData()); + delete (*i); + } +} + +//--------------------------------------------------------- +// Plugin +//--------------------------------------------------------- + +Plugin::Plugin(const QFileInfo* f) + : fi(*f) + { + _instNo = 0; + _libHandle = 0; + _references = 0; + + _libHandle = 0; + _references = 0; + _instNo = 0; + } + +//--------------------------------------------------------- +// LadspaPlugin +//--------------------------------------------------------- + +LadspaPlugin::LadspaPlugin(const QFileInfo* f, + const LADSPA_Descriptor_Function ldf, + const LADSPA_Descriptor* d) + : Plugin(f), ladspa(ldf), plugin(d) + { + SP_TRACE_IN + _inports = 0; + _outports = 0; + _controlInPorts = 0; + _controlOutPorts = 0; + _sampleRate = 44100; + + _instNo = 0; + + _label = QString(d->Label); + _name = QString(d->Name); + _uniqueID = d->UniqueID; + _maker = QString(d->Maker); + _copyright = QString(d->Copyright); + _portCount = d->PortCount; + + for(unsigned long k = 0; k < _portCount; ++k) + { + LADSPA_PortDescriptor pd = d->PortDescriptors[k]; + if(pd & LADSPA_PORT_AUDIO) + { + if(pd & LADSPA_PORT_INPUT) + { + ++_inports; + iIdx.push_back(k); + } + else + if(pd & LADSPA_PORT_OUTPUT) + { + ++_outports; + oIdx.push_back(k); + } + } + else + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + { + ++_controlInPorts; + pIdx.push_back(k); + } + else + if(pd & LADSPA_PORT_OUTPUT) + { + ++_controlOutPorts; + poIdx.push_back(k); + } + } + } + + /*if (SP_DEBUG_LADSPA) { + printf("Label: %s\tLib: %s\tPortCount: %d\n", this->label().toLatin1().constData(), this->lib().toLatin1().constData(), plugin->PortCount); + printf("LADSPA_PORT_CONTROL|LADSPA_PORT_INPUT: %d\t", pIdx.size()); + printf("Input ports: %d\t", iIdx.size()); + printf("Output ports: %d\n\n", oIdx.size()); + }*/ + + LADSPA_Properties properties = plugin->Properties; + _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(properties); + if (_inports != _outports) + _inPlaceCapable = false; + SP_TRACE_OUT + } + +//--------------------------------------------------------- +// incReferences +//--------------------------------------------------------- + +int LadspaPlugin::incReferences(int val) +{ + #ifdef PLUGIN_DEBUGIN + fprintf(stderr, "Plugin::incReferences _references:%d val:%d\n", _references, val); + #endif + + int newref = _references + val; + + if(newref == 0) + { + _references = 0; + if(_libHandle) + { + #ifdef PLUGIN_DEBUGIN + fprintf(stderr, "Plugin::incReferences no more instances, closing library\n"); + #endif + + dlclose(_libHandle); + } + + _libHandle = 0; + ladspa = NULL; + plugin = NULL; + pIdx.clear(); + poIdx.clear(); + iIdx.clear(); + oIdx.clear(); + + return 0; + } + + if(_libHandle == 0) + { + _libHandle = dlopen(fi.filePath().toLatin1().constData(), RTLD_NOW); + + if(_libHandle == 0) + { + fprintf(stderr, "Plugin::incReferences dlopen(%s) failed: %s\n", + fi.filePath().toLatin1().constData(), dlerror()); + return 0; + } + + LADSPA_Descriptor_Function ladspadf = (LADSPA_Descriptor_Function)dlsym(_libHandle, "ladspa_descriptor"); + if(ladspadf) + { + const LADSPA_Descriptor* descr; + for(unsigned long i = 0;; ++i) + { + descr = ladspadf(i); + if(descr == NULL) + break; + + QString desc_label(descr->Label); + if(desc_label == label()) + { + ladspa = ladspadf; + plugin = descr; + + break; + } + } + } + + if(plugin != NULL) + { + _name = QString(plugin->Name); + _uniqueID = plugin->UniqueID; + _maker = QString(plugin->Maker); + _copyright = QString(plugin->Copyright); + + _portCount = plugin->PortCount; + + _inports = 0; + _outports = 0; + _controlInPorts = 0; + _controlOutPorts = 0; + + for(unsigned long k = 0; k < _portCount; ++k) + { + LADSPA_PortDescriptor pd = plugin->PortDescriptors[k]; + if(pd & LADSPA_PORT_AUDIO) + { + if(pd & LADSPA_PORT_INPUT) + { + ++_inports; + iIdx.push_back(k); + } + else + if(pd & LADSPA_PORT_OUTPUT) + { + ++_outports; + oIdx.push_back(k); + } + } + else + if(pd & LADSPA_PORT_CONTROL) + { + if(pd & LADSPA_PORT_INPUT) + { + ++_controlInPorts; + pIdx.push_back(k); + } + else + if(pd & LADSPA_PORT_OUTPUT) + { + ++_controlOutPorts; + poIdx.push_back(k); + } + } + } + } + } + + if(plugin == NULL) + { + dlclose(_libHandle); + _libHandle = 0; + _references = 0; + fprintf(stderr, "Plugin::incReferences Error: %s no plugin!\n", fi.filePath().toLatin1().constData()); + return 0; + } + + LADSPA_Properties properties = plugin->Properties; + _inPlaceCapable = !LADSPA_IS_INPLACE_BROKEN(properties); + if(_inports != _outports) + _inPlaceCapable = false; + + _references = newref; + + return _references; +} + +//--------------------------------------------------------- +// instantiate +//--------------------------------------------------------- + +void* LadspaPlugin::instantiate(int sampleRate) +{ + _sampleRate = sampleRate; + bool success = false; + LADSPA_Handle h = plugin->instantiate(plugin, _sampleRate); + success = (h != NULL); + if (success) + SP_DBG_LADSPA2("Plugin instantiated", label().toLatin1().constData()); + return h; +} + +//--------------------------------------------------------- +// range +//--------------------------------------------------------- + +void LadspaPlugin::port_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) _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; + } + +//--------------------------------------------------------- +// range +//--------------------------------------------------------- + +void LadspaPlugin::range(unsigned long i, float* min, float* max) const + { + SP_TRACE_IN + i = pIdx[i]; + port_range(i, min, max); + SP_TRACE_OUT + } + +//--------------------------------------------------------- +// range +//--------------------------------------------------------- + +void LadspaPlugin::rangeOut(unsigned long i, float* min, float* max) const + { + SP_TRACE_IN + i = poIdx[i]; + port_range(i, min, max); + SP_TRACE_OUT + } + +//--------------------------------------------------------- +// defaultValue +//--------------------------------------------------------- + +float LadspaPlugin::defaultValue(unsigned long k) const + { + SP_TRACE_IN + k = pIdx[k]; + LADSPA_PortRangeHint range = plugin->PortRangeHints[k]; + LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor; + LADSPA_Data val = 1.0; + if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh)) + val = range.LowerBound; + else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh)) + val = range.UpperBound; + else if (LADSPA_IS_HINT_DEFAULT_LOW(rh)) + if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) + val = exp(log(range.LowerBound) * .75 + + log(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); + 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); + else + val = range.LowerBound*.25 + range.UpperBound*.75; + 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; + // No default found. Make one up... + else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh) && LADSPA_IS_HINT_BOUNDED_ABOVE(rh)) + { + if (LADSPA_IS_HINT_LOGARITHMIC(rh)) + val = exp(log(range.LowerBound) * .5 + + log(range.UpperBound) * .5); + else + val = range.LowerBound*.5 + range.UpperBound*.5; + } + else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh)) + val = range.LowerBound; + else if (LADSPA_IS_HINT_BOUNDED_ABOVE(rh)) + { + // Hm. What to do here... Just try 0.0 or the upper bound if less than zero. + //if(range.UpperBound > 0.0) + // val = 0.0; + //else + // val = range.UpperBound; + // Instead try this: Adopt an 'attenuator-like' policy, where upper is the default. + val = range.UpperBound; + return true; + } + + SP_TRACE_OUT + return val; + } + +//--------------------------------------------------------- +// connectInport +//--------------------------------------------------------- +void LadspaPlugin::connectInport(void* handle, unsigned long k, void* datalocation) + { + SP_TRACE_IN + if(!plugin) + return; + plugin->connect_port((LADSPA_Handle)handle, iIdx[k], (LADSPA_Data*)datalocation); + SP_TRACE_OUT + } + +//--------------------------------------------------------- +// connectOutport +//--------------------------------------------------------- +void LadspaPlugin::connectOutport(void* handle, unsigned long k, void* datalocation) + { + SP_TRACE_IN + if(!plugin) + return; + plugin->connect_port((LADSPA_Handle)handle, oIdx[k], (LADSPA_Data*)datalocation); + SP_TRACE_OUT + } + +//--------------------------------------------------------- +// connectCtrlInport +//--------------------------------------------------------- +void LadspaPlugin::connectCtrlInport(void* handle, unsigned long k, void* datalocation) + { + SP_TRACE_IN + if(!plugin) + return; + plugin->connect_port((LADSPA_Handle)handle, pIdx[k], (LADSPA_Data*)datalocation); + SP_TRACE_OUT + } + +//--------------------------------------------------------- +// connectCtrlOutport +//--------------------------------------------------------- +void LadspaPlugin::connectCtrlOutport(void* handle, unsigned long k, void* datalocation) + { + SP_TRACE_IN + if(!plugin) + return; + plugin->connect_port((LADSPA_Handle)handle, poIdx[k], (LADSPA_Data*)datalocation); + SP_TRACE_OUT + } + +//--------------------------------------------------------- +// convertGuiControlValue +// scale control value to gui-slider/checkbox representation +//--------------------------------------------------------- + +float LadspaPlugin::convertGuiControlValue(unsigned long parameter, int val) const + { + SP_TRACE_IN + float floatval = 0; + float min, max; + range(parameter, &min, &max); + + if (isLog(parameter)) { + if (val > 0) { + float logged = SS_map_pluginparam2logdomain(val); + float e = expf(logged) * (max - min); + e+=min; + floatval = e; + } + } + else if (isBool(parameter)) { + floatval = (float) val; + } + else if (isInt(parameter)) { + float scale = (max - min) / SS_PLUGIN_PARAM_MAX; + floatval = (float) round((((float) val) * scale) + min); + } + else { + float scale = (max - min) / SS_PLUGIN_PARAM_MAX; + floatval = (((float) val) * scale) + min; + } + SP_TRACE_OUT + return floatval; + } + + +//--------------------------------------------------------- +// PluginI +//--------------------------------------------------------- + +void PluginI::init() + { + _plugin = 0; + instances = 0; + handle = 0; + controls = 0; + controlsOut = 0; + controlsOutDummy = 0; + audioInPorts = 0; + audioOutPorts = 0; + controlPorts = 0; + controlOutPorts = 0; + _audioInSilenceBuf = 0; + _audioOutDummyBuf = 0; + _hasLatencyOutPort = false; + _latencyOutPort = 0; + _on = true; + } + +PluginI::PluginI() + { + _id = -1; + init(); + } + +//--------------------------------------------------------- +// PluginI +//--------------------------------------------------------- + +PluginI::~PluginI() + { + if (_plugin) { + deactivate(); + _plugin->incReferences(-1); + } + + if(_audioInSilenceBuf) + free(_audioInSilenceBuf); + if(_audioOutDummyBuf) + free(_audioOutDummyBuf); + + if (controlsOutDummy) + delete[] controlsOutDummy; + if (controlsOut) + delete[] controlsOut; + if (controls) + delete[] controls; + if (handle) + delete[] handle; + } + +//--------------------------------------------------------- +// setID +//--------------------------------------------------------- + +void PluginI::setID(int i) +{ + _id = i; +} + +//--------------------------------------------------------- +// getGuiControlValue +// scale control value to gui-slider/checkbox representation +//--------------------------------------------------------- + +int PluginI::getGuiControlValue(unsigned long parameter) const + { + SP_TRACE_IN + float val = param(parameter); + float min, max; + range(parameter, &min, &max); + int intval; + if (isLog(parameter)) { + intval = SS_map_logdomain2pluginparam(logf(val/(max - min) + min)); + } + else if (isBool(parameter)) { + intval = (int) val; + } + else { + float scale = SS_PLUGIN_PARAM_MAX / (max - min); + intval = (int) ((val - min) * scale); + } + SP_TRACE_OUT + return intval; + } + +//--------------------------------------------------------- +// convertGuiControlValue +// scale control value to gui-slider/checkbox representation +//--------------------------------------------------------- + +float PluginI::convertGuiControlValue(unsigned long parameter, int val) const + { + SP_TRACE_IN + float floatval = 0; + float min, max; + range(parameter, &min, &max); + + if (isLog(parameter)) { + if (val > 0) { + float logged = SS_map_pluginparam2logdomain(val); + float e = expf(logged) * (max - min); + e+=min; + floatval = e; + } + } + else if (isBool(parameter)) { + floatval = (float) val; + } + else if (isInt(parameter)) { + float scale = (max - min) / SS_PLUGIN_PARAM_MAX; + floatval = (float) round((((float) val) * scale) + min); + } + else { + float scale = (max - min) / SS_PLUGIN_PARAM_MAX; + floatval = (((float) val) * scale) + min; + } + SP_TRACE_OUT + return floatval; + } + +//--------------------------------------------------------- +// setChannels +//--------------------------------------------------------- + +void PluginI::setChannels(int c) +{ + channel = c; + + if(!_plugin) + return; + + const unsigned long ins = _plugin->inports(); + const unsigned long outs = _plugin->outports(); + int ni = 1; + if(outs) + { + ni = c / outs; + // Ask for one more instance for remainder if required. + const int re = c % outs; + if(re != 0) + ++ni; + } + else + if(ins) + { + ni = c / ins; + // Ask for one more instance for remainder if required. + const int re = c % ins; + if(re != 0) + ++ni; + } + + if(ni < 1) + ni = 1; + + if (ni == instances) + return; + + LADSPA_Handle* handles = new LADSPA_Handle[ni]; + + if(ni > instances) + { + for(int i = 0; i < ni; ++i) + { + if(i < instances) + // Transfer existing handle from old array to new array. + handles[i] = handle[i]; + else + { + // Create a new plugin instance with handle. + // Use the plugin's current sample rate. + handles[i] = _plugin->instantiate(_plugin->sampleRate()); + if(handles[i] == NULL) + { + fprintf(stderr, "PluginI::setChannels: cannot instantiate instance %d\n", i); + + // Although this is a messed up state not easy to get out of (final # of channels?), try not to assert(). + // Whoever uses these will have to check instance count or null handle, and try to gracefully fix it and allow a song save. + for(int k = i; k < ni; ++k) + handles[i] = NULL; + ni = i + 1; + //channel = ?; + break; + } + } + } + } + else + { + for(int i = 0; i < instances; ++i) + { + if(i < ni) + // Transfer existing handle from old array to new array. + handles[i] = handle[i]; + else + { + // Delete existing plugin instance. + // Previously we deleted all the instances and rebuilt from scratch. + // One side effect of this: Since a GUI is constructed only on the first handle, + // previously the native GUI would close when changing channels. Now it doesn't, which is good. + _plugin->deactivate(handle[i]); + _plugin->cleanup(handle[i]); + } + } + } + + // Delete the old array, and set the new array. + delete[] handle; + handle = handles; + + // Connect new instances' ports: + for(unsigned long k = 0; k < controlPorts; ++k) + { + for(int i = instances; i < ni; ++i) + { + // All instances' parameter inputs share the same controls. + // We don't have a mechanism to expose the other instances' inputs. + _plugin->connectCtrlInport(handles[i], k, &controls[k].val); + } + } + + for(unsigned long k = 0; k < controlOutPorts; ++k) + { + // Connect only the first instance's parameter output controls. + // We don't have a mechanism to display the other instances' outputs. + if(instances == 0 && ni > 0) + // Only if the existing instances was zero. We are creating one(s) now. + _plugin->connectCtrlOutport(handle[0], k, &controlsOut[k].val); + else + { + // Connect the rest to dummy ports. + for(int i = instances; i < ni; ++i) + _plugin->connectCtrlOutport(handle[i], k, &controlsOutDummy[k].val); + } + } + + // Activate new instances. + for(int i = instances; i < ni; ++i) + _plugin->activate(handle[i]); + + // Finally, set the new number of instances. + instances = ni; +} + +void PluginI::process(unsigned long frames) +{ + if(!_plugin) + return; + for(int i = 0; i < instances; ++i) + _plugin->apply(handle[i], frames); +} + +//--------------------------------------------------------- +// apply +// If ports is 0, just process controllers only, not audio (do not 'run'). +//--------------------------------------------------------- + +void PluginI::apply(unsigned /*pos*/, unsigned long frames, unsigned long ports, float** bufIn, float** bufOut) +{ + +#ifdef PLUGIN_DEBUGIN_PROCESS + fprintf(stderr, "PluginI::apply nsamp:%lu\n", n); +#endif + + if(!_plugin) + return; + + if(ports > 0) // Don't bother if not 'running'. + { + connect(ports, 0, bufIn, bufOut); + process(frames); + } +} + +//--------------------------------------------------------- +// start +// activate and connect control ports +//--------------------------------------------------------- + +bool PluginI::start() + { + if(!_plugin) + return false; + // Activate all the instances. + return activate(); + } + +//--------------------------------------------------------- +// stop +// deactivate +//--------------------------------------------------------- +bool PluginI::stop() + { + if(!_plugin) + return false; + // Activate all the instances. + return deactivate(); + } + +//--------------------------------------------------------- +// setParam +//--------------------------------------------------------- + +void PluginI::setParam(unsigned long i, float val) +{ + if(i >= controlPorts) + return; + controls[i].val = val; +} + +//--------------------------------------------------------- +// initPluginInstance +// return true on error +//--------------------------------------------------------- + +bool PluginI::initPluginInstance(Plugin* plug, int c, + int sampleRate, unsigned int segmentSize, + bool useDenormalBias, float denormalBias) + { + channel = c; + if(plug == 0) + { + fprintf(stderr, "initPluginInstance: zero plugin\n"); + return true; + } + _plugin = plug; + + if (_plugin->incReferences(1)==0) + return true; + + QString inst("-" + QString::number(_plugin->instNo())); + _name = _plugin->name() + inst; + _label = _plugin->label() + inst; + + const unsigned long ins = _plugin->inports(); + const unsigned long outs = _plugin->outports(); + if(outs) + { + instances = channel / outs; + // Ask for one more instance for remainder if required. + const int re = channel % outs; + if(re != 0) + ++instances; + if(instances < 1) + instances = 1; + } + else + if(ins) + { + instances = channel / ins; + // Ask for one more instance for remainder if required. + const int re = channel % ins; + if(re != 0) + ++instances; + if(instances < 1) + instances = 1; + } + else + instances = 1; + + handle = new LADSPA_Handle[instances]; + for(int i = 0; i < instances; ++i) + handle[i]=NULL; + + for(int i = 0; i < instances; ++i) + { + #ifdef PLUGIN_DEBUGIN + fprintf(stderr, "PluginI::initPluginInstance instance:%d\n", i); + #endif + + handle[i] = _plugin->instantiate(sampleRate); + if(handle[i] == NULL) + return true; + } + + const unsigned long port_count = _plugin->portCount(); + + audioInPorts = 0; + audioOutPorts = 0; + controlPorts = 0; + controlOutPorts = 0; + + unsigned long port = 0; + for (int i = 0; i < instances; ++i) { + for (unsigned long k = 0; k < port_count; ++k) { + if (_plugin->isAudioIn(k)) { + if(port < (unsigned long)channel) + ++audioInPorts; + ++port; + } + } + } + port = 0; + for (int i = 0; i < instances; ++i) { + for (unsigned long k = 0; k < port_count; ++k) { + if (_plugin->isAudioOut(k)) { + if(port < (unsigned long)channel) + ++audioOutPorts; + ++port; + } + } + } + + for(unsigned long k = 0; k < port_count; ++k) + { + if(_plugin->isParameterIn(k)) + ++controlPorts; + else + if(_plugin->isParameterOut(k)) + ++controlOutPorts; + } + + if(controlPorts) + controls = new Port[controlPorts]; + if(controlOutPorts) + { + controlsOut = new Port[controlOutPorts]; + controlsOutDummy = new Port[controlOutPorts]; + } + + for(unsigned long k = 0; k < controlPorts; ++k) + { + // Set the parameter input's initial value to the default. + const double val = _plugin->defaultValue(k); + controls[k].val = val; + // All instances' parameter inputs share the same controls. + // We don't have a mechanism to expose the other instances' inputs. + for(int i = 0; i < instances; ++i) + _plugin->connectCtrlInport(handle[i], k, &controls[k].val); + } + + for(unsigned long k = 0; k < controlOutPorts; ++k) + { + // Set the parameter output's initial value to zero. + controlsOut[k].val = 0.0; + // Check for a latency port. + const char* pname = _plugin->getParameterOutName(k); + if(pname == QString("latency") || pname == QString("_latency")) + { + _hasLatencyOutPort = true; + _latencyOutPort = k; + } + // Connect only the first instance's parameter output controls. + // We don't have a mechanism to display the other instances' outputs. + if(instances > 0) + { + _plugin->connectCtrlOutport(handle[0], k, &controlsOut[k].val); + // Connect the rest to dummy ports. + for(int i = 1; i < instances; ++i) + _plugin->connectCtrlOutport(handle[i], k, &controlsOutDummy[k].val); + } + } + + int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * segmentSize); + + if(rv != 0) + { + fprintf(stderr, + "ERROR: PluginI::initPluginInstance: _audioInSilenceBuf posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + + if(useDenormalBias) + { + for(unsigned q = 0; q < segmentSize; ++q) + { + _audioInSilenceBuf[q] = denormalBias; + } + } + else + { + memset(_audioInSilenceBuf, 0, sizeof(float) * segmentSize); + } + + rv = posix_memalign((void **)&_audioOutDummyBuf, 16, sizeof(float) * segmentSize); + + if(rv != 0) + { + fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioOutDummyBuf posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + + // Don't activate yet. + //activate(); + return false; + } + +//--------------------------------------------------------- +// connect +//--------------------------------------------------------- + +void PluginI::connect(unsigned long ports, unsigned long offset, float** src, float** dst) + { + if(!_plugin) return; + + const unsigned long port_count = _plugin->portCount(); + unsigned long port = 0; + for (int i = 0; i < instances; ++i) { + for (unsigned long k = 0; k < port_count; ++k) { + if (isAudioIn(k)) { + if(port < ports) + _plugin->connectPort(handle[i], k, src[port] + offset); + else + // Connect to an input silence buffer. + _plugin->connectPort(handle[i], k, _audioInSilenceBuf + offset); + ++port; + } + } + } + port = 0; + for (int i = 0; i < instances; ++i) { + for (unsigned long k = 0; k < port_count; ++k) { + if (isAudioOut(k)) { + if(port < ports) + _plugin->connectPort(handle[i], k, dst[port] + offset); + else + // Connect to a dummy buffer. + _plugin->connectPort(handle[i], k, _audioOutDummyBuf + offset); + ++port; + } + } + } + } + +// //--------------------------------------------------------- +// // connect +// //--------------------------------------------------------- +// +// void PluginI::connectInport(unsigned long k, void* datalocation) +// { +// if(!_plugin) return; +// // const unsigned long ins = _plugin->inports(); +// // const unsigned long handle_idx = (k / ins) % instances; +// // const unsigned long port_idx = k % ins; +// // _plugin->connectInport(handle[handle_idx], port_idx, datalocation); +// +// const unsigned long ins = _plugin->inports(); +// for (int i = 0; i < instances; ++i) { +// for (unsigned long k = 0; k < audio; ++k) { +// } +// +// const unsigned long port_count = _plugin->portCount(); +// unsigned long port = 0; +// for (int i = 0; i < instances; ++i) { +// for (unsigned long k = 0; k < port_count; ++k) { +// if (_plugin->isAudioIn(k)) { +// //if(port < ports) +// if(port < channel) +// //_plugin->connectPort(handle[i], k, src[port] + offset); +// _plugin->connectInport(handle[i], k, datalocation); +// else +// // Connect to an input silence buffer. +// _plugin->connectInport(handle[i], k, _audioInSilenceBuf); +// ++port; +// } +// } +// } +// } +// +// void PluginI::connectOutport(unsigned long k, void* datalocation) +// { +// if(!_plugin) return; +// // const unsigned long outs = _plugin->outports(); +// // const unsigned long handle_idx = (k / outs) % instances; +// // const unsigned long port_idx = k % outs; +// // _plugin->connectOutport(handle[handle_idx], port_idx, datalocation); +// +// const unsigned long port_count = _plugin->portCount(); +// unsigned long port = 0; +// for (int i = 0; i < instances; ++i) { +// for (unsigned long k = 0; k < port_count; ++k) { +// if (_plugin->isAudioOut(k)) { +// //if(port < ports) +// if(port < channel) +// _plugin->connectOutport(handle[i], k, datalocation); +// else +// // Connect to a dummy buffer. +// _plugin->connectOutport(handle[i], k, _audioOutDummyBuf); +// ++port; +// } +// } +// } +// } +// +// void PluginI::connectCtrlInport(unsigned long k, void* datalocation) +// { +// if(!_plugin) return; +// const unsigned long ctrl_ins = _plugin->parameter(); +// const unsigned long handle_idx = (k / ctrl_ins) % instances; +// const unsigned long port_idx = k % ctrl_ins; +// _plugin->connectCtrlInport(handle[handle_idx], port_idx, datalocation); +// } +// +// void PluginI::connectCtrlOutport(unsigned long k, void* datalocation) +// { +// if(!_plugin) return; +// const unsigned long ctrl_outs = _plugin->parameterOut(); +// const unsigned long handle_idx = (k / ctrl_outs) % instances; +// const unsigned long port_idx = k % ctrl_outs; +// _plugin->connectCtrlOutport(handle[handle_idx], port_idx, datalocation); +// } + +//--------------------------------------------------------- +// deactivate +//--------------------------------------------------------- + +bool PluginI::deactivate() + { + if(!_plugin) + return false; + for (int i = 0; i < instances; ++i) { + _plugin->deactivate(handle[i]); + _plugin->cleanup(handle[i]); + } + return true; + } + +//--------------------------------------------------------- +// activate +//--------------------------------------------------------- + +bool PluginI::activate() + { + if(!_plugin) + return false; + for (int i = 0; i < instances; ++i) + _plugin->activate(handle[i]); + return true; + } + +//--------------------------------------------------------- +// latency +//--------------------------------------------------------- + +float PluginI::latency() const +{ + if(!_hasLatencyOutPort) + return 0.0; + return controlsOut[_latencyOutPort].val; +} + + +//--------------------------------------------------------- +// setControl +// set plugin instance controller value by name +//--------------------------------------------------------- + +bool PluginI::setControl(const QString& s, float val) + { + if(!_plugin) + return true; + for (unsigned long i = 0; i < controlPorts; ++i) { + if (QString(_plugin->getParameterName(i)) == s) { + setParam(i, val); + return false; + } + } + fprintf(stderr, "PluginI:setControl(%s, %f) controller not found\n", + s.toLatin1().constData(), val); + return true; + } + + +} // namespace MusESimplePlugin diff -Nru muse-2.1.2/synti/libsimpleplugin/simpler_plugingui.cpp muse-3.0.2+ds1/synti/libsimpleplugin/simpler_plugingui.cpp --- muse-2.1.2/synti/libsimpleplugin/simpler_plugingui.cpp 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsimpleplugin/simpler_plugingui.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,115 @@ +// +// C++ Implementation: ssplugingui +// +// Description: +// +// +// Author: Mathias Lundgren , (C) 2004 +// Contributer: (C) Copyright 2011 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. +// +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "simpler_plugingui.h" + +#define SS_PLUGINCHOOSER_NAMECOL 0 +#define SS_PLUGINCHOOSER_LABELCOL 1 +#define SS_PLUGINCHOOSER_INPORTSCOL 2 +#define SS_PLUGINCHOOSER_OUTPORTSCOL 3 +#define SS_PLUGINCHOOSER_CREATORCOL 4 + +namespace MusESimplePlugin { + +//--------------------------------------------------------- +// getPlugin +//--------------------------------------------------------- + +Plugin* SimplerPluginChooser::getPlugin(QWidget* parent) +{ + SimplerPluginChooser* dialog = new SimplerPluginChooser(parent); + Plugin* p = 0; + int rv = dialog->exec(); + if(rv) + p = dialog->getSelectedPlugin(); + delete dialog; + return p; +} + + +SimplerPluginChooser::SimplerPluginChooser(QWidget* parent) + :QDialog(parent) + { + setupUi(this); + selectedPlugin = 0; + + for (iPlugin i=plugins.begin(); i !=plugins.end(); i++) { + //Support for only 2 or 1 inport/outports + if ( ((*i)->outports() == 2 || (*i)->outports() == 1) && ((*i)->inports() == 2 || (*i)->inports() == 1) ) { + QTreeWidgetItem* tmpItem = new QTreeWidgetItem(effectsListView); + tmpItem->setText(SS_PLUGINCHOOSER_NAMECOL, (*i)->name()); + tmpItem->setText(SS_PLUGINCHOOSER_LABELCOL, (*i)->label()); + tmpItem->setText(SS_PLUGINCHOOSER_INPORTSCOL, QString::number((*i)->inports())); + tmpItem->setText(SS_PLUGINCHOOSER_OUTPORTSCOL, QString::number((*i)->outports())); + tmpItem->setText(SS_PLUGINCHOOSER_CREATORCOL, (*i)->maker()); + effectsListView->addTopLevelItem(tmpItem); + } + } + connect(okButton, SIGNAL(pressed()), SLOT(okPressed())); + connect(cancelButton, SIGNAL(pressed()), SLOT(cancelPressed())); + + connect(effectsListView, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); + connect(effectsListView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(doubleClicked(QTreeWidgetItem*))); + } + +void SimplerPluginChooser::selectionChanged() + { + selectedItem = effectsListView->currentItem(); + } + +void SimplerPluginChooser::okPressed() + { + selectedPlugin = findSelectedPlugin(); + done(QDialog::Accepted); + } + +void SimplerPluginChooser::cancelPressed() + { + done(QDialog::Rejected); + } + +void SimplerPluginChooser::doubleClicked(QTreeWidgetItem* item) + { + selectedItem = item; + selectedPlugin = findSelectedPlugin(); + done(QDialog::Accepted); + } + +Plugin* SimplerPluginChooser::findSelectedPlugin() + { + if(!selectedItem) + return 0; + Plugin* selected = 0; + for (iPlugin i = plugins.begin(); i != plugins.end(); i++) { + if ((*i)->name() == selectedItem->text(SS_PLUGINCHOOSER_NAMECOL)) + selected = (*i); + } + return selected; + } + +} // namespace MusESimplePlugin + diff -Nru muse-2.1.2/synti/libsimpleplugin/simpler_plugingui.h muse-3.0.2+ds1/synti/libsimpleplugin/simpler_plugingui.h --- muse-2.1.2/synti/libsimpleplugin/simpler_plugingui.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsimpleplugin/simpler_plugingui.h 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,69 @@ +// +// C++ Interface: ssplugingui +// +// Description: +// +// +// Author: Mathias Lundgren , (C) 2004 +// Contributer: (C) Copyright 2011 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. +// +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#ifndef __SIMPLER_PLUGIN_GUI_H__ +#define __SIMPLER_PLUGIN_GUI_H__ + +#include + +#include "ui_simplepluginchooserbase.h" +#include "libsimpleplugin/simpler_plugin.h" + +namespace MusESimplePlugin { + +//------------------------------- +// SimplerPluginChooser +//------------------------------- +class SimplerPluginChooser : public QDialog, Ui::SimplePluginChooserBase +{ + Q_OBJECT + private: + Plugin* selectedPlugin; + + protected: + + public: + SimplerPluginChooser(QWidget* parent); + static Plugin* getPlugin(QWidget* parent); + Plugin* getSelectedPlugin() { return selectedPlugin; } + + private slots: + void okPressed(); + void cancelPressed(); + void selectionChanged(); + void doubleClicked(QTreeWidgetItem* item); + + private: + QTreeWidgetItem* selectedItem; + Plugin* findSelectedPlugin(); +}; + +} // namespace MusESimplePlugin + +#endif + diff -Nru muse-2.1.2/synti/libsimpleplugin/simpler_plugin.h muse-3.0.2+ds1/synti/libsimpleplugin/simpler_plugin.h --- muse-2.1.2/synti/libsimpleplugin/simpler_plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsimpleplugin/simpler_plugin.h 2018-01-29 20:07:03.000000000 +0000 @@ -0,0 +1,447 @@ +// +// C++ Interface: plugin +// +// Description: +// +// +// (C) Copyright 2000 Werner Schweer (ws@seh.de) +// Additions/modifications: Mathias Lundgren , (C) 2004 +// (C) Copyright 2011 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 __SIMPLER_PLUGIN_H__ +#define __SIMPLER_PLUGIN_H__ + +#include +#include +#include + +#include +#include + +#define SS_PLUGIN_PARAM_MIN 0 +#define SS_PLUGIN_PARAM_MAX 127 + +namespace MusESimplePlugin { + +//--------------------------------------------------------- +// Port +//--------------------------------------------------------- + +struct Port { + float val; + }; + +//--------------------------------------------------------- +// Plugin +//--------------------------------------------------------- + +class Plugin + { + protected: + QFileInfo fi; + + void* _libHandle; + int _references; + int _instNo; + unsigned long _uniqueID; + QString _label; + QString _name; + QString _maker; + QString _copyright; + + // Total number of ports. + unsigned long _portCount; + unsigned long _inports; + unsigned long _outports; + unsigned long _controlInPorts; + unsigned long _controlOutPorts; + bool _inPlaceCapable; + + //PluginFeatures _requiredFeatures; + + std::vector pIdx; //control port numbers + std::vector poIdx; //control out port numbers + std::vector iIdx; //input port numbers + std::vector oIdx; //output port numbers + + public: + Plugin(const QFileInfo* f); + virtual ~Plugin() {} + + int references() const { return _references; } + virtual int incReferences(int) { return _references; } + int instNo() { return _instNo++; } + virtual void* instantiate(int /*sampleRate*/) { return 0; } + + virtual int sampleRate() const { return 44100; } + virtual void setSampleRate(int) { } + + QString label() const { return _label; } + QString name() const { return _name; } + unsigned long id() const { return _uniqueID; } + QString maker() const { return _maker; } + QString copyright() const { return _copyright; } + QString lib(bool complete = true) const { return complete ? fi.completeBaseName() : fi.baseName(); } + QString dirPath(bool complete = true) const { return complete ? fi.absolutePath() : fi.path(); } + QString filePath() const { return fi.filePath(); } + QString fileName() const { return fi.fileName(); } + + // Total number of ports. + unsigned long portCount() const { return _portCount; } + unsigned long parameter() const { return _controlInPorts; } + unsigned long parameterOut() const { return _controlOutPorts; } + unsigned long inports() const { return _inports; } + unsigned long outports() const { return _outports; } + bool inPlaceCapable() const { return _inPlaceCapable; } + + virtual bool isAudioIn(unsigned long) const { return false; } + virtual bool isAudioOut(unsigned long) const { return false; } + virtual bool isParameterIn(unsigned long) const { return false; } + virtual bool isParameterOut(unsigned long) const { return false; } + + virtual bool isLog(unsigned long) const { return false; } + virtual bool isBool(unsigned long) const { return false; } + virtual bool isInt(unsigned long) const { return false; } + virtual bool isLinear(unsigned long) const { return false; } + virtual float defaultValue(unsigned long) const { return 0.0f; } + virtual void range(unsigned long, float* min, float* max) const { + if(min) *min = 0.0f; + if(max) *max = 1.0f; + } + virtual void rangeOut(unsigned long, float* min, float* max) const { + if(min) *min = 0.0f; + if(max) *max = 1.0f; + } + virtual const char* getParameterName(unsigned long /*param*/) const { return ""; } + virtual const char* getParameterOutName(unsigned long /*param*/) const { return ""; } + + virtual void activate(void* /*handle*/) { } + virtual void deactivate(void* /*handle*/) { } + virtual void cleanup(void* /*handle*/) { } + virtual void connectInport(void* /*handle*/, unsigned long, void* /*datalocation*/) { } + virtual void connectOutport(void* /*handle*/, unsigned long, void* /*datalocation*/) { } + virtual void connectCtrlInport(void* /*handle*/, unsigned long, void* /*datalocation*/) { } + virtual void connectCtrlOutport(void* /*handle*/, unsigned long, void* /*datalocation*/) { } + virtual void connectPort(void* /*handle*/, unsigned long /*port*/, float* /*value*/) { } + virtual void apply(void* /*handle*/, unsigned long /*n*/) { } + }; + +//--------------------------------------------------------- +// LadspaPlugin +//--------------------------------------------------------- + +#define IS_AUDIO_IN (LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT) +#define IS_AUDIO_OUT (LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT) +#define IS_PARAMETER_IN (LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT) +#define IS_PARAMETER_OUT (LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT) + +class LadspaPlugin : public Plugin + { +private: +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-private-field" +#endif + LADSPA_Descriptor_Function ladspa; +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif + const LADSPA_Descriptor* plugin; + + // Accepts a master port index. + void port_range(unsigned long i, float*, float*) const; + + protected: + int _sampleRate; + + public: + LadspaPlugin(const QFileInfo* f, const LADSPA_Descriptor_Function, const LADSPA_Descriptor* d); + virtual ~LadspaPlugin() { } + + virtual int incReferences(int); + + virtual int sampleRate() const { return _sampleRate; } + virtual void setSampleRate(int rate) { _sampleRate = rate; } + + virtual bool isAudioIn(unsigned long k) const { + return (plugin->PortDescriptors[k] & IS_AUDIO_IN) == IS_AUDIO_IN; + } + virtual bool isAudioOut(unsigned long k) const { + return (plugin->PortDescriptors[k] & IS_AUDIO_OUT) == IS_AUDIO_OUT; + } + virtual bool isParameterIn(unsigned long k) const { + return (plugin->PortDescriptors[k] & IS_PARAMETER_IN) == IS_PARAMETER_IN; + } + virtual bool isParameterOut(unsigned long k) const { + return (plugin->PortDescriptors[k] & IS_PARAMETER_OUT) == IS_PARAMETER_OUT; + } + + virtual bool isLog(unsigned long k) const { + LADSPA_PortRangeHint r = plugin->PortRangeHints[pIdx[k]]; + return LADSPA_IS_HINT_LOGARITHMIC(r.HintDescriptor); + } + virtual bool isBool(unsigned long k) const { + return LADSPA_IS_HINT_TOGGLED(plugin->PortRangeHints[pIdx[k]].HintDescriptor); + } + virtual bool isInt(unsigned long k) const { + LADSPA_PortRangeHint r = plugin->PortRangeHints[pIdx[k]]; + return LADSPA_IS_HINT_INTEGER(r.HintDescriptor); + } + virtual bool isLinear(unsigned long k) const { + LADSPA_PortRangeHint r = plugin->PortRangeHints[pIdx[k]]; + return !LADSPA_IS_HINT_INTEGER(r.HintDescriptor) && + !LADSPA_IS_HINT_LOGARITHMIC(r.HintDescriptor) && + !LADSPA_IS_HINT_TOGGLED(r.HintDescriptor); + } + virtual void range(unsigned long i, float*, float*) const; + virtual void rangeOut(unsigned long i, float*, float*) const; + virtual const char* getParameterName(unsigned long i) const { + return plugin->PortNames[pIdx[i]]; + } + virtual const char* getParameterOutName(unsigned long i) const { + return plugin->PortNames[poIdx[i]]; + } + virtual float defaultValue(unsigned long) const; + + float convertGuiControlValue(unsigned long parameter, int val) const; + + virtual void* instantiate(int sampleRate); + virtual void connectInport(void* handle, unsigned long k, void* datalocation); + virtual void connectOutport(void* handle, unsigned long k, void* datalocation); + virtual void connectCtrlInport(void* handle, unsigned long k, void* datalocation); + virtual void connectCtrlOutport(void* handle, unsigned long k, void* datalocation); + + virtual void activate(void* handle) { + if (plugin && plugin->activate) + plugin->activate((LADSPA_Handle)handle); + } + virtual void deactivate(void* handle) { + if (plugin && plugin->deactivate) + plugin->deactivate((LADSPA_Handle)handle); + } + virtual void cleanup(void* handle) { + if (plugin && plugin->cleanup) + plugin->cleanup((LADSPA_Handle)handle); + } + virtual void connectPort(void* handle, unsigned long port, float* datalocation) { + if(plugin) + plugin->connect_port((LADSPA_Handle)handle, port, datalocation); + } + virtual void apply(void* handle, unsigned long n) { + if(plugin && plugin->run) + plugin->run((LADSPA_Handle)handle, n); + } + }; + +class PluginI { + Plugin* _plugin; + int channel; + int instances; + int _id; + + LADSPA_Handle* handle; // per instance + Port* controls; + Port* controlsOut; + Port* controlsOutDummy; + + unsigned long audioInPorts; + unsigned long audioOutPorts; + unsigned long controlPorts; + unsigned long controlOutPorts; + + bool _hasLatencyOutPort; + unsigned long _latencyOutPort; + + float *_audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. + float *_audioOutDummyBuf; // A place to connect unused outputs. + + bool _on; + QString _name; + QString _label; + + void init(); + + public: + PluginI(); + virtual ~PluginI(); + + Plugin* plugin() const { return _plugin; } + + //virtual Plugin::PluginFeatures requiredFeatures() const { return _plugin->requiredFeatures(); } + + bool on() const { return _on; } + void setOn(bool val) { _on = val; } + + unsigned long pluginID() { return _plugin->id(); } + void setID(int i); + int id() { return _id; } + + bool inPlaceCapable() const { + if(!_plugin) return false; + return _plugin->inPlaceCapable(); + } + + // Returns true on error. + bool initPluginInstance(Plugin* plug, int channels, + int sampleRate, unsigned int segmentSize, + bool useDenormalBias, float denormalBias); + void setChannels(int); + // Runs the plugin for frames. Any ports involved must already be connected. + void process(unsigned long frames); + // Runs the plugin for frames. This automatically connects the given + // data locations to the ports each time it is called. + void apply(unsigned pos, unsigned long frames, unsigned long ports, float** bufIn, float** bufOut); + + // Connects ports with data locations. Multiple sources and destinations, with offset. + void connect(unsigned long ports, unsigned long offset, float** src, float** dst); +// // Connects a single audio input port to a data location. +// void connectInport(unsigned long k, void* datalocation); +// // Connects a single audio output port to a data location. +// void connectOutport(unsigned long k, void* datalocation); +// // Connects a single control parameter input port to a data location. +// void connectCtrlInport(unsigned long k, void* datalocation); +// // Connects a single control parameter output port to a data location. +// void connectCtrlOutport(unsigned long k, void* datalocation); + + // Returns true on success. + bool start(); + // Returns true on success. + bool stop(); + + bool isAudioIn(unsigned long k) { + if(!_plugin) return false; + return _plugin->isAudioIn(k); + } + + bool isAudioOut(unsigned long k) { + if(!_plugin) return false; + return _plugin->isAudioOut(k); + } + + bool isLog(unsigned long k) const { + if(!_plugin) return false; + return _plugin->isLog(k); + } + bool isBool(unsigned long k) const { + if(!_plugin) return false; + return _plugin->isBool(k); + } + bool isInt(unsigned long k) const { + if(!_plugin) return false; + return _plugin->isInt(k); + } + bool isLinear(unsigned long k) const { + if(!_plugin) return false; + return _plugin->isLinear(k); + } + + void range(unsigned long i, float* min, float* max) const { + if(!_plugin) return; + _plugin->range(i, min, max); + } + void rangeOut(unsigned long i, float* min, float* max) const { + if(!_plugin) return; + _plugin->rangeOut(i, min, max); + } + const char* getParameterName(unsigned long i) const { + if(!_plugin) return 0; + return _plugin->getParameterName(i); + } + const char* getParameterOutName(unsigned long i) const { + if(!_plugin) return 0; + return _plugin->getParameterOutName(i); + } + float defaultValue(unsigned long i) const { + if(!_plugin) return 0.0; + return _plugin->defaultValue(i); + } + + // Return true on success. + bool activate(); + // Return true on success. + bool deactivate(); + QString pluginLabel() const { return _plugin->label(); } + QString label() const { return _label; } + QString name() const { return _name; } + QString lib() const { return _plugin->lib(); } + QString dirPath() const { return _plugin->dirPath(); } + QString fileName() const { return _plugin->fileName(); } + + bool setControl(const QString& s, float val); + + unsigned long inports() const { return audioInPorts; } + unsigned long outports() const { return audioOutPorts; } + unsigned long parameters() const { return controlPorts; } + unsigned long parametersOut() const { return controlOutPorts; } + + void setParam(unsigned long i, float val); + float param(unsigned long i) const { if(i >= controlPorts) return 0.0; return controls[i].val; } + float paramOut(unsigned long i) const { if(i >= controlOutPorts) return 0.0; return controlsOut[i].val; } + + // Alias for getParameterName. + const char* paramName(unsigned long i) const { return getParameterName(i); } + const char* paramOutName(unsigned long i) const { return getParameterOutName(i); } + + float latency() const; + //CtrlValueType ctrlValueType(unsigned long i) const { return _plugin->ctrlValueType(controls[i].idx); } + //CtrlList::Mode ctrlMode(unsigned long i) const { return _plugin->ctrlMode(controls[i].idx); } + + int getGuiControlValue(unsigned long parameter) const; + float convertGuiControlValue(unsigned long parameter, int val) const; + }; + +static inline float fast_log2 (float val) + { + /* don't use reinterpret_cast<> because that prevents this + from being used by pure C code (for example, GnomeCanvasItems) + */ + int* const exp_ptr = (int *)(&val); + int x = *exp_ptr; + const int log_2 = ((x >> 23) & 255) - 128; + x &= ~(255 << 23); + x += 127 << 23; + *exp_ptr = x; + val = ((-1.0f/3) * val + 2) * val - 2.0f/3; // (1) + return (val + log_2); + } + +static inline float fast_log10 (const float val) + { + return fast_log2(val) / 3.312500f; + } + +//--------------------------------------------------------- +// PluginList +//--------------------------------------------------------- + +typedef std::list::iterator iPlugin; + +class PluginList : public std::list { + public: + Plugin* find(const QString& file, const QString& name); + PluginList() {} + ~PluginList(); + }; + +extern void SS_initPlugins(const QString& globalLibPath); +extern PluginList plugins; + +} // namespace MusESimplePlugin + +#endif diff -Nru muse-2.1.2/synti/libsynti/CMakeLists.txt muse-3.0.2+ds1/synti/libsynti/CMakeLists.txt --- muse-2.1.2/synti/libsynti/CMakeLists.txt 2013-03-28 15:17:16.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsynti/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -23,6 +23,9 @@ include(${PROJECT_SOURCE_DIR}/pch.txt) +QT5_WRAP_CPP ( mess_mocs + gui.h + ) ## ## List of source files to compile ## @@ -42,9 +45,14 @@ ## add_library(synti ${MODULES_BUILD} ${PROJECT_BINARY_DIR}/all-pic.h + ${mess_mocs} ${synti_source_files} ) +target_link_libraries(synti + # TODO: Try to remove this dependency by splitting the gui out of synti. + ${QT_LIBRARIES} + ) ## ## Append to the list of translations ## diff -Nru muse-2.1.2/synti/libsynti/gui.cpp muse-3.0.2+ds1/synti/libsynti/gui.cpp --- muse-2.1.2/synti/libsynti/gui.cpp 2013-03-28 15:17:16.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsynti/gui.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -23,10 +23,43 @@ //========================================================= #include "gui.h" -#include "muse/midi.h" +#include "muse/midi_consts.h" +#include #include +SignalGui::SignalGui() +{ + +} +void SignalGui::create() +{ +// int filedes[2]; // 0 - reading 1 - writing +// if (pipe(filedes) == -1) { +// perror("thread:creating pipe4"); +// exit(-1); +// } +// readFd = filedes[0]; +// writeFd = filedes[1]; + +// QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); +// connect(s, SIGNAL(activated(int)), SIGNAL(wakeup())); +} + +void SignalGui::clearSignal() +{ +// printf("clearSignal %ld\n", (long)QThread::currentThreadId()); + +// char c; +// ::read(readFd, &c, 1); +} +void SignalGui::sendSignal() +{ +// printf("sendSignal - emit wakeup() %ld\n", (long)QThread::currentThreadId()); + emit wakeup(); +// write(writeFd, "x", 1); // wakeup GUI +} + //--------------------------------------------------------- // MessGui //--------------------------------------------------------- @@ -36,13 +69,7 @@ // // prepare for interprocess communication: // - int filedes[2]; // 0 - reading 1 - writing - if (pipe(filedes) == -1) { - perror("thread:creating pipe4"); - exit(-1); - } - readFd = filedes[0]; - writeFd = filedes[1]; + guiSignal.create(); wFifoSize = 0; wFifoWindex = 0; wFifoRindex = 0; @@ -65,9 +92,8 @@ void MessGui::readMessage() { - char c; while (rFifoSize) { - ::read(readFd, &c, 1); + guiSignal.clearSignal(); processEvent(rFifo[rFifoRindex]); rFifoRindex = (rFifoRindex + 1) % EVENT_FIFO_SIZE; --rFifoSize; @@ -95,9 +121,6 @@ void MessGui::sendController(int ch, int idx, int val) { -// MusECore::MidiPlayEvent pe(0, 0, ch, MusECore::ME_CONTROLLER, idx, val); -// sendEvent(pe); - sendEvent(MusECore::MidiPlayEvent(0, 0, ch, MusECore::ME_CONTROLLER, idx, val)); } @@ -107,9 +130,6 @@ void MessGui::sendSysex(unsigned char* p, int n) { -// MusECore::MidiPlayEvent pe(0, 0, MusECore::ME_SYSEX, p, n); -// sendEvent(pe); - sendEvent(MusECore::MidiPlayEvent(0, 0, MusECore::ME_SYSEX, p, n)); } @@ -127,7 +147,7 @@ rFifo[rFifoWindex] = ev; rFifoWindex = (rFifoWindex + 1) % EVENT_FIFO_SIZE; ++rFifoSize; - write(writeFd, "x", 1); // wakeup GUI + guiSignal.sendSignal(); } //--------------------------------------------------------- diff -Nru muse-2.1.2/synti/libsynti/gui.h muse-3.0.2+ds1/synti/libsynti/gui.h --- muse-2.1.2/synti/libsynti/gui.h 2013-03-28 15:17:16.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsynti/gui.h 2018-01-06 20:31:35.000000000 +0000 @@ -25,19 +25,32 @@ #ifndef __SYNTH_GUI_H__ #define __SYNTH_GUI_H__ +#include #include "mpevent.h" -const int EVENT_FIFO_SIZE = 256; +const int EVENT_FIFO_SIZE = 4096; class QWidget; +class SignalGui : public QObject { + Q_OBJECT + int writeFd; +public: + SignalGui(); + void create(); + void clearSignal(); + void sendSignal(); +signals: + void wakeup(); +protected: + int readFd; +}; + //--------------------------------------------------------- // MessGui // manage IO from synti-GUI to Host //--------------------------------------------------------- -class MessGui { - int writeFd; - +class MessGui{ // Event Fifo synti -> GUI MusECore::MidiPlayEvent rFifo[EVENT_FIFO_SIZE]; volatile int rFifoSize; @@ -49,9 +62,10 @@ volatile int wFifoSize; int wFifoWindex; int wFifoRindex; + SignalGui guiSignal; protected: - int readFd; + SignalGui *getGuiSignal() { return &guiSignal;} void readMessage(); void sendEvent(const MusECore::MidiPlayEvent& ev); void sendController(int,int,int); diff -Nru muse-2.1.2/synti/libsynti/mess.cpp muse-3.0.2+ds1/synti/libsynti/mess.cpp --- muse-2.1.2/synti/libsynti/mess.cpp 2013-03-28 15:17:16.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsynti/mess.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -21,8 +21,8 @@ //========================================================= #include "mess.h" -#include "muse/midi.h" -#include "muse/midictrl.h" +#include "muse/midi_consts.h" +#include "muse/midictrl_consts.h" static const int FIFO_SIZE = 32; @@ -62,19 +62,6 @@ } //--------------------------------------------------------- -// getGeometry -// dummy -//--------------------------------------------------------- - -void Mess::getGeometry(int* x, int* y, int* w, int* h) const - { - *x = 0; - *y = 0; - *w = 0; - *h = 0; - } - -//--------------------------------------------------------- // getNativeGeometry // dummy //--------------------------------------------------------- @@ -149,6 +136,9 @@ return setController(ev.channel(), MusECore::CTRL_PITCH, ev.dataA()); case MusECore::ME_AFTERTOUCH: return setController(ev.channel(), MusECore::CTRL_AFTERTOUCH, ev.dataA()); + // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore - only CTRL_PROGRAM. + //case MusECore::ME_PROGRAM: + // return setController(ev.channel(), MusECore::CTRL_PROGRAM, ev.dataA()); } return false; } diff -Nru muse-2.1.2/synti/libsynti/mess.h muse-3.0.2+ds1/synti/libsynti/mess.h --- muse-2.1.2/synti/libsynti/mess.h 2013-03-28 15:17:16.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsynti/mess.h 2018-01-29 20:07:03.000000000 +0000 @@ -26,11 +26,69 @@ #define MESS_MAJOR_VERSION 1 #define MESS_MINOR_VERSION 1 -#include #include "mpevent.h" -class QWidget; -class MessP; +struct MessP; + +//--------------------------------------------------------- +// MessConfig +// Information to be passed to MESS::instantiate(). +// The plugin is free to store these values as it wishes +// without relying on any particular libraries. +//--------------------------------------------------------- + +struct MessConfig { + unsigned int _segmentSize; + int _sampleRate; + int _minMeterVal; + bool _useDenormalBias; + float _denormalBias; + bool _leftMouseButtonCanDecrease; + const char* _configPath; + const char* _globalLibPath; + const char* _globalSharePath; + const char* _userPath; + const char* _projectPath; + + MessConfig() { + _segmentSize = 1024; + _sampleRate = 44100; + _minMeterVal = 0; + _useDenormalBias = false; + _denormalBias = 0.0; + _leftMouseButtonCanDecrease = false; + _configPath = 0; + _globalLibPath = 0; + _globalSharePath = 0; + _userPath = 0; + _projectPath = 0; + } + + MessConfig(unsigned int segmentSize, + int sampleRate, + int minMeterVal, + bool useDenormalBias, + float denormalBias, + bool leftMouseButtonCanDecrease, + const char* configPath, + const char* globalLibPath, + const char* globalSharePath, + const char* userPath, + const char* projectPath) + { + _segmentSize = segmentSize; + _sampleRate = sampleRate; + _minMeterVal = minMeterVal; + _useDenormalBias = useDenormalBias; + _denormalBias = denormalBias; + _leftMouseButtonCanDecrease = leftMouseButtonCanDecrease; + _configPath = configPath; + _globalLibPath = globalLibPath; + _globalSharePath = globalSharePath; + _userPath = userPath; + _projectPath = projectPath; + } +}; //--------------------------------------------------------- // MidiPatch @@ -76,7 +134,7 @@ void setSampleRate(int r) { _sampleRate = r; } virtual void processMessages() { }; - virtual void process(float** data, int offset, int len) = 0; + virtual void process(unsigned pos, float** data, int offset, int len) = 0; // the synti has to (re-)implement processEvent() or provide // some of the next three functions: @@ -86,10 +144,14 @@ virtual bool playNote(int, int, int) { return false; } virtual bool sysex(int, const unsigned char*) { return false; } - virtual void getInitData(int* n, const unsigned char**) /*const*/ { *n = 0; } // No const: Synths may need to allocate member pointers. p4.0.27 Tim + virtual void getInitData(int* n, const unsigned char**) /*const*/ { *n = 0; } // No const: Synths may need to allocate member pointers. virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const {return 0;} - virtual QString getPatchName(int, int, bool) const { return "?"; } + virtual const char* getPatchName(int, int, bool) const { return "?"; } virtual const MidiPatch* getPatchInfo(int, const MidiPatch*) const { return 0; } + // True if it found a name. + virtual bool getNoteSampleName(bool /*drum*/, int /*channel*/, + int /*patch*/, int /*note*/, + const char** /*name*/) const { return false; } // synthesizer -> host communication void sendEvent(MusECore::MidiPlayEvent); // called from synti @@ -97,16 +159,12 @@ int eventsPending() const; // GUI interface routines - virtual bool hasGui() const { return false; } - virtual bool guiVisible() const { return false; } - virtual void showGui(bool) {} virtual bool hasNativeGui() const { return false; } virtual bool nativeGuiVisible() const { return false; } virtual void showNativeGui(bool) {} - virtual void getGeometry(int* x, int* y, int* w, int* h) const; - virtual void setGeometry(int, int, int, int) {} virtual void getNativeGeometry(int* x, int* y, int* w, int* h) const; virtual void setNativeGeometry(int, int, int, int) {} + virtual void guiHeartBeat() {} }; //--------------------------------------------------------- @@ -119,7 +177,7 @@ const char* description; const char* version; int majorMessVersion, minorMessVersion; - Mess* (*instantiate)(int sr, QWidget* parent, QString* projectPathPtr, const char* name); + Mess* (*instantiate)(unsigned long long parentWinId, const char* name, const MessConfig* config); }; extern "C" { diff -Nru muse-2.1.2/synti/libsynti/mono.h muse-3.0.2+ds1/synti/libsynti/mono.h --- muse-2.1.2/synti/libsynti/mono.h 2013-03-28 15:17:16.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsynti/mono.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,7 +23,7 @@ //========================================================= #ifndef __SYNTH_MONO_H__ -#define __SYNTH_MONO_H +#define __SYNTH_MONO_H__ #include #include "mess.h" diff -Nru muse-2.1.2/synti/libsynti/poly.cpp muse-3.0.2+ds1/synti/libsynti/poly.cpp --- muse-2.1.2/synti/libsynti/poly.cpp 2013-03-28 15:17:16.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsynti/poly.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -23,7 +23,7 @@ //========================================================= #include "poly.h" -#include "muse/midictrl.h" +#include "muse/midictrl_consts.h" //--------------------------------------------------------- // playNote diff -Nru muse-2.1.2/synti/libsynti/poly.h muse-3.0.2+ds1/synti/libsynti/poly.h --- muse-2.1.2/synti/libsynti/poly.h 2013-03-28 15:17:16.000000000 +0000 +++ muse-3.0.2+ds1/synti/libsynti/poly.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,7 +23,7 @@ //========================================================= #ifndef __SYNTH_POLY_H__ -#define __SYNTH_POLY_H +#define __SYNTH_POLY_H__ #include #include "mess.h" @@ -35,11 +35,11 @@ //--------------------------------------------------------- class MessPoly : public Mess { - float volume; - float expression; + //float volume; + //float expression; // cached values: - float mainLevel; + //float mainLevel; protected: virtual bool playNote(int channel, int pitch, int velo); diff -Nru muse-2.1.2/synti/organ/CMakeLists.txt muse-3.0.2+ds1/synti/organ/CMakeLists.txt --- muse-2.1.2/synti/organ/CMakeLists.txt 2013-03-28 15:17:14.000000000 +0000 +++ muse-3.0.2+ds1/synti/organ/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( organ_mocs +QT5_WRAP_CPP ( organ_mocs organgui.h ) @@ -34,7 +34,7 @@ file (GLOB organ_ui_files organguibase.ui ) -QT4_WRAP_UI ( organ_uis ${organ_ui_files} ) +QT5_WRAP_UI ( organ_uis ${organ_ui_files} ) ## ## List of source files to compile @@ -42,8 +42,6 @@ file (GLOB organ_source_files organ.cpp organgui.cpp - # reverb.cpp - # routing.cpp ) ## @@ -82,6 +80,7 @@ synti # awl ${QT_LIBRARIES} + mpevent_module ) ## diff -Nru muse-2.1.2/synti/organ/common_defs.h muse-3.0.2+ds1/synti/organ/common_defs.h --- muse-2.1.2/synti/organ/common_defs.h 2013-03-28 15:17:14.000000000 +0000 +++ muse-3.0.2+ds1/synti/organ/common_defs.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,7 +23,8 @@ #ifndef __ORGAN_UNIQUE_ID_H #define __ORGAN_UNIQUE_ID_H -// Make sure this number is unique among all the MESS synths. +// Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. #define ORGAN_UNIQUE_ID 1 #endif diff -Nru muse-2.1.2/synti/organ/organ.cpp muse-3.0.2+ds1/synti/organ/organ.cpp --- muse-2.1.2/synti/organ/organ.cpp 2013-03-28 15:17:14.000000000 +0000 +++ muse-3.0.2+ds1/synti/organ/organ.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -28,8 +28,7 @@ #include #include -#include "muse/midi.h" -//#include "libsynti/mpevent.h" +#include "muse/midi_consts.h" #include "muse/mpevent.h" //#include "common_defs.h" @@ -103,7 +102,7 @@ // centibels to amplitude conversion for (int i = 0; i < MAX_ATTENUATION; i++) - cb2amp_tab[i] = pow(10.0, double(i) / -200.0); + cb2amp_tab[i] = exp10(double(i) / -200.0); for (int i = 0; i < 128; ++i) { double freq = 8.176 * exp(double(i)*log(2.0)/12.0); @@ -230,7 +229,7 @@ // Called from host, ONLY if output path is connected. //--------------------------------------------------------- -void Organ::process(float** ports, int offset, int sampleCount) +void Organ::process(unsigned /*pos*/, float** ports, int offset, int sampleCount) { /* // @@ -280,16 +279,20 @@ freq_256_harm4 = freq_256_harm3 * 2; freq_256_harm5 = freq_256_harm4 * 2; for (int i = 0; i < sampleCount; i++) { - int a1=0, a2=0; //prevent compiler warning: unitialized usage of vars a1 & a2 + int a1=0, a2=0; //prevent compiler warning: uninitialized usage of vars a1 & a2 switch(v->state1) { case ATTACK: if (v->envL1.step(&a1)) break; v->state1 = DECAY; + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH case DECAY: if (v->envL2.step(&a1)) break; v->state1 = SUSTAIN; + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH case SUSTAIN: a1 = sustain0; break; @@ -305,10 +308,14 @@ if (v->envH1.step(&a2)) break; v->state2 = DECAY; + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH case DECAY: if (v->envH2.step(&a2)) break; v->state2 = SUSTAIN; + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH case SUSTAIN: a2 = sustain1; break; @@ -340,16 +347,19 @@ freq_256_harm4 = freq_256 * 3; freq_256_harm5 = freq_256_harm3 * 2; for (int i = 0; i < sampleCount; i++) { - int a1=0, a2=0;//prevent compiler warning: unitialized usage of vars a1 & a2 + int a1=0, a2=0;//prevent compiler warning: uninitialized usage of vars a1 & a2 switch(v->state1) { case ATTACK: if (v->envL1.step(&a1)) break; v->state1 = DECAY; + // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: + // FALLTHROUGH case DECAY: if (v->envL2.step(&a1)) break; v->state1 = SUSTAIN; + // FALLTHROUGH case SUSTAIN: a1 = sustain0; break; @@ -365,10 +375,12 @@ if (v->envH1.step(&a2)) break; v->state2 = DECAY; + // FALLTHROUGH case DECAY: if (v->envH2.step(&a2)) break; v->state2 = SUSTAIN; + // FALLTHROUGH case SUSTAIN: a2 = sustain1; break; @@ -741,9 +753,9 @@ // construct a new synthesizer instance //--------------------------------------------------------- -static Mess* instantiate(int sr, QWidget*, QString* /*projectPathPtr*/, const char* name) +static Mess* instantiate(unsigned long long /*parentWinId*/, const char* name, const MessConfig* config) { - Organ* synth = new Organ(sr); + Organ* synth = new Organ(config->_sampleRate); if (synth->init(name)) { delete synth; synth = 0; diff -Nru muse-2.1.2/synti/organ/organgui.cpp muse-3.0.2+ds1/synti/organ/organgui.cpp --- muse-2.1.2/synti/organ/organgui.cpp 2013-03-28 15:17:14.000000000 +0000 +++ muse-3.0.2+ds1/synti/organ/organgui.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -31,13 +31,12 @@ #include #include #include -#include #include #include "common_defs.h" #include "organgui.h" -#include "muse/midi.h" -#include "muse/midictrl.h" +#include "muse/midi_consts.h" +#include "muse/midictrl_consts.h" //#define ORGANGUI_DEBUG @@ -49,8 +48,7 @@ : QWidget(0, Qt::Window), MessGui() { setupUi(this); // p4.0.17 - QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); - connect(s, SIGNAL(activated(int)), SLOT(readMessage(int))); + connect(this->getGuiSignal(),SIGNAL(wakeup()),this,SLOT(readMessage())); dctrl[0] = SynthGuiCtrl(p1, lcd1, SynthGuiCtrl::SLIDER); dctrl[1] = SynthGuiCtrl(p2, lcd2, SynthGuiCtrl::SLIDER); @@ -80,15 +78,6 @@ connect((QCheckBox*)(dctrl[i].editor), SIGNAL(toggled(bool)), map, SLOT(map())); } connect(map, SIGNAL(mapped(int)), this, SLOT(ctrlChanged(int))); - - // work around for probable QT/WM interaction bug. - // for certain window managers, e.g xfce, this window is - // is displayed although not specifically set to show(); - // bug: 2811156 Softsynth GUI unclosable with XFCE4 (and a few others) - // Nov 21, 2012 Hey this causes the thing not to open at all, EVER, on Lubuntu and some others! - // And we had a request to remove this from a knowledgable tester. REMOVE Tim. - ///show(); - ///hide(); } //--------------------------------------------------------- @@ -203,7 +192,7 @@ // readMessage //--------------------------------------------------------- -void OrganGui::readMessage(int) +void OrganGui::readMessage() { MessGui::readMessage(); } diff -Nru muse-2.1.2/synti/organ/organgui.h muse-3.0.2+ds1/synti/organ/organgui.h --- muse-2.1.2/synti/organ/organgui.h 2013-03-28 15:17:14.000000000 +0000 +++ muse-3.0.2+ds1/synti/organ/organgui.h 2018-01-06 20:31:35.000000000 +0000 @@ -48,7 +48,7 @@ private slots: void ctrlChanged(int idx); - void readMessage(int); + void readMessage(); public: virtual void processEvent(const MusECore::MidiPlayEvent&); diff -Nru muse-2.1.2/synti/organ/organ.h muse-3.0.2+ds1/synti/organ/organ.h --- muse-2.1.2/synti/organ/organ.h 2013-03-28 15:17:14.000000000 +0000 +++ muse-3.0.2+ds1/synti/organ/organ.h 2018-01-29 20:07:03.000000000 +0000 @@ -28,7 +28,7 @@ #ifndef __ORGAN_H__ #define __ORGAN_H__ -#include "muse/midictrl.h" +#include "muse/midictrl_consts.h" #include "libsynti/mess.h" #include "common_defs.h" @@ -193,19 +193,15 @@ public: virtual void processMessages(); - virtual void process(float**, int, int); + virtual void process(unsigned pos, float**, int, int); virtual bool playNote(int channel, int pitch, int velo); virtual bool setController(int channel, int ctrl, int val); virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const; - //virtual void getInitData(int*, const unsigned char**) const; virtual void getInitData(int*, const unsigned char**); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** data) const; - //virtual bool guiVisible() const; - //virtual void showGui(bool); - //virtual bool hasGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); virtual bool hasNativeGui() const { return true; } diff -Nru muse-2.1.2/synti/s1/CMakeLists.txt muse-3.0.2+ds1/synti/s1/CMakeLists.txt --- muse-2.1.2/synti/s1/CMakeLists.txt 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/s1/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -60,6 +60,7 @@ ## target_link_libraries(s1 synti + ${QT_LIBRARIES} ) ## diff -Nru muse-2.1.2/synti/s1/s1.cpp muse-3.0.2+ds1/synti/s1/s1.cpp --- muse-2.1.2/synti/s1/s1.cpp 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/s1/s1.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -36,7 +36,8 @@ #include "libsynti/mono.h" #define RESOLUTION 16384 -// Make sure this number is unique among all the MESS synths. +// Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. #define S1_UNIQUE_ID 6 @@ -59,10 +60,7 @@ virtual void note(int channel, int pitch, int velo); //virtual void processMessages(); - virtual void process(float** buffer, int offset, int n); - //virtual bool hasGui() const { return true; } - //virtual bool guiVisible() const { return _showGui; } - //virtual void showGui(bool); + virtual void process(unsigned pos, float** buffer, int offset, int n); virtual bool hasNativeGui() const { return true; } virtual bool nativeGuiVisible() const { return _showGui; } virtual void showNativeGui(bool); @@ -143,7 +141,7 @@ // synthesize n samples into buffer+offset //--------------------------------------------------------- -void S1::process(float** buffer, int offset, int n) +void S1::process(unsigned /*pos*/, float** buffer, int offset, int n) { if (gate == 0) return; @@ -227,10 +225,10 @@ class QWidget; -static Mess* instantiate(int sr, QWidget*, QString* /* projectPathPtr */, const char*) +static Mess* instantiate(unsigned long long /*parentWinId*/, const char* /*name*/, const MessConfig* config) { S1* s1 = new S1(); - s1->setSampleRate(sr); + s1->setSampleRate(config->_sampleRate); return s1; } diff -Nru muse-2.1.2/synti/simpledrums2/CMakeLists.txt muse-3.0.2+ds1/synti/simpledrums2/CMakeLists.txt --- muse-2.1.2/synti/simpledrums2/CMakeLists.txt 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( simpledrums_mocs +QT5_WRAP_CPP ( simpledrums_mocs simpledrumsgui.h ssplugingui.h ) @@ -34,9 +34,8 @@ ## file (GLOB simpledrums_ui_files simpledrumsguibase.ui - sspluginchooserbase.ui ) -QT4_WRAP_UI ( simpledrums_uis ${simpledrums_ui_files} ) +QT5_WRAP_UI ( simpledrums_uis ${simpledrums_ui_files} ) ## ## List of source files to compile @@ -44,7 +43,6 @@ file (GLOB simpledrums_source_files simpledrums.cpp simpledrumsgui.cpp - ssplugin.cpp ssplugingui.cpp ) @@ -92,6 +90,15 @@ target_link_libraries(simpledrums synti ${QT_LIBRARIES} + ${SNDFILE_LIBRARIES} + ${SAMPLERATE_LIBRARIES} + simpler_plugin + simpler_plugingui + mpevent_module + wavepreview_module + widgets + dl + pthread ) ## diff -Nru muse-2.1.2/synti/simpledrums2/common_defs.h muse-3.0.2+ds1/synti/simpledrums2/common_defs.h --- muse-2.1.2/synti/simpledrums2/common_defs.h 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/common_defs.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,7 +23,8 @@ #ifndef __SIMPLEDRUMS_UNIQUE_ID_H #define __SIMPLEDRUMS_UNIQUE_ID_H -// Make sure this number is unique among all the MESS synths. +// Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. #define SIMPLEDRUMS_UNIQUE_ID 4 #endif diff -Nru muse-2.1.2/synti/simpledrums2/common.h muse-3.0.2+ds1/synti/simpledrums2/common.h --- muse-2.1.2/synti/simpledrums2/common.h 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/common.h 2018-01-29 20:07:03.000000000 +0000 @@ -30,15 +30,16 @@ #ifndef __MUSE_TESTO_COMMON_H__ #define __MUSE_TESTO_COMMON_H__ -#include "muse/midictrl.h" +#include "muse/midictrl_consts.h" +#include #define SS_VERSIONSTRING "1.0" -#define SS_DEBUG 0 +#define SS_DEBUG 0 #define SS_DEBUG_INIT 0 #define SS_TRACE_FUNC 0 #define SS_DEBUG_MIDI 0 -#define SS_DEBUG_LADSPA 0 +#define SS_DEBUG_LADSPA 0 #define SS_DEBUG_STATE 0 #define SS_DBG(string) if (SS_DEBUG) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string); @@ -51,11 +52,13 @@ #define SS_DBG_LADSPA(string1) if (SS_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1); #define SS_DBG_LADSPA2(string1, string2) if (SS_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, string2); -#define SS_SYSEX_INIT_DATA_VERSION 1 +#define SS_SYSEX_INIT_DATA_VERSION 3 +// version 2 added pitching support +// version 3 added multi-channel routing support (danvd) #define SS_SYSEX_EFFECT_INIT_DATA_VERSION 2 // Added Jun 15 2011. Original value was SS_SYSEX_INIT_DATA_VERSION (1). p4.0.27 Tim. #define SS_NR_OF_CHANNELS 16 -#define SS_AUDIO_CHANNELS 2 +#define SS_AUDIO_CHANNELS (2 + SS_NR_OF_CHANNELS*2) #define SS_NR_OF_SENDEFFECTS 4 // Controller-related: @@ -67,12 +70,14 @@ #define SS_CHANNEL_SENDFX2 5 #define SS_CHANNEL_SENDFX3 6 #define SS_CHANNEL_SENDFX4 7 +#define SS_CHANNEL_CTRL_PITCH 8 +#define SS_CHANNEL_CTRL_ROUTE 9 #define SS_PLUGIN_RETURN 0 #define SS_PLUGIN_ONOFF 1 #define SS_NR_OF_MASTER_CONTROLLERS 1 -#define SS_NR_OF_CHANNEL_CONTROLLERS 8 +#define SS_NR_OF_CHANNEL_CONTROLLERS 10 #define SS_NR_OF_PLUGIN_CONTROLLERS 2 #define SS_NR_OF_CONTROLLERS (SS_NR_OF_MASTER_CONTROLLERS + (SS_NR_OF_CHANNELS * SS_NR_OF_CHANNEL_CONTROLLERS) + (SS_NR_OF_PLUGIN_CONTROLLERS*SS_NR_OF_SENDEFFECTS)) @@ -87,6 +92,9 @@ #define SS_MASTER_CTRL_VOLUME SS_FIRST_MASTER_CONTROLLER #define SS_CHANNEL_VOLUME_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_VOLUME) +#define SS_CHANNEL_PITCH_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_PITCH) +#define SS_CHANNEL_ROUTE_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_ROUTE) + #define SS_CHANNEL_PAN_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_PAN) #define SS_CHANNEL_NOFF_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_NOFF) #define SS_CHANNEL_ONOFF_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_ONOFF) @@ -119,11 +127,16 @@ SS_SYSEX_SET_PLUGIN_PARAMETER_OK, // synth->gui: set plugin parameter (update gui) SS_SYSEX_ERRORMSG, // synth -> gui: general error message from synth SS_SYSEX_GET_INIT_DATA, // gui->synth: request init data - SS_SYSEX_SEND_INIT_DATA // synth->gui: give gui init data + SS_SYSEX_SEND_INIT_DATA, // synth->gui: give gui init data + SS_SYSEX_PITCH_SAMPLE // gui->synth: set pitch and reload sample }; -extern int SS_samplerate; -extern float SS_map_pluginparam2logdomain(int pluginparam_val); -extern int SS_map_logdomain2pluginparam(float pluginparam_log); +extern int SS_segmentSize; +extern int SS_minMeterVal; +extern QString SS_globalLibPath; +extern QString SS_projectPath; +extern bool SS_useDenormalBias; +extern float SS_denormalBias; + #endif diff -Nru muse-2.1.2/synti/simpledrums2/simpledrums.cpp muse-3.0.2+ds1/synti/simpledrums2/simpledrums.cpp --- muse-2.1.2/synti/simpledrums2/simpledrums.cpp 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/simpledrums.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -23,253 +23,282 @@ // // -#include "muse/midictrl.h" -#include "muse/midi.h" -//#include "libsynti/mpevent.h" +#include "muse/midictrl_consts.h" +#include "muse/midi_consts.h" #include "muse/mpevent.h" //#include "common_defs.h" #include "simpledrums.h" -#include "globals.h" + +#include +#include #include +#include #include const char* SimpleSynth::synth_state_descr[] = - { - "SS_INITIALIZING", - "SS_LOADING_SAMPLE", - "SS_CLEARING_SAMPLE", - "SS_RUNNING" - }; +{ + "SS_INITIALIZING", + "SS_LOADING_SAMPLE", + "SS_CLEARING_SAMPLE", + "SS_RUNNING" +}; const char* SimpleSynth::channel_state_descr[] = - { - "SS_CHANNEL_INACTIVE", - "SS_SAMPLE_PLAYING" - }; +{ + "SS_CHANNEL_INACTIVE", + "SS_SAMPLE_PLAYING" +}; #define SWITCH_SYNTH_STATE(state)\ -synth_state = state; \ -if (SS_DEBUG_STATE) \ - fprintf (stderr, "SS STATE: %s\n", SimpleSynth::synth_state_descr[state]); + synth_state = state; \ + if (SS_DEBUG_STATE) \ + fprintf (stderr, "SS STATE: %s\n", SimpleSynth::synth_state_descr[state]); #define SWITCH_CHAN_STATE(ch, s)\ -channels[ch].state = s; \ -if (SS_DEBUG_STATE) \ - fprintf (stderr, "SS CHAN %d STATE: %s\n", ch, SimpleSynth::channel_state_descr[s]); + channels[ch].state = s; \ + if (SS_DEBUG_STATE) \ + fprintf (stderr, "SS CHAN %d STATE: %s\n", ch, SimpleSynth::channel_state_descr[s]); #define SS_CHANNEL_VOLUME_QUOT 100.0 #define SS_MASTER_VOLUME_QUOT 100.0 -int SS_samplerate; +int SS_segmentSize; +int SS_minMeterVal; +bool SS_useDenormalBias; +float SS_denormalBias; +QString SS_globalLibPath; +QString SS_projectPath; -#define SS_LOG_MAX 0 -#define SS_LOG_MIN -10 -#define SS_LOG_OFFSET SS_LOG_MIN +double rangeToPitch(int value) +{ + // inrange 0 .. 127 + // outrange 0.5 .. 2 + double outValue; + if (value == 64) + outValue = 1.0; + else if (value > 64) { + outValue = double(value) / 64.0; + } + else { // value < 63 + outValue = double(value) / 127.0 + 0.5; + } + + //printf("rangeToPitch(%d) %f\n", value, outValue); + return outValue; +} + +/*int pitchToRange(double pitch) +{ + // inrange 0.5 .. 2 + // outrange 0 .. 127 + int outValue; + if (fabs(pitch -1.0) < 0.0001) + outValue = 64; + else if ( pitch < 1.0){ + outValue = (pitch -0.5) * 127; + } + else if ( pitch > 1.0) { + outValue = (pitch -1.0) * 127 + 32; + } + if (outValue < 0) + outValue = 0; + if (outValue > 127) + outValue = 127; + printf("pitchToRange(%f) %d\n", pitch, outValue); + return outValue; +}*/ -// -// Map plugin parameter on domain [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] to domain [SS_LOG_MIN, SS_LOG_MAX] (log domain) -// -float SS_map_pluginparam2logdomain(int pluginparam_val) - { - float scale = (float) (SS_LOG_MAX - SS_LOG_MIN)/ (float) SS_PLUGIN_PARAM_MAX; - float scaled = (float) pluginparam_val * scale; - float mapped = scaled + SS_LOG_OFFSET; - return mapped; - } -// -// Map plugin parameter on domain to domain [SS_LOG_MIN, SS_LOG_MAX] to [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] (from log-> [0,127]) -// (inverse func to the above) -int SS_map_logdomain2pluginparam(float pluginparam_log) - { - float mapped = pluginparam_log - SS_LOG_OFFSET; - float scale = (float) SS_PLUGIN_PARAM_MAX / (float) (SS_LOG_MAX - SS_LOG_MIN); - int scaled = (int) round(mapped * scale); - return scaled; - } //--------------------------------------------------------- // SimpleSynth //--------------------------------------------------------- SimpleSynth::SimpleSynth(int sr) - : Mess(SS_AUDIO_CHANNELS) - { - SS_TRACE_IN - SS_samplerate = sr; - SS_initPlugins(); - - initBuffer = 0; - initLen = 0; - - simplesynth_ptr = this; - master_vol = 100.0 / SS_MASTER_VOLUME_QUOT; - master_vol_ctrlval = 100; - - //initialize - for (int i=0; idata; - delete channels[i].sample; - } - } - simplesynth_ptr = NULL; - - SS_DBG("Deleting pluginlist"); - //Cleanup plugins: - for (iPlugin i = plugins.begin(); i != plugins.end(); ++i) { - delete (*i); - } - plugins.clear(); - - SS_DBG("Deleting sendfx buffers"); - //Delete sendfx buffers: - for (int i=0; idata; + delete channels[i].sample; + } + } + + SS_DBG("Deleting plugin instances"); + for (int i=0; iisVisible(); - SS_TRACE_OUT - return v; - } +{ + SS_TRACE_IN + bool v = gui->isVisible(); + SS_TRACE_OUT + return v; +} //--------------------------------------------------------- // hasNativeGui @@ -295,11 +324,11 @@ */ //--------------------------------------------------------- bool SimpleSynth::hasNativeGui() const - { - SS_TRACE_IN - SS_TRACE_OUT - return true; - } +{ + SS_TRACE_IN + SS_TRACE_OUT + return true; +} //--------------------------------------------------------- // playNote @@ -313,40 +342,40 @@ */ //--------------------------------------------------------- bool SimpleSynth::playNote(int /*channel*/, int pitch, int velo) - { - SS_TRACE_IN - //Don't bother about channel, we're processing every playnote! - if ((pitch >= SS_LOWEST_NOTE) && (pitch <= SS_HIGHEST_NOTE)) { - bool noteOff = (velo == 0 ? 1 : 0); - int ch = pitch - SS_LOWEST_NOTE; - if(!noteOff) { - if (channels[ch].sample) { - //Turn on the white stuff: - channels[ch].playoffset = 0; - SWITCH_CHAN_STATE(ch , SS_SAMPLE_PLAYING); - channels[ch].cur_velo = (double) velo / 127.0; - channels[ch].gain_factor = channels[ch].cur_velo * channels[ch].volume; - if (SS_DEBUG_MIDI) { - printf("Playing note %d on channel %d\n", pitch, ch); - } - } - } - else { - //Note off: - ///if (channels[ch].noteoff_ignore) { - if (!channels[ch].noteoff_ignore) { // p4.0.27 - if (SS_DEBUG_MIDI) { - printf("Note off on channel %d\n", ch); - } - SWITCH_CHAN_STATE(ch , SS_CHANNEL_INACTIVE); - channels[ch].playoffset = 0; - channels[ch].cur_velo = 0; - } - } +{ + SS_TRACE_IN + //Don't bother about channel, we're processing every playnote! + if ((pitch >= SS_LOWEST_NOTE) && (pitch <= SS_HIGHEST_NOTE)) { + bool noteOff = (velo == 0 ? 1 : 0); + int ch = pitch - SS_LOWEST_NOTE; + if(!noteOff) { + if (channels[ch].sample) { + //Turn on the white stuff: + channels[ch].playoffset = 0; + SWITCH_CHAN_STATE(ch , SS_SAMPLE_PLAYING); + channels[ch].cur_velo = (double) velo / 127.0; + channels[ch].gain_factor = channels[ch].cur_velo * channels[ch].volume; + if (SS_DEBUG_MIDI) { + printf("Playing note %d on channel %d\n", pitch, ch); } - SS_TRACE_OUT - return false; + } + } + else { + //Note off: + ///if (channels[ch].noteoff_ignore) { + if (!channels[ch].noteoff_ignore) { // p4.0.27 + if (SS_DEBUG_MIDI) { + printf("Note off on channel %d\n", ch); + } + SWITCH_CHAN_STATE(ch , SS_CHANNEL_INACTIVE); + channels[ch].playoffset = 0; + channels[ch].cur_velo = 0; + } } + } + SS_TRACE_OUT + return false; +} //--------------------------------------------------------- // processEvent @@ -358,35 +387,33 @@ */ //--------------------------------------------------------- bool SimpleSynth::processEvent(const MusECore::MidiPlayEvent& ev) - { - SS_TRACE_IN - switch(ev.type()) { - case MusECore::ME_CONTROLLER: - if (SS_DEBUG_MIDI) { - printf("SimpleSynth::processEvent - Controller. Chan: %x dataA: %x dataB: %x\n", ev.channel(), ev.dataA(), ev.dataB()); - for (int i=0; i< ev.len(); i++) - printf("%x ", ev.data()[i]); - } - setController(ev.channel(), ev.dataA(), ev.dataB(), false); - //return true; // ?? - return false; - case MusECore::ME_NOTEON: - return playNote(ev.channel(), ev.dataA(), ev.dataB()); - case MusECore::ME_NOTEOFF: - return playNote(ev.channel(), ev.dataA(), 0); - case MusECore::ME_SYSEX: - //Debug print - if (SS_DEBUG_MIDI) { - printf("SimpleSynth::processEvent - Sysex received\n"); - for (int i=0; i< ev.len(); i++) - printf("%x ", ev.data()[i]); - printf("\n"); - } - return sysex(ev.len(), ev.data()); - } - return false; - SS_TRACE_OUT - } +{ + SS_TRACE_IN + switch(ev.type()) { + case MusECore::ME_CONTROLLER: + if (SS_DEBUG_MIDI) { + printf("SimpleSynth::processEvent - Controller. Chan: %x dataA: %x dataB: %x\n", ev.channel(), ev.dataA(), ev.dataB()); + } + setController(ev.channel(), ev.dataA(), ev.dataB(), false); + //return true; // ?? + return false; + case MusECore::ME_NOTEON: + return playNote(ev.channel(), ev.dataA(), ev.dataB()); + case MusECore::ME_NOTEOFF: + return playNote(ev.channel(), ev.dataA(), 0); + case MusECore::ME_SYSEX: + //Debug print + if (SS_DEBUG_MIDI) { + printf("SimpleSynth::processEvent - Sysex received\n"); + for (int i=0; i< ev.len(); i++) + printf("%x ", ev.data()[i]); + printf("\n"); + } + return sysex(ev.len(), ev.data()); + } + return false; + SS_TRACE_OUT +} //--------------------------------------------------------- // setController @@ -401,115 +428,141 @@ */ //--------------------------------------------------------- bool SimpleSynth::setController(int channel, int id, int val) - { - SS_TRACE_IN - if (SS_DEBUG_MIDI) { - printf("SimpleSynth::setController - received controller on channel %d, id %d value %d\n", channel, id, val); - } - - // Channel controllers: - if (id >= SS_FIRST_CHANNEL_CONTROLLER && id <= SS_LAST_CHANNEL_CONTROLLER ) { - // Find out which channel we're dealing with: - id-= SS_FIRST_CHANNEL_CONTROLLER; - int ch = (id / SS_NR_OF_CHANNEL_CONTROLLERS); - id = (id % SS_NR_OF_CHANNEL_CONTROLLERS); - - switch (id) { - case SS_CHANNEL_CTRL_VOLUME: - if (SS_DEBUG_MIDI) - printf("Received channel ctrl volume %d for channel %d\n", val, ch); - channels[ch].volume_ctrlval = val; - updateVolume(ch, val); - break; - case SS_CHANNEL_CTRL_NOFF: - if (SS_DEBUG_MIDI) - printf("Received ctrl noff %d for channel %d\n", val, ch); - channels[ch].noteoff_ignore = val; - break; - case SS_CHANNEL_CTRL_PAN: - { - if (SS_DEBUG_MIDI) - printf("Received ctrl pan %d for channel %d\n", val, ch); - channels[ch].pan = val; - updateBalance(ch, val); - break; - } - case SS_CHANNEL_CTRL_ONOFF: - { - if (SS_DEBUG_MIDI) - printf("Received ctrl onoff %d for channel %d\n", val, ch); - - if (val == false && channels[ch].channel_on == true) { - SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); - channels[ch].channel_on = val; - } - else if (val == true && channels[ch].channel_on == false) { // if it actually _was_ off: - SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); - channels[ch].playoffset = 0; - channels[ch].channel_on = val; - } - break; - } - case SS_CHANNEL_SENDFX1: - case SS_CHANNEL_SENDFX2: - case SS_CHANNEL_SENDFX3: - case SS_CHANNEL_SENDFX4: - { - int fxid = id - SS_CHANNEL_SENDFX1; - channels[ch].sendfxlevel[fxid] = (double)val/127.0; - break; - } - - default: - if (SS_DEBUG_MIDI) - printf("Unknown controller received for channel %d. id=%d\n", ch, id); - break; - } - } - // Master controllers: - else if (id >= SS_FIRST_MASTER_CONTROLLER && id <= SS_LAST_MASTER_CONTROLLER) { - if (SS_DEBUG_MIDI) - printf("Mastervol controller received: %d\n", id); - master_vol_ctrlval = val; - master_vol = (double) master_vol_ctrlval / SS_MASTER_VOLUME_QUOT; - } - // Emmm, this one should've been there in the beginning - else if (id == MusECore::CTRL_VOLUME) { - if (SS_DEBUG_MIDI) { - printf("Ctrl volume received: vol: %d\n", val); - } - master_vol_ctrlval = val; - master_vol = (double) master_vol_ctrlval / SS_MASTER_VOLUME_QUOT; - //This one can't be from the gui, update gui: - guiUpdateMasterVol(val); - } - // Plugin controllers: - else if (id >= SS_FIRST_PLUGIN_CONTROLLER && id <= SS_LAST_PLUGIN_CONTROLLER) { +{ + SS_TRACE_IN + if (SS_DEBUG_MIDI) { + printf("SimpleSynth::setController - received controller on channel %d, id %d value %d\n", channel, id, val); + } + + // Channel controllers: + if (id >= SS_FIRST_CHANNEL_CONTROLLER && id <= SS_LAST_CHANNEL_CONTROLLER ) { + // Find out which channel we're dealing with: + id-= SS_FIRST_CHANNEL_CONTROLLER; + int ch = (id / SS_NR_OF_CHANNEL_CONTROLLERS); + id = (id % SS_NR_OF_CHANNEL_CONTROLLERS); + + switch (id) { + case SS_MASTER_CTRL_VOLUME: + printf("got mastervolume\n"); + break; + case SS_CHANNEL_CTRL_VOLUME: + if (SS_DEBUG_MIDI) + printf("Received channel ctrl volume %d for channel %d\n", val, ch); + channels[ch].volume_ctrlval = val; + updateVolume(ch, val); + break; + case SS_CHANNEL_CTRL_PITCH: + if (SS_DEBUG_MIDI) + printf("Received channel ctrl pitch %d for channel %d\n", val, ch); + channels[ch].pitchInt = val; + printf("SS_CHANNEL_CTRL_PITCH %d\n", channels[channel].pitchInt); + + if (channels[ch].sample != 0) + { + std::string bkStr = channels[ch].sample->filename; + resample(channels[ch].originalSample, + channels[ch].sample, + rangeToPitch(channels[ch].pitchInt), + sampleRate()); + } + break; - int fxid = (id - SS_FIRST_PLUGIN_CONTROLLER) / SS_NR_OF_PLUGIN_CONTROLLERS; - int cmd = (id - SS_FIRST_PLUGIN_CONTROLLER) % SS_NR_OF_PLUGIN_CONTROLLERS; + case SS_CHANNEL_CTRL_NOFF: + if (SS_DEBUG_MIDI) + printf("Received ctrl noff %d for channel %d\n", val, ch); + channels[ch].noteoff_ignore = val; + break; + case SS_CHANNEL_CTRL_PAN: + { + if (SS_DEBUG_MIDI) + printf("Received ctrl pan %d for channel %d\n", val, ch); + channels[ch].pan = val; + updateBalance(ch, val); + break; + } + case SS_CHANNEL_CTRL_ONOFF: + { + if (SS_DEBUG_MIDI) + printf("Received ctrl onoff %d for channel %d\n", val, ch); - // Plugin return-gain: - if (cmd == SS_PLUGIN_RETURN) { - if (SS_DEBUG_MIDI) - printf("Ctrl fx retgain received: fxid: %d val: %d\n", fxid, val); - sendEffects[fxid].retgain_ctrlval = val; - sendEffects[fxid].retgain = (double) val / 75.0; - } - // Plugin on/off: - else if (cmd == SS_PLUGIN_ONOFF) { - if (SS_DEBUG_MIDI) - printf("Ctrl fx onoff received: fxid: %d val: %d\n", fxid, val); - sendEffects[fxid].state = (SS_SendFXState) val; - } - } - else { - if (SS_DEBUG_MIDI) - printf("Unknown controller received: %d\n", id); - } - SS_TRACE_OUT - return false; + if (val == false && channels[ch].channel_on == true) { + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + channels[ch].channel_on = val; + } + else if (val == true && channels[ch].channel_on == false) { // if it actually _was_ off: + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + channels[ch].playoffset = 0; + channels[ch].channel_on = val; + } + break; + } + case SS_CHANNEL_SENDFX1: + case SS_CHANNEL_SENDFX2: + case SS_CHANNEL_SENDFX3: + case SS_CHANNEL_SENDFX4: + { + int fxid = id - SS_CHANNEL_SENDFX1; + channels[ch].sendfxlevel[fxid] = (double)val/127.0; + break; + } + case SS_CHANNEL_CTRL_ROUTE: + if (SS_DEBUG_MIDI) + printf("Received channel ctrl pitch %d for channel %d\n", val, ch); + channels[ch].route = (SS_ChannelRoute)val; + printf("SS_CHANNEL_CTRL_ROUTE %d\n", channels[ch].route); + + break; + + default: + if (SS_DEBUG_MIDI) + printf("Unknown controller received for channel %d. id=%d\n", ch, id); + break; + } + } + // Master controllers: + else if (id >= SS_FIRST_MASTER_CONTROLLER && id <= SS_LAST_MASTER_CONTROLLER) { + if (SS_DEBUG_MIDI) + printf("Mastervol controller received: %d\n", id); + master_vol_ctrlval = val; + master_vol = (double) master_vol_ctrlval / SS_MASTER_VOLUME_QUOT; + } + // Emmm, this one should've been there in the beginning + else if (id == MusECore::CTRL_VOLUME) { + if (SS_DEBUG_MIDI) { + printf("Ctrl volume received: vol: %d\n", val); + } + master_vol_ctrlval = val; + master_vol = (double) master_vol_ctrlval / SS_MASTER_VOLUME_QUOT; + //This one can't be from the gui, update gui: + guiUpdateMasterVol(val); + } + // Plugin controllers: + else if (id >= SS_FIRST_PLUGIN_CONTROLLER && id <= SS_LAST_PLUGIN_CONTROLLER) { + + int fxid = (id - SS_FIRST_PLUGIN_CONTROLLER) / SS_NR_OF_PLUGIN_CONTROLLERS; + int cmd = (id - SS_FIRST_PLUGIN_CONTROLLER) % SS_NR_OF_PLUGIN_CONTROLLERS; + + // Plugin return-gain: + if (cmd == SS_PLUGIN_RETURN) { + if (SS_DEBUG_MIDI) + printf("Ctrl fx retgain received: fxid: %d val: %d\n", fxid, val); + sendEffects[fxid].retgain_ctrlval = val; + sendEffects[fxid].retgain = (double) val / 75.0; + } + // Plugin on/off: + else if (cmd == SS_PLUGIN_ONOFF) { + if (SS_DEBUG_MIDI) + printf("Ctrl fx onoff received: fxid: %d val: %d\n", fxid, val); + sendEffects[fxid].state = (SS_SendFXState) val; } + } + else { + if (SS_DEBUG_MIDI) + printf("Unknown controller received: %d\n", id); + } + SS_TRACE_OUT + return false; +} //--------------------------------------------------------- /*! @@ -517,12 +570,12 @@ */ //--------------------------------------------------------- bool SimpleSynth::setController(int channel, int id, int val, bool /*fromGui*/) - { - SS_TRACE_IN - bool ret = setController(channel, id, val); //Perhaps TODO... Separate events from the gui - SS_TRACE_OUT - return ret; - } +{ + SS_TRACE_IN + bool ret = setController(channel, id, val); //Perhaps TODO... Separate events from the gui + SS_TRACE_OUT + return ret; +} //--------------------------------------------------------- // sysex /*! @@ -534,111 +587,127 @@ */ //--------------------------------------------------------- bool SimpleSynth::sysex(int len, const unsigned char* d) - { - if(len < 3 || d[0] != MUSE_SYNTH_SYSEX_MFG_ID - || d[1] != SIMPLEDRUMS_UNIQUE_ID) - { - if (SS_DEBUG_MIDI) - printf("MusE SimpleDrums: Unknown sysex header\n"); - return false; - } - - const unsigned char* data = d + 2; - SS_TRACE_IN - int cmd = data[0]; - switch (cmd) { - case SS_SYSEX_LOAD_SAMPLE: - { - int channel = data[1]; - //int l = data[2]; - const char* filename = (const char*)(data+3); - if (SS_DEBUG_MIDI) { - printf("Sysex cmd: load sample, filename %s, on channel: %d\n", filename, channel); - } - loadSample(channel, filename); - break; - } - case SS_SYSEX_CLEAR_SAMPLE: - { - int ch = data[1]; - clearSample(ch); - break; - } - - case SS_SYSEX_INIT_DATA: - { - parseInitData(data); - break; - } - - case SS_SYSEX_LOAD_SENDEFFECT: - { - int fxid = data[1]; - QString lib = (const char*) (data + 2); - QString label = (const char*) (data + lib.length() + 3); - if (SS_DEBUG_MIDI) { - printf("Sysex cmd load effect: %d %s %s\n", fxid, lib.toLatin1().constData(), label.toLatin1().constData()); - } - initSendEffect(fxid, lib, label); - break; - } - - case SS_SYSEX_CLEAR_SENDEFFECT: - { - int fxid = data[1]; - if (SS_DEBUG_MIDI) { - printf("Sysex cmd clear effect: %d\n", fxid); - } - sendEffects[fxid].state = SS_SENDFX_OFF; - cleanupPlugin(fxid); - sendEffects[fxid].plugin = 0; - break; - } +{ + if(len < 3 || d[0] != MUSE_SYNTH_SYSEX_MFG_ID + || d[1] != SIMPLEDRUMS_UNIQUE_ID) + { + //if (SS_DEBUG_MIDI) + printf("MusE SimpleDrums: Unknown sysex header\n"); + return false; + } - case SS_SYSEX_SET_PLUGIN_PARAMETER: - { - int fxid = data[1]; - int parameter = data[2]; - int val = data[3]; - // Write it to the plugin: - float floatval = sendEffects[fxid].plugin->convertGuiControlValue(parameter, val); - setFxParameter(fxid, parameter, floatval); - break; - } + const unsigned char* data = d + 2; + SS_TRACE_IN + int cmd = data[0]; + printf("Got sysex %d %d\n", len, cmd ); + switch (cmd) { + + case SS_SYSEX_LOAD_SAMPLE: + { + int channel = data[1]; + //int l = data[2]; + const char* filename = (const char*)(data+3); + if (SS_DEBUG_MIDI) { + printf("Sysex cmd: load sample, filename %s, on channel: %d\n", filename, channel); + } + loadSample(channel, filename); + break; + } + case SS_SYSEX_PITCH_SAMPLE: + { + int channel = data[1]; + channels[channel].pitchInt = data[2]; + + printf("SS_SYSEX_PITCH_SAMPLE %d\n", channels[channel].pitchInt); + + //if (strlen(channels[channel].name) > 0) + // loadSample(channel, channels[channel].name); + break; + } + case SS_SYSEX_CLEAR_SAMPLE: + { + int ch = data[1]; + clearSample(ch); + break; + } + + case SS_SYSEX_INIT_DATA: + { + parseInitData(data); + break; + } + + case SS_SYSEX_LOAD_SENDEFFECT: + { + int fxid = data[1]; + QString lib = (const char*) (data + 2); + QString label = (const char*) (data + lib.length() + 3); + if (SS_DEBUG_MIDI) { + printf("Sysex cmd load effect: %d %s %s\n", fxid, lib.toLatin1().constData(), label.toLatin1().constData()); + } + initSendEffect(fxid, lib, label); + break; + } + + case SS_SYSEX_CLEAR_SENDEFFECT: + { + int fxid = data[1]; + if (SS_DEBUG_MIDI) { + printf("Sysex cmd clear effect: %d\n", fxid); + } + sendEffects[fxid].state = SS_SENDFX_OFF; + cleanupPlugin(fxid); + sendEffects[fxid].plugin = 0; + break; + } + + case SS_SYSEX_SET_PLUGIN_PARAMETER: + { + int fxid = data[1]; + int parameter = data[2]; + int val = data[3]; + // Write it to the plugin: + if(sendEffects[fxid].plugin) + { + float floatval = sendEffects[fxid].plugin->convertGuiControlValue(parameter, val); + setFxParameter(fxid, parameter, floatval); + } + break; + } + + case SS_SYSEX_GET_INIT_DATA: + { + int initdata_len = 0; + const byte* tmp_initdata = NULL; + ///byte* event_data = NULL; + + getInitData(&initdata_len, &tmp_initdata); + ///int totlen = initdata_len + 1; + + ///event_data = new byte[initdata_len + 1]; + ///event_data[0] = SS_SYSEX_SEND_INIT_DATA; + *((byte*)(tmp_initdata) + 1) = SS_SYSEX_SEND_INIT_DATA; // Re-use the synth ID byte as the command byte. + + ///memcpy(event_data + 1, tmp_initdata, initdata_len); + ///delete[] tmp_initdata; + ///tmp_initdata = NULL; - case SS_SYSEX_GET_INIT_DATA: - { - int initdata_len = 0; - const byte* tmp_initdata = NULL; - ///byte* event_data = NULL; - - getInitData(&initdata_len, &tmp_initdata); - ///int totlen = initdata_len + 1; - - ///event_data = new byte[initdata_len + 1]; - ///event_data[0] = SS_SYSEX_SEND_INIT_DATA; - *((byte*)(tmp_initdata) + 1) = SS_SYSEX_SEND_INIT_DATA; // Re-use the synth ID byte as the command byte. - - ///memcpy(event_data + 1, tmp_initdata, initdata_len); - ///delete[] tmp_initdata; - ///tmp_initdata = NULL; - - ///MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, event_data, totlen); - MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, tmp_initdata + 1, initdata_len - 1); // Strip MFG ID. - gui->writeEvent(ev); - ///delete[] event_data; + ///MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, event_data, totlen); + MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, tmp_initdata + 1, initdata_len - 1); // Strip MFG ID. + gui->writeEvent(ev); + ///delete[] event_data; - break; - } + break; + } - default: - if (SS_DEBUG_MIDI) - printf("Unknown sysex cmd received: %d\n", cmd); - break; - } - SS_TRACE_OUT - return false; - } + default: + if (SS_DEBUG_MIDI) + printf("Unknown sysex cmd received: %d\n", cmd); + break; + } + SS_TRACE_OUT + return false; +} //--------------------------------------------------------- // getPatchName @@ -649,12 +718,12 @@ \return QString with patchname */ //--------------------------------------------------------- -QString SimpleSynth::getPatchName(int /*index*/, int, bool) const - { - SS_TRACE_IN - SS_TRACE_OUT - return 0; - } +const char* SimpleSynth::getPatchName(int /*index*/, int, bool) const +{ + SS_TRACE_IN + SS_TRACE_OUT + return 0; +} //--------------------------------------------------------- // getPatchInfo @@ -667,12 +736,12 @@ */ //--------------------------------------------------------- const MidiPatch* SimpleSynth::getPatchInfo(int /*index*/, const MidiPatch* /*patch*/) const - { - SS_TRACE_IN - //index = 0; patch = 0; - SS_TRACE_OUT - return 0; - } +{ + SS_TRACE_IN + //index = 0; patch = 0; + SS_TRACE_OUT + return 0; +} //--------------------------------------------------------- // getControllerInfo @@ -688,26 +757,27 @@ */ //--------------------------------------------------------- int SimpleSynth::getControllerInfo(int index, const char** name, int* controller, int* min, int* max, int* initval) const - { - SS_TRACE_IN - if (index >= SS_NR_OF_CONTROLLERS) { - SS_TRACE_OUT +{ + SS_TRACE_IN + if (index >= SS_NR_OF_CONTROLLERS) { + SS_TRACE_OUT return 0; - } + } - *name = controllers[index].name.c_str(); - *controller = controllers[index].num; - *min = controllers[index].min; - *max = controllers[index].max; - - *initval = 0; // p4.0.27 FIXME NOTE TODO - - if (SS_DEBUG_MIDI) { - printf("setting controller info: index %d name %s controller %d min %d max %d\n", index, *name, *controller, *min, *max); - } - SS_TRACE_OUT - return (index +1); - } + *name = controllers[index].name.c_str(); + *controller = controllers[index].num; + *min = controllers[index].min; + *max = controllers[index].max; + + *initval = 0; // p4.0.27 FIXME NOTE TODO + + if (SS_DEBUG_MIDI) { + printf("setting controller info: index %d name %s controller %d min %d max %d\n", + index, *name, *controller, *min, *max); + } + SS_TRACE_OUT + return (index +1); +} //--------------------------------------------------------- // processMessages @@ -716,22 +786,22 @@ void SimpleSynth::processMessages() { - //Process messages from the gui - while (gui->fifoSize()) { - MusECore::MidiPlayEvent ev = gui->readEvent(); - if (ev.type() == MusECore::ME_SYSEX) { - sysex(ev.len(), ev.data()); - sendEvent(ev); - } - else if (ev.type() == MusECore::ME_CONTROLLER) { - setController(ev.channel(), ev.dataA(), ev.dataB(), true); - sendEvent(ev); - } - else { - if (SS_DEBUG) - printf("SimpleSynth::process(): unknown event, type: %d\n", ev.type()); - } - } + //Process messages from the gui + while (gui->fifoSize()) { + MusECore::MidiPlayEvent ev = gui->readEvent(); + if (ev.type() == MusECore::ME_SYSEX) { + sysex(ev.len(), ev.data()); + sendEvent(ev); + } + else if (ev.type() == MusECore::ME_CONTROLLER) { + setController(ev.channel(), ev.dataA(), ev.dataB(), true); + sendEvent(ev); + } + else { + if (SS_DEBUG) + printf("SimpleSynth::process(): unknown event, type: %d\n", ev.type()); + } + } } //--------------------------------------------------------- @@ -744,9 +814,9 @@ \param len - nr of samples to process */ //--------------------------------------------------------- -void SimpleSynth::process(float** out, int offset, int len) - { - /* +void SimpleSynth::process(unsigned /*pos*/, float** out, int offset, int len) +{ + /* //Process messages from the gui while (gui->fifoSize()) { MusECore::MidiPlayEvent ev = gui->readEvent(); @@ -764,8 +834,8 @@ } } */ - - if (synth_state == SS_RUNNING) { + + if (synth_state == SS_RUNNING) { //Temporary mix-doubles double out1, out2; @@ -777,11 +847,11 @@ // Clear send-channels. Skips if fx not turned on for (int i=0; idata; - gain_factor = channels[ch].gain_factor; - // Current velocity factor: - - if (channels[ch].sample->channels == 2) { - // - // Stereo sample: - // - // Add from sample: - out1 = (double) (data[channels[ch].playoffset] * gain_factor * channels[ch].balanceFactorL); - out2 = (double) (data[channels[ch].playoffset + 1] * gain_factor * channels[ch].balanceFactorR); - channels[ch].playoffset += 2; - } - else { - // - // Mono sample: - // - out1 = (double) (data[channels[ch].playoffset] * gain_factor * channels[ch].balanceFactorL); - out2 = (double) (data[channels[ch].playoffset] * gain_factor * channels[ch].balanceFactorR); - channels[ch].playoffset++; - } - - processBuffer[0][i] = out1; - processBuffer[1][i] = out2; - - // If send-effects tap is on, tap signal to respective lineout channel - for (int j=0; j2 inputs are ignored - } - } - - // - // If we've reached the last sample, set state to inactive - // - if (channels[ch].playoffset >= channels[ch].sample->samples) { - SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); - channels[ch].playoffset = 0; - break; - } - } - // Add contribution for this channel, for this frame, to final result: - for (int i=0; imeterVal [ch] = 0.0; + } + + // If channels is turned off, skip: + if (channels[ch].channel_on == false) + continue; + + //If sample isn't playing, skip: + if (channels[ch].state == SS_SAMPLE_PLAYING) { + memset(processBuffer[0], 0, SS_PROCESS_BUFFER_SIZE * sizeof(double)); + memset(processBuffer[1], 0, SS_PROCESS_BUFFER_SIZE * sizeof(double)); + + for (int i=0; idata; + gain_factor = channels[ch].gain_factor; + // Current velocity factor: + + if (channels[ch].sample->channels == 2) { + // + // Stereo sample: + // + // Add from sample: + out1 = (double) (data[channels[ch].playoffset] * gain_factor * channels[ch].balanceFactorL); + out2 = (double) (data[channels[ch].playoffset + 1] * gain_factor * channels[ch].balanceFactorR); + channels[ch].playoffset += 2; + } + else { + // + // Mono sample: + // + out1 = (double) (data[channels[ch].playoffset] * gain_factor * channels[ch].balanceFactorL); + out2 = (double) (data[channels[ch].playoffset] * gain_factor * channels[ch].balanceFactorR); + channels[ch].playoffset++; + } + + processBuffer[0][i] = out1; + processBuffer[1][i] = out2; + + // If send-effects tap is on, tap signal to respective lineout channel + for (int j=0; j2 inputs are ignored } + } + + // + // If we've reached the last sample, set state to inactive + // + if (channels[ch].playoffset >= channels[ch].sample->samples) { + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + channels[ch].playoffset = 0; + break; + } } - // Do something funny with the sendies: - for (int j=0; jprocess(len); - for (int i=0; i gui->meterVal[ch]){ + gui->meterVal[ch] = f; } + } + } + if(gui){ + if(gui->meterVal[ch] > gui->peakVal[ch]){ + gui->peakVal[ch] = gui->meterVal[ch]; + } + } + } + } + // Do something funny with the sendies: + for (int j=0; jprocess(len); + for (int i=0; isetVisible(val); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + gui->setVisible(val); + SS_TRACE_OUT +} + +void SimpleSynth::guiHeartBeat() +{ + if(gui) + gui->heartBeat(); +}; //--------------------------------------------------------- /*! @@ -908,28 +1011,31 @@ */ //--------------------------------------------------------- bool SimpleSynth::init(const char* name) - { - SS_TRACE_IN - SWITCH_SYNTH_STATE(SS_INITIALIZING); - gui = new SimpleSynthGui(); - gui->show(); - gui->setWindowTitle(name); - SWITCH_SYNTH_STATE(SS_RUNNING); - SS_TRACE_OUT - return true; - } +{ + SS_TRACE_IN + SWITCH_SYNTH_STATE(SS_INITIALIZING); + gui = new SimpleSynthGui(sampleRate()); + //gui->show(); + gui->setWindowTitle(name); + for(int i = 0; i < SS_NR_OF_CHANNELS; i++){ + guiUpdateNoff(i, channels[i].noteoff_ignore); //update nOff gui checkbox (on by default now) + } + SWITCH_SYNTH_STATE(SS_RUNNING); + SS_TRACE_OUT + return true; +} //--------------------------------------------------------- // getInitBuffer //--------------------------------------------------------- void SimpleSynth::setupInitBuffer(int len) { - if (len > initLen) { - if (initBuffer) - delete [] initBuffer; - initBuffer = new byte[len]; - initLen = len; - } + if (len > initLen) { + if (initBuffer) + delete [] initBuffer; + initBuffer = new byte[len]; + initLen = len; + } } //--------------------------------------------------------- @@ -942,904 +1048,953 @@ //--------------------------------------------------------- //void SimpleSynth::getInitData(int* n, const unsigned char** data) const void SimpleSynth::getInitData(int* n, const unsigned char** data) - { - SS_TRACE_IN - // Calculate length of data - // For each channel, we need to store volume, pan, noff, onoff - int len = SS_NR_OF_CHANNEL_CONTROLLERS * SS_NR_OF_CHANNELS; - // Sampledata: filenames len - for (int i=0; ifilename.c_str()) + 2; - len+=filenamelen; - } - else - len++; //Add place for SS_NO_SAMPLE - } - len+=3; // 1 place for SS_SYSEX_INIT_DATA, 1 byte for master vol, 1 byte for version data - - // Effect data length - len++; //Add place for SS_SYSEX_INIT_DATA_VERSION, as control +{ + SS_TRACE_IN + // Calculate length of data + // For each channel, we need to store volume, pan, noff, onoff + int len = SS_NR_OF_CHANNEL_CONTROLLERS * SS_NR_OF_CHANNELS; + // Sampledata: filenames len + for (int i=0; ifilename.c_str()) + 2; + len+=filenamelen; + } + else + len++; //Add place for SS_NO_SAMPLE + } + len+=3; // 1 place for SS_SYSEX_INIT_DATA, 1 byte for master vol, 1 byte for version data + + // Effect data length + len++; //Add place for SS_SYSEX_INIT_DATA_VERSION, as control + + for (int i=0; ilib().size() + 2; + int labelnamelen = plugin->pluginLabel().size() + 2; + len+=(namelen + labelnamelen); + + ///len+=3; //1 byte for nr of parameters, 1 byte for return gain, 1 byte for effect on/off + // p4.0.27 Tim. + len+=6; //4 bytes for nr of parameters, 1 byte for return gain, 1 byte for effect on/off - for (int i=0; ilib().size() + 2; - int labelnamelen = plugin->label().size() + 2; - len+=(namelen + labelnamelen); - - ///len+=3; //1 byte for nr of parameters, 1 byte for return gain, 1 byte for effect on/off - // p4.0.27 Tim. - len+=6; //4 bytes for nr of parameters, 1 byte for return gain, 1 byte for effect on/off - - len+=sendEffects[i].nrofparameters; // 1 byte for each parameter value - } - else { - len++; //place for SS_NO_PLUGIN - } - } + len+=sendEffects[i].nrofparameters; // 1 byte for each parameter value + } + else { + len++; //place for SS_NO_PLUGIN + } + } - len += 2; // For header. + len += 2; // For header. - // First, SS_SYSEX_INIT_DATA - - ///byte* buffer = new byte[len]; - setupInitBuffer(len); - - memset(initBuffer, 0, len); - //initBuffer[0] = SS_SYSEX_INIT_DATA; - //initBuffer[1] = SS_SYSEX_INIT_DATA_VERSION; - initBuffer[0] = MUSE_SYNTH_SYSEX_MFG_ID; - initBuffer[1] = SIMPLEDRUMS_UNIQUE_ID; - initBuffer[2] = SS_SYSEX_INIT_DATA; - initBuffer[3] = SS_SYSEX_INIT_DATA_VERSION; - if (SS_DEBUG_INIT) { - printf("Length of init data: %d\n", len); - //printf("initBuffer[0] - SS_SYSEX_INIT_DATA: %d\n", SS_SYSEX_INIT_DATA); - //printf("initBuffer[1] - SS_SYSEX_INIT_DATA_VERSION: %d\n", SS_SYSEX_INIT_DATA_VERSION); - printf("initBuffer[2] - SS_SYSEX_INIT_DATA: %d\n", SS_SYSEX_INIT_DATA); - printf("initBuffer[3] - SS_SYSEX_INIT_DATA_VERSION: %d\n", SS_SYSEX_INIT_DATA_VERSION); - } - //int i = 2; - int i = 4; - // All channels: - // 0 - volume ctrlval (0-127) - // 1 - pan (0-127) - // 2 - noff ignore (0-1) - // 3 - channel on/off (0-1) - // 4 - 7 - sendfx 1-4 (0-127) - // 8 - len of filename, n - // 9 - 9+n - filename - for (int ch=0; chfilename.c_str()) + 1; - initBuffer[i+8] = (byte) filenamelen; - memcpy((initBuffer+(i+9)), channels[ch].sample->filename.c_str(), filenamelen); - if (SS_DEBUG_INIT) { - printf("initBuffer[%d] - filenamelen: %d\n", i+8, filenamelen); - printf("initBuffer[%d] - initBuffer[%d] - filename: ", (i+9), (i+9) + filenamelen - 1); - for (int j = i+9; j< i+9+filenamelen; j++) { - printf("%c",initBuffer[j]); - } - printf("\n"); - } - i+= (SS_NR_OF_CHANNEL_CONTROLLERS + 1 + filenamelen); - } - else { - initBuffer[i+8] = SS_NO_SAMPLE; - if (SS_DEBUG_INIT) { - printf("initBuffer[%d]: SS_NO_SAMPLE: - %d\n", i+8, SS_NO_SAMPLE); - } - i+= (SS_NR_OF_CHANNEL_CONTROLLERS + 1); - } - } if (SS_DEBUG_INIT) { - printf("initBuffer[%d]: Master vol: - %d\n", i, master_vol_ctrlval); + printf("Channel %d:\n", ch); + printf("initBuffer[%d] - channels[ch].volume_ctrlval = \t%d\n", i, channels[ch].volume_ctrlval); + printf("initBuffer[%d] - channels[ch].pan = \t\t%d\n", i+1, channels[ch].pan); + printf("initBuffer[%d] - channels[ch].noteoff_ignore = \t%d\n", i+2, channels[ch].noteoff_ignore ); + printf("initBuffer[%d] - channels[ch].channel_on = \t%d\n", i+3, channels[ch].channel_on); + for (int j= i+4; j < i+8; j++) { + printf("initBuffer[%d] - channels[ch].sendfxlevel[%d]= \t%d\n", j, j-i-4, (int)round(channels[ch].sendfxlevel[j-i-4] * 127.0)); + } + } + if (channels[ch].sample) { + int filenamelen = strlen(channels[ch].sample->filename.c_str()) + 1; + initBuffer[i+10] = (byte) filenamelen; + memcpy((initBuffer+(i+11)), channels[ch].sample->filename.c_str(), filenamelen); + if (SS_DEBUG_INIT) { + printf("initBuffer[%d] - filenamelen: %d\n", i+10, filenamelen); + printf("initBuffer[%d] - initBuffer[%d] - filename: ", (i+11), (i+11) + filenamelen - 1); + for (int j = i+11; j< i+11+filenamelen; j++) { + printf("%c",initBuffer[j]); } - initBuffer[i] = master_vol_ctrlval; - *(data) = initBuffer; *n = len; - i++; - - //Send effects: - - ///initBuffer[i] = SS_SYSEX_INIT_DATA_VERSION; //Just for check - // Jun 10 2011. Bumped version up from 1 (with its own ID). p4.0.27 Tim - initBuffer[i] = SS_SYSEX_EFFECT_INIT_DATA_VERSION; //Just for check - - if (SS_DEBUG_INIT) { - printf("initBuffer[%d]: Control value, SS_SYSEX_EFFECT_INIT_DATA_VERSION = %d\n", i, SS_SYSEX_EFFECT_INIT_DATA_VERSION); + printf("\n"); + } + i+= (SS_NR_OF_CHANNEL_CONTROLLERS + 1 + filenamelen); + } + else { + initBuffer[i+10] = SS_NO_SAMPLE; + if (SS_DEBUG_INIT) { + printf("initBuffer[%d]: SS_NO_SAMPLE: - %d\n", i+9, SS_NO_SAMPLE); + } + i+= (SS_NR_OF_CHANNEL_CONTROLLERS + 1); + } + } + if (SS_DEBUG_INIT) { + printf("initBuffer[%d]: Master vol: - %d\n", i, master_vol_ctrlval); + } + initBuffer[i] = master_vol_ctrlval; + *(data) = initBuffer; *n = len; + i++; + + //Send effects: + + ///initBuffer[i] = SS_SYSEX_INIT_DATA_VERSION; //Just for check + // Jun 10 2011. Bumped version up from 1 (with its own ID). p4.0.27 Tim + initBuffer[i] = SS_SYSEX_EFFECT_INIT_DATA_VERSION; //Just for check + + if (SS_DEBUG_INIT) { + printf("initBuffer[%d]: Control value, SS_SYSEX_EFFECT_INIT_DATA_VERSION = %d\n", i, SS_SYSEX_EFFECT_INIT_DATA_VERSION); + } + + i++; + for (int j=0; jpluginLabel().size() + 1; + initBuffer[i] = labelnamelen; + memcpy((initBuffer+i+1), sendEffects[j].plugin->pluginLabel().toLatin1().constData(), labelnamelen); + if (SS_DEBUG_INIT) { + printf("initBuffer[%d] - labelnamelen: %d\n", i, labelnamelen); + printf("initBuffer[%d] - initBuffer[%d] - filename: ", (i+1), (i+1) + labelnamelen - 1); + for (int k = i+1; k < i+1+labelnamelen; k++) { + printf("%c",initBuffer[k]); } + printf("\n"); + } - i++; - for (int j=0; jlabel().size() + 1; - initBuffer[i] = labelnamelen; - memcpy((initBuffer+i+1), sendEffects[j].plugin->label().toLatin1().constData(), labelnamelen); - if (SS_DEBUG_INIT) { - printf("initBuffer[%d] - labelnamelen: %d\n", i, labelnamelen); - printf("initBuffer[%d] - initBuffer[%d] - filename: ", (i+1), (i+1) + labelnamelen - 1); - for (int k = i+1; k < i+1+labelnamelen; k++) { - printf("%c",initBuffer[k]); - } - printf("\n"); - } - - i+=(labelnamelen + 1); - - int namelen = sendEffects[j].plugin->lib().size() + 1; - initBuffer[i] = namelen; - memcpy((initBuffer+i+1), sendEffects[j].plugin->lib().toLatin1().constData(), namelen); - if (SS_DEBUG_INIT) { - printf("initBuffer[%d] - libnamelen : %d\n", i, namelen); - printf("initBuffer[%d] - initBuffer[%d] - filename: ", (i+1), (i+1) + namelen - 1); - for (int k = i+1; k < i+1+namelen; k++) { - printf("%c",initBuffer[k]); - } - printf("\n"); - } - - i+=(namelen + 1); - - ///initBuffer[i]=sendEffects[j].nrofparameters; - // Jun 10 2011. Changed to 32 bit. p4.0.27 Tim. - *((unsigned*)&initBuffer[i]) = sendEffects[j].nrofparameters; - if (SS_DEBUG_INIT) { - printf("initBuffer[%d]: sendEffects[%d].nrofparameters=%d\n", i, j, *((unsigned*)&initBuffer[i])); - } - ///i++; - i+=4; - - initBuffer[i]=sendEffects[j].retgain_ctrlval; - if (SS_DEBUG_INIT) { - printf("initBuffer[%d]: sendEffects[%d].retgain_ctrlval=%d\n", i, j, initBuffer[i]); - } - i++; - - // Jun 10 2011. This one was missing. p4.0.27 Tim. - initBuffer[i] = sendEffects[j].state; - if (SS_DEBUG_INIT) { - printf("initBuffer[%d]: sendEffects[%d].state=%d\n", i, j, initBuffer[i]); - } - i++; - - for (int k=0; kgetGuiControlValue(k); - if (SS_DEBUG_INIT) { - printf("initBuffer[%d]: sendEffects[%d].parameterval[%d]=%d\n", i, j, k, initBuffer[i]); - } - i++; - } - } - // No plugin loaded: - else { - initBuffer[i] = SS_NO_PLUGIN; - if (SS_DEBUG_INIT) { - printf("initBuffer[%d]: SS_NO_PLUGIN\n", i); - } - i++; - } + i+=(labelnamelen + 1); + + int namelen = sendEffects[j].plugin->lib().size() + 1; + initBuffer[i] = namelen; + memcpy((initBuffer+i+1), sendEffects[j].plugin->lib().toLatin1().constData(), namelen); + if (SS_DEBUG_INIT) { + printf("initBuffer[%d] - libnamelen : %d\n", i, namelen); + printf("initBuffer[%d] - initBuffer[%d] - filename: ", (i+1), (i+1) + namelen - 1); + for (int k = i+1; k < i+1+namelen; k++) { + printf("%c",initBuffer[k]); } + printf("\n"); + } - SS_TRACE_OUT + i+=(namelen + 1); + + ///initBuffer[i]=sendEffects[j].nrofparameters; + // Jun 10 2011. Changed to 32 bit. p4.0.27 Tim. + *((unsigned*)&initBuffer[i]) = sendEffects[j].nrofparameters; + if (SS_DEBUG_INIT) { + printf("initBuffer[%d]: sendEffects[%d].nrofparameters=%d\n", i, j, *((unsigned*)&initBuffer[i])); + } + ///i++; + i+=4; + + initBuffer[i]=sendEffects[j].retgain_ctrlval; + if (SS_DEBUG_INIT) { + printf("initBuffer[%d]: sendEffects[%d].retgain_ctrlval=%d\n", i, j, initBuffer[i]); + } + i++; + + // Jun 10 2011. This one was missing. p4.0.27 Tim. + initBuffer[i] = sendEffects[j].state; + if (SS_DEBUG_INIT) { + printf("initBuffer[%d]: sendEffects[%d].state=%d\n", i, j, initBuffer[i]); + } + i++; + + for (int k=0; kgetGuiControlValue(k); + if (SS_DEBUG_INIT) { + printf("initBuffer[%d]: sendEffects[%d].parameterval[%d]=%d\n", i, j, k, initBuffer[i]); + } + i++; + } } + // No plugin loaded: + else { + initBuffer[i] = SS_NO_PLUGIN; + if (SS_DEBUG_INIT) { + printf("initBuffer[%d]: SS_NO_PLUGIN\n", i); + } + i++; + } + } + + SS_TRACE_OUT +} /*! \fn SimpleSynth::parseInitData() */ void SimpleSynth::parseInitData(const unsigned char* data) - { - SS_TRACE_IN - //int len = strlen((const char*)data); +{ + SS_TRACE_IN + //int len = strlen((const char*)data); + //if (SS_DEBUG_INIT) + { + printf("buffer[1], SS_SYSEX_INIT_DATA_VERSION=%d\n", *(data+1)); + } + int dataVersion = *(data+1); + const byte* ptr = data+2; + for (int ch=0; ch cast to "long" - // *(ptr+...) has type byte --> use %hhd as format item - /* old code + printf("Channel %d:\n", ch); + //willyfoobar-2011-02-13 + // ptr-data might be long long on 64 bit machine --> cast to "long" + // *(ptr+...) has type byte --> use %hhd as format item + /* old code printf("buffer[%ld] - channels[ch].volume_ctrlval = \t%d\n", ptr-data, *ptr); printf("buffer[%ld] - channels[ch].pan = \t\t%d\n", ptr-data+1, *(ptr+1)); printf("buffer[%ld] - channels[ch].noteoff_ignore = \t%d\n", ptr-data+2, *(ptr+2)); printf("buffer[%ld] - channels[ch].channel_on = \t%d\n", ptr-data+3, *(ptr+3)); */ - printf("buffer[%ld] - channels[ch].volume_ctrlval = \t%hhd\n", long(ptr-data), *ptr); - printf("buffer[%ld] - channels[ch].pan = \t\t%hhd\n", long(ptr-data+1), *(ptr+1)); - printf("buffer[%ld] - channels[ch].noteoff_ignore = \t%hhd\n", long(ptr-data+2), *(ptr+2)); - printf("buffer[%ld] - channels[ch].channel_on = \t%hhd\n", long(ptr-data+3), *(ptr+3)); - } - updateVolume(ch, *(ptr)); - guiUpdateVolume(ch, *(ptr)); - - channels[ch].pan = *(ptr+1); - updateBalance(ch, *(ptr+1)); - guiUpdateBalance(ch, *(ptr+1)); - - channels[ch].noteoff_ignore = *(ptr+2); - guiUpdateNoff(ch, *(ptr+2)); - - channels[ch].channel_on = *(ptr+3); - guiUpdateChoff(ch, *(ptr+3)); - - ptr+=4; + printf("buffer[%ld] - channels[ch].volume_ctrlval = \t%hhd\n", long(ptr-data), *ptr); + printf("buffer[%ld] - channels[ch].pan = \t\t%hhd\n", long(ptr-data+1), *(ptr+1)); + printf("buffer[%ld] - channels[ch].noteoff_ignore = \t%hhd\n", long(ptr-data+2), *(ptr+2)); + printf("buffer[%ld] - channels[ch].channel_on = \t%hhd\n", long(ptr-data+3), *(ptr+3)); + } + updateVolume(ch, *(ptr)); + guiUpdateVolume(ch, *(ptr)); + + channels[ch].pan = *(ptr+1); + updateBalance(ch, *(ptr+1)); + guiUpdateBalance(ch, *(ptr+1)); + + channels[ch].noteoff_ignore = *(ptr+2); + guiUpdateNoff(ch, *(ptr+2)); + + channels[ch].channel_on = *(ptr+3); + guiUpdateChoff(ch, *(ptr+3)); + + ptr+=4; + + for (int i=0; i<4; i++) { + channels[ch].sendfxlevel[i] = (float) (*(ptr)/127.0); + guiUpdateSendFxLevel(ch, i, *(ptr)); + ptr++; + } - for (int i=0; i<4; i++) { - channels[ch].sendfxlevel[i] = (float) (*(ptr)/127.0); - guiUpdateSendFxLevel(ch, i, *(ptr)); - ptr++; - } + // + // + // + if (dataVersion > 1) { + updatePitch(ch, *(ptr)); + guiUpdatePitch(ch, *(ptr)); + ptr++; + } - bool hasSample = *(ptr); - ptr++; + if(dataVersion > 2) { + channels[ch].route = (SS_ChannelRoute)*(ptr); + guiUpdateRoute(ch, *(ptr)); + ptr++; + } - channels[ch].sample = 0; - channels[ch].playoffset = 0; - SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); - if (SS_DEBUG_INIT) { - printf("parseInitData: channel %d, volume: %f pan: %d bfL %f bfR %f chON %d s1: %f s2: %f s3: %f s4: %f\n", - ch, - channels[ch].volume, - channels[ch].pan, - channels[ch].balanceFactorL, - channels[ch].balanceFactorR, - channels[ch].channel_on, - channels[ch].sendfxlevel[0], - channels[ch].sendfxlevel[1], - channels[ch].sendfxlevel[2], - channels[ch].sendfxlevel[3] - ); - } - if (hasSample) { - std::string filenametmp = (const char*) ptr; - ptr+= strlen(filenametmp.c_str()) + 1; - //printf("We should load %s\n", filenametmp.c_str()); - loadSample(ch, filenametmp.c_str()); - } - else { - //Clear sample - clearSample(ch); - guiNotifySampleCleared(ch); - } - } - //Master vol: - master_vol_ctrlval = *(ptr); - master_vol = (double) master_vol_ctrlval / SS_MASTER_VOLUME_QUOT; - guiUpdateMasterVol(master_vol_ctrlval); - if (SS_DEBUG_INIT) { - printf("Master vol: %d\n", master_vol_ctrlval); - } + bool hasSample = *(ptr); ptr++; - // Effects: - ///if (*(ptr) != SS_SYSEX_INIT_DATA_VERSION) { - int effver = *(ptr); - if (effver < 1 || effver > SS_SYSEX_EFFECT_INIT_DATA_VERSION) { - //if (SS_DEBUG_INIT) - fprintf(stderr, "Error loading init data - effect init version is from future or too old. Skipping...\n"); - SS_TRACE_OUT + channels[ch].sample = 0; + channels[ch].playoffset = 0; + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + if (SS_DEBUG_INIT) { + printf("parseInitData: channel %d, volume: %f pan: %d bfL %f bfR %f chON %d s1: %f s2: %f s3: %f s4: %f\n", + ch, + channels[ch].volume, + channels[ch].pan, + channels[ch].balanceFactorL, + channels[ch].balanceFactorR, + channels[ch].channel_on, + channels[ch].sendfxlevel[0], + channels[ch].sendfxlevel[1], + channels[ch].sendfxlevel[2], + channels[ch].sendfxlevel[3] + ); + } + if (hasSample) { + std::string filenametmp = (const char*) ptr; + ptr+= strlen(filenametmp.c_str()) + 1; + //printf("We should load %s\n", filenametmp.c_str()); + loadSample(ch, filenametmp.c_str()); + } + else { + //Clear sample + clearSample(ch); + guiNotifySampleCleared(ch); + } + } + //Master vol: + master_vol_ctrlval = *(ptr); + master_vol = (double) master_vol_ctrlval / SS_MASTER_VOLUME_QUOT; + guiUpdateMasterVol(master_vol_ctrlval); + if (SS_DEBUG_INIT) { + printf("Master vol: %d\n", master_vol_ctrlval); + } + ptr++; + + // Effects: + ///if (*(ptr) != SS_SYSEX_INIT_DATA_VERSION) { + int effver = *(ptr); + if (effver < 1 || effver > SS_SYSEX_EFFECT_INIT_DATA_VERSION) { + //if (SS_DEBUG_INIT) + fprintf(stderr, "Error loading init data - effect init version is from future or too old. Skipping...\n"); + SS_TRACE_OUT return; - } - - ptr++; + } - for (int i=0; iwriteEvent(ev); - - // Jun 10 2011. This one was missing. p4.0.27 Tim. - if(effver >= 2) - { - if (SS_DEBUG_INIT) - printf("buffer[%ld] - sendeffect[%d] state=%d\n", long(ptr-data), i, *(ptr)); - sendEffects[i].state = (SS_SendFXState) *(ptr); - MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_PLUGIN_ONOFF_CONTROLLER(i), sendEffects[i].state); - gui->writeEvent(ev); - ptr++; - } - - ///for (int j=0; jconvertGuiControlValue(j, *(ptr))); - ptr++; - } - } - else { - if (sendEffects[i].plugin) - cleanupPlugin(i); - ptr++; - } - } + for (int i=0; iwriteEvent(ev); + + // Jun 10 2011. This one was missing. p4.0.27 Tim. + if(effver >= 2) + { + if (SS_DEBUG_INIT) + printf("buffer[%ld] - sendeffect[%d] state=%d\n", long(ptr-data), i, *(ptr)); + sendEffects[i].state = (SS_SendFXState) *(ptr); + MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_PLUGIN_ONOFF_CONTROLLER(i), sendEffects[i].state); + gui->writeEvent(ev); + ptr++; + } - SS_TRACE_OUT + ///for (int j=0; jconvertGuiControlValue(j, *(ptr))); + ptr++; + } + } + else { + if (sendEffects[i].plugin) + cleanupPlugin(i); + ptr++; } + } + + SS_TRACE_OUT +} /*! \fn SimpleSynth::loadSample(int chno, const char* filename) */ bool SimpleSynth::loadSample(int chno, const char* filename) - { - SS_TRACE_IN - SS_Channel* ch = &channels[chno]; +{ + SS_TRACE_IN + SS_Channel* ch = &channels[chno]; - // Thread stuff: - SS_SampleLoader* loader = new SS_SampleLoader; - loader->channel = ch; - loader->ch_no = chno; - if (SS_DEBUG) { - printf("Loader filename is: %s\n", filename); - } + // Thread stuff: + SS_SampleLoader* loader = new SS_SampleLoader; + loader->channel = ch; + loader->ch_no = chno; + loader->synth = this; + loader->sampleRate = sampleRate(); + if (SS_DEBUG) { + printf("Loader filename is: %s\n", filename); + } + + if (QFile::exists(filename)) + { + loader->filename = std::string(filename); + } + else + { + printf("current path: %s\nmuseProject %s\nfilename %s\n", + QDir::currentPath().toLatin1().constData(), + SS_projectPath.toLatin1().constData(), + filename); + QFileInfo fi(filename); + if (QFile::exists(fi.fileName())) + loader->filename = QDir::currentPath().toStdString() + "/" + fi.fileName().toStdString(); + else { - if (QFile::exists(filename)) - { - loader->filename = std::string(filename); - } - else - { - //printf("current path: %s \nmuseProject %s\nfilename %s\n",QDir::currentPath().toLatin1().data(), MusEGlobal::museProject.toLatin1().data(), filename); - //MusEGlobal::museProject - QFileInfo fi(filename); - if (QFile::exists(fi.fileName())) - loader->filename = QDir::currentPath().toStdString() + "/" + fi.fileName().toStdString(); - else { - - // TODO: Strings should be translated, this does - // however require the class to be derived from qobject - // tried in vain to make the call in the gui object - // could'nt get it to work due to symbol missing in .so ... - QString newName = QFileDialog::getOpenFileName(0, - QString("Can't find sample: %1 - Choose sample").arg(filename), - filename, - QString("Samples *.wav *.ogg *.flac (*.wav *.WAV *.ogg *.flac);;All files (*)")); - loader->filename = newName.toStdString(); - } - } + // TODO: Strings should be translated, this does + // however require the class to be derived from qobject + // tried in vain to make the call in the gui object + // could'nt get it to work due to symbol missing in .so ... + QString newName = QFileDialog::getOpenFileName(0, + QString("Can't find sample: %1 - Choose sample").arg(filename), + filename, + QString("Samples *.wav *.ogg *.flac (*.wav *.WAV *.ogg *.flac);;All files (*)")); + loader->filename = newName.toStdString(); + } + } + + + pthread_t sampleThread; + pthread_attr_t* attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); + pthread_attr_init(attributes); + pthread_attr_setdetachstate(attributes, PTHREAD_CREATE_DETACHED); + if (pthread_create(&sampleThread, attributes, ::loadSampleThread, (void*) loader)) { + perror("creating thread failed:"); + pthread_attr_destroy(attributes); + delete loader; + return false; + } + pthread_attr_destroy(attributes); + SS_TRACE_OUT + return true; +} - pthread_t sampleThread; - pthread_attr_t* attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); - pthread_attr_init(attributes); - pthread_attr_setdetachstate(attributes, PTHREAD_CREATE_DETACHED); - if (pthread_create(&sampleThread, attributes, ::loadSampleThread, (void*) loader)) { - perror("creating thread failed:"); - pthread_attr_destroy(attributes); - delete loader; - return false; - } - pthread_attr_destroy(attributes); - SS_TRACE_OUT - return true; - } +void resample(SS_Sample *origSample, SS_Sample* newSample, double pitch, int sample_rate) +{ + // Get new nr of frames: + double srcratio = (double) sample_rate / (double) origSample->samplerate * pitch; + newSample->frames = (long) floor(((double) origSample->frames * srcratio)); + //smp->frames = (sfi.channels == 1 ? smp->frames * 2 : smp->frames ); // Double nr of new frames if mono->stereo + newSample->samples = newSample->frames * newSample->channels; + newSample->samplerate = sample_rate; + + // Allocate mem for the new one + float* newData = new float[newSample->frames * newSample->channels]; + memset(newData, 0, sizeof(float)* newSample->frames * newSample->channels); + + // libsamplerate & co (secret rabbits in the code!) + SRC_DATA srcdata; + srcdata.data_in = origSample->data; + srcdata.data_out = newData; + srcdata.input_frames = origSample->frames; + srcdata.output_frames = newSample->frames; + srcdata.src_ratio = (double) newSample->samplerate / (double) origSample->samplerate * pitch; + + if (SS_DEBUG) { + printf("Converting sample....\n"); + } + + if (src_simple(&srcdata, SRC_SINC_BEST_QUALITY, origSample->channels)) { + SS_ERROR("Error when resampling, ignoring current sample"); + //TODO: deallocate and stuff + } + else if (SS_DEBUG) { + printf("Sample converted. %ld input frames used, %ld output frames generated\n", + srcdata.input_frames_used, + srcdata.output_frames_gen); + } + float *oldData = newSample->data; + newSample->data = newData; + if (oldData) { + delete oldData; + } +} /*! \fn loadSampleThread(void* p) - \brief Since process needs to respond withing a certain time, loading of samples need to be done in a separate thread + \brief Since process needs to respond within a certain time, loading of samples need to be done in a separate thread */ static void* loadSampleThread(void* p) - { - SS_TRACE_IN - pthread_mutex_lock(&SS_LoaderMutex); - - // Crit section: - SS_State prevState = synth_state; - SWITCH_SYNTH_STATE(SS_LOADING_SAMPLE); - SS_SampleLoader* loader = (SS_SampleLoader*) p; - SS_Channel* ch = loader->channel; - int ch_no = loader->ch_no; - - if (ch->sample) { - delete[] ch->sample->data; - delete ch->sample; - } - ch->sample = new SS_Sample; - SS_Sample* smp = ch->sample; - - SNDFILE* sf; - const char* filename = loader->filename.c_str(); - SF_INFO sfi; - - if (SS_DEBUG) - printf("loadSampleThread: filename = %s\n", filename); - - sf = sf_open(filename, SFM_READ, &sfi); - if (sf == 0) { - fprintf(stderr,"Error opening file: %s\n", filename); - SWITCH_SYNTH_STATE(prevState); - simplesynth_ptr->guiSendSampleLoaded(false, loader->ch_no, filename); - delete ch->sample; ch->sample = 0; - delete loader; - pthread_mutex_unlock(&SS_LoaderMutex); - SS_TRACE_OUT - pthread_exit(0); - } - - //Print some info: - if (SS_DEBUG) { - printf("Sample info:\n"); - printf("Frames: \t%ld\n", (long) sfi.frames); - printf("Channels: \t%d\n", sfi.channels); - printf("Samplerate: \t%d\n", sfi.samplerate); - } - - // - // Allocate and read the thingie - // - - // If current samplerate is the same as MusE's: - if (SS_samplerate == sfi.samplerate) { - smp->data = new float[sfi.channels * sfi.frames]; - sf_count_t n = sf_readf_float(sf, smp->data, sfi.frames); - smp->frames = sfi.frames; - smp->samples = (n * sfi.channels); - smp->channels = sfi.channels; - if (SS_DEBUG) { - printf("%ld frames read\n", (long) n); - } - } - else // otherwise, resample: - { - smp->channels = sfi.channels; - // Get new nr of frames: - double srcratio = (double) SS_samplerate/ (double) sfi.samplerate; - smp->frames = (long) floor(((double) sfi.frames * srcratio)); - smp->frames = (sfi.channels == 1 ? smp->frames * 2 : smp->frames ); // Double nr of new frames if mono->stereo - smp->samples = smp->frames * smp->channels; - - if (SS_DEBUG) { - //wilyfoobar-2011-02-13 - // arg2 :sfi.frames is of type sf_count_t (== 64 bit) (long long) - // this requires format %lld (twice 'l' in format string (arg1) - // old code//printf("Resampling from %ld frames to %ld frames - srcration: %lf\n", sfi.frames, smp->frames, srcratio); - //printf("Resampling from %lld frames to %ld frames - srcration: %lf\n", sfi.frames, smp->frames, srcratio); - // Changed by Tim. Just avoid the hassle for now. Need to determine 32/64 bit and provide two different printf lines. - printf("Resampling to %ld frames - srcration: %lf\n", smp->frames, srcratio); - printf("Nr of new samples: %ld\n", smp->samples); - } - - // Read to temporary: - float temp[sfi.frames * sfi.channels]; - int frames_read = sf_readf_float(sf, temp, sfi.frames); - if (frames_read != sfi.frames) { - fprintf(stderr,"Error reading sample %s\n", filename); - simplesynth_ptr->guiSendSampleLoaded(false, loader->ch_no, filename); - sf_close(sf); - SWITCH_SYNTH_STATE(prevState); - delete ch->sample; ch->sample = 0; - delete loader; - pthread_mutex_unlock(&SS_LoaderMutex); - pthread_exit(0); - SS_TRACE_OUT - } - - // Allocate mem for the new one - smp->data = new float[smp->frames * smp->channels]; - memset(smp->data, 0, sizeof(float)* smp->frames * smp->channels); - - // libsamplerate & co (secret rabbits in the code!) - SRC_DATA srcdata; - srcdata.data_in = temp; - srcdata.data_out = smp->data; - srcdata.input_frames = sfi.frames; - srcdata.output_frames = smp->frames; - srcdata.src_ratio = (double) SS_samplerate / (double) sfi.samplerate; - - if (SS_DEBUG) { - printf("Converting sample....\n"); - } +{ + SS_TRACE_IN + pthread_mutex_lock(&SS_LoaderMutex); - if (src_simple(&srcdata, SRC_SINC_BEST_QUALITY, sfi.channels)) { - SS_ERROR("Error when resampling, ignoring current sample"); - //TODO: deallocate and stuff - } - else if (SS_DEBUG) { - printf("Sample converted. %ld input frames used, %ld output frames generated\n", - srcdata.input_frames_used, - srcdata.output_frames_gen); - } - } - //Just close the dam thing - sf_close(sf); - SWITCH_SYNTH_STATE(prevState); - ch->sample->filename = loader->filename; - simplesynth_ptr->guiSendSampleLoaded(true, ch_no, filename); + // Crit section: + SS_SampleLoader* loader = (SS_SampleLoader*) p; + SimpleSynth* synth = loader->synth; + SS_State prevState = synth->synth_state; + synth->SWITCH_SYNTH_STATE(SS_LOADING_SAMPLE); + + SS_Channel* ch = loader->channel; + const int ch_no = loader->ch_no; + const int sample_rate = loader->sampleRate; + + if (ch->sample) { + delete[] ch->sample->data; + delete ch->sample; + } + + SNDFILE* sf; + const char* filename = loader->filename.c_str(); + SF_INFO sfi; + + if (SS_DEBUG) + printf("loadSampleThread: filename = %s\n", filename); + + sf = sf_open(filename, SFM_READ, &sfi); + if (sf == 0) { + fprintf(stderr,"Error opening file: %s\n", filename); + synth->SWITCH_SYNTH_STATE(prevState); + synth->guiSendSampleLoaded(false, loader->ch_no, filename); + delete ch->sample; ch->sample = 0; delete loader; pthread_mutex_unlock(&SS_LoaderMutex); SS_TRACE_OUT - pthread_exit(0); - } - - -//static Mess* instantiate(int sr, const char* name) -static Mess* instantiate(int sr, QWidget*, QString* /*projectPathPtr*/, const char* name) - { - printf("SimpleSynth sampleRate %d\n", sr); - SimpleSynth* synth = new SimpleSynth(sr); - if (!synth->init(name)) { - delete synth; - synth = 0; - } - return synth; - } + pthread_exit(0); + } + //Print some info: + if (SS_DEBUG) { + printf("Sample info:\n"); + printf("Frames: \t%ld\n", (long) sfi.frames); + printf("Channels: \t%d\n", sfi.channels); + printf("Samplerate: \t%d\n", sfi.samplerate); + } + + // + // Allocate and read the thingie + // + + // // If current samplerate is the same as MusE's and no pitching is needed: + // if (SS_samplerate == sfi.samplerate && (ch->pitch -1.0) < 0.001) { + // smp->data = new float[sfi.channels * sfi.frames]; + // sf_count_t n = sf_readf_float(sf, smp->data, sfi.frames); + // smp->frames = sfi.frames; + // smp->samples = (n * sfi.channels); + // smp->channels = sfi.channels; + // if (SS_DEBUG) { + // printf("%ld frames read\n", (long) n); + // } + // } + // else // otherwise, resample: + { + ch->sample = new SS_Sample; + SS_Sample* smp = ch->sample; + //smp->data = 0; // reset the data so we won't accidentally delete unallocated data + ch->originalSample = new SS_Sample; + SS_Sample* origSmp = ch->originalSample; + + smp->channels = sfi.channels; + origSmp->channels = sfi.channels; + + // Read to original sample storage: + float *origSample = new float[sfi.frames * sfi.channels]; + int frames_read = sf_readf_float(sf, origSample, sfi.frames); + if (frames_read != sfi.frames) { + fprintf(stderr,"Error reading sample %s\n", filename); + synth->guiSendSampleLoaded(false, loader->ch_no, filename); + sf_close(sf); + synth->SWITCH_SYNTH_STATE(prevState); + delete ch->sample; ch->sample = 0; + delete loader; + pthread_mutex_unlock(&SS_LoaderMutex); + pthread_exit(0); + SS_TRACE_OUT + } + + origSmp->channels = sfi.channels; + origSmp->frames = sfi.frames; + origSmp->samplerate = sfi.samplerate; + origSmp->data = origSample; + + resample(origSmp, smp, rangeToPitch(ch->pitchInt), sample_rate); + } + //Just close the dam thing + sf_close(sf); + synth->SWITCH_SYNTH_STATE(prevState); + ch->sample->filename = loader->filename; + synth->guiSendSampleLoaded(true, ch_no, filename); + delete loader; + pthread_mutex_unlock(&SS_LoaderMutex); + SS_TRACE_OUT + pthread_exit(0); +} /*! \fn SimpleSynth::updateBalance(int pan) */ void SimpleSynth::updateBalance(int ch, int val) - { - SS_TRACE_IN - channels[ch].pan = val; - - // Balance: - channels[ch].balanceFactorL = 1.0; - channels[ch].balanceFactorR = 1.0; - double offset = 0; - int dev = val - 64; - offset = (double) dev / 64.0; - if (offset < 0) { - channels[ch].balanceFactorR = 1.0 + offset; - } - else { - channels[ch].balanceFactorL = 1.0 - offset; - } +{ + SS_TRACE_IN + channels[ch].pan = val; - if (SS_DEBUG_MIDI) - printf("balanceFactorL %f balanceFactorR %f\n", channels[ch].balanceFactorL, channels[ch].balanceFactorR); - SS_TRACE_OUT - } + // Balance: + channels[ch].balanceFactorL = 1.0; + channels[ch].balanceFactorR = 1.0; + double offset = 0; + int dev = val - 64; + offset = (double) dev / 64.0; + if (offset < 0) { + channels[ch].balanceFactorR = 1.0 + offset; + } + else { + channels[ch].balanceFactorL = 1.0 - offset; + } + + if (SS_DEBUG_MIDI) + printf("balanceFactorL %f balanceFactorR %f\n", channels[ch].balanceFactorL, channels[ch].balanceFactorR); + SS_TRACE_OUT +} /*! \fn SimpleSynth::updateVolume(int invol_ctrlval) */ void SimpleSynth::updateVolume(int ch, int invol_ctrlval) - { - SS_TRACE_IN - channels[ch].volume = (double)invol_ctrlval/ (double) SS_CHANNEL_VOLUME_QUOT; - channels[ch].volume_ctrlval = invol_ctrlval; - SS_TRACE_OUT - } +{ + SS_TRACE_IN + channels[ch].volume = (double)invol_ctrlval/ (double) SS_CHANNEL_VOLUME_QUOT; + channels[ch].volume_ctrlval = invol_ctrlval; + SS_TRACE_OUT +} + +/*! + \fn SimpleSynth::updatePitch(int inpitch_ctrlval) + */ +void SimpleSynth::updatePitch(int ch, int inpitch_ctrlval) +{ + SS_TRACE_IN + channels[ch].pitchInt = inpitch_ctrlval; + SS_TRACE_OUT +} + +/*! + \fn SimpleSynth::guiUpdatePitch(int ch, int bal) + */ +void SimpleSynth::guiUpdatePitch(int ch, int bal) +{ + SS_TRACE_IN + MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, SS_CHANNEL_PITCH_CONTROLLER(ch), bal); + gui->writeEvent(ev); + SS_TRACE_OUT +} + +void SimpleSynth::guiUpdateRoute(int ch, int val) +{ + SS_TRACE_IN + MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, SS_CHANNEL_ROUTE_CONTROLLER(ch), val); + gui->writeEvent(ev); + SS_TRACE_OUT +} /*! \fn SimpleSynth::guiUpdateBalance(int ch, int bal) */ void SimpleSynth::guiUpdateBalance(int ch, int bal) - { - SS_TRACE_IN - MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, SS_CHANNEL_PAN_CONTROLLER(ch), bal); - gui->writeEvent(ev); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, SS_CHANNEL_PAN_CONTROLLER(ch), bal); + gui->writeEvent(ev); + SS_TRACE_OUT +} /*! \fn SimpleSynth::guiUpdateVolume(int ch, int val) */ void SimpleSynth::guiUpdateVolume(int ch, int val) - { - SS_TRACE_IN - MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_CHANNEL_VOLUME_CONTROLLER(ch), val); - gui->writeEvent(ev); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_CHANNEL_VOLUME_CONTROLLER(ch), val); + gui->writeEvent(ev); + SS_TRACE_OUT +} /*! \fn SimpleSynth::guiUpdateNoff(bool b) */ void SimpleSynth::guiUpdateNoff(int ch, bool b) - { - SS_TRACE_IN - MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_CHANNEL_NOFF_CONTROLLER(ch), b); - gui->writeEvent(ev); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_CHANNEL_NOFF_CONTROLLER(ch), b); + gui->writeEvent(ev); + SS_TRACE_OUT +} /*! \fn SimpleSynth::guiUpdateChoff(int ch, bool b) */ void SimpleSynth::guiUpdateChoff(int ch, bool b) - { - SS_TRACE_IN - MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_CHANNEL_ONOFF_CONTROLLER(ch), b); - gui->writeEvent(ev); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_CHANNEL_ONOFF_CONTROLLER(ch), b); + gui->writeEvent(ev); + SS_TRACE_OUT +} /*! \fn SimpleSynth::guiUpdateMasterVol(int val) */ void SimpleSynth::guiUpdateMasterVol(int val) - { - SS_TRACE_IN - MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_MASTER_CTRL_VOLUME, val); - gui->writeEvent(ev); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_MASTER_CTRL_VOLUME, val); + gui->writeEvent(ev); + SS_TRACE_OUT +} /*! \fn SimpleSynth::guiUpdateSendFxLevel(int fxid, int level) */ void SimpleSynth::guiUpdateSendFxLevel(int channel, int fxid, int level) - { - SS_TRACE_IN - MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_CHANNEL_SENDFX_CONTROLLER(channel, fxid), level); - gui->writeEvent(ev); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, SS_CHANNEL_SENDFX_CONTROLLER(channel, fxid), level); + gui->writeEvent(ev); + SS_TRACE_OUT +} /*! \fn SimpleSynth::guiSendSampleLoaded(int ch, const char* filename) */ void SimpleSynth::guiSendSampleLoaded(bool success, int ch, const char* filename) - { - SS_TRACE_IN - int len = strlen(filename) + 3; //2 + filenamelen + 1; - //int len = strlen(filename) + 3 + 2; //2 + filenamelen + 1, + 2 for header; - byte out[len]; - - //out[0] = MUSE_SYNTH_SYSEX_MFG_ID; - //out[1] = SIMPLEDRUMS_UNIQUE_ID; - if (success) { - out[0] = SS_SYSEX_LOAD_SAMPLE_OK; - //out[2] = SS_SYSEX_LOAD_SAMPLE_OK; - } - else { - out[0] = SS_SYSEX_LOAD_SAMPLE_ERROR; - //out[2] = SS_SYSEX_LOAD_SAMPLE_ERROR; - } - out[1] = ch; - //out[3] = ch; - memcpy(out+2, filename, strlen(filename)+1); - //memcpy(out+4, filename, strlen(filename)+1); - MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, out, len); +{ + SS_TRACE_IN + int len = strlen(filename) + 3; //2 + filenamelen + 1; + //int len = strlen(filename) + 3 + 2; //2 + filenamelen + 1, + 2 for header; + byte out[len]; + + //out[0] = MUSE_SYNTH_SYSEX_MFG_ID; + //out[1] = SIMPLEDRUMS_UNIQUE_ID; + if (success) { + out[0] = SS_SYSEX_LOAD_SAMPLE_OK; + //out[2] = SS_SYSEX_LOAD_SAMPLE_OK; + } + else { + out[0] = SS_SYSEX_LOAD_SAMPLE_ERROR; + //out[2] = SS_SYSEX_LOAD_SAMPLE_ERROR; + } + out[1] = ch; + //out[3] = ch; + memcpy(out+2, filename, strlen(filename)+1); + //memcpy(out+4, filename, strlen(filename)+1); + MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, out, len); + if(gui) gui->writeEvent(ev); - SS_TRACE_OUT - } + SS_TRACE_OUT +} /*! \fn SimpleSynth::guiSendError(const char* errorstring) */ void SimpleSynth::guiSendError(const char* errorstring) - { - SS_TRACE_IN - byte out[strlen(errorstring)+2]; - //byte out[strlen(errorstring)+4]; - //out[0] = MUSE_SYNTH_SYSEX_MFG_ID; - //out[1] = SIMPLEDRUMS_UNIQUE_ID; - out[0] = SS_SYSEX_ERRORMSG; - //out[2] = SS_SYSEX_ERRORMSG; - memcpy(out+1, errorstring, strlen(errorstring) +1); - //memcpy(out+3, errorstring, strlen(errorstring) +1); - SS_TRACE_OUT - } - -extern "C" - { - static MESS descriptor = { - "SimpleDrums", - "SimpleDrums by Mathias Lundgren", // (lunar_shuttle@users.sf.net)", - "0.1.1", //Version string - MESS_MAJOR_VERSION, MESS_MINOR_VERSION, - instantiate, - }; - // We must compile with -fvisibility=hidden to avoid namespace - // conflicts with global variables. - // Only visible symbol is "mess_descriptor". - // (TODO: all plugins should be compiled this way) - - __attribute__ ((visibility("default"))) - const MESS* mess_descriptor() { return &descriptor; } - } - +{ + SS_TRACE_IN + byte out[strlen(errorstring)+2]; + //byte out[strlen(errorstring)+4]; + //out[0] = MUSE_SYNTH_SYSEX_MFG_ID; + //out[1] = SIMPLEDRUMS_UNIQUE_ID; + out[0] = SS_SYSEX_ERRORMSG; + //out[2] = SS_SYSEX_ERRORMSG; + memcpy(out+1, errorstring, strlen(errorstring) +1); + //memcpy(out+3, errorstring, strlen(errorstring) +1); + SS_TRACE_OUT +} /*! \fn SimpleSynth::initSendEffect(int sendeffectid, QString lib, QString name) */ bool SimpleSynth::initSendEffect(int id, QString lib, QString name) - { - SS_TRACE_IN - bool success = false; - if (sendEffects[id].plugin) { - //Cleanup if one was already there: - cleanupPlugin(id); - } - sendEffects[id].plugin = (LadspaPlugin*) plugins.find(lib, name); - LadspaPlugin* plugin = sendEffects[id].plugin; - if (plugin) { //We found one - - sendEffects[id].inputs = plugin->inports(); - sendEffects[id].outputs = plugin->outports(); - - if (plugin->instantiate()) { - SS_DBG2("Plugin instantiated", name.toLatin1().constData()); - SS_DBG_I("Parameters", plugin->parameter()); - SS_DBG_I("No of inputs", plugin->inports()); - SS_DBG_I("No of outputs",plugin->outports()); - SS_DBG_I("Inplace-capable", plugin->inPlaceCapable()); - - // Connect inputs/outputs: - // If single output/input, only use first channel in sendFxLineOut/sendFxReturn - SS_DBG("Connecting ports..."); - plugin->connectInport(0, sendFxLineOut[id][0]); - if (plugin->inports() == 2) - plugin->connectInport(1, sendFxLineOut[id][1]); - else if (plugin->inports() > 2) { - fprintf(stderr, "Plugin has more than 2 inputs, not supported\n"); - } - - plugin->connectOutport(0, sendFxReturn[id][0]); - if (plugin->outports() == 2) - plugin->connectOutport(1, sendFxReturn[id][1]); - else if (plugin->outports() > 2) { - fprintf(stderr, "Plugin has more than 2 outputs, not supported\n"); - } - SS_DBG("Ports connected"); - if (plugin->start()) { - sendEffects[id].state = SS_SENDFX_ON; - success = true; - - int n = plugin->parameter(); - sendEffects[id].nrofparameters = n; - - // This is not nice, but freeverb doesn't want to play until some values are set: - if (name == "freeverb3") { - setFxParameter(id, 2, 0.5); - setFxParameter(id, 3, 0.5); - setFxParameter(id, 4, 0.5); - guiUpdateFxParameter(id, 2, 0.5); - guiUpdateFxParameter(id, 3, 0.5); - guiUpdateFxParameter(id, 4, 0.5); - } - } - //TODO: cleanup if failed - } - } - - //Notify gui - ///int len = 3; - int len = 2 + 4; // Char is not enough for many plugins. Was causing crash. Changed to 32 bits. p4.0.27 Tim. - //int len = 5; - byte out[len]; - out[0] = SS_SYSEX_LOAD_SENDEFFECT_OK; - out[1] = id; - //out[0] = MUSE_SYNTH_SYSEX_MFG_ID; - //out[1] = SIMPLEDRUMS_UNIQUE_ID; - //out[2] = SS_SYSEX_LOAD_SENDEFFECT_OK; - //out[3] = id; - int j=0; - for (iPlugin i = plugins.begin(); i!=plugins.end(); i++, j++) { - if ((*i)->lib() == plugin->lib() && (*i)->label() == plugin->label()) { - ///out[2] = j; - //out[4] = j; - *((unsigned*)(out + 2)) = j; - MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, out, len); - gui->writeEvent(ev); - } - } - - if (!success) { - QString errorString = "Error loading plugin \"" + plugin->label() + "\""; - guiSendError(errorString.toLatin1().constData()); - } - SS_TRACE_OUT - return success; - } +{ + SS_TRACE_IN + bool success = false; + if (sendEffects[id].plugin) { + //Cleanup if one was already there: + cleanupPlugin(id); + } + MusESimplePlugin::Plugin* plug = MusESimplePlugin::plugins.find(lib, name); + if(!plug) + { + fprintf(stderr, "initSendEffect: cannot find plugin id:%d lib:%s name:%s\n", + id, lib.toLatin1().constData(), name.toLatin1().constData()); + return false; + } + + MusESimplePlugin::PluginI* plugin = new MusESimplePlugin::PluginI(); + if(plugin->initPluginInstance( + plug, + 2, // Channels + sampleRate(), + SS_segmentSize, + SS_useDenormalBias, + SS_denormalBias)) + { + fprintf(stderr, "initSendEffect: cannot instantiate plugin <%s>\n", + plug->name().toLatin1().constData()); + // Make sure to delete the plugini. + delete plugin; + return false; + } + + sendEffects[id].plugin = plugin; + + + sendEffects[id].inputs = plugin->inports(); + sendEffects[id].outputs = plugin->outports(); + + SS_DBG2("Plugin instantiated", name.toLatin1().constData()); + SS_DBG_I("Parameters", (int)plugin->parameters()); + SS_DBG_I("No of inputs", (int)plugin->inports()); + SS_DBG_I("No of outputs", (int)plugin->outports()); + SS_DBG_I("Inplace-capable", plugin->inPlaceCapable()); + + // Connect inputs/outputs: + plugin->connect( + 2, // ports + 0, // offset + sendFxLineOut[id], + sendFxReturn[id]); + + SS_DBG("Ports connected"); + if (plugin->start()) + { + sendEffects[id].state = SS_SENDFX_ON; + success = true; + + int n = plugin->parameters(); + sendEffects[id].nrofparameters = n; + + // This is not nice, but freeverb doesn't want to play until some values are set: + if (name == "freeverb3") { + setFxParameter(id, 2, 0.5); + setFxParameter(id, 3, 0.5); + setFxParameter(id, 4, 0.5); + guiUpdateFxParameter(id, 2, 0.5); + guiUpdateFxParameter(id, 3, 0.5); + guiUpdateFxParameter(id, 4, 0.5); + } + } + //TODO: cleanup if failed + + //Notify gui + ///int len = 3; + int len = 2 + sizeof(MusESimplePlugin::PluginI*); + //int len = 5; + byte out[len]; + out[0] = SS_SYSEX_LOAD_SENDEFFECT_OK; + out[1] = id; + //out[0] = MUSE_SYNTH_SYSEX_MFG_ID; + //out[1] = SIMPLEDRUMS_UNIQUE_ID; + //out[2] = SS_SYSEX_LOAD_SENDEFFECT_OK; + //out[3] = id; + ///out[2] = j; + //out[4] = j; + *((MusESimplePlugin::PluginI**)(&out[2])) = plugin; + MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, out, len); + gui->writeEvent(ev); + + if (!success) { + QString errorString = "Error loading plugin \"" + plugin->label() + "\""; + guiSendError(errorString.toLatin1().constData()); + } + SS_TRACE_OUT + return success; +} /*! \fn SimpleSynth::setSendFxLevel(int channel, int effectid, double val) */ void SimpleSynth::setSendFxLevel(int channel, int effectid, double val) - { - SS_TRACE_IN - channels[channel].sendfxlevel[effectid] = val; - SS_TRACE_OUT - } +{ + SS_TRACE_IN + channels[channel].sendfxlevel[effectid] = val; + SS_TRACE_OUT +} /*! \fn SimpleSynth::cleanupPlugin(int id) */ void SimpleSynth::cleanupPlugin(int id) - { - SS_TRACE_IN - LadspaPlugin* plugin = sendEffects[id].plugin; - plugin->stop(); - SS_DBG2("Stopped fx", plugin->label().toLatin1().constData()); - sendEffects[id].nrofparameters = 0; - sendEffects[id].state = SS_SENDFX_OFF; - sendEffects[id].plugin = 0; - - byte d[2]; - //byte d[4]; - d[0] = SS_SYSEX_CLEAR_SENDEFFECT_OK; - d[1] = id; - //d[0] = MUSE_SYNTH_SYSEX_MFG_ID; - //d[1] = SIMPLEDRUMS_UNIQUE_ID; - //d[2] = SS_SYSEX_CLEAR_SENDEFFECT_OK; - //d[3] = id; - MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 2); - //MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 4); - gui->writeEvent(ev); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + MusESimplePlugin::PluginI* plugin = sendEffects[id].plugin; + //plugin->stop(); + if(plugin) + SS_DBG2("Stopped fx", plugin->label().toLatin1().constData()); + sendEffects[id].nrofparameters = 0; + sendEffects[id].state = SS_SENDFX_OFF; + if(plugin) + delete plugin; + + sendEffects[id].plugin = 0; + + byte d[2]; + //byte d[4]; + d[0] = SS_SYSEX_CLEAR_SENDEFFECT_OK; + d[1] = id; + //d[0] = MUSE_SYNTH_SYSEX_MFG_ID; + //d[1] = SIMPLEDRUMS_UNIQUE_ID; + //d[2] = SS_SYSEX_CLEAR_SENDEFFECT_OK; + //d[3] = id; + MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 2); + //MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 4); + gui->writeEvent(ev); + SS_TRACE_OUT +} /*! @@ -1847,17 +2002,20 @@ \brief Set fx-parameter on plugin and notify gui */ void SimpleSynth::setFxParameter(int fxid, int param, float val) - { - SS_TRACE_IN - LadspaPlugin* plugin = sendEffects[fxid].plugin; - if (SS_DEBUG_LADSPA) { - printf("Setting fx parameter: %f\n", val); - } - plugin->setParam(param, val); - //sendEffects[fxid].parameter[param] = val; - //guiUpdateFxParameter(fxid, param, val); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + MusESimplePlugin::PluginI* plugin = sendEffects[fxid].plugin; + if(plugin) + { + if (SS_DEBUG_LADSPA) { + printf("Setting fx parameter: %f\n", val); + } + plugin->setParam(param, val); + } + //sendEffects[fxid].parameter[param] = val; + //guiUpdateFxParameter(fxid, param, val); + SS_TRACE_OUT +} @@ -1866,46 +2024,50 @@ \brief Notify gui of changed fx-parameter */ void SimpleSynth::guiUpdateFxParameter(int fxid, int param, float val) - { - SS_TRACE_IN - LadspaPlugin* plugin = sendEffects[fxid].plugin; - float min, max; - plugin->range(param, &min, &max); - //offset: - val-= min; - - int intval = plugin->getGuiControlValue(param); - /*if (plugin->isLog(param)) { - intval = SS_map_logdomain2pluginparam(logf(val/(max - min) + min)); - } - else if (plugin->isBool(param)) { - intval = (int) val; - } - else { - float scale = SS_PLUGIN_PARAM_MAX / (max - min); - intval = (int) ((val - min) * scale); - }*/ - if (SS_DEBUG_MIDI) { - printf("Updating gui, fx parameter. fxid=%d, param=%d val=%d\n", fxid, param, intval); - } - - byte d[4]; - //byte d[6]; - d[0] = SS_SYSEX_SET_PLUGIN_PARAMETER_OK; - d[1] = fxid; - d[2] = param; - d[3] = intval; - //d[0] = MUSE_SYNTH_SYSEX_MFG_ID; - //d[1] = SIMPLEDRUMS_UNIQUE_ID; - //d[2] = SS_SYSEX_SET_PLUGIN_PARAMETER_OK; - //d[3] = fxid; - //d[4] = param; - //d[5] = intval; - MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 4); - //MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 6); - gui->writeEvent(ev); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + MusESimplePlugin::PluginI* plugin = sendEffects[fxid].plugin; + int intval = 0; + if(plugin) + { + float min, max; + plugin->range(param, &min, &max); + //offset: + val-= min; + + intval = plugin->getGuiControlValue(param); + /*if (plugin->isLog(param)) { + intval = SS_map_logdomain2pluginparam(logf(val/(max - min) + min)); + } + else if (plugin->isBool(param)) { + intval = (int) val; + } + else { + float scale = SS_PLUGIN_PARAM_MAX / (max - min); + intval = (int) ((val - min) * scale); + }*/ + if (SS_DEBUG_MIDI) { + printf("Updating gui, fx parameter. fxid=%d, param=%d val=%d\n", fxid, param, intval); + } + } + + byte d[4]; + //byte d[6]; + d[0] = SS_SYSEX_SET_PLUGIN_PARAMETER_OK; + d[1] = fxid; + d[2] = param; + d[3] = intval; + //d[0] = MUSE_SYNTH_SYSEX_MFG_ID; + //d[1] = SIMPLEDRUMS_UNIQUE_ID; + //d[2] = SS_SYSEX_SET_PLUGIN_PARAMETER_OK; + //d[3] = fxid; + //d[4] = param; + //d[5] = intval; + MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 4); + //MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 6); + gui->writeEvent(ev); + SS_TRACE_OUT +} /*! @@ -1913,48 +2075,85 @@ \brief Clears a sample (actually clears a channel) */ void SimpleSynth::clearSample(int ch) - { - SS_TRACE_IN +{ + SS_TRACE_IN + if (channels[ch].sample) { + if (SS_DEBUG) + printf("Clearing sample on channel %d\n", ch); + SS_State prevstate = synth_state; + SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); + SWITCH_SYNTH_STATE(SS_CLEARING_SAMPLE); + if (channels[ch].sample->data) { + delete[] channels[ch].sample->data; + channels[ch].sample->data = 0; + } if (channels[ch].sample) { - if (SS_DEBUG) - printf("Clearing sample on channel %d\n", ch); - SS_State prevstate = synth_state; - SWITCH_CHAN_STATE(ch, SS_CHANNEL_INACTIVE); - SWITCH_SYNTH_STATE(SS_CLEARING_SAMPLE); - if (channels[ch].sample->data) { - delete[] channels[ch].sample->data; - channels[ch].sample->data = 0; - } - if (channels[ch].sample) { - delete channels[ch].sample; - channels[ch].sample = 0; - } - SWITCH_SYNTH_STATE(prevstate); - guiNotifySampleCleared(ch); - if (SS_DEBUG) { - printf("Clear sample - sample cleared on channel %d\n", ch); - } - } - SS_TRACE_OUT + delete channels[ch].sample; + channels[ch].sample = 0; } + SWITCH_SYNTH_STATE(prevstate); + guiNotifySampleCleared(ch); + if (SS_DEBUG) { + printf("Clear sample - sample cleared on channel %d\n", ch); + } + } + SS_TRACE_OUT +} /*! \fn SimpleSynth::guiNotifySampleCleared(int ch) */ void SimpleSynth::guiNotifySampleCleared(int ch) - { - SS_TRACE_IN - byte d[2]; - //byte d[4]; - d[0] = SS_SYSEX_CLEAR_SAMPLE_OK; - d[1] = (byte) ch; - //d[0] = MUSE_SYNTH_SYSEX_MFG_ID; - //d[1] = SIMPLEDRUMS_UNIQUE_ID; - //d[2] = SS_SYSEX_CLEAR_SAMPLE_OK; - //d[3] = (byte) ch; - MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 2); - //MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 4); - gui->writeEvent(ev); - SS_TRACE_OUT - } +{ + SS_TRACE_IN + byte d[2]; + //byte d[4]; + d[0] = SS_SYSEX_CLEAR_SAMPLE_OK; + d[1] = (byte) ch; + //d[0] = MUSE_SYNTH_SYSEX_MFG_ID; + //d[1] = SIMPLEDRUMS_UNIQUE_ID; + //d[2] = SS_SYSEX_CLEAR_SAMPLE_OK; + //d[3] = (byte) ch; + MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 2); + //MusECore::MidiPlayEvent ev(0, 0, MusECore::ME_SYSEX, d, 4); + gui->writeEvent(ev); + SS_TRACE_OUT +} + +static Mess* instantiate(unsigned long long /*parentWinId*/, const char* name, const MessConfig* config) +{ + printf("SimpleSynth sampleRate:%d minMeterVal:%d\n", config->_sampleRate, config->_minMeterVal); + SS_segmentSize = config->_segmentSize; + SS_minMeterVal = config->_minMeterVal; + SS_useDenormalBias = config->_useDenormalBias; + SS_denormalBias = config->_denormalBias; + SS_globalLibPath = QString(config->_globalLibPath); + SS_projectPath = QString(config->_projectPath); + SimpleSynth* synth = new SimpleSynth(config->_sampleRate); + if (!synth->init(name)) { + delete synth; + synth = 0; + } + return synth; +} + +extern "C" +{ +static MESS descriptor = { + "SimpleDrums", + "SimpleDrums by Mathias Lundgren", // (lunar_shuttle@users.sf.net)", + "0.1.1", //Version string + MESS_MAJOR_VERSION, MESS_MINOR_VERSION, + instantiate, +}; +// We must compile with -fvisibility=hidden to avoid namespace +// conflicts with global variables. +// Only visible symbol is "mess_descriptor". +// (TODO: all plugins should be compiled this way) + +__attribute__ ((visibility("default"))) +const MESS* mess_descriptor() { return &descriptor; } +} + + diff -Nru muse-2.1.2/synti/simpledrums2/simpledrumsguibase.ui muse-3.0.2+ds1/synti/simpledrums2/simpledrumsguibase.ui --- muse-2.1.2/synti/simpledrums2/simpledrumsguibase.ui 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/simpledrumsguibase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -1,10 +1,8 @@ - - - - + + SimpleDrumsGuiBase - - + + 0 0 @@ -12,11 +10,37 @@ 509 - + DrumSynth 0.1 + + + + + Audio file assignments. Within parantesis: note number and traditional midi drum name + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + - + diff -Nru muse-2.1.2/synti/simpledrums2/simpledrumsgui.cpp muse-3.0.2+ds1/synti/simpledrums2/simpledrumsgui.cpp --- muse-2.1.2/synti/simpledrums2/simpledrumsgui.cpp 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/simpledrumsgui.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -6,6 +6,7 @@ // // Author: Mathias Lundgren , (C) 2004 // Contributer: (C) Copyright 2011 Tim E. Real (terminator356 at users.sourceforge.net) +// Multi-channel support and metering - Andrew Deryabin, 2015 // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -27,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -35,10 +35,10 @@ #include "common_defs.h" #include "simpledrumsgui.h" -//#include "libsynti/mpevent.h" #include "muse/mpevent.h" -#include "muse/midi.h" +#include "muse/midi_consts.h" #include "ssplugingui.h" +#include "wavepreview.h" #define SS_VOLUME_MIN_VALUE 0 #define SS_VOLUME_MAX_VALUE 127 @@ -92,26 +92,25 @@ #define SS_GUI_WINDOW_HEIGHT (SS_BTNGRP_HEIGHT + SS_MAIN_GROUPBOX_HEIGHT) #define SS_MAIN_GROUPBOX_WIDTH SS_GUI_WINDOW_WIDTH -SimpleSynthGui* simplesynthgui_ptr; QString labelStrings[] = { - "C 1", - "C#1", - "D 1", - "D#1", - "E 1", - "F 1", - "F#1", - "G 1", - - "G#1", - "A 1", - "A#1", - "B 1", - "C 2", - "C#2", - "D 2", - "D#2", + "C 1 Bass drum 1", + "C#1 Side stick", + "D 1 Acoustic snare", + "D#1 Hand clap", + "E 1 Electric snare", + "F 1 Low floor tom", + "F#1 Closed hi-hat", + "G 1 High floor tom", + + "G#1 Pedal hi-hat", + "A 1 Low tom", + "A#1 Open hi-hat", + "B 1 Low-mid tom", + "C 2 Hi-mid tom", + "C#2 Crash cymbal", + "D 2 High tom", + "D#2 Ride cymbal 1", }; @@ -119,35 +118,35 @@ \fn QChannelSlider::QChannelSlider(Qt::Orientation orientation, int ch, QWidget* parent, const char* name) */ QChannelSlider::QChannelSlider(Qt::Orientation orientation, int ch, QWidget* parent) - : QSlider(orientation, parent) + : QSlider(orientation, parent) { - channel = ch; - setMinimumHeight(50); + channel = ch; + setMinimumHeight(50); } void QChannelSlider::sliderChange(SliderChange change) { - QSlider::sliderChange(change); - if(change == QAbstractSlider::SliderValueChange) - emit valueChanged(channel, value()); + QSlider::sliderChange(change); + if(change == QAbstractSlider::SliderValueChange) + emit valueChanged(channel, value()); } /*! \fn QChannelSlider::getChannel() */ int QChannelSlider::getChannel() - { - return channel; - } +{ + return channel; +} /*! \fn QChannelSlider::setChannel(int ch) */ void QChannelSlider::setChannel(int ch) - { - channel = ch; - } +{ + channel = ch; +} /*! \fn QChannelSlider::setValue(int val) @@ -163,23 +162,23 @@ */ QInvertedSlider::QInvertedSlider(Qt::Orientation o, QWidget* parent) - : QSlider(o, parent) + : QSlider(o, parent) { - setInvertedAppearance(true); // p4.0.27 + setInvertedAppearance(true); // p4.0.27 } void QInvertedSlider::sliderChange(SliderChange change) { - QSlider::sliderChange(change); - if(change == QAbstractSlider::SliderValueChange) - emit invertedValueChanged(value()); + QSlider::sliderChange(change); + if(change == QAbstractSlider::SliderValueChange) + emit invertedValueChanged(value()); } QInvertedChannelSlider::QInvertedChannelSlider(Qt::Orientation o, int channel, QWidget* parent) - : QChannelSlider(o, channel, parent) + : QChannelSlider(o, channel, parent) { - setInvertedAppearance(true); // p4.0.27 - //setInvertedControls(true); + setInvertedAppearance(true); // p4.0.27 + //setInvertedControls(true); } /*! @@ -215,48 +214,49 @@ */ QChannelCheckbox::QChannelCheckbox(QWidget* parent, int ch) : QCheckBox(parent) - { - channel = ch; - connect(this, SIGNAL(clicked()), SLOT(isClicked())); - } +{ + channel = ch; + connect(this, SIGNAL(clicked()), SLOT(isClicked())); +} /*! \fn QChannelCheckbox::isClicked() */ void QChannelCheckbox::isClicked() - { - emit channelState(channel, this->isChecked()); - } +{ + emit channelState(channel, this->isChecked()); +} /*! \fn QChannelButton::QChannelButton(QWidget* parent, const char* text, int ch, const char* name) */ QChannelButton::QChannelButton(QWidget* parent, const char* text, int ch) - : QPushButton(parent), channel (ch) - { - connect(this, SIGNAL(clicked()), SLOT(isClicked())); - setText(text); - } + : QPushButton(parent), channel (ch) +{ + connect(this, SIGNAL(clicked()), SLOT(isClicked())); + setText(text); +} /*! \fn QChannelButton::isClicked() */ void QChannelButton::isClicked() - { - emit channelState(channel, this->isChecked()); - } +{ + emit channelState(channel, this->isChecked()); +} /*! \fn QChannelDial() */ QChannelDial::QChannelDial(QWidget* parent, int ch, int fxid) : QDial(parent) - { - setTracking(true); - channel = ch; - sendfxid = fxid; - } +{ + setTracking(true); + channel = ch; + sendfxid = fxid; + connect(this, SIGNAL(sliderReleased()), SLOT(forwardSliderMoved())); +} /*! \fn QChannelSlider::setValue(int val) @@ -271,587 +271,606 @@ void QChannelDial::sliderChange(SliderChange change) { - QDial::sliderChange(change); - if(change == QAbstractSlider::SliderValueChange) - emit valueChanged(channel, sendfxid, value()); + QDial::sliderChange(change); + if(change == QAbstractSlider::SliderValueChange) + emit valueChanged(channel, sendfxid, value()); +} + +void QChannelDial::forwardSliderMoved() +{ + printf("forwardSliderMoved();\n"); + emit sliderMoved(channel, value()); } /*! \fn SimpleSynthGui::SimpleSynthGui() */ -SimpleSynthGui::SimpleSynthGui() - { - SS_TRACE_IN - setupUi(this); - simplesynthgui_ptr = this; - pluginGui = new SS_PluginGui(this); - pluginGui->hide(); - - QVBoxLayout* mainLayout = new QVBoxLayout(this); //, 3); - -// mainLayout->setSpacing(0); -// mainLayout->setMargin(0); - QHBoxLayout* channelLayout = new QHBoxLayout; - channelLayout->setSpacing(1); - channelLayout->setMargin(0); - mainLayout->addLayout(channelLayout); - - - - //this->setFixedWidth(SS_GUI_WINDOW_WIDTH); - //this->setFixedHeight(SS_GUI_WINDOW_HEIGHT); - for (int i=0; isetMinimumSize(SS_BTNGRP_WIDTH, SS_BTNGRP_HEIGHT); - channelButtonGroups[i]->setTitle(QString::number(i + 1)); - - QString name = QString("volumeSlider"); - name.append(i + 1); - - channelLayout->addWidget(channelButtonGroups[i]); - - QVBoxLayout* inchnlLayout = new QVBoxLayout(channelButtonGroups[i]); //, 2, 0, "channelinternallayout"); - inchnlLayout->setAlignment(Qt::AlignHCenter); - inchnlLayout->setSpacing(1); - inchnlLayout->setMargin(0); - - - onOff[i] = new QChannelCheckbox(channelButtonGroups[i], i); -// onOff[i]->setMinimumSize(SS_ONOFF_WIDTH, SS_ONOFF_HEIGHT); - onOff[i]->setToolTip("Channel " + QString::number(i + 1) + " on/off"); - inchnlLayout->addWidget(onOff[i]); - connect(onOff[i], SIGNAL(channelState(int, bool)), SLOT(channelOnOff(int, bool))); - - ///volumeSliders[i] = new QInvertedChannelSlider(Qt::Vertical, i, channelButtonGroups[i]); - // By Tim. p4.0.27 Inverted was not correct type. Maybe was work in progress, rest of code was not converted yet? - volumeSliders[i] = new QChannelSlider(Qt::Vertical, i, channelButtonGroups[i]); - - volumeSliders[i]->setMinimum(SS_VOLUME_MIN_VALUE); - volumeSliders[i]->setMaximum(SS_VOLUME_MAX_VALUE); - - ///volumeSliders[i]->setValue(SS_VOLUME_MAX_VALUE - SS_VOLUME_DEFAULT_VALUE); - volumeSliders[i]->setValue(SS_VOLUME_DEFAULT_VALUE); // p4.0.27 - -// volumeSliders[i]->setMinimumSize(SS_VOLSLDR_WIDTH, SS_VOLSLDR_LENGTH); - volumeSliders[i]->setToolTip("Volume, channel " + QString::number(i + 1)); -// setMinimumSize(SS_VOLSLDR_WIDTH, SS_VOLSLDR_LENGTH); - inchnlLayout->addWidget(volumeSliders[i]); - connect(volumeSliders[i], SIGNAL(valueChanged(int, int)), SLOT(volumeChanged(int, int))); - - nOffLabel[i] = new QLabel(channelButtonGroups[i]); -// nOffLabel[i]->setMinimumSize(SS_NONOFF_LABEL_WIDTH, SS_NONOFF_LABEL_HEIGHT); - nOffLabel[i]->setText("nOff"); - inchnlLayout->addWidget(nOffLabel[i]); - - nOffIgnore[i] = new QChannelCheckbox(channelButtonGroups[i], i); -// nOffIgnore[i]->setMinimumSize(SS_NONOFF_WIDTH, SS_NONOFF_HEIGHT); - nOffIgnore[i]->setToolTip("Note off ignore, channel " + QString::number(i + 1)); - inchnlLayout->addWidget(nOffIgnore[i]); - connect(nOffIgnore[i], SIGNAL(channelState(int, bool)),SLOT(channelNoteOffIgnore(int, bool))); - - panSliders[i] = new QChannelSlider(Qt::Horizontal, i, channelButtonGroups[i]); - panSliders[i]->setRange(0, 127); - panSliders[i]->setValue(SS_PANSLDR_DEFAULT_VALUE); -// panSliders[i]->setMinimumSize(SS_PANSLDR_WIDTH, SS_PANSLDR_LENGTH); - panSliders[i]->setToolTip("Pan, channel " + QString::number(i + 1)); - inchnlLayout->addWidget(panSliders[i]); - connect(panSliders[i], SIGNAL(valueChanged(int, int)), SLOT(panChanged(int, int))); - - QGridLayout* dialGrid = new QGridLayout; - inchnlLayout->addLayout(dialGrid); - sendFxDial[i][0] = new QChannelDial(channelButtonGroups[i], i, 0); - sendFxDial[i][0]->setRange(0, 127); - sendFxDial[i][0]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); - sendFxDial[i][0]->setToolTip("Fx 1 send amount"); - //inchnlLayout->addWidget(sendFxDial[i][0]); - dialGrid->addWidget(sendFxDial[i][0], 0, 0, Qt::AlignCenter | Qt::AlignTop); - - connect(sendFxDial[i][0], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); - - sendFxDial[i][1] = new QChannelDial(channelButtonGroups[i], i, 1); - sendFxDial[i][1]->setRange(0, 127); - //inchnlLayout->add(sendFxDial[i][1]); - dialGrid->addWidget(sendFxDial[i][1], 0, 1, Qt::AlignCenter | Qt::AlignTop); - sendFxDial[i][1]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); - sendFxDial[i][1]->setToolTip("Fx 2 send amount"); - - connect(sendFxDial[i][1], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); - - sendFxDial[i][2] = new QChannelDial(channelButtonGroups[i], i, 2); - sendFxDial[i][2]->setRange(0, 127); - sendFxDial[i][2]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); - //inchnlLayout->add(sendFxDial[i][2]); - dialGrid->addWidget(sendFxDial[i][2], 1, 0, Qt::AlignCenter | Qt::AlignTop); - sendFxDial[i][2]->setToolTip("Fx 3 send amount"); - connect(sendFxDial[i][2], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); - - sendFxDial[i][3] = new QChannelDial(channelButtonGroups[i], i, 3); - sendFxDial[i][3]->setRange(0, 127); - sendFxDial[i][3]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); - sendFxDial[i][3]->setToolTip("Fx 4 send amount"); - - dialGrid->addWidget(sendFxDial[i][3], 1, 1, Qt::AlignCenter | Qt::AlignTop); - connect(sendFxDial[i][3], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); - inchnlLayout->activate(); - //channelLayout->activate(); - } +SimpleSynthGui::SimpleSynthGui(int sampleRate) +{ + SS_TRACE_IN + setupUi(this); + + setSampleRate(sampleRate); + + pluginGui = new SS_PluginGui(this); + pluginGui->hide(); + + for (int i=0; isetMinimumSize(SS_BTNGRP_WIDTH, SS_BTNGRP_HEIGHT); + channelButtonGroups[i]->setTitle(QString::number(i + 1)); + + QString name = QString("volumeSlider"); + name.append(i + 1); + + channelLayout->addWidget(channelButtonGroups[i]); + + QVBoxLayout* inchnlLayout = new QVBoxLayout(channelButtonGroups[i]); //, 2, 0, "channelinternallayout"); + inchnlLayout->setAlignment(Qt::AlignHCenter); + inchnlLayout->setSpacing(1); + inchnlLayout->setMargin(0); + + + onOff[i] = new QChannelCheckbox(channelButtonGroups[i], i); + // onOff[i]->setMinimumSize(SS_ONOFF_WIDTH, SS_ONOFF_HEIGHT); + onOff[i]->setToolTip("Channel " + QString::number(i + 1) + " on/off"); + inchnlLayout->addWidget(onOff[i]); + connect(onOff[i], SIGNAL(channelState(int, bool)), SLOT(channelOnOff(int, bool))); + + ///volumeSliders[i] = new QInvertedChannelSlider(Qt::Vertical, i, channelButtonGroups[i]); + // By Tim. p4.0.27 Inverted was not correct type. Maybe was work in progress, rest of code was not converted yet? + QHBoxLayout *volLayout = new QHBoxLayout(channelButtonGroups[i]); + + volumeSliders[i] = new QChannelSlider(Qt::Vertical, i, channelButtonGroups[i]); + + volumeSliders[i]->setMinimum(SS_VOLUME_MIN_VALUE); + volumeSliders[i]->setMaximum(SS_VOLUME_MAX_VALUE); + + ///volumeSliders[i]->setValue(SS_VOLUME_MAX_VALUE - SS_VOLUME_DEFAULT_VALUE); + volumeSliders[i]->setValue(SS_VOLUME_DEFAULT_VALUE); // p4.0.27 + + // volumeSliders[i]->setMinimumSize(SS_VOLSLDR_WIDTH, SS_VOLSLDR_LENGTH); + volumeSliders[i]->setToolTip("Volume, channel " + QString::number(i + 1)); + // setMinimumSize(SS_VOLSLDR_WIDTH, SS_VOLSLDR_LENGTH); + volLayout->addWidget(volumeSliders[i]); + + chnMeter[i] = new MusEGui::Meter(channelButtonGroups[i]); + chnMeter[i]->setFixedWidth(9); + chnMeter[i]->setVal(0.0, 0.0, false); + meterVal[i] = peakVal[i] = 0.0; + chnMeter[i]->setRange(SS_minMeterVal, 10.0); + chnMeter[i]->show(); + volLayout->addWidget(chnMeter[i]); + + inchnlLayout->addLayout(volLayout); + //inchnlLayout->addWidget(volumeSliders[i]); + connect(volumeSliders[i], SIGNAL(valueChanged(int, int)), SLOT(volumeChanged(int, int))); + + pitchKnobs[i] = new QChannelDial(channelButtonGroups[i], i, 0); + pitchKnobs[i]->setRange(-63,63); + pitchKnobs[i]->setValue(0); + pitchKnobs[i]->setToolTip("Pitch, channel " + QString::number(i + 1)); + pitchKnobs[i]->setFixedSize(30,30); + inchnlLayout->addWidget(pitchKnobs[i]); + connect(pitchKnobs[i], SIGNAL(valueChanged(int,int,int)), SLOT(pitchChanged(int,int, int))); + + + nOffLabel[i] = new QLabel(channelButtonGroups[i]); + nOffLabel[i]->setText("nOff"); + inchnlLayout->addWidget(nOffLabel[i]); + + nOffIgnore[i] = new QChannelCheckbox(channelButtonGroups[i], i); + nOffIgnore[i]->setToolTip("Note off ignore, channel " + QString::number(i + 1)); + inchnlLayout->addWidget(nOffIgnore[i]); + connect(nOffIgnore[i], SIGNAL(channelState(int, bool)),SLOT(channelNoteOffIgnore(int, bool))); + + panSliders[i] = new QChannelSlider(Qt::Horizontal, i, channelButtonGroups[i]); + panSliders[i]->setRange(0, 127); + panSliders[i]->setValue(SS_PANSLDR_DEFAULT_VALUE); + panSliders[i]->setToolTip("Pan, channel " + QString::number(i + 1)); + inchnlLayout->addWidget(panSliders[i]); + connect(panSliders[i], SIGNAL(valueChanged(int, int)), SLOT(panChanged(int, int))); + + QGridLayout* dialGrid = new QGridLayout; + inchnlLayout->addLayout(dialGrid); + sendFxDial[i][0] = new QChannelDial(channelButtonGroups[i], i, 0); + sendFxDial[i][0]->setRange(0, 127); + sendFxDial[i][0]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); + sendFxDial[i][0]->setToolTip("Fx 1 send amount"); + dialGrid->addWidget(sendFxDial[i][0], 0, 0, Qt::AlignCenter | Qt::AlignTop); + + connect(sendFxDial[i][0], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); + + sendFxDial[i][1] = new QChannelDial(channelButtonGroups[i], i, 1); + sendFxDial[i][1]->setRange(0, 127); + dialGrid->addWidget(sendFxDial[i][1], 0, 1, Qt::AlignCenter | Qt::AlignTop); + sendFxDial[i][1]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); + sendFxDial[i][1]->setToolTip("Fx 2 send amount"); + + connect(sendFxDial[i][1], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); + + sendFxDial[i][2] = new QChannelDial(channelButtonGroups[i], i, 2); + sendFxDial[i][2]->setRange(0, 127); + sendFxDial[i][2]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); + dialGrid->addWidget(sendFxDial[i][2], 1, 0, Qt::AlignCenter | Qt::AlignTop); + sendFxDial[i][2]->setToolTip("Fx 3 send amount"); + connect(sendFxDial[i][2], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); + + sendFxDial[i][3] = new QChannelDial(channelButtonGroups[i], i, 3); + sendFxDial[i][3]->setRange(0, 127); + sendFxDial[i][3]->setMaximumSize(SS_SENDFX_WIDTH, SS_SENDFX_HEIGHT); + sendFxDial[i][3]->setToolTip("Fx 4 send amount"); + + dialGrid->addWidget(sendFxDial[i][3], 1, 1, Qt::AlignCenter | Qt::AlignTop); + connect(sendFxDial[i][3], SIGNAL(valueChanged(int, int, int)), SLOT(sendFxChanged(int, int, int))); + + chnRoutingCb[i] = new QComboBox(channelButtonGroups[i]); + chnRoutingCb[i]->addItem(tr("Mix"), QVariant(0)); + chnRoutingCb[i]->addItem(tr("Chn"), QVariant(1)); + chnRoutingCb[i]->setMaximumSize(SS_PANSLDR_WIDTH, SS_PANSLDR_LENGTH); + chnRoutingCb[i]->setToolTip(tr("Channel routing")); + QFont chnRFont; + chnRFont.setPointSize(6); + chnRoutingCb[i]->setFont(chnRFont); + connect(chnRoutingCb[i], SIGNAL(currentIndexChanged(int)), this, SLOT(routeChanged(int))); + inchnlLayout->addWidget(chnRoutingCb[i]); + + inchnlLayout->activate(); + } + + masterSlider = new QSlider(Qt::Vertical, this); + masterSlider->setToolTip("Master volume"); + channelLayout->addWidget(masterSlider); + masterSlider->setRange(0, 127); + masterSlider->setValue((int)(SS_MASTERVOL_DEFAULT_VALUE*SS_VOLUME_MAX_VALUE)); // p4.0.27 + + connect(masterSlider, SIGNAL(valueChanged(int)), SLOT(masterVolChanged(int))); // p4.0.27 + + + int i=0; + + for (int c=0; c<2; c++) { + for (int r=0; raddLayout(strip, r, c); + + QLabel* channelLabel = new QLabel(QString::number(i + 1) + ": (" +labelStrings[i] + ")"); + strip->addWidget(channelLabel); + + sampleNameLineEdit[i] = new QLineEdit(); + sampleNameLineEdit[i]->setReadOnly(true); + sampleNameLineEdit[i]->setFixedWidth(180); + strip->addWidget(sampleNameLineEdit[i]); + + loadSampleButton[i] = new QChannelButton(0, "L", i); + loadSampleButton[i]->setToolTip("Load sample on channel " + QString::number(i + 1)); + loadSampleButton[i]->setFixedSize(23,23); + strip->addWidget(loadSampleButton[i]); + connect(loadSampleButton[i], SIGNAL(channelState(int, bool)), SLOT(loadSampleDialogue(int))); + + clearSampleButton[i] = new QChannelButton(0, "C", i); + clearSampleButton[i]->setToolTip("Clear sample on channel " + QString::number(i + 1)); + clearSampleButton[i]->setFixedSize(23,23); + strip->addWidget(clearSampleButton[i]); + connect(clearSampleButton[i], SIGNAL(channelState(int, bool)), SLOT(clearSample(int))); + i++; + } + } + + // Right bottom panel: + QGroupBox* rbPanel= new QGroupBox(); + mgbLayout->addWidget(rbPanel, 1, 3, 7, 1, Qt::AlignCenter); + QGridLayout* rbLayout = new QGridLayout(rbPanel); + + openPluginsButton = new QPushButton("&Send Effects"); + openPluginsButton->setToolTip("Configure LADSPA send effects"); + connect(openPluginsButton, SIGNAL(clicked()), SLOT(openPluginButtonClicked())); + rbLayout->addWidget(openPluginsButton, 2, 1, Qt::AlignCenter | Qt::AlignVCenter); + rbLayout->setSpacing(0); + rbLayout->setMargin(0); + aboutButton = new QPushButton("About SimpleDrums"); + connect(aboutButton, SIGNAL(clicked()), SLOT(aboutButtonClicked())); + rbLayout->addWidget(aboutButton, 4, 1, Qt::AlignLeft | Qt::AlignVCenter); + + + loadButton = new QPushButton(tr("&Load setup"), rbPanel); + connect(loadButton, SIGNAL(clicked()), SLOT(loadSetup())); + saveButton = new QPushButton(tr("&Save setup"), rbPanel); + connect(saveButton, SIGNAL(clicked()), SLOT(saveSetup())); + rbLayout->addWidget(loadButton, 3, 1, Qt::AlignCenter | Qt::AlignVCenter); + rbLayout->addWidget(saveButton, 4, 1, Qt::AlignCenter | Qt::AlignVCenter); + rbLayout->addWidget(aboutButton, 6, 1, Qt::AlignCenter | Qt::AlignVCenter); - //Master buttongroup: - masterButtonGroup = new QGroupBox(this); - channelLayout->addWidget(masterButtonGroup); - QVBoxLayout* mbgLayout = new QVBoxLayout(masterButtonGroup); - mbgLayout->setAlignment(Qt::AlignCenter); - mbgLayout->setSpacing(1); - mbgLayout->setMargin(0); - -// masterButtonGroup->setMinimumSize(SS_BTNGRP_WIDTH, SS_BTNGRP_HEIGHT); - - ///masterSlider = new QInvertedSlider(Qt::Vertical, masterButtonGroup); - // By Tim. p4.0.27 Inverted was not correct type. Maybe was work in progress, rest of code was not converted yet? - masterSlider = new QSlider(Qt::Vertical, masterButtonGroup); - - masterSlider->setToolTip("Master volume"); - mbgLayout->addWidget(masterSlider); - masterSlider->setRange(0, 127); - - ///masterSlider->setValue(SS_VOLUME_MAX_VALUE - (int)(SS_MASTERVOL_DEFAULT_VALUE*SS_VOLUME_MAX_VALUE)); - masterSlider->setValue((int)(SS_MASTERVOL_DEFAULT_VALUE*SS_VOLUME_MAX_VALUE)); // p4.0.27 - -// masterSlider->setMinimumSize(SS_MASTERSLDR_WIDTH, SS_MASTERSLDR_HEIGHT); - - ///connect(masterSlider, SIGNAL(invertedValueChanged(int)), SLOT(masterVolChanged(int))); - connect(masterSlider, SIGNAL(valueChanged(int)), SLOT(masterVolChanged(int))); // p4.0.27 - - //Main groupbox - mainGroupBox = new QGroupBox(this); - mainLayout->addWidget(mainGroupBox); - - QGridLayout* mgbLayout = new QGridLayout(mainGroupBox); // , 8, 3, 1); - mgbLayout->setSpacing(1); - mgbLayout->setMargin(0); - - int i=0; - - for (int c=0; c<2; c++) { - for (int r=0; raddLayout(strip, r, c); - - //QLabel* channelLabel = new QLabel(QString("Ch ") + QString::number(i + 1), mainGroupBox); - QLabel* channelLabel = new QLabel(QString::number(i + 1) + ": (" +labelStrings[i] + ")", mainGroupBox); - strip->addWidget(channelLabel); - - sampleNameLineEdit[i] = new QLineEdit(mainGroupBox); - sampleNameLineEdit[i]->setReadOnly(true); - strip->addWidget(sampleNameLineEdit[i]); - - loadSampleButton[i] = new QChannelButton(mainGroupBox, "L", i); -// loadSampleButton[i]->setMinimumSize(SS_SAMPLE_LOAD_WIDTH, SS_SAMPLE_LOAD_HEIGHT); - loadSampleButton[i]->setToolTip("Load sample on channel " + QString::number(i + 1)); - strip->addWidget(loadSampleButton[i]); - connect(loadSampleButton[i], SIGNAL(channelState(int, bool)), SLOT(loadSampleDialogue(int))); - - clearSampleButton[i] = new QChannelButton(mainGroupBox, "C", i); -// clearSampleButton[i]->setMinimumSize(SS_SAMPLE_CLEAR_WIDTH, SS_SAMPLE_CLEAR_HEIGHT); - clearSampleButton[i]->setToolTip("Clear sample on channel " + QString::number(i + 1)); - strip->addWidget(clearSampleButton[i]); - connect(clearSampleButton[i], SIGNAL(channelState(int, bool)), SLOT(clearSample(int))); + lastDir = ""; + connect(this->getGuiSignal(),SIGNAL(wakeup()),this,SLOT(readMessage())); - i++; - } - } - - // Right bottom panel: - QGroupBox* rbPanel= new QGroupBox(mainGroupBox); - mgbLayout->addWidget(rbPanel, 1, 3, 7, 1, Qt::AlignCenter); - QGridLayout* rbLayout = new QGridLayout(rbPanel); // 6, 1, 8, 5); - - openPluginsButton = new QPushButton("&Send Effects"); - openPluginsButton->setToolTip("Configure LADSPA send effects"); - connect(openPluginsButton, SIGNAL(clicked()), SLOT(openPluginButtonClicked())); - rbLayout->addWidget(openPluginsButton, 2, 1, Qt::AlignCenter | Qt::AlignVCenter); - rbLayout->setSpacing(0); - rbLayout->setMargin(0); - aboutButton = new QPushButton("About SimpleDrums"); - connect(aboutButton, SIGNAL(clicked()), SLOT(aboutButtonClicked())); -//TD rbLayout->addRowSpacing(3, 20); - rbLayout->addWidget(aboutButton, 4, 1, Qt::AlignLeft | Qt::AlignVCenter); - - - loadButton = new QPushButton(tr("&Load setup"), rbPanel); - connect(loadButton, SIGNAL(clicked()), SLOT(loadSetup())); - saveButton = new QPushButton(tr("&Save setup"), rbPanel); - connect(saveButton, SIGNAL(clicked()), SLOT(saveSetup())); - //rbLayout->addWidget(openPluginsButton, 1, 1, Qt::AlignCenter | Qt::AlignVCenter); -// rbLayout->addRowSpacing(2, 20); - rbLayout->addWidget(loadButton, 3, 1, Qt::AlignCenter | Qt::AlignVCenter); - rbLayout->addWidget(saveButton, 4, 1, Qt::AlignCenter | Qt::AlignVCenter); -// rbLayout->addRowSpacing(5, 20); - rbLayout->addWidget(aboutButton, 6, 1, Qt::AlignCenter | Qt::AlignVCenter); - - lastDir = ""; - //Connect socketnotifier to fifo - QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); - connect(s, SIGNAL(activated(int)), SLOT(readMessage(int))); - SS_TRACE_OUT - } + SS_TRACE_OUT +} /*! \fn SimpleSynthGui::~SimpleSynthGui() */ SimpleSynthGui::~SimpleSynthGui() - { - SS_TRACE_IN - simplesynthgui_ptr = 0; - delete pluginGui; - SS_TRACE_OUT - } +{ + SS_TRACE_IN + delete pluginGui; + SS_TRACE_OUT +} /*! - \fn SimpleSynthGui::readMessage(int) + \fn SimpleSynthGui::readMessage() */ -void SimpleSynthGui::readMessage(int) - { - MessGui::readMessage(); - } +void SimpleSynthGui::readMessage() +{ + MessGui::readMessage(); +} /*! \fn SimpleSynthGui::processEvent(const MusECore::MidiPlayEvent& ev) */ void SimpleSynthGui::processEvent(const MusECore::MidiPlayEvent& ev) - { - SS_TRACE_IN - if (SS_DEBUG_MIDI) { - printf("GUI received midi event\n"); - } - if (ev.type() == MusECore::ME_CONTROLLER) { - int id = ev.dataA(); - int val = ev.dataB(); - - // Channel controllers: - if (id >= SS_FIRST_CHANNEL_CONTROLLER && id <= SS_LAST_CHANNEL_CONTROLLER ) { - // Find out which channel we're dealing with: - id-= SS_FIRST_CHANNEL_CONTROLLER; - int ch = (id / SS_NR_OF_CHANNEL_CONTROLLERS); - id = (id % SS_NR_OF_CHANNEL_CONTROLLERS); - - int fxid = -1; - - if (SS_DEBUG_MIDI) { - printf("GUI received midi controller - id: %d val %d channel %d\n", id, val, ch); - } - - switch(id) { - case SS_CHANNEL_CTRL_VOLUME: - volumeSliders[ch]->blockSignals(true); - - ///volumeSliders[ch]->setValue(SS_VOLUME_MAX_VALUE - val); - volumeSliders[ch]->setValue(val); // p4.0.27 - - volumeSliders[ch]->blockSignals(false); - break; - - case SS_CHANNEL_CTRL_PAN: - panSliders[ch]->blockSignals(true); - panSliders[ch]->setValue(val); - panSliders[ch]->blockSignals(false); - break; - - case SS_CHANNEL_CTRL_NOFF: - nOffIgnore[ch]->blockSignals(true); - nOffIgnore[ch]->setChecked(val); - nOffIgnore[ch]->blockSignals(false); - break; - - case SS_CHANNEL_CTRL_ONOFF: - onOff[ch]->blockSignals(true); - onOff[ch]->setChecked(val); - onOff[ch]->blockSignals(false); - break; - - case SS_CHANNEL_SENDFX1: - case SS_CHANNEL_SENDFX2: - case SS_CHANNEL_SENDFX3: - case SS_CHANNEL_SENDFX4: - fxid = id - SS_CHANNEL_SENDFX1; - if (SS_DEBUG_MIDI) { - printf("SimpleSynthGui::processEvent - Channel sendfx, fxid: %d, val: %d\n", fxid, val); - } - sendFxDial[ch][fxid]->blockSignals(true); - sendFxDial[ch][fxid]->setValue(val); - sendFxDial[ch][fxid]->blockSignals(false); - break; - - default: - if (SS_DEBUG_MIDI) - printf("SimpleSynthGui::processEvent - unknown controller received: %d\n", id); - } - } - // Master controllers: - else if (id >= SS_FIRST_MASTER_CONTROLLER && id <= SS_LAST_MASTER_CONTROLLER) { - if (id == SS_MASTER_CTRL_VOLUME) { - masterSlider->blockSignals(true); - - ///masterSlider->setValue(SS_MASTERVOL_MAX_VALUE - val); - masterSlider->setValue(val); // p4.0.27 - - masterSlider->blockSignals(false); - } - } - else if (id>= SS_FIRST_PLUGIN_CONTROLLER && id <= SS_LAST_PLUGIN_CONTROLLER) { - int fxid = (id - SS_FIRST_PLUGIN_CONTROLLER) / SS_NR_OF_PLUGIN_CONTROLLERS; - int cmd = (id - SS_FIRST_PLUGIN_CONTROLLER) % SS_NR_OF_PLUGIN_CONTROLLERS; - - // Plugin return-gain: - if (cmd == SS_PLUGIN_RETURN) { - if (SS_DEBUG_MIDI) - printf("SimpleSynthGui::processEvent - fx retgain received: fxid: %d val: %d\n", fxid, val); - - SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)fxid); - pf->setRetGain(val); - } - // Plugin on/off: - else if (cmd == SS_PLUGIN_ONOFF) { // p4.0.27 - if (SS_DEBUG_MIDI) - printf("SimpleSynthGui::processEvent - fx onoff received: fxid: %d val: %d\n", fxid, val); - SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)fxid); - pf->setOnOff(val); - } - } +{ + SS_TRACE_IN + if (SS_DEBUG_MIDI) { + printf("GUI received midi event\n"); + } + if (ev.type() == MusECore::ME_CONTROLLER) { + int id = ev.dataA(); + int val = ev.dataB(); + + // Channel controllers: + if (id >= SS_FIRST_CHANNEL_CONTROLLER && id <= SS_LAST_CHANNEL_CONTROLLER ) { + // Find out which channel we're dealing with: + id-= SS_FIRST_CHANNEL_CONTROLLER; + int ch = (id / SS_NR_OF_CHANNEL_CONTROLLERS); + id = (id % SS_NR_OF_CHANNEL_CONTROLLERS); + + int fxid = -1; + + if (SS_DEBUG_MIDI) { + printf("GUI received midi controller - id: %d val %d channel %d\n", id, val, ch); + } + + switch(id) { + case SS_CHANNEL_CTRL_VOLUME: + volumeSliders[ch]->blockSignals(true); + volumeSliders[ch]->setValue(val); + volumeSliders[ch]->blockSignals(false); + break; + case SS_CHANNEL_CTRL_PITCH: + pitchKnobs[ch]->blockSignals(true); + pitchKnobs[ch]->setValue(-(val-63)); + pitchKnobs[ch]->blockSignals(false); + break; + + case SS_CHANNEL_CTRL_PAN: + panSliders[ch]->blockSignals(true); + panSliders[ch]->setValue(val); + panSliders[ch]->blockSignals(false); + break; + + case SS_CHANNEL_CTRL_NOFF: + nOffIgnore[ch]->blockSignals(true); + nOffIgnore[ch]->setChecked(val); + nOffIgnore[ch]->blockSignals(false); + break; + + case SS_CHANNEL_CTRL_ONOFF: + onOff[ch]->blockSignals(true); + onOff[ch]->setChecked(val); + onOff[ch]->blockSignals(false); + break; + + case SS_CHANNEL_CTRL_ROUTE: + chnRoutingCb[ch]->setCurrentIndex(val); + break; + + case SS_CHANNEL_SENDFX1: + case SS_CHANNEL_SENDFX2: + case SS_CHANNEL_SENDFX3: + case SS_CHANNEL_SENDFX4: + fxid = id - SS_CHANNEL_SENDFX1; + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui::processEvent - Channel sendfx, fxid: %d, val: %d\n", fxid, val); } - // - // Sysexes: - // - else if (ev.type() == MusECore::ME_SYSEX) { - byte* data = ev.data(); - //byte* data = d + 2; - int cmd = *data; - switch (cmd) { - case SS_SYSEX_LOAD_SAMPLE_OK: { - int ch = *(data+1); - QString filename = (const char*) (data+2); - sampleNameLineEdit[ch]->setText(filename.section('/',-1,-1)); - if (SS_DEBUG_MIDI) { - printf("SimpleSynthGui - sample %s loaded OK on channel: %d\n", filename.toLatin1().constData(), ch); - } - if (!onOff[ch]->isChecked()) { - onOff[ch]->blockSignals(true); - onOff[ch]->setChecked(true); - onOff[ch]->blockSignals(false); - channelOnOff(ch, true); - } - break; - } - - case SS_SYSEX_LOAD_SAMPLE_ERROR: { - //int ch = *(data+1); - const char* filename = (const char*) (data+2); - /*QMessageBox* yn = new QMessageBox("Sample not found", "Failed to load sample: " + QString(filename) + "\n" + - "Do you want to open file browser and try to locate it elsewhere?", - QMessageBox::Warning, - QMessageBox::Yes, - QMessageBox::No, - QMessageBox::NoButton, - this);*/ - /*int res = QMessageBox::warning(this, - "SimpleDrums","Failed to load sample: " + QString(filename) + "\n" + - "Do you want to open file browser and try to locate it elsewhere?", - "&Yes", "&No"); - */ - //int res = yn->exec(); - printf("Error: Sample %s not found! TODO: Fix this\n", filename); - //if (res == 0) { - // loadSampleDialogue(ch); - // } - break; - } - - case SS_SYSEX_LOAD_SENDEFFECT_OK: { - if (SS_DEBUG_MIDI) { - printf("SimpleSynthGui - sysex load sendeffect OK on fxid: %d\n", *(data+1)); - } - int fxid = *(data+1); - SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)fxid); - ///pf->updatePluginValue(*(data+2)); - pf->updatePluginValue( *((unsigned*)(data+2)) ); // p4.0.27 - break; - } - - case SS_SYSEX_CLEAR_SENDEFFECT_OK: { - if (SS_DEBUG_MIDI) { - printf("SimpleSynthGui - sysex clear sendeffect OK on fxid: %d\n", *(data+1)); - } - SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)*(data+1)); - pf->clearPluginDisplay(); - break; - } - - case SS_SYSEX_CLEAR_SAMPLE_OK: { - if (SS_DEBUG_MIDI) { - printf("SimpleSynthGui - sysex clear samle OK on channel: %d\n", *(data+1)); - } - byte ch = *(data+1); - sampleNameLineEdit[ch]->setText(""); - break; - } - - case SS_SYSEX_SET_PLUGIN_PARAMETER_OK: { - if (SS_DEBUG_MIDI) { - printf("SimpleSynthGui - plugin parameter OK on fxid: %d\n", *(data+1)); - } - SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)*(data+1)); - int param = *(data+2); - int val = *(data+3); - pf->blockSignals(true); - pf->setParameterValue(param, val); - pf->blockSignals(false); - break; - } + sendFxDial[ch][fxid]->blockSignals(true); + sendFxDial[ch][fxid]->setValue(val); + sendFxDial[ch][fxid]->blockSignals(false); + break; + + default: + if (SS_DEBUG_MIDI) + printf("SimpleSynthGui::processEvent - unknown controller received: %d\n", id); + } + } + // Master controllers: + else if (id >= SS_FIRST_MASTER_CONTROLLER && id <= SS_LAST_MASTER_CONTROLLER) { + if (id == SS_MASTER_CTRL_VOLUME) { + masterSlider->blockSignals(true); + + ///masterSlider->setValue(SS_MASTERVOL_MAX_VALUE - val); + masterSlider->setValue(val); // p4.0.27 + + masterSlider->blockSignals(false); + } + } + else if (id>= SS_FIRST_PLUGIN_CONTROLLER && id <= SS_LAST_PLUGIN_CONTROLLER) { + int fxid = (id - SS_FIRST_PLUGIN_CONTROLLER) / SS_NR_OF_PLUGIN_CONTROLLERS; + int cmd = (id - SS_FIRST_PLUGIN_CONTROLLER) % SS_NR_OF_PLUGIN_CONTROLLERS; + + // Plugin return-gain: + if (cmd == SS_PLUGIN_RETURN) { + if (SS_DEBUG_MIDI) + printf("SimpleSynthGui::processEvent - fx retgain received: fxid: %d val: %d\n", fxid, val); + + SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)fxid); + pf->setRetGain(val); + } + // Plugin on/off: + else if (cmd == SS_PLUGIN_ONOFF) { // p4.0.27 + if (SS_DEBUG_MIDI) + printf("SimpleSynthGui::processEvent - fx onoff received: fxid: %d val: %d\n", fxid, val); + SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)fxid); + pf->setOnOff(val); + } + } + } + // + // Sysexes: + // + else if (ev.type() == MusECore::ME_SYSEX) { + byte* data = ev.data(); + //byte* data = d + 2; + int cmd = *data; + switch (cmd) { + case SS_SYSEX_LOAD_SAMPLE_OK: { + int ch = *(data+1); + QString filename = (const char*) (data+2); + sampleNameLineEdit[ch]->setText(filename.section('/',-1,-1)); + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - sample %s loaded OK on channel: %d\n", filename.toLatin1().constData(), ch); + } + if (!onOff[ch]->isChecked()) { + onOff[ch]->blockSignals(true); + onOff[ch]->setChecked(true); + onOff[ch]->blockSignals(false); + channelOnOff(ch, true); + } + break; + } + + case SS_SYSEX_LOAD_SAMPLE_ERROR: { + const char* filename = (const char*) (data+2); + printf("Error: Sample %s not found! TODO: Fix this\n", filename); + break; + } + + case SS_SYSEX_LOAD_SENDEFFECT_OK: { + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - sysex load sendeffect OK on fxid: %d\n", *(data+1)); + } + int fxid = *(data+1); + SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)fxid); + ///pf->updatePluginValue(*(data+2)); + pf->updatePluginValue( *((MusESimplePlugin::PluginI**)(data+2)) ); + break; + } + + case SS_SYSEX_CLEAR_SENDEFFECT_OK: { + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - sysex clear sendeffect OK on fxid: %d\n", *(data+1)); + } + SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)*(data+1)); + pf->clearPluginDisplay(); + break; + } + + case SS_SYSEX_CLEAR_SAMPLE_OK: { + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - sysex clear samle OK on channel: %d\n", *(data+1)); + } + byte ch = *(data+1); + sampleNameLineEdit[ch]->setText(""); + break; + } + + case SS_SYSEX_SET_PLUGIN_PARAMETER_OK: { + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui - plugin parameter OK on fxid: %d\n", *(data+1)); + } + SS_PluginFront* pf = pluginGui->getPluginFront((unsigned)*(data+1)); + int param = *(data+2); + int val = *(data+3); + pf->blockSignals(true); + pf->setParameterValue(param, val); + pf->blockSignals(false); + break; + } - case SS_SYSEX_SEND_INIT_DATA: { -// FN: TODO + case SS_SYSEX_SEND_INIT_DATA: { + // FN: TODO #if 1 - const unsigned initdata_len = ev.len() - 1; - byte* init_data = (data + 1); - QFileInfo fileInfo = QFileInfo(lastSavedProject); - - lastProjectDir = fileInfo.path(); - if (fileInfo.suffix() != "sds" && fileInfo.suffix() != "SDS") { - lastSavedProject += ".sds"; - fileInfo = QFileInfo(lastSavedProject); - } - QFile theFile(fileInfo.filePath()); - - // Write data - if (theFile.open(QIODevice::WriteOnly)) { - theFile.write((const char*)&initdata_len, sizeof(initdata_len)); // First write length - if (theFile.write((const char*)init_data, initdata_len) == -1) { - // Fatal error writing - QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning, "SimpleDrums error Dialog", "Fatal error when writing to file. Setup not saved.", - QMessageBox::Ok, this); - msgBox->exec(); - delete msgBox; - } - theFile.close(); - } - else { - // An error occured when opening - QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning, "SimpleDrums error Dialog", "Error opening file. Setup was not saved.", - QMessageBox::Ok, this); - msgBox->exec(); - delete msgBox; - } + const unsigned initdata_len = ev.len() - 1; + byte* init_data = (data + 1); + QFileInfo fileInfo = QFileInfo(lastSavedProject); + + lastProjectDir = fileInfo.path(); + if (fileInfo.suffix() != "sds" && fileInfo.suffix() != "SDS") { + lastSavedProject += ".sds"; + fileInfo = QFileInfo(lastSavedProject); + } + QFile theFile(fileInfo.filePath()); + + // Write data + if (theFile.open(QIODevice::WriteOnly)) { + theFile.write((const char*)&initdata_len, sizeof(initdata_len)); // First write length + if (theFile.write((const char*)init_data, initdata_len) == -1) { + // Fatal error writing + QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning, "SimpleDrums error Dialog", "Fatal error when writing to file. Setup not saved.", + QMessageBox::Ok, this); + msgBox->exec(); + delete msgBox; + } + theFile.close(); + } + else { + // An error occurred when opening + QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning, "SimpleDrums error Dialog", "Error opening file. Setup was not saved.", + QMessageBox::Ok, this); + msgBox->exec(); + delete msgBox; + } #endif - break; - } + break; + } - default: - if (SS_DEBUG_MIDI) { - printf("SimpleSynthGui::processEvent - unknown sysex cmd received: %d\n", cmd); - } - break; - } - } - SS_TRACE_OUT + default: + if (SS_DEBUG_MIDI) { + printf("SimpleSynthGui::processEvent - unknown sysex cmd received: %d\n", cmd); + } + break; } + } + SS_TRACE_OUT +} /*! \fn SimpleSynthGui::volumeChanged(int val) */ void SimpleSynthGui::volumeChanged(int channel, int val) - { - setChannelVolume(channel, val); - } +{ + setChannelVolume(channel, val); +} + +/*! + \fn SimpleSynthGui::pitchChanged(int val) + */ +void SimpleSynthGui::pitchChanged(int channel, int, int val) +{ + printf("Gui::pitchChanged %d %d\n", channel, val); + setChannelPitch(channel, -val+63); +} /*! \fn SimpleSynthGui::panChanged(int channel, int value) */ void SimpleSynthGui::panChanged(int channel, int value) - { - sendController(0, SS_CHANNEL_PAN_CONTROLLER(channel), value); - } +{ + sendController(0, SS_CHANNEL_PAN_CONTROLLER(channel), value); +} /*! \fn SimpleSynthGui::channelOnOff(int channel, bool state) */ void SimpleSynthGui::channelOnOff(int channel, bool state) - { - sendController(0, SS_CHANNEL_ONOFF_CONTROLLER(channel), state); - } +{ + sendController(0, SS_CHANNEL_ONOFF_CONTROLLER(channel), state); +} /*! \fn SimpleSynthGui::channelNoteOffIgnore(bool state) */ void SimpleSynthGui::channelNoteOffIgnore(int channel, bool state) - { - sendController(0, SS_CHANNEL_NOFF_CONTROLLER(channel), (int) state); - } +{ + sendController(0, SS_CHANNEL_NOFF_CONTROLLER(channel), (int) state); +} /*! \fn SimpleSynthGui::sendFxChanged(int ch, int fxid, int val) */ void SimpleSynthGui::sendFxChanged(int ch, int fxid, int val) - { - sendController(0, SS_CHANNEL_SENDFX_CONTROLLER(ch, fxid), (int) val); - } +{ + sendController(0, SS_CHANNEL_SENDFX_CONTROLLER(ch, fxid), (int) val); +} /*! \fn SimpleSynthGui::masterVolChanged(int val) */ void SimpleSynthGui::masterVolChanged(int val) - { - sendController(0, SS_MASTER_CTRL_VOLUME, val); - } +{ + sendController(0, SS_MASTER_CTRL_VOLUME, val); +} /*! \fn SimpleSynthGui::setChannelVolume(int channel, byte volume) */ void SimpleSynthGui::setChannelVolume(int channel, int volume) - { - //volumeSliders[channel]->setValue(SS_VOLUME_MAX_VALUE - volume); - sendController(0, SS_CHANNEL_VOLUME_CONTROLLER(channel), (int)volume); - } +{ + //volumeSliders[channel]->setValue(SS_VOLUME_MAX_VALUE - volume); + sendController(0, SS_CHANNEL_VOLUME_CONTROLLER(channel), (int)volume); +} + +/*! + \fn SimpleSynthGui::setChannelPitch(int channel, byte pitch) + */ +void SimpleSynthGui::setChannelPitch(int channel, int pitch) +{ + sendController(0, SS_CHANNEL_PITCH_CONTROLLER(channel), (int)pitch); +} + +void SimpleSynthGui::setChannelRoute(int channel, int route) +{ + sendController(0, SS_CHANNEL_ROUTE_CONTROLLER(channel), (int)route); +} /*! \fn SimpleSynthGui::loadSampleDialogue(int channel) */ void SimpleSynthGui::loadSampleDialogue(int channel) - { - QString filename = - QFileDialog::getOpenFileName( - this, - tr("Load sample dialog"), - lastDir, - QString("Samples *.wav *.ogg *.flac (*.wav *.WAV *.ogg *.flac);;All files (*)")); - - if (filename != QString::null) { - QFileInfo fi(filename); - lastDir = fi.path(); - - if (SS_DEBUG) - printf("lastDir = %s\n", lastDir.toLatin1().constData()); - - //int l = filename.length() + 4; - int l = filename.length() + 6; - byte d[l]; - - //d[0] = SS_SYSEX_LOAD_SAMPLE; - //d[1] = (byte) channel; - //d[2] = (byte) filename.length(); - d[0] = MUSE_SYNTH_SYSEX_MFG_ID; - d[1] = SIMPLEDRUMS_UNIQUE_ID; - d[2] = SS_SYSEX_LOAD_SAMPLE; - d[3] = (byte) channel; - d[4] = (byte) filename.length(); - //memcpy(d+3, filename.toLatin1().constData(), filename.length()+1); - memcpy(d+5, filename.toLatin1().constData(), filename.length()+1); - sendSysex(d, l); - } - } +{ + MusECore::AudioPreviewDialog dlg(this, sampleRate()); + dlg.setWindowTitle(tr("Load sample dialog")); + dlg.setDirectory(lastDir); + if(dlg.exec() == QFileDialog::Rejected) + { + return; + } + + QStringList filenames = dlg.selectedFiles(); + if(filenames.size() < 1) + { + return; + } + QString filename = filenames [0]; + /*dlg.getOpenFileName(this, + tr("Load sample dialog"), + lastDir, + QString("Samples *.wav *.ogg *.flac (*.wav *.WAV *.ogg *.flac);;All files (*)")); + + QString filename = + QFileDialog::getOpenFileName( + this, + tr("Load sample dialog"), + lastDir, + QString("Samples *.wav *.ogg *.flac (*.wav *.WAV *.ogg *.flac);;All files (*)")); +*/ + if (filename != QString::null) { + QFileInfo fi(filename); + lastDir = fi.path(); + + if (SS_DEBUG) + printf("lastDir = %s\n", lastDir.toLatin1().constData()); + + //int l = filename.length() + 4; + int l = filename.length() + 6; + byte d[l]; + + //d[0] = SS_SYSEX_LOAD_SAMPLE; + //d[1] = (byte) channel; + //d[2] = (byte) filename.length(); + d[0] = MUSE_SYNTH_SYSEX_MFG_ID; + d[1] = SIMPLEDRUMS_UNIQUE_ID; + d[2] = SS_SYSEX_LOAD_SAMPLE; + d[3] = (byte) channel; + d[4] = (byte) filename.length(); + memcpy(d+5, filename.toLatin1().constData(), filename.length()+1); + sendSysex(d, l); + } +} @@ -859,136 +878,134 @@ \fn SimpleSynthGui::clearSample(int ch) */ void SimpleSynthGui::clearSample(int ch) - { - if (sampleNameLineEdit[ch]->text().length() > 0) { //OK, we've got a live one here - //byte d[2]; - byte d[4]; - //d[0] = SS_SYSEX_CLEAR_SAMPLE; - //d[1] = (byte) ch; - d[0] = MUSE_SYNTH_SYSEX_MFG_ID; - d[1] = SIMPLEDRUMS_UNIQUE_ID; - d[2] = SS_SYSEX_CLEAR_SAMPLE; - d[3] = (byte) ch; - //sendSysex(d, 2); - sendSysex(d, 4); - sampleNameLineEdit[ch]->setText(""); - } - } +{ + if (sampleNameLineEdit[ch]->text().length() > 0) { //OK, we've got a live one here + //byte d[2]; + byte d[4]; + //d[0] = SS_SYSEX_CLEAR_SAMPLE; + //d[1] = (byte) ch; + d[0] = MUSE_SYNTH_SYSEX_MFG_ID; + d[1] = SIMPLEDRUMS_UNIQUE_ID; + d[2] = SS_SYSEX_CLEAR_SAMPLE; + d[3] = (byte) ch; + //sendSysex(d, 2); + sendSysex(d, 4); + sampleNameLineEdit[ch]->setText(""); + } +} /*! \fn SimpleSynthGui::displayPluginGui() */ void SimpleSynthGui::displayPluginGui() - { - pluginGui->show(); - } +{ + pluginGui->show(); +} /*! \fn SimpleSynthGui::loadEffectInvoked(int fxid, QString lib, QString label) */ void SimpleSynthGui::loadEffectInvoked(int fxid, QString lib, QString label) - { - //int l = 4 + lib.length() + label.length(); - int l = 6 + lib.length() + label.length(); - byte d[l]; - //d[0] = SS_SYSEX_LOAD_SENDEFFECT; - //d[1] = (byte) fxid; - d[0] = MUSE_SYNTH_SYSEX_MFG_ID; - d[1] = SIMPLEDRUMS_UNIQUE_ID; - d[2] = SS_SYSEX_LOAD_SENDEFFECT; - d[3] = (byte) fxid; - //memcpy (d+2, lib.toLatin1().constData(), lib.length()+1); - //memcpy (d+3+lib.length(), label.toLatin1().constData(), label.length()+1); - memcpy (d+4, lib.toLatin1().constData(), lib.length()+1); - memcpy (d+5+lib.length(), label.toLatin1().constData(), label.length()+1); - sendSysex(d, l); - } +{ + //int l = 4 + lib.length() + label.length(); + int l = 6 + lib.length() + label.length(); + byte d[l]; + //d[0] = SS_SYSEX_LOAD_SENDEFFECT; + //d[1] = (byte) fxid; + d[0] = MUSE_SYNTH_SYSEX_MFG_ID; + d[1] = SIMPLEDRUMS_UNIQUE_ID; + d[2] = SS_SYSEX_LOAD_SENDEFFECT; + d[3] = (byte) fxid; + memcpy (d+4, lib.toLatin1().constData(), lib.length()+1); + memcpy (d+5+lib.length(), label.toLatin1().constData(), label.length()+1); + sendSysex(d, l); +} /*! \fn SimpleSynthGui::returnLevelChanged(int fxid, int val) */ void SimpleSynthGui::returnLevelChanged(int fxid, int val) - { - sendController(0, SS_PLUGIN_RETURNLEVEL_CONTROLLER(fxid), val); - } +{ + sendController(0, SS_PLUGIN_RETURNLEVEL_CONTROLLER(fxid), val); +} /*! \fn SimpleSynthGui::toggleEffectOnOff(int fxid, int state) */ void SimpleSynthGui::toggleEffectOnOff(int fxid, int state) - { - sendController(0, SS_PLUGIN_ONOFF_CONTROLLER(fxid), state); - } +{ + sendController(0, SS_PLUGIN_ONOFF_CONTROLLER(fxid), state); +} /*! \fn SimpleSynthGui::clearPlugin(int fxid) */ void SimpleSynthGui::clearPlugin(int fxid) - { - //byte d[2]; - byte d[4]; - //d[0] = SS_SYSEX_CLEAR_SENDEFFECT; - //d[1] = fxid; - d[0] = MUSE_SYNTH_SYSEX_MFG_ID; - d[1] = SIMPLEDRUMS_UNIQUE_ID; - d[2] = SS_SYSEX_CLEAR_SENDEFFECT; - d[3] = fxid; - //sendSysex(d, 2); - sendSysex(d, 4); - } +{ + //byte d[2]; + byte d[4]; + //d[0] = SS_SYSEX_CLEAR_SENDEFFECT; + //d[1] = fxid; + d[0] = MUSE_SYNTH_SYSEX_MFG_ID; + d[1] = SIMPLEDRUMS_UNIQUE_ID; + d[2] = SS_SYSEX_CLEAR_SENDEFFECT; + d[3] = fxid; + //sendSysex(d, 2); + sendSysex(d, 4); +} /*! \fn SimpleSynthGui::effectParameterChanged(int fxid, int parameter, int val) */ void SimpleSynthGui::effectParameterChanged(int fxid, int parameter, int val) - { - //int len = 4; - int len = 6; - byte d[len]; - //d[0] = SS_SYSEX_SET_PLUGIN_PARAMETER; - //d[1] = (byte) fxid; - //d[2] = (byte) parameter; - //d[3] = (byte) val; - d[0] = MUSE_SYNTH_SYSEX_MFG_ID; - d[1] = SIMPLEDRUMS_UNIQUE_ID; - d[2] = SS_SYSEX_SET_PLUGIN_PARAMETER; - d[3] = (byte) fxid; - d[4] = (byte) parameter; - d[5] = (byte) val; - sendSysex(d, len); - } +{ + //int len = 4; + int len = 6; + byte d[len]; + //d[0] = SS_SYSEX_SET_PLUGIN_PARAMETER; + //d[1] = (byte) fxid; + //d[2] = (byte) parameter; + //d[3] = (byte) val; + d[0] = MUSE_SYNTH_SYSEX_MFG_ID; + d[1] = SIMPLEDRUMS_UNIQUE_ID; + d[2] = SS_SYSEX_SET_PLUGIN_PARAMETER; + d[3] = (byte) fxid; + d[4] = (byte) parameter; + d[5] = (byte) val; + sendSysex(d, len); +} /*! \fn SimpleSynthGui::openPluginButtonClicked() */ void SimpleSynthGui::openPluginButtonClicked() - { - if (pluginGui->isVisible()) - pluginGui->raise(); - else - displayPluginGui(); - } +{ + if (pluginGui->isVisible()) + pluginGui->raise(); + else + displayPluginGui(); +} /*! \fn SimpleSynthGui::aboutButtonClicked() */ void SimpleSynthGui::aboutButtonClicked() - { - QString caption = "SimpleDrums ver"; - caption+= SS_VERSIONSTRING; - ///QString text = caption + "\n\n(C) Copyright 2000-2004 Mathias Lundgren (lunar_shuttle@users.sf.net), Werner Schweer\nPublished under the GNU Public License"; - QString text = caption + "\n\n(C) Copyright 2000-2004 Mathias Lundgren (lunar_shuttle@users.sf.net), Werner Schweer\n" - "Fixes/mods: (C) Copyright 2011 Tim E. Real (terminator356@users.sf.net)\nPublished under the GNU Public License"; - QMessageBox* msgBox = new QMessageBox(caption, text, QMessageBox::NoIcon, - QMessageBox::Ok, Qt::NoButton, Qt::NoButton, this); - msgBox->exec(); - } +{ + QString caption = "SimpleDrums ver"; + caption+= SS_VERSIONSTRING; + ///QString text = caption + "\n\n(C) Copyright 2000-2004 Mathias Lundgren (lunar_shuttle@users.sf.net), Werner Schweer\nPublished under the GNU Public License"; + QString text = caption + "\n\n(C) Copyright 2000-2004 Mathias Lundgren (lunar_shuttle@users.sf.net), Werner Schweer\n" + "Fixes/mods: (C) Copyright 2011 Tim E. Real (terminator356@users.sf.net)\nPublished under the GNU Public License"; + QMessageBox* msgBox = new QMessageBox(caption, text, QMessageBox::NoIcon, + QMessageBox::Ok, Qt::NoButton, Qt::NoButton, this); + msgBox->exec(); +} /*! @@ -996,42 +1013,42 @@ \brief Load setup from file */ void SimpleSynthGui::loadSetup() - { - bool success = true; - QString filename = - QFileDialog::getOpenFileName(this, "Load setup dialog", lastProjectDir, - QString("*.sds *.SDS")); - - if (filename != QString::null) { - QFile theFile(filename); - if (theFile.open(QIODevice::ReadOnly)) { - unsigned initdata_len = 0; - if (theFile.read((char*)&initdata_len, sizeof(initdata_len)) == -1) - success = false; - - ///byte* init_data = new byte[initdata_len]; - byte* init_data = new byte[initdata_len + 2]; // 2 for MFG ID and synth ID. - init_data[0] = MUSE_SYNTH_SYSEX_MFG_ID; - init_data[1] = SIMPLEDRUMS_UNIQUE_ID; - //if (theFile.read((char*)(init_data), initdata_len) == -1) - if (theFile.read((char*)(init_data + 2), initdata_len) == -1) - success = false; - - if (!success) { - QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning, "SimpleDrums Error Dialog", "Error opening/reading from file. Setup not loaded.", - QMessageBox::Ok, this); - msgBox->exec(); - delete msgBox; - } - else { - ///sendSysex(init_data, initdata_len); - sendSysex(init_data, initdata_len + 2); - } +{ + bool success = true; + QString filename = + QFileDialog::getOpenFileName(this, "Load setup dialog", lastProjectDir, + QString("*.sds *.SDS")); + + if (filename != QString::null) { + QFile theFile(filename); + if (theFile.open(QIODevice::ReadOnly)) { + unsigned initdata_len = 0; + if (theFile.read((char*)&initdata_len, sizeof(initdata_len)) == -1) + success = false; + + ///byte* init_data = new byte[initdata_len]; + byte* init_data = new byte[initdata_len + 2]; // 2 for MFG ID and synth ID. + init_data[0] = MUSE_SYNTH_SYSEX_MFG_ID; + init_data[1] = SIMPLEDRUMS_UNIQUE_ID; + //if (theFile.read((char*)(init_data), initdata_len) == -1) + if (theFile.read((char*)(init_data + 2), initdata_len) == -1) + success = false; + + if (!success) { + QMessageBox* msgBox = new QMessageBox(QMessageBox::Warning, "SimpleDrums Error Dialog", "Error opening/reading from file. Setup not loaded.", + QMessageBox::Ok, this); + msgBox->exec(); + delete msgBox; + } + else { + ///sendSysex(init_data, initdata_len); + sendSysex(init_data, initdata_len + 2); + } - delete[] init_data; - } - } + delete[] init_data; } + } +} /*! @@ -1039,21 +1056,52 @@ \brief Save setup to file */ void SimpleSynthGui::saveSetup() - { - QString filename = - QFileDialog::getSaveFileName(this, "Save setup dialog", lastProjectDir, - QString("*.sds *.SDS")); - - if (filename != QString::null) { - lastSavedProject = filename; - //byte d[1]; - byte d[3]; - //d[0] = SS_SYSEX_GET_INIT_DATA; - d[0] = MUSE_SYNTH_SYSEX_MFG_ID; - d[1] = SIMPLEDRUMS_UNIQUE_ID; - d[2] = SS_SYSEX_GET_INIT_DATA; - //sendSysex(d, 1); // Makes synth send gui initdata, where rest of the saving takes place - sendSysex(d, 3); // Makes synth send gui initdata, where rest of the saving takes place - } - } +{ + QString filename = + QFileDialog::getSaveFileName(this, "Save setup dialog", lastProjectDir, + QString("*.sds *.SDS")); + + if (filename != QString::null) { + lastSavedProject = filename; + //byte d[1]; + byte d[3]; + //d[0] = SS_SYSEX_GET_INIT_DATA; + d[0] = MUSE_SYNTH_SYSEX_MFG_ID; + d[1] = SIMPLEDRUMS_UNIQUE_ID; + d[2] = SS_SYSEX_GET_INIT_DATA; + //sendSysex(d, 1); // Makes synth send gui initdata, where rest of the saving takes place + sendSysex(d, 3); // Makes synth send gui initdata, where rest of the saving takes place + } +} + +void SimpleSynthGui::routeChanged(int index) +{ + QObject *obj = sender(); + int ch = -1; + for(int i = 0; i < SS_NR_OF_CHANNELS; i++) + { + if(chnRoutingCb [i] == obj) + { + ch = i; + break; + } + } + if(ch == -1) + { + return; + } + + fprintf(stderr, "SS: change routing: chn %d -> %d\n", ch, index); + + setChannelRoute(ch, index); + +} + +void SimpleSynthGui::heartBeat() +{ + for(int i = 0; i < SS_NR_OF_CHANNELS; i++){ + chnMeter[i]->setVal(meterVal[i], peakVal[i], false); + //chnMeter[i]->update(); + } +} diff -Nru muse-2.1.2/synti/simpledrums2/simpledrumsgui.h muse-3.0.2+ds1/synti/simpledrums2/simpledrumsgui.h --- muse-2.1.2/synti/simpledrums2/simpledrumsgui.h 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/simpledrumsgui.h 2018-01-29 20:07:03.000000000 +0000 @@ -34,6 +34,8 @@ //#include #include #include +#include +#include #include "libsynti/gui.h" #include "ui_simpledrumsguibase.h" @@ -154,6 +156,7 @@ signals: void valueChanged(int channel, int fxid, int val); + void sliderMoved(int channel, int val); ///public slots: /// virtual void setValue(int val); @@ -161,7 +164,9 @@ protected: int channel; int sendfxid; - virtual void sliderChange(SliderChange change); + virtual void sliderChange(SliderChange change); + private slots: + void forwardSliderMoved(); }; //-------------------------------------- @@ -174,13 +179,14 @@ // MESS interface: virtual void processEvent(const MusECore::MidiPlayEvent& ev); void setChannelVolume(int channel, int volume); + void setChannelPitch(int channel, int volume); + void setChannelRoute(int channel, int route); void displayPluginGui(); QGroupBox* channelButtonGroups[SS_NR_OF_CHANNELS]; - QGroupBox* masterButtonGroup; - QGroupBox* mainGroupBox; ///QInvertedChannelSlider* volumeSliders[SS_NR_OF_CHANNELS]; QChannelSlider* volumeSliders[SS_NR_OF_CHANNELS]; // p4.0.27 Tim. Inverted not correct. Was WIP? + QChannelDial* pitchKnobs[SS_NR_OF_CHANNELS]; QChannelSlider* panSliders[SS_NR_OF_CHANNELS]; QChannelCheckbox* onOff[SS_NR_OF_CHANNELS]; @@ -200,40 +206,57 @@ QPushButton* loadButton; QPushButton* saveButton; + QComboBox* chnRoutingCb[SS_NR_OF_CHANNELS]; + MusEGui::Meter* chnMeter[SS_NR_OF_CHANNELS]; + double meterVal[SS_NR_OF_CHANNELS]; + double peakVal[SS_NR_OF_CHANNELS]; + QString lastDir; QString lastSavedProject; QString lastProjectDir; SS_PluginGui* pluginGui; + int _sampleRate; + public: - SimpleSynthGui(); + SimpleSynthGui(int sampleRate); virtual ~SimpleSynthGui(); + int sampleRate() const { return _sampleRate; } + // Returns true if the value was changed. + bool setSampleRate(int sampleRate) { + bool r = _sampleRate != sampleRate; + _sampleRate = sampleRate; + return r; + } + public slots: void loadEffectInvoked(int fxid, QString lib, QString label); void returnLevelChanged(int fxid, int val); void toggleEffectOnOff(int fxid, int state); void clearPlugin(int fxid); void effectParameterChanged(int fxid, int parameter, int val); + void heartBeat(); private slots: void volumeChanged(int channel, int val); + void pitchChanged(int channel, int, int val); void panChanged(int channel, int value); void channelOnOff(int channel, bool state); void channelNoteOffIgnore(int channel, bool state); void masterVolChanged(int val); void loadSampleDialogue(int channel); - void readMessage(int); + void readMessage(); void clearSample(int ch); void sendFxChanged(int ch, int fxid, int val); void openPluginButtonClicked(); void aboutButtonClicked(); void loadSetup(); void saveSetup(); + void routeChanged(int index); + friend class SimpleSynth; }; -extern SimpleSynthGui* simplesynthgui_ptr; - #endif diff -Nru muse-2.1.2/synti/simpledrums2/simpledrums.h muse-3.0.2+ds1/synti/simpledrums2/simpledrums.h --- muse-2.1.2/synti/simpledrums2/simpledrums.h 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/simpledrums.h 2018-01-29 20:07:03.000000000 +0000 @@ -30,171 +30,189 @@ #include "libsynti/mess.h" #include "common.h" #include "common_defs.h" -//#include "libsynti/mpevent.h" #include "muse/mpevent.h" #include "simpledrumsgui.h" -#include "ssplugin.h" +#include "libsimpleplugin/simpler_plugin.h" #define SS_NO_SAMPLE 0 #define SS_NO_PLUGIN 0 -#define SS_PROCESS_BUFFER_SIZE 4096 //TODO: Add initialization method for nr of frames in each process from MusE - if nr of frames > than this, this will fail +#define SS_PROCESS_BUFFER_SIZE SS_segmentSize #define SS_SENDFX_BUFFER_SIZE SS_PROCESS_BUFFER_SIZE enum SS_ChannelState - { - SS_CHANNEL_INACTIVE=0, - SS_SAMPLE_PLAYING, - }; +{ + SS_CHANNEL_INACTIVE=0, + SS_SAMPLE_PLAYING +}; enum SS_State - { - SS_INITIALIZING=0, - SS_LOADING_SAMPLE, - SS_CLEARING_SAMPLE, - SS_RUNNING, - }; +{ + SS_INITIALIZING=0, + SS_LOADING_SAMPLE, + SS_CLEARING_SAMPLE, + SS_RUNNING +}; enum SS_SendFXState - { - SS_SENDFX_OFF=0, - SS_SENDFX_ON - }; +{ + SS_SENDFX_OFF=0, + SS_SENDFX_ON +}; struct SS_SendFx - { - SS_SendFXState state; - LadspaPlugin* plugin; - int inputs; - int outputs; - int retgain_ctrlval; - double retgain; - int nrofparameters; - }; +{ + SS_SendFXState state; + MusESimplePlugin::PluginI* plugin; + int inputs; + int outputs; + int retgain_ctrlval; + double retgain; + int nrofparameters; +}; struct SS_Sample - { - float* data; - int samplerate; - int bits; - std::string filename; - long samples; - long frames; - int channels; - SF_INFO sfinfo; - }; +{ + SS_Sample() { data = 0; } + float* data; + int samplerate; + //int bits; + std::string filename; + long samples; + long frames; + int channels; + //SF_INFO sfinfo; +}; + +enum SS_ChannelRoute +{ + SS_CHN_ROUTE_MIX = 0, + SS_CHN_ROUTE_CHN, +}; struct SS_Channel - { - SS_ChannelState state; - const char* name; - SS_Sample* sample; - int playoffset; - bool noteoff_ignore; - - double volume; - int volume_ctrlval; - - double cur_velo; - double gain_factor; - - int pan; - double balanceFactorL; - double balanceFactorR; - - bool channel_on; - - //Send fx: - double sendfxlevel[SS_NR_OF_SENDEFFECTS]; - }; +{ + SS_ChannelState state; + const char* name; + SS_Sample* sample; + SS_Sample* originalSample; + int playoffset; + bool noteoff_ignore; + + double volume; + int volume_ctrlval; + + double cur_velo; + double gain_factor; + + int pan; + double balanceFactorL; + double balanceFactorR; + int pitchInt; + + bool channel_on; + + SS_ChannelRoute route; + + //Send fx: + double sendfxlevel[SS_NR_OF_SENDEFFECTS]; +}; struct SS_Controller - { - std::string name; - int num; - int min, max; - }; +{ + std::string name; + int num; + int min, max; +}; -struct SS_SampleLoader - { - SS_Channel* channel; - std::string filename; - int ch_no; - }; +double rangeToPitch(int value); +//int pitchToRange(double pitch); class SimpleSynth : public Mess - { - public: - SimpleSynth(int); - - virtual ~SimpleSynth(); - - //virtual bool guiVisible() const; - //virtual bool hasGui() const; - virtual bool nativeGuiVisible() const; - virtual bool hasNativeGui() const; - virtual bool playNote(int arg1, int arg2, int arg3); - virtual bool processEvent(const MusECore::MidiPlayEvent& arg1); - virtual bool setController(int arg1, int arg2, int arg3); - virtual bool sysex(int arg1, const unsigned char* arg2); - virtual QString getPatchName(int arg1, int arg2, bool arg3) const; - virtual const MidiPatch* getPatchInfo(int arg1, const MidiPatch* arg2) const; - virtual int getControllerInfo(int arg1, const char** arg2, int* arg3, int* arg4, int* arg5, int* arg6) const; - virtual void processMessages(); - virtual void process(float** data, int offset, int len); - //virtual void showGui(bool arg1); - virtual void showNativeGui(bool arg1); - ///virtual void getInitData(int*, const unsigned char**) const; - virtual void getInitData(int*, const unsigned char**); - // This is only a kludge required to support old songs' midistates. Do not use in any new synth. - virtual int oldMidiStateHeader(const unsigned char** data) const; - bool init(const char* name); - void guiSendSampleLoaded(bool success, int ch, const char* filename); - void guiSendError(const char* errorstring); +{ +public: + SimpleSynth(int); + + virtual ~SimpleSynth(); + + virtual bool nativeGuiVisible() const; + virtual bool hasNativeGui() const; + virtual bool playNote(int arg1, int arg2, int arg3); + virtual bool processEvent(const MusECore::MidiPlayEvent& arg1); + virtual bool setController(int arg1, int arg2, int arg3); + virtual bool sysex(int arg1, const unsigned char* arg2); + virtual const char* getPatchName(int arg1, int arg2, bool arg3) const; + virtual const MidiPatch* getPatchInfo(int arg1, const MidiPatch* arg2) const; + virtual int getControllerInfo(int arg1, const char** arg2, int* arg3, int* arg4, int* arg5, int* arg6) const; + virtual void processMessages(); + virtual void process(unsigned pos, float** data, int offset, int len); + virtual void showNativeGui(bool arg1); + virtual void guiHeartBeat(); + virtual void getInitData(int*, const unsigned char**); + // This is only a kludge required to support old songs' midistates. Do not use in any new synth. + virtual int oldMidiStateHeader(const unsigned char** data) const; + bool init(const char* name); + void guiSendSampleLoaded(bool success, int ch, const char* filename); + void guiSendError(const char* errorstring); + + SS_State synth_state; - static const char* synth_state_descr[]; - static const char* channel_state_descr[]; + static const char* synth_state_descr[]; + static const char* channel_state_descr[]; private: - SimpleSynthGui* gui; + SimpleSynthGui* gui; + + byte* initBuffer; + int initLen; + void setupInitBuffer(int len); + + SS_Channel channels[SS_NR_OF_CHANNELS]; + SS_Controller controllers[SS_NR_OF_CONTROLLERS]; + bool setController(int channel, int id, int val, bool fromGui); + bool loadSample(int ch_no, const char* filename); + void parseInitData(const unsigned char* data); + void updateVolume(int ch, int in_volume_ctrlval); + void updatePitch(int ch, int inpitch_ctrlval); + void updateBalance(int ch, int pan); + void guiNotifySampleCleared(int ch); + void guiUpdateBalance(int ch, int bal); + void guiUpdatePitch(int ch, int bal); + void guiUpdateRoute(int ch, int val); + void guiUpdateVolume(int ch, int val); + void guiUpdateNoff(int ch, bool b); + void guiUpdateChoff(int ch, bool b); + void guiUpdateMasterVol(int val); + void guiUpdateFxParameter(int fxid, int param, float val); + void guiUpdateSendFxLevel(int channel, int fxid, int level); + // Returns true on success. + bool initSendEffect(int sendeffectid, QString lib, QString name); + void setSendFxLevel(int channel, int effectid, double val); + void cleanupPlugin(int id); + void setFxParameter(int fxid, int param, float val); + void clearSample(int ch); + + double master_vol; + int master_vol_ctrlval; + + //Send effects: + SS_SendFx sendEffects[SS_NR_OF_SENDEFFECTS]; + float* sendFxLineOut[SS_NR_OF_SENDEFFECTS][2]; //stereo output (fed into LADSPA inputs),sent from the individual channels -> LADSPA fx + float* sendFxReturn[SS_NR_OF_SENDEFFECTS][2]; //stereo inputs, from LADSPA plugins, sent from LADSPA -> SS and added to the mix + double* processBuffer[2]; +}; - byte* initBuffer; - int initLen; - void setupInitBuffer(int len); - - SS_Channel channels[SS_NR_OF_CHANNELS]; - SS_Controller controllers[SS_NR_OF_CONTROLLERS]; - bool setController(int channel, int id, int val, bool fromGui); - bool loadSample(int ch_no, const char* filename); - void parseInitData(const unsigned char* data); - void updateVolume(int ch, int in_volume_ctrlval); - void updateBalance(int ch, int pan); - void guiNotifySampleCleared(int ch); - void guiUpdateBalance(int ch, int bal); - void guiUpdateVolume(int ch, int val); - void guiUpdateNoff(int ch, bool b); - void guiUpdateChoff(int ch, bool b); - void guiUpdateMasterVol(int val); - void guiUpdateFxParameter(int fxid, int param, float val); - void guiUpdateSendFxLevel(int channel, int fxid, int level); - bool initSendEffect(int sendeffectid, QString lib, QString name); - void setSendFxLevel(int channel, int effectid, double val); - void cleanupPlugin(int id); - void setFxParameter(int fxid, int param, float val); - void clearSample(int ch); - double master_vol; - int master_vol_ctrlval; - - //Send effects: - SS_SendFx sendEffects[SS_NR_OF_SENDEFFECTS]; - float* sendFxLineOut[SS_NR_OF_SENDEFFECTS][2]; //stereo output (fed into LADSPA inputs),sent from the individual channels -> LADSPA fx - float* sendFxReturn[SS_NR_OF_SENDEFFECTS][2]; //stereo inputs, from LADSPA plugins, sent from LADSPA -> SS and added to the mix - double* processBuffer[2]; - }; +struct SS_SampleLoader +{ + SS_Channel* channel; + std::string filename; + int ch_no; + SimpleSynth* synth; + int sampleRate; +}; +void resample(SS_Sample *origSmp, SS_Sample* newSample, double pitch, int sample_rate); static void* loadSampleThread(void*); static pthread_mutex_t SS_LoaderMutex; -static SS_State synth_state; -static SimpleSynth* simplesynth_ptr; #endif diff -Nru muse-2.1.2/synti/simpledrums2/ssplugin.cpp muse-3.0.2+ds1/synti/simpledrums2/ssplugin.cpp --- muse-2.1.2/synti/simpledrums2/ssplugin.cpp 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/ssplugin.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -26,7 +26,7 @@ // #include -#include +#include #include #include #include @@ -34,6 +34,32 @@ #include "ssplugin.h" #include "common.h" +#define SS_LOG_MAX 0 +#define SS_LOG_MIN -10 +#define SS_LOG_OFFSET SS_LOG_MIN + + +// +// Map plugin parameter on domain [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] to domain [SS_LOG_MIN, SS_LOG_MAX] (log domain) +// +float SS_map_pluginparam2logdomain(int pluginparam_val) +{ + float scale = (float) (SS_LOG_MAX - SS_LOG_MIN)/ (float) SS_PLUGIN_PARAM_MAX; + float scaled = (float) pluginparam_val * scale; + float mapped = scaled + SS_LOG_OFFSET; + return mapped; +} +// +// Map plugin parameter on domain to domain [SS_LOG_MIN, SS_LOG_MAX] to [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] (from log-> [0,127]) +// (inverse func to the above) +int SS_map_logdomain2pluginparam(float pluginparam_log) +{ + float mapped = pluginparam_log - SS_LOG_OFFSET; + float scale = (float) SS_PLUGIN_PARAM_MAX / (float) (SS_LOG_MAX - SS_LOG_MIN); + int scaled = (int) round(mapped * scale); + return scaled; +} + PluginList plugins; @@ -52,10 +78,10 @@ if (SS_DEBUG_LADSPA) { printf("loadPluginLib: %s\n", fi->fileName().toLatin1().constData()); } - void* handle = dlopen(fi->filePath().toAscii().data(), RTLD_NOW); + void* handle = dlopen(fi->filePath().toLatin1().constData(), RTLD_NOW); if (handle == 0) { fprintf(stderr, "dlopen(%s) failed: %s\n", - fi->filePath().toAscii().data(), dlerror()); + fi->filePath().toLatin1().constData(), dlerror()); return; } LADSPA_Descriptor_Function ladspa = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor"); @@ -67,7 +93,7 @@ "Unable to find ladspa_descriptor() function in plugin " "library file \"%s\": %s.\n" "Are you sure this is a LADSPA plugin file?\n", - fi->filePath().toAscii().data(), + fi->filePath().toLatin1().constData(), txt); return;//exit(1); } diff -Nru muse-2.1.2/synti/simpledrums2/ssplugingui.cpp muse-3.0.2+ds1/synti/simpledrums2/ssplugingui.cpp --- muse-2.1.2/synti/simpledrums2/ssplugingui.cpp 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/ssplugingui.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -26,7 +26,6 @@ // // #include "ssplugingui.h" -#include "ssplugin.h" #include "simpledrumsgui.h" #define SS_PLUGINGUI_XOFF 300 @@ -41,107 +40,6 @@ #define SS_PLUGINFRONT_INC_PARAM_MIN 60 #define SS_PLUGINGUI_HEIGHT (SS_NR_OF_SENDEFFECTS * SS_PLUGINFRONT_MINHEIGHT) -#define SS_PLUGINCHOOSER_NAMECOL 0 -#define SS_PLUGINCHOOSER_LABELCOL 1 -#define SS_PLUGINCHOOSER_INPORTSCOL 2 -#define SS_PLUGINCHOOSER_OUTPORTSCOL 3 -#define SS_PLUGINCHOOSER_CREATORCOL 4 - - -/*! - \fn SS_PluginChooser::SS_PluginChooser(QWidget* parent, const char* name = 0) - */ -SS_PluginChooser::SS_PluginChooser(QWidget* parent) - :QDialog(parent) - { - SS_TRACE_IN - setupUi(this); - selectedPlugin = 0; - - for (iPlugin i=plugins.begin(); i !=plugins.end(); i++) { - //Support for only 2 or 1 inport/outports - if ( ((*i)->outports() == 2 || (*i)->outports() == 1) && ((*i)->inports() == 2 || (*i)->inports() == 1) ) { - QTreeWidgetItem* tmpItem = new QTreeWidgetItem(effectsListView); - tmpItem->setText(SS_PLUGINCHOOSER_NAMECOL, (*i)->name()); - tmpItem->setText(SS_PLUGINCHOOSER_LABELCOL, (*i)->label()); - tmpItem->setText(SS_PLUGINCHOOSER_INPORTSCOL, QString::number((*i)->inports())); - tmpItem->setText(SS_PLUGINCHOOSER_OUTPORTSCOL, QString::number((*i)->outports())); - tmpItem->setText(SS_PLUGINCHOOSER_CREATORCOL, (*i)->maker()); - effectsListView->addTopLevelItem(tmpItem); - } - } - connect(okButton, SIGNAL(pressed()), SLOT(okPressed())); - connect(cancelButton, SIGNAL(pressed()), SLOT(cancelPressed())); - - //connect(effectsListView, SIGNAL(selectionChanged(QTreeWidgetItem*)), SLOT(selectionChanged(QTreeWidgetItem*))); - //connect(effectsListView, SIGNAL(doubleClicked(QTreeWidgetItem*)), SLOT(doubleClicked(QTreeWidgetItem*))); - connect(effectsListView, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); // p4.0.27 - connect(effectsListView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(doubleClicked(QTreeWidgetItem*))); // - - SS_TRACE_OUT - } - -/*! - \fn SS_PluginChooser::selectionChanged(QListViewItem* item) - */ -//void SS_PluginChooser::selectionChanged(QTreeWidgetItem* item) -void SS_PluginChooser::selectionChanged() - { - SS_TRACE_IN - //selectedItem = item; - selectedItem = effectsListView->currentItem(); - SS_TRACE_OUT - } - -/*! - \fn SS_PluginChooser::okPressed() - */ -void SS_PluginChooser::okPressed() - { - SS_TRACE_IN - selectedPlugin = findSelectedPlugin(); - done(QDialog::Accepted); - SS_TRACE_OUT - } - -/*! - \fn SS_PluginChooser::cancelPressed() - */ -void SS_PluginChooser::cancelPressed() - { - SS_TRACE_IN - SS_TRACE_OUT - done(QDialog::Rejected); - } - -/*! - \fn SS_PluginChooser::doubleClicked(QListViewItem* item) - */ -void SS_PluginChooser::doubleClicked(QTreeWidgetItem* item) - { - SS_TRACE_IN - selectedItem = item; // p4.0.27 Tim - selectedPlugin = findSelectedPlugin(); - SS_TRACE_OUT - done(QDialog::Accepted); - } - -/*! - \fn SS_PluginChooser::getSelectedPlugin() - */ -LadspaPlugin* SS_PluginChooser::findSelectedPlugin() - { - SS_TRACE_IN - if(!selectedItem) // p4.0.27 Tim - return 0; - LadspaPlugin* selected = 0; - for (iPlugin i=plugins.begin(); i != plugins.end(); i++) { - if ((*i)->name() == selectedItem->text(SS_PLUGINCHOOSER_NAMECOL)) - selected = (LadspaPlugin*) (*i); - } - SS_TRACE_OUT - return selected; - } /*! \fn SS_PluginFront::SS_PluginFront(QWidget* parent, const char* name = 0) @@ -290,11 +188,11 @@ { SS_TRACE_IN if (!pluginChooser) - pluginChooser = new SS_PluginChooser(this); + pluginChooser = new MusESimplePlugin::SimplerPluginChooser(this); pluginChooser->exec(); if ((pluginChooser->result() == QDialog::Accepted) && pluginChooser->getSelectedPlugin()) { - Plugin* p = pluginChooser->getSelectedPlugin(); + MusESimplePlugin::Plugin* p = pluginChooser->getSelectedPlugin(); //printf("Selected plugin: %s\n", pluginChooser->getSelectedPlugin()->name().toLatin1().constData()); emit loadPlugin(fxid, p->lib(), p->label()); } @@ -309,11 +207,10 @@ emit returnLevelChanged(fxid, val); } - /*! - \fn SS_PluginFront::updatePluginValue(unsigned i) + \fn SS_PluginFront::updatePluginValue(PluginI* plugi) */ -void SS_PluginFront::updatePluginValue(unsigned k) +void SS_PluginFront::updatePluginValue(MusESimplePlugin::PluginI* plugi) { SS_TRACE_IN // If parameters are shown - close them @@ -321,15 +218,7 @@ expandButtonPressed(); } - unsigned j=0; - if (k > plugins.size()) { - fprintf(stderr, "Internal error, tried to update plugin w range outside of list\n"); - return; - } - - iPlugin i; - for (i = plugins.begin(); j != k; i++, j++) ; - plugin = (LadspaPlugin*) *(i); + plugin = plugi; setPluginName(plugin->label()); outGainSlider->setEnabled(true); clearFxButton->setEnabled(true); @@ -406,7 +295,7 @@ QRect pf = geometry(); if (!expanded) { - plugin->parameter() == 1 ? sizeIncrease = SS_PLUGINFRONT_INC_PARAM_MIN : sizeIncrease = plugin->parameter() * SS_PLUGINFRONT_INC_PARAM; + plugin->parameters() == 1 ? sizeIncrease = SS_PLUGINFRONT_INC_PARAM_MIN : sizeIncrease = plugin->parameters() * SS_PLUGINFRONT_INC_PARAM; pf.setHeight(pf.height() + sizeIncrease); setMinimumSize(QSize(pf.width(), pf.height())); setMaximumSize(QSize(SS_PLUGINGUI_MAX_WIDTH, pf.height())); @@ -424,7 +313,7 @@ expGroup->deleteLater(); paramWidgets.clear(); expGroup = 0; - plugin->parameter() == 1 ? sizeIncrease = (0-SS_PLUGINFRONT_INC_PARAM_MIN) : sizeIncrease = 0 - (plugin->parameter() * SS_PLUGINFRONT_INC_PARAM); + plugin->parameters() == 1 ? sizeIncrease = (0-SS_PLUGINFRONT_INC_PARAM_MIN) : sizeIncrease = 0 - (plugin->parameters() * SS_PLUGINFRONT_INC_PARAM); expandButton->setText("->"); expanded = false; pf.setHeight(pf.height() + sizeIncrease); @@ -450,7 +339,7 @@ expGroup = new QGroupBox(this); expGroup->setMinimumSize(QSize(50, 50)); - expGroup->setMaximumSize(QSize(SS_PLUGINGUI_MAX_WIDTH, (plugin->parameter() * SS_PLUGINFRONT_INC_PARAM - SS_PLUGINFRONT_MARGIN))); + expGroup->setMaximumSize(QSize(SS_PLUGINGUI_MAX_WIDTH, (plugin->parameters() * SS_PLUGINFRONT_INC_PARAM - SS_PLUGINFRONT_MARGIN))); expGroup->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); expLayout->addWidget(expGroup); expGroup->show(); @@ -459,7 +348,7 @@ //TD expGroupLayout->setResizeMode(QLayout::FreeResize); expGroupLayout->setContentsMargins(SS_PLUGINFRONT_MARGIN, SS_PLUGINFRONT_MARGIN, SS_PLUGINFRONT_MARGIN, SS_PLUGINFRONT_MARGIN); - for (int i=0; i < plugin->parameter(); i++) { + for (unsigned long i=0; i < plugin->parameters(); i++) { QHBoxLayout* paramStrip = new QHBoxLayout; // (expGroupLayout, 3); expGroupLayout->addLayout(paramStrip); paramStrip->setAlignment(Qt::AlignLeft); @@ -474,7 +363,7 @@ if (plugin->isBool(i)) { SS_ParameterCheckBox* paramCheckBox = new SS_ParameterCheckBox(expGroup, plugin, fxid, i); paramCheckBox->setEnabled(true); - paramCheckBox->setParamValue((int) plugin->getControlValue(i)); + paramCheckBox->setParamValue((int) plugin->param(i)); paramCheckBox->show(); paramStrip->addWidget(paramCheckBox); connect(paramCheckBox, SIGNAL(valueChanged(int, int, int)), SLOT(parameterValueChanged(int, int, int))); @@ -533,17 +422,18 @@ pluginFronts[i] = new SS_PluginFront(this, i); pluginFronts[i]->update(); layout->addWidget(pluginFronts[i]); - connect(pluginFronts[i], SIGNAL(loadPlugin(int, QString, QString)), simplesynthgui_ptr, SLOT(loadEffectInvoked(int, QString, QString))); - connect(pluginFronts[i], SIGNAL(returnLevelChanged(int, int)), simplesynthgui_ptr, SLOT(returnLevelChanged(int, int))); - connect(pluginFronts[i], SIGNAL(fxToggled(int, int)), simplesynthgui_ptr, SLOT(toggleEffectOnOff(int, int))); - connect(pluginFronts[i], SIGNAL(clearPlugin(int)), simplesynthgui_ptr, SLOT(clearPlugin(int))); + connect(pluginFronts[i], SIGNAL(loadPlugin(int, QString, QString)), + parent, SLOT(loadEffectInvoked(int, QString, QString))); + connect(pluginFronts[i], SIGNAL(returnLevelChanged(int, int)), + parent, SLOT(returnLevelChanged(int, int))); + connect(pluginFronts[i], SIGNAL(fxToggled(int, int)), + parent, SLOT(toggleEffectOnOff(int, int))); + connect(pluginFronts[i], SIGNAL(clearPlugin(int)), + parent, SLOT(clearPlugin(int))); connect(pluginFronts[i], SIGNAL(sizeChanged(int, int)), SLOT(pluginFrontSizeChanged(int, int))); - connect(pluginFronts[i], SIGNAL(effectParameterChanged(int, int, int)), simplesynthgui_ptr, SLOT(effectParameterChanged(int, int, int))); + connect(pluginFronts[i], SIGNAL(effectParameterChanged(int, int, int)), + parent, SLOT(effectParameterChanged(int, int, int))); } - - // FIXME: These are causing window height to be fixed way too small - can't see anything. Why? It was working before. Tim p4.0.49 - //setMinimumSize(QSize(SS_PLUGINGUI_WIDTH, geometry().height())); - //setMaximumSize(QSize(SS_PLUGINGUI_MAX_WIDTH, geometry().height())); } diff -Nru muse-2.1.2/synti/simpledrums2/ssplugingui.h muse-3.0.2+ds1/synti/simpledrums2/ssplugingui.h --- muse-2.1.2/synti/simpledrums2/ssplugingui.h 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/ssplugingui.h 2018-01-29 20:07:03.000000000 +0000 @@ -31,21 +31,18 @@ #include #include #include -#include -//#include -//#include +#include -#include "ui_sspluginchooserbase.h" #include "common.h" -#include "ssplugin.h" +#include "libsimpleplugin/simpler_plugin.h" +#include "libsimpleplugin/simpler_plugingui.h" class SS_ParameterWidget { protected: int fxid; int parameter; - - LadspaPlugin* plugin; + MusESimplePlugin::PluginI* plugin; public: SS_ParameterWidget() { } @@ -62,7 +59,7 @@ Q_OBJECT public: - SS_ParameterCheckBox(QWidget* parent, LadspaPlugin* in_plugin, int in_id, int in_parameter) + SS_ParameterCheckBox(QWidget* parent, MusESimplePlugin::PluginI* in_plugin, int in_id, int in_parameter) : QCheckBox(parent) , SS_ParameterWidget() { SS_TRACE_IN @@ -87,7 +84,7 @@ Q_OBJECT public: - SS_ParameterSlider(QWidget* parent, LadspaPlugin* in_plugin, int in_id, int in_parameter) + SS_ParameterSlider(QWidget* parent, MusESimplePlugin::PluginI* in_plugin, int in_id, int in_parameter) : QSlider(Qt::Horizontal, parent), SS_ParameterWidget() { SS_TRACE_IN @@ -120,33 +117,6 @@ typedef std::list::iterator SS_iParameterWidgetList ; //------------------------------- -// SS_PluginChooser -//------------------------------- -class SS_PluginChooser : public QDialog, Ui::SS_PluginChooserBase -{ - Q_OBJECT - private: - LadspaPlugin* selectedPlugin; - protected: - - public: - SS_PluginChooser(QWidget* parent); - LadspaPlugin* getSelectedPlugin() { SS_TRACE_IN SS_TRACE_OUT return selectedPlugin; } - - private slots: - void okPressed(); - void cancelPressed(); - ///void selectionChanged(QTreeWidgetItem* item); - void selectionChanged(); - void doubleClicked(QTreeWidgetItem* item); - - private: - QTreeWidgetItem* selectedItem; - LadspaPlugin* findSelectedPlugin(); - -}; - -//------------------------------- // SS_PluginGuiFront //------------------------------- class SS_PluginFront : public QGroupBox @@ -161,10 +131,9 @@ QPushButton* clearFxButton; QPushButton* expandButton; QSlider* outGainSlider; - SS_PluginChooser* pluginChooser; - LadspaPlugin* plugin; + MusESimplePlugin::SimplerPluginChooser* pluginChooser; + MusESimplePlugin::PluginI* plugin; QGroupBox* expGroup; - int fxid; bool expanded; @@ -177,7 +146,7 @@ SS_PluginFront(QWidget* parent, int id); void setPluginName(QString name); ~SS_PluginFront(); - void updatePluginValue(unsigned i); + void updatePluginValue(MusESimplePlugin::PluginI* plugi); void clearPluginDisplay(); void setParameterValue(int param, int val); void setRetGain(int val); diff -Nru muse-2.1.2/synti/simpledrums2/ssplugin.h muse-3.0.2+ds1/synti/simpledrums2/ssplugin.h --- muse-2.1.2/synti/simpledrums2/ssplugin.h 2013-03-28 15:17:15.000000000 +0000 +++ muse-3.0.2+ds1/synti/simpledrums2/ssplugin.h 2017-12-04 21:01:19.000000000 +0000 @@ -81,7 +81,15 @@ class LadspaPlugin : public Plugin { +private: +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-private-field" +#endif LADSPA_Descriptor_Function ladspa; +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif const LADSPA_Descriptor* plugin; LADSPA_Handle handle; bool active; diff -Nru muse-2.1.2/synti/vam/CMakeLists.txt muse-3.0.2+ds1/synti/vam/CMakeLists.txt --- muse-2.1.2/synti/vam/CMakeLists.txt 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/vam/CMakeLists.txt 2018-01-29 20:07:03.000000000 +0000 @@ -24,7 +24,7 @@ ## ## Expand Qt macros in source files ## -QT4_WRAP_CPP ( vam_mocs +QT5_WRAP_CPP ( vam_mocs vamgui.h ) @@ -34,7 +34,7 @@ file (GLOB vam_ui_files vamguibase.ui ) -QT4_WRAP_UI ( vam_uis ${vam_ui_files} ) +QT5_WRAP_UI ( vam_uis ${vam_ui_files} ) ## ## List of source files to compile @@ -82,6 +82,9 @@ # al synti ${QT_LIBRARIES} + icons + mpevent_module + xml_module ) ## diff -Nru muse-2.1.2/synti/vam/common_defs.h muse-3.0.2+ds1/synti/vam/common_defs.h --- muse-2.1.2/synti/vam/common_defs.h 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/vam/common_defs.h 2017-12-04 21:01:19.000000000 +0000 @@ -23,7 +23,8 @@ #ifndef __VAM_UNIQUE_ID_H #define __VAM_UNIQUE_ID_H -// Make sure this number is unique among all the MESS synths. +// Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. +// 127 is reserved for special MusE system messages. #define VAM_UNIQUE_ID 2 #endif diff -Nru muse-2.1.2/synti/vam/README muse-3.0.2+ds1/synti/vam/README --- muse-2.1.2/synti/vam/README 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/vam/README 2018-01-26 21:59:38.000000000 +0000 @@ -19,7 +19,7 @@ ( and everything that MusE requires, like Qt3 ) Unpack VAM in $MUSESOURCEDIR/synthi and run from vam/ make; - make install. VAM won't compile automaticly if you run make + make install. VAM won't compile automatically if you run make from $MUSESOURCEDIR. $MUSESOURCEDIR is the directory you have the MusE source. For optimizing add -march=(your arch) to the compiler arguments diff -Nru muse-2.1.2/synti/vam/vam.cpp muse-3.0.2+ds1/synti/vam/vam.cpp --- muse-2.1.2/synti/vam/vam.cpp 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/vam/vam.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -31,16 +31,24 @@ #include #include "libsynti/mess.h" -#include "muse/midi.h" -#include "muse/midictrl.h" +#include "muse/midi_consts.h" +#include "muse/midictrl_consts.h" #include "common_defs.h" #include "vam.h" #include "vamgui.h" #include "libsynti/mono.h" +std::string VAM_configPath; + // Denormalise floats, only actually needed for PIII and very recent PowerPC -#define DENORMALISE(fv) (((*(unsigned int*)&(fv))&0x7f800000)==0)?0.0f:(fv) +//#define DENORMALISE(fv) (((*(unsigned int*)&(fv))&0x7f800000)==0)?0.0f:(fv) +inline float DENORMALISE(float fv) +{ + char *c = reinterpret_cast(&fv); + unsigned int *ui = reinterpret_cast(c); + return ((((*ui)&0x7f800000)) == 0)?0.0f:(fv); +} // A fast, truncating towards 0 modulo function. ANSI C doesn't define // which % will do, most truncate towards -inf @@ -193,20 +201,16 @@ public: virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const; - //virtual void getInitData(int* n, const unsigned char**p) const; virtual void getInitData(int* n, const unsigned char**p); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** data) const; - //virtual bool guiVisible() const; - //virtual void showGui(bool); - //virtual bool hasGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); virtual bool hasNativeGui() const { return true; } virtual void getNativeGeometry(int* x, int* y, int* w, int* h) const; virtual void setNativeGeometry(int x, int y, int w, int h); virtual void processMessages(); - virtual void process(float**, int, int); + virtual void process(unsigned pos, float**, int, int); virtual void note(int channel, int pitch, int velo); virtual bool setController(int channel, int ctrl, int val); virtual bool sysex(int, const unsigned char*); @@ -331,7 +335,7 @@ int i; float tmp; for(i = 0; i < CB_AMP_SIZE; i++) { - cb2amp_tab[i] = pow(10.0, double(i) / -300.0); + cb2amp_tab[i] = exp10(double(i) / -300.0); //cb2amp_tab[i] = 1.0 - i/(float)CB_AMP_SIZE; } for(i = 0; i < LIN2EXP_SIZE; i++) { @@ -340,9 +344,9 @@ } int sr = sampleRate(); /* Build up denormalised oscilator wavetables, these are sample_rate - long, costs more RAM to create them but makes freqency calcs much - cheaper, and means that interpolation isn't that neccesary, esp if - you use integer frequncies */ + long, costs more RAM to create them but makes frequency calcs much + cheaper, and means that interpolation isn't that necessary, esp if + you use integer frequencies */ float *tmp_tbl = new float[sr]; const int lag = sr/50; @@ -471,7 +475,7 @@ // Called from host, ONLY if output path is connected. //--------------------------------------------------------- -void VAM::process(float** ports, int offset, int sampleCount) +void VAM::process(unsigned /*pos*/, float** ports, int offset, int sampleCount) { /* // @@ -1097,9 +1101,10 @@ class QWidget; -static Mess* instantiate(int sr, QWidget*, QString*, const char* name) +static Mess* instantiate(unsigned long long /*parentWinId*/, const char* name, const MessConfig* config) { - VAM* vam = new VAM(sr); + VAM_configPath = std::string(config->_configPath); + VAM* vam = new VAM(config->_sampleRate); if (vam->init(name)) { delete vam; return 0; diff -Nru muse-2.1.2/synti/vam/vamguibase.ui muse-3.0.2+ds1/synti/vam/vamguibase.ui --- muse-2.1.2/synti/vam/vamguibase.ui 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/vam/vamguibase.ui 2017-12-04 21:01:19.000000000 +0000 @@ -75,7 +75,7 @@ 0 - + 3 @@ -190,7 +190,7 @@ 0 - + 3 @@ -206,7 +206,7 @@ 0 - + 3 @@ -222,7 +222,7 @@ 0 - + 3 @@ -238,7 +238,7 @@ 0 - + 3 @@ -254,7 +254,7 @@ 0 - + 3 @@ -270,7 +270,7 @@ 0 - + 3 @@ -332,7 +332,7 @@ 0 - + 3 @@ -673,7 +673,7 @@ 0 - + 3 @@ -699,7 +699,7 @@ 0 - + 3 @@ -747,7 +747,7 @@ 0 - + 3 @@ -763,7 +763,7 @@ 0 - + 3 @@ -779,7 +779,7 @@ 0 - + 3 @@ -815,7 +815,7 @@ 0 - + 3 @@ -831,7 +831,7 @@ 0 - + 3 @@ -887,7 +887,7 @@ 0 - + 3 @@ -913,7 +913,7 @@ 0 - + 3 @@ -1108,7 +1108,7 @@ 0 - + 3 @@ -1153,7 +1153,7 @@ 0 - + 3 @@ -1179,7 +1179,7 @@ 0 - + 3 @@ -1278,7 +1278,7 @@ 0 - + 3 @@ -1304,7 +1304,7 @@ 0 - + 3 @@ -1320,7 +1320,7 @@ 0 - + 3 @@ -1346,7 +1346,7 @@ 0 - + 3 @@ -1362,7 +1362,7 @@ 0 - + 3 @@ -1388,7 +1388,7 @@ 0 - + 3 diff -Nru muse-2.1.2/synti/vam/vamgui.cpp muse-3.0.2+ds1/synti/vam/vamgui.cpp --- muse-2.1.2/synti/vam/vamgui.cpp 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/vam/vamgui.cpp 2018-01-29 20:07:03.000000000 +0000 @@ -42,12 +42,10 @@ #include #include #include -#include -#include "muse/globals.h" #include "muse/xml.h" -#include "muse/midi.h" -#include "muse/midictrl.h" +#include "muse/midi_consts.h" +#include "muse/midictrl_consts.h" #include "muse/icons.h" const char *vam_ctrl_names[] = { @@ -164,7 +162,7 @@ void Preset::writeConfiguration(MusECore::Xml& xml, int level) { //xml.tag(level++, "preset name=\"%s\"", name.ascii()); - xml.tag(level++, "preset name=\"%s\"", MusECore::Xml::xmlString(name).toAscii().constData()); + xml.tag(level++, "preset name=\"%s\"", MusECore::Xml::xmlString(name).toLatin1().constData()); for (int i = 0; i < NUM_CONTROLLER; ++i) { xml.tag(level, "control idx=\"%d\" val=\"%d\" /", i, ctrl[i]); } @@ -180,8 +178,7 @@ MessGui() { setupUi(this); - QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); - connect(s, SIGNAL(activated(int)), SLOT(readMessage(int))); + connect(this->getGuiSignal(),SIGNAL(wakeup()),this,SLOT(readMessage())); loadPresets->setIcon(QIcon(*MusEGui::openIcon)); savePresets->setIcon(QIcon(*MusEGui::saveIcon)); @@ -248,16 +245,6 @@ ctrlLo = 0; dataHi = 0; dataLo = 0; - presetFileName = NULL; - - // work around for probable QT/WM interaction bug. - // for certain window managers, e.g xfce, this window is - // is displayed although not specifically set to show(); - // bug: 2811156 Softsynth GUI unclosable with XFCE4 (and a few others) - // Nov 21, 2012 Hey this causes the thing not to open at all, EVER, on Lubuntu and some others! - // And we had a request to remove this from a knowledgable tester. REMOVE Tim. - ///show(); - ///hide(); } //--------------------------------------------------------- @@ -602,20 +589,13 @@ void VAMGui::loadPresetsPressed() { #if 1 // TODO - QString iname; - QString s(MusEGlobal::configPath); - -/* QString filename = QFileDialog::getOpenFileName(lastdir, QString("*.[Ss][Ff]2"), - this, - "Load Soundfont dialog", - "Choose soundfont");*/ QString fn = QFileDialog::getOpenFileName(this, tr("MusE: Load VAM Presets"), - s, "Presets (*.vam)"); + QString::fromStdString(VAM_configPath), "Presets (*.vam)"); if (fn.isEmpty()) return; bool popenFlag=false; - FILE* f = fopen(fn.toAscii().constData(),"r");//fileOpen(this, fn, QString(".pre"), "r", popenFlag, true); + FILE* f = fopen(fn.toLatin1().constData(),"r");//fileOpen(this, fn, QString(".pre"), "r", popenFlag, true); if (f == 0) return; presets.clear(); @@ -666,8 +646,6 @@ else fclose(f); - if (presetFileName) delete presetFileName; - presetFileName = new QString(fn); QString dots ("..."); fileName->setText(fn.right(32).insert(0, dots)); @@ -681,17 +659,17 @@ //--------------------------------------------------------- // doSavePresets //--------------------------------------------------------- -void VAMGui::doSavePresets(const QString& fn, bool showWarning) +void VAMGui::doSavePresets(const QString& fn, bool /*_showWarning*/) { - showWarning=showWarning; // prevent of unsused variable warning + //_showWarning=_showWarning; // prevent of unsused variable warning #if 1 bool popenFlag=false; if (fn=="") { printf("empty name\n"); return; } - printf("fn=%s\n",fn.toAscii().constData()); - FILE* f = fopen(fn.toAscii().constData(),"w");//fileOpen(this, fn, QString(".pre"), "w", popenFlag, false, showWarning); + printf("fn=%s\n",fn.toLatin1().constData()); + FILE* f = fopen(fn.toLatin1().constData(),"w");//fileOpen(this, fn, QString(".pre"), "w", popenFlag, false, showWarning); if (f == 0) return; MusECore::Xml xml(f); @@ -718,9 +696,9 @@ void VAMGui::savePresetsPressed() { #if 1 // TODO - QString s(MusEGlobal::configPath); QString fn = QFileDialog::getSaveFileName(this, tr("MusE: Save VAM Presets"), - s, "Presets (*.vam)"); + QString::fromStdString(VAM_configPath), + "Presets (*.vam)"); if (fn.isEmpty()) return; doSavePresets (fn, true); @@ -734,17 +712,12 @@ void VAMGui::savePresetsToFilePressed() { - if (!presetFileName ) { - - QString s(MusEGlobal::configPath); - QString fn = QFileDialog::getSaveFileName(this, tr("MusE: Save VAM Presets"), - s, "Presets (*.vam)"); - presetFileName = new QString(fn); - } - if (*presetFileName == QString("")) + QString fn = QFileDialog::getSaveFileName(this, tr("MusE: Save VAM Presets"), + QString::fromStdString(VAM_configPath), + "Presets (*.vam)"); + if (fn == QString("")) return; - //if presetFileName-> - doSavePresets (*presetFileName, false); + doSavePresets(fn, false); } //--------------------------------------------------------- @@ -760,7 +733,7 @@ // readMessage //--------------------------------------------------------- -void VAMGui::readMessage(int) +void VAMGui::readMessage() { MessGui::readMessage(); } diff -Nru muse-2.1.2/synti/vam/vamgui.h muse-3.0.2+ds1/synti/vam/vamgui.h --- muse-2.1.2/synti/vam/vamgui.h 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/vam/vamgui.h 2018-01-29 20:07:03.000000000 +0000 @@ -29,7 +29,7 @@ #include "libsynti/gui.h" class QListWidgetItem; -class Preset; +struct Preset; class QString; class QSignalMapper; @@ -46,8 +46,6 @@ int dataHi; int dataLo; SynthGuiCtrl dctrl[NUM_CONTROLLER]; - QString * presetFileName; - void sendControllerChange(int ctrl, int val); void initParameter(); @@ -68,7 +66,7 @@ void deletePresetPressed(); void doSavePresets(const QString&, bool); void savePresetsToFilePressed(); - void readMessage(int); + void readMessage(); protected: virtual void sysexReceived(const unsigned char*, int); diff -Nru muse-2.1.2/synti/vam/vam.h muse-3.0.2+ds1/synti/vam/vam.h --- muse-2.1.2/synti/vam/vam.h 2013-03-28 15:17:13.000000000 +0000 +++ muse-3.0.2+ds1/synti/vam/vam.h 2018-01-29 20:07:03.000000000 +0000 @@ -21,7 +21,9 @@ #ifndef __VAM_H #define __VAM_H -#include "muse/midictrl.h" +#include "muse/midictrl_consts.h" + +#include enum { //DCO1_PITCHMOD, @@ -43,4 +45,7 @@ //#define VAM_DEBUG +extern std::string VAM_configPath; + + #endif /* __VAM_H */ diff -Nru muse-2.1.2/vestige/aeffectx.h muse-3.0.2+ds1/vestige/aeffectx.h --- muse-2.1.2/vestige/aeffectx.h 2013-03-28 15:17:20.000000000 +0000 +++ muse-3.0.2+ds1/vestige/aeffectx.h 2017-12-04 21:01:19.000000000 +0000 @@ -105,7 +105,7 @@ const int effEditTop = 20; const int effProcessEvents = 25; const int effGetEffectName = 45; -const int effGetParameterProperties = 47; // missing +const int effGetParameterProperties = 56; // missing const int effGetVendorString = 47; const int effGetProductString = 48; const int effGetVendorVersion = 49; @@ -114,7 +114,7 @@ const int kEffectMagic = CCONST( 'V', 's', 't', 'P' ); const int kVstLangEnglish = 1; -const int kVstMidiType = 1;; +const int kVstMidiType = 1; const int kVstTransportPlaying = 1 << 1; /* validity flags for a VstTimeInfo structure, this info comes from the web */ @@ -163,14 +163,17 @@ } ; - - +#ifdef __clang__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-private-field" +#endif class VstEvent { char dump[sizeof( VstMidiEvent )]; - } ; - +#ifdef __clang__ +#pragma GCC diagnostic pop +#endif diff -Nru muse-2.1.2/xpm/cpu.xpm muse-3.0.2+ds1/xpm/cpu.xpm --- muse-2.1.2/xpm/cpu.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/cpu.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,147 @@ +/* XPM */ +static const char * cpu_xpm[] = { +"22 22 122 2", +" c None", +". c #C6D7D2", +"+ c #C3D1CF", +"@ c #C5CCCE", +"# c #AEBEBA", +"$ c #A9D1BE", +"% c #DCE2E3", +"& c #D8DCDF", +"* c #CFD5D9", +"= c #C6CED2", +"- c #BEC6CB", +"; c #BAC3C6", +"> c #A1B4AF", +", c #B0CAC1", +"' c #D2D7DA", +") c #DFE3E4", +"! c #D7DCDF", +"~ c #C7CED2", +"{ c #B7BEC4", +"] c #B0B8BF", +"^ c #A8B4B8", +"/ c #9CABAB", +"( c #96A7A5", +"_ c #AFC1BF", +": c #BDC4C9", +"< c #CED4D8", +"[ c #AEB7BD", +"} c #A7B0B7", +"| c #9FA9B0", +"1 c #96A1A9", +"2 c #95A1A7", +"3 c #7D9991", +"4 c #A9B8B9", +"5 c #AFB7BD", +"6 c #BCC3C8", +"7 c #9EA9B0", +"8 c #97A2A9", +"9 c #8F9AA3", +"0 c #969CA8", +"a c #2A734E", +"b c #327D54", +"c c #89ABA0", +"d c #A5B3B5", +"e c #A9B2B8", +"f c #AFB8BD", +"g c #98A3AA", +"h c #8B949B", +"i c #838C92", +"j c #216C43", +"k c #488F68", +"l c #A3AEB3", +"m c #A9B1B6", +"n c #A8B2B7", +"o c #AEB7BC", +"p c #91999F", +"q c #899399", +"r c #3D7558", +"s c #47945D", +"t c #508D59", +"u c #99AAAA", +"v c #ACB2B9", +"w c #ADB4B9", +"x c #B4BCC0", +"y c #BFC6CB", +"z c #9AA2A8", +"A c #979CA4", +"B c #4F7E68", +"C c #34704B", +"D c #4A9A70", +"E c #7BA492", +"F c #B2B5BB", +"G c #A2A8AB", +"H c #A1A7A8", +"I c #D3D7DB", +"J c #E0E4E6", +"K c #A4ADB3", +"L c #9A9EA5", +"M c #4E7F68", +"N c #3C7652", +"O c #49A474", +"P c #419568", +"Q c #409869", +"R c #4B9F72", +"S c #6F9884", +"T c #B7C0BF", +"U c #D5D9DC", +"V c #CFD5D8", +"W c #ACB5B9", +"X c #9B9FA5", +"Y c #729287", +"Z c #397751", +"` c #50A779", +" . c #55AF7F", +".. c #61BA8A", +"+. c #77B495", +"@. c #D5D6DB", +"#. c #C9CED1", +"$. c #C7CDD0", +"%. c #C1C8CD", +"&. c #B9BFC3", +"*. c #B0B3B9", +"=. c #7E978F", +"-. c #347851", +";. c #54AC7D", +">. c #61B287", +",. c #B5C4C0", +"'. c #CED4D7", +"). c #C7CDD1", +"!. c #C5CBCF", +"~. c #BCC3C9", +"{. c #8DA09C", +"]. c #2F7D51", +"^. c #48A573", +"/. c #79AF94", +"(. c #9FBCB1", +"_. c #A0B9B3", +":. c #83A798", +"<. c #3D835C", +"[. c #3F9F6C", +"}. c #4AA072", +"|. c #419668", +"1. c #488660", +" ", +" ", +" ", +" ", +" . + @ # ", +" $ % & * = - ; > ", +" , ' ) ! * ~ - { ] ^ / ( ", +" _ : < ) ! * = - { [ } | 1 2 3 ", +" 4 5 6 < ) ! * = - { [ } 7 8 9 0 a b ", +" c d e f 6 < ) ! * ~ - { [ } | g h i j ", +" k l m n f 6 < ) ! * = - { o } | p q r ", +"s t u v w x y < ) ! * ~ - { [ } z A B C ", +" D E F G H I J ! * ~ - { 5 K L M N ", +" O P Q R S T U V = - { W X Y Z ", +" ` ...+.@.#.$.%.&.*.=.-. ", +" ;.>.,.'.).!.~.{.]. ", +" ^./.(._.:.<. ", +" [.}.|. ", +" 1. ", +" ", +" ", +" "}; diff -Nru muse-2.1.2/xpm/.directory muse-3.0.2+ds1/xpm/.directory --- muse-2.1.2/xpm/.directory 2013-03-28 15:16:17.000000000 +0000 +++ muse-3.0.2+ds1/xpm/.directory 2017-12-04 21:01:19.000000000 +0000 @@ -1,8 +1,10 @@ [Dolphin] AdditionalInfo=Details_Size,Details_Date,CustomizedDetails -PreviewsShown=true -Timestamp=2013,2,6,22,27,20 +SortRole=type +Timestamp=2017,3,13,13,10,41 +Version=3 ViewMode=1 +VisibleRoles=Details_text,Details_size,Details_date,Details_type,CustomizedDetails [Settings] HiddenFilesShown=true diff -Nru muse-2.1.2/xpm/midi_thru_off5.xpm muse-3.0.2+ds1/xpm/midi_thru_off5.xpm --- muse-2.1.2/xpm/midi_thru_off5.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/midi_thru_off5.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,18 @@ +/* XPM */ +static const char * midi_thru_off5_xpm[] = { +"9 11 4 1", +" c None", +". c #000000", +"+ c #5E2B2B", +"@ c #553E3E", +" . ", +" .. ", +" .+. ", +"....++. ", +".+++@++. ", +".@@@@@++.", +".@@@@@+. ", +"....@+. ", +" .+. ", +" .. ", +" . "}; diff -Nru muse-2.1.2/xpm/midi_thru_on5.xpm muse-3.0.2+ds1/xpm/midi_thru_on5.xpm --- muse-2.1.2/xpm/midi_thru_on5.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/midi_thru_on5.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,18 @@ +/* XPM */ +static const char * midi_thru_on5_xpm[] = { +"9 11 4 1", +" c None", +". c #000000", +"+ c #FF0C0B", +"@ c #F18888", +" . ", +" .. ", +" .+. ", +"....++. ", +".+++@++. ", +".@@@@@++.", +".@@@@@+. ", +"....@+. ", +" .+. ", +" .. ", +" . "}; diff -Nru muse-2.1.2/xpm/pianoNew.xpm muse-3.0.2+ds1/xpm/pianoNew.xpm --- muse-2.1.2/xpm/pianoNew.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/pianoNew.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,179 @@ +/* XPM */ +static const char * pianoNew_xpm[] = { +"24 24 152 2", +" g None", +". g #424242", +"+ g #3F3F3F", +"@ g #3E3E3E", +"# g #373737", +"$ g #2D2D2D", +"% g #222222", +"& g #F6F6F6", +"* g #B7B7B7", +"= g #444444", +"- g #343434", +"; g #313131", +"> g #959595", +", g #F2F2F2", +"' g #CACACA", +") g #4C4C4C", +"! g #191919", +"~ g #0E0E0E", +"{ g #606060", +"] g #E9E9E9", +"^ g #FFFFFF", +"/ g #ACACAC", +"( g #262626", +"_ g #232323", +": g #909090", +"< g #FBFBFB", +"[ g #CFCFCF", +"} g #080808", +"| g #020202", +"1 g #484848", +"2 g #E8E8E8", +"3 g #E1E1E1", +"4 g #EDEDED", +"5 g #979797", +"6 g #161616", +"7 g #181818", +"8 g #868686", +"9 g #FAFAFA", +"0 g #CECECE", +"a g #010101", +"b g #000000", +"c g #333333", +"d g #D8D8D8", +"e g #F3F3F3", +"f g #F7F7F7", +"g g #818181", +"h g #121212", +"i g #0C0C0C", +"j g #090909", +"k g #0B0B0B", +"l g #7C7C7C", +"m g #F8F8F8", +"n g #202020", +"o g #C3C3C3", +"p g #6B6B6B", +"q g #050505", +"r g #777777", +"s g #D1D1D1", +"t g #141414", +"u g #ADADAD", +"v g #FCFCFC", +"w g #EBEBEB", +"x g #595959", +"y g #040404", +"z g #737373", +"A g #D2D2D2", +"B g #353535", +"C g #949494", +"D g #FDFDFD", +"E g #494949", +"F g #030303", +"G g #707070", +"H g #D3D3D3", +"I g #383838", +"J g #797979", +"K g #DCDCDC", +"L g #111111", +"M g #151515", +"N g #787878", +"O g #F1F1F1", +"P g #D5D5D5", +"Q g #101010", +"R g #717171", +"S g #F0F0F0", +"T g #E3E3E3", +"U g #6A6A6A", +"V g #323232", +"W g #919191", +"X g #DBDBDB", +"Y g #666666", +"Z g #8E8E8E", +"` g #E0E0E0", +" . g #ECECEC", +".. g #A1A1A1", +"+. g #747474", +"@. g #616161", +"#. g #7A7A7A", +"$. g #BABABA", +"%. g #E2E2E2", +"&. g #969696", +"*. g #696969", +"=. g #585858", +"-. g #727272", +";. g #7D7D7D", +">. g #BFBFBF", +",. g #F5F5F5", +"'. g #E5E5E5", +"). g #E6E6E6", +"!. g #A3A3A3", +"~. g #A6A6A6", +"{. g #EFEFEF", +"]. g #DDDDDD", +"^. g #D6D6D6", +"/. g #F4F4F4", +"(. g #A8A8A8", +"_. g #B3B3B3", +":. g #DFDFDF", +"<. g #EAEAEA", +"[. g #E7E7E7", +"}. g #E4E4E4", +"|. g #9C9C9C", +"1. g #B4B4B4", +"2. g #CDCDCD", +"3. g #DADADA", +"4. g #989898", +"5. g #B6B6B6", +"6. g #DEDEDE", +"7. g #C7C7C7", +"8. g #C8C8C8", +"9. g #C1C1C1", +"0. g #B9B9B9", +"a. g #7F7F7F", +"b. g #C4C4C4", +"c. g #C0C0C0", +"d. g #BDBDBD", +"e. g #BBBBBB", +"f. g #C9C9C9", +"g. g #C5C5C5", +"h. g #9D9D9D", +"i. g #9E9E9E", +"j. g #9F9F9F", +"k. g #8B8B8B", +"l. g #A5A5A5", +"m. g #A4A4A4", +"n. g #A0A0A0", +"o. g #676767", +"p. g #757575", +"q. g #929292", +"r. g #CCCCCC", +"s. g #D4D4D4", +"t. g #808080", +"u. g #8C8C8C", +" ", +" ", +" . + @ # $ % ", +" & * = - ; - > , ' ) % ! ~ { ] ", +" ^ / - ( _ ( : < [ + ~ } | 1 2 3 ", +" 4 < 5 % ! 6 7 8 9 0 - a b b c d 4 ", +" e f g h i j k l m [ ; b b b n o & ", +" f , p q | a q r f s c b b b t u v ", +" < w x a b b y z & A B b b b k C v ", +" D 3 E b b b F G e H I b b b y J & ", +" ^ K ) L h h M N O P 1 Q h h t R S ", +" ^ T U V - c # W O X Y V - c B Z , ` ", +" D ...+.@.{ #.$.O %.&.*.=.-.;.>.,.'. ", +" m , ).K !.~.'.w {.] ].o Z ^.w {.e ] ", +" ` /.{. .2 (._./.S .2 ).H &.:.9 e {.<. ", +" T S w 2 %..._.S .] '.%.A > ^.,.{. .] ", +" '. .[.}.].|.1.4 <.).%.:.A > 2., .] [. ", +" [.<.'.T 3.4.5.4 ] '.3 6.H 5 7.O <.[.).>. ", +" 8.7.o 9.0.a.|.7.b.c.d.e._.g ~.0 f.g.b./ ", +" h.i.i.j.4.p k.(.~.~.l.m.n.o.p.~.l.!...q. ", +" 8.' ' r.o k._.s.H H s.s.s t.u.s.s.s.P d. ", +" ", +" ", +" "}; diff -Nru muse-2.1.2/xpm/presetsNew.xpm muse-3.0.2+ds1/xpm/presetsNew.xpm --- muse-2.1.2/xpm/presetsNew.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/presetsNew.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,140 @@ +/* XPM */ +static const char * presetsNew_xpm[] = { +"24 24 113 2", +" c None", +". c #8DB4DA", +"+ c #81ACD8", +"@ c #7FABD7", +"# c #7EAAD7", +"$ c #7BA9D7", +"% c #7EABD7", +"& c #7AA8D7", +"* c #7FABD6", +"= c #EBEBEA", +"- c #F0F0F0", +"; c #EFEFEF", +"> c #EEEEEE", +", c #ECECEC", +"' c #EBEBEB", +") c #D4D4D4", +"! c #4584C3", +"~ c #3A7EC3", +"{ c #377BC2", +"] c #367BC2", +"^ c #377CC2", +"/ c #4182C5", +"( c #4686C7", +"_ c #4F8BC7", +": c #E3E3E3", +"< c #E9EAE9", +"[ c #E7E7E7", +"} c #E4E4E4", +"| c #E1E1E1", +"1 c #DEDEDE", +"2 c #CACACA", +"3 c #3D7DC0", +"4 c #3178C0", +"5 c #2F76C0", +"6 c #347AC1", +"7 c #3B7FC4", +"8 c #4385C6", +"9 c #4E8AC6", +"0 c #E5E5E5", +"a c #E2E2E2", +"b c #E0DFE0", +"c c #DCDCDC", +"d c #DADADA", +"e c #C7C7C7", +"f c #3E7EC1", +"g c #3379C0", +"h c #3177C0", +"i c #357BC1", +"j c #3D80C4", +"k c #4485C7", +"l c #4E8BC6", +"m c #DBDBDB", +"n c #DFDFDF", +"o c #DDDDDD", +"p c #D8D8D8", +"q c #C5C5C5", +"r c #4181C2", +"s c #3379C1", +"t c #3279C0", +"u c #397EC2", +"v c #4082C5", +"w c #4687C7", +"x c #508CC7", +"y c #DDDDDE", +"z c #D6D6D6", +"A c #C3C3C3", +"B c #4684C4", +"C c #397DC3", +"D c #3A7FC3", +"E c #3E82C5", +"F c #4A8AC9", +"G c #538EC8", +"H c #D5D5D5", +"I c #D9D9D9", +"J c #D7D7D7", +"K c #D5D5D4", +"L c #C1C1C1", +"M c #4E87C2", +"N c #4784C2", +"O c #4482C1", +"P c #4583C2", +"Q c #4885C3", +"R c #4C88C4", +"S c #518CC6", +"T c #598FC6", +"U c #C4C4C4", +"V c #C9C8C9", +"W c #C8C8C8", +"X c #C6C6C6", +"Y c #B4B4B4", +"Z c #D6D6D5", +"` c #E1E1E0", +" . c #E0E0DF", +".. c #DEDEDD", +"+. c #DDDDDC", +"@. c #EDEDED", +"#. c #EAEBEA", +"$. c #E0E0E0", +"%. c #E6E6E6", +"&. c #DEDDDE", +"*. c #CECECE", +"=. c #CCCCCC", +"-. c #D4D3D3", +";. c #DFDEDF", +">. c #D0D0D0", +",. c #DBDCDB", +"'. c #CBCBCB", +"). c #D5D6D5", +"!. c #D4D5D5", +"~. c #D4D4D5", +"{. c #D2D2D2", +"]. c #CFCFCF", +"^. c #C3C4C4", +" ", +" ", +" . + @ @ # $ % & * = - - ; > , ' ' ) ", +" ! ~ { ] ^ ~ / ( _ : ' ' < [ } | 1 2 ", +" 3 4 5 5 5 6 7 8 9 1 0 0 } a b c d e ", +" f g 5 5 h i j k l m | | | n o d p q ", +" r ^ s t 6 u v w x p y y o c d p z A ", +" B j ~ C D E k F G H d d I p J K ) L ", +" M N O O P Q R S T U V W W e X U A Y ", +" ", +" ", +" Z ` ` .n ..+.+.I ", +" | > > @.#.[ : $.) ", +" I [ %.%.} | &.m *. ", +" z : : a | 1 c I =. ", +" -.n ;.;.o m I J 2 ", +" >.,.m m I p z H W ", +" '.).!.~.) {.>.].^. ", +" ", +" ", +" ", +" ", +" ", +" "}; diff -Nru muse-2.1.2/xpm/record.xpm muse-3.0.2+ds1/xpm/record.xpm --- muse-2.1.2/xpm/record.xpm 2013-03-28 15:16:17.000000000 +0000 +++ muse-3.0.2+ds1/xpm/record.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -1,160 +1,22 @@ /* XPM */ static const char * record_xpm[] = { -"15 15 142 2", -" c None", -". c #9B0500", -"+ c #900601", -"@ c #AF0205", -"# c #980401", -"$ c #AF0206", -"% c #A70205", -"& c #A80204", -"* c #A9040A", -"= c #990502", -"- c #A00400", -"; c #CC0004", -"> c #A30404", -", c #BD0212", -"' c #B20207", -") c #AC0201", -"! c #A30800", -"~ c #A70409", -"{ c #990500", -"] c #D50002", -"^ c #DB0400", -"/ c #E70004", -"( c #F20010", -"_ c #E30018", -": c #E00010", -"< c #C00202", -"[ c #AB0200", -"} c #AB0800", -"| c #A90401", -"1 c #A50406", -"2 c #B90201", -"3 c #DE1800", -"4 c #E62300", -"5 c #F10900", -"6 c #FF0016", -"7 c #E60000", -"8 c #E40800", -"9 c #E10900", -"0 c #BB0600", -"a c #BE0200", -"b c #B00200", -"c c #DA0400", -"d c #EB2500", -"e c #F33000", -"f c #F80D00", -"g c #FF0018", -"h c #FE0F00", -"i c #F11000", -"j c #E40F00", -"k c #DF0B00", -"l c #B30200", -"m c #A20200", -"n c #BB0304", -"o c #A00403", -"p c #E40000", -"q c #F90B00", -"r c #FF0F00", -"s c #F90006", -"t c #FB0012", -"u c #F40D00", -"v c #EE0800", -"w c #E70000", -"x c #DA0000", -"y c #D00004", -"z c #AC0500", -"A c #A40202", -"B c #B40207", -"C c #F40010", -"D c #FD0014", -"E c #FF0008", -"F c #F70009", -"G c #EE0010", -"H c #D6000F", -"I c #CF0009", -"J c #A80800", -"K c #BB0211", -"L c #E10018", -"M c #EA0000", -"N c #F10B00", -"O c #EE3000", -"P c #EF0F00", -"Q c #F5000D", -"R c #DA0010", -"S c #B10202", -"T c #A50200", -"U c #A70600", -"V c #9B0403", -"W c #970500", -"X c #E20010", -"Y c #E30800", -"Z c #EF1000", -"` c #F10800", -" . c #FF0009", -".. c #F01000", -"+. c #E10800", -"@. c #DE0000", -"#. c #DA0004", -"$. c #B80207", -"%. c #AF0207", -"&. c #EC0004", -"*. c #AB0204", -"=. c #B30207", -"-. c #E20800", -";. c #E20F00", -">. c #E50002", -",. c #EB000F", -"'. c #EF000D", -"). c #E00000", -"!. c #D70F00", -"~. c #B90500", -"{. c #B10200", -"]. c #AF020C", -"^. c #A90201", -"/. c #A70200", -"(. c #DA0B00", -"_. c #E20D00", -":. c #D90000", -"<. c #D5000F", -"[. c #DD0010", -"}. c #DC0004", -"|. c #D30400", -"1. c #AD0200", -"2. c #AE0204", -"3. c #B3020A", -"4. c #9A0600", -"5. c #AC0800", -"6. c #AF0200", -"7. c #C90000", -"8. c #CD0004", -"9. c #D10009", -"0. c #CA0004", -"a. c #B50207", -"b. c #B40200", -"c. c #9C0400", -"d. c #A90400", -"e. c #A30200", -"f. c #AA0200", -"g. c #B6020E", -"h. c #A90402", -"i. c #A50800", -"j. c #AB0600", -"k. c #B50302", -" ", -" . + @ # $ % & ", -" * = - ; > , - ' ) ! ", -" ~ { ] ^ / ( _ : < [ } ", -" | 1 2 3 4 5 6 7 8 9 0 - a ", -" > b c d e f g h i j k l m n ", -" o - p q r s t u v w x y [ z ", -" A B C 6 g D t E F G H I [ J ", -" o K L M r N E O P Q R S T U ", -" V W X Y Z ` ...+.@.#.$.%.&.", -" *.=.< -.;.>.,.'.).!.~.{.]. ", -" ^./.(._.:.<.[.}.|.1.2.3. ", -" 4.5.6.7.8.9.0.a.b.c.} ", -" d.e.6.b e.f.g.3. ", -" h.z i.j.k. "}; +"15 15 4 1", +" c None", +". c #9F4D4D", +"+ c #9D2424", +"@ c #9B0000", +" .+. ", +" .@@@@@. ", +" .@@@@@@@@@. ", +" @@@@@@@@@@@ ", +" .@@@@@@@@@@@. ", +" @@@@@@@@@@@@@ ", +".@@@@@@@@@@@@@.", +"+@@@@@@@@@@@@@+", +".@@@@@@@@@@@@@.", +" @@@@@@@@@@@@@ ", +" .@@@@@@@@@@@. ", +" @@@@@@@@@@@ ", +" .@@@@@@@@@. ", +" .@@@@@. ", +" .+. "}; diff -Nru muse-2.1.2/xpm/router_filter_destination_routes.xpm muse-3.0.2+ds1/xpm/router_filter_destination_routes.xpm --- muse-2.1.2/xpm/router_filter_destination_routes.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/router_filter_destination_routes.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,24 @@ +/* XPM */ +static const char * router_filter_destination_routes_xpm[] = { +"16 16 5 1", +" c None", +". c #000000", +"+ c #828282", +"@ c #FFFFFF", +"# c #FFFF00", +"................", +".+++++++.@@@@@@.", +".###++++.@@@@@@.", +".+++#+++.@....@.", +".####+++.@@@@@@.", +".+++#+++.@@@@@@.", +".++++#++.@@@@@@.", +".#######.@....@.", +".++++#++.@@@@@@.", +".+++#+++.@@@@@@.", +".####+++.@@@@@@.", +".+++#+++.@...@@.", +".###++++.@@@@@@.", +".+++++++.@@@@@@.", +".+++++++.@@@@@@.", +"................"}; diff -Nru muse-2.1.2/xpm/router_filter_destination.xpm muse-3.0.2+ds1/xpm/router_filter_destination.xpm --- muse-2.1.2/xpm/router_filter_destination.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/router_filter_destination.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,24 @@ +/* XPM */ +static const char * router_filter_destination_xpm[] = { +"16 16 5 1", +" c None", +". c #000000", +"+ c #A0A0A4", +"@ c #DCDCDC", +"# c #FFFFFF", +" .... ", +" +.@##@.+ ", +" +.##++##. ", +" .@####+#@.", +" .########.", +" .########.", +" .@######@.", +" +.######.+", +" ...@##@.+ ", +" ...+....+ ", +" ...++++++ ", +" ...++ ", +" ...++ ", +" ...++ ", +" +.++ ", +" ++ "}; diff -Nru muse-2.1.2/xpm/router_filter_source_routes.xpm muse-3.0.2+ds1/xpm/router_filter_source_routes.xpm --- muse-2.1.2/xpm/router_filter_source_routes.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/router_filter_source_routes.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,24 @@ +/* XPM */ +static const char * router_filter_source_routes_xpm[] = { +"16 16 5 1", +" c None", +". c #000000", +"+ c #FFFFFF", +"@ c #828282", +"# c #FFFF00", +"................", +".++++++.@@@@@@@.", +".++++++.@@@@###.", +".+....+.@@@#@@@.", +".++++++.@@@####.", +".++++++.@@@#@@@.", +".++++++.@@#@@@@.", +".+....+.#######.", +".++++++.@@#@@@@.", +".++++++.@@@#@@@.", +".++++++.@@@####.", +".+...++.@@@#@@@.", +".++++++.@@@@###.", +".++++++.@@@@@@@.", +".++++++.@@@@@@@.", +"................"}; diff -Nru muse-2.1.2/xpm/router_filter_source.xpm muse-3.0.2+ds1/xpm/router_filter_source.xpm --- muse-2.1.2/xpm/router_filter_source.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/router_filter_source.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,24 @@ +/* XPM */ +static const char * router_filter_source_xpm[] = { +"16 16 5 1", +" c None", +". c #000000", +"+ c #A0A0A4", +"@ c #DCDCDC", +"# c #FFFFFF", +" .... ", +" +.@##@.+ ", +" .##++##.+ ", +".@#+####@. ", +".########. ", +".########. ", +".@######@. ", +"+.######.+ ", +" +.@##@... ", +" +....+... ", +" ++++++... ", +" ++... ", +" ++... ", +" ++... ", +" ++.+ ", +" ++ "}; diff -Nru muse-2.1.2/xpm/router_view_splitter.xpm muse-3.0.2+ds1/xpm/router_view_splitter.xpm --- muse-2.1.2/xpm/router_view_splitter.xpm 1970-01-01 00:00:00.000000000 +0000 +++ muse-3.0.2+ds1/xpm/router_view_splitter.xpm 2017-12-04 21:01:19.000000000 +0000 @@ -0,0 +1,39 @@ +/* XPM */ +static const char * router_view_splitter_xpm[] = { +"16 16 20 1", +" c None", +". c #ECE1DA", +"+ c #C1BAB5", +"@ c #EFE5DE", +"# c #BDB5B1", +"$ c #FAEFEC", +"% c #B8AFAB", +"& c #FEF3EF", +"* c #B4A9A5", +"= c #000000", +"- c #FFFEFA", +"; c #AFA5A0", +"> c #FFFFFF", +", c #A59C96", +"' c #ABA09A", +") c #F7ECE8", +"! c #E5D6D3", +"~ c #C6BEBB", +"{ c #D3C4BD", +"] c #D0CAC7", +" ", +" .+. ", +" @#@ ", +" $%$ ", +" &*& ", +" = -;- = ", +" == >,> == ", +" =====>'>===== ", +" == -;- == ", +" = &*& = ", +" )%) ", +" @#@ ", +" .+. ", +" !~! ", +" {]{ ", +" "};