//========================================================= // MusE // Linux Music Editor // $Id: confmport.cpp,v 1.9.2.10 2009/12/15 03:39:58 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; version 2 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include #include #include #include #include #include #include #include #include "confmport.h" #include "app.h" #include "icons.h" #include "globals.h" #include "arranger.h" #include "midiport.h" #include "mididev.h" #include "xml.h" #include "midisyncimpl.h" #include "midifilterimpl.h" #include "ctrlcombo.h" #include "minstrument.h" #include "synth.h" #include "audio.h" #include "midiseq.h" #include "driver/alsamidi.h" #include "driver/jackmidi.h" #include "audiodev.h" #include "menutitleitem.h" #include "utils.h" #include "popupmenu.h" namespace MusEGlobal { extern std::vector synthis; } namespace MusEGui { enum { DEVCOL_NO = 0, DEVCOL_GUI, DEVCOL_REC, DEVCOL_PLAY, DEVCOL_INSTR, DEVCOL_NAME, DEVCOL_INROUTES, DEVCOL_OUTROUTES, DEVCOL_DEF_IN_CHANS, DEVCOL_DEF_OUT_CHANS, DEVCOL_STATE }; //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- 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 //--------------------------------------------------------- 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 = MusEGlobal::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(!MusEGlobal::song->midis()->empty()) // && MusEGlobal::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) { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { // Remove all routes from this port to the tracks first. MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); if(defch) MusEGlobal::audio->msgAddRoute(MusECore::Route(no, defch), MusECore::Route(*it, defch)); } MusEGlobal::song->update(SC_ROUTE); } } } 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); MusEGlobal::midiPorts[no].setDefaultInChannels(chbits); mdevView->item(item->row(), DEVCOL_DEF_IN_CHANS)->setText(MusECore::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 = MusEGlobal::midiPorts[no].defaultOutChannels(); // Turn on if and when multiple output routes are supported. DELETETHIS?? #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(!MusEGlobal::song->midis()->empty()) // && MusEGlobal::midiPorts[no].device()) { // Turn off if and when multiple output routes are supported. #if 1 if(!defch) // No channels selected? Just return. return; #endif 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) { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); // Turn on if and when multiple output routes are supported. DELETETHIS?? #if 0 for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { // Remove all routes from this port to the tracks first. MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); if(defch) MusEGlobal::audio->msgAddRoute(MusECore::Route(no, defch), MusECore::Route(*it, defch)); } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); #else for(int ch = 0; ch < MIDI_CHANNELS; ++ch) if(defch & (1 << ch)) { MusEGlobal::audio->msgIdle(true); for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { // Leave drum track channel at current setting. if((*it)->type() == MusECore::Track::DRUM) (*it)->setOutPortAndUpdate(no); else (*it)->setOutPortAndChannelAndUpdate(no, ch); } MusEGlobal::audio->msgIdle(false); MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_MIDI_TRACK_PROP); // Stop at the first output channel found. break; } #endif } } } else { #if 0 // Turn on if and when multiple output routes are supported. DELETETHIS?? int chbits; if(actid == MIDI_CHANNELS) // Toggle all. { chbits = (defch == allch) ? 0 : allch; if(defpup) for(int i = 0; i < MIDI_CHANNELS; ++i) { QAction* act = defpup->findActionFromData(i); if(act) act->setChecked(chbits); } } else chbits = defch ^ (1 << actid); MusEGlobal::midiPorts[no].setDefaultOutChannels(chbits); mdevView->item(item->row(), DEVCOL_DEF_OUT_CHANS)->setText(MusECore::bitmap2String(chbits)); #else if(actid < MIDI_CHANNELS) { int chbits = 1 << actid; // Are we toggling off? if(chbits & defch) { // Just clear this port's default channels. MusEGlobal::midiPorts[no].setDefaultOutChannels(0); mdevView->item(item->row(), DEVCOL_DEF_OUT_CHANS)->setText(MusECore::bitmap2String(0)); } else { // Multiple out routes not supported. Make the setting exclusive to this port - exclude all other ports. MusECore::setPortExclusiveDefOutChan(no, chbits); int j = mdevView->rowCount(); for(int i = 0; i < j; ++i) mdevView->item(i, DEVCOL_DEF_OUT_CHANS)->setText(MusECore::bitmap2String(i == no ? chbits : 0)); if(defpup) { QAction* a; for(int i = 0; i < MIDI_CHANNELS; ++i) { a = defpup->findActionFromData(i); if(a) a->setChecked(i == actid); } } } } #endif } } //--------------------------------------------------------- // mdevViewItemRenamed //--------------------------------------------------------- void MPConfig::mdevViewItemRenamed(QTableWidgetItem* item) { int col = item->column(); QString s = item->text(); if(item == 0) return; switch(col) { // Enabled: Use editor (Not good - only responds if text changed. We need to respond always). // Disabled: Use pop-up menu. DELETETHIS? #if 0 case DEVCOL_DEF_IN_CHANS: { QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if(no < 0 || no >= MIDI_PORTS) return; int allch = (1 << MIDI_CHANNELS) - 1; int ch = allch & string2bitmap(s); MusEGlobal::midiPorts[no].setDefaultInChannels(ch); if(!MusEGlobal::song->midis()->empty() && MusEGlobal::midiPorts[no].device()) // Only if there are tracks, and device is valid. { int ret = QMessageBox::question(this, tr("Default input connections"), tr("Setting will apply to new midi tracks.\n" "Do you want to apply to all existing midi tracks now?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if(ret == QMessageBox::Yes) { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { // Remove all routes from this port to the tracks first. MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); if(ch) MusEGlobal::audio->msgAddRoute(MusECore::Route(no, ch), MusECore::Route(*it, ch)); } } } MusEGlobal::song->update(); } break; #endif // Enabled: Use editor (Not good - only responds if text changed. We need to respond always). // Disabled: Use pop-up menu. // Only turn on if and when multiple output routes are supported. DELETETHIS? #if 0 case DEVCOL_DEF_OUT_CHANS: { QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if(no < 0 || no >= MIDI_PORTS) return; int allch = (1 << MIDI_CHANNELS) - 1; int ch = allch & string2bitmap(s); MusEGlobal::midiPorts[no].setDefaultOutChannels(ch); if(!MusEGlobal::song->midis()->empty() && MusEGlobal::midiPorts[no].device()) // Only if there are tracks, and device is valid. { int ret = QMessageBox::question(this, tr("Default output connections"), tr("Setting will apply to new midi tracks.\n" "Do you want to apply to all existing midi tracks now?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if(ret == QMessageBox::Yes) { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { // Remove all routes from the tracks to this port first. MusEGlobal::audio->msgRemoveRoute(MusECore::Route(*it, allch), MusECore::Route(no, allch)); if(ch) MusEGlobal::audio->msgAddRoute(MusECore::Route(*it, ch), MusECore::Route(no, ch)); } } } MusEGlobal::song->update(); } break; # endif case DEVCOL_NAME: { QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if(no < 0 || no >= MIDI_PORTS) return; MusECore::MidiPort* port = &MusEGlobal::midiPorts[no]; MusECore::MidiDevice* dev = port->device(); // Only Jack midi devices. if(!dev || dev->deviceType() != MusECore::MidiDevice::JACK_MIDI) return; if(dev->name() == s) return; if(MusEGlobal::midiDevices.find(s)) { QMessageBox::critical(this, tr("MusE: bad device name"), tr("please choose a unique device name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); songChanged(-1); return; } dev->setName(s); MusEGlobal::song->update(); } break; default: break; } } //--------------------------------------------------------- // rbClicked //--------------------------------------------------------- void MPConfig::rbClicked(QTableWidgetItem* item) { if (item == 0) return; QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if (no < 0 || no >= MIDI_PORTS) return; int n; MusECore::MidiPort* port = &MusEGlobal::midiPorts[no]; MusECore::MidiDevice* dev = port->device(); int rwFlags = dev ? dev->rwFlags() : 0; int openFlags = dev ? dev->openFlags() : 0; QTableWidget* listView = item->tableWidget(); QPoint ppt = listView->visualItemRect(item).bottomLeft(); QPoint mousepos = QCursor::pos(); int col = item->column(); ppt += QPoint(0, listView->horizontalHeader()->height()); ppt = listView->mapToGlobal(ppt); switch (col) { case DEVCOL_GUI: if (dev == 0) return; if (port->hasNativeGui()) { port->instrument()->showNativeGui(!port->nativeGuiVisible()); item->setIcon(port->nativeGuiVisible() ? QIcon(*dotIcon) : QIcon(*dothIcon)); } return; case DEVCOL_REC: if (dev == 0 || !(rwFlags & 2)) return; openFlags ^= 0x2; dev->setOpenFlags(openFlags); MusEGlobal::midiSeq->msgSetMidiDevice(port, dev); // reopen device item->setIcon(openFlags & 2 ? QIcon(*dotIcon) : QIcon(*dothIcon)); if(dev->deviceType() == MusECore::MidiDevice::JACK_MIDI) { if(dev->openFlags() & 2) { item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setIcon(QIcon(*buttondownIcon)); item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setText(tr("in")); } else { item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setIcon(QIcon()); item->tableWidget()->item(item->row(), DEVCOL_INROUTES)->setText(""); } } return; case DEVCOL_PLAY: if (dev == 0 || !(rwFlags & 1)) return; openFlags ^= 0x1; dev->setOpenFlags(openFlags); MusEGlobal::midiSeq->msgSetMidiDevice(port, dev); // reopen device item->setIcon(openFlags & 1 ? QIcon(*dotIcon) : QIcon(*dothIcon)); if(dev->deviceType() == MusECore::MidiDevice::JACK_MIDI) { if(dev->openFlags() & 1) { item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setIcon(QIcon(*buttondownIcon)); item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setText(tr("out")); } else { item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setIcon(QIcon()); item->tableWidget()->item(item->row(), DEVCOL_OUTROUTES)->setText(""); } } return; case DEVCOL_INROUTES: case DEVCOL_OUTROUTES: { if(!MusEGlobal::checkAudioDevice()) return; if(MusEGlobal::audioDevice->deviceType() != MusECore::AudioDevice::JACK_AUDIO) // Only if Jack is running. return; if(!dev) return; // Only Jack midi devices. if(dev->deviceType() != MusECore::MidiDevice::JACK_MIDI) return; if(!(dev->openFlags() & ((col == DEVCOL_OUTROUTES) ? 1 : 2))) return; MusECore::RouteList* rl = (col == DEVCOL_OUTROUTES) ? dev->outRoutes() : dev->inRoutes(); QMenu* pup = 0; int gid = 0; std::list sl; pup = new QMenu(this); _redisplay: pup->clear(); gid = 0; // Jack input ports if device is writable, and jack output ports if device is readable. sl = (col == DEVCOL_OUTROUTES) ? MusEGlobal::audioDevice->inputPorts(true, _showAliases) : MusEGlobal::audioDevice->outputPorts(true, _showAliases); QAction* act; act = pup->addAction(tr("Show first aliases")); act->setData(gid); act->setCheckable(true); act->setChecked(_showAliases == 0); ++gid; act = pup->addAction(tr("Show second aliases")); act->setData(gid); act->setCheckable(true); act->setChecked(_showAliases == 1); ++gid; pup->addSeparator(); for(std::list::iterator ip = sl.begin(); ip != sl.end(); ++ip) { act = pup->addAction(*ip); act->setData(gid); act->setCheckable(true); MusECore::Route rt(*ip, (col == DEVCOL_OUTROUTES), -1, MusECore::Route::JACK_ROUTE); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if (*ir == rt) { act->setChecked(true); break; } } ++gid; } act = pup->exec(ppt); if(act) { n = act->data().toInt(); if(n == 0) // Show first aliases { if(_showAliases == 0) _showAliases = -1; else _showAliases = 0; goto _redisplay; // Go back } else if(n == 1) // Show second aliases { if(_showAliases == 1) _showAliases = -1; else _showAliases = 1; goto _redisplay; // Go back } QString s(act->text()); if(col == DEVCOL_OUTROUTES) // Writeable { MusECore::Route srcRoute(dev, -1); MusECore::Route dstRoute(s, true, -1, MusECore::Route::JACK_ROUTE); MusECore::ciRoute iir = rl->begin(); for(; iir != rl->end(); ++iir) { if(*iir == dstRoute) break; } if(iir != rl->end()) // disconnect MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); else // connect MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); } else { MusECore::Route srcRoute(s, false, -1, MusECore::Route::JACK_ROUTE); MusECore::Route dstRoute(dev, -1); MusECore::ciRoute iir = rl->begin(); for(; iir != rl->end(); ++iir) { if(*iir == srcRoute) break; } if(iir != rl->end()) // disconnect MusEGlobal::audio->msgRemoveRoute(srcRoute, dstRoute); else // connect MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); // FIXME: // Routes can't be re-read until the message sent from msgAddRoute1() // has had time to be sent and actually affected the routes. //goto _redisplay; // Go back } delete pup; } return; case DEVCOL_DEF_IN_CHANS: { defpup = new MusEGui::PopupMenu(this, true); defpup->addAction(new MusEGui::MenuTitleItem("Channel", defpup)); QAction* act = 0; int chbits = MusEGlobal::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(!MusEGlobal::song->midis()->empty()); // && MusEGlobal::midiPorts[no].device()); DELETETHIS connect(defpup, SIGNAL(triggered(QAction*)), SLOT(changeDefInputRoutes(QAction*))); // DELETETHIS 2 //connect(defpup, SIGNAL(aboutToHide()), MusEGlobal::muse, SLOT(routingPopupMenuAboutToHide())); //defpup->popup(QCursor::pos()); defpup->exec(QCursor::pos()); delete defpup; defpup = 0; } return; case DEVCOL_DEF_OUT_CHANS: { defpup = new MusEGui::PopupMenu(this, true); defpup->addAction(new MusEGui::MenuTitleItem("Channel", defpup)); QAction* act = 0; int chbits = MusEGlobal::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. DELETETHIS? #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(!MusEGlobal::song->midis()->empty()); // && MusEGlobal::midiPorts[no].device()); connect(defpup, SIGNAL(triggered(QAction*)), SLOT(changeDefOutputRoutes(QAction*))); defpup->exec(QCursor::pos()); delete defpup; defpup = 0; } return; case DEVCOL_NAME: { // Did we click in the text area? if((mousepos.x() - ppt.x()) > buttondownIcon->width()) { // Start the renaming of the cell... QModelIndex current = item->tableWidget()->currentIndex(); if (item->flags() & Qt::ItemIsEditable) item->tableWidget()->edit(current.sibling(current.row(), DEVCOL_NAME)); return; } else // We clicked the 'down' button. { QMenu* pup = new QMenu(this); QAction* act; // Could do it this way... //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" input")); //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" output")); //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" combo")); // ... or keep it simple and let the user click on the green lights instead. act = pup->addAction(tr("Create Jack device")); act->setData(0); typedef std::map asmap; typedef std::map::iterator imap; asmap mapALSA; asmap mapJACK; asmap mapSYNTH; int aix = 0x10000000; int jix = 0x20000000; int six = 0x30000000; for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { if((*i)->deviceType() == MusECore::MidiDevice::ALSA_MIDI) { mapALSA.insert( std::pair (std::string((*i)->name().toLatin1().constData()), aix) ); ++aix; } else if((*i)->deviceType() == MusECore::MidiDevice::JACK_MIDI) { mapJACK.insert( std::pair (std::string((*i)->name().toLatin1().constData()), jix) ); ++jix; } else if((*i)->deviceType() == MusECore::MidiDevice::SYNTH_MIDI) { mapSYNTH.insert( std::pair (std::string((*i)->name().toLatin1().constData()), six) ); ++six; } else printf("MPConfig::rbClicked unknown midi device: %s\n", (*i)->name().toLatin1().constData()); } if(!mapALSA.empty()) { pup->addSeparator(); pup->addAction(new MusEGui::MenuTitleItem("ALSA:", pup)); for(imap i = mapALSA.begin(); i != mapALSA.end(); ++i) { int idx = i->second; //if(idx > sz) // Sanity check DELETETHIS 2 // continue; QString s(i->first.c_str()); MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::ALSA_MIDI); if(md) { //if(!dynamic_cast(md)) DELETETHIS if(md->deviceType() != MusECore::MidiDevice::ALSA_MIDI) continue; act = pup->addAction(md->name()); act->setData(idx); act->setCheckable(true); act->setChecked(md == dev); } } } if(!mapSYNTH.empty()) { pup->addSeparator(); pup->addAction(new MusEGui::MenuTitleItem("SYNTH:", pup)); for(imap i = mapSYNTH.begin(); i != mapSYNTH.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::SYNTH_MIDI); if(md) { if(md->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) continue; act = pup->addAction(md->name()); act->setData(idx); act->setCheckable(true); act->setChecked(md == dev); } } } if(!mapJACK.empty()) { pup->addSeparator(); pup->addAction(new MusEGui::MenuTitleItem("JACK:", pup)); for(imap i = mapJACK.begin(); i != mapJACK.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::JACK_MIDI); if(md) { if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) continue; act = pup->addAction(md->name()); act->setData(idx); act->setCheckable(true); act->setChecked(md == dev); } } } act = pup->exec(ppt); if(!act) { delete pup; return; } n = act->data().toInt(); MusECore::MidiDevice* sdev = 0; if(n < 0x10000000) { delete pup; if(n <= 2) { sdev = MusECore::MidiJackDevice::createJackMidiDevice(); if(sdev) { int of = 3; switch(n) { case 0: of = 3; break; case 1: of = 2; break; case 2: of = 1; break; } sdev->setOpenFlags(of); } } } else { int typ; if(n < 0x20000000) typ = MusECore::MidiDevice::ALSA_MIDI; else if(n < 0x30000000) typ = MusECore::MidiDevice::JACK_MIDI; else //if(n < 0x40000000) typ = MusECore::MidiDevice::SYNTH_MIDI; sdev = MusEGlobal::midiDevices.find(act->text(), typ); delete pup; // Is it the current device? Reset it to . if(sdev == dev) sdev = 0; } int allch = (1 << MIDI_CHANNELS) - 1; MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); // Remove track routes to/from an existing port already using the selected device... if(sdev) { for(int i = 0; i < MIDI_PORTS; ++i) { if(MusEGlobal::midiPorts[i].device() == sdev) { for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) MusEGlobal::audio->msgRemoveRoute(MusECore::Route(i, allch), MusECore::Route(*it, allch)); // Turn on if and when multiple output routes are supported. DELETETHIS? #if 0 for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); #endif break; } } } // Remove all track routes to/from this port... for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) // Remove all routes from this port to the tracks. MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); // Turn on if and when multiple output routes are supported. DELETETHIS? #if 0 for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); #endif MusEGlobal::midiSeq->msgSetMidiDevice(port, sdev); MusEGlobal::muse->changeConfig(true); // save configuration file // Add all track routes to/from this port... if(sdev) { int chbits = MusEGlobal::midiPorts[no].defaultInChannels(); // Do not add input routes to synths. if(!sdev->isSynti()) { for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { // Connect all the specified routes. if(chbits) MusEGlobal::audio->msgAddRoute(MusECore::Route(no, chbits), MusECore::Route(*it, chbits)); } } // chbits = MusEGlobal::midiPorts[no].defaultOutChannels(); // Turn on if and when multiple output routes are supported. DELETETHIS? #if 0 for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { // Connect all the specified routes. if(chbits) MusEGlobal::audio->msgAddRoute(MusECore::Route(no, chbits), MusECore::Route(*it, chbits)); } #else // REMOVE Tim. // for(int ch = 0; ch < MIDI_CHANNELS; ++ch) // if(chbits & (1 << ch)) // { // MusEGlobal::audio->msgIdle(true); // for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) // { // // We are only interested in tracks which use this port being changed now. // if((*it)->outPort() != no) // continue; // // Leave drum track channel at current setting. // REMOVE Tim. // //if((*it)->type() == MusECore::Track::DRUM) // // (*it)->setOutPortAndUpdate(no); // //else // // (*it)->setOutPortAndChannelAndUpdate(no, ch); // (*it)->setOutPortAndUpdate(no); // } // MusEGlobal::audio->msgIdle(false); // // Stop at the first output channel found. // break; // } #endif } MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(); } } return; case DEVCOL_INSTR: { if (dev && dev->isSynti()) return; if (instrPopup == 0) instrPopup = new QMenu(this); instrPopup->clear(); for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { // By T356. // Do not list synths. Although it is possible to assign a synth // as an instrument to a non-synth device, we should not allow this. // (One reason is that the 'show gui' column is then enabled, which // makes no sense for a non-synth device). MusECore::SynthI* si = dynamic_cast(*i); if(!si) instrPopup->addAction((*i)->iname()); } QAction* act = instrPopup->exec(ppt); if(!act) return; QString s = act->text(); item->tableWidget()->item(item->row(), DEVCOL_INSTR)->setText(s); for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { if ((*i)->iname() == s) { port->setInstrument(*i); break; } } MusEGlobal::song->update(); } return; } } //--------------------------------------------------------- // MPConfig::setToolTip //--------------------------------------------------------- void MPConfig::setToolTip(QTableWidgetItem *item, int col) { switch (col) { case DEVCOL_NO: item->setToolTip(tr("Port Number")); break; case DEVCOL_GUI: item->setToolTip(tr("Enable gui")); break; case DEVCOL_REC: item->setToolTip(tr("Enable reading")); break; case DEVCOL_PLAY: item->setToolTip(tr("Enable writing")); break; case DEVCOL_INSTR: item->setToolTip(tr("Port instrument")); break; case DEVCOL_NAME: item->setToolTip(tr("Midi device name. Click to edit (Jack)")); break; case DEVCOL_INROUTES: item->setToolTip(tr("Connections from Jack Midi outputs")); break; case DEVCOL_OUTROUTES: item->setToolTip(tr("Connections to Jack Midi inputs")); break; case DEVCOL_DEF_IN_CHANS: item->setToolTip(tr("Auto-connect these channels to new midi tracks")); break; // Turn on if and when multiple output routes are supported. DELETETHIS? #if 0 case DEVCOL_DEF_OUT_CHANS: item->setToolTip(tr("Auto-connect new midi tracks to these channels")); break; #else 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; } } //--------------------------------------------------------- // MPConfig::setWhatsThis //--------------------------------------------------------- void MPConfig::setWhatsThis(QTableWidgetItem *item, int col) { switch (col) { case DEVCOL_NO: item->setWhatsThis(tr("Port Number")); break; case DEVCOL_GUI: item->setWhatsThis(tr("Enable gui for device")); break; case DEVCOL_REC: item->setWhatsThis(tr("Enable reading from device")); break; case DEVCOL_PLAY: item->setWhatsThis(tr("Enable writing to device")); break; case DEVCOL_NAME: item->setWhatsThis(tr("Name of the midi device associated with" " this port number. Click to edit Jack midi name.")); break; case DEVCOL_INSTR: item->setWhatsThis(tr("Instrument connected to port")); break; case DEVCOL_INROUTES: item->setWhatsThis(tr("Connections from Jack Midi output ports")); break; case DEVCOL_OUTROUTES: item->setWhatsThis(tr("Connections to Jack Midi input ports")); break; case DEVCOL_DEF_IN_CHANS: item->setWhatsThis(tr("Auto-connect these channels, on this port, to new midi tracks.")); break; case DEVCOL_DEF_OUT_CHANS: // Turn on if and when multiple output routes are supported. DELETETHIS? #if 0 item->setWhatsThis(tr("Connect new midi tracks to these channels, on this port.")); break; #else 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: break; } } //--------------------------------------------------------- // MPConfig::addItem() //--------------------------------------------------------- void MPConfig::addItem(int row, int col, QTableWidgetItem *item, QTableWidget *table) { setWhatsThis(item, col); table->setItem(row, col, item); } //--------------------------------------------------------- // MPConfig // Midi Port Config //--------------------------------------------------------- MPConfig::MPConfig(QWidget* parent) : QDialog(parent) { setupUi(this); QSettings settings("MusE", "MusE-qt"); restoreGeometry(settings.value("MPConfig/geometry").toByteArray()); mdevView->setRowCount(MIDI_PORTS); mdevView->verticalHeader()->hide(); mdevView->setSelectionMode(QAbstractItemView::SingleSelection); mdevView->setShowGrid(false); instrPopup = 0; defpup = 0; _showAliases = 1; // 0: Show second aliases, if available. QStringList columnnames; columnnames << tr("Port") << tr("GUI") << tr("I") << tr("O") << tr("Instrument") << tr("Device Name") << tr("In routes") << tr("Out routes") << tr("Def in ch") << tr("Def out ch") << tr("State"); mdevView->setColumnCount(columnnames.size()); mdevView->setHorizontalHeaderLabels(columnnames); for (int i = 0; i < columnnames.size(); ++i) { setWhatsThis(mdevView->horizontalHeaderItem(i), i); setToolTip(mdevView->horizontalHeaderItem(i), i); } mdevView->setFocusPolicy(Qt::NoFocus); connect(mdevView, SIGNAL(itemPressed(QTableWidgetItem*)), this, SLOT(rbClicked(QTableWidgetItem*))); connect(mdevView, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(mdevViewItemRenamed(QTableWidgetItem*))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedFlags_t)), SLOT(songChanged(MusECore::SongChangedFlags_t))); connect(synthList, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); connect(instanceList, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); connect(addInstance, SIGNAL(clicked()), SLOT(addInstanceClicked())); connect(synthList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(addInstanceClicked())); connect(removeInstance, SIGNAL(clicked()), SLOT(removeInstanceClicked())); connect(instanceList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(removeInstanceClicked())); connect(applyButton, SIGNAL(clicked()), SLOT(apply())); connect(okButton, SIGNAL(clicked()), SLOT(okClicked())); songChanged(SC_CONFIG); } MPConfig::~MPConfig() { } //--------------------------------------------------------- // selectionChanged //--------------------------------------------------------- void MPConfig::selectionChanged() { addInstance->setEnabled(synthList->currentItem()); removeInstance->setEnabled(instanceList->currentItem()); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MPConfig::songChanged(MusECore::SongChangedFlags_t flags) { if(!(flags & (SC_CONFIG | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED))) return; // Get currently selected index... int no = -1; QTableWidgetItem* sitem = mdevView->currentItem(); if(sitem) { QString id = sitem->tableWidget()->item(sitem->row(), DEVCOL_NO)->text(); no = atoi(id.toLatin1().constData()) - 1; if(no < 0 || no >= MIDI_PORTS) no = -1; } 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. MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* dev = port->device(); QString s; s.setNum(i+1); QTableWidgetItem* itemno = new QTableWidgetItem(s); addItem(i, DEVCOL_NO, itemno, mdevView); itemno->setTextAlignment(Qt::AlignHCenter); itemno->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemstate = new QTableWidgetItem(port->state()); addItem(i, DEVCOL_STATE, itemstate, mdevView); itemstate->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* iteminstr = new QTableWidgetItem(port->instrument() ? port->instrument()->iname() : tr("")); addItem(i, DEVCOL_INSTR, iteminstr, mdevView); iteminstr->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemname = new QTableWidgetItem; addItem(i, DEVCOL_NAME, itemname, mdevView); itemname->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemgui = new QTableWidgetItem; addItem(i, DEVCOL_GUI, itemgui, mdevView); itemgui->setTextAlignment(Qt::AlignHCenter); itemgui->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemrec = new QTableWidgetItem; addItem(i, DEVCOL_REC, itemrec, mdevView); itemrec->setTextAlignment(Qt::AlignHCenter); itemrec->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemplay = new QTableWidgetItem; addItem(i, DEVCOL_PLAY, itemplay, mdevView); itemplay->setTextAlignment(Qt::AlignHCenter); itemplay->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemout = new QTableWidgetItem; addItem(i, DEVCOL_OUTROUTES, itemout, mdevView); itemout->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemin = new QTableWidgetItem; addItem(i, DEVCOL_INROUTES, itemin, mdevView); itemin->setFlags(Qt::ItemIsEnabled); // Ignore synth devices. Default input routes make no sense for them (right now). QTableWidgetItem* itemdefin = new QTableWidgetItem((dev && dev->isSynti()) ? QString() : MusECore::bitmap2String(port->defaultInChannels())); addItem(i, DEVCOL_DEF_IN_CHANS, itemdefin, mdevView); // Enabled: Use editor (not good). Disabled: Use pop-up menu. DELETETHIS #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. DELETETHIS? #if 0 QTableWidgetItem* itemdefout = new QTableWidgetItem(MusECore::bitmap2String(port->defaultOutChannels())); addItem(i, DEVCOL_DEF_OUT_CHANS, itemdefout, mdevView); itemdefout->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled); #else QTableWidgetItem* itemdefout = new QTableWidgetItem(MusECore::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()); // Is it a Jack midi device? Allow renaming. if (dev->deviceType() == MusECore::MidiDevice::JACK_MIDI) itemname->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled); if (dev->rwFlags() & 0x2) itemrec->setIcon(dev->openFlags() & 2 ? QIcon(*dotIcon) : QIcon(*dothIcon)); else itemrec->setIcon(QIcon(QPixmap())); if (dev->rwFlags() & 0x1) itemplay->setIcon( dev->openFlags() & 1 ? QIcon(*dotIcon) : QIcon(*dothIcon)); else itemplay->setIcon(QIcon(QPixmap())); } else { itemname->setText(tr("")); itemgui->setIcon(QIcon(*dothIcon)); itemrec->setIcon(QIcon(QPixmap())); itemplay->setIcon(QIcon(QPixmap())); } if (port->hasNativeGui()) itemgui->setIcon(port->nativeGuiVisible() ? QIcon(*dotIcon) : QIcon(*dothIcon)); else itemgui->setIcon(QIcon(QPixmap())); if (!(dev && dev->isSynti())) iteminstr->setIcon(QIcon(*buttondownIcon)); itemname->setIcon(QIcon(*buttondownIcon)); if(dev && dev->deviceType() == MusECore::MidiDevice::JACK_MIDI) { if(dev->rwFlags() & 1) { if(dev->openFlags() & 1) { itemout->setIcon(QIcon(*buttondownIcon)); itemout->setText(tr("out")); } } if(dev->rwFlags() & 2) { if(dev->openFlags() & 2) { itemin->setIcon(QIcon(*buttondownIcon)); itemin->setText(tr("in")); } } } if(i == no) sitem = itemno; } if(sitem) mdevView->setCurrentItem(sitem); QString s; synthList->clear(); for (std::vector::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) { QTreeWidgetItem* item = new QTreeWidgetItem(synthList); item->setText(0, QString((*i)->baseName())); item->setText(1, MusECore::synthType2String((*i)->synthType())); s.setNum((*i)->instances()); item->setText(2, s); item->setText(3, QString((*i)->name())); item->setText(4, QString((*i)->version())); item->setText(5, QString((*i)->description())); } instanceList->clear(); MusECore::SynthIList* sl = MusEGlobal::song->syntis(); for (MusECore::iSynthI si = sl->begin(); si != sl->end(); ++si) { QTreeWidgetItem* iitem = new QTreeWidgetItem(instanceList); iitem->setText(0, (*si)->name()); iitem->setText(1, MusECore::synthType2String((*si)->synth()->synthType())); if ((*si)->midiPort() == -1) s = tr(""); else s.setNum((*si)->midiPort() + 1); iitem->setText(2, s); } synthList->resizeColumnToContents(1); mdevView->resizeColumnsToContents(); mdevView->horizontalHeader()->setResizeMode(DEVCOL_NO ,QHeaderView::Fixed); mdevView->horizontalHeader()->setResizeMode(DEVCOL_REC ,QHeaderView::Fixed); mdevView->horizontalHeader()->setResizeMode(DEVCOL_PLAY ,QHeaderView::Fixed); mdevView->horizontalHeader()->setResizeMode(DEVCOL_GUI ,QHeaderView::Fixed); mdevView->horizontalHeader()->setStretchLastSection( true ); selectionChanged(); } //--------------------------------------------------------- // addInstanceClicked //--------------------------------------------------------- void MPConfig::addInstanceClicked() { QTreeWidgetItem* item = synthList->currentItem(); if (item == 0) return; // Add at end of list. MusECore::SynthI *si = MusEGlobal::song->createSynthI(item->text(0), item->text(3), MusECore::string2SynthType(item->text(1))); if(!si) return; // add instance last in midi device list for (int i = 0; i < MIDI_PORTS; ++i) { MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* dev = port->device(); if (dev==0) { MusEGlobal::midiSeq->msgSetMidiDevice(port, si); MusEGlobal::muse->changeConfig(true); // save configuration file MusEGlobal::song->update(); break; } } } //--------------------------------------------------------- // removeInstanceClicked //--------------------------------------------------------- void MPConfig::removeInstanceClicked() { QTreeWidgetItem* item = instanceList->currentItem(); if (item == 0) return; MusECore::SynthIList* sl = MusEGlobal::song->syntis(); MusECore::iSynthI ii; for (ii = sl->begin(); ii != sl->end(); ++ii) { if( (*ii)->iname() == item->text(0) && MusECore::synthType2String((*ii)->synth()->synthType()) == item->text(1) ) break; } if (ii == sl->end()) { printf("synthesizerConfig::removeInstanceClicked(): synthi not found\n"); return; } MusEGlobal::audio->msgRemoveTrack(*ii); } //--------------------------------------------------------- // configMidiPorts //--------------------------------------------------------- void MusE::configMidiPorts() { if (!midiPortConfig) { midiPortConfig = new MusEGui::MPConfig(this); } midiPortConfig->show(); midiPortConfig->raise(); midiPortConfig->activateWindow(); } } // namespace MusEGui