diff options
author | Tim E. Real <termtech@rogers.com> | 2011-02-24 17:27:04 +0000 |
---|---|---|
committer | Tim E. Real <termtech@rogers.com> | 2011-02-24 17:27:04 +0000 |
commit | cc12a523f8f4065f133b1ec38ccba2e7be74d48b (patch) | |
tree | ce42861856bbf2b23ae7f21f02aabbc775ad9a32 /muse2/muse | |
parent | 8e4d18fdcc12c7202e287cc8a2ec2214667c8844 (diff) |
Improved midi auto-connections. Fixed Organ synth crashing.
Diffstat (limited to 'muse2/muse')
-rw-r--r-- | muse2/muse/app.cpp | 142 | ||||
-rw-r--r-- | muse2/muse/app.h | 3 | ||||
-rw-r--r-- | muse2/muse/arranger/tlist.cpp | 14 | ||||
-rw-r--r-- | muse2/muse/conf.cpp | 34 | ||||
-rw-r--r-- | muse2/muse/confmport.cpp | 456 | ||||
-rw-r--r-- | muse2/muse/confmport.h | 5 | ||||
-rw-r--r-- | muse2/muse/midiport.cpp | 30 | ||||
-rw-r--r-- | muse2/muse/midiport.h | 5 | ||||
-rw-r--r-- | muse2/muse/mixer/astrip.cpp | 6 | ||||
-rw-r--r-- | muse2/muse/route.cpp | 11 | ||||
-rw-r--r-- | muse2/muse/song.cpp | 49 | ||||
-rw-r--r-- | muse2/muse/song.h | 3 | ||||
-rw-r--r-- | muse2/muse/track.cpp | 17 | ||||
-rw-r--r-- | muse2/muse/track.h | 7 | ||||
-rw-r--r-- | muse2/muse/widgets/mtrackinfo.cpp | 22 |
15 files changed, 674 insertions, 130 deletions
diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index 42f003df..c483fcb5 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -1687,7 +1687,8 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool loadAll if (mixer2) mixer2->clear(); arranger->clear(); // clear track info - if (clearSong()) + //if (clearSong()) + if (clearSong(loadAll)) // Allow not touching things like midi ports. p4.0.17 TESTING: Maybe some problems... return; QFileInfo fi(name); @@ -1937,7 +1938,16 @@ void MusE::loadTemplate() tr("MusE: load template"), 0, MFileDialog::GLOBAL_VIEW); if (!fn.isEmpty()) { // museProject = QFileInfo(fn).absolutePath(); + loadProjectFile(fn, true, true); + // With templates, don't clear midi ports. + // Any named ports in the template file are useless since they likely + // would not be found on other users' machines. + // So keep whatever the user currently has set up for ports. + // Note that this will also keep the current window configurations etc. + // but actually that's also probably a good thing. p4.0.17 Tim. TESTING: Maybe some problems... + //loadProjectFile(fn, true, false); + setUntitledProject(); } } @@ -2210,13 +2220,9 @@ PopupMenu* MusE::getRoutingPopupMenu() void MusE::updateRouteMenus(Track* track, QObject* master) { // NOTE: The purpose of this routine is to make sure the items actually reflect - // the routing status. And with MusE-1 QT3, it was also required to actually - // check the items since QT3 didn't do it for us. - // But now with MusE-2 and QT4, QT4 checks an item when it is clicked. - // So this routine is less important now, since 99% of the time, the items - // will be in the right checked state. - // But we still need this in case for some reason a route could not be - // added (or removed). Then the item will be properly un-checked (or checked) here. + // the routing status. + // In case for some reason a route could not be added (or removed). + // Then the item will be properly un-checked (or checked) here. //if(!track || track != gRoutingPopupMenuMaster || track->type() == Track::AUDIO_AUX) //if(!track || track->type() == Track::AUDIO_AUX) @@ -2371,6 +2377,12 @@ void MusE::routingPopupMenuActivated(Track* track, int n) if(n == -1) return; + if(n == 0) // p4.0.17 + { + muse->configMidiPorts(); + return; + } + iRouteMenuMap imm = gRoutingMenuMap.find(n); if(imm == gRoutingMenuMap.end()) return; @@ -2446,11 +2458,12 @@ void MusE::routingPopupMenuActivated(Track* track, int n) MidiPort* mp = &midiPorts[mdidx]; MidiDevice* md = mp->device(); - if(!md) - return; + //if(!md) // Removed p4.0.17 Allow connections to ports with no device. + // return; //if(!(md->rwFlags() & 2)) - if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) + //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) + if(md && !(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2))) // p4.0.17 return; int chmask = 0; @@ -2802,22 +2815,46 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) } else { + // Warn if no devices available. Add an item to open midi config. p4.0.17 + int pi = 0; + for( ; pi < MIDI_PORTS; ++pi) + { + MidiDevice* md = midiPorts[pi].device(); + if(md && !md->isSynti() && (md->rwFlags() & 2)) + break; + } + if(pi == MIDI_PORTS) + { + act = pup->addAction(tr("Warning: No midi input devices!")); + act->setCheckable(false); + act->setData(-1); + pup->addSeparator(); + } + act = pup->addAction(QIcon(*settings_midiport_softsynthsIcon), tr("Open midi config...")); + act->setCheckable(false); + act->setData(gid); + pup->addSeparator(); + ++gid; + + pup->addAction(new MenuTitleItem("Midi input ports", pup)); + for(int i = 0; i < MIDI_PORTS; ++i) { // NOTE: Could possibly list all devices, bypassing ports, but no, let's stick with ports. MidiPort* mp = &midiPorts[i]; MidiDevice* md = mp->device(); - if(!md) - continue; + //if(!md) + // continue; - if(!(md->rwFlags() & (dst ? 1 : 2))) + // p4.0.17 Do not list synth devices! + if(md && md->isSynti()) + continue; + + if(md && !(md->rwFlags() & 2)) continue; //printf("MusE::prepareRoutingPopupMenu adding submenu portnum:%d\n", i); - PopupMenu* subp = new PopupMenu(pup); - subp->setTitle(md->name()); - // MusE-2: Check this - needed with QMenu? Help says no. No - verified, it actually causes double triggers! //connect(subp, SIGNAL(triggered(QAction*)), pup, SIGNAL(triggered(QAction*))); //connect(subp, SIGNAL(aboutToHide()), pup, SIGNAL(aboutToHide())); @@ -2825,7 +2862,8 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) int chanmask = 0; // p3.3.50 To reduce number of routes required, from one per channel to just one containing a channel mask. // Look for the first route to this midi port. There should always be only a single route for each midi port, now. - for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) + iRoute ir = rl->begin(); + for( ; ir != rl->end(); ++ir) { if(ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == i) { @@ -2834,6 +2872,12 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) break; } } + // p4.0.17 List ports with no device, but with routes to this track, in the main popup. + if(!md && ir == rl->end()) + continue; + + PopupMenu* subp = new PopupMenu(pup); + subp->setTitle(QString("%1:").arg(i+1) + (md ? md->name() : tr("<none>"))); for(int ch = 0; ch < MIDI_CHANNELS; ++ch) { @@ -2852,7 +2896,7 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) ++gid; } //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. - act = subp->addAction(QString("Toggle all")); + act = subp->addAction(tr("Toggle all")); //act->setCheckable(true); act->setData(gid); Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. @@ -2860,6 +2904,61 @@ PopupMenu* MusE::prepareRoutingPopupMenu(Track* track, bool dst) ++gid; pup->addMenu(subp); } + + #if 0 + // p4.0.17 List ports with no device and no in routes, in a separate popup. + PopupMenu* morep = new PopupMenu(pup); + morep->setTitle(tr("More...")); + for(int i = 0; i < MIDI_PORTS; ++i) + { + MidiPort* mp = &midiPorts[i]; + if(mp->device()) + continue; + + PopupMenu* subp = new PopupMenu(morep); + subp->setTitle(QString("%1:").arg(i) + tr("<none>")); + + // MusE-2: Check this - needed with QMenu? Help says no. No - verified, it actually causes double triggers! + //connect(subp, SIGNAL(triggered(QAction*)), pup, SIGNAL(triggered(QAction*))); + //connect(subp, SIGNAL(aboutToHide()), pup, SIGNAL(aboutToHide())); + + iRoute ir = rl->begin(); + for( ; ir != rl->end(); ++ir) + { + if(ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == i) + break; + } + if(ir != rl->end()) + continue; + + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + act = subp->addAction(QString("Channel %1").arg(ch+1)); + act->setCheckable(true); + act->setData(gid); + + int chbit = 1 << ch; + Route srcRoute(i, chbit); // In accordance with new channel mask, use the bit position. + + gRoutingMenuMap.insert( pRouteMenuMap(gid, srcRoute) ); + + //if(chanmask & chbit) // Is the channel already set? Show item check mark. + // act->setChecked(true); + + ++gid; + } + //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. + act = subp->addAction(QString("Toggle all")); + //act->setCheckable(true); + act->setData(gid); + Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits. + gRoutingMenuMap.insert( pRouteMenuMap(gid, togRoute) ); + ++gid; + morep->addMenu(subp); + } + pup->addMenu(morep); + #endif + } if(pup->actions().isEmpty()) @@ -4615,9 +4714,10 @@ MusE::lash_idle_cb () // clearSong // return true if operation aborted // called with sequencer stopped +// If clear_all is false, it will not touch things like midi ports. //--------------------------------------------------------- -bool MusE::clearSong() +bool MusE::clearSong(bool clear_all) { if (song->dirty) { int n = 0; @@ -4665,7 +4765,7 @@ again: } } microSleep(100000); - song->clear(false); + song->clear(false, clear_all); microSleep(100000); return false; } diff --git a/muse2/muse/app.h b/muse2/muse/app.h index 013f6efc..f3a3dc4c 100644 --- a/muse2/muse/app.h +++ b/muse2/muse/app.h @@ -190,7 +190,8 @@ class MusE : public QMainWindow void processTrack(MidiTrack* track); void write(Xml& xml) const; - bool clearSong(); + // If clear_all is false, it will not touch things like midi ports. + bool clearSong(bool clear_all = true); bool save(const QString&, bool); void setUntitledProject(); void setConfigDefaults(); diff --git a/muse2/muse/arranger/tlist.cpp b/muse2/muse/arranger/tlist.cpp index a76be9c2..4a9d3d32 100644 --- a/muse2/muse/arranger/tlist.cpp +++ b/muse2/muse/arranger/tlist.cpp @@ -545,6 +545,7 @@ void TList::portsPopupMenu(Track* t, int x, int y) track->setOutPortAndUpdate(n); } audio->msgIdle(false); + audio->msgUpdateSoloStates(); // (p4.0.14) p4.0.17 song->update(); } else @@ -569,7 +570,9 @@ void TList::portsPopupMenu(Track* t, int x, int y) //audio->msgSetTrackOutPort(track, n); track->setOutPortAndUpdate(n); audio->msgIdle(false); - song->update(); + //song->update(); + audio->msgUpdateSoloStates(); // (p4.0.14) p4.0.17 + song->update(SC_MIDI_TRACK_PROP); // } } delete p; @@ -1104,7 +1107,7 @@ void TList::mousePressEvent(QMouseEvent* ev) //if(button == QMouseEvent::LeftButton) // portsPopupMenu(t, x, t->y() - ypos); - audio->msgUpdateSoloStates(); // p4.0.14 + //audio->msgUpdateSoloStates(); // p4.0.14 //song->update(SC_ROUTE); // break; @@ -1459,7 +1462,9 @@ void TList::wheelEvent(QWheelEvent* ev) mt->setOutPortAndUpdate(port); audio->msgIdle(false); - song->update(SC_ROUTE); + audio->msgUpdateSoloStates(); // p4.0.14 + //song->update(SC_ROUTE); + song->update(SC_MIDI_TRACK_PROP); // p4.0.17 } } break; @@ -1483,7 +1488,8 @@ void TList::wheelEvent(QWheelEvent* ev) // may result in adding/removing mixer strip: //song->update(-1); - song->update(SC_MIDI_TRACK_PROP); + audio->msgUpdateSoloStates(); // p4.0.14 + song->update(SC_MIDI_TRACK_PROP); } } else { diff --git a/muse2/muse/conf.cpp b/muse2/muse/conf.cpp index 5ddc058d..e4d57a3a 100644 --- a/muse2/muse/conf.cpp +++ b/muse2/muse/conf.cpp @@ -211,8 +211,11 @@ static void readConfigMidiPort(Xml& xml) int openFlags = 1; bool thruFlag = false; - int dic = 0; - int doc = 0; + //int dic = 0; + //int doc = 0; + int dic = -1; // p4.0.17 + int doc = -1; + MidiSyncInfo tmpSi; int type = MidiDevice::ALSA_MIDI; @@ -288,9 +291,16 @@ static void readConfigMidiPort(Xml& xml) MidiPort* mp = &midiPorts[idx]; mp->setInstrument(registerMidiInstrument(instrument)); // By Tim. - mp->setDefaultInChannels(dic); - mp->setDefaultOutChannels(doc); - + if(dic != -1) // p4.0.17 Leave them alone unless set by song. + mp->setDefaultInChannels(dic); + if(doc != -1) + // p4.0.17 Turn on if and when multiple output routes supported. + #if 0 + mp->setDefaultOutChannels(doc); + #else + setPortExclusiveDefOutChan(idx, doc); + #endif + mp->syncInfo().copyParams(tmpSi); // p3.3.50 Indicate the port was found in the song file, even if no device is assigned to it. mp->setFoundInSongFile(true); @@ -1037,10 +1047,18 @@ static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo) for (int i = 0; i < MIDI_PORTS; ++i) { bool used = false; MidiPort* mport = &midiPorts[i]; + MidiDevice* dev = mport->device(); // Route check by Tim. Port can now be used for routing even if no device. // Also, check for other non-defaults and save port, to preserve settings even if no device. if(!mport->noInRoute() || !mport->noOutRoute() || - mport->defaultInChannels() || mport->defaultOutChannels() || + // p4.0.17 Since MidiPort:: and MidiDevice::writeRouting() ignore ports with no device, ignore them here, too. + // This prevents bogus routes from being saved and propagated in the med file. + // Hmm tough decision, should we save if no device? That would preserve routes in case user upgrades HW, + // or ALSA reorders or renames devices etc etc, then we have at least kept the track <-> port routes. + //if(((!mport->noInRoute() || !mport->noOutRoute()) && dev) || + //mport->defaultInChannels() || mport->defaultOutChannels() || + mport->defaultInChannels() != (1<<MIDI_CHANNELS)-1 || // p4.0.17 Default is now to connect to all channels. + mport->defaultOutChannels() || (!mport->instrument()->iname().isEmpty() && mport->instrument()->iname() != "GM") || !mport->syncInfo().isDefault()) used = true; @@ -1058,12 +1076,12 @@ static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo) } } - MidiDevice* dev = mport->device(); if (!used && !dev) continue; xml.tag(level++, "midiport idx=\"%d\"", i); - if(mport->defaultInChannels()) + //if(mport->defaultInChannels()) + if(mport->defaultInChannels() != (1<<MIDI_CHANNELS)-1) // p4.0.17 Default is now to connect to all channels. xml.intTag(level, "defaultInChans", mport->defaultInChannels()); if(mport->defaultOutChannels()) xml.intTag(level, "defaultOutChans", mport->defaultOutChannels()); diff --git a/muse2/muse/confmport.cpp b/muse2/muse/confmport.cpp index 77e6889c..2c72c9e3 100644 --- a/muse2/muse/confmport.cpp +++ b/muse2/muse/confmport.cpp @@ -12,6 +12,7 @@ #include <stdio.h> #include <QMenu> +#include <QAction> #include <QMessageBox> #include <QPixmap> #include <QTableWidget> @@ -37,16 +38,185 @@ #include "audiodev.h" #include "menutitleitem.h" #include "utils.h" +#include "popupmenu.h" extern std::vector<Synth*> synthis; enum { DEVCOL_NO = 0, DEVCOL_GUI, DEVCOL_REC, DEVCOL_PLAY, DEVCOL_INSTR, DEVCOL_NAME, - //DEVCOL_STATE }; - //DEVCOL_ROUTES, DEVCOL_STATE }; - //DEVCOL_INROUTES, DEVCOL_OUTROUTES, DEVCOL_STATE }; // p3.3.55 DEVCOL_INROUTES, DEVCOL_OUTROUTES, DEVCOL_DEF_IN_CHANS, DEVCOL_DEF_OUT_CHANS, DEVCOL_STATE }; //--------------------------------------------------------- +// changeDefInputRoutes +//--------------------------------------------------------- + +void MPConfig::changeDefInputRoutes(QAction* act) +{ + QTableWidgetItem* item = mdevView->currentItem(); + if(item == 0) + return; + QString id = mdevView->item(item->row(), DEVCOL_NO)->text(); + int no = atoi(id.toLatin1().constData()) - 1; + if(no < 0 || no >= MIDI_PORTS) + return; + int actid = act->data().toInt(); + int allch = (1 << MIDI_CHANNELS) - 1; + int defch = midiPorts[no].defaultInChannels(); + + if(actid == MIDI_CHANNELS + 1) // Apply to all tracks now. + { + // Are there tracks, and is there a port device? + // Tested: Hmm, allow ports with no device since that is a valid situation. + if(!song->midis()->empty()) // && midiPorts[no].device()) + { + int ret = QMessageBox::question(this, tr("Default input connections"), + tr("Are you sure you want to apply to all existing midi tracks now?"), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Cancel); + if(ret == QMessageBox::Ok) + { + MidiTrackList* mtl = song->midis(); + for(iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) + { + // Remove all routes from this port to the tracks first. + audio->msgRemoveRoute(Route(no, allch), Route(*it, allch)); + if(defch) + audio->msgAddRoute(Route(no, defch), Route(*it, defch)); + } + //audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + } + } + } + else + { + int chbits; + if(actid == MIDI_CHANNELS) // Toggle all. + { + chbits = (defch == allch) ? 0 : allch; + if(defpup) + for(int i = 0; i < MIDI_CHANNELS; ++i) + { + QAction* act = defpup->findActionFromData(i); + if(act) + act->setChecked(chbits); + } + } + else + chbits = defch ^ (1 << actid); + midiPorts[no].setDefaultInChannels(chbits); + mdevView->item(item->row(), DEVCOL_DEF_IN_CHANS)->setText(bitmap2String(chbits)); + } +} + +//--------------------------------------------------------- +// changeDefOutputRoutes +//--------------------------------------------------------- + +void MPConfig::changeDefOutputRoutes(QAction* act) +{ + QTableWidgetItem* item = mdevView->currentItem(); + if(item == 0) + return; + QString id = mdevView->item(item->row(), DEVCOL_NO)->text(); + int no = atoi(id.toLatin1().constData()) - 1; + if(no < 0 || no >= MIDI_PORTS) + return; + int actid = act->data().toInt(); + int defch = midiPorts[no].defaultOutChannels(); + // Turn on if and when multiple output routes are supported. + #if 0 + int allch = (1 << MIDI_CHANNELS) - 1; + #endif + + if(actid == MIDI_CHANNELS + 1) // Apply to all tracks now. + { + // Are there tracks, and is there a port device? + // Tested: Hmm, allow ports with no device since that is a valid situation. + if(!song->midis()->empty()) // && midiPorts[no].device()) + { + int ret = QMessageBox::question(this, tr("Default output connections"), + tr("Are you sure you want to apply to all existing midi tracks now?"), + QMessageBox::Ok | QMessageBox::Cancel, + QMessageBox::Cancel); + if(ret == QMessageBox::Ok) + { + MidiTrackList* mtl = song->midis(); + // Turn on if and when multiple output routes are supported. + #if 0 + for(iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) + { + // Remove all routes from this port to the tracks first. + audio->msgRemoveRoute(Route(no, allch), Route(*it, allch)); + if(defch) + audio->msgAddRoute(Route(no, defch), Route(*it, defch)); + } + audio->msgUpdateSoloStates(); + song->update(SC_ROUTE); + #else + int ch = 0; + for( ; ch < MIDI_CHANNELS; ++ch) + if(defch & (1 << ch)) break; + + audio->msgIdle(true); + for(iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) + { + // Leave drum track channel at current setting. + if((*it)->type() == Track::DRUM) + (*it)->setOutPortAndUpdate(no); + else + (*it)->setOutPortAndChannelAndUpdate(no, ch); + } + audio->msgIdle(false); + audio->msgUpdateSoloStates(); + song->update(SC_MIDI_TRACK_PROP); + #endif + } + } + } + else + { + #if 0 // Turn on if and when multiple output routes are supported. + int chbits; + if(actid == MIDI_CHANNELS) // Toggle all. + { + chbits = (defch == allch) ? 0 : allch; + if(defpup) + for(int i = 0; i < MIDI_CHANNELS; ++i) + { + QAction* act = defpup->findActionFromData(i); + if(act) + act->setChecked(chbits); + } + } + else + chbits = defch ^ (1 << actid); + midiPorts[no].setDefaultOutChannels(chbits); + mdevView->item(item->row(), DEVCOL_DEF_OUT_CHANS)->setText(bitmap2String(chbits)); + #else + if(actid < MIDI_CHANNELS) + { + int chbits = 1 << actid; + // Multiple out routes not supported. Make the setting exclusive to this port - exclude all other ports. + setPortExclusiveDefOutChan(no, chbits); + int j = mdevView->rowCount(); + for(int i = 0; i < j; ++i) + mdevView->item(i, DEVCOL_DEF_OUT_CHANS)->setText(bitmap2String(i == no ? chbits : 0)); + if(defpup) + { + QAction* a; + for(int i = 0; i < MIDI_CHANNELS; ++i) + { + a = defpup->findActionFromData(i); + if(a) + a->setChecked(i == actid); + } + } + } + #endif + } +} + +//--------------------------------------------------------- // mdevViewItemRenamed //--------------------------------------------------------- @@ -59,26 +229,81 @@ void MPConfig::mdevViewItemRenamed(QTableWidgetItem* item) return; switch(col) { + // Enabled: Use editor (Not good - only responds if text changed. We need to respond always). + // Disabled: Use pop-up menu. + #if 0 case DEVCOL_DEF_IN_CHANS: { QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if(no < 0 || no >= MIDI_PORTS) return; - midiPorts[no].setDefaultInChannels(((1 << MIDI_CHANNELS) - 1) & string2bitmap(s)); + int allch = (1 << MIDI_CHANNELS) - 1; + int ch = allch & string2bitmap(s); + midiPorts[no].setDefaultInChannels(ch); + + if(!song->midis()->empty() && midiPorts[no].device()) // Only if there are tracks, and device is valid. + { + int ret = QMessageBox::question(this, tr("Default input connections"), + tr("Setting will apply to new midi tracks.\n" + "Do you want to apply to all existing midi tracks now?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if(ret == QMessageBox::Yes) + { + MidiTrackList* mtl = song->midis(); + for(iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) + { + // Remove all routes from this port to the tracks first. + audio->msgRemoveRoute(Route(no, allch), Route(*it, allch)); + if(ch) + audio->msgAddRoute(Route(no, ch), Route(*it, ch)); + } + } + } song->update(); } break; + #endif + + // Enabled: Use editor (Not good - only responds if text changed. We need to respond always). + // Disabled: Use pop-up menu. + // Only turn on if and when multiple output routes are supported. + #if 0 case DEVCOL_DEF_OUT_CHANS: { QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if(no < 0 || no >= MIDI_PORTS) return; - midiPorts[no].setDefaultOutChannels(((1 << MIDI_CHANNELS) - 1) & string2bitmap(s)); + int allch = (1 << MIDI_CHANNELS) - 1; + int ch = allch & string2bitmap(s); + midiPorts[no].setDefaultOutChannels(ch); + + if(!song->midis()->empty() && midiPorts[no].device()) // Only if there are tracks, and device is valid. + { + int ret = QMessageBox::question(this, tr("Default output connections"), + tr("Setting will apply to new midi tracks.\n" + "Do you want to apply to all existing midi tracks now?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if(ret == QMessageBox::Yes) + { + MidiTrackList* mtl = song->midis(); + for(iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) + { + // Remove all routes from the tracks to this port first. + audio->msgRemoveRoute(Route(*it, allch), Route(no, allch)); + if(ch) + audio->msgAddRoute(Route(*it, ch), Route(no, ch)); + } + } + } song->update(); } break; + # endif + case DEVCOL_NAME: { QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); @@ -134,93 +359,76 @@ void MPConfig::rbClicked(QTableWidgetItem* item) int rwFlags = dev ? dev->rwFlags() : 0; int openFlags = dev ? dev->openFlags() : 0; QTableWidget* listView = item->tableWidget(); - //printf("MPConfig::rbClicked cpt x:%d y:%d\n", cpt.x(), cpt.y()); - //printf("MPConfig::rbClicked new cpt x:%d y:%d\n", cpt.x(), cpt.y()); - //printf("MPConfig::rbClicked new mapped cpt x:%d y:%d\n", cpt.x(), cpt.y()); QPoint ppt = listView->visualItemRect(item).bottomLeft(); QPoint mousepos = QCursor::pos(); - //printf("MPConfig::rbClicked ppt x:%d y:%d\n", ppt.x(), ppt.y()); int col = item->column(); ppt += QPoint(0, listView->horizontalHeader()->height()); - //printf("MPConfig::rbClicked new ppt x:%d y:%d\n", ppt.x(), ppt.y()); ppt = listView->mapToGlobal(ppt); - //printf("MPConfig::rbClicked new mapped ppt x:%d y:%d\n", ppt.x(), ppt.y()); switch (col) { case DEVCOL_GUI: if (dev == 0) - //break; return; if (port->hasGui()) { port->instrument()->showGui(!port->guiVisible()); item->setIcon(port->guiVisible() ? QIcon(*dotIcon) : QIcon(*dothIcon)); } - //break; return; case DEVCOL_REC: if (dev == 0 || !(rwFlags & 2)) - //break; return; openFlags ^= 0x2; dev->setOpenFlags(openFlags); midiSeq->msgSetMidiDevice(port, dev); // reopen device item->setIcon(openFlags & 2 ? QIcon(*dotIcon) : QIcon(*dothIcon)); - // p3.3.55 if(dev->deviceType() == MidiDevice::JACK_MIDI) { if(dev->openFlags() & 2) { - //item->setPixmap(DEVCOL_INROUTES, *buttondownIcon); + item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setIcon(QIcon(*buttondownIcon)); item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setText(tr("in")); } else { - //item->setPixmap(DEVCOL_INROUTES, *buttondownIcon); + item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setIcon(QIcon()); item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setText(""); } } - - //break; return; case DEVCOL_PLAY: if (dev == 0 || !(rwFlags & 1)) - //break; return; openFlags ^= 0x1; dev->setOpenFlags(openFlags); midiSeq->msgSetMidiDevice(port, dev); // reopen device item->setIcon(openFlags & 1 ? QIcon(*dotIcon) : QIcon(*dothIcon)); - // p3.3.55 if(dev->deviceType() == MidiDevice::JACK_MIDI) { if(dev->openFlags() & 1) { - //item->setPixmap(DEVCOL_OUTROUTES, *buttondownIcon); + item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setIcon(QIcon(*buttondownIcon)); item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setText(tr("out")); } else { - //item->setPixmap(DEVCOL_OUTROUTES, *buttondownIcon); + item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setIcon(QIcon()); item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setText(""); } } - - //break; return; - //case DEVCOL_ROUTES: - case DEVCOL_INROUTES: // p3.3.55 + case DEVCOL_INROUTES: case DEVCOL_OUTROUTES: { if(!checkAudioDevice()) return; - if(audioDevice->deviceType() != AudioDevice::JACK_AUDIO) // p3.3.52 Only if Jack is running. + if(audioDevice->deviceType() != AudioDevice::JACK_AUDIO) // Only if Jack is running. return; if(!dev) @@ -232,13 +440,12 @@ void MPConfig::rbClicked(QTableWidgetItem* item) if(dev->deviceType() != MidiDevice::JACK_MIDI) return; - //if(!(dev->rwFlags() & 3)) - //if(!(dev->rwFlags() & ((col == DEVCOL_OUTROUTES) ? 1 : 2))) // p3.3.55 + //if(!(dev->rwFlags() & ((col == DEVCOL_OUTROUTES) ? 1 : 2))) if(!(dev->openFlags() & ((col == DEVCOL_OUTROUTES) ? 1 : 2))) return; //RouteList* rl = (dev->rwFlags() & 1) ? dev->outRoutes() : dev->inRoutes(); - RouteList* rl = (col == DEVCOL_OUTROUTES) ? dev->outRoutes() : dev->inRoutes(); // p3.3.55 + RouteList* rl = (col == DEVCOL_OUTROUTES) ? dev->outRoutes() : dev->inRoutes(); QMenu* pup = 0; int gid = 0; std::list<QString> sl; @@ -250,7 +457,6 @@ void MPConfig::rbClicked(QTableWidgetItem* item) // Jack input ports if device is writable, and jack output ports if device is readable. //sl = (dev->rwFlags() & 1) ? audioDevice->inputPorts(true, _showAliases) : audioDevice->outputPorts(true, _showAliases); - // p3.3.55 sl = (col == DEVCOL_OUTROUTES) ? audioDevice->inputPorts(true, _showAliases) : audioDevice->outputPorts(true, _showAliases); //for (int i = 0; i < channel; ++i) @@ -283,7 +489,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) //Route dst(*ip, true, i); //Route rt(*ip, (dev->rwFlags() & 1), -1, Route::JACK_ROUTE); - Route rt(*ip, (col == DEVCOL_OUTROUTES), -1, Route::JACK_ROUTE); // p3.3.55 + Route rt(*ip, (col == DEVCOL_OUTROUTES), -1, Route::JACK_ROUTE); for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) { if (*ir == rt) @@ -324,8 +530,8 @@ void MPConfig::rbClicked(QTableWidgetItem* item) QString s(act->text()); - //if(dev->rwFlags() & 1) // Writable - if(col == DEVCOL_OUTROUTES) // Writable p3.3.55 + //if(dev->rwFlags() & 1) // Writeable + if(col == DEVCOL_OUTROUTES) // Writeable { Route srcRoute(dev, -1); Route dstRoute(s, true, -1, Route::JACK_ROUTE); @@ -345,7 +551,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) } else //if(dev->rwFlags() & 2) // Readable - //if(col == DEVCOL_INROUTES) // Readable p3.3.55 + //if(col == DEVCOL_INROUTES) // Readable { Route srcRoute(s, false, -1, Route::JACK_ROUTE); Route dstRoute(dev, -1); @@ -367,7 +573,6 @@ void MPConfig::rbClicked(QTableWidgetItem* item) audio->msgUpdateSoloStates(); song->update(SC_ROUTE); - // p3.3.46 //delete pup; // FIXME: // Routes can't be re-read until the message sent from msgAddRoute1() @@ -381,11 +586,86 @@ void MPConfig::rbClicked(QTableWidgetItem* item) return; case DEVCOL_DEF_IN_CHANS: + // Enabled: Use editor (Not good - only responds if text changed. We need to respond always). + // Disabled: Use pop-up menu. + #if 0 + return; + #else + { + defpup = new PopupMenu(this); + defpup->addAction(new MenuTitleItem("Channel", defpup)); + QAction* act = 0; + int chbits = midiPorts[no].defaultInChannels(); + for(int i = 0; i < MIDI_CHANNELS; ++i) + { + act = defpup->addAction(QString().setNum(i + 1)); + act->setData(i); + act->setCheckable(true); + act->setChecked((1 << i) & chbits); + } + + act = defpup->addAction(tr("Toggle all")); + act->setData(MIDI_CHANNELS); + + defpup->addSeparator(); + act = defpup->addAction(tr("Change all tracks now")); + act->setData(MIDI_CHANNELS + 1); + // Enable only if there are tracks, and port has a device. + // Tested: Hmm, allow ports with no device since that is a valid situation. + act->setEnabled(!song->midis()->empty()); // && midiPorts[no].device()); + + connect(defpup, SIGNAL(triggered(QAction*)), SLOT(changeDefInputRoutes(QAction*))); + //connect(defpup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); + //defpup->popup(QCursor::pos()); + defpup->exec(QCursor::pos()); + delete defpup; + defpup = 0; + } + return; + #endif + case DEVCOL_DEF_OUT_CHANS: + // Enabled: Use editor (Not good - only responds if text changed. We need to respond always). + // Disabled: Use pop-up menu. + // Only turn on if and when multiple output routes are supported. + #if 0 + return; + #else { + defpup = new PopupMenu(this); + defpup->addAction(new MenuTitleItem("Channel", defpup)); + QAction* act = 0; + int chbits = midiPorts[no].defaultOutChannels(); + for(int i = 0; i < MIDI_CHANNELS; ++i) + { + act = defpup->addAction(QString().setNum(i + 1)); + act->setData(i); + act->setCheckable(true); + act->setChecked((1 << i) & chbits); + } + + // Turn on if and when multiple output routes are supported. + #if 0 + act = defpup->addAction(tr("Toggle all")); + act->setData(MIDI_CHANNELS); + #endif + + defpup->addSeparator(); + act = defpup->addAction(tr("Change all tracks now")); + act->setData(MIDI_CHANNELS + 1); + // Enable only if there are tracks, and port has a device. + // Tested: Hmm, allow ports with no device since that is a valid situation. + act->setEnabled(!song->midis()->empty()); // && midiPorts[no].device()); + + connect(defpup, SIGNAL(triggered(QAction*)), SLOT(changeDefOutputRoutes(QAction*))); + //connect(defpup, SIGNAL(aboutToHide()), muse, SLOT(routingPopupMenuAboutToHide())); + //defpup->popup(QCursor::pos()); + defpup->exec(QCursor::pos()); + delete defpup; + defpup = 0; } - //break; return; + #endif case DEVCOL_NAME: { @@ -550,7 +830,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) if(n < 0x10000000) { delete pup; - if(n <= 2) // p3.3.55 + if(n <= 2) { sdev = MidiJackDevice::createJackMidiDevice(); @@ -615,7 +895,7 @@ void MPConfig::rbClicked(QTableWidgetItem* item) instrPopup->addAction((*i)->iname()); } - QAction* act = instrPopup->exec(ppt, 0); + QAction* act = instrPopup->exec(ppt); if(!act) //break; return; @@ -652,8 +932,13 @@ void MPConfig::setToolTip(QTableWidgetItem *item, int col) //case DEVCOL_ROUTES: item->setToolTip(tr("Jack midi ports")); break; case DEVCOL_INROUTES: item->setToolTip(tr("Connections from Jack Midi outputs")); break; case DEVCOL_OUTROUTES: item->setToolTip(tr("Connections to Jack Midi inputs")); break; - case DEVCOL_DEF_IN_CHANS: item->setToolTip(tr("Connect these to new midi tracks")); break; - case DEVCOL_DEF_OUT_CHANS: item->setToolTip(tr("Connect new midi tracks to this (first listed only)")); break; + case DEVCOL_DEF_IN_CHANS: item->setToolTip(tr("Auto-connect these channels to new midi tracks")); break; + // Turn on if and when multiple output routes are supported. + #if 0 + case DEVCOL_DEF_OUT_CHANS: item->setToolTip(tr("Auto-connect new midi tracks to these channels")); break; + #else + case DEVCOL_DEF_OUT_CHANS: item->setToolTip(tr("Auto-connect new midi tracks to this channel")); break; + #endif case DEVCOL_STATE: item->setToolTip(tr("Device state")); break; default: return; } @@ -686,17 +971,21 @@ void MPConfig::setWhatsThis(QTableWidgetItem *item, int col) case DEVCOL_OUTROUTES: item->setWhatsThis(tr("Connections to Jack Midi input ports")); break; case DEVCOL_DEF_IN_CHANS: - item->setWhatsThis(tr("Connect these channels, on this port, to new midi tracks.\n" - "Example:\n" - " 1 2 3 channel 1 2 and 3\n" - " 1-3 same\n" - " 1-3 5 channel 1 2 3 and 5\n" - " all all channels\n" - " none no channels")); break; + //item->setWhatsThis(tr("Auto-connect these channels, on this port, to new midi tracks.\n" + // "Example:\n" + // " 1 2 3 channel 1 2 and 3\n" + // " 1-3 same\n" + // " 1-3 5 channel 1 2 3 and 5\n" + // " all all channels\n" + // " none no channels")); break; + item->setWhatsThis(tr("Auto-connect these channels, on this port, to new midi tracks.")); break; case DEVCOL_DEF_OUT_CHANS: - item->setWhatsThis(tr("Connect new midi tracks to these channels, on this port.\n" - "See default in channels.\n" - "NOTE: Currently only one output port and channel supported (first found)")); break; + // Turn on if and when multiple output routes are supported. + #if 0 + item->setWhatsThis(tr("Connect new midi tracks to these channels, on this port.")); break; + #else + item->setWhatsThis(tr("Connect new midi tracks to this channel, on this port.")); break; + #endif case DEVCOL_STATE: item->setWhatsThis(tr("State: result of opening the device")); break; default: @@ -732,6 +1021,7 @@ MPConfig::MPConfig(QWidget* parent) //popup = 0; instrPopup = 0; + defpup = 0; _showAliases = -1; // 0: Show first aliases, if available. Nah, stick with -1: none at first. QStringList columnnames; @@ -768,7 +1058,8 @@ 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())); - songChanged(0); + //songChanged(0); + songChanged(SC_CONFIG); } @@ -793,7 +1084,10 @@ void MPConfig::selectionChanged() void MPConfig::songChanged(int flags) { // Is it simply a midi controller value adjustment? Forget it. - if(flags == SC_MIDI_CONTROLLER) + //if(flags == SC_MIDI_CONTROLLER) + // return; + // No need for anything but this, yet. + if(!(flags & SC_CONFIG)) return; // Get currently selected index... @@ -809,6 +1103,7 @@ void MPConfig::songChanged(int flags) sitem = 0; mdevView->clearContents(); + int defochs = 0; for (int i = MIDI_PORTS-1; i >= 0; --i) { mdevView->blockSignals(true); // otherwise itemChanged() is triggered and bad things happen. @@ -849,14 +1144,50 @@ void MPConfig::songChanged(int flags) QTableWidgetItem* itemin = new QTableWidgetItem; addItem(i, DEVCOL_INROUTES, itemin, mdevView); itemin->setFlags(Qt::ItemIsEnabled); - QTableWidgetItem* itemdefin = new QTableWidgetItem(bitmap2String(port->defaultInChannels())); + //QTableWidgetItem* itemdefin = new QTableWidgetItem(bitmap2String(port->defaultInChannels())); + // Ignore synth devices. Default input routes make no sense for them (right now). + QTableWidgetItem* itemdefin = new QTableWidgetItem((dev && dev->isSynti()) ? + QString() : bitmap2String(port->defaultInChannels())); addItem(i, DEVCOL_DEF_IN_CHANS, itemdefin, mdevView); - itemdefin->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled); + // Enabled: Use editor (not good). Disabled: Use pop-up menu. + #if 0 + itemdefin->setFlags((dev && dev->isSynti()) ? Qt::NoItemFlags : Qt::ItemIsEditable | Qt::ItemIsEnabled); + # else + if(dev && dev->isSynti()) + itemdefin->setFlags(Qt::NoItemFlags); + else + { + itemdefin->setFlags(Qt::ItemIsEnabled); + itemdefin->setIcon(QIcon(*buttondownIcon)); + } + #endif + + // Turn on if and when multiple output routes are supported. + #if 0 QTableWidgetItem* itemdefout = new QTableWidgetItem(bitmap2String(port->defaultOutChannels())); addItem(i, DEVCOL_DEF_OUT_CHANS, itemdefout, mdevView); itemdefout->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled); - mdevView->blockSignals(false); - + #else + //QTableWidgetItem* itemdefout = new QTableWidgetItem(QString("--")); + QTableWidgetItem* itemdefout = new QTableWidgetItem(bitmap2String(0)); + defochs = port->defaultOutChannels(); + if(defochs) + { + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(defochs & (1 << ch)) + { + itemdefout->setText(QString().setNum(ch + 1)); + break; + } + } + } + addItem(i, DEVCOL_DEF_OUT_CHANS, itemdefout, mdevView); + itemdefout->setFlags(Qt::ItemIsEnabled); + itemdefout->setIcon(QIcon(*buttondownIcon)); + #endif + + mdevView->blockSignals(false); if (dev) { itemname->setText(dev->name()); @@ -898,20 +1229,23 @@ void MPConfig::songChanged(int flags) //item->setPixmap(DEVCOL_ROUTES, *buttondownIcon); //item->setText(DEVCOL_ROUTES, tr("routes")); - // p3.3.55 if(dev->rwFlags() & 1) //if(dev->openFlags() & 1) { - itemout->setIcon(QIcon(*buttondownIcon)); if(dev->openFlags() & 1) + { + itemout->setIcon(QIcon(*buttondownIcon)); itemout->setText(tr("out")); + } } if(dev->rwFlags() & 2) //if(dev->openFlags() & 2) { - itemin->setIcon(QIcon(*buttondownIcon)); if(dev->openFlags() & 2) + { + itemin->setIcon(QIcon(*buttondownIcon)); itemin->setText(tr("in")); + } } } diff --git a/muse2/muse/confmport.h b/muse2/muse/confmport.h index d8bd663b..f09c4d65 100644 --- a/muse2/muse/confmport.h +++ b/muse2/muse/confmport.h @@ -18,6 +18,8 @@ class QTreeWidget; class QTableWidget; class QPoint; class QMenu; +class QAction; +class PopupMenu; class Xml; //--------------------------------------------------------- @@ -28,6 +30,7 @@ class Xml; class MPConfig : public QDialog, Ui::SynthConfigBase { QMenu* instrPopup; //QMenu* popup; + PopupMenu* defpup; int _showAliases; // -1: None. 0: First aliases. 1: Second aliases etc. void setWhatsThis(QTableWidgetItem *item, int col); void setToolTip(QTableWidgetItem *item, int col); @@ -42,6 +45,8 @@ class MPConfig : public QDialog, Ui::SynthConfigBase { void selectionChanged(); void addInstanceClicked(); void removeInstanceClicked(); + void changeDefInputRoutes(QAction* act); + void changeDefOutputRoutes(QAction* act); public: MPConfig(QWidget* parent=0); diff --git a/muse2/muse/midiport.cpp b/muse2/muse/midiport.cpp index 0220a353..6d04cff2 100644 --- a/muse2/muse/midiport.cpp +++ b/muse2/muse/midiport.cpp @@ -40,6 +40,9 @@ void initMidiPorts() ///port->setInstrument(genericMidiInstrument); port->setInstrument(registerMidiInstrument("GM")); // Changed by Tim. port->syncInfo().setPort(i); + // p4.0.17 Set the first channel on the first port to auto-connect to midi track outputs. + if(i == 0) + port->setDefaultOutChannels(1); } } @@ -50,7 +53,8 @@ void initMidiPorts() MidiPort::MidiPort() : _state("not configured") { - _defaultInChannels = 0; + //_defaultInChannels = 0; + _defaultInChannels = (1 << MIDI_CHANNELS) -1; // p4.0.17 Default is now to connect to all channels. _defaultOutChannels = 0; _device = 0; _instrument = 0; @@ -1026,15 +1030,16 @@ void MidiPort::setNullSendValue(int v) } //--------------------------------------------------------- -// writeRouting // p3.3.50 +// writeRouting //--------------------------------------------------------- void MidiPort::writeRouting(int level, Xml& xml) const { // If this device is not actually in use by the song, do not write any routes. // This prevents bogus routes from being saved and propagated in the med file. - if(!device()) - return; + // p4.0.17 Reverted. Allow ports with no device to save. + //if(!device()) + // return; QString s; @@ -1062,3 +1067,20 @@ void MidiPort::writeRouting(int level, Xml& xml) const } } +// p4.0.17 Turn off if and when multiple output routes supported. +#if 1 +//--------------------------------------------------------- +// setPortExclusiveDefOutChan +//--------------------------------------------------------- + +void setPortExclusiveDefOutChan(int port, int c) +{ + if(port < 0 || port >= MIDI_PORTS) + return; + midiPorts[port].setDefaultOutChannels(c); + for(int i = 0; i < MIDI_PORTS; ++i) + if(i != port) + midiPorts[i].setDefaultOutChannels(0); +} +#endif + diff --git a/muse2/muse/midiport.h b/muse2/muse/midiport.h index 0fa33a11..46107a8d 100644 --- a/muse2/muse/midiport.h +++ b/muse2/muse/midiport.h @@ -127,6 +127,11 @@ class MidiPort { extern MidiPort midiPorts[MIDI_PORTS]; extern void initMidiPorts(); +// p4.0.17 Turn off if and when multiple output routes supported. +#if 1 +extern void setPortExclusiveDefOutChan(int /*port*/, int /*chan*/); +#endif + class QMenu; class QWidget; //extern QPopupMenu* midiPortsPopup(QWidget*); diff --git a/muse2/muse/mixer/astrip.cpp b/muse2/muse/mixer/astrip.cpp index 1ed476ab..4e3fbe26 100644 --- a/muse2/muse/mixer/astrip.cpp +++ b/muse2/muse/mixer/astrip.cpp @@ -1568,7 +1568,11 @@ static int addMidiPorts(AudioTrack* t, PopupMenu* pup, int id, RouteMenuMap& mm, //if(!(md->rwFlags() & (isOutput ? 1 : 2))) // continue; - RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); + // p4.0.17 Do not list synth devices! + if(md->isSynti()) + continue; + + RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); PopupMenu* subp = new PopupMenu(pup); subp->setTitle(md->name()); diff --git a/muse2/muse/route.cpp b/muse2/muse/route.cpp index c3df6517..6f42c1f2 100644 --- a/muse2/muse/route.cpp +++ b/muse2/muse/route.cpp @@ -401,6 +401,15 @@ void addRoute(Route src, Route dst) } */ + MidiPort *mp = &midiPorts[src.midiPort]; + + // p4.0.17 Do not allow ports with synth midi devices to connect to audio ins! + if(dst.track->type() == Track::AUDIO_INPUT && mp->device() && mp->device()->isSynti()) + { + fprintf(stderr, "addRoute: destination is audio in, but source midi port:%d is synth device\n", src.midiPort); + return; + } + if(dst.channel < 1 || dst.channel >= (1 << MIDI_CHANNELS)) { fprintf(stderr, "addRoute: source is midi port:%d, but destination channel mask:%d out of range\n", src.midiPort, dst.channel); @@ -414,8 +423,6 @@ void addRoute(Route src, Route dst) // return; //} - MidiPort *mp = &midiPorts[src.midiPort]; - src.channel = dst.channel; RouteList* outRoutes = mp->outRoutes(); //for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index 6b9c7e09..cf972b8c 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -260,17 +260,20 @@ Track* Song::addTrack(int t) { MidiTrack* mt = (MidiTrack*)track; int c, cbi, ch; - bool defOutFound = false; /// TODO: Remove this when multiple out routes supported. + bool defOutFound = false; /// TODO: Remove this if and when multiple output routes supported. for(int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &midiPorts[i]; - c = mp->defaultInChannels(); - if(c) + if(mp->device()) // Only if device is valid. p4.0.17 { - audio->msgAddRoute(Route(i, c), Route(track, c)); - updateFlags |= SC_ROUTE; - } + c = mp->defaultInChannels(); + if(c) + { + audio->msgAddRoute(Route(i, c), Route(track, c)); + updateFlags |= SC_ROUTE; + } + } if(!defOutFound) /// { @@ -278,7 +281,7 @@ Track* Song::addTrack(int t) if(c) { - /// TODO: Switch when multiple out routes supported. + /// TODO: Switch if and when multiple output routes supported. #if 0 audio->msgAddRoute(Route(track, c), Route(i, c)); updateFlags |= SC_ROUTE; @@ -290,7 +293,8 @@ Track* Song::addTrack(int t) { defOutFound = true; mt->setOutPort(i); - mt->setOutChannel(ch); + if(type != Track::DRUM) // p4.0.17 Leave drum tracks at channel 10. + mt->setOutChannel(ch); updateFlags |= SC_ROUTE; break; } @@ -2019,9 +2023,10 @@ void Song::panic() // signal - emit signals for changes if true // called from constructor as clear(false) and // from MusE::clearSong() as clear(false) +// If clear_all is false, it will not touch things like midi ports. //--------------------------------------------------------- -void Song::clear(bool signal) +void Song::clear(bool signal, bool /*clear_all*/) { if(debugMsg) printf("Song::clear\n"); @@ -2046,8 +2051,9 @@ void Song::clear(bool signal) // p3.3.50 Reset this. midiPorts[i].setFoundInSongFile(false); - // This will also close the device. - midiPorts[i].setMidiDevice(0); + //if(clear_all) // Allow not touching devices. p4.0.17 TESTING: Maybe some problems... + // This will also close the device. + midiPorts[i].setMidiDevice(0); } _synthIs.clearDelete(); @@ -2064,15 +2070,18 @@ void Song::clear(bool signal) //if((*imd)->deviceType() == MidiDevice::JACK_MIDI) if(dynamic_cast< MidiJackDevice* >(*imd)) { - // Remove the device from the list. - midiDevices.erase(imd); - // Since Jack midi devices are created dynamically, we must delete them. - // The destructor unregisters the device from Jack, which also disconnects all device-to-jack routes. - // This will also delete all midi-track-to-device routes, they point to non-existant midi tracks - // which were all deleted above - delete (*imd); - loop = true; - break; + //if(clear_all) // Allow not touching devices. p4.0.17 TESTING: Maybe some problems... + { + // Remove the device from the list. + midiDevices.erase(imd); + // Since Jack midi devices are created dynamically, we must delete them. + // The destructor unregisters the device from Jack, which also disconnects all device-to-jack routes. + // This will also delete all midi-track-to-device routes, they point to non-existant midi tracks + // which were all deleted above + delete (*imd); + loop = true; + break; + } } else //if((*imd)->deviceType() == MidiDevice::ALSA_MIDI) diff --git a/muse2/muse/song.h b/muse2/muse/song.h index 90dc3205..adcbc0ae 100644 --- a/muse2/muse/song.h +++ b/muse2/muse/song.h @@ -168,7 +168,8 @@ class Song : public QObject { QString getSongInfo() { return songInfoStr; } void setSongInfo(QString info) { songInfoStr = info; } - void clear(bool signal); + // If clear_all is false, it will not touch things like midi ports. + void clear(bool signal, bool clear_all = true); void update(int flags = -1); void cleanupForQuit(); diff --git a/muse2/muse/track.cpp b/muse2/muse/track.cpp index 5038820e..bd3d7548 100644 --- a/muse2/muse/track.cpp +++ b/muse2/muse/track.cpp @@ -447,6 +447,23 @@ void MidiTrack::setOutPortAndUpdate(int i) } //--------------------------------------------------------- +// setOutPortAndChannelAndUpdate +//--------------------------------------------------------- + +void MidiTrack::setOutPortAndChannelAndUpdate(int port, int ch) +{ + if(_outPort == port && _outChannel == ch) + return; + + //removePortCtrlEvents(); + removePortCtrlEvents(this); + _outPort = port; + _outChannel = ch; + //addPortCtrlEvents(); + addPortCtrlEvents(this); +} + +//--------------------------------------------------------- // setInPortAndChannelMask // For old song files with port mask (max 32 ports) and channel mask (16 channels), // before midi routing was added (the iR button). p3.3.48 diff --git a/muse2/muse/track.h b/muse2/muse/track.h index 48c9474e..531dd2d5 100644 --- a/muse2/muse/track.h +++ b/muse2/muse/track.h @@ -239,8 +239,11 @@ class MidiTrack : public Track { void setOutChannel(int i) { _outChannel = i; } void setOutPort(int i) { _outPort = i; } - void setOutChanAndUpdate(int i); - void setOutPortAndUpdate(int i); + // These will transfer controller data to the new selected port and/or channel. + void setOutChanAndUpdate(int /*chan*/); + void setOutPortAndUpdate(int /*port*/); + // Combines both port and channel operations. + void setOutPortAndChannelAndUpdate(int /*port*/, int /*chan*/); //void setInPortMask(int i) { _inPortMask = i; } ///void setInPortMask(unsigned int i) { _inPortMask = i; } // Obsolete diff --git a/muse2/muse/widgets/mtrackinfo.cpp b/muse2/muse/widgets/mtrackinfo.cpp index 04911b02..acdfb42f 100644 --- a/muse2/muse/widgets/mtrackinfo.cpp +++ b/muse2/muse/widgets/mtrackinfo.cpp @@ -614,18 +614,30 @@ void MidiTrackInfo::inRoutesPressed() PopupMenu* pup = muse->prepareRoutingPopupMenu(selected, false); //PopupView* pup = muse->prepareRoutingPopupView(selected, false); - if(!pup) { - int ret = QMessageBox::warning(this, tr("No inputs"), - tr("There are no midi inputs.\n" + /* + QPoint ppt = QCursor::pos(); + + int i = 0; + for( ; i < MIDI_PORTS; ++i) + { + if(midiPorts[i].device() && !midiPorts[pi].device()->isSynti()) + break; + } + if(!pup || i == MIDI_PORTS) + { + int ret = QMessageBox::warning(this, tr("No devices"), + tr("There are no midi port devices defined.\n" "Do you want to open the midi configuration dialog?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok); if (ret == QMessageBox::Ok) { - printf("open config midi ports\n"); + //printf("open config midi ports\n"); muse->configMidiPorts(); } - return; + if(!pup) + return; } + */ ///gRoutingPopupMenuMaster = midiTrackInfo; gRoutingPopupMenuMaster = this; |