summaryrefslogtreecommitdiff
path: root/muse2/muse/widgets/routepopup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse2/muse/widgets/routepopup.cpp')
-rw-r--r--muse2/muse/widgets/routepopup.cpp1416
1 files changed, 1416 insertions, 0 deletions
diff --git a/muse2/muse/widgets/routepopup.cpp b/muse2/muse/widgets/routepopup.cpp
new file mode 100644
index 00000000..910d693d
--- /dev/null
+++ b/muse2/muse/widgets/routepopup.cpp
@@ -0,0 +1,1416 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+//
+// RoutePopupMenu.cpp
+// (C) Copyright 2011 Tim E. Real (terminator356 A T sourceforge D O T net)
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2.
+//
+// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+//=============================================================================
+
+#include "app.h"
+#include "routepopup.h"
+#include "midiport.h"
+#include "mididev.h"
+#include "audio.h"
+#include "driver/audiodev.h"
+#include "song.h"
+#include "track.h"
+#include "synth.h"
+#include "route.h"
+#include "icons.h"
+#include "menutitleitem.h"
+#include "popupmenu.h"
+
+//---------------------------------------------------------
+// addMenuItem
+//---------------------------------------------------------
+
+int RoutePopupMenu::addMenuItem(AudioTrack* track, Track* route_track, PopupMenu* lb, int id, int channel, int channels, bool isOutput)
+{
+ // totalInChannels is only used by syntis.
+ int toch = ((AudioTrack*)track)->totalOutChannels();
+ // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user.
+ if(track->channels() == 1)
+ toch = 1;
+
+ // Don't add the last stray mono route if the track is stereo.
+ //if(route_track->channels() > 1 && (channel+1 == chans))
+ // return id;
+
+ RouteList* rl = isOutput ? track->outRoutes() : track->inRoutes();
+
+ QAction* act;
+
+ QString s(route_track->name());
+
+ act = lb->addAction(s);
+ act->setCheckable(true);
+
+ int ach = channel;
+ int bch = -1;
+
+ Route r(route_track, isOutput ? ach : bch, channels);
+
+ r.remoteChannel = isOutput ? bch : ach;
+
+ act->setData(qVariantFromValue(r));
+
+ for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir)
+ {
+ if(ir->type == Route::TRACK_ROUTE && ir->track == route_track && ir->remoteChannel == r.remoteChannel)
+ {
+ int tcompch = r.channel;
+ if(tcompch == -1)
+ tcompch = 0;
+ int tcompchs = r.channels;
+ if(tcompchs == -1)
+ tcompchs = isOutput ? track->channels() : route_track->channels();
+
+ int compch = ir->channel;
+ if(compch == -1)
+ compch = 0;
+ int compchs = ir->channels;
+ if(compchs == -1)
+ compchs = isOutput ? track->channels() : ir->track->channels();
+
+ if(compch == tcompch && compchs == tcompchs)
+ {
+ act->setChecked(true);
+ break;
+ }
+ }
+ }
+ return ++id;
+}
+
+//---------------------------------------------------------
+// addAuxPorts
+//---------------------------------------------------------
+
+int RoutePopupMenu::addAuxPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput)
+ {
+ AuxList* al = song->auxs();
+ for (iAudioAux i = al->begin(); i != al->end(); ++i) {
+ Track* track = *i;
+ if (t == track)
+ continue;
+ id = addMenuItem(t, track, lb, id, channel, channels, isOutput);
+ }
+ return id;
+ }
+
+//---------------------------------------------------------
+// addInPorts
+//---------------------------------------------------------
+
+int RoutePopupMenu::addInPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput)
+ {
+ InputList* al = song->inputs();
+ for (iAudioInput i = al->begin(); i != al->end(); ++i) {
+ Track* track = *i;
+ if (t == track)
+ continue;
+ id = addMenuItem(t, track, lb, id, channel, channels, isOutput);
+ }
+ return id;
+ }
+
+//---------------------------------------------------------
+// addOutPorts
+//---------------------------------------------------------
+
+int RoutePopupMenu::addOutPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput)
+ {
+ OutputList* al = song->outputs();
+ for (iAudioOutput i = al->begin(); i != al->end(); ++i) {
+ Track* track = *i;
+ if (t == track)
+ continue;
+ id = addMenuItem(t, track, lb, id, channel, channels, isOutput);
+ }
+ return id;
+ }
+
+//---------------------------------------------------------
+// addGroupPorts
+//---------------------------------------------------------
+
+int RoutePopupMenu::addGroupPorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput)
+ {
+ GroupList* al = song->groups();
+ for (iAudioGroup i = al->begin(); i != al->end(); ++i) {
+ Track* track = *i;
+ if (t == track)
+ continue;
+ id = addMenuItem(t, track, lb, id, channel, channels, isOutput);
+ }
+ return id;
+ }
+
+//---------------------------------------------------------
+// addWavePorts
+//---------------------------------------------------------
+
+int RoutePopupMenu::addWavePorts(AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput)
+ {
+ WaveTrackList* al = song->waves();
+ for (iWaveTrack i = al->begin(); i != al->end(); ++i) {
+ Track* track = *i;
+ if (t == track)
+ continue;
+ id = addMenuItem(t, track, lb, id, channel, channels, isOutput);
+ }
+ return id;
+ }
+
+//---------------------------------------------------------
+// addSyntiPorts
+//---------------------------------------------------------
+
+int RoutePopupMenu::addSyntiPorts(AudioTrack* t, PopupMenu* lb, int id,
+ int channel, int channels, bool isOutput)
+{
+ RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes();
+
+ QAction* act;
+
+ SynthIList* al = song->syntis();
+ for (iSynthI i = al->begin(); i != al->end(); ++i)
+ {
+ Track* track = *i;
+ if (t == track)
+ continue;
+ int toch = ((AudioTrack*)track)->totalOutChannels();
+ // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user.
+ if(track->channels() == 1)
+ toch = 1;
+
+ // totalInChannels is only used by syntis.
+ int chans = (!isOutput || track->type() != Track::AUDIO_SOFTSYNTH) ? toch : ((AudioTrack*)track)->totalInChannels();
+
+ int tchans = (channels != -1) ? channels: t->channels();
+ if(tchans == 2)
+ {
+ // Ignore odd numbered left-over mono channel.
+ //chans = chans & ~1;
+ //if(chans != 0)
+ chans -= 1;
+ }
+
+ if(chans > 0)
+ {
+ PopupMenu* chpup = new PopupMenu(lb, true);
+ chpup->setTitle(track->name());
+ for(int ch = 0; ch < chans; ++ch)
+ {
+ char buffer[128];
+ if(tchans == 2)
+ snprintf(buffer, 128, "%s %d,%d", chpup->tr("Channel").toLatin1().constData(), ch+1, ch+2);
+ else
+ snprintf(buffer, 128, "%s %d", chpup->tr("Channel").toLatin1().constData(), ch+1);
+ act = chpup->addAction(QString(buffer));
+ act->setCheckable(true);
+
+ int ach = (channel == -1) ? ch : channel;
+ int bch = (channel == -1) ? -1 : ch;
+
+ Route rt(track, (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? ach : bch, tchans);
+ rt.remoteChannel = (t->type() != Track::AUDIO_SOFTSYNTH || isOutput) ? bch : ach;
+
+ act->setData(qVariantFromValue(rt));
+
+ for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir)
+ {
+ if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel)
+ {
+ int tcompch = rt.channel;
+ if(tcompch == -1)
+ tcompch = 0;
+ int tcompchs = rt.channels;
+ if(tcompchs == -1)
+ tcompchs = isOutput ? t->channels() : track->channels();
+
+ int compch = ir->channel;
+ if(compch == -1)
+ compch = 0;
+ int compchs = ir->channels;
+ if(compchs == -1)
+ compchs = isOutput ? t->channels() : ir->track->channels();
+
+ if(compch == tcompch && compchs == tcompchs)
+ {
+ act->setChecked(true);
+ break;
+ }
+ }
+ }
+ ++id;
+ }
+
+ lb->addMenu(chpup);
+ }
+ }
+ return id;
+}
+
+//---------------------------------------------------------
+// addMultiChannelOutPorts
+//---------------------------------------------------------
+
+int RoutePopupMenu::addMultiChannelPorts(AudioTrack* t, PopupMenu* pup, int id, bool isOutput)
+{
+ int toch = t->totalOutChannels();
+ // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user.
+ if(t->channels() == 1)
+ toch = 1;
+
+ // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less.
+ // totalInChannels is only used by syntis.
+ int chans = (isOutput || t->type() != Track::AUDIO_SOFTSYNTH) ? toch : t->totalInChannels();
+
+ if(chans > 1)
+ pup->addAction(new MenuTitleItem("<Mono>", pup));
+
+ //
+ // If it's more than one channel, create a sub-menu. If it's just one channel, don't bother with a sub-menu...
+ //
+
+ PopupMenu* chpup = pup;
+
+ for(int ch = 0; ch < chans; ++ch)
+ {
+ // If more than one channel, create the sub-menu.
+ if(chans > 1)
+ chpup = new PopupMenu(pup, true);
+
+ if(isOutput)
+ {
+ switch(t->type())
+ {
+
+ case Track::AUDIO_INPUT:
+ case Track::WAVE:
+ case Track::AUDIO_GROUP:
+ case Track::AUDIO_SOFTSYNTH:
+ case Track::AUDIO_AUX:
+ id = addWavePorts(t, chpup, id, ch, 1, isOutput);
+ id = addOutPorts(t, chpup, id, ch, 1, isOutput);
+ id = addGroupPorts(t, chpup, id, ch, 1, isOutput);
+ id = addSyntiPorts(t, chpup, id, ch, 1, isOutput);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch(t->type())
+ {
+
+ case Track::AUDIO_OUTPUT:
+ id = addWavePorts(t, chpup, id, ch, 1, isOutput);
+ id = addInPorts(t, chpup, id, ch, 1, isOutput);
+ id = addGroupPorts(t, chpup, id, ch, 1, isOutput);
+ id = addAuxPorts(t, chpup, id, ch, 1, isOutput);
+ id = addSyntiPorts(t, chpup, id, ch, 1, isOutput);
+ break;
+ case Track::WAVE:
+ case Track::AUDIO_SOFTSYNTH:
+ case Track::AUDIO_GROUP:
+ id = addWavePorts(t, chpup, id, ch, 1, isOutput);
+ id = addInPorts(t, chpup, id, ch, 1, isOutput);
+ id = addGroupPorts(t, chpup, id, ch, 1, isOutput);
+ id = addAuxPorts(t, chpup, id, ch, 1, isOutput);
+ id = addSyntiPorts(t, chpup, id, ch, 1, isOutput);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // If more than one channel, add the created sub-menu.
+ if(chans > 1)
+ {
+ char buffer[128];
+ snprintf(buffer, 128, "%s %d", pup->tr("Channel").toLatin1().constData(), ch+1);
+ chpup->setTitle(QString(buffer));
+ pup->addMenu(chpup);
+ }
+ }
+
+ // For stereo listing, ignore odd numbered left-over channels.
+ chans -= 1;
+ if(chans > 0)
+ {
+ // Ignore odd numbered left-over channels.
+ //int schans = (chans & ~1) - 1;
+
+ pup->addSeparator();
+ pup->addAction(new MenuTitleItem("<Stereo>", pup));
+
+ //
+ // If it's more than two channels, create a sub-menu. If it's just two channels, don't bother with a sub-menu...
+ //
+
+ chpup = pup;
+ if(chans <= 2)
+ // Just do one iteration.
+ chans = 1;
+
+ for(int ch = 0; ch < chans; ++ch)
+ {
+ // If more than two channels, create the sub-menu.
+ if(chans > 2)
+ chpup = new PopupMenu(pup, true);
+
+ if(isOutput)
+ {
+ switch(t->type())
+ {
+ case Track::AUDIO_INPUT:
+ case Track::WAVE:
+ case Track::AUDIO_GROUP:
+ case Track::AUDIO_SOFTSYNTH:
+ case Track::AUDIO_AUX:
+ id = addWavePorts(t, chpup, id, ch, 2, isOutput);
+ id = addOutPorts(t, chpup, id, ch, 2, isOutput);
+ id = addGroupPorts(t, chpup, id, ch, 2, isOutput);
+ id = addSyntiPorts(t, chpup, id, ch, 2, isOutput);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch(t->type())
+ {
+ case Track::AUDIO_OUTPUT:
+ id = addWavePorts(t, chpup, id, ch, 2, isOutput);
+ id = addInPorts(t, chpup, id, ch, 2, isOutput);
+ id = addGroupPorts(t, chpup, id, ch, 2, isOutput);
+ id = addAuxPorts(t, chpup, id, ch, 2, isOutput);
+ id = addSyntiPorts(t, chpup, id, ch, 2, isOutput);
+ break;
+ case Track::WAVE:
+ case Track::AUDIO_SOFTSYNTH:
+ case Track::AUDIO_GROUP:
+ id = addWavePorts(t, chpup, id, ch, 2, isOutput);
+ id = addInPorts(t, chpup, id, ch, 2, isOutput);
+ id = addGroupPorts(t, chpup, id, ch, 2, isOutput);
+ id = addAuxPorts(t, chpup, id, ch, 2, isOutput);
+ id = addSyntiPorts(t, chpup, id, ch, 2, isOutput);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // If more than two channels, add the created sub-menu.
+ if(chans > 2)
+ {
+ char buffer[128];
+ snprintf(buffer, 128, "%s %d,%d", pup->tr("Channel").toLatin1().constData(), ch+1, ch+2);
+ chpup->setTitle(QString(buffer));
+ pup->addMenu(chpup);
+ }
+ }
+ }
+
+ return id;
+}
+
+//---------------------------------------------------------
+// nonSyntiTrackAddSyntis
+//---------------------------------------------------------
+
+int RoutePopupMenu::nonSyntiTrackAddSyntis(AudioTrack* t, PopupMenu* lb, int id, bool isOutput)
+{
+ RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes();
+
+ QAction* act;
+ SynthIList* al = song->syntis();
+ for (iSynthI i = al->begin(); i != al->end(); ++i)
+ {
+ Track* track = *i;
+ if (t == track)
+ continue;
+
+ int toch = ((AudioTrack*)track)->totalOutChannels();
+ // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user.
+ if(track->channels() == 1)
+ toch = 1;
+
+ // totalInChannels is only used by syntis.
+ int chans = (!isOutput || track->type() != Track::AUDIO_SOFTSYNTH) ? toch : ((AudioTrack*)track)->totalInChannels();
+
+ //int schans = synti->channels();
+ //if(schans < chans)
+ // chans = schans;
+// int tchans = (channels != -1) ? channels: t->channels();
+// if(tchans == 2)
+// {
+ // Ignore odd numbered left-over mono channel.
+ //chans = chans & ~1;
+ //if(chans != 0)
+// chans -= 1;
+// }
+ //int tchans = (channels != -1) ? channels: t->channels();
+
+ if(chans > 0)
+ {
+ PopupMenu* chpup = new PopupMenu(lb, true);
+ chpup->setTitle(track->name());
+ if(chans > 1)
+ chpup->addAction(new MenuTitleItem("<Mono>", chpup));
+
+ for(int ch = 0; ch < chans; ++ch)
+ {
+ char buffer[128];
+ snprintf(buffer, 128, "%s %d", chpup->tr("Channel").toLatin1().constData(), ch+1);
+ act = chpup->addAction(QString(buffer));
+ act->setCheckable(true);
+
+ int ach = ch;
+ int bch = -1;
+
+ Route rt(track, isOutput ? bch : ach, 1);
+
+ rt.remoteChannel = isOutput ? ach : bch;
+
+ act->setData(qVariantFromValue(rt));
+
+ for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir)
+ {
+ if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel)
+ {
+ int tcompch = rt.channel;
+ if(tcompch == -1)
+ tcompch = 0;
+ int tcompchs = rt.channels;
+ if(tcompchs == -1)
+ tcompchs = isOutput ? t->channels() : track->channels();
+
+ int compch = ir->channel;
+ if(compch == -1)
+ compch = 0;
+ int compchs = ir->channels;
+ if(compchs == -1)
+ compchs = isOutput ? t->channels() : ir->track->channels();
+
+ if(compch == tcompch && compchs == tcompchs)
+ {
+ act->setChecked(true);
+ break;
+ }
+ }
+ }
+ ++id;
+ }
+
+ chans -= 1;
+ if(chans > 0)
+ {
+ // Ignore odd numbered left-over channels.
+ //int schans = (chans & ~1) - 1;
+
+ chpup->addSeparator();
+ chpup->addAction(new MenuTitleItem("<Stereo>", chpup));
+
+ for(int ch = 0; ch < chans; ++ch)
+ {
+ char buffer[128];
+ snprintf(buffer, 128, "%s %d,%d", chpup->tr("Channel").toLatin1().constData(), ch+1, ch+2);
+ act = chpup->addAction(QString(buffer));
+ act->setCheckable(true);
+
+ int ach = ch;
+ int bch = -1;
+
+ Route rt(track, isOutput ? bch : ach, 2);
+
+ rt.remoteChannel = isOutput ? ach : bch;
+
+ act->setData(qVariantFromValue(rt));
+
+ for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir)
+ {
+ if(ir->type == Route::TRACK_ROUTE && ir->track == track && ir->remoteChannel == rt.remoteChannel)
+ {
+ int tcompch = rt.channel;
+ if(tcompch == -1)
+ tcompch = 0;
+ int tcompchs = rt.channels;
+ if(tcompchs == -1)
+ tcompchs = isOutput ? t->channels() : track->channels();
+
+ int compch = ir->channel;
+ if(compch == -1)
+ compch = 0;
+ int compchs = ir->channels;
+ if(compchs == -1)
+ compchs = isOutput ? t->channels() : ir->track->channels();
+
+ if(compch == tcompch && compchs == tcompchs)
+ {
+ act->setChecked(true);
+ break;
+ }
+ }
+ }
+ ++id;
+ }
+ }
+
+ lb->addMenu(chpup);
+ }
+ }
+ return id;
+}
+
+//---------------------------------------------------------
+// addMidiPorts
+//---------------------------------------------------------
+
+int RoutePopupMenu::addMidiPorts(AudioTrack* t, PopupMenu* pup, int id, bool isOutput)
+{
+ QAction* act;
+ for(int i = 0; i < MIDI_PORTS; ++i)
+ {
+ MidiPort* mp = &midiPorts[i];
+ MidiDevice* md = mp->device();
+
+ // This is desirable, but could lead to 'hidden' routes unless we add more support
+ // such as removing the existing routes when user changes flags.
+ // So for now, just list all valid ports whether read or write.
+ if(!md)
+ continue;
+ //if(!(md->rwFlags() & (isOutput ? 1 : 2)))
+ // continue;
+
+ // Do not list synth devices!
+ if(md->isSynti())
+ continue;
+
+ RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes();
+
+ PopupMenu* subp = new PopupMenu(pup, true);
+ subp->setTitle(md->name());
+
+ int chanmask = 0;
+ // To reduce number of routes required, from one per channel to just one containing a channel mask.
+ // Look for the first route to this midi port. There should always be only a single route for each midi port, now.
+ for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir)
+ {
+ if(ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == i)
+ {
+ // We have a route to the midi port. Grab the channel mask.
+ chanmask = ir->channel;
+ break;
+ }
+ }
+
+ for(int ch = 0; ch < MIDI_CHANNELS; ++ch)
+ {
+ act = subp->addAction(QString("Channel %1").arg(ch+1));
+ act->setCheckable(true);
+
+ int chbit = 1 << ch;
+ Route srcRoute(i, chbit); // In accordance with channel mask, use the bit position.
+
+ act->setData(qVariantFromValue(srcRoute));
+
+ if(chanmask & chbit) // Is the channel already set? Show item check mark.
+ act->setChecked(true);
+
+ ++id;
+ }
+
+ //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id.
+ act = subp->addAction(QString("Toggle all"));
+ //act->setCheckable(true);
+ Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits.
+ act->setData(qVariantFromValue(togRoute));
+ ++id;
+
+ pup->addMenu(subp);
+ }
+ return id;
+}
+
+
+//======================
+// RoutePopupMenu
+//======================
+
+RoutePopupMenu::RoutePopupMenu(QWidget* parent, Track* track, bool isOutput)
+ : _track(track), _isOutMenu(isOutput)
+{
+ _pup = new PopupMenu(parent, true);
+ init();
+}
+
+RoutePopupMenu::RoutePopupMenu(const QString& title, QWidget* parent, Track* track, bool isOutput)
+ : _track(track), _isOutMenu(isOutput)
+{
+ _pup = new PopupMenu(title, parent, true);
+ init();
+}
+
+RoutePopupMenu::~RoutePopupMenu()
+{
+ //printf("RoutePopupMenu::~RoutePopupMenu\n");
+ // Make sure to clear which clears and deletes any sub popups.
+ _pup->clear();
+ delete _pup;
+}
+
+void RoutePopupMenu::init()
+{
+ connect(song, SIGNAL(songChanged(int)), SLOT(songChanged(int)));
+}
+
+void RoutePopupMenu::songChanged(int val)
+{
+ if(val & (SC_ROUTE | SC_CHANNELS | SC_CONFIG))
+ updateRouteMenus();
+}
+
+void RoutePopupMenu::updateRouteMenus()
+{
+ // NOTE: The purpose of this routine is to make sure the items actually reflect
+ // the routing status.
+ // In case for some reason a route could not be added (or removed).
+ // Then the item will be properly un-checked (or checked) here.
+
+ //printf("RoutePopupMenu::updateRouteMenus\n");
+
+ if(!_track || !_pup || _pup->actions().isEmpty() || !_pup->isVisible())
+ return;
+
+ RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes();
+
+ // Clear all the action check marks.
+ _pup->clearAllChecks();
+
+ // Take care of Midi Port to Audio Input routes first...
+ if(_isOutMenu && _track->isMidiTrack())
+ {
+ int port = ((MidiTrack*)_track)->outPort();
+ if(port >= 0 && port < MIDI_PORTS)
+ {
+ MidiPort* mp = &midiPorts[port];
+ RouteList* mprl = mp->outRoutes();
+ for (ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir)
+ {
+ if(ir->type == Route::TRACK_ROUTE && ir->track && ir->track->type() == Track::AUDIO_INPUT)
+ {
+ for(int ch = 0; ch < MIDI_CHANNELS; ++ch)
+ {
+ int chbits = 1 << ch;
+ if(ir->channel & chbits)
+ {
+ Route r(ir->track, chbits);
+ //printf("RoutePopupMenu::updateRouteMenus MidiPort to AudioInput chbits:%d\n", chbits);
+ QAction* act = _pup->findActionFromData(qVariantFromValue(r));
+ if(act)
+ act->setChecked(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Now check the ones that are found in the route list.
+ for(ciRoute irl = rl->begin(); irl != rl->end(); ++irl)
+ {
+ // Do MidiTrack to MidiPort routes...
+ if(irl->type == Route::MIDI_PORT_ROUTE)
+ {
+ //printf("RoutePopupMenu::updateRouteMenus MIDI_PORT_ROUTE\n");
+ for(int ch = 0; ch < MIDI_CHANNELS; ++ch)
+ {
+ int chbits = 1 << ch;
+ if(irl->channel & chbits)
+ {
+ Route r(irl->midiPort, chbits);
+ QAction* act = _pup->findActionFromData(qVariantFromValue(r));
+ if(act)
+ act->setChecked(true);
+ }
+ }
+ }
+ else
+ // Do all other routes...
+ {
+ //printf("RoutePopupMenu::updateRouteMenus other irl type:%d\n", irl->type);
+ QAction* act = _pup->findActionFromData(qVariantFromValue(*irl));
+ if(act)
+ act->setChecked(true);
+ }
+ }
+}
+
+void RoutePopupMenu::popupActivated(QAction* action)
+{
+ if(!action || !_track || !_pup || _pup->actions().isEmpty())
+ return;
+
+ if(_track->isMidiTrack())
+ {
+ RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes();
+
+ // Take care of Route data items first...
+ if(qVariantCanConvert<Route>(action->data()))
+ {
+ Route aRoute = action->data().value<Route>();
+
+ // Support Midi Port to Audio Input track routes.
+ if(aRoute.type == Route::TRACK_ROUTE && aRoute.track && aRoute.track->type() == Track::AUDIO_INPUT)
+ {
+ //if(gIsOutRoutingPopupMenu) // Try to avoid splitting like this.
+ {
+ int chbit = aRoute.channel;
+ int port = ((MidiTrack*)_track)->outPort();
+ if(port < 0 || port >= MIDI_PORTS)
+ return;
+
+ MidiPort* mp = &midiPorts[port];
+ //MidiDevice* md = mp->device();
+
+ // This is desirable, but could lead to 'hidden' routes unless we add more support
+ // such as removing the existing routes when user changes flags.
+ // So for now, just list all valid ports whether read or write.
+ //if(!md)
+ // return;
+ //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2)))
+ // return;
+
+ Route bRoute(port, chbit);
+
+ int chmask = 0;
+ RouteList* mprl = _isOutMenu ? mp->outRoutes() : mp->inRoutes();
+ ciRoute ir = mprl->begin();
+ for (; ir != mprl->end(); ++ir)
+ {
+ if(ir->type == Route::TRACK_ROUTE && ir->track == aRoute.track) // Is there already a route to this port?
+ {
+ chmask = ir->channel; // Grab the channel mask.
+ break;
+ }
+ }
+ if ((chmask & chbit) == chbit) // Is the channel's bit(s) set?
+ {
+ // disconnect
+ if(_isOutMenu)
+ audio->msgRemoveRoute(bRoute, aRoute);
+ else
+ audio->msgRemoveRoute(aRoute, bRoute);
+ }
+ else
+ {
+ // connect
+ if(_isOutMenu)
+ audio->msgAddRoute(bRoute, aRoute);
+ else
+ audio->msgAddRoute(aRoute, bRoute);
+ }
+
+ audio->msgUpdateSoloStates();
+ song->update(SC_ROUTE);
+
+ }
+ return;
+ }
+ else if(aRoute.type == Route::MIDI_PORT_ROUTE)
+ {
+ int chbit = aRoute.channel;
+ Route bRoute(_track, chbit);
+ int mdidx = aRoute.midiPort;
+
+ MidiPort* mp = &midiPorts[mdidx];
+ MidiDevice* md = mp->device();
+ //if(!md) // Rem. Allow connections to ports with no device.
+ // return;
+
+ //if(!(md->rwFlags() & 2))
+ //if(!(md->rwFlags() & (gIsOutRoutingPopupMenu ? 1 : 2)))
+ if(md && !(md->rwFlags() & (_isOutMenu ? 1 : 2)))
+ return;
+
+ int chmask = 0;
+ ciRoute iir = rl->begin();
+ for (; iir != rl->end(); ++iir)
+ {
+ if(iir->type == Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port?
+ {
+ chmask = iir->channel; // Grab the channel mask.
+ break;
+ }
+ }
+ if ((chmask & chbit) == chbit) // Is the channel's bit(s) set?
+ {
+ // disconnect
+ if(_isOutMenu)
+ audio->msgRemoveRoute(bRoute, aRoute);
+ else
+ audio->msgRemoveRoute(aRoute, bRoute);
+ }
+ else
+ {
+ // connect
+ if(_isOutMenu)
+ audio->msgAddRoute(bRoute, aRoute);
+ else
+ audio->msgAddRoute(aRoute, bRoute);
+ }
+
+ audio->msgUpdateSoloStates();
+ song->update(SC_ROUTE);
+ }
+ }
+ else
+ // ... now take care of integer data items.
+ if(qVariantCanConvert<int>(action->data()))
+ {
+ int n = action->data().value<int>();
+ if(!_isOutMenu && n == 0)
+ muse->configMidiPorts();
+ return;
+ }
+ }
+ else
+ {
+ AudioTrack* t = (AudioTrack*)_track;
+ RouteList* rl = _isOutMenu ? t->outRoutes() : t->inRoutes();
+
+ if(!qVariantCanConvert<Route>(action->data()))
+ return;
+
+ if(_isOutMenu)
+ {
+ Route dstRoute = action->data().value<Route>();
+ Route srcRoute(t, dstRoute.channel, dstRoute.channels);
+ srcRoute.remoteChannel = dstRoute.remoteChannel;
+
+ // check if route src->dst exists:
+ ciRoute irl = rl->begin();
+ for (; irl != rl->end(); ++irl) {
+ if (*irl == dstRoute)
+ break;
+ }
+ if (irl != rl->end()) {
+ // disconnect if route exists
+ audio->msgRemoveRoute(srcRoute, dstRoute);
+ }
+ else {
+ // connect if route does not exist
+ audio->msgAddRoute(srcRoute, dstRoute);
+ }
+ audio->msgUpdateSoloStates();
+ song->update(SC_ROUTE);
+ }
+ else
+ {
+ Route srcRoute = action->data().value<Route>();
+
+ // Support Midi Port to Audio Input routes.
+ if(_track->type() == Track::AUDIO_INPUT && srcRoute.type == Route::MIDI_PORT_ROUTE)
+ {
+ int chbit = srcRoute.channel;
+ Route dstRoute(t, chbit);
+ int mdidx = srcRoute.midiPort;
+ int chmask = 0;
+ ciRoute iir = rl->begin();
+ for (; iir != rl->end(); ++iir)
+ {
+ if(iir->type == Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port?
+ {
+ chmask = iir->channel; // Grab the channel mask.
+ break;
+ }
+ }
+
+ if ((chmask & chbit) == chbit) // Is the channel's bit(s) set?
+ {
+ //printf("routingPopupMenuActivated: removing src route ch:%d dst route ch:%d\n", srcRoute.channel, dstRoute.channel);
+ audio->msgRemoveRoute(srcRoute, dstRoute);
+ }
+ else
+ {
+ //printf("routingPopupMenuActivated: adding src route ch:%d dst route ch:%d\n", srcRoute.channel, dstRoute.channel);
+ audio->msgAddRoute(srcRoute, dstRoute);
+ }
+
+ audio->msgUpdateSoloStates();
+ song->update(SC_ROUTE);
+ return;
+ }
+
+ Route dstRoute(t, srcRoute.channel, srcRoute.channels);
+ dstRoute.remoteChannel = srcRoute.remoteChannel;
+
+ ciRoute irl = rl->begin();
+ for (; irl != rl->end(); ++irl) {
+ if (*irl == srcRoute)
+ break;
+ }
+ if (irl != rl->end()) {
+ // disconnect
+ audio->msgRemoveRoute(srcRoute, dstRoute);
+ }
+ else {
+ // connect
+ audio->msgAddRoute(srcRoute, dstRoute);
+ }
+ audio->msgUpdateSoloStates();
+ song->update(SC_ROUTE);
+ }
+
+
+ }
+ //else
+ //{
+ //}
+}
+
+void RoutePopupMenu::prepare()
+{
+ _pup->disconnect();
+ _pup->clear();
+
+ if(!_track)
+ return;
+
+ connect(_pup, SIGNAL(triggered(QAction*)), SLOT(popupActivated(QAction*)));
+
+ if(_track->isMidiTrack())
+ {
+ RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes();
+
+ int gid = 0;
+ QAction* act = 0;
+
+ if(_isOutMenu)
+ {
+ // Support Midi Port to Audio Input track routes.
+ int port = ((MidiTrack*)_track)->outPort();
+ if(port >= 0 && port < MIDI_PORTS)
+ {
+ MidiPort* mp = &midiPorts[port];
+
+ // Do not list synth devices! Requiring valid device is desirable,
+ // but could lead to 'hidden' routes unless we add more support
+ // such as removing the existing routes when user changes flags.
+ // So for now, just list all valid ports whether read or write.
+ if(mp->device() && !mp->device()->isSynti())
+ {
+ RouteList* mprl = mp->outRoutes();
+ int chbits = 1 << ((MidiTrack*)_track)->outChannel();
+ //MidiDevice* md = mp->device();
+ //if(!md)
+ // continue;
+
+ _pup->addSeparator();
+ _pup->addAction(new MenuTitleItem(tr("Soloing chain"), _pup));
+ PopupMenu* subp = new PopupMenu(_pup, true);
+ subp->setTitle(tr("Audio returns"));
+ _pup->addMenu(subp);
+
+ InputList* al = song->inputs();
+ for (ciAudioInput i = al->begin(); i != al->end(); ++i)
+ {
+ Track* t = *i;
+ QString s(t->name());
+ act = subp->addAction(s);
+ act->setCheckable(true);
+ Route r(t, chbits);
+ act->setData(qVariantFromValue(r));
+ for(ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir)
+ {
+ if(ir->type == Route::TRACK_ROUTE && ir->track == t && (ir->channel & chbits))
+ {
+ act->setChecked(true);
+ break;
+ }
+ }
+ ++gid;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Warn if no devices available. Add an item to open midi config.
+ 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;
+
+ // 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);
+
+ int chanmask = 0;
+ // To reduce number of routes required, from one per channel to just one containing a channel mask.
+ // Look for the first route to this midi port. There should always be only a single route for each midi port, now.
+ ciRoute ir = rl->begin();
+ for( ; ir != rl->end(); ++ir)
+ {
+ if(ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == i)
+ {
+ // We have a route to the midi port. Grab the channel mask.
+ chanmask = ir->channel;
+ break;
+ }
+ }
+ // 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, true);
+ subp->setTitle(QString("%1:").arg(i+1) + (md ? md->name() : tr("<none>")));
+
+ for(int ch = 0; ch < MIDI_CHANNELS; ++ch)
+ {
+ act = subp->addAction(QString("Channel %1").arg(ch+1));
+ act->setCheckable(true);
+ int chbit = 1 << ch;
+ Route srcRoute(i, chbit); // In accordance with channel mask, use the bit position.
+ act->setData(qVariantFromValue(srcRoute));
+ if(chanmask & chbit) // Is the channel already set? Show item check mark.
+ act->setChecked(true);
+ ++gid;
+ }
+ //gid = MIDI_PORTS * MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id.
+ act = subp->addAction(tr("Toggle all"));
+ //act->setCheckable(true);
+ Route togRoute(i, (1 << MIDI_CHANNELS) - 1); // Set all channel bits.
+ act->setData(qVariantFromValue(togRoute));
+ ++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, true);
+ 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, true);
+ 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
+
+ }
+ return;
+ }
+ else
+ {
+ AudioTrack* t = (AudioTrack*)_track;
+ int channel = t->channels();
+ if(_isOutMenu)
+ {
+ RouteList* orl = t->outRoutes();
+
+ QAction* act = 0;
+ int gid = 0;
+ gid = 0;
+
+ switch(_track->type())
+ {
+ case Track::AUDIO_OUTPUT:
+ {
+ for(int i = 0; i < channel; ++i)
+ {
+ char buffer[128];
+ snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), i+1);
+ MenuTitleItem* titel = new MenuTitleItem(QString(buffer), _pup);
+ _pup->addAction(titel);
+
+ if(!checkAudioDevice())
+ {
+ _pup->clear();
+ return;
+ }
+ std::list<QString> ol = audioDevice->inputPorts();
+ for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip)
+ {
+ act = _pup->addAction(*ip);
+ act->setCheckable(true);
+
+ Route dst(*ip, true, i, Route::JACK_ROUTE);
+ act->setData(qVariantFromValue(dst));
+ ++gid;
+ for(ciRoute ir = orl->begin(); ir != orl->end(); ++ir)
+ {
+ if(*ir == dst)
+ {
+ act->setChecked(true);
+ break;
+ }
+ }
+ }
+ if(i+1 != channel)
+ _pup->addSeparator();
+ }
+
+ //
+ // Display using separate menu for audio inputs:
+ //
+ _pup->addSeparator();
+ _pup->addAction(new MenuTitleItem(tr("Soloing chain"), _pup));
+ PopupMenu* subp = new PopupMenu(_pup, true);
+ subp->setTitle(tr("Audio returns"));
+ _pup->addMenu(subp);
+ gid = addInPorts(t, subp, gid, -1, -1, true);
+ //
+ // Display all in the same menu:
+ //
+ //_pup->addSeparator();
+ //MenuTitleItem* title = new MenuTitleItem(tr("Audio returns"), _pup);
+ //_pup->addAction(title);
+ //gid = addInPorts(t, _pup, gid, -1, -1, true);
+ }
+ break;
+ case Track::AUDIO_SOFTSYNTH:
+ gid = addMultiChannelPorts(t, _pup, gid, true);
+ break;
+
+ case Track::AUDIO_INPUT:
+ case Track::WAVE:
+ case Track::AUDIO_GROUP:
+ case Track::AUDIO_AUX:
+ gid = addWavePorts( t, _pup, gid, -1, -1, true);
+ gid = addOutPorts( t, _pup, gid, -1, -1, true);
+ gid = addGroupPorts( t, _pup, gid, -1, -1, true);
+ gid = nonSyntiTrackAddSyntis(t, _pup, gid, true);
+ break;
+ default:
+ _pup->clear();
+ return;
+ }
+ }
+ else
+ {
+ if(_track->type() == Track::AUDIO_AUX)
+ return;
+
+ RouteList* irl = t->inRoutes();
+
+ QAction* act = 0;
+ int gid = 0;
+ gid = 0;
+
+ switch(_track->type())
+ {
+ case Track::AUDIO_INPUT:
+ {
+ for(int i = 0; i < channel; ++i)
+ {
+ char buffer[128];
+ snprintf(buffer, 128, "%s %d", tr("Channel").toLatin1().constData(), i+1);
+ MenuTitleItem* titel = new MenuTitleItem(QString(buffer), _pup);
+ _pup->addAction(titel);
+
+ if(!checkAudioDevice())
+ {
+ _pup->clear();
+ return;
+ }
+ std::list<QString> ol = audioDevice->outputPorts();
+ for(std::list<QString>::iterator ip = ol.begin(); ip != ol.end(); ++ip)
+ {
+ act = _pup->addAction(*ip);
+ act->setCheckable(true);
+
+ Route dst(*ip, true, i, Route::JACK_ROUTE);
+ act->setData(qVariantFromValue(dst));
+ ++gid;
+ for(ciRoute ir = irl->begin(); ir != irl->end(); ++ir)
+ {
+ if(*ir == dst)
+ {
+ act->setChecked(true);
+ break;
+ }
+ }
+ }
+ if(i+1 != channel)
+ _pup->addSeparator();
+ }
+
+ //
+ // Display using separate menus for midi ports and audio outputs:
+ //
+ _pup->addSeparator();
+ _pup->addAction(new MenuTitleItem(tr("Soloing chain"), _pup));
+ PopupMenu* subp = new PopupMenu(_pup, true);
+ subp->setTitle(tr("Audio sends"));
+ _pup->addMenu(subp);
+ gid = addOutPorts(t, subp, gid, -1, -1, false);
+ subp = new PopupMenu(_pup, true);
+ subp->setTitle(tr("Midi port sends"));
+ _pup->addMenu(subp);
+ addMidiPorts(t, subp, gid, false);
+ //
+ // Display all in the same menu:
+ //
+ //_pup->addAction(new MenuTitleItem(tr("Audio sends"), _pup));
+ //gid = addOutPorts(t, _pup, gid, -1, -1, false);
+ //_pup->addSeparator();
+ //_pup->addAction(new MenuTitleItem(tr("Midi sends"), _pup));
+ //addMidiPorts(t, _pup, gid, false);
+ }
+ break;
+ case Track::AUDIO_OUTPUT:
+ gid = addWavePorts( t, _pup, gid, -1, -1, false);
+ gid = addInPorts( t, _pup, gid, -1, -1, false);
+ gid = addGroupPorts(t, _pup, gid, -1, -1, false);
+ gid = addAuxPorts( t, _pup, gid, -1, -1, false);
+ gid = nonSyntiTrackAddSyntis(t, _pup, gid, false);
+ break;
+ case Track::WAVE:
+ gid = addWavePorts( t, _pup, gid, -1, -1, false);
+ gid = addInPorts( t, _pup, gid, -1, -1, false);
+ gid = addGroupPorts(t, _pup, gid, -1, -1, false);
+ gid = addAuxPorts( t, _pup, gid, -1, -1, false);
+ gid = nonSyntiTrackAddSyntis(t, _pup, gid, false);
+ break;
+ case Track::AUDIO_GROUP:
+ gid = addWavePorts( t, _pup, gid, -1, -1, false);
+ gid = addInPorts( t, _pup, gid, -1, -1, false);
+ gid = addGroupPorts(t, _pup, gid, -1, -1, false);
+ gid = addAuxPorts( t, _pup, gid, -1, -1, false);
+ gid = nonSyntiTrackAddSyntis(t, _pup, gid, false);
+ break;
+
+ case Track::AUDIO_SOFTSYNTH:
+ gid = addMultiChannelPorts(t, _pup, gid, false);
+ break;
+ default:
+ _pup->clear();
+ return;
+ }
+ }
+ }
+}
+
+void RoutePopupMenu::exec(Track* track, bool isOutput)
+{
+ if(track)
+ {
+ _track = track;
+ _isOutMenu = isOutput;
+ }
+ prepare();
+ _pup->exec();
+}
+
+void RoutePopupMenu::exec(const QPoint& p, Track* track, bool isOutput)
+{
+ if(track)
+ {
+ _track = track;
+ _isOutMenu = isOutput;
+ }
+ prepare();
+ _pup->exec(p);
+}
+
+void RoutePopupMenu::popup(const QPoint& p, Track* track, bool isOutput)
+{
+ if(track)
+ {
+ _track = track;
+ _isOutMenu = isOutput;
+ }
+ prepare();
+ _pup->popup(p);
+}
+