diff options
author | Tim E. Real <termtech@rogers.com> | 2012-10-20 04:10:19 +0000 |
---|---|---|
committer | Tim E. Real <termtech@rogers.com> | 2012-10-20 04:10:19 +0000 |
commit | c4aca7b81e76e0f098ac0f855599bdf1e3cf1e22 (patch) | |
tree | 177c77d19a5867866cff997c01a187fc54530759 /muse2/muse | |
parent | 5821f678ca1c95a97170588a77303e2f14e4dbdb (diff) |
Improved: Midi initializations. New settings options, can be 'quiet'. Complete rewrite of initializations coding.
Improved: Midi controller graphs: Control selector 'S' popup now stay-open, AND NOW with multi-coloured dots.
Bonus! Pianoroll and drum edit 'Ctrl' buttons ALSO now popup this very same menu.
Improved: 'Old' drum track 'drum controllers' display and operation: Fixed several problems.
Diffstat (limited to 'muse2/muse')
42 files changed, 2555 insertions, 809 deletions
diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index dd94f743..296b4eff 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -1107,6 +1107,9 @@ void MusE::loadProjectFile(const QString& name, bool songTemplate, bool doReadMi QApplication::restoreOverrideCursor(); + // Prompt and send init sequences. + MusEGlobal::audio->msgInitMidiDevices(false); + if (MusEGlobal::song->getSongInfo().length()>0 && MusEGlobal::song->showSongInfoOnStartup()) { startSongInfo(false); } @@ -1285,7 +1288,6 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool doReadM } } } - } //--------------------------------------------------------- diff --git a/muse2/muse/arranger/tlist.cpp b/muse2/muse/arranger/tlist.cpp index dc65b0c1..b3be35ea 100644 --- a/muse2/muse/arranger/tlist.cpp +++ b/muse2/muse/arranger/tlist.cpp @@ -369,7 +369,7 @@ void TList::paint(const QRect& r) drawCenteredPixmap(p, blacksquareIcon, r); else if (track->solo()) - drawCenteredPixmap(p, bluedotIcon, r); + drawCenteredPixmap(p, checkSquareIcon, r); break; case COL_TIMELOCK: if (track->isMidiTrack() @@ -1151,7 +1151,10 @@ void TList::portsPopupMenu(MusECore::Track* t, int x, int y) MusEGlobal::audio->msgIdle(false); MusEGlobal::audio->msgUpdateSoloStates(); // (p4.0.14) p4.0.17 MusEGlobal::song->update(SC_MIDI_TRACK_PROP); // - } + } + + // Prompt and send init sequences. + MusEGlobal::audio->msgInitMidiDevices(false); } break; diff --git a/muse2/muse/audio.cpp b/muse2/muse/audio.cpp index b03366e1..b9f1ee8c 100644 --- a/muse2/muse/audio.cpp +++ b/muse2/muse/audio.cpp @@ -706,7 +706,7 @@ void Audio::processMsg(AudioMsg* msg) } break; case SEQM_INIT_DEVICES: - initDevices(); + initDevices(msg->a); break; case SEQM_MIDI_LOCAL_OFF: sendLocalOff(); diff --git a/muse2/muse/audio.h b/muse2/muse/audio.h index 7c3d73ce..a403e5bf 100644 --- a/muse2/muse/audio.h +++ b/muse2/muse/audio.h @@ -278,7 +278,7 @@ class Audio { void msgUndo(); void msgRedo(); void msgLocalOff(); - void msgInitMidiDevices(); + void msgInitMidiDevices(bool force = true); void msgResetMidiDevices(); void msgIdle(bool); void msgBounce(); @@ -330,7 +330,7 @@ class Audio { bool freewheel() const { return _freewheel; } void setFreewheel(bool val); int getFrameOffset() const { return frameOffset; } - void initDevices(); + void initDevices(bool force = true); AudioOutput* audioMaster() const { return _audioMaster; } AudioOutput* audioMonitor() const { return _audioMonitor; } diff --git a/muse2/muse/conf.cpp b/muse2/muse/conf.cpp index 21a50e3d..4c14cff0 100644 --- a/muse2/muse/conf.cpp +++ b/muse2/muse/conf.cpp @@ -888,6 +888,12 @@ void readConfiguration(Xml& xml, bool doReadMidiPortConfig, bool doReadGlobalCon MusEGlobal::config.guiDivision = xml.parseInt(); else if (tag == "rtcTicks") MusEGlobal::config.rtcTicks = 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 == "minMeter") MusEGlobal::config.minMeter = xml.parseInt(); else if (tag == "minSlider") @@ -1206,6 +1212,9 @@ void MusE::writeGlobalConfiguration(int level, MusECore::Xml& xml) const xml.intTag(level, "division", MusEGlobal::config.division); xml.intTag(level, "rtcTicks", MusEGlobal::config.rtcTicks); + xml.intTag(level, "midiSendInit", MusEGlobal::config.midiSendInit); + xml.intTag(level, "warnInitPending", MusEGlobal::config.warnInitPending); + xml.intTag(level, "midiSendCtlDefaults", MusEGlobal::config.midiSendCtlDefaults); xml.intTag(level, "minMeter", MusEGlobal::config.minMeter); xml.doubleTag(level, "minSlider", MusEGlobal::config.minSlider); xml.intTag(level, "freewheelMode", MusEGlobal::config.freewheelMode); diff --git a/muse2/muse/confmport.cpp b/muse2/muse/confmport.cpp index cd5c95e9..b0e68b09 100644 --- a/muse2/muse/confmport.cpp +++ b/muse2/muse/confmport.cpp @@ -69,12 +69,33 @@ enum { DEVCOL_NO = 0, DEVCOL_GUI, DEVCOL_REC, DEVCOL_PLAY, DEVCOL_INSTR, DEVCOL_ //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- + void MPConfig::closeEvent(QCloseEvent *event) { + apply(); QSettings settings("MusE", "MusE-qt"); settings.setValue("MPConfig/geometry", saveGeometry()); QWidget::closeEvent(event); } + +//--------------------------------------------------------- +// apply +//--------------------------------------------------------- + +void MPConfig::apply() +{ + MusEGlobal::audio->msgInitMidiDevices(false); // false = Don't force +} + +//--------------------------------------------------------- +// okClicked +//--------------------------------------------------------- + +void MPConfig::okClicked() +{ + close(); +} + //--------------------------------------------------------- // changeDefInputRoutes //--------------------------------------------------------- @@ -1116,6 +1137,10 @@ MPConfig::MPConfig(QWidget* parent) connect(synthList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(addInstanceClicked())); connect(removeInstance, SIGNAL(clicked()), SLOT(removeInstanceClicked())); connect(instanceList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(removeInstanceClicked())); + + connect(applyButton, SIGNAL(clicked()), SLOT(apply())); + connect(okButton, SIGNAL(clicked()), SLOT(okClicked())); + songChanged(SC_CONFIG); } diff --git a/muse2/muse/confmport.h b/muse2/muse/confmport.h index 04147090..39e29bb6 100644 --- a/muse2/muse/confmport.h +++ b/muse2/muse/confmport.h @@ -68,6 +68,9 @@ class MPConfig : public QDialog, Ui::SynthConfigBase { void removeInstanceClicked(); void changeDefInputRoutes(QAction* act); void changeDefOutputRoutes(QAction* act); + void apply(); + void okClicked(); + public slots: void closeEvent(QCloseEvent*e); diff --git a/muse2/muse/ctrl/ctrlcanvas.cpp b/muse2/muse/ctrl/ctrlcanvas.cpp index 1b243204..1774174f 100644 --- a/muse2/muse/ctrl/ctrlcanvas.cpp +++ b/muse2/muse/ctrl/ctrlcanvas.cpp @@ -40,6 +40,7 @@ #include "ctrlpanel.h" #include "midiedit/drummap.h" #include "drumedit.h" +#include "drummap.h" static MusECore::MidiCtrlValList veloList(MusECore::CTRL_VELOCITY); // dummy @@ -513,13 +514,13 @@ void CtrlCanvas::partControllers(const MusECore::MidiPart* part, int num, int* d if(!mt->isDrumTrack() && curDrumPitch != -1) printf("keyfilter != -1 in non drum track?\n"); - if((mt->type() == MusECore::Track::DRUM) && (curDrumPitch > 0) && ((num & 0xff) == 0xff)) + if((mt->type() == MusECore::Track::DRUM) && (curDrumPitch >= 0) && ((num & 0xff) == 0xff)) { di = (num & ~0xff) | curDrumPitch; n = (num & ~0xff) | MusEGlobal::drumMap[curDrumPitch].anote; // construct real controller number mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[curDrumPitch].port]; } - else if ((mt->type() == MusECore::Track::NEW_DRUM) && (curDrumPitch > 0) && ((num & 0xff) == 0xff)) //FINDMICHJETZT does this work? + else if ((mt->type() == MusECore::Track::NEW_DRUM) && (curDrumPitch >= 0) && ((num & 0xff) == 0xff)) //FINDMICHJETZT does this work? { di = (num & ~0xff) | curDrumPitch; n = (num & ~0xff) | curDrumPitch; @@ -598,7 +599,7 @@ void CtrlCanvas::updateItems() if(_cnum == MusECore::CTRL_VELOCITY && e.type() == MusECore::Note) { newev = 0; - if (curDrumPitch == -1) // and NOT >0 + if (curDrumPitch == -1) // and NOT >=0 { // This is interesting - it would allow ALL drum note velocities to be shown. // But currently the drum list ALWAYS has a selected item so this is not supposed to happen. @@ -609,8 +610,40 @@ void CtrlCanvas::updateItems() if(newev && e.selected()) selection.push_back(newev); } - else if (e.type() == MusECore::Controller && e.dataA() == _didx) + else if (e.type() == MusECore::Controller) { + int ctl = e.dataA(); + if(part->track() && part->track()->type() == MusECore::Track::DRUM && (_cnum & 0xff) == 0xff) + { + //MusECore::MidiPort* port = &MusEGlobal::midiPorts[part->track()->outPort()]; + if(curDrumPitch < 0) + //if(curDrumPitch >= 0) + continue; + + //{ + //MusECore::MidiPort* port = &MusEGlobal::midiPorts[MusEGlobal::drumMap[curDrumPitch].port]; + //MusECore::MidiPort* port = &MusEGlobal::midiPorts[MusEGlobal::drumMap[ctl & 0x7f].port]; + int port = MusEGlobal::drumMap[ctl & 0x7f].port; + int chan = MusEGlobal::drumMap[ctl & 0x7f].channel; + //MusECore::MidiPort* cur_port = &MusEGlobal::midiPorts[MusEGlobal::drumMap[curDrumPitch].port]; + int cur_port = MusEGlobal::drumMap[curDrumPitch].port; + int cur_chan = MusEGlobal::drumMap[curDrumPitch].channel; + if((port != cur_port) || (chan != cur_chan)) + continue; + // Is it a drum controller event, according to the track port's instrument? + //MusECore::MidiController *mc = port->drumController(ctl); + //if(!mc) + // continue; + //if(mc) + //{ + //if(MusEGlobal::drumMap[ctl & 0x7f].channel != e. + // continue; + ctl = (ctl & ~0xff) | MusEGlobal::drumMap[ctl & 0x7f].anote; + //} + //} + } + if(ctl == _dnum) + { if(mcvl && last.empty()) { lastce = new CEvent(MusECore::Event(), part, mcvl->value(part->tick())); @@ -624,6 +657,7 @@ void CtrlCanvas::updateItems() if(e.selected()) selection.push_back(lastce); last = e; + } } } } @@ -1577,18 +1611,23 @@ void CtrlCanvas::pdrawItems(QPainter& p, const QRect& rect, const MusECore::Midi MusECore::MidiTrack* mt = part->track(); MusECore::MidiPort* mp; + int cnum = _cnum; + bool is_drum_ctl = (mt->type() == MusECore::Track::DRUM) && (curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff); - if((mt->type() == MusECore::Track::DRUM) && (curDrumPitch > 0) && ((_cnum & 0xff) == 0xff)) - mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[curDrumPitch].port]; + if(is_drum_ctl) + { + mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[curDrumPitch].port]; + cnum = (_cnum & ~0xff) | MusEGlobal::drumMap[curDrumPitch].anote; + } else mp = &MusEGlobal::midiPorts[mt->outPort()]; - MusECore::MidiController* mc = mp->midiController(_cnum); + MusECore::MidiController* mc = mp->midiController(cnum); int min; int max; int bias; - if(_cnum == MusECore::CTRL_PROGRAM) + if(cnum == MusECore::CTRL_PROGRAM) { min = 1; max = 128; @@ -1609,14 +1648,15 @@ void CtrlCanvas::pdrawItems(QPainter& p, const QRect& rect, const MusECore::Midi CEvent* e = *i; // Draw unselected part controller events (lines) on top of selected part events (bars). if(e->part() != part) - { 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) + continue; int tick = mapx(!ev.empty() ? ev.tick() + e->part()->tick() : 0); int val = e->val(); int pval = val; - if(_cnum == MusECore::CTRL_PROGRAM) + if(cnum == MusECore::CTRL_PROGRAM) { if((val & 0xff) == 0xff) // What to do here? prog = 0xff should not be allowed, but may still be encountered. @@ -1629,7 +1669,7 @@ void CtrlCanvas::pdrawItems(QPainter& p, const QRect& rect, const MusECore::Midi lval = MusECore::CTRL_VAL_UNKNOWN; else { - if(_cnum == MusECore::CTRL_PROGRAM) + if(cnum == MusECore::CTRL_PROGRAM) lval = wh - ((pval - min - bias) * wh / (max - min)); else lval = wh - ((val - min - bias) * wh / (max - min)); @@ -1662,7 +1702,7 @@ void CtrlCanvas::pdrawItems(QPainter& p, const QRect& rect, const MusECore::Midi lval = MusECore::CTRL_VAL_UNKNOWN; else { - if(_cnum == MusECore::CTRL_PROGRAM) + if(cnum == MusECore::CTRL_PROGRAM) lval = wh - ((pval - min - bias) * wh / (max - min)); else lval = wh - ((val - min - bias) * wh / (max - min)); @@ -1689,6 +1729,123 @@ void CtrlCanvas::pdrawItems(QPainter& p, const QRect& rect, const MusECore::Midi } //--------------------------------------------------------- +// pdrawExtraDrumCtrlItems +//--------------------------------------------------------- + +void CtrlCanvas::pdrawExtraDrumCtrlItems(QPainter& p, const QRect& rect, const MusECore::MidiPart* part, int drum_ctl) +{ + int x = rect.x() - 1; // compensate for 3 pixel line width + int w = rect.width() + 2; + int wh = height(); + + noEvents=true; + + { + if(!part) + return; + + MusECore::MidiTrack* mt = part->track(); + MusECore::MidiPort* mp; + int cnum = _cnum; + bool is_drum_ctl = (mt->type() == MusECore::Track::DRUM) && (curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff); + + if(is_drum_ctl) + { + mp = &MusEGlobal::midiPorts[MusEGlobal::drumMap[curDrumPitch].port]; + cnum = (_cnum & ~0xff) | MusEGlobal::drumMap[curDrumPitch].anote; + } + else + mp = &MusEGlobal::midiPorts[mt->outPort()]; + + MusECore::MidiController* mc = mp->midiController(cnum); + + int min; + int max; + int bias; + if(cnum == MusECore::CTRL_PROGRAM) + { + min = 1; + max = 128; + bias = 0; + } + else + { + min = mc->minVal(); + max = mc->maxVal(); + bias = mc->bias(); + } + int x1 = rect.x(); + int lval = MusECore::CTRL_VAL_UNKNOWN; + //bool selected = false; + for (iCEvent i = items.begin(); i != items.end(); ++i) + { + noEvents=false; + CEvent* e = *i; + // Draw unselected part controller events (lines) on top of selected part events (bars). + if(e->part() != part) + continue; + 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) + continue; + if(drum_ctl != -1 && (!is_drum_ctl || (ev.type() == MusECore::Controller && ev.dataA() == _didx))) + continue; + int tick = mapx(!ev.empty() ? ev.tick() + e->part()->tick() : 0); + int val = e->val(); + int pval = val; + if(cnum == MusECore::CTRL_PROGRAM) + { + if((val & 0xff) == 0xff) + // What to do here? prog = 0xff should not be allowed, but may still be encountered. + pval = 1; + else + pval = (val & 0x7f) + 1; + } + if (tick <= x) { + if (val == MusECore::CTRL_VAL_UNKNOWN) + lval = MusECore::CTRL_VAL_UNKNOWN; + else + { + if(cnum == MusECore::CTRL_PROGRAM) + lval = wh - ((pval - min - bias) * wh / (max - min)); + else + lval = wh - ((val - min - bias) * wh / (max - min)); + } + //selected = e->selected(); + continue; + } + if (tick > x+w) + break; + + if (lval != MusECore::CTRL_VAL_UNKNOWN) + { + p.setPen(Qt::gray); + p.drawLine(x1, lval, tick, lval); + } + + x1 = tick; + if (val == MusECore::CTRL_VAL_UNKNOWN) + lval = MusECore::CTRL_VAL_UNKNOWN; + else + { + if(cnum == MusECore::CTRL_PROGRAM) + lval = wh - ((pval - min - bias) * wh / (max - min)); + else + lval = wh - ((val - min - bias) * wh / (max - min)); + } + //selected = e->selected(); + } + + if (lval != MusECore::CTRL_VAL_UNKNOWN) + { + p.setPen(Qt::gray); + p.drawLine(x1, lval, x + w, lval); + } + } +} + +//--------------------------------------------------------- // pdraw //--------------------------------------------------------- @@ -1744,6 +1901,21 @@ void CtrlCanvas::pdraw(QPainter& p, const QRect& rect) // Draw items for all parts - other than current part pdrawItems(p, rect, part, velo, !velo); } + + // Special: Draw fg drum controller items for non-current selected drum, for the current part + // FIXME TODO Finish this off, not correct yet. + if(curPart && curPart->track() && curPart->track()->type() == MusECore::Track::DRUM && + curDrumPitch >= 0 && ((_cnum & 0xff) == 0xff)) + { + int port = MusEGlobal::drumMap[curDrumPitch].port; + int anote = MusEGlobal::drumMap[curDrumPitch].anote; + for(int i = 0; i < DRUM_MAPSIZE; ++i) + { + if(i != curDrumPitch && MusEGlobal::drumMap[i].port == port && MusEGlobal::drumMap[i].anote == anote) + pdrawExtraDrumCtrlItems(p, rect, curPart, anote); + } + } + if(velo) { // Draw fg velocity items for the current part @@ -1883,24 +2055,6 @@ void CtrlCanvas::setCurDrumPitch(int instrument) else curDrumPitch = -2; // this means "invalid", but not "unused" } - - // DELETETHIS - //printf("CtrlCanvas::setCurDrumPitch curDrumPitch:%d\n", curDrumPitch); - - // - // check if current controller is only valid for - // a specific drum instrument - // - // Removed by T356. - //if(curTrack && (curTrack->type() == MusECore::Track::DRUM) && ((_controller->num() & 0xff) == 0xff)) { - //if(curTrack && (curTrack->type() == MusECore::Track::DRUM) && ((_cnum & 0xff) == 0xff)) { - // reset to default - // TODO: check, if new drum instrument has a similar controller - // configured - // _cnum = MusECore::CTRL_VELOCITY; - // } - // Removed by T356 - //songChanged(-1); } void CtrlCanvas::curPartHasChanged(MusECore::Part*) diff --git a/muse2/muse/ctrl/ctrlcanvas.h b/muse2/muse/ctrl/ctrlcanvas.h index e38edc22..d1ee2e04 100644 --- a/muse2/muse/ctrl/ctrlcanvas.h +++ b/muse2/muse/ctrl/ctrlcanvas.h @@ -132,7 +132,8 @@ class CtrlCanvas : public MusEGui::View { void deleteVal(int x1, int x2, int y); bool setCurTrackAndPart(); - void pdrawItems(QPainter&, const QRect&, const MusECore::MidiPart*, bool, bool); + void pdrawItems(QPainter& p, const QRect& rect, const MusECore::MidiPart* part, bool velo, bool fg); + void pdrawExtraDrumCtrlItems(QPainter& p, const QRect& rect, const MusECore::MidiPart* part, int drum_ctl); void partControllers(const MusECore::MidiPart*, int, int*, int*, MusECore::MidiController**, MusECore::MidiCtrlValList**); diff --git a/muse2/muse/ctrl/ctrlpanel.cpp b/muse2/muse/ctrl/ctrlpanel.cpp index 0fc66b3a..cb14200e 100644 --- a/muse2/muse/ctrl/ctrlpanel.cpp +++ b/muse2/muse/ctrl/ctrlpanel.cpp @@ -56,6 +56,7 @@ #include "audio.h" #include "menutitleitem.h" #include "popupmenu.h" +#include "helper.h" namespace MusEGui { @@ -197,12 +198,7 @@ void CtrlPanel::heartBeat() // Auto bias... v -= _ctrl->bias(); if (double(v) != _knob->value()) - { - // Added by Tim. p3.3.6 - //printf("CtrlPanel::heartBeat setting knob\n"); - _knob->setValue(double(v)); - } } } else if(v != _val) @@ -522,469 +518,174 @@ void CtrlPanel::setHeight(int h) setFixedHeight(h); } -#if 0 // DELETETHIS. yeah, really! -// when flo added the new style drumtracks in trunk, he changed a -// lot of things. he didn't update that disabled area here, so -// after releasing 2.0, when we continue developing on trunk, -// then the below code is not only disabled but INVALID (as in WRONG) - -/* WARNING: INVALID CODE! *\ - * the code which has been disabled by the above #if 0 is partly * - * OBSOLETE! it lacks support for new-style drum tracks, especially * - * the drum-controller-handling for these! * - * * - * when you ever enable that code again, first check the changes * - * flo93 did somewhere between revision 1188 and 1188+something * - * (let's say, 1195; it's NOT the revision in which this comment * - * has been introduced) in experimental to the currently enabled * - * code below. then apply similar changes to the currently disabled * -\* code here! */ -#error "INVALID CODE. please check the comment in ctrlpanel.cpp which starts with 'WARNING: INVALID CODE'" -just to be sure: dear compiler, please refuse to compile. -dear user: read the comment above! - -struct CI { - QString s; - bool used; - CI(const QString& ss, bool u) : s(ss), used(u) {} - }; - -//--------------------------------------------------------- -// ctrlPopup -//--------------------------------------------------------- - +//--------------------------------------------------- +// ctrlPopup +//--------------------------------------------------- + void CtrlPanel::ctrlPopup() { - //--------------------------------------------------- - // build list of midi controllers for current - // MusECore::MidiPort/channel - //--------------------------------------------------- - MusECore::PartList* parts = editor->parts(); MusECore::Part* part = editor->curCanvasPart(); - MusECore::MidiTrack* track = (MusECore::MidiTrack*)(part->track()); - int channel = track->outChannel(); - MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; - int curDrumPitch = ctrlcanvas->getCurDrumPitch(); - bool isDrum = track->type() == MusECore::Track::DRUM; - bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; - - QMenu* pop = new QMenu; - //pop->clear(); - pop->addAction(tr("Velocity"))->setData(1); + int curDrumPitch = ctrlcanvas->getCurDrumPitch(); - MidiCtrlValListList* cll = port->controller(); - int min = channel << 24; - int max = min + 0x1000000; - - std::list<CI> sList; - typedef std::list<CI>::iterator isList; - - for (MusECore::iMidiCtrlValList i = cll->lower_bound(min); i != cll->lower_bound(max); ++i) { - MidiCtrlValList* cl = i->second; - MusECore::MidiController* c = port->midiController(cl->num()); - // dont show drum specific controller if not a drum track - if ((c->num() & 0xff) == 0xff) { - if (isDrum) - { - // only show controller for curDrumPitch: - if ((curDrumPitch == -1) || ((cl->num() & 0xff) != MusEGlobal::drumMap[curDrumPitch].anote)) - continue; - } - else if (isNewDrum) - { - // only show controller for curDrumPitch: FINDMICH does this work? - if ((curDrumPitch == -1) || ((cl->num() & 0xff) != curDrumPitch)) - continue; - } - else - continue; - } - isList i = sList.begin(); - for (; i != sList.end(); ++i) { - if (i->s == c->name()) - break; - } - if (i == sList.end()) { - bool used = false; - for (MusECore::iPart ip = parts->begin(); ip != parts->end(); ++ip) { - MusECore::EventList* el = ip->second->events(); - for (MusECore::iEvent ie = el->begin(); ie != el->end(); ++ie) { - MusECore::Event e = ie->second; - if ((e.type() == MusECore::Controller) && (e.dataA() == cl->num())) { - used = true; - break; - } - } - if (used) - break; - } - sList.push_back(CI(c->name(), used)); - } - } - for (isList i = sList.begin(); i != sList.end(); ++i) { - if (i->used) - pop->addAction(QIcon(*greendotIcon), i->s); - else - pop->addAction(i->s); - } - - pop->addAction(QIcon(*configureIcon), tr("add new ..."))->setData(2); - QAction *act = pop->exec(selCtrl->mapToGlobal(QPoint(0,0))); + PopupMenu* pup = new PopupMenu(true); // true = enable stay open. Don't bother with parent. + int est_width = populateMidiCtrlMenu(pup, parts, part, curDrumPitch); + QPoint ep = mapToGlobal(QPoint(0,0)); + //int newx = ep.x() - ctrlMainPop->width(); // Too much! Width says 640. Maybe because it hasn't been shown yet . + int newx = ep.x() - est_width; + if(newx < 0) + newx = 0; + ep.setX(newx); + connect(pup, SIGNAL(triggered(QAction*)), SLOT(ctrlPopupTriggered(QAction*))); + pup->exec(ep); + delete pup; selCtrl->setDown(false); - - if (!act) - { - delete pop; - return; - } - - int rv = act->data().toInt(); - QString s = act->text(); - delete pop; - - if (rv == 1) { // special case velocity - emit controllerChanged(MusECore::CTRL_VELOCITY); - } - else if (rv == 2) { - // - // add new controller - // - QMenu* pop1 = new QMenu(this); - //pop1->setCheckable(false); // Qt4 doc says not needed. - // - // populate popup with all controllers available for - // current instrument - // - MidiInstrument* instr = port->instrument(); - MusECore::MidiControllerList* mcl = instr->controller(); - for (iMusECore::MidiController ci = mcl->begin(); ci != mcl->end(); ++ci) - { - int num = ci->second->num(); - if (isDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) - num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; - if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) //FINDMICHJETZT does this work? - num = (num & ~0xff) + curDrumPitch; - - if(cll->find(channel, num) == cll->end()) - pop1->addAction(ci->second->name()); - } - QAction *act2 = pop1->exec(selCtrl->mapToGlobal(QPoint(0,0))); - if (act2) { - QString s = act2->text(); - MusECore::MidiController* c; - for (iMusECore::MidiController ci = mcl->begin(); ci != mcl->end(); ++ci) { - c = ci->second; - if (c->name() == s) { - int num = c->num(); - if (isDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) - num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; - if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) //FINDMICHJETZT does this work? - num = (num & ~0xff) + curDrumPitch; - - if(cll->find(channel, num) == cll->end()) - { - MidiCtrlValList* vl = new MidiCtrlValList(num); - - cll->add(channel, vl); - emit controllerChanged(c->num()); - //MusEGlobal::song->update(SC_MIDI_CONTROLLER_ADD); - } - else - emit controllerChanged(c->num()); - break; - } - } - } - delete pop1; - } - else { - ///QString s = act->text(); - MusECore::iMidiCtrlValList i = cll->begin(); - for (; i != cll->end(); ++i) { - MidiCtrlValList* cl = i->second; - MusECore::MidiController* c = port->midiController(cl->num()); - if (c->name() == s) { - emit controllerChanged(c->num()); - break; - } - } - if (i == cll->end()) { - printf("CtrlPanel: controller %s not found!", s.toLatin1().constData()); - } - } } -#else // p4.0.25 Tim -struct CI { - int num; - QString s; - bool used; - bool instrument; - CI(int n, const QString& ss, bool u, bool i) : num(n), s(ss), used(u), instrument(i) {} - }; - -void CtrlPanel::ctrlPopup() - { - //--------------------------------------------------- - // build list of midi controllers for current - // MusECore::MidiPort/channel - //--------------------------------------------------- - - MusECore::PartList* parts = editor->parts(); - MusECore::Part* part = editor->curCanvasPart(); - MusECore::MidiTrack* track = (MusECore::MidiTrack*)(part->track()); - int channel = track->outChannel(); - MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; - int curDrumPitch = ctrlcanvas->getCurDrumPitch(); - bool isDrum = track->type() == MusECore::Track::DRUM; - bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; - MusECore::MidiInstrument* instr = port->instrument(); - MusECore::MidiControllerList* mcl = instr->controller(); - - MusECore::MidiCtrlValListList* cll = port->controller(); - int min = channel << 24; - int max = min + 0x1000000; - - std::list<CI> sList; - typedef std::list<CI>::iterator isList; - - for (MusECore::iMidiCtrlValList it = cll->lower_bound(min); it != cll->lower_bound(max); ++it) { - MusECore::MidiCtrlValList* cl = it->second; - MusECore::MidiController* c = port->midiController(cl->num()); - // dont show drum specific controller if not a drum track - if ((c->num() & 0xff) == 0xff) { - if (isDrum) - { - // only show controller for curDrumPitch: - if ((curDrumPitch == -1) || ((cl->num() & 0xff) != MusEGlobal::drumMap[curDrumPitch].anote)) - continue; - } - else if (isNewDrum) - { - // only show controller for curDrumPitch: FINDMICH does this work? - if ((curDrumPitch == -1) || ((cl->num() & 0xff) != curDrumPitch)) - continue; - } - else - continue; - } - isList i = sList.begin(); - for (; i != sList.end(); ++i) { - //if (i->s == c->name()) - if (i->num == c->num()) - break; - } - if (i == sList.end()) { - bool used = false; - for (MusECore::iPart ip = parts->begin(); ip != parts->end(); ++ip) { - MusECore::EventList* el = ip->second->events(); - for (MusECore::iEvent ie = el->begin(); ie != el->end(); ++ie) { - MusECore::Event e = ie->second; - if ((e.type() == MusECore::Controller) && (e.dataA() == cl->num())) { - used = true; - break; - } - } - if (used) - break; - } - //sList.push_back(CI(c->name(), used)); - bool isinstr = ( mcl->find(c->num()) != mcl->end() ); - int cnum = c->num(); - // 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(cnum, - isinstr ? MusECore::midiCtrlNumString(cnum, true) + c->name() : MusECore::midiCtrlName(cnum, true), - used, isinstr)); - } - } - - MusEGui::PopupMenu* ctrlMainPop = new MusEGui::PopupMenu; - - //ctrlMainPop->addSeparator(); - ctrlMainPop->addAction(new MusEGui::MenuTitleItem(tr("Instrument-defined"), ctrlMainPop)); - - //ctrlMainPop->addAction(QIcon(*configureIcon), tr("Add ..."))->setData(max + 1); - - // Add instrument-defined controllers. - for (isList i = sList.begin(); i != sList.end(); ++i) - { - if(!i->instrument) - continue; - if (i->used) - ctrlMainPop->addAction(QIcon(*greendotIcon), i->s)->setData(i->num); - else - ctrlMainPop->addAction(i->s)->setData(i->num); - } - - ctrlMainPop->addAction(QIcon(*configureIcon), tr("Add ..."))->setData(max + 1); - //ctrlMainPop->addAction(QIcon(*midi_edit_instrumentIcon), tr("Edit instruments"))->setData(max + 2); - - ctrlMainPop->addSeparator(); - ctrlMainPop->addAction(new MusEGui::MenuTitleItem(tr("Others"), ctrlMainPop)); - - //ctrlMainPop->addAction(QIcon(*configureIcon), tr("Add ..."))->setData(max + 3); - - ctrlMainPop->addAction(tr("Velocity"))->setData(max); - - // Add global default controllers (all controllers not found in instrument). - for (isList i = sList.begin(); i != sList.end(); ++i) - { - if(i->instrument) - continue; - if (i->used) - ctrlMainPop->addAction(QIcon(*greendotIcon), i->s)->setData(i->num); - else - ctrlMainPop->addAction(i->s)->setData(i->num); - } - - ctrlMainPop->addAction(QIcon(*configureIcon), tr("Add ..."))->setData(max + 3); +//--------------------------------------------------------- +// ctrlPopupTriggered +//--------------------------------------------------------- - //connect(ctrlMainPop, SIGNAL(hovered(QAction*)), SLOT(ctrlMainPopHovered(QAction*))); - - QAction *act = ctrlMainPop->exec(selCtrl->mapToGlobal(QPoint(0,0))); - selCtrl->setDown(false); - - if (!act) - { - delete ctrlMainPop; - return; - } - - int rv = act->data().toInt(); - delete ctrlMainPop; - - if (rv == max) { // special case velocity - emit controllerChanged(MusECore::CTRL_VELOCITY); - } - else if (rv == max + 1) { // add new instrument controller - - MusEGui::PopupMenu * ctrlSubPop = new MusEGui::PopupMenu(this); - ctrlSubPop->addAction(new MusEGui::MenuTitleItem(tr("Instrument-defined"), ctrlSubPop)); - - // - // populate popup with all controllers available for - // current instrument - // - - //ctrlSubPop->addAction(QIcon(*midi_edit_instrumentIcon), tr("Edit instruments"))->setData(max + 2); - - for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) +void CtrlPanel::ctrlPopupTriggered(QAction* act) +{ + if(!act || (act->data().toInt() == -1)) + return; + + MusECore::Part* part = editor->curCanvasPart(); + MusECore::MidiTrack* track = (MusECore::MidiTrack*)(part->track()); + int channel = track->outChannel(); + MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; + int curDrumPitch = ctrlcanvas->getCurDrumPitch(); + bool isDrum = track->type() == MusECore::Track::DRUM; + bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; + MusECore::MidiInstrument* instr = port->instrument(); + MusECore::MidiControllerList* mcl = instr->controller(); + + MusECore::MidiCtrlValListList* cll = port->controller(); + int min = channel << 24; + int max = min + 0x1000000; + + int rv = act->data().toInt(); + + if (rv == max) { // special case velocity + emit controllerChanged(MusECore::CTRL_VELOCITY); + } + else if (rv == max + 1) { // add new instrument controller + + PopupMenu * ctrlSubPop = new PopupMenu(this, true); // true = enable stay open + ctrlSubPop->addAction(new MenuTitleItem(tr("Instrument-defined"), ctrlSubPop)); + + // + // populate popup with all controllers available for + // current instrument + // + + for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) + { + int num = ci->second->num(); + if((num & 0xff) == 0xff) { - int num = ci->second->num(); - if((num & 0xff) == 0xff) - { - if (isDrum && curDrumPitch!=-1) - num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; - else if (isNewDrum && curDrumPitch!=-1) - num = (num & ~0xff) + curDrumPitch; //FINDMICH does this work? - else // dont show drum specific controller if not a drum track - continue; - } - - if(cll->find(channel, num) == cll->end()) - ctrlSubPop->addAction(MusECore::midiCtrlNumString(num, true) + ci->second->name())->setData(num); - } - - // Don't allow editing instrument if it's a synth - if(!port->device() || port->device()->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) - ctrlSubPop->addAction(QIcon(*midi_edit_instrumentIcon), tr("Edit instrument ..."))->setData(max + 2); - - //connect(ctrlSubPop, SIGNAL(hovered(QAction*)), SLOT(ctrlSubPopHovered(QAction*))); - - QAction *act2 = ctrlSubPop->exec(selCtrl->mapToGlobal(QPoint(0,0))); - if (act2) + if (isDrum && curDrumPitch!=-1) + num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; + else if (isNewDrum && curDrumPitch!=-1) + num = (num & ~0xff) + curDrumPitch; //FINDMICH does this work? + else // dont show drum specific controller if not a drum track + continue; + } + + if(cll->find(channel, num) == cll->end()) + ctrlSubPop->addAction(MusECore::midiCtrlNumString(num, true) + ci->second->name())->setData(num); + } + + // Don't allow editing instrument if it's a synth + if(!port->device() || port->device()->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) + ctrlSubPop->addAction(QIcon(*midi_edit_instrumentIcon), tr("Edit instrument ..."))->setData(max + 2); + + QAction *act2 = ctrlSubPop->exec(selCtrl->mapToGlobal(QPoint(0,0))); + if (act2) + { + int rv2 = act2->data().toInt(); + + if (rv2 == max + 2) // edit instrument + MusEGlobal::muse->startEditInstrument(); + else // select new instrument control + { + MusECore::MidiController* c; + for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) { - int rv2 = act2->data().toInt(); - - if (rv2 == max + 2) // edit instrument - MusEGlobal::muse->startEditInstrument(); - else // select new instrument control - { - MusECore::MidiController* c; - for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) - { - c = ci->second; - int num = c->num(); - if (isDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) - num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; - else if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) - num = (num & ~0xff) + curDrumPitch; //FINDMICHJETZT does this work? - - if(num != rv2) - continue; - - if(cll->find(channel, num) == cll->end()) - { - MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); - - cll->add(channel, vl); - emit controllerChanged(c->num()); - //MusEGlobal::song->update(SC_MIDI_CONTROLLER_ADD); - } - else - emit controllerChanged(c->num()); - break; - } - } - } - delete ctrlSubPop; - } - - //else if (rv == max + 2) // edit instrument - // MusEGlobal::muse->startEditInstrument(); - - else if (rv == max + 3) { // add new other controller - MusEGui::PopupMenu* ctrlSubPop = new MusEGui::PopupMenu(this); - ctrlSubPop->addAction(new MusEGui::MenuTitleItem(tr("Common Controls"), ctrlSubPop)); - - for(int num = 0; num < 127; ++num) - if(cll->find(channel, num) == cll->end()) - ctrlSubPop->addAction(MusECore::midiCtrlName(num, true))->setData(num); - QAction *act2 = ctrlSubPop->exec(selCtrl->mapToGlobal(QPoint(0,0))); - if (act2) { - int rv2 = act2->data().toInt(); - int num = rv2; + c = ci->second; + int num = c->num(); if (isDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; - if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + else if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) num = (num & ~0xff) + curDrumPitch; //FINDMICHJETZT does this work? - + + if(num != rv2) + continue; + if(cll->find(channel, num) == cll->end()) { MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); cll->add(channel, vl); - emit controllerChanged(rv2); - //MusEGlobal::song->update(SC_MIDI_CONTROLLER_ADD); + emit controllerChanged(c->num()); } else - emit controllerChanged(rv2); - } - delete ctrlSubPop; + emit controllerChanged(c->num()); + break; } - else { // Select a control - //QString s = act->text(); - MusECore::iMidiCtrlValList i = cll->begin(); - for (; i != cll->end(); ++i) { - MusECore::MidiCtrlValList* cl = i->second; - MusECore::MidiController* c = port->midiController(cl->num()); - //if (c->name() == s) { - if (c->num() == rv) { - emit controllerChanged(c->num()); - break; - } - } - if (i == cll->end()) { - //printf("CtrlPanel: controller %s not found!", s.toLatin1().constData()); - printf("CtrlPanel: controller number %d not found!", rv); - } - } - } -#endif + } + } + delete ctrlSubPop; + } + + //else if (rv == max + 2) // edit instrument + // MusEGlobal::muse->startEditInstrument(); + + else if (rv == max + 3) { // add new other controller + PopupMenu* ctrlSubPop = new PopupMenu(this, true); // true = enable stay open + ctrlSubPop->addAction(new MenuTitleItem(tr("Common Controls"), ctrlSubPop)); + + for(int num = 0; num < 127; ++num) + if(cll->find(channel, num) == cll->end()) + ctrlSubPop->addAction(MusECore::midiCtrlName(num, true))->setData(num); + QAction *act2 = ctrlSubPop->exec(selCtrl->mapToGlobal(QPoint(0,0))); + if (act2) { + int rv2 = act2->data().toInt(); + int num = rv2; + if (isDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; + if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + curDrumPitch; //FINDMICHJETZT does this work? + + if(cll->find(channel, num) == cll->end()) + { + MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); + + cll->add(channel, vl); + emit controllerChanged(rv2); + } + else + emit controllerChanged(rv2); + } + delete ctrlSubPop; + } + else { // Select a control + MusECore::iMidiCtrlValList i = cll->begin(); + for (; i != cll->end(); ++i) { + MusECore::MidiCtrlValList* cl = i->second; + MusECore::MidiController* c = port->midiController(cl->num()); + if (c->num() == rv) { + emit controllerChanged(c->num()); + break; + } + } + if (i == cll->end()) { + printf("CtrlPanel: controller number %d not found!", rv); + } + } + +} //--------------------------------------------------------- // ctrlRightClicked diff --git a/muse2/muse/ctrl/ctrlpanel.h b/muse2/muse/ctrl/ctrlpanel.h index 1a5245dc..58b8d8c7 100644 --- a/muse2/muse/ctrl/ctrlpanel.h +++ b/muse2/muse/ctrl/ctrlpanel.h @@ -26,6 +26,7 @@ #include <QWidget> class QPushButton; +class QAction; namespace MusECore { class MidiController; @@ -68,6 +69,7 @@ class CtrlPanel: public QWidget { void ctrlChanged(double val); void labelDoubleClicked(); void ctrlRightClicked(const QPoint& p, int id); + void ctrlPopupTriggered(QAction* act); protected slots: virtual void heartBeat(); diff --git a/muse2/muse/gconfig.cpp b/muse2/muse/gconfig.cpp index ff5545ef..c88c3fc5 100644 --- a/muse2/muse/gconfig.cpp +++ b/muse2/muse/gconfig.cpp @@ -127,6 +127,9 @@ GlobalConfigValues config = { 384, // division; 1024, // rtcTicks + 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 -60, // int minMeter; -60.0, // double minSlider; false, // use Jack freewheel diff --git a/muse2/muse/gconfig.h b/muse2/muse/gconfig.h index a9d2c0a0..6fa85846 100644 --- a/muse2/muse/gconfig.h +++ b/muse2/muse/gconfig.h @@ -136,6 +136,9 @@ struct GlobalConfigValues { int division; int rtcTicks; + 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 int minMeter; double minSlider; bool freewheelMode; diff --git a/muse2/muse/helper.cpp b/muse2/muse/helper.cpp index ae2b0352..21986831 100644 --- a/muse2/muse/helper.cpp +++ b/muse2/muse/helper.cpp @@ -40,6 +40,8 @@ #include "audiodev.h" #include "midi.h" #include "midiseq.h" +#include "popupmenu.h" +#include "menutitleitem.h" #include <QMenu> #include <QApplication> @@ -733,12 +735,8 @@ void populateMidiPorts() } #else // this code is disabled -// DELETETHIS uhm, yeah... do we need this? -DISABLED AND MAYBE OUT-OF-DATE CODE! -the code below is disabled for a longer period of time, -there were certain changes and merges. dunno if that code -works at all. before activating it again, intensively -verify whether its still okay! + +// Please don't remove this section as it may be improved. // ------------------------------------------------------------------------------------------------------- // populateMidiPorts() @@ -865,5 +863,198 @@ void populateMidiPorts() #endif // populateMidiPorts +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) {} + }; + +//--------------------------------------------------- +// populateMidiCtrlMenu +// Returns estimated width of the completed menu. +//--------------------------------------------------- + +int populateMidiCtrlMenu(PopupMenu* menu, MusECore::PartList* part_list, MusECore::Part* cur_part, int curDrumPitch) + { + //--------------------------------------------------- + // build list of midi controllers for current + // MusECore::MidiPort/channel + //--------------------------------------------------- + + MusECore::MidiTrack* track = (MusECore::MidiTrack*)(cur_part->track()); + int channel = track->outChannel(); + MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; + bool isDrum = track->type() == MusECore::Track::DRUM; + bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; + MusECore::MidiInstrument* instr = port->instrument(); + MusECore::MidiControllerList* mcl = instr->controller(); + + MusECore::MidiCtrlValListList* cll = port->controller(); + int min = channel << 24; + int max = min + 0x1000000; + + int est_width = 0; + + std::list<CI> sList; + typedef std::list<CI>::iterator isList; + + for (MusECore::iMidiCtrlValList it = cll->lower_bound(min); it != cll->lower_bound(max); ++it) { + MusECore::MidiCtrlValList* cl = it->second; + MusECore::MidiController* c = port->midiController(cl->num()); + bool isDrumCtrl = ((c->num() & 0xff) == 0xff); + + // dont show drum specific controller if not a drum track + if (isDrumCtrl) { + if (isDrum) + { + // only show controller for curDrumPitch: + if ((curDrumPitch == -1) || ((cl->num() & 0xff) != MusEGlobal::drumMap[curDrumPitch].anote)) + continue; + } + else if (isNewDrum) + { + // only show controller for curDrumPitch: FINDMICH does this work? + if ((curDrumPitch == -1) || ((cl->num() & 0xff) != curDrumPitch)) + continue; + } + else + continue; + } + isList i = sList.begin(); + for (; i != sList.end(); ++i) { + //if (i->s == c->name()) + if (i->num == c->num()) + break; + } + + //printf("\n** c->num():%d cl->num:%d\n", c->num(), cl->num()); + if (i == sList.end()) { + bool off = cl->hwVal() == MusECore::CTRL_VAL_UNKNOWN; // Does it have a value or is it 'off' + 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; + if(e.type() != MusECore::Controller) + continue; + //e.dump(); + int ctl_num = e.dataA(); + if(!isNewDrum) + { + // Is it a drum controller event, according to the track port's instrument? + MusECore::MidiController *mc = port->drumController(ctl_num); + if(mc) + { + if((ctl_num & 0xff) != curDrumPitch) + continue; + // Change the controller event's index into the drum map to an instrument note. + ctl_num = (ctl_num & ~0xff) | MusEGlobal::drumMap[ctl_num & 0x7f].anote; + } + } + if(ctl_num == cl->num()) + { + used = true; + break; + } + + } + if (used) + break; + } + bool isinstr = ( mcl->find(c->num()) != mcl->end() ); + int cnum = c->num(); + // 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(cnum, + isinstr ? MusECore::midiCtrlNumString(cnum, true) + c->name() : MusECore::midiCtrlName(cnum, true), + used, off, isinstr)); + } + } + + QString stext = QWidget::tr("Instrument-defined"); + int fmw = menu->fontMetrics().width(stext); + if(fmw > est_width) + est_width = fmw; + + menu->addAction(new MenuTitleItem(stext, menu)); + + // Add instrument-defined controllers. + for (isList i = sList.begin(); i != sList.end(); ++i) + { + if(!i->instrument) + continue; + + fmw = menu->fontMetrics().width(i->s); + if(fmw > est_width) + est_width = fmw; + + if (i->used && !i->off) + menu->addAction(QIcon(*orangedotIcon), i->s)->setData(i->num); + else if (i->used) + menu->addAction(QIcon(*greendotIcon), i->s)->setData(i->num); + else if(!i->off) + menu->addAction(QIcon(*bluedotIcon), i->s)->setData(i->num); + else + menu->addAction(i->s)->setData(i->num); + } + + stext = QWidget::tr("Add ..."); + fmw = menu->fontMetrics().width(stext); + if(fmw > est_width) + est_width = fmw; + menu->addAction(QIcon(*configureIcon), stext)->setData(max + 1); + + menu->addSeparator(); + + stext = QWidget::tr("Others"); + fmw = menu->fontMetrics().width(stext); + if(fmw > est_width) + est_width = fmw; + menu->addAction(new MenuTitleItem(stext, menu)); + + stext = QWidget::tr("Velocity"); + fmw = menu->fontMetrics().width(stext); + if(fmw > est_width) + est_width = fmw; + menu->addAction(stext)->setData(max); + + // Add global default controllers (all controllers not found in instrument). + for (isList i = sList.begin(); i != sList.end(); ++i) + { + if(i->instrument) + continue; + + fmw = menu->fontMetrics().width(i->s); + if(fmw > est_width) + est_width = fmw; + + if (i->used && !i->off) + menu->addAction(QIcon(*orangedotIcon), i->s)->setData(i->num); + else if (i->used) + menu->addAction(QIcon(*greendotIcon), i->s)->setData(i->num); + else if(!i->off) + menu->addAction(QIcon(*bluedotIcon), i->s)->setData(i->num); + else + menu->addAction(i->s)->setData(i->num); + } + + stext = QWidget::tr("Add ..."); + fmw = menu->fontMetrics().width(stext); + if(fmw > est_width) + est_width = fmw; + menu->addAction(QIcon(*configureIcon), stext)->setData(max + 3); + + est_width += 60; // Add about 60 for the coloured lights on the left. + + return est_width; + } + + + } // namespace MusEGui diff --git a/muse2/muse/helper.h b/muse2/muse/helper.h index 8671dce3..0eab7ee2 100644 --- a/muse2/muse/helper.h +++ b/muse2/muse/helper.h @@ -38,7 +38,7 @@ class QWidget; namespace MusECore { class Part; class Track; - +class PartList; QString pitch2string(int v); Part* partFromSerialNumber(int serial); @@ -63,6 +63,8 @@ void record_controller_change_and_maybe_send(unsigned tick, int ctrl_num, int va } namespace MusEGui { +class PopupMenu; + QMenu* populateAddSynth(QWidget* parent); QActionGroup* populateAddTrack(QMenu* addTrack, bool populateAll=false, bool evenIgnoreDrumPreference=false); QStringList localizedStringListFromCharArray(const char** array, const char* context); @@ -73,6 +75,7 @@ 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); } #endif diff --git a/muse2/muse/icons.cpp b/muse2/muse/icons.cpp index cfe30495..dab1067b 100644 --- a/muse2/muse/icons.cpp +++ b/muse2/muse/icons.cpp @@ -189,9 +189,11 @@ //#include "xpm/darkgreendot.xpm" #include "xpm/bluedot.xpm" #include "xpm/graydot.xpm" +#include "xpm/orangedot.xpm" #include "xpm/off.xpm" #include "xpm/blacksquare.xpm" #include "xpm/blacksqcheck.xpm" +#include "xpm/checksquare.xpm" #include "xpm/mastertrackS.xpm" #include "xpm/localoffS.xpm" @@ -425,9 +427,11 @@ QPixmap* greendotIcon; //QPixmap* darkgreendotIcon; QPixmap* graydotIcon; QPixmap* bluedotIcon; +QPixmap* orangedotIcon; QPixmap* offIcon; QPixmap* blacksquareIcon; QPixmap* blacksqcheckIcon; +QPixmap* checkSquareIcon; QPixmap* addtrack_addmiditrackIcon; QPixmap* addtrack_audiogroupIcon; @@ -648,9 +652,11 @@ void initIcons() //darkgreendotIcon = new MPIXMAP(darkgreendot_xpm, NULL); bluedotIcon = new MPIXMAP(bluedot_xpm, NULL); graydotIcon = new MPIXMAP(graydot_xpm, NULL); + orangedotIcon = new MPIXMAP(orangedot_xpm, NULL); offIcon = new MPIXMAP(off_xpm, NULL); blacksquareIcon = new MPIXMAP(blacksquare_xpm, NULL); blacksqcheckIcon = new MPIXMAP(blacksqcheck_xpm, NULL); + checkSquareIcon = new MPIXMAP(checksquare_xpm, NULL); mastertrackSIcon = new MPIXMAP(mastertrackS_xpm, NULL); localoffSIcon = new MPIXMAP(localoffS_xpm, NULL); @@ -885,10 +891,12 @@ void deleteIcons() //delete darkgreendotIcon; delete bluedotIcon; delete graydotIcon; + delete orangedotIcon; delete offIcon; delete blacksquareIcon; delete blacksqcheckIcon; - + delete checkSquareIcon; + delete mastertrackSIcon; delete localoffSIcon; delete miditransformSIcon; diff --git a/muse2/muse/icons.h b/muse2/muse/icons.h index 3ee7cfea..a6c95577 100644 --- a/muse2/muse/icons.h +++ b/muse2/muse/icons.h @@ -170,9 +170,11 @@ extern QPixmap* greendotIcon; //extern QPixmap* darkgreendotIcon; extern QPixmap* graydotIcon; extern QPixmap* bluedotIcon; +extern QPixmap* orangedotIcon; extern QPixmap* offIcon; extern QPixmap* blacksquareIcon; extern QPixmap* blacksqcheckIcon; +extern QPixmap* checkSquareIcon; extern QPixmap* mastertrackSIcon; extern QPixmap* localoffSIcon; diff --git a/muse2/muse/midi.cpp b/muse2/muse/midi.cpp index f1d5124d..33b98c00 100644 --- a/muse2/muse/midi.cpp +++ b/muse2/muse/midi.cpp @@ -169,7 +169,7 @@ QString nameSysex(unsigned int len, const unsigned char* buf) 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; // p4.0.27 + 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; @@ -521,7 +521,7 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, printf("ERROR: THIS SHOULD NEVER HAPPEN: k==i in midi.cpp:buildMidiEventList()\n"); else mel.erase(k); - i = mel.begin(); // p4.0.34 + i = mel.begin(); continue; } } @@ -592,49 +592,15 @@ void Audio::panic() //--------------------------------------------------------- // initDevices -// - called on seek to position 0 +// - called when instrument init sequences plus controller +// defaults should be checked and/or sent // - called from arranger pulldown menu //--------------------------------------------------------- -void Audio::initDevices() +void Audio::initDevices(bool force) { - // - // mark all used ports - // - bool activePorts[MIDI_PORTS]; - for (int i = 0; i < MIDI_PORTS; ++i) - activePorts[i] = false; - - MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); - for (MusECore::iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) { - MusECore::MidiTrack* track = *it; - activePorts[track->outPort()] = true; - } - if (MusEGlobal::song->click()) - activePorts[MusEGlobal::clickPort] = true; - - // - // test for explicit instrument initialization - // - for (int i = 0; i < MIDI_PORTS; ++i) { - if (!activePorts[i]) - continue; - - MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; - MusECore::MidiInstrument* instr = port->instrument(); - MidiDevice* md = port->device(); - - if (instr && md) { - EventList* events = instr->midiInit(); - if (events->empty()) - continue; - for (iEvent ie = events->begin(); ie != events->end(); ++ie) { - MusECore::MidiPlayEvent ev(0, i, 0, ie->second); - md->putEvent(ev); - } - activePorts[i] = false; // no standard initialization - } + MusEGlobal::midiPorts[i].sendPendingInitializations(force); } } @@ -992,12 +958,12 @@ void Audio::processMidi() RouteList* irl = track->inRoutes(); for(ciRoute r = irl->begin(); r != irl->end(); ++r) { - if(!r->isValid() || (r->type != Route::MIDI_PORT_ROUTE)) // p3.3.49 + if(!r->isValid() || (r->type != Route::MIDI_PORT_ROUTE)) continue; - int devport = r->midiPort; // + int devport = r->midiPort; if (devport == -1) continue; - MidiDevice* dev = MusEGlobal::midiPorts[devport].device(); // + MidiDevice* dev = MusEGlobal::midiPorts[devport].device(); if(!dev) continue; int channelMask = r->channel; @@ -1316,7 +1282,7 @@ void Audio::processMidi() for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { // We are done with the 'frozen' recording fifos, remove the events. - (*id)->afterProcess(); // p4.0.34 + (*id)->afterProcess(); // ALSA devices handled by another thread. if((*id)->deviceType() != MidiDevice::ALSA_MIDI) diff --git a/muse2/muse/mididev.cpp b/muse2/muse/mididev.cpp index 9f303e43..e8f88960 100644 --- a/muse2/muse/mididev.cpp +++ b/muse2/muse/mididev.cpp @@ -37,12 +37,14 @@ #include "config.h" #include "gconfig.h" #include "globals.h" +#include "globaldefs.h" #include "audio.h" #include "audiodev.h" #include "midiseq.h" #include "sync.h" #include "midiitransform.h" #include "part.h" +#include "drummap.h" namespace MusEGlobal { MusECore::MidiDeviceList midiDevices; @@ -131,6 +133,13 @@ MidiDevice::MidiDevice(const QString& n) init(); } +void MidiDevice::setPort(int p) +{ + _port = p; + if(_port != -1) + MusEGlobal::midiPorts[_port].clearInitSent(); +} + //--------------------------------------------------------- // filterEvent // return true if event filtered @@ -372,7 +381,7 @@ void MidiDeviceList::remove(MidiDevice* dev) // sendNullRPNParams //--------------------------------------------------------- -bool MidiDevice::sendNullRPNParams(int chn, bool nrpn) +bool MidiDevice::sendNullRPNParams(unsigned time, int port, int chn, bool nrpn) { if(_port == -1) return false; @@ -386,16 +395,16 @@ bool MidiDevice::sendNullRPNParams(int chn, bool nrpn) if(nvh != 0xff) { if(nrpn) - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f)); + putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_HNRPN, nvh & 0x7f)); else - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f)); + putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_HRPN, nvh & 0x7f)); } if(nvl != 0xff) { if(nrpn) - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f)); + putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_LNRPN, nvl & 0x7f)); else - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f)); + putMidiEvent(MidiPlayEvent(time, port, chn, ME_CONTROLLER, CTRL_LRPN, nvl & 0x7f)); } return true; } @@ -437,22 +446,25 @@ bool MidiDevice::putEvent(const MidiPlayEvent& ev) //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(0, 0, chn, ME_PITCHBEND, b, 0)); + return putMidiEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, 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(0, 0, chn, ME_CONTROLLER, CTRL_HBANK, hb)); + putMidiEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb)); if (lb != 0xff) - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LBANK, lb)); - return putMidiEvent(MidiPlayEvent(0, 0, chn, ME_PROGRAM, pr, 0)); + 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 @@ -464,52 +476,52 @@ bool MidiDevice::putEvent(const MidiPlayEvent& ev) int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, ctrlH, dataH)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, ctrlL, dataL)); + 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(0, 0, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_HDATA, b)); + 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(chn, false); + 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(0, 0, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_HDATA, b)); + 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(chn, true); + 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(0, 0, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); + 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(chn, false); + 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(0, 0, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_HDATA, dataH)); - putMidiEvent(MidiPlayEvent(0, 0, chn, ME_CONTROLLER, CTRL_LDATA, dataL)); + 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(chn, true); + sendNullRPNParams(t, port, chn, true); } else { printf("putEvent: unknown controller type 0x%x\n", a); @@ -649,6 +661,7 @@ void MidiDevice::handleSeek() return; MidiPort* mp = &MusEGlobal::midiPorts[_port]; + MidiInstrument* instr = mp->instrument(); MidiCtrlValListList* cll = mp->controller(); int pos = MusEGlobal::audio->tickPos(); @@ -691,10 +704,56 @@ void MidiDevice::handleSeek() // 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) + { + if(MusEGlobal::drumMap[i].port != _port || usedChans[MusEGlobal::drumMap[i].channel]) + continue; + usedChans[MusEGlobal::drumMap[i].channel] = 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(); iMidiCtrlVal imcv = vl->iValue(pos); + //bool done = false; if(imcv != vl->end()) { Part* p = imcv->second.part; @@ -707,10 +766,30 @@ void MidiDevice::handleSeek() 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)); + { + //_playEvents.add(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, 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); - //mp->sendEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val), pos == 0 || imcv->first == pos); + mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, imcv->second.val), imcv->first == pos); + //mp->sendEvent(MidiPlayEvent(0, _port, chan, ME_CONTROLLER, ctlnum, imcv->second.val), pos == 0 || imcv->first == pos); + //done = true; + } + } + + // 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()) + // Darn, without refinement we can only do this at position 0, due to possible 'skipped' values outside parts, above. + if(imcv == vl->end() && 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); + } } } diff --git a/muse2/muse/mididev.h b/muse2/muse/mididev.h index 18a58623..2fbd817c 100644 --- a/muse2/muse/mididev.h +++ b/muse2/muse/mididev.h @@ -99,7 +99,7 @@ class MidiDevice { virtual void setName(const QString& s) { _name = s; } int midiPort() const { return _port; } - void setPort(int p) { _port = p; } + void setPort(int p); int rwFlags() const { return _rwFlags; } int openFlags() const { return _openFlags; } @@ -140,7 +140,7 @@ class MidiDevice { void setSysexFIFOProcessed(bool v) { _sysexFIFOProcessed = v; } bool sysexReadingChunks() { return _sysexReadingChunks; } void setSysexReadingChunks(bool v) { _sysexReadingChunks = v; } - bool sendNullRPNParams(int, bool); + bool sendNullRPNParams(unsigned time, int port, int chan, bool); }; //--------------------------------------------------------- diff --git a/muse2/muse/midiedit/drumedit.cpp b/muse2/muse/midiedit/drumedit.cpp index 4d7fa906..7a86f010 100644 --- a/muse2/muse/midiedit/drumedit.cpp +++ b/muse2/muse/midiedit/drumedit.cpp @@ -61,6 +61,8 @@ #include "gconfig.h" #include "functions.h" #include "helper.h" +#include "popupmenu.h" +#include "menutitleitem.h" #include "widgets/function_dialogs/quantize.h" namespace MusEGui { @@ -455,7 +457,7 @@ DrumEdit::DrumEdit(MusECore::PartList* pl, QWidget* parent, const char* name, un //--------------------------------------------------- split1 = new MusEGui::Splitter(Qt::Vertical, mainw, "split1"); - QPushButton* ctrl = new QPushButton(tr("ctrl"), mainw); + ctrl = new QPushButton(tr("ctrl"), mainw); ctrl->setObjectName("Ctrl"); ctrl->setFont(MusEGlobal::config.fonts[3]); ctrl->setFocusPolicy(Qt::NoFocus); @@ -598,7 +600,7 @@ DrumEdit::DrumEdit(MusECore::PartList* pl, QWidget* parent, const char* name, un connect(info, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(info, SIGNAL(escapePressed()), SLOT(focusCanvas())); - connect(ctrl, SIGNAL(clicked()), SLOT(addCtrl())); + connect(ctrl, SIGNAL(clicked()), SLOT(addCtrlClicked())); QClipboard* cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged())); @@ -1220,12 +1222,201 @@ void DrumEdit::selectionChanged() } //--------------------------------------------------------- +// ctrlPopupTriggered +//--------------------------------------------------------- + +void DrumEdit::ctrlPopupTriggered(QAction* act) +{ + // TODO Merge most of this with duplicate code in piano roll, + // maybe by putting it in a new function near populateMidiCtrlMenu. + + if(!act || (act->data().toInt() == -1)) + return; + + int newCtlNum = -1; + MusECore::Part* part = curCanvasPart(); + MusECore::MidiTrack* track = (MusECore::MidiTrack*)(part->track()); + int channel = track->outChannel(); + MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; + int curDrumPitch = curDrumInstrument(); + bool isDrum = track->type() == MusECore::Track::DRUM; + bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; + MusECore::MidiInstrument* instr = port->instrument(); + MusECore::MidiControllerList* mcl = instr->controller(); + + MusECore::MidiCtrlValListList* cll = port->controller(); + int min = channel << 24; + int max = min + 0x1000000; + + int rv = act->data().toInt(); + + if (rv == max) { // special case velocity + newCtlNum = MusECore::CTRL_VELOCITY; + } + else if (rv == max + 1) { // add new instrument controller + + PopupMenu * ctrlSubPop = new PopupMenu(this, true); // true = enable stay open + ctrlSubPop->addAction(new MenuTitleItem(tr("Instrument-defined"), ctrlSubPop)); + + // + // populate popup with all controllers available for + // current instrument + // + + for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) + { + int num = ci->second->num(); + if((num & 0xff) == 0xff) + { + if (isDrum && curDrumPitch!=-1) + num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; + else if (isNewDrum && curDrumPitch!=-1) + num = (num & ~0xff) + curDrumPitch; //FINDMICH does this work? + else // dont show drum specific controller if not a drum track + continue; + } + + if(cll->find(channel, num) == cll->end()) + ctrlSubPop->addAction(MusECore::midiCtrlNumString(num, true) + ci->second->name())->setData(num); + } + + // Don't allow editing instrument if it's a synth + if(!port->device() || port->device()->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) + ctrlSubPop->addAction(QIcon(*midi_edit_instrumentIcon), tr("Edit instrument ..."))->setData(max + 2); + + QAction *act2 = ctrlSubPop->exec(ctrl->mapToGlobal(QPoint(0,0))); + if (act2) + { + int rv2 = act2->data().toInt(); + + if (rv2 == max + 2) // edit instrument + MusEGlobal::muse->startEditInstrument(); + else // select new instrument control + { + MusECore::MidiController* c; + for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) + { + c = ci->second; + int num = c->num(); + if (isDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; + else if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + curDrumPitch; //FINDMICHJETZT does this work? + + if(num != rv2) + continue; + + if(cll->find(channel, num) == cll->end()) + { + MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); + + cll->add(channel, vl); + newCtlNum = c->num(); + } + else + newCtlNum = c->num(); + break; + } + } + } + delete ctrlSubPop; + } + + //else if (rv == max + 2) // edit instrument + // MusEGlobal::muse->startEditInstrument(); + + else if (rv == max + 3) { // add new other controller + PopupMenu* ctrlSubPop = new PopupMenu(this, true); // true = enable stay open + ctrlSubPop->addAction(new MenuTitleItem(tr("Common Controls"), ctrlSubPop)); + + for(int num = 0; num < 127; ++num) + if(cll->find(channel, num) == cll->end()) + ctrlSubPop->addAction(MusECore::midiCtrlName(num, true))->setData(num); + QAction *act2 = ctrlSubPop->exec(ctrl->mapToGlobal(QPoint(0,0))); + if (act2) { + int rv2 = act2->data().toInt(); + int num = rv2; + if (isDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; + if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + curDrumPitch; //FINDMICHJETZT does this work? + + if(cll->find(channel, num) == cll->end()) + { + MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); + + cll->add(channel, vl); + newCtlNum = rv2; + } + else + newCtlNum = rv2; + } + delete ctrlSubPop; + } + else { // Select a control + MusECore::iMidiCtrlValList i = cll->begin(); + for (; i != cll->end(); ++i) { + MusECore::MidiCtrlValList* cl = i->second; + MusECore::MidiController* c = port->midiController(cl->num()); + if (c->num() == rv) { + newCtlNum = c->num(); + break; + } + } + if (i == cll->end()) { + printf("DrumEdit: controller number %d not found!", rv); + } + } + + if(newCtlNum != -1) + { + CtrlEdit* ctrlEdit = new CtrlEdit(split1, this, xscale, true, "drumCtrlEdit"); + ctrlEdit->setController(newCtlNum); + setupNewCtrl(ctrlEdit); + } +} + +//--------------------------------------------------------- +// addCtrlClicked +//--------------------------------------------------------- + +void DrumEdit::addCtrlClicked() +{ + PopupMenu* pup = new PopupMenu(true); // true = enable stay open. Don't bother with parent. + connect(pup, SIGNAL(triggered(QAction*)), SLOT(ctrlPopupTriggered(QAction*))); + + int est_width = populateMidiCtrlMenu(pup, parts(), curCanvasPart(), curDrumInstrument()); + + QPoint ep = ctrl->mapToGlobal(QPoint(0,0)); + //int newx = ep.x() - pup->width(); // Too much! Width says 640. Maybe because it hasn't been shown yet . + int newx = ep.x() - est_width; + if(newx < 0) + newx = 0; + ep.setX(newx); + pup->exec(ep); + delete pup; + + ctrl->setDown(false); +} + +//--------------------------------------------------------- // addCtrl //--------------------------------------------------------- -CtrlEdit* DrumEdit::addCtrl() +CtrlEdit* DrumEdit::addCtrl(int ctl_num) { CtrlEdit* ctrlEdit = new CtrlEdit(split1, this, xscale, true, "drumCtrlEdit"); + ctrlEdit->setController(ctl_num); + setupNewCtrl(ctrlEdit); + return ctrlEdit; + } + +//--------------------------------------------------------- +// setupNewCtrl +//--------------------------------------------------------- + +void DrumEdit::setupNewCtrl(CtrlEdit* ctrlEdit) +{ connect(hscroll, SIGNAL(scrollChanged(int)), ctrlEdit, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), ctrlEdit, SLOT(setXMag(int))); connect(ctrlEdit, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); @@ -1255,9 +1446,8 @@ CtrlEdit* DrumEdit::addCtrl() ctrlEdit->show(); ctrlEditList.push_back(ctrlEdit); - return ctrlEdit; - } - +} + //--------------------------------------------------------- // removeCtrl //--------------------------------------------------------- diff --git a/muse2/muse/midiedit/drumedit.h b/muse2/muse/midiedit/drumedit.h index 7ebf2fd9..63246e2e 100644 --- a/muse2/muse/midiedit/drumedit.h +++ b/muse2/muse/midiedit/drumedit.h @@ -35,7 +35,9 @@ #include "shortcuts.h" #include "event.h" #include "dcanvas.h" +#include "midictrl.h" +class QAction; class QCloseEvent; class QLabel; class QMenu; @@ -44,6 +46,7 @@ class QResizeEvent; class QToolButton; class QWidget; class QComboBox; +class QPushButton; namespace MusECore { @@ -110,6 +113,7 @@ class DrumEdit : public MidiEditor { MusEGui::Header* header; QToolBar* tools; QComboBox *stepLenWidget; + QPushButton* ctrl; static int _rasterInit; static int _dlistWidthInit, _dcanvasWidthInit; @@ -123,6 +127,7 @@ class DrumEdit : public MidiEditor { QAction *groupNoneAction, *groupChanAction, *groupMaxAction; void initShortcuts(); + void setupNewCtrl(CtrlEdit* ctrlEdit); virtual void closeEvent(QCloseEvent*); QWidget* genToolbar(QWidget* parent); @@ -147,6 +152,8 @@ class DrumEdit : public MidiEditor { void configChanged(); void songChanged1(MusECore::SongChangedFlags_t); void setStep(QString); + void addCtrlClicked(); + void ctrlPopupTriggered(QAction* act); void updateGroupingActions(); void set_ignore_hide(bool); @@ -165,7 +172,6 @@ class DrumEdit : public MidiEditor { void execDeliveredScript(int); void execUserScript(int); void focusCanvas(); - CtrlEdit* addCtrl(); void ourDrumMapChanged(bool); virtual void updateHScrollRange(); @@ -180,6 +186,8 @@ class DrumEdit : public MidiEditor { static void readConfiguration(MusECore::Xml& xml); static void writeConfiguration(int, MusECore::Xml&); + CtrlEdit* addCtrl(int ctl_num = MusECore::CTRL_VELOCITY); + bool old_style_drummap_mode() { return _old_style_drummap_mode; } group_mode_t group_mode() { return _group_mode; } bool ignore_hide() { return _ignore_hide; } diff --git a/muse2/muse/midiedit/pianoroll.cpp b/muse2/muse/midiedit/pianoroll.cpp index 9ede827e..44b0cc66 100644 --- a/muse2/muse/midiedit/pianoroll.cpp +++ b/muse2/muse/midiedit/pianoroll.cpp @@ -62,6 +62,8 @@ #include "audio.h" #include "functions.h" #include "helper.h" +#include "popupmenu.h" +#include "menutitleitem.h" #include "cmd.h" @@ -314,7 +316,7 @@ PianoRoll::PianoRoll(MusECore::PartList* pl, QWidget* parent, const char* name, hsplitter->setChildrenCollapsible(true); hsplitter->setHandleWidth(2); - QPushButton* ctrl = new QPushButton(tr("ctrl"), mainw); + ctrl = new QPushButton(tr("ctrl"), mainw); ctrl->setObjectName("Ctrl"); ctrl->setFont(MusEGlobal::config.fonts[3]); ctrl->setToolTip(tr("Add Controller View")); @@ -401,7 +403,7 @@ PianoRoll::PianoRoll(MusECore::PartList* pl, QWidget* parent, const char* name, connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int))); - connect(ctrl, SIGNAL(clicked()), SLOT(addCtrl())); + connect(ctrl, SIGNAL(clicked()), SLOT(addCtrlClicked())); connect(info, SIGNAL(valueChanged(MusEGui::NoteInfo::ValType, int)), SLOT(noteinfoChanged(MusEGui::NoteInfo::ValType, int))); connect(info, SIGNAL(deltaModeChanged(bool)), SLOT(deltaModeChanged(bool))); @@ -793,29 +795,217 @@ void PianoRoll::noteinfoChanged(MusEGui::NoteInfo::ValType type, int val) } //--------------------------------------------------------- +// ctrlPopupTriggered +//--------------------------------------------------------- + +void PianoRoll::ctrlPopupTriggered(QAction* act) +{ + // TODO Merge most of this with duplicate code in drum edit, + // maybe by putting it in a new function near populateMidiCtrlMenu. + + if(!act || (act->data().toInt() == -1)) + return; + + int newCtlNum = -1; + MusECore::Part* part = curCanvasPart(); + MusECore::MidiTrack* track = (MusECore::MidiTrack*)(part->track()); + int channel = track->outChannel(); + MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; + int curDrumPitch = curDrumInstrument(); + bool isDrum = track->type() == MusECore::Track::DRUM; + bool isNewDrum = track->type() == MusECore::Track::NEW_DRUM; + MusECore::MidiInstrument* instr = port->instrument(); + MusECore::MidiControllerList* mcl = instr->controller(); + + MusECore::MidiCtrlValListList* cll = port->controller(); + int min = channel << 24; + int max = min + 0x1000000; + + int rv = act->data().toInt(); + + if (rv == max) { // special case velocity + newCtlNum = MusECore::CTRL_VELOCITY; + } + else if (rv == max + 1) { // add new instrument controller + + PopupMenu * ctrlSubPop = new PopupMenu(this, true); // true = enable stay open + ctrlSubPop->addAction(new MenuTitleItem(tr("Instrument-defined"), ctrlSubPop)); + + // + // populate popup with all controllers available for + // current instrument + // + + for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) + { + int num = ci->second->num(); + if((num & 0xff) == 0xff) + { + if (isDrum && curDrumPitch!=-1) + num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; + else if (isNewDrum && curDrumPitch!=-1) + num = (num & ~0xff) + curDrumPitch; //FINDMICH does this work? + else // dont show drum specific controller if not a drum track + continue; + } + + if(cll->find(channel, num) == cll->end()) + ctrlSubPop->addAction(MusECore::midiCtrlNumString(num, true) + ci->second->name())->setData(num); + } + + // Don't allow editing instrument if it's a synth + if(!port->device() || port->device()->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) + ctrlSubPop->addAction(QIcon(*midi_edit_instrumentIcon), tr("Edit instrument ..."))->setData(max + 2); + + QAction *act2 = ctrlSubPop->exec(ctrl->mapToGlobal(QPoint(0,0))); + if (act2) + { + int rv2 = act2->data().toInt(); + + if (rv2 == max + 2) // edit instrument + MusEGlobal::muse->startEditInstrument(); + else // select new instrument control + { + MusECore::MidiController* c; + for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) + { + c = ci->second; + int num = c->num(); + if (isDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; + else if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + curDrumPitch; //FINDMICHJETZT does this work? + + if(num != rv2) + continue; + + if(cll->find(channel, num) == cll->end()) + { + MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); + + cll->add(channel, vl); + newCtlNum = c->num(); + } + else + newCtlNum = c->num(); + break; + } + } + } + delete ctrlSubPop; + } + + //else if (rv == max + 2) // edit instrument + // MusEGlobal::muse->startEditInstrument(); + + else if (rv == max + 3) { // add new other controller + PopupMenu* ctrlSubPop = new PopupMenu(this, true); // true = enable stay open + ctrlSubPop->addAction(new MenuTitleItem(tr("Common Controls"), ctrlSubPop)); + + for(int num = 0; num < 127; ++num) + if(cll->find(channel, num) == cll->end()) + ctrlSubPop->addAction(MusECore::midiCtrlName(num, true))->setData(num); + QAction *act2 = ctrlSubPop->exec(ctrl->mapToGlobal(QPoint(0,0))); + if (act2) { + int rv2 = act2->data().toInt(); + int num = rv2; + if (isDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + MusEGlobal::drumMap[curDrumPitch].anote; + if (isNewDrum && ((num & 0xff) == 0xff) && curDrumPitch!=-1) + num = (num & ~0xff) + curDrumPitch; //FINDMICHJETZT does this work? + + if(cll->find(channel, num) == cll->end()) + { + MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); + + cll->add(channel, vl); + newCtlNum = rv2; + } + else + newCtlNum = rv2; + } + delete ctrlSubPop; + } + else { // Select a control + MusECore::iMidiCtrlValList i = cll->begin(); + for (; i != cll->end(); ++i) { + MusECore::MidiCtrlValList* cl = i->second; + MusECore::MidiController* c = port->midiController(cl->num()); + if (c->num() == rv) { + newCtlNum = c->num(); + break; + } + } + if (i == cll->end()) { + printf("PianoRoll: controller number %d not found!", rv); + } + } + + if(newCtlNum != -1) + { + CtrlEdit* ctrlEdit = new CtrlEdit(ctrlLane, this, xscale, false, "pianoCtrlEdit"); + ctrlEdit->setController(newCtlNum); + setupNewCtrl(ctrlEdit); + } +} + +//--------------------------------------------------------- +// addCtrlClicked +//--------------------------------------------------------- + +void PianoRoll::addCtrlClicked() +{ + PopupMenu* pup = new PopupMenu(true); // true = enable stay open. Don't bother with parent. + connect(pup, SIGNAL(triggered(QAction*)), SLOT(ctrlPopupTriggered(QAction*))); + + int est_width = populateMidiCtrlMenu(pup, parts(), curCanvasPart(), -1); // _curDrumInstrument); + + QPoint ep = ctrl->mapToGlobal(QPoint(0,0)); + //int newx = ep.x() - ctrlMainPop->width(); // Too much! Width says 640. Maybe because it hasn't been shown yet . + int newx = ep.x() - est_width; + if(newx < 0) + newx = 0; + ep.setX(newx); + pup->exec(ep); + delete pup; + + ctrl->setDown(false); +} + +//--------------------------------------------------------- // addCtrl //--------------------------------------------------------- -CtrlEdit* PianoRoll::addCtrl() +CtrlEdit* PianoRoll::addCtrl(int ctl_num) { CtrlEdit* ctrlEdit = new CtrlEdit(ctrlLane/* formerly splitter*/, this, xscale, false, "pianoCtrlEdit"); // ccharrett - connect(tools2, SIGNAL(toolChanged(int)), ctrlEdit, SLOT(setTool(int))); - connect(hscroll, SIGNAL(scrollChanged(int)), ctrlEdit, SLOT(setXPos(int))); - connect(hscroll, SIGNAL(scaleChanged(int)), ctrlEdit, SLOT(setXMag(int))); - connect(ctrlEdit, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); - connect(ctrlEdit, SIGNAL(destroyedCtrl(CtrlEdit*)), SLOT(removeCtrl(CtrlEdit*))); - connect(ctrlEdit, SIGNAL(yposChanged(int)), toolbar, SLOT(setInt(int))); - - ctrlEdit->setTool(tools2->curTool()); - ctrlEdit->setXPos(hscroll->pos()); - ctrlEdit->setXMag(hscroll->getScaleValue()); - - ctrlEdit->show(); - ctrlEditList.push_back(ctrlEdit); + ctrlEdit->setController(ctl_num); + setupNewCtrl(ctrlEdit); return ctrlEdit; } //--------------------------------------------------------- +// setupNewCtrl +//--------------------------------------------------------- + +void PianoRoll::setupNewCtrl(CtrlEdit* ctrlEdit) +{ + connect(tools2, SIGNAL(toolChanged(int)), ctrlEdit, SLOT(setTool(int))); + connect(hscroll, SIGNAL(scrollChanged(int)), ctrlEdit, SLOT(setXPos(int))); + connect(hscroll, SIGNAL(scaleChanged(int)), ctrlEdit, SLOT(setXMag(int))); + connect(ctrlEdit, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); + connect(ctrlEdit, SIGNAL(destroyedCtrl(CtrlEdit*)), SLOT(removeCtrl(CtrlEdit*))); + connect(ctrlEdit, SIGNAL(yposChanged(int)), toolbar, SLOT(setInt(int))); + + ctrlEdit->setTool(tools2->curTool()); + ctrlEdit->setXPos(hscroll->pos()); + ctrlEdit->setXMag(hscroll->getScaleValue()); + + ctrlEdit->show(); + ctrlEditList.push_back(ctrlEdit); +} + +//--------------------------------------------------------- // removeCtrl //--------------------------------------------------------- diff --git a/muse2/muse/midiedit/pianoroll.h b/muse2/muse/midiedit/pianoroll.h index 9c699724..75e3c9af 100644 --- a/muse2/muse/midiedit/pianoroll.h +++ b/muse2/muse/midiedit/pianoroll.h @@ -35,6 +35,7 @@ #include "midieditor.h" #include "tools.h" #include "event.h" +#include "midictrl.h" class QAction; class QLabel; @@ -129,7 +130,8 @@ class PianoRoll : public MidiEditor { MusEGui::Splitter* splitter; MusEGui::Splitter* hsplitter; MusEGui::Splitter* ctrlLane; - + QPushButton* ctrl; + QToolButton* speaker; QToolBar* tools; MusEGui::EditToolBar* tools2; @@ -146,6 +148,7 @@ class PianoRoll : public MidiEditor { void initShortcuts(); + void setupNewCtrl(CtrlEdit* ctrlEdit); void setEventColorMode(int); QWidget* genToolbar(QWidget* parent); virtual void closeEvent(QCloseEvent*); @@ -171,6 +174,8 @@ class PianoRoll : public MidiEditor { void toggleTrackInfo(); void updateTrackInfo(); void deltaModeChanged(bool); + void addCtrlClicked(); + void ctrlPopupTriggered(QAction* act); signals: void isDeleting(MusEGui::TopWin*); @@ -180,7 +185,6 @@ class PianoRoll : public MidiEditor { void execDeliveredScript(int id); void execUserScript(int id); void focusCanvas(); - CtrlEdit* addCtrl(); public: PianoRoll(MusECore::PartList*, QWidget* parent = 0, const char* name = 0, unsigned initPos = INT_MAX); @@ -189,6 +193,7 @@ class PianoRoll : public MidiEditor { virtual void writeStatus(int, MusECore::Xml&) const; static void readConfiguration(MusECore::Xml&); static void writeConfiguration(int, MusECore::Xml&); + CtrlEdit* addCtrl(int ctl_num = MusECore::CTRL_VELOCITY); }; } // namespace MusEGui diff --git a/muse2/muse/midiport.cpp b/muse2/muse/midiport.cpp index b36781bc..c1288e6e 100644 --- a/muse2/muse/midiport.cpp +++ b/muse2/muse/midiport.cpp @@ -21,6 +21,8 @@ // //========================================================= +#include <set> + #include <QMenu> #include <QApplication> @@ -30,13 +32,17 @@ #include "midi.h" #include "minstrument.h" #include "xml.h" +#include "gconfig.h" #include "globals.h" +#include "globaldefs.h" #include "mpevent.h" #include "synth.h" #include "app.h" #include "song.h" #include "menutitleitem.h" #include "icons.h" +#include "track.h" +#include "drummap.h" namespace MusEGlobal { MusECore::MidiPort midiPorts[MIDI_PORTS]; @@ -68,6 +74,7 @@ void initMidiPorts() MidiPort::MidiPort() : _state("not configured") { + _initializationsSent = false; _defaultInChannels = (1 << MIDI_CHANNELS) -1; // p4.0.17 Default is now to connect to all channels. _defaultOutChannels = 0; _device = 0; @@ -143,6 +150,7 @@ void MidiPort::setMidiDevice(MidiDevice* dev) _instrument = genericMidiInstrument; _device->setPort(-1); _device->close(); + _initializationsSent = false; } if (dev) { for (int i = 0; i < MIDI_PORTS; ++i) { @@ -163,70 +171,7 @@ void MidiPort::setMidiDevice(MidiDevice* dev) } _state = _device->open(); _device->setPort(portno()); - - // By T356. Send all instrument controller initial (default) values to all midi channels now, - // except where explicitly initialized in the song. - // By sending ALL instrument controller initial values, even if those controllers are NOT - // in the song, we can ensure better consistency between songs. - // For example: A song is loaded which has a 'reverb level' controller initial value of '100'. - // Then a song is loaded which has no such controller (hence no explicit initial value). - // The 'reverb level' controller would still be at '100', and could adversely affect the song, - // but if the instrument has an available initial value of say '0', it will be used instead. - // - - // NOT for syntis. Use midiState and/or initParams for that. - if(_instrument && !_device->isSynti()) - { - MidiControllerList* cl = _instrument->controller(); - MidiController* mc; - for(ciMidiController imc = cl->begin(); imc != cl->end(); ++imc) - { - mc = imc->second; - for(int chan = 0; chan < MIDI_CHANNELS; ++chan) - { - ciMidiCtrlValList i; - // Look for an initial value for this midi controller, on this midi channel, in the song... - for(i = _controller->begin(); i != _controller->end(); ++i) - { - int channel = i->first >> 24; - int cntrl = i->first & 0xffffff; - int val = i->second->hwVal(); - if(channel == chan && cntrl == mc->num() && val != CTRL_VAL_UNKNOWN) - break; - } - // If no initial value was found for this midi controller, on this midi channel, in the song... - if(i == _controller->end()) - { - // If the instrument's midi controller has an initial value, send it now. - if(mc->initVal() != CTRL_VAL_UNKNOWN) - { - int ctl = mc->num(); - // Note the addition of bias! - // Retry added. Use default attempts and delay. - _device->putEventWithRetry(MidiPlayEvent(0, portno(), chan, - ME_CONTROLLER, ctl, mc->initVal() + mc->bias())); - // 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()); - } - } - } - } - } - - // init HW controller state - for (iMidiCtrlValList i = _controller->begin(); i != _controller->end(); ++i) { - int channel = i->first >> 24; - int cntrl = i->first & 0xffffff; - int val = i->second->hwVal(); - if (val != CTRL_VAL_UNKNOWN) { - // Retry added. Use default attempts and delay. - _device->putEventWithRetry(MidiPlayEvent(0, portno(), channel, - ME_CONTROLLER, cntrl, val)); - // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... - setHwCtrlState(channel, cntrl, val); - } - } + _initializationsSent = false; } else @@ -234,12 +179,182 @@ void MidiPort::setMidiDevice(MidiDevice* dev) } //--------------------------------------------------------- +// sendPendingInitializations +// Return true if success. +//--------------------------------------------------------- + +bool MidiPort::sendPendingInitializations(bool force) +{ + if(!_device || !(_device->openFlags() & 1)) // Not writable? + return false; + + bool rv = true; + int port = portno(); + + // + // test for explicit instrument initialization + // + + unsigned last_tick = 0; + MusECore::MidiInstrument* instr = instrument(); + if(instr && MusEGlobal::config.midiSendInit && (force || !_initializationsSent)) + { + // Send the Instrument Init sequences. + EventList* events = instr->midiInit(); + if(!events->empty()) + { + 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); + } + // Give a bit of time for the last Init sysex to settle? + last_tick += 100; + } + _initializationsSent = true; // Mark as having been sent. + } + + // Send the Instrument controller default values. + sendInitialControllers(last_tick); + + return rv; +} + +//--------------------------------------------------------- +// sendInitialControllers +// Return true if success. +//--------------------------------------------------------- + +bool MidiPort::sendInitialControllers(unsigned start_time) +{ + bool rv = true; + int port = portno(); + + // Find all channels of 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) + { + if(MusEGlobal::drumMap[i].port != port || usedChans[MusEGlobal::drumMap[i].channel]) + continue; + usedChans[MusEGlobal::drumMap[i].channel] = 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. + } + + // NOT for syntis. Use midiState and/or initParams for that. + if(MusEGlobal::config.midiSendInit && MusEGlobal::config.midiSendCtlDefaults && _instrument && !_device->isSynti()) + { + MidiControllerList* cl = _instrument->controller(); + MidiController* mc; + for(ciMidiController imc = cl->begin(); imc != cl->end(); ++imc) + { + mc = imc->second; + for(int chan = 0; chan < MIDI_CHANNELS; ++chan) + { + if(!usedChans[chan]) + continue; // This channel on this port is not used in the song. + ciMidiCtrlValList i; + // Look for an initial value for this midi controller, on this midi channel, in the song... + for(i = _controller->begin(); i != _controller->end(); ++i) + { + int channel = i->first >> 24; + int cntrl = i->first & 0xffffff; + int val = i->second->hwVal(); + if(channel == chan && cntrl == mc->num() && val != CTRL_VAL_UNKNOWN) + break; + } + // If no initial value was found for this midi controller, on this midi channel, in the song... + if(i == _controller->end()) + { + // If the instrument's midi controller has an initial value, send it now. + if(mc->initVal() != CTRL_VAL_UNKNOWN) + { + 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())); + // 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()); + } + } + } + } + } + + // init HW controller state + for (iMidiCtrlValList i = _controller->begin(); i != _controller->end(); ++i) + { + int channel = i->first >> 24; + if(!usedChans[channel]) + continue; // This channel on this port is not used in the song. + int cntrl = i->first & 0xffffff; + 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)); + // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... + setHwCtrlState(channel, cntrl, val); + } + } + + return rv; +} + +//--------------------------------------------------------- +// setInstrument +//--------------------------------------------------------- + +void MidiPort::setInstrument(MidiInstrument* i) +{ + _instrument = i; + _initializationsSent = false; +} + +//--------------------------------------------------------- // clearDevice //--------------------------------------------------------- void MidiPort::clearDevice() { _device = 0; + _initializationsSent = false; _state = "not configured"; } diff --git a/muse2/muse/midiport.h b/muse2/muse/midiport.h index 0faccce2..3acaa6ee 100644 --- a/muse2/muse/midiport.h +++ b/muse2/muse/midiport.h @@ -59,6 +59,9 @@ class MidiPort { // 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. int _defaultOutChannels; // + // 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; RouteList _inRoutes, _outRoutes; @@ -98,7 +101,7 @@ class MidiPort { void setMidiDevice(MidiDevice* dev); const QString& portname() const; MidiInstrument* instrument() const { return _instrument; } - void setInstrument(MidiInstrument* i) { _instrument = i; } + void setInstrument(MidiInstrument* i); MidiController* midiController(int num) const; MidiCtrlValList* addManagedController(int channel, int ctrl); void tryCtrlInitVal(int chan, int ctl, int val); @@ -136,6 +139,13 @@ class MidiPort { unsigned char s, unsigned char f, unsigned char sf, int devid = -1); void sendMMCStop(int devid = -1); void sendMMCDeferredPlay(int devid = -1); + + // Send Instrument Init sequences and controller defaults etc. + 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; } bool sendHwCtrlState(const MidiPlayEvent&, bool forceSend = false ); bool sendEvent(const MidiPlayEvent&, bool forceSend = false ); diff --git a/muse2/muse/midiseq.cpp b/muse2/muse/midiseq.cpp index 2cfc1917..32e9de4c 100644 --- a/muse2/muse/midiseq.cpp +++ b/muse2/muse/midiseq.cpp @@ -203,11 +203,6 @@ void MidiSeq::processStop() void MidiSeq::processSeek() { - int pos = MusEGlobal::audio->tickPos(); - // TODO Try to move this into MusEGlobal::audio::seek(). - if (pos == 0 && !MusEGlobal::song->record()) - MusEGlobal::audio->initDevices(); - //--------------------------------------------------- // set all controller //--------------------------------------------------- diff --git a/muse2/muse/mpevent.cpp b/muse2/muse/mpevent.cpp index 1ad09ff3..c1b0e5dc 100644 --- a/muse2/muse/mpevent.cpp +++ b/muse2/muse/mpevent.cpp @@ -211,13 +211,7 @@ bool MEvent::operator<(const MEvent& e) const // notes if (channel() == e.channel()) - { -// REMOVE Tim. -// return (type() == ME_NOTEOFF -// || (type() == ME_NOTEON && dataB() == 0) -// || type() != ME_NOTEON; // Make note-ons last so that controllers such as program come before notes played. 1/31/2012 Tim. return sortingWeight() < e.sortingWeight(); - } int map[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 14, 15 }; return map[channel()] < map[e.channel()]; diff --git a/muse2/muse/route.cpp b/muse2/muse/route.cpp index 18672159..223b763a 100644 --- a/muse2/muse/route.cpp +++ b/muse2/muse/route.cpp @@ -265,6 +265,9 @@ void addRoute(Route src, Route dst) #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); } else diff --git a/muse2/muse/seqmsg.cpp b/muse2/muse/seqmsg.cpp index f60a2d51..fcffc332 100644 --- a/muse2/muse/seqmsg.cpp +++ b/muse2/muse/seqmsg.cpp @@ -39,6 +39,8 @@ #include "arranger.h" #include "plugin.h" #include "driver/jackmidi.h" +#include "midi_warn_init_pending_impl.h" +#include "gconfig.h" namespace MusECore { @@ -1114,11 +1116,82 @@ void Audio::msgResetMidiDevices() // msgInitMidiDevices //--------------------------------------------------------- -void Audio::msgInitMidiDevices() +void Audio::msgInitMidiDevices(bool force) { + // + // test for explicit instrument initialization + // + + if(!force && MusEGlobal::config.warnInitPending) + { + bool found = false; + if(MusEGlobal::song->click()) + { + MidiPort* mp = &MusEGlobal::midiPorts[MusEGlobal::clickPort]; + if(mp->device() && + (mp->device()->openFlags() & 1) && + mp->instrument() && !mp->instrument()->midiInit()->empty() && + !mp->initSent()) + found = true; + } + + if(!found) + { + for(int i = 0; i < MIDI_PORTS; ++i) + { + MidiPort* mp = &MusEGlobal::midiPorts[i]; + if(mp->device() && (mp->device()->openFlags() & 1) && + mp->instrument() && !mp->instrument()->midiInit()->empty() && + !mp->initSent()) + { + found = true; + break; + } + } + } + + if(found) + { + MusEGui::MidiWarnInitPendingDialog dlg; + int rv = dlg.exec(); + bool warn = !dlg.dontAsk(); + if(warn != MusEGlobal::config.warnInitPending) + { + MusEGlobal::config.warnInitPending = warn; + //MusEGlobal::muse->changeConfig(true); // Save settings? No, wait till close. + } + if(rv != QDialog::Accepted) + { + if(MusEGlobal::config.midiSendInit) + MusEGlobal::config.midiSendInit = false; + //return; + } + else + { + if(!MusEGlobal::config.midiSendInit) + MusEGlobal::config.midiSendInit = true; + } + } + } + +// We can either try to do it in one cycle with one message, +// or by idling the sequencer (gaining safe access to all structures) +// for as much time as we need. +// Here we COULD get away with the audio 'hiccup' that idling causes, +// because it's unlikely someone would initialize during play... +// But no midi is processed, so let's switch this only if requiring +// large numbers of init values causes a problem later... +#if 1 AudioMsg msg; msg.id = SEQM_INIT_DEVICES; + msg.a = force; sendMessage(&msg, false); +#else + msgIdle(true); + initDevices(force); + msgIdle(false); +#endif + } //--------------------------------------------------------- diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index 8f189759..036892f0 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -2113,8 +2113,8 @@ void Song::clear(bool signal, bool clear_all) // Clear all midi port controller values. for(int i = 0; i < MIDI_PORTS; ++i) - // Don't remove the controllers, just the values. - MusEGlobal::midiPorts[i].controller()->clearDelete(false); + // Remove the controllers AND the values so we start with a clean slate. + MusEGlobal::midiPorts[i].controller()->clearDelete(true); _masterFlag = true; loopFlag = false; diff --git a/muse2/muse/widgets/CMakeLists.txt b/muse2/muse/widgets/CMakeLists.txt index 0e9d369d..3aad8b92 100644 --- a/muse2/muse/widgets/CMakeLists.txt +++ b/muse2/muse/widgets/CMakeLists.txt @@ -54,12 +54,14 @@ QT4_WRAP_CPP (widget_mocs hitscale.h intlabel.h knob.h + knob_and_meter.h lcombo.h menutitleitem.h meter.h metronome.h midi_audio_control.h midisyncimpl.h + midi_warn_init_pending_impl.h mixdowndialog.h mlabel.h mtscale.h @@ -125,6 +127,7 @@ file (GLOB widgets_ui_files itransformbase.ui metronomebase.ui midisync.ui + midi_warn_init_pending.ui midi_audio_control_base.ui mittransposebase.ui mixdowndialogbase.ui @@ -170,12 +173,14 @@ file (GLOB widgets_source_files hitscale.cpp intlabel.cpp knob.cpp + knob_and_meter.cpp lcombo.cpp menutitleitem.cpp meter.cpp metronome.cpp midi_audio_control.cpp midisyncimpl.cpp + midi_warn_init_pending_impl.cpp mixdowndialog.cpp mlabel.cpp mmath.cpp diff --git a/muse2/muse/widgets/genset.cpp b/muse2/muse/widgets/genset.cpp index 5d94a41d..5c4b2dfb 100644 --- a/muse2/muse/widgets/genset.cpp +++ b/muse2/muse/widgets/genset.cpp @@ -153,6 +153,9 @@ void GlobalSettingsConfig::updateSettings() } } + midiSendInit->setChecked(MusEGlobal::config.midiSendInit); + midiWarnInitPending->setChecked(MusEGlobal::config.warnInitPending); + midiSendCtlDefaults->setChecked(MusEGlobal::config.midiSendCtlDefaults); guiRefreshSelect->setValue(MusEGlobal::config.guiRefresh); minSliderSelect->setValue(int(MusEGlobal::config.minSlider)); minMeterSelect->setValue(MusEGlobal::config.minMeter); @@ -269,6 +272,9 @@ void GlobalSettingsConfig::apply() MusEGlobal::config.useOutputLimiter = outputLimiterCheckBox->isChecked(); MusEGlobal::config.vstInPlace = vstInPlaceCheckBox->isChecked(); MusEGlobal::config.rtcTicks = rtcResolutions[rtcticks]; + MusEGlobal::config.midiSendInit = midiSendInit->isChecked(); + MusEGlobal::config.warnInitPending = midiWarnInitPending->isChecked(); + MusEGlobal::config.midiSendCtlDefaults = midiSendCtlDefaults->isChecked(); MusEGlobal::config.projectBaseFolder = projDirEntry->text(); diff --git a/muse2/muse/widgets/gensetbase.ui b/muse2/muse/widgets/gensetbase.ui index c204b9f4..30d823ec 100644 --- a/muse2/muse/widgets/gensetbase.ui +++ b/muse2/muse/widgets/gensetbase.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>525</width> - <height>549</height> + <width>544</width> + <height>593</height> </rect> </property> <property name="windowTitle"> @@ -37,7 +37,7 @@ </property> <layout class="QHBoxLayout" name="qhboxProjDir"> <property name="spacing"> - <number>-1</number> + <number>6</number> </property> <item> <widget class="QLabel" name="textLabel_ProjDir"> @@ -1029,14 +1029,94 @@ Adjusts responsiveness of audio controls and <attribute name="title"> <string>Midi</string> </attribute> - <layout class="QVBoxLayout" name="verticalLayout_7"> - <item> + <layout class="QGridLayout" name="gridLayout_7"> + <item row="2" column="0"> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Record new style drum tracks</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QRadioButton" name="recordAllButton"> + <property name="text"> + <string>Record all instruments</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="dontRecHiddenButton"> + <property name="text"> + <string>Don't record hidden instruments</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="dontRecMutedButton"> + <property name="text"> + <string>Don't record muted instruments</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="dontRecBothButton"> + <property name="text"> + <string>Don't record hidden or muted instruments</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <widget class="QGroupBox" name="groupBox_5"> + <property name="title"> + <string>Instrument initialization</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="midiSendInit"> + <property name="text"> + <string>Send instrument initialization sequences</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="midiWarnInitPending"> + <property name="text"> + <string>Warn if instrument initialization sequences pending</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="midiSendCtlDefaults"> + <property name="text"> + <string>Send instrument controller default values if none in song, at init or rewind</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="3" column="0"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>191</width> + <height>166</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0"> <widget class="QGroupBox" name="GroupBox2"> <property name="title"> <string>Ticks</string> </property> - <layout class="QGridLayout"> - <item row="0" column="0"> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="0" column="0" rowspan="2"> <widget class="QLabel" name="TextLabel3"> <property name="text"> <string>RTC Resolution @@ -1049,6 +1129,12 @@ Adjusts responsiveness of audio controls and </item> <item row="0" column="1"> <widget class="QComboBox" name="rtcResolutionSelect"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <item> <property name="text"> <string>1024</string> @@ -1081,19 +1167,14 @@ Adjusts responsiveness of audio controls and </item> </widget> </item> - <item row="1" column="0"> - <widget class="QLabel" name="midiResLabel"> - <property name="text"> - <string>Midi Resolution -(Ticks/Quarternote)</string> - </property> - <property name="wordWrap"> - <bool>false</bool> - </property> - </widget> - </item> - <item row="1" column="1"> + <item row="1" column="1" rowspan="2"> <widget class="QComboBox" name="midiDivisionSelect"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="currentIndex"> <number>3</number> </property> @@ -1145,6 +1226,17 @@ Adjusts responsiveness of audio controls and </widget> </item> <item row="2" column="0"> + <widget class="QLabel" name="midiResLabel"> + <property name="text"> + <string>Midi Resolution +(Ticks/Quarternote)</string> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="3" column="0"> <widget class="QLabel" name="TextLabel4"> <property name="text"> <string>Displayed Resolution @@ -1155,8 +1247,14 @@ Adjusts responsiveness of audio controls and </property> </widget> </item> - <item row="2" column="1"> + <item row="3" column="1"> <widget class="QComboBox" name="guiDivisionSelect"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="currentIndex"> <number>3</number> </property> @@ -1210,56 +1308,6 @@ Adjusts responsiveness of audio controls and </layout> </widget> </item> - <item> - <widget class="QGroupBox" name="groupBox_3"> - <property name="title"> - <string>Record new style drum tracks</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <item> - <widget class="QRadioButton" name="recordAllButton"> - <property name="text"> - <string>Record all instruments</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="dontRecHiddenButton"> - <property name="text"> - <string>Don't record hidden instruments</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="dontRecMutedButton"> - <property name="text"> - <string>Don't record muted instruments</string> - </property> - </widget> - </item> - <item> - <widget class="QRadioButton" name="dontRecBothButton"> - <property name="text"> - <string>Don't record hidden or muted instruments</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <spacer name="verticalSpacer_3"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>191</width> - <height>166</height> - </size> - </property> - </spacer> - </item> </layout> </widget> <widget class="QWidget" name="tab3"> @@ -1629,8 +1677,8 @@ left button behave like the middle button in such areas.</string> <rect> <x>0</x> <y>0</y> - <width>481</width> - <height>386</height> + <width>96</width> + <height>26</height> </rect> </property> <layout class="QHBoxLayout" name="horizontalLayout_3"> diff --git a/muse2/muse/widgets/knob.h b/muse2/muse/widgets/knob.h index 114284d0..fcaedfaa 100644 --- a/muse2/muse/widgets/knob.h +++ b/muse2/muse/widgets/knob.h @@ -49,7 +49,7 @@ class Knob : public SliderBase, public ScaleIf gainType, }; - private: + protected: bool hasScale; int d_borderWidth; @@ -81,7 +81,7 @@ class Knob : public SliderBase, public ScaleIf void recalcAngle(); void valueChange(); void rangeChange(); - void drawKnob(QPainter *p, const QRect &r); + virtual void drawKnob(QPainter *p, const QRect &r); void drawMarker(QPainter *p, double arc, const QColor &c); virtual void paintEvent(QPaintEvent *); diff --git a/muse2/muse/widgets/knob_and_meter.cpp b/muse2/muse/widgets/knob_and_meter.cpp new file mode 100644 index 00000000..482becae --- /dev/null +++ b/muse2/muse/widgets/knob_and_meter.cpp @@ -0,0 +1,609 @@ +//====================================================================== +// MusE +// Linux Music Editor +// knob_and_meter.cpp +// (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. +// +//========================================================= + +#include <stdio.h> +#include "knob_and_meter.h" +#include <cmath> +#include "mmath.h" + +#include <QPainter> +#include <QPainterPath> +#include <QPalette> +#include <QPaintEvent> +#include <QResizeEvent> + +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@. +//--------------------------------------------------------- + + +//--------------------------------------------------------- +// KnobWithMeter +//--------------------------------------------------------- + +KnobWithMeter::KnobWithMeter(QWidget* parent, const char* name) + : Knob(parent, name) + { +// hasScale = false; +// +// d_borderWidth = 4; +// d_shineWidth = 3; +// 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 = palette().mid().color(); +// 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; +// +// setMinimumSize(30,30); +// setUpdateTime(50); + } + +//------------------------------------------------------------ +// QwtKnob::setTotalAngle +// Set the total angle by which the knob can be turned +// +// Syntax +// void QwtKnob::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 Knob::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); +// } +// +//------------------------------------------------------------ +// Knob::setRange +// Set the range and step size of the knob +// +// Sets the paramaters 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)) +// { +// 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); +// } +// +//------------------------------------------------------------ +// QwtKnob::drawKnob +// const QRect &r -- borders of the knob +//------------------------------------------------------------ + +void KnobWithMeter::drawKnob(QPainter* p, const QRect& r) + { + const QPalette& pal = palette(); + + QRect aRect; + aRect.setRect(kRect.x() + d_borderWidth, + kRect.y() + d_borderWidth, + kRect.width() - 2*d_borderWidth, + kRect.height() - 2*d_borderWidth); + + int width = kRect.width(); + int height = kRect.height(); + int size = qMin(width, height); + + p->setRenderHint(QPainter::Antialiasing, true); + + QPainterPath drawingPath, updatePath, finalPath, cornerPath; + + // + // draw the rim + // + + QLinearGradient linearg(QPoint(r.x(),r.y()), 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(),r.y(),size,size); + + + // + // draw shiny surrounding + // + + QPen pn; + pn.setCapStyle(Qt::FlatCap); + + pn.setColor(d_shinyColor.lighter(l_const + abs(value() * l_slope))); + pn.setWidth(d_shineWidth * 2); + p->setPen(pn); + p->drawArc(aRect, 0, 360 * 16); + + // + // draw button face + // + + QRadialGradient gradient(size/2, size/2, size-d_borderWidth, size/2-d_borderWidth, 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, isEnabled() ? d_markerColor : Qt::gray); + drawMarker(p, d_angle, pal.currentColorGroup() == QPalette::Disabled ? + pal.color(QPalette::Disabled, QPalette::WindowText) : d_markerColor); + } + +//------------------------------------------------------------ +//.F QwtSliderBase::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 Knob::valueChange() +// { +// recalcAngle(); +// d_newVal++; +// repaint(kRect); +// SliderBase::valueChange(); +// } +// +//------------------------------------------------------------ +//.F QwtKnob::getValue +// Determine the value corresponding to a specified position +// +//.u Parameters: +// const QPoint &p -- point +// +//.u Description: +// Called by QwtSliderBase +//------------------------------------------------------------ + +// double Knob::getValue(const QPoint &p) +// { +// double newValue; +// double oneTurn; +// double eqValue; +// double arc; +// +// const QRect& r = rect(); +// +// double dx = double((r.x() + r.width() / 2) - p.x() ); +// double dy = double((r.y() + r.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 QwtKnob::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 Knob::getScrollMode( QPoint &p, const Qt::MouseButton &/*button*/, int &scrollMode, int &direction)// prevent compiler warning : unsused parameter +// { +// int dx, dy, r; +// double arc; +// +// /*Qt::ButtonState but= button ;*/ // prevent compiler warning : unsused variable +// r = kRect.width() / 2; +// +// dx = kRect.x() + r - p.x(); +// dy = kRect.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; +// } +// return; +// } +// + + +//------------------------------------------------------------ +//.F QwtKnob::rangeChange +// Notify a change of the range +// +//.u Description +// Called by QwtSliderBase +//------------------------------------------------------------ + +// void Knob::rangeChange() +// { +// if (!hasUserScale()) +// { +// d_scale.setScale(minValue(), maxValue(), +// d_maxMajor, d_maxMinor); +// } +// recalcAngle(); +// resize(size()); +// repaint(); +// } + +void KnobWithMeter::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; +// } + Knob::mousePressEvent(e); +} + +//--------------------------------------------------------- +// resizeEvent +//--------------------------------------------------------- + +void KnobWithMeter::resizeEvent(QResizeEvent* ev) + { + Knob::resizeEvent(ev); +// int width, width_2; +// +// const QRect& r = rect(); +// +// // printf("resize %d %d %d\n", r.height(), r.width(), d_knobWidth); +// +// // width = MusECore::qwtMin(MusECore::qwtMin(r.height(), r.width()), d_knobWidth); +// width = MusECore::qwtMin(r.height(), r.width()); +// width_2 = width / 2; +// +// int x = r.x() + r.width() / 2 - width_2; +// int y = r.y() + r.height() / 2 - width_2; +// +// kRect.setRect(x, y, width, width); +// +// x = kRect.x() - d_scaleDist; +// y = kRect.y() - d_scaleDist; +// int w = width + 2 * d_scaleDist; +// +// d_scale.setGeometry(x, y, w, ScaleDraw::Round); + } + +//------------------------------------------------------------ +// paintEvent +//------------------------------------------------------------ + +void KnobWithMeter::paintEvent(QPaintEvent* e) + { +/* QPainter p(this); + const QRect &r = e->rect(); + + if ((r == kRect) && d_newVal ) { // event from valueChange() + if (d_newVal > 1) // lost paintEvents()? + drawKnob(&p, kRect); + else { + drawMarker(&p, d_oldAngle, d_curFaceColor); + drawMarker(&p, d_angle, d_markerColor); + } + } + else { + p.eraseRect(rect()); + if (hasScale) + d_scale.draw(&p); + drawKnob(&p, kRect); + } + d_newVal = 0; +*/ + + const QRect &r = e->rect(); + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing, true); + if(hasScale) + d_scale.draw(&p); + + ///drawKnob(&p, kRect); + drawKnob(&p, r); + + //drawMarker(&p, d_oldAngle, d_curFaceColor); + //drawMarker(&p, d_angle, d_markerColor); + + d_newVal = 0; + } + +//------------------------------------------------------------ +//.- +//.F QwtKnob::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 QwtKnob::drawMarker(QPainter *p) +// +//------------------------------------------------------------ +// void Knob::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 = kRect.width() / 2 - d_borderWidth + d_shineWidth; +// if (radius < 3) radius = 3; +// int ym = kRect.y() + radius + d_borderWidth - d_shineWidth; +// int xm = kRect.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); +// +// p->setRenderHint(QPainter::Antialiasing, true); +// p->drawLine( xm, +// ym, +// xm - int(rint(sa * re)), +// ym - int(rint(ca * re))); +// +// break; +// } +// +// +// } +// + +//------------------------------------------------------------ +// +//.F QwtKnob::setKnobWidth +// Change the knob's width. +// +//.u Syntax +//.f void QwtKnob::setKnobWidth(int w) +// +//.u Parameters +//.p int w -- new width +// +//.u Description +// The specified width must be >= 5, or it will be clipped. +// +//------------------------------------------------------------ +// void Knob::setKnobWidth(int w) +// { +// d_knobWidth = MusECore::qwtMax(w,5); +// resize(size()); +// repaint(); +// } +// +//------------------------------------------------------------ +// +//.F QwtKnob::setBorderWidth +// Set the knob's border width +// +//.u Syntax +//.f void QwtKnob::setBorderWidth(int bw) +// +//.u Parameters +//.p int bw -- new border width +// +//------------------------------------------------------------ +// void Knob::setBorderWidth(int bw) +// { +// d_borderWidth = MusECore::qwtMax(bw, 0); +// resize(size()); +// repaint(); +// } + +//------------------------------------------------------------ +//.- +//.F QwtKnob::recalcAngle +// Recalculate the marker angle corresponding to the +// current value +// +//.u Syntax +//.f void QwtKnob::recalcAngle() +// +//------------------------------------------------------------ +// void Knob::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 Knob::setFaceColor(const QColor c) +// { +// d_faceColor = c; +// if(!_faceColSel) +// //update(FALSE); +// repaint(); +// } + +//------------------------------------------------------------ +// setAltFaceColor +//------------------------------------------------------------ +// void Knob::setAltFaceColor(const QColor c) +// { +// d_altFaceColor = c; +// if(_faceColSel) +// //update(FALSE); +// repaint(); +// } + +//------------------------------------------------------------ +// selectFaceColor +//------------------------------------------------------------ +// void Knob::selectFaceColor(bool alt) +// { +// _faceColSel = alt; +// if(alt) +// d_curFaceColor = d_altFaceColor; +// else +// d_curFaceColor = d_faceColor; +// //update(FALSE); +// repaint(); +// } + +//------------------------------------------------------------ +// setMarkerColor +//------------------------------------------------------------ +// void Knob::setMarkerColor(const QColor c) +// { +// d_markerColor = c; +// //update(FALSE); +// repaint(); +// } + +} // namespace MusEGui diff --git a/muse2/muse/widgets/knob_and_meter.h b/muse2/muse/widgets/knob_and_meter.h new file mode 100644 index 00000000..989c8567 --- /dev/null +++ b/muse2/muse/widgets/knob_and_meter.h @@ -0,0 +1,114 @@ +//========================================================= +// MusE +// Linux Music Editor +// knob_and_meter.h +// (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 __KNOB_AND_METER_H__ +#define __KNOB_AND_METER_H__ + +#include "knob.h" +#include "sclif.h" +#include <QColor> +#include <QResizeEvent> +#include <QPaintEvent> + +namespace MusEGui { + +//--------------------------------------------------------- +// KnobWithMeter +//--------------------------------------------------------- + +class KnobWithMeter : public Knob + { + Q_OBJECT + +// public: +// enum Symbol { Line, Dot }; +// +// enum KnobType { +// panType, +// auxType, +// gainType, +// }; +// +// private: +// bool hasScale; +// +// 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 kRect; +// bool _faceColSel; +// QColor d_faceColor; +// QColor d_shinyColor; +// QColor d_rimColor; +// QColor d_curFaceColor; +// QColor d_altFaceColor; +// QColor d_markerColor; +// +// void recalcAngle(); +// void valueChange(); +// void rangeChange(); + void drawKnob(QPainter *p, const QRect &r); +// void drawMarker(QPainter *p, double arc, const QColor &c); + + virtual void paintEvent(QPaintEvent *e); + 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 scaleChange() { repaint(); } +// void fontChange(const QFont &) { repaint(); } + + public: + KnobWithMeter(QWidget* parent = 0, const char *name = 0); + ~KnobWithMeter() {} + +// void setRange(double vmin, double vmax, double vstep = 0.0, +// int pagesize = 1); +// 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 markerColor() { return d_markerColor; } +// void setMarkerColor(const QColor c); + }; + +} // namespace MusEGui + +#endif diff --git a/muse2/muse/widgets/midi_warn_init_pending.ui b/muse2/muse/widgets/midi_warn_init_pending.ui new file mode 100644 index 00000000..7b88811b --- /dev/null +++ b/muse2/muse/widgets/midi_warn_init_pending.ui @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>midiWarnInitPendingBase</class> + <widget class="QDialog" name="midiWarnInitPendingBase"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>371</width> + <height>207</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Instrument initialization</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>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?</string> + </property> + <property name="textFormat"> + <enum>Qt::AutoText</enum> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="dontAskAgain"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Don't ask me again</string> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::No|QDialogButtonBox::Yes</set> + </property> + <property name="centerButtons"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>midiWarnInitPendingBase</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>midiWarnInitPendingBase</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/muse2/muse/widgets/midi_warn_init_pending_impl.cpp b/muse2/muse/widgets/midi_warn_init_pending_impl.cpp new file mode 100644 index 00000000..41e9b51f --- /dev/null +++ b/muse2/muse/widgets/midi_warn_init_pending_impl.cpp @@ -0,0 +1,33 @@ +//========================================================= +// MusE +// Linux Music Editor +// midi_warn_init_pending_impl.cpp +// (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. +// +//========================================================= + +#include "midi_warn_init_pending_impl.h" + +namespace MusEGui { + +MidiWarnInitPendingDialog::MidiWarnInitPendingDialog() +{ + setupUi(this); +} + +} // namespace MusEGui + diff --git a/muse2/muse/widgets/midi_warn_init_pending_impl.h b/muse2/muse/widgets/midi_warn_init_pending_impl.h new file mode 100644 index 00000000..d24ee129 --- /dev/null +++ b/muse2/muse/widgets/midi_warn_init_pending_impl.h @@ -0,0 +1,40 @@ +//========================================================= +// MusE +// Linux Music Editor +// midi_warn_init_pending_impl.h +// (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 __MIDI_WARN_INIT_PENDING_IMPL_H__ +#define __MIDI_WARN_INIT_PENDING_IMPL_H__ + +#include "ui_midi_warn_init_pending.h" + +namespace MusEGui { + +class MidiWarnInitPendingDialog : public QDialog, public Ui::midiWarnInitPendingBase +{ + Q_OBJECT + +public: + MidiWarnInitPendingDialog(); + bool dontAsk() const { return dontAskAgain->isChecked(); } +}; + +} // namespace MusEGui + +#endif
\ No newline at end of file diff --git a/muse2/muse/widgets/musewidgetsplug.cpp b/muse2/muse/widgets/musewidgetsplug.cpp index 83a9fca9..70cdcd0f 100644 --- a/muse2/muse/widgets/musewidgetsplug.cpp +++ b/muse2/muse/widgets/musewidgetsplug.cpp @@ -155,6 +155,9 @@ MusEGlobal::GlobalConfigValues config = { 384, // division; 1024, // rtcTicks + 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 -60, // int minMeter; -60.0, // double minSlider; false, // use Jack freewheel diff --git a/muse2/muse/widgets/synthconfigbase.ui b/muse2/muse/widgets/synthconfigbase.ui index 500241a8..266518e4 100644 --- a/muse2/muse/widgets/synthconfigbase.ui +++ b/muse2/muse/widgets/synthconfigbase.ui @@ -6,72 +6,14 @@ <rect> <x>0</x> <y>0</y> - <width>810</width> - <height>492</height> + <width>820</width> + <height>475</height> </rect> </property> <property name="windowTitle"> <string>Midi Port and Soft Synth Configuration</string> </property> - <layout class="QGridLayout"> - <item row="1" column="1"> - <widget class="QGroupBox" name="GroupBox3"> - <property name="title"> - <string>Instances</string> - </property> - <layout class="QGridLayout"> - <item row="0" column="0"> - <widget class="QTreeWidget" name="instanceList"> - <property name="allColumnsShowFocus"> - <bool>true</bool> - </property> - <column> - <property name="text"> - <string>Name</string> - </property> - </column> - <column> - <property name="text"> - <string>Type</string> - </property> - </column> - <column> - <property name="text"> - <string>Midi Port</string> - </property> - </column> - </widget> - </item> - <item row="1" column="0"> - <layout class="QHBoxLayout"> - <item> - <widget class="QPushButton" name="removeInstance"> - <property name="text"> - <string>Remove Instance</string> - </property> - </widget> - </item> - <item> - <spacer name="Spacer2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeType"> - <enum>QSizePolicy::Expanding</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>113</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - </layout> - </widget> - </item> + <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0" colspan="2"> <widget class="QGroupBox" name="groupBox10"> <property name="title"> @@ -170,6 +112,95 @@ </layout> </widget> </item> + <item row="1" column="1"> + <widget class="QGroupBox" name="GroupBox3"> + <property name="title"> + <string>Instances</string> + </property> + <layout class="QGridLayout"> + <item row="0" column="0"> + <widget class="QTreeWidget" name="instanceList"> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <column> + <property name="text"> + <string>Name</string> + </property> + </column> + <column> + <property name="text"> + <string>Type</string> + </property> + </column> + <column> + <property name="text"> + <string>Midi Port</string> + </property> + </column> + </widget> + </item> + <item row="1" column="0"> + <layout class="QHBoxLayout"> + <item> + <widget class="QPushButton" name="removeInstance"> + <property name="text"> + <string>Remove Instance</string> + </property> + </widget> + </item> + <item> + <spacer name="Spacer2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>113</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="applyButton"> + <property name="text"> + <string>&Apply</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="okButton"> + <property name="text"> + <string>&OK</string> + </property> + </widget> + </item> + </layout> + </item> </layout> </widget> <layoutdefault spacing="6" margin="11"/> |