diff options
author | Florian Jung <flo@windfisch.org> | 2011-12-14 15:08:02 +0000 |
---|---|---|
committer | Florian Jung <flo@windfisch.org> | 2011-12-14 15:08:02 +0000 |
commit | c36a5508aa42e596b005425208054af9a60734b4 (patch) | |
tree | fde0504e0c25b8f39ed6f5f7f7332943e4a95c7f /muse2/muse | |
parent | 42126f3b398802eb24c8d9acd2591ef4dbe7257d (diff) |
pulled fixes from release into trunk
Diffstat (limited to 'muse2/muse')
103 files changed, 4275 insertions, 2448 deletions
diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index bd6bc560..148f3072 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -4,6 +4,7 @@ // $Id: app.cpp,v 1.113.2.68 2009/12/21 14:51:51 spamatica Exp $ // // (C) Copyright 1999-2011 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -33,6 +34,8 @@ #include <QProgressDialog> #include <QMdiArea> #include <QMdiSubWindow> +#include <QSocketNotifier> +#include <QString> #include <iostream> @@ -55,6 +58,7 @@ #include "filedialog.h" #include "gconfig.h" #include "gui.h" +#include "helper.h" #include "icons.h" #include "instruments/editinstrument.h" #include "listedit.h" @@ -84,8 +88,11 @@ extern void exitJackAudio(); extern void exitDummyAudio(); extern void exitOSC(); extern void exitMidiAlsa(); -} +extern void initMidiSequencer(); +extern void initAudio(); +extern void initAudioPrefetch(); +} namespace MusEGui { @@ -94,12 +101,12 @@ namespace MusEGui { static pthread_t watchdogThread; //ErrorHandler *error; - #define PROJECT_LIST_LEN 6 static QString* projectList[PROJECT_LIST_LEN]; #ifdef HAVE_LASH #include <lash/lash.h> +#include <lo/lo_osc_types.h> lash_client_t * lash_client = 0; extern snd_seq_t * alsaSeq; #endif /* HAVE_LASH */ @@ -171,14 +178,7 @@ bool MusE::seqStart() if(MusEGlobal::realTimeScheduling) { { - //pfprio = MusEGlobal::realTimePriority - 5; - // p3.3.40 pfprio = MusEGlobal::realTimePriority + 1; - - //midiprio = MusEGlobal::realTimePriority - 2; - // p3.3.37 - //midiprio = MusEGlobal::realTimePriority + 1; - // p3.3.40 midiprio = MusEGlobal::realTimePriority + 2; } } @@ -198,9 +198,6 @@ bool MusE::seqStart() MusEGlobal::audioPrefetch->msgSeek(0, true); // force - //MusEGlobal::midiSeqRunning = !midiSeq->start(MusEGlobal::realTimeScheduling ? MusEGlobal::realTimePriority : 0); - // Changed by Tim. p3.3.22 - //MusEGlobal::midiSeq->start(MusEGlobal::realTimeScheduling ? MusEGlobal::realTimePriority : 0); MusEGlobal::midiSeq->start(midiprio); int counter=0; @@ -224,7 +221,7 @@ bool MusE::seqStart() } return true; } - + //--------------------------------------------------------- // stop //--------------------------------------------------------- @@ -258,6 +255,7 @@ bool MusE::seqRestart() } seqStop(); } + if(!seqStart()) return false; @@ -299,7 +297,7 @@ void addProject(const QString& name) //--------------------------------------------------------- //MusE::MusE(int argc, char** argv) : QMainWindow(0, "mainwindow") -MusE::MusE(int argc, char** argv) : QMainWindow() +MusE::MusE(int /*argc*/, char** /*argv*/) : QMainWindow() { // By T356. For LADSPA plugins in plugin.cpp // QWidgetFactory::addWidgetFactory( new PluginWidgetFactory ); ddskrjo @@ -333,6 +331,7 @@ MusE::MusE(int argc, char** argv) : QMainWindow() progress = 0; activeTopWin = NULL; currentMenuSharingTopwin = NULL; + waitingForTopwin = NULL; appName = QString("MusE"); setWindowTitle(appName); @@ -346,8 +345,6 @@ MusE::MusE(int argc, char** argv) : QMainWindow() MusEGlobal::heartBeatTimer = new QTimer(this); MusEGlobal::heartBeatTimer->setObjectName("timer"); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), MusEGlobal::song, SLOT(beat())); - - connect(this, SIGNAL(activeTopWinChanged(MusEGui::TopWin*)), SLOT(activeTopWinChangedSlot(MusEGui::TopWin*))); #ifdef ENABLE_PYTHON @@ -460,7 +457,7 @@ MusE::MusE(int argc, char** argv) : QMainWindow() MusEGlobal::panicAction->setWhatsThis(tr("send note off to all midi channels")); connect(MusEGlobal::panicAction, SIGNAL(activated()), MusEGlobal::song, SLOT(panic())); - MusECore::initMidiInstruments(); + MusECore::initMidiInstruments(); MusECore::initMidiPorts(); MusECore::initMidiDevices(); @@ -706,7 +703,7 @@ MusE::MusE(int argc, char** argv) : QMainWindow() //rlimit lim; //getrlimit(RLIMIT_RTPRIO, &lim); //printf("RLIMIT_RTPRIO soft:%d hard:%d\n", lim.rlim_cur, lim.rlim_max); // Reported 80, 80 even with non-RT kernel. - + if (MusEGlobal::realTimePriority < sched_get_priority_min(SCHED_FIFO)) MusEGlobal::realTimePriority = sched_get_priority_min(SCHED_FIFO); else if (MusEGlobal::realTimePriority > sched_get_priority_max(SCHED_FIFO)) @@ -721,18 +718,19 @@ MusE::MusE(int argc, char** argv) : QMainWindow() MusEGlobal::midiRTPrioOverride = sched_get_priority_max(SCHED_FIFO); } - // Changed by Tim. p3.3.17 - //MusEGlobal::midiSeq = new MusECore::MidiSeq(MusEGlobal::realTimeScheduling ? MusEGlobal::realTimePriority : 0, "Midi"); - MusEGlobal::midiSeq = new MusECore::MidiSeq("Midi"); - MusEGlobal::audio = new MusECore::Audio(); - //MusEGlobal::audioPrefetch = new MusECore::AudioPrefetch(0, "Disc"); - MusEGlobal::audioPrefetch = new MusECore::AudioPrefetch("Prefetch"); - + MusECore::initMidiSequencer(); + MusECore::initAudio(); + + // Moved here from Audio::Audio + QSocketNotifier* ss = new QSocketNotifier(MusEGlobal::audio->getFromThreadFdr(), QSocketNotifier::Read, this); + connect(ss, SIGNAL(activated(int)), MusEGlobal::song, SLOT(seqSignal(int))); + + MusECore::initAudioPrefetch(); + //--------------------------------------------------- // Popups //--------------------------------------------------- - // when adding a menu to the main window, remember adding it to // either the leadingMenus or trailingMenus list! // also do NOT use menuBar()->addMenu(QString&), but ALWAYS @@ -961,6 +959,11 @@ MusE::MusE(int argc, char** argv) : QMainWindow() transport = new MusEGui::Transport(this, "transport"); bigtime = 0; + MusEGlobal::song->blockSignals(false); + + // Load start song moved to main.cpp p4.0.41 REMOVE Tim. + /* + //--------------------------------------------------- // load project // if no songname entered on command line: @@ -975,7 +978,8 @@ MusE::MusE(int argc, char** argv) : QMainWindow() name = argv[0]; else if (MusEGlobal::config.startMode == 0) { if (argc < 2) - name = projectList[0] ? *projectList[0] : QString("untitled"); + //name = projectList[0] ? *projectList[0] : QString("untitled"); + name = projectList[0] ? *projectList[0] : MusEGui::getUniqueUntitledName(); // p4.0.40 else name = argv[0]; printf("starting with selected song %s\n", MusEGlobal::config.startSong.toLatin1().constData()); @@ -989,19 +993,47 @@ MusE::MusE(int argc, char** argv) : QMainWindow() printf("starting with pre configured song %s\n", MusEGlobal::config.startSong.toLatin1().constData()); name = MusEGlobal::config.startSong; } - MusEGlobal::song->blockSignals(false); - loadProjectFile(name, useTemplate, true); - + + // loadProjectFile(name, useTemplate, true); //commented out by flo: see below (*) + */ + changeConfig(false); QSettings settings("MusE", "MusE-qt"); restoreGeometry(settings.value("MusE/geometry").toByteArray()); //restoreState(settings.value("MusE/windowState").toByteArray()); - MusEGlobal::song->update(); - - updateWindowMenu(); + MusEGlobal::song->update(); // commented out by flo: will be done by the below (*) + updateWindowMenu(); // same here + + // Load start song moved to main.cpp p4.0.41 REMOVE Tim. + /* + + // this is (*). + // this is a really hackish workaround for the loading-on-startup problem. + // i have absolutely no idea WHY it breaks when using loadProjectFile() + // above, but it does on my machine (it doesn't on others!). + // the problem can be worked around by delaying loading the song file. + // i use hackishSongOpenTimer for this, which calls after 10ms a slot + // which then does the actual loadProjectFile() call. + // FIXME: please, if anyone finds the real problem, FIX it and + // remove that dirty, dirty workaround! + hackishSongOpenFilename=name; + hackishSongOpenUseTemplate=useTemplate; + hackishSongOpenTimer=new QTimer(this); + hackishSongOpenTimer->setInterval(10); + hackishSongOpenTimer->setSingleShot(true); + connect(hackishSongOpenTimer, SIGNAL(timeout()), this, SLOT(hackishSongOpenTimerTimeout())); + hackishSongOpenTimer->start(); + */ } +// Load start song moved to main.cpp p4.0.41 REMOVE Tim. +//void MusE::hackishSongOpenTimerTimeout() +//{ + ///loadProjectFile(hackishSongOpenFilename, hackishSongOpenUseTemplate, true); + //loadProjectFile(hackishSongOpenFilename, hackishSongOpenUseTemplate, !hackishSongOpenUseTemplate); +//} + MusE::~MusE() { } @@ -1015,6 +1047,44 @@ void MusE::setHeartBeat() MusEGlobal::heartBeatTimer->start(1000/MusEGlobal::config.guiRefresh); } +//--------------------------------------------------- +// loadDefaultSong +// if no songname entered on command line: +// startMode: 0 - load last song +// 1 - load default template +// 2 - load configured start song +//--------------------------------------------------- + +void MusE::loadDefaultSong(int argc, char** argv) +{ + QString name; + bool useTemplate = false; + if (argc >= 2) + name = argv[0]; + else if (MusEGlobal::config.startMode == 0) { + if (argc < 2) + //name = projectList[0] ? *projectList[0] : QString("untitled"); + name = projectList[0] ? *projectList[0] : MusEGui::getUniqueUntitledName(); // p4.0.40 + else + name = argv[0]; + printf("starting with selected song %s\n", MusEGlobal::config.startSong.toLatin1().constData()); + } + else if (MusEGlobal::config.startMode == 1) { + printf("starting with default template\n"); + name = MusEGlobal::museGlobalShare + QString("/templates/default.med"); + useTemplate = true; + } + else if (MusEGlobal::config.startMode == 2) { + printf("starting with pre configured song %s\n", MusEGlobal::config.startSong.toLatin1().constData()); + name = MusEGlobal::config.startSong; + } + //loadProjectFile(name, useTemplate, true); + loadProjectFile(name, useTemplate, !useTemplate); + + //MusEGlobal::song->update(); + //updateWindowMenu(); +} + //--------------------------------------------------------- // resetDevices //--------------------------------------------------------- @@ -1156,7 +1226,8 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool loadAll QApplication::restoreOverrideCursor(); return; } - project.setFile("untitled"); + //project.setFile("untitled"); + project.setFile(MusEGui::getUniqueUntitledName()); // p4.0.40 MusEGlobal::museProject = MusEGlobal::museProjectInitPath; } else { @@ -1214,7 +1285,8 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool loadAll } if (!songTemplate) { addProject(project.absoluteFilePath()); - setWindowTitle(QString("MusE: Song: ") + project.completeBaseName()); + //setWindowTitle(QString("MusE: Song: ") + project.completeBaseName()); + setWindowTitle(QString("MusE: Song: ") + MusEGui::projectTitleFromFilename(project.absoluteFilePath())); } MusEGlobal::song->dirty = false; progress->setValue(30); @@ -1236,7 +1308,7 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool loadAll // set the geometry if the mixer has already been created. if(mixer1) { - //if(mixer1->geometry().size() != MusEGlobal::config.mixer1.geometry.size()) // p3.3.53 Moved below + //if(mixer1->geometry().size() != MusEGlobal::config.mixer1.geometry.size()) // Moved below // mixer1->resize(MusEGlobal::config.mixer1.geometry.size()); if(mixer1->geometry().topLeft() != MusEGlobal::config.mixer1.geometry.topLeft()) @@ -1244,7 +1316,7 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool loadAll } if(mixer2) { - //if(mixer2->geometry().size() != MusEGlobal::config.mixer2.geometry.size()) // p3.3.53 Moved below + //if(mixer2->geometry().size() != MusEGlobal::config.mixer2.geometry.size()) // Moved below // mixer2->resize(MusEGlobal::config.mixer2.geometry.size()); if(mixer2->geometry().topLeft() != MusEGlobal::config.mixer2.geometry.topLeft()) @@ -1273,7 +1345,7 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool loadAll arrangerView->scoreNamingChanged(); // inform the score menus about the new scores and their names progress->setValue(50); - // p3.3.53 Try this AFTER the song update above which does a mixer update... Tested OK - mixers resize properly now. + // Try this AFTER the song update above which does a mixer update... Tested OK - mixers resize properly now. if (loadAll) { if(mixer1) @@ -1336,10 +1408,13 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool loadAll void MusE::setUntitledProject() { setConfigDefaults(); - QString name("untitled"); + //QString name("untitled"); + QString name(MusEGui::getUniqueUntitledName()); // p4.0.40 + MusEGlobal::museProject = "./"; //QFileInfo(name).absolutePath(); project.setFile(name); - setWindowTitle(tr("MusE: Song: %1").arg(project.completeBaseName())); + //setWindowTitle(tr("MusE: Song: %1").arg(project.completeBaseName())); + setWindowTitle(tr("MusE: Song: %1").arg(MusEGui::projectTitleFromFilename(name))); } //--------------------------------------------------------- @@ -1391,14 +1466,14 @@ void MusE::loadTemplate() if (!fn.isEmpty()) { // MusEGlobal::museProject = QFileInfo(fn).absolutePath(); - loadProjectFile(fn, true, true); + //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); + loadProjectFile(fn, true, false); setUntitledProject(); } @@ -1410,7 +1485,11 @@ void MusE::loadTemplate() bool MusE::save() { - if (project.completeBaseName() == "untitled") + //if (project.completeBaseName() == "untitled") // p4.0.40 Must catch "untitled 1" "untitled 2" etc + //if (MusEGui::projectTitleFromFilename(project.absoluteFilePath()) == "untitled") + //if (MusEGui::projectTitleFromFilename(project.absoluteFilePath()) == MusEGui::getUniqueUntitledName()) + ///if (project.absoluteFilePath() == MusEGui::getUniqueUntitledName()) + if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) return saveAs(); else return save(project.filePath(), false); @@ -1542,7 +1621,7 @@ void MusE::closeEvent(QCloseEvent* event) MusECore::exitMetronome(); // Make sure to delete the menu. ~routingPopupMenu() will NOT be called automatically. - // Even though it is a child of MusE, it just passes MusE onto the underlying PopupMenus. p4.0.26 + // Even though it is a child of MusE, it just passes MusE onto the underlying PopupMenus. if(routingPopupMenu) delete routingPopupMenu; #if 0 @@ -1572,7 +1651,7 @@ void MusE::closeEvent(QCloseEvent* event) } #ifdef HAVE_LASH - // Disconnect gracefully from LASH. Tim. p3.3.14 + // Disconnect gracefully from LASH. if(lash_client) { if(MusEGlobal::debugMsg) @@ -1590,7 +1669,6 @@ void MusE::closeEvent(QCloseEvent* event) printf("MusE: Exiting OSC\n"); MusECore::exitOSC(); - // p3.3.47 delete MusEGlobal::audioPrefetch; delete MusEGlobal::audio; delete MusEGlobal::midiSeq; @@ -1619,8 +1697,8 @@ void MusE::showMarker(bool flag) markerView = new MusEGui::MarkerView(this); connect(markerView, SIGNAL(closed()), SLOT(markerClosed())); - toplevels.push_back(markerView); markerView->show(); + toplevels.push_back(markerView); } markerView->setVisible(flag); viewMarkerAction->setChecked(flag); @@ -1642,6 +1720,20 @@ void MusE::markerClosed() setCurrentMenuSharingTopwin(NULL); updateWindowMenu(); + + // focus the last activated topwin which is not the marker view + QList<QMdiSubWindow*> l = mdiArea->subWindowList(QMdiArea::StackingOrder); + for (QList<QMdiSubWindow*>::iterator lit=l.begin(); lit!=l.end(); lit++) + if ((*lit)->isVisible() && (*lit)->widget() != markerView) + { + if (MusEGlobal::debugMsg) + printf("bringing '%s' to front instead of closed arranger window\n",(*lit)->widget()->windowTitle().toAscii().data()); + + bringToFront((*lit)->widget()); + + break; + } + } //--------------------------------------------------------- @@ -1675,6 +1767,20 @@ void MusE::arrangerClosed() { viewArrangerAction->setChecked(false); updateWindowMenu(); + + // focus the last activated topwin which is not the arranger view + QList<QMdiSubWindow*> l = mdiArea->subWindowList(QMdiArea::StackingOrder); + for (QList<QMdiSubWindow*>::iterator lit=l.begin(); lit!=l.end(); lit++) + if ((*lit)->isVisible() && (*lit)->widget() != arrangerView) + { + if (MusEGlobal::debugMsg) + printf("bringing '%s' to front instead of closed arranger window\n",(*lit)->widget()->windowTitle().toAscii().data()); + + bringToFront((*lit)->widget()); + + break; + } + } //--------------------------------------------------------- @@ -1716,7 +1822,7 @@ MusEGui::RoutePopupMenu* MusE::getRoutingPopupMenu() bool MusE::saveAs() { QString name; - if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) { + //if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) // Use project dialog always now. if (MusEGlobal::config.useProjectSaveDialog) { MusEGui::ProjectCreateImpl pci(MusEGlobal::muse); if (pci.exec() == QDialog::Rejected) { @@ -1737,10 +1843,11 @@ bool MusE::saveAs() QMessageBox::warning(this,"Path error","Can't create project path", QMessageBox::Ok); return false; } - } - else { - name = MusEGui::getSaveFileName(QString(""), MusEGlobal::med_file_save_pattern, this, tr("MusE: Save As")); - } + //} + //else { + // name = MusEGui::getSaveFileName(QString(""), MusEGlobal::med_file_save_pattern, this, tr("MusE: Save As")); + //} + bool ok = false; if (!name.isEmpty()) { QString tempOldProj = MusEGlobal::museProject; @@ -1748,7 +1855,8 @@ bool MusE::saveAs() ok = save(name, true); if (ok) { project.setFile(name); - setWindowTitle(tr("MusE: Song: %1").arg(project.completeBaseName())); + //setWindowTitle(tr("MusE: Song: %1").arg(project.completeBaseName())); + setWindowTitle(tr("MusE: Song: %1").arg(MusEGui::projectTitleFromFilename(name))); addProject(name); } else @@ -1830,8 +1938,8 @@ void MusE::openInScoreEdit(MusEGui::ScoreEdit* destination, MusECore::PartList* if (destination==NULL) // if no destination given, create a new one { destination = new MusEGui::ScoreEdit(this, 0, _arranger->cursorValue()); - destination->show(); toplevels.push_back(destination); + destination->show(); connect(destination, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(destination, SIGNAL(name_changed()), arrangerView, SLOT(scoreNamingChanged())); //connect(muse, SIGNAL(configChanged()), destination, SLOT(config_changed())); @@ -1866,10 +1974,10 @@ void MusE::startPianoroll(MusECore::PartList* pl, bool showDefaultCtrls) { MusEGui::PianoRoll* pianoroll = new MusEGui::PianoRoll(pl, this, 0, _arranger->cursorValue()); - if(showDefaultCtrls) // p4.0.12 + if(showDefaultCtrls) pianoroll->addCtrl(); - pianoroll->show(); toplevels.push_back(pianoroll); + pianoroll->show(); connect(pianoroll, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(MusEGlobal::muse, SIGNAL(configChanged()), pianoroll, SLOT(configChanged())); updateWindowMenu(); @@ -1890,8 +1998,8 @@ void MusE::startListEditor() void MusE::startListEditor(MusECore::PartList* pl) { MusEGui::ListEdit* listEditor = new MusEGui::ListEdit(pl); - listEditor->show(); toplevels.push_back(listEditor); + listEditor->show(); connect(listEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(MusEGlobal::muse,SIGNAL(configChanged()), listEditor, SLOT(configChanged())); updateWindowMenu(); @@ -1904,8 +2012,8 @@ void MusE::startListEditor(MusECore::PartList* pl) void MusE::startMasterEditor() { MusEGui::MasterEdit* masterEditor = new MusEGui::MasterEdit(); - masterEditor->show(); toplevels.push_back(masterEditor); + masterEditor->show(); connect(masterEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); updateWindowMenu(); } @@ -1917,8 +2025,8 @@ void MusE::startMasterEditor() void MusE::startLMasterEditor() { MusEGui::LMaster* lmaster = new MusEGui::LMaster(); - lmaster->show(); toplevels.push_back(lmaster); + lmaster->show(); connect(lmaster, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(MusEGlobal::muse, SIGNAL(configChanged()), lmaster, SLOT(configChanged())); updateWindowMenu(); @@ -1939,10 +2047,10 @@ void MusE::startDrumEditor() void MusE::startDrumEditor(MusECore::PartList* pl, bool showDefaultCtrls) { MusEGui::DrumEdit* drumEditor = new MusEGui::DrumEdit(pl, this, 0, _arranger->cursorValue()); - if(showDefaultCtrls) // p4.0.12 + if(showDefaultCtrls) drumEditor->addCtrl(); - drumEditor->show(); toplevels.push_back(drumEditor); + drumEditor->show(); connect(drumEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(MusEGlobal::muse, SIGNAL(configChanged()), drumEditor, SLOT(configChanged())); updateWindowMenu(); @@ -1966,8 +2074,8 @@ void MusE::startWaveEditor(MusECore::PartList* pl) { MusEGui::WaveEdit* waveEditor = new MusEGui::WaveEdit(pl); waveEditor->show(); - connect(MusEGlobal::muse, SIGNAL(configChanged()), waveEditor, SLOT(configChanged())); toplevels.push_back(waveEditor); + connect(MusEGlobal::muse, SIGNAL(configChanged()), waveEditor, SLOT(configChanged())); connect(waveEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); updateWindowMenu(); } @@ -2088,6 +2196,19 @@ void MusE::toplevelDeleting(MusEGui::TopWin* tl) { activeTopWin=NULL; emit activeTopWinChanged(NULL); + + // focus the last activated topwin which is not the deleting one + QList<QMdiSubWindow*> l = mdiArea->subWindowList(QMdiArea::StackingOrder); + for (QList<QMdiSubWindow*>::iterator lit=l.begin(); lit!=l.end(); lit++) + if ((*lit)->isVisible() && (*lit)->widget() != tl) + { + if (MusEGlobal::debugMsg) + printf("bringing '%s' to front instead of closed window\n",(*lit)->widget()->windowTitle().toAscii().data()); + + bringToFront((*lit)->widget()); + + break; + } } if (tl == currentMenuSharingTopwin) @@ -2168,7 +2289,7 @@ void MusE::kbAccel(int key) MusEGlobal::song->setPlay(true); } - // p4.0.10 Tim. Normally each editor window handles these, to inc by the editor's raster snap value. + // Normally each editor window handles these, to inc by the editor's raster snap value. // But users were asking for a global version - "they don't work when I'm in mixer or transport". // Since no editor claimed the key event, we don't know a specific editor's snap setting, // so adopt a policy where the arranger is the 'main' raster reference, I guess... @@ -2649,7 +2770,8 @@ MusE::lash_idle_cb () int ok = save (ss.toAscii(), false); if (ok) { project.setFile(ss.toAscii()); - setWindowTitle(tr("MusE: Song: %1").arg(project.completeBaseName())); + //setWindowTitle(tr("MusE: Song: %1").arg(project.completeBaseName())); + setWindowTitle(tr("MusE: Song: %1").arg(MusEGui::projectTitleFromFilename(project.absoluteFilePath()))); addProject(ss.toAscii()); MusEGlobal::museProject = QFileInfo(ss.toAscii()).absolutePath(); } @@ -3071,21 +3193,32 @@ void MusE::focusChanged(QWidget*, QWidget* now) if (currentMenuSharingTopwin && (currentMenuSharingTopwin!=activeTopWin)) currentMenuSharingTopwin->storeInitialState(); - + // if the activated widget is a QMdiSubWindow containing some TopWin + if ( (dynamic_cast<QMdiSubWindow*>(ptr)!=0) && + (dynamic_cast<MusEGui::TopWin*>( ((QMdiSubWindow*)ptr)->widget() )!=0) ) + { + waitingForTopwin=(MusEGui::TopWin*) ((QMdiSubWindow*)ptr)->widget(); + return; + } while (ptr) { + if (MusEGlobal::heavyDebugMsg) + printf("focusChanged: at widget %p with type %s\n",ptr, typeid(*ptr).name()); + if ( (dynamic_cast<MusEGui::TopWin*>(ptr)!=0) || // *ptr is a TopWin or a derived class (ptr==this) ) // the main window is selected break; ptr=dynamic_cast<QWidget*>(ptr->parent()); //in the unlikely case that ptr is a QObject, this returns NULL, which stops the loop } + MusEGui::TopWin* win=dynamic_cast<MusEGui::TopWin*>(ptr); // ptr is either NULL, this or the pointer to a TopWin - if (ptr!=this) // if the main win is selected, don't treat that as "none", but also don't handle it + + // if the main win or some deleting topwin is selected, + // don't treat that as "none", but also don't handle it + if (ptr!=this && (!win || !win->deleting()) ) { - MusEGui::TopWin* win=dynamic_cast<MusEGui::TopWin*>(ptr); - // now 'win' is either NULL or the pointer to the active TopWin if (win!=activeTopWin) { @@ -3233,6 +3366,26 @@ void MusE::shareMenuAndToolbarChanged(MusEGui::TopWin* win, bool val) } } +void MusE::topwinMenuInited(MusEGui::TopWin* topwin) +{ + if (topwin==NULL) + return; + + if (topwin == waitingForTopwin) + { + if (waitingForTopwin->deleting()) + { + waitingForTopwin=NULL; + } + else + { + activeTopWin=waitingForTopwin; + waitingForTopwin=NULL; + emit activeTopWinChanged(activeTopWin); + } + } +} + void MusE::updateWindowMenu() { bool sep; @@ -3286,6 +3439,8 @@ void MusE::updateWindowMenu() void MusE::bringToFront(QWidget* widget) { MusEGui::TopWin* win=dynamic_cast<MusEGui::TopWin*>(widget); + if (!win) return; + if (win->isMdiWin()) { win->show(); @@ -3449,4 +3604,19 @@ void MusE::tileSubWindows() } } +QString MusE::projectTitle() const +{ + return MusEGui::projectTitleFromFilename(project.fileName()); +} + +QString MusE::projectPath() const +{ + return MusEGui::projectPathFromFilename(project.absoluteFilePath()); +} + +QString MusE::projectExtension() const +{ + return MusEGui::projectExtensionFromFilename(project.fileName()); +} + } //namespace MusEGui diff --git a/muse2/muse/app.h b/muse2/muse/app.h index 27722e9a..184581dc 100644 --- a/muse2/muse/app.h +++ b/muse2/muse/app.h @@ -43,6 +43,7 @@ class QToolBar; class QToolButton; class QProgressDialog; class QMdiArea; +class QTimer; namespace MusECore { class AudioOutput; @@ -91,7 +92,7 @@ class TopWin; class Transport; class VisibleTracks; -#define MENU_ADD_SYNTH_ID_BASE 0x1000 +#define MENU_ADD_SYNTH_ID_BASE 0x8000 //--------------------------------------------------------- @@ -128,6 +129,7 @@ class MusE : public QMainWindow TopWin* activeTopWin; TopWin* currentMenuSharingTopwin; + TopWin* waitingForTopwin; std::list<QToolBar*> requiredToolbars; //always displayed std::list<QToolBar*> optionalToolbars; //only displayed when no toolbar-sharing window is active @@ -213,6 +215,10 @@ class MusE : public QMainWindow MidiTransformerDialog* midiTransformerDialog; QMenu* openRecent; + //QTimer* hackishSongOpenTimer; + //QString hackishSongOpenFilename; + //bool hackishSongOpenUseTemplate; + bool readMidi(FILE*); void read(MusECore::Xml& xml, bool skipConfig, bool isTemplate); void processTrack(MusECore::MidiTrack* track); @@ -327,6 +333,8 @@ class MusE : public QMainWindow void arrangeSubWindowsColumns(); void tileSubWindows(); + //void hackishSongOpenTimerTimeout(); + public slots: bool saveAs(); void bounceToFile(MusECore::AudioOutput* ao = 0); @@ -371,12 +379,14 @@ class MusE : public QMainWindow void addMdiSubWindow(QMdiSubWindow*); void shareMenuAndToolbarChanged(MusEGui::TopWin*, bool); + void topwinMenuInited(MusEGui::TopWin*); void updateWindowMenu(); public: MusE(int argc, char** argv); ~MusE(); + void loadDefaultSong(int argc, char** argv); Arranger* arranger() const { return _arranger; } QRect configGeometryMain; QProgressDialog *progress; @@ -384,10 +394,13 @@ class MusE : public QMainWindow void kbAccel(int); void changeConfig(bool writeFlag); void seqStop(); - bool seqStart(); + bool seqStart(); void setHeartBeat(); void importController(int, MusECore::MidiPort*, int); QString projectName() { return project.fileName(); } + QString projectTitle() const; + QString projectPath() const; + QString projectExtension() const; QWidget* mixer1Window(); QWidget* mixer2Window(); QWidget* transportWindow(); diff --git a/muse2/muse/arranger/alayout.cpp b/muse2/muse/arranger/alayout.cpp index a892356c..119da498 100644 --- a/muse2/muse/arranger/alayout.cpp +++ b/muse2/muse/arranger/alayout.cpp @@ -117,7 +117,7 @@ void TLLayout::setGeometry(const QRect &rect) QSize s1 = li[1]->sizeHint(); QSize s2 = li[2]->sizeHint(); - QSize s3 = li[3]->sizeHint(); + //QSize s3 = li[3]->sizeHint(); QSize s4 = li[4]->sizeHint(); QSize s5 = li[5]->sizeHint(); diff --git a/muse2/muse/arranger/arranger.cpp b/muse2/muse/arranger/arranger.cpp index ae753378..cb024070 100644 --- a/muse2/muse/arranger/arranger.cpp +++ b/muse2/muse/arranger/arranger.cpp @@ -569,27 +569,40 @@ void Arranger::songChanged(int type) // Is it simply a midi controller value adjustment? Forget it. if(type != SC_MIDI_CONTROLLER) { - unsigned endTick = MusEGlobal::song->len(); - int offset = AL::sigmap.ticksMeasure(endTick); - hscroll->setRange(-offset, endTick + offset); //DEBUG - canvas->setOrigin(-offset, 0); - time->setOrigin(-offset, 0); - - int bar, beat; - unsigned tick; - AL::sigmap.tickValues(endTick, &bar, &beat, &tick); - if (tick || beat) - ++bar; - lenEntry->blockSignals(true); - lenEntry->setValue(bar); - lenEntry->blockSignals(false); - - if(type & SC_SONG_TYPE) // p4.0.7 Tim. + // TEST p4.0.36 Try these, may need more/less. + if(type & ( SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | + SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED)) + { + unsigned endTick = MusEGlobal::song->len(); + int offset = AL::sigmap.ticksMeasure(endTick); + hscroll->setRange(-offset, endTick + offset); //DEBUG + canvas->setOrigin(-offset, 0); + time->setOrigin(-offset, 0); + + int bar, beat; + unsigned tick; + AL::sigmap.tickValues(endTick, &bar, &beat, &tick); + if (tick || beat) + ++bar; + lenEntry->blockSignals(true); + lenEntry->setValue(bar); + lenEntry->blockSignals(false); + } + + if(type & SC_SONG_TYPE) setMode(MusEGlobal::song->mtype()); - trackSelectionChanged(); - canvas->partsChanged(); - typeBox->setCurrentIndex(int(MusEGlobal::song->mtype())); + if(type & SC_SELECTION) // TEST p4.0.36 Try this alone, may need more. + trackSelectionChanged(); + + // Keep this light, partsChanged is a heavy move! TEST p4.0.36 Try these, may need more. + if(type & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | + SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | + SC_SIG | SC_TEMPO)) // Maybe sig. Requires tempo. + canvas->partsChanged(); + + //typeBox->setCurrentIndex(int(MusEGlobal::song->mtype())); // REMOVE Tim. Redundant. + if (type & SC_SIG) time->redraw(); if (type & SC_TEMPO) @@ -616,6 +629,14 @@ void Arranger::songChanged(int type) } } } + + // TEST p4.0.36 Try this + if(type & ( //SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | + SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | + SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED)) //| + //SC_SIG | SC_TEMPO)) // Maybe sig. and tempo. No, moved above. + canvas->redraw(); + } updateTrackInfo(type); diff --git a/muse2/muse/arranger/arrangerview.cpp b/muse2/muse/arranger/arrangerview.cpp index 3b726845..52be5194 100644 --- a/muse2/muse/arranger/arrangerview.cpp +++ b/muse2/muse/arranger/arrangerview.cpp @@ -37,7 +37,7 @@ #include <QMessageBox> #include <QMimeData> #include <QPushButton> -#include <QResizeEvent> +//#include <QResizeEvent> #include <QScrollArea> #include <QScrollBar> #include <QSettings> @@ -129,6 +129,7 @@ ArrangerView::ArrangerView(QWidget* parent) connect(muse, SIGNAL(configChanged()), arranger, SLOT(configChanged())); connect(arranger, SIGNAL(setUsedTool(int)), editTools, SLOT(set(int))); connect(arranger, SIGNAL(selectionChanged()), SLOT(selectionChanged())); + connect(MusEGlobal::song, SIGNAL(songChanged(int)), visTracks, SLOT(updateVisibleTracksButtons())); @@ -167,8 +168,8 @@ ArrangerView::ArrangerView(QWidget* parent) scoreSubmenu = new QMenu(tr("Score"), this); scoreSubmenu->setIcon(QIcon(*scoreIconSet)); - scoreAllInOneSubsubmenu = new QMenu(tr("all parts in one staff"), this); - scoreOneStaffPerTrackSubsubmenu = new QMenu(tr("one staff per part"), this); + scoreAllInOneSubsubmenu = new QMenu(tr("all tracks in one staff"), this); + scoreOneStaffPerTrackSubsubmenu = new QMenu(tr("one staff per track"), this); scoreSubmenu->addMenu(scoreAllInOneSubsubmenu); scoreSubmenu->addMenu(scoreOneStaffPerTrackSubsubmenu); @@ -193,6 +194,10 @@ ArrangerView::ArrangerView(QWidget* parent) strGlobalInsertAction = new QAction(tr("Global Insert"), this); strGlobalSplitAction = new QAction(tr("Global Split"), this); + strGlobalCutSelAction = new QAction(tr("Global Cut - selected tracks"), this); + strGlobalInsertSelAction = new QAction(tr("Global Insert - selected tracks"), this); + strGlobalSplitSelAction = new QAction(tr("Global Split - selected tracks"), this); + //------------------------------------------------------------- @@ -246,7 +251,11 @@ ArrangerView::ArrangerView(QWidget* parent) menuStructure->addAction(strGlobalCutAction); menuStructure->addAction(strGlobalInsertAction); menuStructure->addAction(strGlobalSplitAction); - + menuStructure->addSeparator(); + menuStructure->addAction(strGlobalCutSelAction); + menuStructure->addAction(strGlobalInsertSelAction); + menuStructure->addAction(strGlobalSplitSelAction); + QMenu* functions_menu = menuBar()->addMenu(tr("Functions")); @@ -341,6 +350,9 @@ ArrangerView::ArrangerView(QWidget* parent) connect(strGlobalCutAction, SIGNAL(activated()), SLOT(globalCut())); connect(strGlobalInsertAction, SIGNAL(activated()), SLOT(globalInsert())); connect(strGlobalSplitAction, SIGNAL(activated()), SLOT(globalSplit())); + connect(strGlobalCutSelAction, SIGNAL(activated()), SLOT(globalCutSel())); + connect(strGlobalInsertSelAction, SIGNAL(activated()), SLOT(globalInsertSel())); + connect(strGlobalSplitSelAction, SIGNAL(activated()), SLOT(globalSplitSel())); @@ -351,7 +363,7 @@ ArrangerView::ArrangerView(QWidget* parent) connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged())); connect(cb, SIGNAL(selectionChanged()), SLOT(clipboardChanged())); - + MusEGlobal::muse->topwinMenuInited(this); // work around for probable QT/WM interaction bug. // for certain window managers, e.g xfce, this window is @@ -368,7 +380,7 @@ ArrangerView::~ArrangerView() void ArrangerView::closeEvent(QCloseEvent* e) { - emit deleted(static_cast<TopWin*>(this)); + emit isDeleting(static_cast<TopWin*>(this)); emit closed(); e->accept(); } @@ -729,4 +741,9 @@ void ArrangerView::globalCut() { MusECore::globalCut(); } void ArrangerView::globalInsert() { MusECore::globalInsert(); } void ArrangerView::globalSplit() { MusECore::globalSplit(); } +// variants only applicable for selected tracks +void ArrangerView::globalCutSel() { MusECore::globalCut(true); } +void ArrangerView::globalInsertSel() { MusECore::globalInsert(true); } +void ArrangerView::globalSplitSel() { MusECore::globalSplit(true); } + } // namespace MusEGui diff --git a/muse2/muse/arranger/arrangerview.h b/muse2/muse/arranger/arrangerview.h index de610bd6..c56767a6 100644 --- a/muse2/muse/arranger/arrangerview.h +++ b/muse2/muse/arranger/arrangerview.h @@ -89,7 +89,8 @@ class ArrangerView : public TopWin QMenu* master; QAction *strGlobalCutAction, *strGlobalInsertAction, *strGlobalSplitAction; - QAction *trackMidiAction, *trackDrumAction, *trackWaveAction, *trackAOutputAction, *trackAGroupAction; + QAction *strGlobalCutSelAction, *strGlobalInsertSelAction, *strGlobalSplitSelAction; + QAction *trackMidiAction, *trackDrumAction, *trackWaveAction, *trackAOutputAction, *trackAGroupAction; QAction *trackAInputAction, *trackAAuxAction; QAction *editCutAction, *editCopyAction, *editCopyRangeAction; QAction *editPasteAction, *editPasteCloneAction, *editPasteDialogAction, *editPasteCloneDialogAction; @@ -119,11 +120,14 @@ class ArrangerView : public TopWin void globalCut(); void globalInsert(); void globalSplit(); - void cmd(int); + void globalCutSel(); + void globalInsertSel(); + void globalSplitSel(); + void cmd(int); void addNewTrack(QAction* action); signals: - void deleted(MusEGui::TopWin*); + void isDeleting(MusEGui::TopWin*); void closed(); public slots: diff --git a/muse2/muse/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index d19fa082..f190de3b 100644 --- a/muse2/muse/arranger/pcanvas.cpp +++ b/muse2/muse/arranger/pcanvas.cpp @@ -3,7 +3,7 @@ // Linux Music Editor // $Id: pcanvas.cpp,v 1.48.2.26 2009/11/22 11:08:33 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -302,12 +302,9 @@ void PartCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dtyp QPoint newpos = raster(QPoint(nx, ny)); selectItem(ci, true); - MusECore::UndoOp operation=moveItem(ci, newpos, dtype); - if (operation.type != MusECore::UndoOp::DoNothing) - { + bool result=moveItem(operations, ci, newpos, dtype); + if (result) ci->move(newpos); - operations.push_back(operation); - } if(moving.size() == 1) { itemReleased(curItem, newpos); @@ -326,37 +323,41 @@ void PartCanvas::moveCanvasItems(CItemList& items, int dp, int dx, DragType dtyp //--------------------------------------------------------- // Changed by T356. -MusECore::UndoOp PartCanvas::moveItem(CItem* item, const QPoint& newpos, DragType t) +bool PartCanvas::moveItem(MusECore::Undo& operations, CItem* item, const QPoint& newpos, DragType t) { - MusECore::UndoOp result; NPart* npart = (NPart*) item; MusECore::Part* spart = npart->part(); MusECore::Track* track = npart->track(); + MusECore::Track* dtrack=NULL; unsigned dtick = newpos.x(); unsigned ntrack = y2pitch(item->mp().y()); MusECore::Track::TrackType type = track->type(); if (tracks->index(track) == ntrack && (dtick == spart->tick())) { - return MusECore::UndoOp(MusECore::UndoOp::DoNothing); + return false; } if (ntrack >= tracks->size()) { ntrack = tracks->size(); if (MusEGlobal::debugMsg) printf("PartCanvas::moveItem - add new track\n"); - MusECore::Track* newTrack = MusEGlobal::song->addTrack(type); // Add at end of list. + dtrack = MusEGlobal::song->addTrack(operations, type); // Add at end of list. + if (type == MusECore::Track::WAVE) { MusECore::WaveTrack* st = (MusECore::WaveTrack*) track; - MusECore::WaveTrack* dt = (MusECore::WaveTrack*) newTrack; + MusECore::WaveTrack* dt = (MusECore::WaveTrack*) dtrack; dt->setChannels(st->channels()); } emit tracklistChanged(); } - MusECore::Track* dtrack = tracks->index(ntrack); - if (dtrack->type() != type) { - QMessageBox::critical(this, QString("MusE"), - tr("Cannot copy/move/clone to different Track-Type")); - return MusECore::UndoOp(MusECore::UndoOp::DoNothing); + else + { + dtrack = tracks->index(ntrack); + if (dtrack->type() != type) { + QMessageBox::critical(this, QString("MusE"), + tr("Cannot copy/move/clone to different Track-Type")); + return false; + } } - + MusECore::Part* dpart; bool clone = (t == MOVE_CLONE || (t == MOVE_COPY && spart->events()->arefCount() > 1)); @@ -397,22 +398,24 @@ MusECore::UndoOp PartCanvas::moveItem(CItem* item, const QPoint& newpos, DragTyp if (t == MOVE_COPY || t == MOVE_CLONE) { // These will not increment ref count, and will not chain clones... // TODO FINDMICH: is this still correct (by flo93)? i doubt it! - result=MusECore::UndoOp(MusECore::UndoOp::AddPart,dpart); + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart,dpart)); } else if (t == MOVE_MOVE) { dpart->setSelected(spart->selected()); // These will increment ref count if not a clone, and will chain clones... // TODO FINDMICH: is this still correct (by flo93)? i doubt it! - result=MusECore::UndoOp(MusECore::UndoOp::ModifyPart,spart, dpart, true, false); + operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyPart,spart, dpart, true, false)); spart->setSelected(false); } - // else // will never happen -> result will always be defined + // else // will never happen -> operations will never be empty if (MusEGlobal::song->len() < (dpart->lenTick() + dpart->tick())) - MusEGlobal::song->setLen(dpart->lenTick() + dpart->tick()); - - return result; + operations.push_back( MusECore::UndoOp(MusECore::UndoOp::ModifySongLen, + dpart->lenTick() + dpart->tick(), + MusEGlobal::song->len() ) ); + + return true; } //--------------------------------------------------------- @@ -883,8 +886,8 @@ void PartCanvas::mousePress(QMouseEvent* event) default: if (item) emit trackChanged(item->part()->track()); - else - emit trackChanged(NULL); + //else -- don't see the point of removing track selection, commenting out (rj) + // emit trackChanged(NULL); break; case CutTool: if (item) splitItem(item, pt); @@ -1193,7 +1196,7 @@ void PartCanvas::keyPress(QKeyEvent* event) //If we're at topmost, leave if (!track) { - printf("no track above!\n"); + //printf("no track above!\n"); return; } int middle = curItem->x() + curItem->part()->lenTick()/2; @@ -1327,17 +1330,25 @@ void PartCanvas::keyPress(QKeyEvent* event) curItem = newItem; selectItem(newItem, true); - //Check if we've hit the upper or lower boundaries of the window. If so, set a new position + //Check if we've hit the left, right, upper or lower boundaries of the window. If so, scroll to new position. if (newItem->x() < mapxDev(0)) { - int curpos = pos[0]; - setPos(0,newItem->x(),true); - setPos(0,curpos,false); //Dummy to put the current position back once we've scrolled + emit horizontalScroll(rmapx(newItem->x() - xorg) - 10); // Leave some room. + } + else if (newItem->x() + newItem->width() > mapxDev(width())) { + int mx = rmapx(newItem->x()); + int newx = mx + rmapx(newItem->width()) - width(); + emit horizontalScroll( (newx > mx ? mx - 10 : newx + 10) - rmapx(xorg) ); + } + + if (newItem->y() < mapyDev(0)) { + int my = rmapy(newItem->y()); + int newy = my + rmapy(newItem->height()) - height(); + emit verticalScroll( (newy < my ? my - 10 : newy + 10) - rmapy(yorg) ); } - else if (newItem->x() > mapxDev(width())) { - int curpos = pos[0]; - setPos(0,newItem->x(),true); - setPos(0,curpos,false); //Dummy to put the current position back once we've scrolled + else if (newItem->y() + newItem->height() > mapyDev(height())) { + emit verticalScroll( rmapy(newItem->y() + newItem->height() - yorg) - height() + 10); } + redraw(); } } @@ -2299,9 +2310,9 @@ void PartCanvas::drawMidiPart(QPainter& p, const QRect&, MusECore::EventList* ev //else // color_brightness=64; // otherwise use dark color if (brightness >= 12000 && !pt->selected()) - color_brightness=64; // 96; // too bright: use dark color + color_brightness=54; // 96; // too bright: use dark color else - color_brightness=190; //160; // too dark: use lighter color + color_brightness=200; //160; // too dark: use lighter color } else color_brightness=80; @@ -3207,9 +3218,13 @@ void PartCanvas::viewDropEvent(QDropEvent* event) if (!track) { // we need to create a track for this drop if (text.endsWith(".mpt", Qt::CaseInsensitive)) { - track = MusEGlobal::song->addTrack(MusECore::Track::MIDI); // Add at end of list. + MusECore::Undo operations; + track = MusEGlobal::song->addTrack(operations, MusECore::Track::MIDI); // Add at end of list. + MusEGlobal::song->applyOperationGroup(operations); } else { - track = MusEGlobal::song->addTrack(MusECore::Track::WAVE); // Add at end of list. + MusECore::Undo operations; + track = MusEGlobal::song->addTrack(operations, MusECore::Track::WAVE); // Add at end of list. + MusEGlobal::song->applyOperationGroup(operations); } } if (track->type() == MusECore::Track::WAVE && @@ -3634,7 +3649,8 @@ void PartCanvas::drawAutomation(QPainter& p, const QRect& rr, MusECore::AudioTra { double y; if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume - y = dbToVal(cl->curVal()); // represent volume between 0 and 1 + //printf("log conversion val=%f min=%f max=%f\n", cl->curVal(), min, max); + y = logToVal(cl->curVal(), min, max); // represent volume between 0 and 1 if (y < 0) y = 0.0; } else @@ -3647,7 +3663,8 @@ void PartCanvas::drawAutomation(QPainter& p, const QRect& rr, MusECore::AudioTra { double y = ic->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume - y = dbToVal(y); // represent volume between 0 and 1 + //printf("log conversion val=%f min=%f max=%f\n", cl->curVal(), min, max); + y = logToVal(y, min, max); // represent volume between 0 and 1 if (y < 0) y = 0.0; } else @@ -3746,7 +3763,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo { double y; if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume - y = dbToVal(cl->curVal()); // represent volume between 0 and 1 + y = logToVal(cl->curVal(), min, max); // represent volume between 0 and 1 if (y < 0) y = 0.0; } else @@ -3759,7 +3776,7 @@ void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, boo { double y = ic->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume - y = dbToVal(y); // represent volume between 0 and 1 + y = logToVal(y, min, max); // represent volume between 0 and 1 if (y < 0) y = 0; } else @@ -3951,7 +3968,8 @@ void PartCanvas::processAutomationMovements(QPoint pos, bool addPoint) automation.currentCtrlList->range(&min,&max); double cvval; if (automation.currentCtrlList->valueType() == MusECore::VAL_LOG ) { // use db scale for volume - cvval = valToDb(yfraction); + printf("log conversion val=%f min=%f max=%f\n", yfraction, min, max); + cvval = valToLog(yfraction, min, max); //printf("calc yfraction = %f v=%f ",yfraction,cvval); if (cvval< min) cvval=min; if (cvval>max) cvval=max; @@ -3984,13 +4002,45 @@ void PartCanvas::processAutomationMovements(QPoint pos, bool addPoint) } -double PartCanvas::dbToVal(double inDb) +//--------------------------------------------------------- +// +// logToVal +// - represent logarithmic value on linear scale from 0 to 1 +// +//--------------------------------------------------------- +double PartCanvas::logToVal(double inLog, double min, double max) { - return (20.0*MusECore::fast_log10(inDb)+60.0) / 70.0; + //printf("logToVal inLog %f :", inLog); + if (inLog < min) inLog = min; + if (inLog > max) inLog = max; + double linMin = 20.0*MusECore::fast_log10(min); + double linMax = 20.0*MusECore::fast_log10(max); + double linVal = 20.0*MusECore::fast_log10(inLog); + + double outVal = (linVal-linMin) / (linMax - linMin); + // printf("inLog %f outVal %f linVal %f min %f max %f dbMin %f dbMax %f\n", inLog, outVal, linVal, min, max, linMin, linMax); + + return outVal; } -double PartCanvas::valToDb(double inV) + +//--------------------------------------------------------- +// +// valToLog +// - represent value from 0 to 1 as logarithmic value between min and max +// +//--------------------------------------------------------- +double PartCanvas::valToLog(double inV, double min, double max) { - return exp10((inV*70.0-60.0)/20.0); + double linMin = 20.0*MusECore::fast_log10(min); + double linMax = 20.0*MusECore::fast_log10(max); + + double linVal = (inV * (linMax - linMin)) + linMin; + double outVal = exp10((linVal)/20.0); + + //printf("::valToLog inV %f outVal %f linVal %f min %f max %f\n", inV, outVal, linVal, min, max); + if (outVal > max) outVal = max; + if (outVal < min) outVal = min; + return outVal; } //--------------------------------------------------------- diff --git a/muse2/muse/arranger/pcanvas.h b/muse2/muse/arranger/pcanvas.h index 78b88a5c..34395688 100644 --- a/muse2/muse/arranger/pcanvas.h +++ b/muse2/muse/arranger/pcanvas.h @@ -119,7 +119,7 @@ class PartCanvas : public Canvas { virtual void newItem(CItem*,bool); virtual bool deleteItem(CItem*); virtual void moveCanvasItems(CItemList&, int, int, DragType); - virtual MusECore::UndoOp moveItem(CItem*, const QPoint&, DragType); + virtual bool moveItem(MusECore::Undo& operations, CItem*, const QPoint&, DragType); virtual void updateSong(DragType, int); virtual void startDrag(CItem*, DragType); @@ -148,8 +148,8 @@ class PartCanvas : public Canvas { void checkAutomation(MusECore::Track * t, const QPoint& pointer, bool addNewCtrl); void processAutomationMovements(QPoint pos, bool addPoint); - double dbToVal(double inDb); - double valToDb(double inV); + double logToVal(double inLog, double min, double max); + double valToLog(double inV, double min, double max); protected: virtual void drawCanvas(QPainter&, const QRect&); diff --git a/muse2/muse/arranger/tlist.cpp b/muse2/muse/arranger/tlist.cpp index 1e3ad419..620a24b7 100644 --- a/muse2/muse/arranger/tlist.cpp +++ b/muse2/muse/arranger/tlist.cpp @@ -62,6 +62,7 @@ #include "synth.h" #include "config.h" #include "popupmenu.h" +#include "menutitleitem.h" #ifdef DSSI_SUPPORT #include "dssihost.h" @@ -392,7 +393,7 @@ void TList::paint(const QRect& r) countVisible++; } //int count = ((MusECore::AudioTrack*)track)->controller()->size(); //commented out by flo: gives a "unused variable" warning - s.sprintf(" %d(%d) visible",countVisible, countAll); + s.sprintf(" %d(%d) %s",countVisible, countAll, tr("visible").toAscii().data()); } @@ -584,12 +585,17 @@ void TList::portsPopupMenu(MusECore::Track* t, int x, int y) switch(t->type()) { case MusECore::Track::MIDI: case MusECore::Track::DRUM: + // FINDMICHJETZT: this is a notice for flo's experimental + // branch! don't forget NEW_DRUM here! + // please don't remove this. i'll do it when + // the time is there. case MusECore::Track::AUDIO_SOFTSYNTH: { MusECore::MidiTrack* track = (MusECore::MidiTrack*)t; //QPopupMenu* p = MusECore::midiPortsPopup(0); MusECore::MidiDevice* md = 0; + int potential_new_port_no=-1; int port = -1; if(t->type() == MusECore::Track::AUDIO_SOFTSYNTH) { @@ -602,25 +608,158 @@ void TList::portsPopupMenu(MusECore::Track* t, int x, int y) port = track->outPort(); QMenu* p = MusECore::midiPortsPopup(this, port); // 0, port); + + if (t->type()==MusECore::Track::MIDI || t->type()==MusECore::Track::DRUM) //FINDMICHJETZT + { + // extend that menu a bit + + + // find first free port number + // do not permit numbers already used in other tracks! + // except if it's only used in this track. + int no; + for (no=0;no<MIDI_PORTS;no++) + if (MusEGlobal::midiPorts[no].device()==NULL) + { + MusECore::ciTrack it; + for (it=MusEGlobal::song->tracks()->begin(); it!=MusEGlobal::song->tracks()->end(); it++) + { + MusECore::MidiTrack* mt=dynamic_cast<MusECore::MidiTrack*>(*it); + if (mt && mt!=t && mt->outPort()==no) + break; + } + if (it == MusEGlobal::song->tracks()->end()) + break; + } + + if (no==MIDI_PORTS) + { + delete p; + printf("THIS IS VERY UNLIKELY TO HAPPEN: no free midi ports! you have used all %i!\n",MIDI_PORTS); + break; + } + + + potential_new_port_no=no; + typedef std::map<std::string, int > asmap; + typedef std::map<std::string, int >::iterator imap; + + asmap mapALSA; + asmap mapJACK; + + int aix = 0x10000000; + int jix = 0x20000000; + for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + if((*i)->deviceType() == MusECore::MidiDevice::ALSA_MIDI) + { + // don't add devices which are used somewhere + int j; + for (j=0;j<MIDI_PORTS;j++) + if (MusEGlobal::midiPorts[j].device() == *i) + break; + + if (j==MIDI_PORTS) mapALSA.insert( std::pair<std::string, int> (std::string((*i)->name().toLatin1().constData()), aix) ); + + ++aix; + } + else if((*i)->deviceType() == MusECore::MidiDevice::JACK_MIDI) + { + // don't add devices which are used somewhere + int j; + for (j=0;j<MIDI_PORTS;j++) + if (MusEGlobal::midiPorts[j].device() == *i) + break; + + if (j==MIDI_PORTS) mapJACK.insert( std::pair<std::string, int> (std::string((*i)->name().toLatin1().constData()), jix) ); + ++jix; + } + } + + if (!mapALSA.empty() || !mapJACK.empty()) + { + QMenu* pup = p->addMenu(tr("Unused Devices")); + QAction* act; + + + if (!mapALSA.empty()) + { + pup->addAction(new MusEGui::MenuTitleItem("ALSA:", pup)); + + for(imap i = mapALSA.begin(); i != mapALSA.end(); ++i) + { + int idx = i->second; + QString s(i->first.c_str()); + MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::ALSA_MIDI); + if(md) + { + if(md->deviceType() != MusECore::MidiDevice::ALSA_MIDI) + continue; + + act = pup->addAction(md->name()); + act->setData(idx); + } + } + } + + if (!mapALSA.empty() && !mapJACK.empty()) + pup->addSeparator(); + + if (!mapJACK.empty()) + { + pup->addAction(new MusEGui::MenuTitleItem("JACK:", pup)); + + for(imap i = mapJACK.begin(); i != mapJACK.end(); ++i) + { + int idx = i->second; + QString s(i->first.c_str()); + MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::JACK_MIDI); + if(md) + { + if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) + continue; + + act = pup->addAction(md->name()); + act->setData(idx); + } + } + } + } + } + + QAction* act = p->exec(mapToGlobal(QPoint(x, y)), 0); if(!act) { delete p; break; } - + + QString acttext=act->text(); int n = act->data().toInt(); delete p; if(n < 0) // Invalid item. break; - if(n >= MIDI_PORTS) // Show port config dialog. + if(n == MIDI_PORTS) // Show port config dialog. { MusEGlobal::muse->configMidiPorts(); break; } + else if (n & 0x30000000) + { + int typ; + if (n & 0x10000000) + typ = MusECore::MidiDevice::ALSA_MIDI; + else + typ = MusECore::MidiDevice::JACK_MIDI; + MusECore::MidiDevice* sdev = MusEGlobal::midiDevices.find(acttext, typ); + + MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[potential_new_port_no], sdev); + n=potential_new_port_no; + } // Changed by T356. //track->setOutPort(n); //MusEGlobal::audio->msgSetTrackOutPort(track, n); @@ -1049,7 +1188,7 @@ void TList::mousePressEvent(QMouseEvent* ev) if(act) { t = MusEGlobal::song->addNewTrack(act); // Add at end of list. - if(t) + if(t && t->isVisible()) { MusEGlobal::song->deselectTracks(); t->setSelected(true); @@ -1064,13 +1203,13 @@ void TList::mousePressEvent(QMouseEvent* ev) //delete synp; delete p; } - else if (button == Qt::LeftButton) { + /*else if (button == Qt::LeftButton) { if (!ctrl) { MusEGlobal::song->deselectTracks(); emit selectionChanged(0); } - } + }*/ return; } @@ -1108,7 +1247,8 @@ void TList::mousePressEvent(QMouseEvent* ev) return; } - mode = START_DRAG; + + mode = NORMAL; switch (col) { case COL_CLEF: @@ -1137,8 +1277,8 @@ void TList::mousePressEvent(QMouseEvent* ev) } delete p; } - break; + case COL_AUTOMATION: { if (!t->isMidiTrack()) { @@ -1174,6 +1314,8 @@ void TList::mousePressEvent(QMouseEvent* ev) case COL_RECORD: { + mode = START_DRAG; + bool val = !(t->recordFlag()); if (button == Qt::LeftButton) { if (!t->isMidiTrack()) { @@ -1212,6 +1354,7 @@ void TList::mousePressEvent(QMouseEvent* ev) } break; case COL_NONE: + mode = START_DRAG; break; case COL_CLASS: if (t->isMidiTrack()) @@ -1232,9 +1375,10 @@ void TList::mousePressEvent(QMouseEvent* ev) //MusEGlobal::audio->msgUpdateSoloStates(); // p4.0.14 //MusEGlobal::song->update(SC_ROUTE); // - break; + case COL_MUTE: + mode = START_DRAG; // p3.3.29 if ((button == Qt::RightButton) || (((QInputEvent*)ev)->modifiers() & Qt::ShiftModifier)) t->setOff(!t->off()); @@ -1248,11 +1392,13 @@ void TList::mousePressEvent(QMouseEvent* ev) MusEGlobal::song->update(SC_MUTE); break; case COL_SOLO: + mode = START_DRAG; MusEGlobal::audio->msgSetSolo(t, !t->solo()); MusEGlobal::song->update(SC_SOLO); break; case COL_NAME: + mode = START_DRAG; if (button == Qt::LeftButton) { if (!ctrl) { MusEGlobal::song->deselectTracks(); @@ -1327,11 +1473,13 @@ void TList::mousePressEvent(QMouseEvent* ev) break; case COL_TIMELOCK: + mode = START_DRAG; t->setLocked(!t->locked()); break; case COL_OCHANNEL: { + mode = START_DRAG; // or not? (flo) int delta = 0; if (button == Qt::RightButton) delta = 1; @@ -1397,6 +1545,9 @@ void TList::mousePressEvent(QMouseEvent* ev) } } break; + + default: + mode = START_DRAG; } redraw(); } diff --git a/muse2/muse/audio.cpp b/muse2/muse/audio.cpp index fd90c2c2..0b1574cc 100644 --- a/muse2/muse/audio.cpp +++ b/muse2/muse/audio.cpp @@ -25,8 +25,6 @@ #include <cmath> #include <errno.h> -#include <QSocketNotifier> - #include "app.h" #include "song.h" #include "node.h" @@ -51,10 +49,18 @@ namespace MusEGlobal { MusECore::Audio* audio; MusECore::AudioDevice* audioDevice; // current audio device in use -extern unsigned int volatile midiExtSyncTicks; // p3.3.25 +extern unsigned int volatile midiExtSyncTicks; } namespace MusECore { + + +void initAudio() +{ + MusEGlobal::audio = new Audio(); +} + + extern double curTime(); //static const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; @@ -166,8 +172,11 @@ Audio::Audio() exit(-1); } sigFd = filedes[1]; - QSocketNotifier* ss = new QSocketNotifier(filedes[0], QSocketNotifier::Read); - MusEGlobal::song->connect(ss, SIGNAL(activated(int)), MusEGlobal::song, SLOT(seqSignal(int))); + sigFdr = filedes[0]; + + // Moved to MusE::MusE + //QSocketNotifier* ss = new QSocketNotifier(filedes[0], QSocketNotifier::Read); + //MusEGlobal::song->connect(ss, SIGNAL(activated(int)), MusEGlobal::song, SLOT(seqSignal(int))); } //--------------------------------------------------------- @@ -182,7 +191,9 @@ bool Audio::start() //process(MusEGlobal::segmentSize); // warm up caches state = STOP; _loopCount = 0; - MusEGlobal::muse->setHeartBeat(); + + MusEGlobal::muse->setHeartBeat(); + if (MusEGlobal::audioDevice) { //_running = true; //MusEGlobal::audioDevice->start(); @@ -221,9 +232,11 @@ bool Audio::start() // shall we really stop JACK transport and locate to // saved position? - MusEGlobal::audioDevice->stopTransport(); + MusEGlobal::audioDevice->stopTransport(); + //MusEGlobal::audioDevice->seekTransport(MusEGlobal::song->cPos().frame()); - MusEGlobal::audioDevice->seekTransport(MusEGlobal::song->cPos()); + MusEGlobal::audioDevice->seekTransport(MusEGlobal::song->cPos()); + return true; } @@ -531,6 +544,26 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) // Pre-process the metronome. ((AudioTrack*)metronome)->preProcessAlways(); + // Process Aux tracks first. + for(ciTrack it = tl->begin(); it != tl->end(); ++it) + { + if((*it)->isMidiTrack()) + continue; + track = (AudioTrack*)(*it); + if(!track->processed() && track->type() == Track::AUDIO_AUX) + { + //printf("Audio::process1 Do aux: track:%s\n", track->name().toLatin1().constData()); + channels = track->channels(); + // Just a dummy buffer. + float* buffer[channels]; + float data[frames * channels]; + for (int i = 0; i < channels; ++i) + buffer[i] = data + i * frames; + //printf("Audio::process1 calling track->copyData for track:%s\n", track->name().toLatin1()); + track->copyData(samplePos, channels, -1, -1, frames, buffer); + } + } + OutputList* ol = MusEGlobal::song->outputs(); for (ciAudioOutput i = ol->begin(); i != ol->end(); ++i) (*i)->process(samplePos, offset, frames); @@ -548,8 +581,11 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) track = (AudioTrack*)(*it); // Ignore unprocessed tracks which have an output route, because they will be processed by // whatever track(s) they are routed to. - if(!track->processed() && track->noOutRoute() && (track->type() != Track::AUDIO_OUTPUT)) + //if(!track->processed() && track->noOutRoute() && (track->type() != Track::AUDIO_OUTPUT)) + // No, do all. + if(!track->processed() && (track->type() != Track::AUDIO_OUTPUT)) { + //printf("Audio::process1 track:%s\n", track->name().toLatin1().constData()); channels = track->channels(); // Just a dummy buffer. float* buffer[channels]; diff --git a/muse2/muse/audio.h b/muse2/muse/audio.h index 8d89be78..3c4eb17d 100644 --- a/muse2/muse/audio.h +++ b/muse2/muse/audio.h @@ -166,7 +166,9 @@ class Audio { int fromThreadFdw, fromThreadFdr; // message pipe int sigFd; // pipe fd for messages to gui - + //QSocketNotifier* _socketNotifier; + int sigFdr; + // record values: Pos startRecordPos; Pos endRecordPos; @@ -189,8 +191,12 @@ class Audio { public: Audio(); - virtual ~Audio() {} + virtual ~Audio() { } + // Access to message pipe (like from gui namespace), otherwise audio would need to depend on gui. + int getFromThreadFdw() { return sigFd; } + int getFromThreadFdr() { return sigFdr; } + void process(unsigned frames); bool sync(int state, unsigned frame); void shutdown(); diff --git a/muse2/muse/audioprefetch.cpp b/muse2/muse/audioprefetch.cpp index 9406911f..1fcb7cef 100644 --- a/muse2/muse/audioprefetch.cpp +++ b/muse2/muse/audioprefetch.cpp @@ -39,7 +39,11 @@ MusECore::AudioPrefetch* audioPrefetch; namespace MusECore { -// Added by Tim. p3.3.20 +void initAudioPrefetch() +{ + MusEGlobal::audioPrefetch = new AudioPrefetch("Prefetch"); +} + //#define AUDIOPREFETCH_DEBUG enum { PREFETCH_TICK, PREFETCH_SEEK @@ -140,6 +144,7 @@ void AudioPrefetch::msgTick() { PrefetchMsg msg; msg.id = PREFETCH_TICK; + msg.pos = 0; // seems to be unused, was uninitalized. while (sendMsg1(&msg, sizeof(msg))) { printf("AudioPrefetch::msgTick(): send failed!\n"); } diff --git a/muse2/muse/audiotrack.cpp b/muse2/muse/audiotrack.cpp index b674e20c..aceacc75 100644 --- a/muse2/muse/audiotrack.cpp +++ b/muse2/muse/audiotrack.cpp @@ -107,7 +107,7 @@ AudioTrack::AudioTrack(TrackType t) _automationType = AUTO_OFF; //setChannels(1); setChannels(2); - addController(new CtrlList(AC_VOLUME,"Volume",0.0,3.16 /* roughly 10 db */, VAL_LOG)); + addController(new CtrlList(AC_VOLUME,"Volume",0.001,3.163 /* roughly 10 db */, VAL_LOG)); addController(new CtrlList(AC_PAN, "Pan", -1.0, 1.0, VAL_LINEAR)); addController(new CtrlList(AC_MUTE,"Mute",0.0,1.0, VAL_LINEAR, true /*dont show in arranger */)); @@ -1610,8 +1610,40 @@ void AudioAux::read(Xml& xml) // getData //--------------------------------------------------------- -bool AudioAux::getData(unsigned /*pos*/, int ch, unsigned /*samples*/, float** data) +bool AudioAux::getData(unsigned pos, int ch, unsigned samples, float** data) { + // Make sure all the aux-supporting tracks are processed first so aux data is gathered. p4.0.37 + TrackList* tl = MusEGlobal::song->tracks(); + AudioTrack* track; + for(ciTrack it = tl->begin(); it != tl->end(); ++it) + { + if((*it)->isMidiTrack()) + continue; + track = (AudioTrack*)(*it); + // If there are any Aux route paths to the track, defer processing until the second main track processing pass. + if(!track->processed() && track->hasAuxSend() && !track->auxRefCount()) + { + int chans = track->channels(); + // Just a dummy buffer. + float* buff[chans]; + float buff_data[samples * chans]; + for (int i = 0; i < chans; ++i) + buff[i] = buff_data + i * samples; + + //printf("AudioAux::getData name:%s\n calling copyData on:%s auxRefCount:%d\n", + // name().toLatin1().constData(), track->name().toLatin1().constData(), track->auxRefCount()); + + track->copyData(pos, chans, -1, -1, samples, buff); + + /* float* buff[ch]; + float buff_data[samples * ch]; + for (int i = 0; i < ch; ++i) + buff[i] = buff_data + i * samples; + //printf("Audio::process1 calling track->copyData for track:%s\n", track->name().toLatin1()); + track->copyData(pos, ch, -1, -1, samples, buff); */ + } + } + for (int i = 0; i < ch; ++i) data[i] = buffer[i % channels()]; return true; diff --git a/muse2/muse/cliplist/cliplist.cpp b/muse2/muse/cliplist/cliplist.cpp index 1055f449..60041dae 100644 --- a/muse2/muse/cliplist/cliplist.cpp +++ b/muse2/muse/cliplist/cliplist.cpp @@ -32,6 +32,7 @@ #include "wave.h" #include "xml.h" #include "ui_cliplisteditorbase.h" +#include "app.h" namespace MusEGui { @@ -168,6 +169,7 @@ ClipListEdit::ClipListEdit(QWidget* parent) connect(editor->len, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(lenChanged(const MusECore::Pos&))); updateList(); + MusEGlobal::muse->topwinMenuInited(this); } ClipListEdit::~ClipListEdit() diff --git a/muse2/muse/cobject.cpp b/muse2/muse/cobject.cpp index 80fc7eae..50586d45 100644 --- a/muse2/muse/cobject.cpp +++ b/muse2/muse/cobject.cpp @@ -50,7 +50,7 @@ bool TopWin::initInited=false; TopWin::TopWin(ToplevelType t, QWidget* parent, const char* name, Qt::WindowFlags f) : QMainWindow(parent, f) { - _isDeleting = false; + _isDeleting = false; if (initInited==false) initConfiguration(); @@ -78,7 +78,10 @@ TopWin::TopWin(ToplevelType t, QWidget* parent, const char* name, Qt::WindowFlag mdisubwin=NULL; _sharesToolsAndMenu=_defaultSubwin[_type] ? _sharesWhenSubwin[_type] : _sharesWhenFree[_type]; if (_defaultSubwin[_type]) + { setIsMdiWin(true); + _savedToolbarState=_toolbarNonsharedInit[_type]; + } if (_sharesToolsAndMenu) menuBar()->hide(); @@ -125,7 +128,11 @@ void TopWin::readStatus(MusECore::Xml& xml) if (!sharesToolsAndMenu()) { if (!restoreState(QByteArray::fromHex(xml.parse1().toAscii()))) - fprintf(stderr,"ERROR: couldn't restore toolbars. however, this is not really a problem.\n"); + { + fprintf(stderr,"ERROR: couldn't restore toolbars. trying default configuration...\n"); + if (!restoreState(_toolbarNonsharedInit[_type])) + fprintf(stderr,"ERROR: couldn't restore default toolbars. this is not really a problem.\n"); + } } else { @@ -565,4 +572,15 @@ void TopWin::resize(const QSize& s) resize(s.width(), s.height()); } +TopWin* ToplevelList::findType(TopWin::ToplevelType type) const +{ + for (ciToplevel i = begin(); i != end(); ++i) + { + if((*i)->type() == type) + return (*i); + } + return 0; +} + + } // namespace MusEGui diff --git a/muse2/muse/cobject.h b/muse2/muse/cobject.h index 309d8424..f6ea2ce1 100644 --- a/muse2/muse/cobject.h +++ b/muse2/muse/cobject.h @@ -141,9 +141,21 @@ class TopWin : public QMainWindow }; -typedef std::list <TopWin*> ToplevelList; -typedef ToplevelList::iterator iToplevel; -typedef ToplevelList::const_iterator ciToplevel; +//--------------------------------------------------------- +// ToplevelList +//--------------------------------------------------------- + +//typedef std::list <TopWin*> ToplevelList; +//typedef ToplevelList::iterator iToplevel; +//typedef ToplevelList::const_iterator ciToplevel; + +typedef std::list<TopWin*>::iterator iToplevel; +typedef std::list<TopWin*>::const_iterator ciToplevel; + +class ToplevelList : public std::list<TopWin* > { + public: + TopWin* findType(TopWin::ToplevelType) const; + }; } // namespace MusEGui diff --git a/muse2/muse/conf.cpp b/muse2/muse/conf.cpp index c11adf7b..ea2b33f3 100644 --- a/muse2/muse/conf.cpp +++ b/muse2/muse/conf.cpp @@ -220,7 +220,7 @@ static void readPortChannel(Xml& xml, int midiPort) // readConfigMidiPort //--------------------------------------------------------- -static void readConfigMidiPort(Xml& xml) +static void readConfigMidiPort(Xml& xml, bool skipConfig) { int idx = 0; QString device; @@ -234,11 +234,9 @@ static void readConfigMidiPort(Xml& xml) // FIXME: TODO: Make this user-configurable! QString instrument("GM"); + int rwFlags = 3; int openFlags = 1; - bool thruFlag = false; - //int dic = 0; - //int doc = 0; - int dic = -1; // p4.0.17 + int dic = -1; int doc = -1; MidiSyncInfo tmpSi; @@ -251,6 +249,19 @@ static void readConfigMidiPort(Xml& xml) QString tag = xml.s1(); switch (token) { case Xml::TagStart: + + // skipConfig added so it doesn't overwrite midi ports. p4.0.41 Tim. + // Try to keep the controller information. But, this may need to be moved below. + // Also may want to try to keep sync info, but that's a bit risky, so let's not for now. + if (tag == "channel") { + readPortChannel(xml, idx); + break; + } + else if (skipConfig){ + xml.skip(tag); + break; + } + if (tag == "name") device = xml.parse1(); else if (tag == "type") @@ -262,6 +273,8 @@ static void readConfigMidiPort(Xml& xml) } else if (tag == "openFlags") openFlags = xml.parseInt(); + else if (tag == "rwFlags") // Jack midi devs need this. p4.0.41 + rwFlags = xml.parseInt(); else if (tag == "defaultInChans") dic = xml.parseInt(); else if (tag == "defaultOutChans") @@ -270,16 +283,15 @@ static void readConfigMidiPort(Xml& xml) tmpSi.read(xml); else if (tag == "instrument") { instrument = xml.parse1(); - // Moved by Tim. - //MusEGlobal::midiPorts[idx].setInstrument( + //MusEGlobal::midiPorts[idx].setInstrument( // Moved below // registerMidiInstrument(instrument) // ); } else if (tag == "midithru") - thruFlag = xml.parseInt(); // obsolete - else if (tag == "channel") { - readPortChannel(xml, idx); - } + xml.parseInt(); // obsolete + //else if (tag == "channel") { + // readPortChannel(xml, idx); // Moved above + // } else xml.unknown("MidiDevice"); break; @@ -290,6 +302,10 @@ static void readConfigMidiPort(Xml& xml) break; case Xml::TagEnd: if (tag == "midiport") { + + if(skipConfig) // p4.0.41 + return; + //if (idx > MIDI_PORTS) { if (idx < 0 || idx >= MIDI_PORTS) { fprintf(stderr, "bad midi port %d (>%d)\n", @@ -305,9 +321,8 @@ static void readConfigMidiPort(Xml& xml) if(!dev && type == MidiDevice::JACK_MIDI) { if(MusEGlobal::debugMsg) - fprintf(stderr, "readConfigMidiPort: creating jack midi device %s\n", device.toLatin1().constData()); - //dev = MidiJackDevice::createJackMidiDevice(device, openFlags); - dev = MidiJackDevice::createJackMidiDevice(device); // p3.3.55 + fprintf(stderr, "readConfigMidiPort: creating jack midi device %s with rwFlags:%d\n", device.toLatin1().constData(), rwFlags); + dev = MidiJackDevice::createJackMidiDevice(device, rwFlags); } if(MusEGlobal::debugMsg && !dev) @@ -315,7 +330,7 @@ static void readConfigMidiPort(Xml& xml) MidiPort* mp = &MusEGlobal::midiPorts[idx]; - mp->setInstrument(registerMidiInstrument(instrument)); // By Tim. + mp->setInstrument(registerMidiInstrument(instrument)); if(dic != -1) // p4.0.17 Leave them alone unless set by song. mp->setDefaultInChannels(dic); if(doc != -1) @@ -480,7 +495,7 @@ static void loadConfigMetronom(Xml& xml) // readSeqConfiguration //--------------------------------------------------------- -static void readSeqConfiguration(Xml& xml) +static void readSeqConfiguration(Xml& xml, bool skipConfig) { for (;;) { Xml::Token token = xml.parse(); @@ -492,7 +507,7 @@ static void readSeqConfiguration(Xml& xml) if (tag == "metronom") loadConfigMetronom(xml); else if (tag == "midiport") - readConfigMidiPort(xml); + readConfigMidiPort(xml, skipConfig); else if (tag == "rcStop") MusEGlobal::rcStopNote = xml.parseInt(); else if (tag == "rcEnable") @@ -542,7 +557,7 @@ void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig midiport configuration and VOLUME. */ if (tag == "sequencer") { - readSeqConfiguration(xml); + readSeqConfiguration(xml, readOnlySequencer); break; } else if (readOnlySequencer) { @@ -580,8 +595,10 @@ void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig MidiTrack::setVisible((bool)xml.parseInt()); else if (tag == "inputTracksVisible") AudioInput::setVisible((bool)xml.parseInt()); - else if (tag == "outputTracksVisible") + else if (tag == "outputTracksVisible") { + printf("output track set from config!\n"); AudioOutput::setVisible((bool)xml.parseInt()); + } else if (tag == "synthTracksVisible") SynthI::setVisible((bool)xml.parseInt()); else if (tag == "bigtimeVisible") @@ -624,7 +641,18 @@ void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig MusEGlobal::config.geometryTransport = readGeometry(xml, tag); else if (tag == "geometryBigTime") MusEGlobal::config.geometryBigTime = readGeometry(xml, tag); - + else if (tag == "Mixer") { + if(mixers == 0) + MusEGlobal::config.mixer1.read(xml); + else + MusEGlobal::config.mixer2.read(xml); + ++mixers; + } + else if (tag == "geometryMain") + MusEGlobal::config.geometryMain = readGeometry(xml, tag); + + // don't insert else if(...) clauses between + // this line and "Global config stuff begins here". else if (!doReadGlobalConfig) { xml.skip(tag); break; @@ -636,9 +664,6 @@ void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig // ---- Global config stuff begins here ---- - else if (tag == "geometryMain") - MusEGlobal::config.geometryMain = readGeometry(xml, tag); - else if (tag == "theme") MusEGlobal::config.style = xml.parse1(); else if (tag == "styleSheetFile") @@ -847,20 +872,6 @@ void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig MusEGlobal::config.canvasBgPixmap = xml.parse1(); else if (tag == "canvasCustomBgList") MusEGlobal::config.canvasCustomBgList = xml.parse1().split(";", QString::SkipEmptyParts); - - //else if (tag == "mixer1") - // MusEGlobal::config.mixer1.read(xml); - //else if (tag == "mixer2") - // MusEGlobal::config.mixer2.read(xml); - else if (tag == "Mixer") - { - if(mixers == 0) - MusEGlobal::config.mixer1.read(xml); - else - MusEGlobal::config.mixer2.read(xml); - ++mixers; - } - else if (tag == "bigtimeForegroundcolor") MusEGlobal::config.bigTimeForegroundColor = readColor(xml); else if (tag == "bigtimeBackgroundcolor") @@ -932,8 +943,8 @@ void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig MusEGlobal::config.minControlProcessPeriod = xml.parseUInt(); else if (tag == "guiRefresh") MusEGlobal::config.guiRefresh = xml.parseInt(); - else if (tag == "userInstrumentsDir") - MusEGlobal::config.userInstrumentsDir = xml.parse1(); + else if (tag == "userInstrumentsDir") // Obsolete + MusEGlobal::config.userInstrumentsDir = xml.parse1(); // Keep for compatibility else if (tag == "startMode") MusEGlobal::config.startMode = xml.parseInt(); else if (tag == "startSong") @@ -950,6 +961,10 @@ void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig MusEGlobal::config.leftMouseButtonCanDecrease = xml.parseInt(); else if (tag == "rangeMarkerWithoutMMB") MusEGlobal::config.rangeMarkerWithoutMMB = xml.parseInt(); + else if (tag == "addHiddenTracks") + MusEGlobal::config.addHiddenTracks = xml.parseInt(); + else if (tag == "unhideTracks") + MusEGlobal::config.unhideTracks = xml.parseInt(); // ---- the following only skips obsolete entries ---- else if ((tag == "arranger") || (tag == "geometryPianoroll") || (tag == "geometryDrumedit")) @@ -1173,7 +1188,6 @@ static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo) if (dev) { xml.strTag(level, "name", dev->name()); - // p3.3.38 //if(dynamic_cast<MidiJackDevice*>(dev)) if(dev->deviceType() != MidiDevice::ALSA_MIDI) //xml.intTag(level, "type", MidiDevice::JACK_MIDI); @@ -1183,6 +1197,9 @@ static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo) // openFlags was read before, but never written here. //xml.intTag(level, "record", dev->rwFlags() & 0x2 ? 1 : 0); xml.intTag(level, "openFlags", dev->openFlags()); + + if(dev->deviceType() == MidiDevice::JACK_MIDI) + xml.intTag(level, "rwFlags", dev->rwFlags()); // Need this. Jack midi devs are created by app. p4.0.41 } mport->syncInfo().write(level, xml); // write out registered controller for all channels @@ -1249,9 +1266,8 @@ void MusE::writeGlobalConfiguration(int level, MusECore::Xml& xml) const xml.intTag(level, "dummyAudioBufSize", MusEGlobal::config.dummyAudioBufSize); xml.intTag(level, "dummyAudioSampleRate", MusEGlobal::config.dummyAudioSampleRate); xml.uintTag(level, "minControlProcessPeriod", MusEGlobal::config.minControlProcessPeriod); - xml.intTag(level, "guiRefresh", MusEGlobal::config.guiRefresh); - xml.strTag(level, "userInstrumentsDir", MusEGlobal::config.userInstrumentsDir); + // Removed by Orcan. 20101220 //xml.strTag(level, "helpBrowser", config.helpBrowser); xml.intTag(level, "extendedMidi", MusEGlobal::config.extendedMidi); @@ -1287,6 +1303,17 @@ void MusE::writeGlobalConfiguration(int level, MusECore::Xml& xml) const xml.intTag(level, "leftMouseButtonCanDecrease", MusEGlobal::config.leftMouseButtonCanDecrease); xml.intTag(level, "rangeMarkerWithoutMMB", MusEGlobal::config.rangeMarkerWithoutMMB); + xml.intTag(level, "unhideTracks", MusEGlobal::config.unhideTracks); + xml.intTag(level, "addHiddenTracks", MusEGlobal::config.addHiddenTracks); + + xml.intTag(level, "waveTracksVisible", MusECore::WaveTrack::visible()); + xml.intTag(level, "auxTracksVisible", MusECore::AudioAux::visible()); + xml.intTag(level, "groupTracksVisible", MusECore::AudioGroup::visible()); + xml.intTag(level, "midiTracksVisible", MusECore::MidiTrack::visible()); + xml.intTag(level, "inputTracksVisible", MusECore::AudioInput::visible()); + xml.intTag(level, "outputTracksVisible", MusECore::AudioOutput::visible()); + xml.intTag(level, "synthTracksVisible", MusECore::SynthI::visible()); + //for (int i = 0; i < 6; ++i) { for (int i = 0; i < NUM_FONTS; ++i) { char buffer[32]; @@ -1339,7 +1366,6 @@ void MusE::writeGlobalConfiguration(int level, MusECore::Xml& xml) const xml.colorTag(level, "auxTrackBg", MusEGlobal::config.auxTrackBg); xml.colorTag(level, "synthTrackBg", MusEGlobal::config.synthTrackBg); - // Removed by Tim. p3.3.6 //xml.intTag(level, "txSyncPort", txSyncPort); //xml.intTag(level, "rxSyncPort", rxSyncPort); xml.intTag(level, "mtctype", MusEGlobal::mtcType); @@ -1366,11 +1392,8 @@ void MusE::writeGlobalConfiguration(int level, MusECore::Xml& xml) const xml.intTag(level, "bigtimeVisible", MusEGlobal::config.bigTimeVisible); xml.intTag(level, "transportVisible", MusEGlobal::config.transportVisible); - //xml.intTag(level, "mixerVisible", MusEGlobal::config.mixerVisible); // Obsolete xml.intTag(level, "mixer1Visible", MusEGlobal::config.mixer1Visible); xml.intTag(level, "mixer2Visible", MusEGlobal::config.mixer2Visible); - //MusEGlobal::config.mixer1.write(level, xml, "mixer1"); - //MusEGlobal::config.mixer2.write(level, xml, "mixer2"); MusEGlobal::config.mixer1.write(level, xml); MusEGlobal::config.mixer2.write(level, xml); @@ -1423,20 +1446,9 @@ void MusE::writeConfiguration(int level, MusECore::Xml& xml) const xml.intTag(level, "midiFilterCtrl3", MusEGlobal::midiFilterCtrl3); xml.intTag(level, "midiFilterCtrl4", MusEGlobal::midiFilterCtrl4); - xml.intTag(level, "waveTracksVisible", MusECore::WaveTrack::visible()); - xml.intTag(level, "auxTracksVisible", MusECore::AudioAux::visible()); - xml.intTag(level, "groupTracksVisible", MusECore::AudioGroup::visible()); - xml.intTag(level, "midiTracksVisible", MusECore::MidiTrack::visible()); - xml.intTag(level, "inputTracksVisible", MusECore::AudioInput::visible()); - xml.intTag(level, "outputTracksVisible", MusECore::AudioOutput::visible()); - xml.intTag(level, "synthTracksVisible", MusECore::SynthI::visible()); - // Removed by Tim. p3.3.6 - //xml.intTag(level, "txDeviceId", txDeviceId); //xml.intTag(level, "rxDeviceId", rxDeviceId); - // Changed by Tim. p3.3.6 - //xml.intTag(level, "txSyncPort", txSyncPort); /* // To keep old muse versions happy... @@ -1458,8 +1470,6 @@ void MusE::writeConfiguration(int level, MusECore::Xml& xml) const } */ - // Added by Tim. p3.3.6 - //xml.tag(level++, "midiSyncInfo"); //for(iMusECore::MidiDevice id = MusECore::MusEGlobal::midiDevices.begin(); id != MusECore::MusEGlobal::midiDevices.end(); ++id) //{ @@ -1487,8 +1497,6 @@ void MusE::writeConfiguration(int level, MusECore::Xml& xml) const xml.intTag(level, "bigtimeVisible", viewBigtimeAction->isChecked()); xml.intTag(level, "transportVisible", viewTransportAction->isChecked()); - //xml.intTag(level, "markerVisible", viewMarkerAction->isChecked()); // Obsolete (done by song's toplevel list) - //xml.intTag(level, "mixerVisible", menuView->isItemChecked(aid1)); // Obsolete xml.geometryTag(level, "geometryMain", this); // FINDME: maybe remove this? do we want // the main win to jump around when loading? @@ -1497,18 +1505,13 @@ void MusE::writeConfiguration(int level, MusECore::Xml& xml) const if (bigtime) xml.geometryTag(level, "geometryBigTime", bigtime); - //if (audioMixer) - // xml.geometryTag(level, "geometryMixer", audioMixer); // Obsolete xml.intTag(level, "mixer1Visible", viewMixerAAction->isChecked()); xml.intTag(level, "mixer2Visible", viewMixerBAction->isChecked()); if (mixer1) - //mixer1->write(level, xml, "mixer1"); mixer1->write(level, xml); if (mixer2) - //mixer2->write(level, xml, "mixer2"); mixer2->write(level, xml); - //_arranger->writeStatus(level, xml); // Obsolete. done by song's toplevel list. arrangerview also handles arranger. writeSeqConfiguration(level, xml, true); MusEGui::write_function_dialog_config(level, xml); @@ -1646,18 +1649,12 @@ namespace MusEGlobal { // write //--------------------------------------------------------- -//void MixerConfig::write(MusECore::Xml& xml, const char* name) void MixerConfig::write(int level, MusECore::Xml& xml) -//void MixerConfig::write(int level, MusECore::Xml& xml, const char* name) { - //xml.stag(QString(name)); - //xml.tag(level++, name.toLatin1().constData()); xml.tag(level++, "Mixer"); - //xml.tag(level++, name); - + xml.strTag(level, "name", name); - //xml.tag("geometry", geometry); xml.qrectTag(level, "geometry", geometry); xml.intTag(level, "showMidiTracks", showMidiTracks); @@ -1669,19 +1666,14 @@ void MixerConfig::write(int level, MusECore::Xml& xml) xml.intTag(level, "showAuxTracks", showAuxTracks); xml.intTag(level, "showSyntiTracks", showSyntiTracks); - //xml.etag(name); - //xml.etag(level, name.toLatin1().constData()); xml.etag(level, "Mixer"); - //xml.etag(level, name); } //--------------------------------------------------------- // read //--------------------------------------------------------- -//void MixerConfig::read(QDomNode node) void MixerConfig::read(MusECore::Xml& xml) -//void MixerConfig::read(MusECore::Xml& xml, const QString& name) { for (;;) { MusECore::Xml::Token token(xml.parse()); diff --git a/muse2/muse/confmport.cpp b/muse2/muse/confmport.cpp index 00914b36..d45dd370 100644 --- a/muse2/muse/confmport.cpp +++ b/muse2/muse/confmport.cpp @@ -183,22 +183,25 @@ void MPConfig::changeDefOutputRoutes(QAction* act) MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE); #else - int ch = 0; - for( ; ch < MIDI_CHANNELS; ++ch) - if(defch & (1 << ch)) break; - - 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); + 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 } } @@ -902,8 +905,105 @@ void MPConfig::rbClicked(QTableWidgetItem* item) 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. + #if 0 + for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) + MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); + + //MusEGlobal::audio->msgUpdateSoloStates(); + //MusEGlobal::song->update(SC_ROUTE); + #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. + #if 0 + for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) + MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); + + //MusEGlobal::audio->msgUpdateSoloStates(); + //MusEGlobal::song->update(SC_ROUTE); + #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) + { + // Remove all routes from this port to the tracks first. + //MusEGlobal::audio->msgRemoveRoute(MusECore::Route(no, allch), MusECore::Route(*it, allch)); + // Now 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. + #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)); + // Now connect all the specified routes. + if(chbits) + MusEGlobal::audio->msgAddRoute(MusECore::Route(no, chbits), MusECore::Route(*it, chbits)); + } + //MusEGlobal::audio->msgUpdateSoloStates(); + //MusEGlobal::song->update(SC_ROUTE); + #else + 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) + { + // 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 + } + + //MusEGlobal::audio->msgUpdateSoloStates(); + ////MusEGlobal::song->update(SC_ROUTE); + + MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(); } } @@ -1061,7 +1161,8 @@ 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. + //_showAliases = -1; // 0: Show first aliases, if available. Nah, stick with -1: none at first. + _showAliases = 0; // 0: Show first aliases, if available. QStringList columnnames; columnnames << tr("Port") @@ -1125,8 +1226,7 @@ void MPConfig::songChanged(int flags) // Is it simply a midi controller value adjustment? Forget it. //if(flags == SC_MIDI_CONTROLLER) // return; - // No need for anything but this, yet. - if(!(flags & SC_CONFIG)) + if(!(flags & (SC_CONFIG | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED))) return; // Get currently selected index... @@ -1299,32 +1399,29 @@ void MPConfig::songChanged(int flags) synthList->clear(); for (std::vector<MusECore::Synth*>::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) { - //s = (*i)->baseName(); - //s = (*i)->name(); - QTreeWidgetItem* item = new QTreeWidgetItem(synthList); - //item->setText(0, s); item->setText(0, QString((*i)->baseName())); + item->setText(1, MusECore::synthType2String((*i)->synthType())); s.setNum((*i)->instances()); - item->setText(1, s); - item->setTextAlignment(1, Qt::AlignHCenter); - //item->setText(2, QString((*i)->baseName())); - item->setText(2, QString((*i)->name())); + item->setText(2, s); + //item->setTextAlignment(2, Qt::AlignHCenter); + item->setText(3, QString((*i)->name())); - item->setText(3, QString((*i)->version())); - item->setText(4, QString((*i)->description())); + 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("<none>"); else s.setNum((*si)->midiPort() + 1); - iitem->setText(1, s); - iitem->setTextAlignment(1, Qt::AlignHCenter); + iitem->setText(2, s); + //iitem->setTextAlignment(2, Qt::AlignHCenter); } synthList->resizeColumnToContents(1); mdevView->resizeColumnsToContents(); @@ -1345,7 +1442,10 @@ void MPConfig::addInstanceClicked() QTreeWidgetItem* item = synthList->currentItem(); if (item == 0) return; - MusECore::SynthI *si = MusEGlobal::song->createSynthI(item->text(0), item->text(2)); // Add at end of list. + // 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; @@ -1374,8 +1474,9 @@ void MPConfig::removeInstanceClicked() MusECore::SynthIList* sl = MusEGlobal::song->syntis(); MusECore::iSynthI ii; for (ii = sl->begin(); ii != sl->end(); ++ii) { - if ((*ii)->iname() == item->text(0)) - break; + 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"); diff --git a/muse2/muse/ctrl/ctrlcanvas.cpp b/muse2/muse/ctrl/ctrlcanvas.cpp index f55eea5d..60e19ba7 100644 --- a/muse2/muse/ctrl/ctrlcanvas.cpp +++ b/muse2/muse/ctrl/ctrlcanvas.cpp @@ -1928,7 +1928,7 @@ void CtrlCanvas::drawOverlay(QPainter& p) //p.setFont(MusEGlobal::config.fonts[3]); //p.setPen(Qt::black); //p.drawText(width()/2-100,height()/2-10, "Use shift + pencil or line tool to draw new events"); - p.drawText(2 , y * 2, "Use shift + pencil or line tool to draw new events"); + p.drawText(2 , y * 2, "Drawing hint: Hold Ctrl to affect only existing events"); } } diff --git a/muse2/muse/driver/alsamidi.cpp b/muse2/muse/driver/alsamidi.cpp index 3b6f5a20..4687f17f 100644 --- a/muse2/muse/driver/alsamidi.cpp +++ b/muse2/muse/driver/alsamidi.cpp @@ -48,6 +48,7 @@ static int alsaSeqFdo = -1; snd_seq_t* alsaSeq = 0; static snd_seq_addr_t musePort; +static snd_seq_addr_t announce_adr; //--------------------------------------------------------- // MidiAlsaDevice @@ -85,42 +86,55 @@ QString MidiAlsaDevice::open() QString estr; int wer = 0; int rer = 0; + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + //snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); + snd_seq_port_info_set_addr(pinfo, &adr); + + int cap = snd_seq_port_info_get_capability(pinfo); // subscribe for writing if (_openFlags & 1) { - snd_seq_port_subscribe_set_sender(subs, &musePort); - snd_seq_port_subscribe_set_dest(subs, &adr); - // Not already subscribed (or error)? Then try subscribing. - if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) - { - //int error = snd_seq_subscribe_port(alsaSeq, subs); - wer = snd_seq_subscribe_port(alsaSeq, subs); - //if (error < 0) - if(wer < 0) - //return QString("Play: ")+QString(snd_strerror(error)); - estr += (QString("Play: ") + QString(snd_strerror(wer)) + QString(" ")); - } - if(!wer) + if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE) + { + snd_seq_port_subscribe_set_sender(subs, &musePort); + snd_seq_port_subscribe_set_dest(subs, &adr); + // Not already subscribed (or error)? Then try subscribing. + if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) + { + //int error = snd_seq_subscribe_port(alsaSeq, subs); + wer = snd_seq_subscribe_port(alsaSeq, subs); + //if (error < 0) + if(wer < 0) + //return QString("Play: ")+QString(snd_strerror(error)); + estr += (QString("Play: ") + QString(snd_strerror(wer)) + QString(" ")); + } + } + if(!wer && (cap & SND_SEQ_PORT_CAP_WRITE)) _writeEnable = true; } // subscribe for reading if (_openFlags & 2) { - snd_seq_port_subscribe_set_dest(subs, &musePort); - snd_seq_port_subscribe_set_sender(subs, &adr); - // Not already subscribed (or error)? Then try subscribing. - if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) - { - //int error = snd_seq_subscribe_port(alsaSeq, subs); - rer = snd_seq_subscribe_port(alsaSeq, subs); - //if (error < 0) - if(rer < 0) - //return QString("Rec: ") + QString(snd_strerror(error)); - estr += (QString("Rec: ") + QString(snd_strerror(rer))); - } - if(!rer) + if(cap & SND_SEQ_PORT_CAP_SUBS_READ) + { + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &adr); + // Not already subscribed (or error)? Then try subscribing. + if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) + { + //int error = snd_seq_subscribe_port(alsaSeq, subs); + rer = snd_seq_subscribe_port(alsaSeq, subs); + //if (error < 0) + if(rer < 0) + //return QString("Rec: ") + QString(snd_strerror(error)); + estr += (QString("Rec: ") + QString(snd_strerror(rer))); + } + } + if(!rer && (cap & SND_SEQ_PORT_CAP_READ)) _readEnable = true; } @@ -141,6 +155,16 @@ void MidiAlsaDevice::close() // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. snd_seq_port_subscribe_alloca(&subs); + int wer = 0; + int rer = 0; + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + //snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); + snd_seq_port_info_set_addr(pinfo, &adr); + + int cap = snd_seq_port_info_get_capability(pinfo); + // This function appears to be called only by MidiPort::setMidiDevice(), // which closes then opens the device. // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags. @@ -160,37 +184,49 @@ void MidiAlsaDevice::close() //if (_openFlags & 1) { //if (!(_openFlags & 1)) { - snd_seq_port_subscribe_set_sender(subs, &musePort); - snd_seq_port_subscribe_set_dest(subs, &adr); - - // Already subscribed? Then unsubscribe. - if(!snd_seq_get_port_subscription(alsaSeq, subs)) - { - if(!snd_seq_unsubscribe_port(alsaSeq, subs)) - _writeEnable = false; - else - printf("MidiAlsaDevice::close Error unsubscribing alsa midi port for writing\n"); + if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE) + { + snd_seq_port_subscribe_set_sender(subs, &musePort); + snd_seq_port_subscribe_set_dest(subs, &adr); + + // Already subscribed? Then unsubscribe. + if(!snd_seq_get_port_subscription(alsaSeq, subs)) + { + wer = snd_seq_unsubscribe_port(alsaSeq, subs); + //if(!wer) + // _writeEnable = false; + //else + if(wer < 0) + printf("MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for writing: %s\n", adr.client, adr.port, snd_strerror(wer)); + } + //else + //_writeEnable = false; } - else - _writeEnable = false; + _writeEnable = false; } //if (_openFlags & 2) { //if (!(_openFlags & 2)) { - snd_seq_port_subscribe_set_dest(subs, &musePort); - snd_seq_port_subscribe_set_sender(subs, &adr); - - // Already subscribed? Then unsubscribe. - if(!snd_seq_get_port_subscription(alsaSeq, subs)) - { - if(!snd_seq_unsubscribe_port(alsaSeq, subs)) - _readEnable = false; - else - printf("MidiAlsaDevice::close Error unsubscribing alsa midi port for reading\n"); - } - else - _readEnable = false; + if(cap & SND_SEQ_PORT_CAP_SUBS_READ) + { + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &adr); + + // Already subscribed? Then unsubscribe. + if(!snd_seq_get_port_subscription(alsaSeq, subs)) + { + rer = snd_seq_unsubscribe_port(alsaSeq, subs); + //if(!rer) + // _readEnable = false; + //else + if(rer < 0) + printf("MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for reading: %s\n", adr.client, adr.port, snd_strerror(rer)); + } + //else + // _readEnable = false; + } + _readEnable = false; } } @@ -698,6 +734,13 @@ bool initMidiAlsa() snd_seq_client_info_set_client(cinfo, -1); while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { + const char* cname = snd_seq_client_info_get_name(cinfo); + //printf( "ALSA client name: %s\n", cname); + + // Put Midi Through and user clients after others. Insert other unwanted clients here: // p4.0.41 + if(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT || strcmp("Midi Through", cname) == 0) + continue; + snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca(&pinfo); snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); @@ -705,6 +748,8 @@ bool initMidiAlsa() while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) { unsigned int capability = snd_seq_port_info_get_capability(pinfo); + if (capability & SND_SEQ_PORT_CAP_NO_EXPORT) // Ignore ports like "qjackctl" or "port". p4.0.41 + continue; if ((capability & outCap) == 0) { const char *name = snd_seq_port_info_get_name(pinfo); if (strcmp("Timer", name) == 0 || @@ -726,29 +771,52 @@ bool initMidiAlsa() adr.client, adr.port, flags, capability); MusEGlobal::midiDevices.add(dev); - - /* - // Experimental... Need to list 'sensible' devices first and ignore unwanted ones... - // Add instance last in midi device list. - for(int i = 0; i < MIDI_PORTS; ++i) - { - MidiPort* mp = &MusEGlobal::midiPorts[i]; - if(mp->device() == 0) - { - // midiSeq might not be initialzed yet! - //MusEGlobal::midiSeq->msgSetMidiDevice(mp, dev); - mp->setMidiDevice(dev); - - //muse->changeConfig(true); // save configuration file - //update(); - break; - } } - */ - + } + + snd_seq_client_info_set_client(cinfo, -1); // Reset + while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { + const char* cname = snd_seq_client_info_get_name(cinfo); + //printf( "ALSA client name: %s\n", cname); + + // Put Midi Through and user clients after others. Insert other unwanted clients here: // p4.0.41 + if( !(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT || strcmp("Midi Through", cname) == 0) ) + continue; + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); + snd_seq_port_info_set_port(pinfo, -1); + + while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) { + unsigned int capability = snd_seq_port_info_get_capability(pinfo); + if (capability & SND_SEQ_PORT_CAP_NO_EXPORT) // Ignore ports like "qjackctl" or "port". p4.0.41 + continue; + if ((capability & outCap) == 0) { + const char *name = snd_seq_port_info_get_name(pinfo); + if (strcmp("Timer", name) == 0 || + strcmp("Announce", name) == 0 || + strcmp("Receiver", name) == 0) + continue; + } + snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo); + MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo))); + int flags = 0; + if (capability & outCap) + flags |= 1; + if (capability & inCap) + flags |= 2; + dev->setrwFlags(flags); + if (MusEGlobal::debugMsg) + printf("ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", + snd_seq_port_info_get_name(pinfo), + adr.client, adr.port, + flags, capability); + MusEGlobal::midiDevices.add(dev); } } - + + //snd_seq_set_client_name(alsaSeq, "MusE Sequencer"); snd_seq_set_client_name(alsaSeq, MusEGlobal::audioDevice->clientName()); @@ -783,14 +851,14 @@ bool initMidiAlsa() // alsa port changes //----------------------------------------- - snd_seq_addr_t aadr; - aadr.client = SND_SEQ_CLIENT_SYSTEM; - aadr.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE; + //snd_seq_addr_t aadr; + announce_adr.client = SND_SEQ_CLIENT_SYSTEM; + announce_adr.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE; snd_seq_port_subscribe_t* subs; snd_seq_port_subscribe_alloca(&subs); snd_seq_port_subscribe_set_dest(subs, &musePort); - snd_seq_port_subscribe_set_sender(subs, &aadr); + snd_seq_port_subscribe_set_sender(subs, &announce_adr); error = snd_seq_subscribe_port(alsaSeq, subs); if (error < 0) { printf("Alsa: Subscribe System failed: %s", snd_strerror(error)); @@ -807,12 +875,35 @@ bool initMidiAlsa() void exitMidiAlsa() { if(alsaSeq) - { - int error = snd_seq_close(alsaSeq); // FIXME Hm, this did not get rid of a buch of valgrind leaks. - if(error < 0) + { + int error = 0; + snd_seq_port_subscribe_t* subs; + // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. + snd_seq_port_subscribe_alloca(&subs); + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca(&pinfo); + //snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); + snd_seq_port_info_set_addr(pinfo, &announce_adr); + + snd_seq_port_subscribe_set_dest(subs, &musePort); + snd_seq_port_subscribe_set_sender(subs, &announce_adr); + + // Already subscribed? Then unsubscribe. + if(!snd_seq_get_port_subscription(alsaSeq, subs)) { - fprintf(stderr, "Could not close ALSA sequencer: %s\n", snd_strerror(error)); - } + error = snd_seq_unsubscribe_port(alsaSeq, subs); + if(error < 0) + printf("MusE: exitMidiAlsa: Error unsubscribing alsa midi Announce port %d:%d for reading: %s\n", announce_adr.client, announce_adr.port, snd_strerror(error)); + } + + error = snd_seq_delete_simple_port(alsaSeq, musePort.port); + if(error < 0) + fprintf(stderr, "MusE: Could not delete ALSA simple port: %s\n", snd_strerror(error)); + + error = snd_seq_close(alsaSeq); + if(error < 0) + fprintf(stderr, "MusE: Could not close ALSA sequencer: %s\n", snd_strerror(error)); } } @@ -852,7 +943,9 @@ void alsaScanMidiPorts() snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); snd_seq_port_info_set_port(pinfo, -1); while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) { - unsigned int capability = snd_seq_port_info_get_capability(pinfo); + unsigned int capability = snd_seq_port_info_get_capability(pinfo); + if (capability & SND_SEQ_PORT_CAP_NO_EXPORT) // Ignore ports like "qjackctl" or "port". p4.0.41 + continue; if (((capability & outCap) == 0) && ((capability & inCap) == 0)) continue; @@ -902,6 +995,8 @@ void alsaScanMidiPorts() // // check for devices to add // + // TODO: Possibly auto-add them to available midi ports. p4.0.41 + // for (std::list<AlsaPort>::iterator k = portList.begin(); k != portList.end(); ++k) { iMidiDevice i = MusEGlobal::midiDevices.begin(); // printf("ALSA port: <%s>\n", k->name); @@ -922,6 +1017,8 @@ void alsaScanMidiPorts() // printf("add device\n"); } } + + } //--------------------------------------------------------- diff --git a/muse2/muse/driver/jack.cpp b/muse2/muse/driver/jack.cpp index 733353d8..421152a7 100644 --- a/muse2/muse/driver/jack.cpp +++ b/muse2/muse/driver/jack.cpp @@ -735,7 +735,11 @@ void JackAudioDevice::connectJackMidiPorts() { RouteList* rl = md->outRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->type != Route::JACK_ROUTE) + continue; connect(port, r->jackPort); + } } } @@ -748,7 +752,11 @@ void JackAudioDevice::connectJackMidiPorts() { RouteList* rl = md->inRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) + { + if(r->type != Route::JACK_ROUTE) + continue; connect(r->jackPort, port); + } } } } @@ -939,6 +947,8 @@ void JackAudioDevice::graphChanged() for (int i = 0;i < 20;i++) { erased = false; for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if(irl->type != Route::JACK_ROUTE) + continue; if (irl->channel != channel) continue; QString name = irl->name(); @@ -977,6 +987,8 @@ void JackAudioDevice::graphChanged() while (*pn) { bool found = false; for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if(irl->type != Route::JACK_ROUTE) + continue; if (irl->channel != channel) continue; QString name = irl->name(); @@ -1027,6 +1039,8 @@ void JackAudioDevice::graphChanged() for (int i = 0; i < 20 ; i++) { erased = false; for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if(irl->type != Route::JACK_ROUTE) + continue; if (irl->channel != channel) continue; QString name = irl->name(); @@ -1064,6 +1078,8 @@ void JackAudioDevice::graphChanged() while (*pn) { bool found = false; for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if(irl->type != Route::JACK_ROUTE) + continue; if (irl->channel != channel) continue; QString name = irl->name(); @@ -1139,6 +1155,8 @@ void JackAudioDevice::graphChanged() { erased = false; for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if(irl->type != Route::JACK_ROUTE) + continue; //if (irl->channel != channel) // continue; QString name = irl->name(); @@ -1181,6 +1199,8 @@ void JackAudioDevice::graphChanged() while (*pn) { bool found = false; for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if(irl->type != Route::JACK_ROUTE) + continue; //if (irl->channel != channel) // continue; QString name = irl->name(); @@ -1239,6 +1259,8 @@ void JackAudioDevice::graphChanged() { erased = false; for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if(irl->type != Route::JACK_ROUTE) + continue; //if (irl->channel != channel) // continue; QString name = irl->name(); @@ -1280,6 +1302,8 @@ void JackAudioDevice::graphChanged() while (*pn) { bool found = false; for (ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { + if(irl->type != Route::JACK_ROUTE) + continue; //if (irl->channel != channel) // continue; QString name = irl->name(); @@ -1482,6 +1506,8 @@ void JackAudioDevice::start(int /*priority*/) RouteList* rl = ai->inRoutes(); void* port = ai->jackPort(ch); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { + if(ir->type != Route::JACK_ROUTE) + continue; if (ir->channel == ch) connect(ir->jackPort, port); } @@ -1495,6 +1521,8 @@ void JackAudioDevice::start(int /*priority*/) RouteList* rl = ai->outRoutes(); void* port = ai->jackPort(ch); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { + if(r->type != Route::JACK_ROUTE) + continue; if (r->channel == ch) { connect(port, r->jackPort); } @@ -1603,6 +1631,107 @@ int JackAudioDevice::frameDelay() const #endif //--------------------------------------------------------- +// getJackPorts +//--------------------------------------------------------- + +void JackAudioDevice::getJackPorts(const char** ports, std::list<QString>& name_list, bool midi, bool physical, int aliases) + { + if (JACK_DEBUG) + printf("JackAudioDevice::getJackPorts()\n"); + //std::list<QString> clientList; + //if(!checkJackClient(_client)) return clientList; + //if(!checkJackClient(_client)) return; + QString qname; + //const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; + //const char** ports = jack_get_ports(_client, 0, type, JackPortIsInput); + //const char** ports = jack_get_ports(_client, 0, type, jflags); + + QString cname(jack_get_client_name(_client)); + + for (const char** p = ports; p && *p; ++p) { + jack_port_t* port = jack_port_by_name(_client, *p); + //int flags = jack_port_flags(port); + //if (!(flags & JackPortIsInput)) + // continue; + //char buffer[128]; + + int port_flags = jack_port_flags(port); + //printf("JackAudioDevice::getJackPorts port: %s flags: %d\n", *p, port_flags); + + // Ignore our own client ports. + if(jack_port_is_mine(_client, port)) + { + if(MusEGlobal::debugMsg) + printf("JackAudioDevice::getJackPorts ignoring own port: %s\n", *p); + continue; + } + + int nsz = jack_port_name_size(); + char buffer[nsz]; + + bool mthrough = false; + + if(midi) + { + strncpy(buffer, *p, nsz); + char a2[nsz]; + char* al[2]; + al[0] = buffer; + al[1] = a2; + int na = jack_port_get_aliases(port, al); + if(na >= 1) + { + qname = QString(al[0]); + //printf("Checking port name for: %s\n", (QString("alsa_pcm:") + cname + QString("/")).toLatin1().constData()); + // Ignore our own ALSA client! + if(qname.startsWith(QString("alsa_pcm:") + cname + QString("/"))) + continue; + // Put Midi Through after all others. + mthrough = qname.startsWith(QString("alsa_pcm:Midi-Through/")); + //if((physical && mthrough) || (!physical && !mthrough)) + //if(physical && mthrough) + // continue; + } + } + // Put physical/terminal ports before others. + bool is_phys = (port_flags & (JackPortIsTerminal | JackPortIsPhysical)) && !mthrough; + if((physical && !is_phys) || (!physical && is_phys)) + continue; + + + strncpy(buffer, *p, nsz); + if((aliases == 0) || (aliases == 1)) + { + char a2[nsz]; + char* al[2]; + al[0] = buffer; + al[1] = a2; + int na = jack_port_get_aliases(port, al); + int a = aliases; + if(a >= na) + { + a = na; + if(a > 0) + a--; + } + qname = QString(al[a]); + } + else + qname = QString(buffer); + + //clientList.push_back(QString(buffer)); + name_list.push_back(qname); + } + + // p3.3.37 + //if(ports) + //free(ports); + // jack_free(ports); // p4.0.29 + + //return clientList; + } + +//--------------------------------------------------------- // outputPorts //--------------------------------------------------------- @@ -1612,9 +1741,11 @@ std::list<QString> JackAudioDevice::outputPorts(bool midi, int aliases) printf("JackAudioDevice::outputPorts()\n"); std::list<QString> clientList; if(!checkJackClient(_client)) return clientList; - QString qname; const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; const char** ports = jack_get_ports(_client, 0, type, JackPortIsOutput); + + /* + QString qname; for (const char** p = ports; p && *p; ++p) { jack_port_t* port = jack_port_by_name(_client, *p); //int flags = jack_port_flags(port); @@ -1666,12 +1797,15 @@ std::list<QString> JackAudioDevice::outputPorts(bool midi, int aliases) //clientList.push_back(QString(buffer)); clientList.push_back(qname); } - - // p3.3.37 - if(ports) - //free(ports); - jack_free(ports); // p4.0.29 + */ + if(ports) + { + getJackPorts(ports, clientList, midi, true, aliases); // Get physical ports first. + getJackPorts(ports, clientList, midi, false, aliases); // Get non-physical ports last. + jack_free(ports); + } + return clientList; } @@ -1683,11 +1817,14 @@ std::list<QString> JackAudioDevice::inputPorts(bool midi, int aliases) { if (JACK_DEBUG) printf("JackAudioDevice::inputPorts()\n"); + std::list<QString> clientList; if(!checkJackClient(_client)) return clientList; - QString qname; const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; const char** ports = jack_get_ports(_client, 0, type, JackPortIsInput); + + /* + QString qname; for (const char** p = ports; p && *p; ++p) { jack_port_t* port = jack_port_by_name(_client, *p); //int flags = jack_port_flags(port); @@ -1739,12 +1876,15 @@ std::list<QString> JackAudioDevice::inputPorts(bool midi, int aliases) //clientList.push_back(QString(buffer)); clientList.push_back(qname); } - - // p3.3.37 - if(ports) - //free(ports); - jack_free(ports); // p4.0.29 + */ + if(ports) + { + getJackPorts(ports, clientList, midi, true, aliases); // Get physical ports first. + getJackPorts(ports, clientList, midi, false, aliases); // Get non-physical ports last. + jack_free(ports); + } + return clientList; } diff --git a/muse2/muse/driver/jackaudio.h b/muse2/muse/driver/jackaudio.h index bd78d481..c4d37db9 100644 --- a/muse2/muse/driver/jackaudio.h +++ b/muse2/muse/driver/jackaudio.h @@ -50,6 +50,7 @@ class JackAudioDevice : public AudioDevice { // Free-running frame counter incremented always in process. jack_nframes_t _frameCounter; + void getJackPorts(const char** ports, std::list<QString>& name_list, bool midi, bool physical, int aliases); static int processAudio(jack_nframes_t frames, void*); public: diff --git a/muse2/muse/dssihost.cpp b/muse2/muse/dssihost.cpp index 92120dbb..2e4e6d35 100644 --- a/muse2/muse/dssihost.cpp +++ b/muse2/muse/dssihost.cpp @@ -99,6 +99,7 @@ static void scanDSSILib(QFileInfo& fi) // ddskrjo removed const for argument // That way we cover all bases - effect plugins and synths. // Non-synths will show up in the ladspa effect dialog, while synths will show up here... // There should be nothing left out... + // TIP: Until we add programs to plugins, comment these four checks to load dssi effects as synths, in order to have programs. if(descr->run_synth || descr->run_synth_adding || descr->run_multiple_synths || @@ -498,6 +499,9 @@ bool DssiSynthIF::init(DssiSynth* s) int inports = synth->_inports; if(inports != 0) { + posix_memalign((void**)&audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + memset(audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + audioInBuffers = new float*[inports]; for(int k = 0; k < inports; ++k) { @@ -723,6 +727,7 @@ DssiSynthIF::DssiSynthIF(SynthI* s) controls = 0; controlsOut = 0; audioInBuffers = 0; + audioInSilenceBuf = 0; audioOutBuffers = 0; } @@ -785,6 +790,9 @@ DssiSynthIF::~DssiSynthIF() delete[] audioInBuffers; } + if(audioInSilenceBuf) + free(audioInSilenceBuf); + if(audioOutBuffers) { for(unsigned long i = 0; i < synth->_outports; ++i) @@ -1420,7 +1428,7 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t // getData //--------------------------------------------------------- -MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MPEventList* el, MusECore::iMPEvent i, unsigned pos, int ports, unsigned n, float** buffer) +MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MPEventList* el, MusECore::iMPEvent start_event, unsigned pos, int ports, unsigned nframes, float** buffer) { //#ifdef DSSI_DEBUG // fprintf(stderr, "DssiSynthIF::getData elsize:%d pos:%d ports:%d samples:%d processed already?:%d\n", el->size(), pos, ports, n, synti->processed()); @@ -1440,19 +1448,11 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP // All ports must be connected to something! unsigned long nop, k; - // First, copy the given input buffers to our local input buffers. - //np = portsin > synth->_inports ? synth->_inports : portsin; - //for(k = 0; k < np; ++k) - // memcpy(audioInBuffers[k], inbuffer[k], sizeof(float) * n); - //for(; k < portsin; ++k) - // memset(audioInBuffers[k], 0, sizeof(float) * n); // Watch our limits. //willyfoobar-2011-02-13 //old code//np = ports > synth->_outports ? synth->_outports : ports; nop = ((unsigned long) ports) > synth->_outports ? synth->_outports : ((unsigned long) ports); - // TODO Number of inports requested? - //nip = ((unsigned long) iports) > synth->_inports ? synth->_inports : ((unsigned long) iports); const DSSI_Descriptor* dssi = synth->dssi; const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; @@ -1484,18 +1484,109 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP // Note for dssi-vst this MUST equal MusEGlobal::audio period. It doesn't like broken-up runs (it stutters), // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length. //unsigned long fixedsize = 2048; - unsigned long fixedsize = n; + unsigned long fixedsize = nframes; // For now, the fixed size is clamped to the MusEGlobal::audio buffer size. // TODO: We could later add slower processing over several cycles - // so that users can select a small MusEGlobal::audio period but a larger control period. - if(fixedsize > n) - fixedsize = n; + if(fixedsize > nframes) + fixedsize = nframes; unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; - if(min_per > n) - min_per = n; + if(min_per > nframes) + min_per = nframes; + + // + // p4.0.38 Handle inputs... + // + if(!((MusECore::AudioTrack*)synti)->noInRoute()) + { + RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes(); + iRoute i = irl->begin(); + if(i->track->isMidiTrack()) + { + //if(MusEGlobal::debugMsg) + //printf("DssiSynthIF::getData: Error: First route is a midi track route!\n"); + } + else + { + int ch = i->channel == -1 ? 0 : i->channel; + int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; + int chs = i->channels == -1 ? 0 : i->channels; + + if((unsigned)ch < synth->_inports && (unsigned)(ch + chs) <= synth->_inports) + { + //printf("DssiSynthIF::getData calling copyData on %s ch:%d remch:%d chs:%d\n", i->track->name().toLatin1().constData(), ch, remch, chs); + + int h = remch + chs; + for(int j = remch; j < h; ++j) + { + //printf(" setting used idx:%d\n", j); + synth->iUsedIdx[j] = true; + } + + ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &audioInBuffers[remch]); + } + } + + ++i; + for(; i != irl->end(); ++i) + { + if(i->track->isMidiTrack()) + { + //if(MusEGlobal::debugMsg) + // printf("DssiSynthIF::getData: Error: Route is a midi track route!\n"); + continue; + } + + int ch = i->channel == -1 ? 0 : i->channel; + int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; + int chs = i->channels == -1 ? 0 : i->channels; + if((unsigned)ch < synth->_inports && (unsigned)(ch + chs) <= synth->_inports) + { + //printf("DssiSynthIF::getData calling addData on %s ch:%d remch:%d chs:%d\n", i->track->name().toLatin1().constData(), ch, remch, chs); + + bool u1 = synth->iUsedIdx[remch]; + if(chs >= 2) + { + bool u2 = synth->iUsedIdx[remch + 1]; + if(u1 && u2) + ((MusECore::AudioTrack*)i->track)->addData(pos, chs, ch, -1, nframes, &audioInBuffers[remch]); + else + if(!u1 && !u2) + ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &audioInBuffers[remch]); + else + { + if(u1) + ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, 1, nframes, &audioInBuffers[remch]); + else + ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, 1, nframes, &audioInBuffers[remch]); + + if(u2) + ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch + 1, 1, nframes, &audioInBuffers[remch + 1]); + else + ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch + 1, 1, nframes, &audioInBuffers[remch + 1]); + } + } + else + { + if(u1) + ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, -1, nframes, &audioInBuffers[remch]); + else + ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, -1, nframes, &audioInBuffers[remch]); + } + + int h = remch + chs; + for(int j = remch; j < h; ++j) + { + //printf(" setting used idx:%d\n", j); + synth->iUsedIdx[j] = true; + } + } + } + } + // Process automation control values now. // TODO: This needs to be respect frame resolution. Put this inside the sample loop below. if(MusEGlobal::automation && synti && synti->automationType() != AUTO_OFF && id() != -1) @@ -1507,11 +1598,11 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP } } - while(sample < n) + while(sample < nframes) { //unsigned long nsamp = n; //unsigned long nsamp = n - sample; - unsigned long nsamp = usefixedrate ? fixedsize : n - sample; + unsigned long nsamp = usefixedrate ? fixedsize : nframes - sample; bool found = false; unsigned long frame = 0; unsigned long index = 0; @@ -1525,7 +1616,7 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP // The events happened in the last period or even before that. Shift into this period with + n. This will sync with MusEGlobal::audio. // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. //evframe = (pos + frameOffset > v.frame + n) ? 0 : v.frame - pos - frameOffset + n; - evframe = (syncFrame > v.frame + n) ? 0 : v.frame - syncFrame + n; + evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; // Protection. Observed this condition. Why? Supposed to be linear timestamps. if(found && evframe < frame) { @@ -1545,7 +1636,7 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP //if(v.frame < startPos || v.frame >= (endPos + frameOffset) //if(evframe < sample || evframe >= n //if(evframe < sample || evframe >= (n + frameOffset) - if(evframe >= n + if(evframe >= nframes //|| (found && v.frame != frame) //|| (!usefixedrate && found && !v.unique && v.frame != frame) //|| (found && !v.unique && evframe != frame) @@ -1567,6 +1658,31 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP index = v.idx; // Set the ladspa control port value. controls[v.idx].val = v.value; + + // Need to update the automation value, otherwise it overwrites later with the last MusEGlobal::automation value. + if(id() != -1) + { + // Since we are now in the audio thread context, there's no need to send a message, + // just modify directly. + //MusEGlobal::audio->msgSetPluginCtrlVal(_track, genACnum(_id, k), controls[k].val); + synti->setPluginCtrlVal(genACnum(id(), v.idx), v.value); + + // Record automation. + // NO! Take care of this immediately in the OSC control handler, because we don't want + // any delay. + // OTOH Since this is the actual place and time where the control ports values + // are set, best to reflect what happens here to automation. + // However for dssi-vst it might be best to handle it that way. + + //AutomationType at = _track->automationType(); + // TODO: Taken from our native gui control handlers. + // This may need modification or may cause problems - + // we don't have the luxury of access to the dssi gui controls ! + //if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) + // enableController(k, false); + //_track->recordAutomation(id, v.value); + } + } // Process automation control values now. @@ -1583,8 +1699,8 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP if(found && !usefixedrate) //nsamp = frame - sample + 1; nsamp = frame - sample; - if(sample + nsamp >= n) // Safety check. - nsamp = n - sample; + if(sample + nsamp >= nframes) // Safety check. + nsamp = nframes - sample; //printf("DssiSynthIF::getData n:%d frame:%lu sample:%lu nsamp:%lu pos:%d fOffset:%d syncFrame:%lu loopcount:%d\n", // n, frame, sample, nsamp, pos, frameOffset, syncFrame, loopcount); // REMOVE Tim. @@ -1596,10 +1712,10 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP nevents = 0; // Process event list events... - for(; i != el->end(); ++i) + for(; start_event != el->end(); ++start_event) { //if(i->time() >= (endPos + frameOffset)) // NOTE: frameOffset? Tested, examined printouts of times: Seems OK for playback. - if(i->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again... + if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again... break; #ifdef DSSI_DEBUG @@ -1611,40 +1727,40 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP if(synti->midiPort() != -1) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[synti->midiPort()]; - if(i->type() == MusECore::ME_CONTROLLER) + if(start_event->type() == MusECore::ME_CONTROLLER) { - int da = i->dataA(); - int db = i->dataB(); + int da = start_event->dataA(); + int db = start_event->dataB(); db = mp->limitValToInstrCtlRange(da, db); - if(!mp->setHwCtrlState(i->channel(), da, db)) + if(!mp->setHwCtrlState(start_event->channel(), da, db)) continue; } else - if(i->type() == MusECore::ME_PITCHBEND) + if(start_event->type() == MusECore::ME_PITCHBEND) { - int da = mp->limitValToInstrCtlRange(MusECore::CTRL_PITCH, i->dataA()); - if(!mp->setHwCtrlState(i->channel(), MusECore::CTRL_PITCH, da)) + int da = mp->limitValToInstrCtlRange(MusECore::CTRL_PITCH, start_event->dataA()); + if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PITCH, da)) continue; } else - if(i->type() == MusECore::ME_PROGRAM) + if(start_event->type() == MusECore::ME_PROGRAM) { - if(!mp->setHwCtrlState(i->channel(), MusECore::CTRL_PROGRAM, i->dataA())) + if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PROGRAM, start_event->dataA())) continue; } } // Returns false if the event was not filled. It was handled, but some other way. - if(processEvent(*i, &events[nevents])) + if(processEvent(*start_event, &events[nevents])) { // Time-stamp the event. p4.0.15 Tim. - int ft = i->time() - frameOffset - pos; + int ft = start_event->time() - frameOffset - pos; if(ft < 0) ft = 0; //if (ft >= (int)MusEGlobal::segmentSize) if (ft >= int(sample + nsamp)) { - printf("DssiSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", i->time(), pos, frameOffset, ft, sample, nsamp); + printf("DssiSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", start_event->time(), pos, frameOffset, ft, sample, nsamp); ///if (ft > (int)MusEGlobal::segmentSize) //ft = MusEGlobal::segmentSize - 1; ft = sample + nsamp - 1; @@ -1696,61 +1812,6 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP } } - /* - // - // p3.3.39 Handle inputs... - // - //if((MusEGlobal::song->bounceTrack != this) && !noInRoute()) - if(!((MusECore::AudioTrack*)synti)->noInRoute()) - { - RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes(); - iRoute i = irl->begin(); - if(!i->track->isMidiTrack()) - { - //if(MusEGlobal::debugMsg) - printf("DssiSynthIF::getData: Error: First route is a midi track route!\n"); - } - else - { - int ch = i->channel == -1 ? 0 : i->channel; - int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; - int chs = i->channels == -1 ? 0 : i->channels; - - // TODO: - //if(ch >= synth->_inports) - //iUsedIdx[ch] = true; - //if(chs == 2) - // iUsedIdx[ch + 1] = true; - - //((MusECore::AudioTrack*)i->track)->copyData(framePos, channels, nframe, bp); - ((MusECore::AudioTrack*)i->track)->copyData(pos, ports, - //(i->track->type() == Track::AUDIO_SOFTSYNTH && i->channel != -1) ? i->channel : 0, - i->channel, - i->channels, - n, bp); - } - - //unsigned pos, int ports, unsigned n, float** buffer - - ++i; - for(; i != irl->end(); ++i) - { - if(i->track->isMidiTrack()) - { - //if(MusEGlobal::debugMsg) - printf("DssiSynthIF::getData: Error: Route is a midi track route!\n"); - continue; - } - //((MusECore::AudioTrack*)i->track)->addData(framePos, channels, nframe, bp); - ((MusECore::AudioTrack*)i->track)->addData(framePos, channels, - //(i->track->type() == Track::AUDIO_SOFTSYNTH && i->channel != -1) ? i->channel : 0, - i->channel, - i->channels, - nframe, bp); - } - } - */ - k = 0; // Connect the given buffers directly to the ports, up to a max of synth ports. for(; k < nop; ++k) @@ -1758,9 +1819,21 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP // Connect the remaining ports to some local buffers (not used yet). for(; k < synth->_outports; ++k) descr->connect_port(handle, synth->oIdx[k], audioOutBuffers[k] + sample); - // Just connect all inputs to some local buffers (not used yet). TODO: Support inputs. + // Connect all inputs either to some local buffers, or a silence buffer. for(k = 0; k < synth->_inports; ++k) - descr->connect_port(handle, synth->iIdx[k], audioInBuffers[k] + sample); + { + //printf(" k:%d synth->iIdx[k]:%d\n", k, synth->iIdx[k]); + if(synth->iUsedIdx[k]) + { + synth->iUsedIdx[k] = false; // Reset + descr->connect_port(handle, synth->iIdx[k], audioInBuffers[k] + sample); + } + else + { + //printf(" input used size:%ld idx:%ld = %d silencing...\n", synth->iUsedIdx.size(), k, synth->iUsedIdx[k]); + descr->connect_port(handle, synth->iIdx[k], audioInSilenceBuf + sample); + } + } // Run the synth for a period of time. This processes events and gets/fills our local buffers... if(synth->dssi->run_synth) @@ -1772,10 +1845,10 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP snd_seq_event_t* ev = events; synth->dssi->run_multiple_synths(1, &handle, nsamp, &ev, &nevents); } + // TIP: Until we add programs to plugins, uncomment these four checks to load dssi effects as synths, in order to have programs. //else //if(synth->dssi->LADSPA_Plugin->run) //{ - // // Just a test, worked OK. // synth->dssi->LADSPA_Plugin->run(handle, nsamp); //} @@ -1783,14 +1856,13 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP loopcount++; // REMOVE Tim. } - return i; + return start_event; } //--------------------------------------------------------- // putEvent //--------------------------------------------------------- -//bool DssiSynthIF::putEvent(const MidiEvent& ev) bool DssiSynthIF::putEvent(const MusECore::MidiPlayEvent& ev) { #ifdef DSSI_DEBUG @@ -2033,16 +2105,37 @@ int DssiSynthIF::oscControl(unsigned long port, float value) fprintf(stderr, "DssiSynthIF::oscControl: fifo overflow: in control number:%lu\n", cport); } - MusECore::ciMidiCtl2LadspaPort ip = synth->port2MidiCtlMap.find(cport); - if(ip != synth->port2MidiCtlMap.end()) + // Record automation: + // Take care of this immediately, because we don't want the silly delay associated with + // processing the fifo one-at-a-time in the apply(). + // NOTE: With some vsts we don't receive control events until the user RELEASES a control. + // So the events all arrive at once when the user releases a control. + // That makes this pretty useless... But what the heck... + if(id() != -1) { + //int id = genACnum(_id, cport); + unsigned long pid = genACnum(id(), cport); + AutomationType at = synti->automationType(); + + // TODO: Taken from our native gui control handlers. + // This may need modification or may cause problems - + // we don't have the luxury of access to the dssi gui controls ! + if(at == AUTO_WRITE || (MusEGlobal::audio->isPlaying() && at == AUTO_TOUCH)) + enableController(cport, false); + + synti->recordAutomation(pid, value); + } + + //MusECore::ciMidiCtl2LadspaPort ip = synth->port2MidiCtlMap.find(cport); + //if(ip != synth->port2MidiCtlMap.end()) + //{ // TODO: TODO: Update midi MusE's midi controller knobs, sliders, boxes etc with a call to the midi port's setHwCtrlState() etc. // But first we need a ladspa2MidiValue() function! ... // // //float val = ladspa2MidiValue(ld, i, ?, ?); - } + //} return 0; } @@ -2064,8 +2157,8 @@ int DssiSynthIF::oscMidi(int a, int b, int c) { MusECore::MidiPlayEvent event(0, port, channel, a, b, c); - #ifdef DSSI_DEBUG - printf(stderr, "DssiSynthIF::oscMidi midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB()); + #ifdef DSSI_DEBUG + printf("DssiSynthIF::oscMidi midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB()); #endif MusEGlobal::midiPorts[port].sendEvent(event); @@ -2329,7 +2422,8 @@ QString DssiSynthIF::name() const { return synti->nam QString DssiSynthIF::lib() const { return synth ? synth->completeBaseName() : QString(); } QString DssiSynthIF::dirPath() const { return synth ? synth->absolutePath() : QString(); } QString DssiSynthIF::fileName() const { return synth ? synth->fileName() : QString(); } -MusECore::AudioTrack* DssiSynthIF::track() { return (MusECore::AudioTrack*)synti; } +QString DssiSynthIF::titlePrefix() const { return QString(); } +MusECore::AudioTrack* DssiSynthIF::track() { return (MusECore::AudioTrack*)synti; } void DssiSynthIF::enableController(unsigned long i, bool v) { controls[i].enCtrl = v; } bool DssiSynthIF::controllerEnabled(unsigned long i) const { return controls[i].enCtrl; } bool DssiSynthIF::controllerEnabled2(unsigned long i) const { return controls[i].en2Ctrl; } diff --git a/muse2/muse/dssihost.h b/muse2/muse/dssihost.h index 9b00b642..2836d72e 100644 --- a/muse2/muse/dssihost.h +++ b/muse2/muse/dssihost.h @@ -76,7 +76,8 @@ class DssiSynth : public Synth { unsigned long _portCount, _inports, _outports, _controlInPorts, _controlOutPorts; std::vector<unsigned long> iIdx; // Audio input index to port number. std::vector<unsigned long> oIdx; // Audio output index to port number. - std::vector<bool> iUsedIdx; // During process, tells whether an audio input port was used by any input routes. + //std::vector<bool> iUsedIdx; // During process, tells whether an audio input port was used by any input routes. + std::vector<int> iUsedIdx; // During process, tells whether an audio input port was used by any input routes. std::vector<unsigned long> rpIdx; // Port number to control input index. Item is -1 if it's not a control input. MusECore::MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to DSSI port numbers. MusECore::MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps DSSI port numbers to midi controller numbers. @@ -88,6 +89,8 @@ class DssiSynth : public Synth { public: DssiSynth(QFileInfo&, const DSSI_Descriptor*); // removed const for QFileInfo virtual ~DssiSynth(); + virtual Type synthType() const { return DSSI_SYNTH; } + virtual void incInstances(int); virtual SynthIF* createSIF(SynthI*); @@ -122,6 +125,7 @@ class DssiSynthIF : public SynthIF, public PluginIBase float** audioInBuffers; float** audioOutBuffers; + float* audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. public: DssiSynthIF(SynthI* s); @@ -195,6 +199,7 @@ class DssiSynthIF : public SynthIF, public PluginIBase QString lib() const; QString dirPath() const; QString fileName() const; + QString titlePrefix() const; MusECore::AudioTrack* track(); void enableController(unsigned long /*i*/, bool v = true); bool controllerEnabled(unsigned long /*i*/) const; diff --git a/muse2/muse/gconfig.cpp b/muse2/muse/gconfig.cpp index bb9f42f4..0abbbd76 100644 --- a/muse2/muse/gconfig.cpp +++ b/muse2/muse/gconfig.cpp @@ -129,8 +129,8 @@ GlobalConfigValues config = { -60.0, // double minSlider; false, // use Jack freewheel 20, // int guiRefresh; - QString(""), // userInstrumentsDir - //QString(""), // helpBrowser; // Obsolete + QString(""), // userInstrumentsDir // Obsolete. Must keep for compatibility. + //QString(""), // helpBrowser; // Obsolete true, // extendedMidi 384, // division for smf export QString(""), // copyright string for smf export @@ -187,7 +187,12 @@ GlobalConfigValues config = { 64, // minControlProcessPeriod false, // popupsDefaultStayOpen false, // leftMouseButtonCanDecrease - false // rangeMarkerWithoutMMB + false, // rangeMarkerWithoutMMB + true, // addHiddenTracks + true // unhideTracks + }; +//GlobalConfigValues globalConfig = config; + } // namespace MusEGlobal diff --git a/muse2/muse/gconfig.h b/muse2/muse/gconfig.h index d9038ea1..95b88170 100644 --- a/muse2/muse/gconfig.h +++ b/muse2/muse/gconfig.h @@ -109,7 +109,7 @@ struct GlobalConfigValues { double minSlider; bool freewheelMode; int guiRefresh; - QString userInstrumentsDir; + QString userInstrumentsDir; // Obsolete. Must keep for compatibility. bool extendedMidi; // extended smf format int midiDivision; // division for smf export @@ -164,6 +164,9 @@ struct GlobalConfigValues { bool popupsDefaultStayOpen; bool leftMouseButtonCanDecrease; bool rangeMarkerWithoutMMB; + bool addHiddenTracks; + bool unhideTracks; + }; diff --git a/muse2/muse/globals.cpp b/muse2/muse/globals.cpp index 05ba1011..d50ea1eb 100644 --- a/muse2/muse/globals.cpp +++ b/muse2/muse/globals.cpp @@ -165,6 +165,12 @@ const char* med_file_save_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; +const char* project_create_file_save_pattern[] = { + QT_TRANSLATE_NOOP("file_patterns", "Uncompressed med Files (*.med)"), + QT_TRANSLATE_NOOP("file_patterns", "gzip compressed med Files (*.med.gz)"), + QT_TRANSLATE_NOOP("file_patterns", "bzip2 compressed med Files (*.med.bz2)"), + 0 + }; const char* image_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "(*.jpg *.gif *.png)"), diff --git a/muse2/muse/globals.h b/muse2/muse/globals.h index ae624fb8..7563c171 100644 --- a/muse2/muse/globals.h +++ b/muse2/muse/globals.h @@ -94,6 +94,7 @@ extern const char* midi_file_pattern[]; extern const char* midi_file_save_pattern[]; extern const char* med_file_pattern[]; extern const char* med_file_save_pattern[]; +extern const char* project_create_file_save_pattern[]; extern const char* image_file_pattern[]; //extern const char* ctrl_file_pattern[]; extern const char* part_file_pattern[]; diff --git a/muse2/muse/helper.cpp b/muse2/muse/helper.cpp index 682bfe8e..f560fbfb 100644 --- a/muse2/muse/helper.cpp +++ b/muse2/muse/helper.cpp @@ -20,6 +20,8 @@ // //========================================================= +#include <list> + #include "helper.h" #include "part.h" #include "track.h" @@ -28,8 +30,22 @@ #include "icons.h" #include "synth.h" #include "functions.h" +#include "gconfig.h" + +#include "driver/jackmidi.h" +#include "route.h" +#include "mididev.h" +#include "globaldefs.h" +#include "audio.h" +#include "audiodev.h" +#include "midiseq.h" #include <QApplication> +#include <QDir> +#include <QFileInfo> +#include <QFileDialog> +#include <QString> +//#include <QTemporaryFile> #ifdef DSSI_SUPPORT #include "dssihost.h" @@ -118,170 +134,56 @@ QMenu* populateAddSynth(QWidget* parent) //typedef std::multimap<std::string, int, addSynth_cmp_str >::iterator imap; typedef std::multimap<std::string, int >::iterator imap; - MusECore::MessSynth* synMESS = 0; - QMenu* synpMESS = 0; - asmap mapMESS; - - #ifdef DSSI_SUPPORT - MusECore::DssiSynth* synDSSI = 0; - QMenu* synpDSSI = 0; - asmap mapDSSI; - #endif - #ifdef VST_SUPPORT - MusECore::VstSynth* synVST = 0; - QMenu* synpVST = 0; - asmap mapVST; - #endif + int ntypes = MusECore::Synth::SYNTH_TYPE_END; + asmap smaps[ntypes]; + QMenu* mmaps[ntypes]; + for(int itype = 0; itype < ntypes; ++itype) + mmaps[itype] = 0; - // Not necessary, but what the heck. - QMenu* synpOther = 0; - asmap mapOther; + MusECore::Synth* synth; + MusECore::Synth::Type type; - //const int synth_base_id = 0x1000; int ii = 0; for(std::vector<MusECore::Synth*>::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) { - synMESS = dynamic_cast<MusECore::MessSynth*>(*i); - if(synMESS) - { - mapMESS.insert( std::pair<std::string, int> (std::string(synMESS->description().toLower().toLatin1().constData()), ii) ); - } - else - { - - #ifdef DSSI_SUPPORT - synDSSI = dynamic_cast<MusECore::DssiSynth*>(*i); - if(synDSSI) - { - mapDSSI.insert( std::pair<std::string, int> (std::string(synDSSI->description().toLower().toLatin1().constData()), ii) ); - } - else - #endif - - { - #ifdef VST_SUPPORT - synVST = dynamic_cast<MusECore::VstSynth*>(*i); - if(synVST) - { - mapVST.insert( std::pair<std::string, int> (std::string(synVST->description().toLower().toLatin1().constData()), ii) ); - } - else - #endif - - { - mapOther.insert( std::pair<std::string, int> (std::string((*i)->description().toLower().toLatin1().constData()), ii) ); - } - } - } + synth = *i; + type = synth->synthType(); + if(type >= ntypes) + continue; + smaps[type].insert( std::pair<std::string, int> (std::string(synth->description().toLower().toLatin1().constData()), ii) ); ++ii; } int sz = MusEGlobal::synthis.size(); - for(imap i = mapMESS.begin(); i != mapMESS.end(); ++i) - { - int idx = i->second; - if(idx > sz) // Sanity check - continue; - MusECore::Synth* s = MusEGlobal::synthis[idx]; - if(s) + for(int itype = 0; itype < ntypes; ++itype) + { + for(imap i = smaps[itype].begin(); i != smaps[itype].end(); ++i) { - // No MESS sub-menu yet? Create it now. - if(!synpMESS) - synpMESS = new QMenu(parent); - QAction* sM = synpMESS->addAction(s->description() + " <" + s->name() + ">"); - sM->setData(MENU_ADD_SYNTH_ID_BASE + idx); - } - } - - #ifdef DSSI_SUPPORT - for(imap i = mapDSSI.begin(); i != mapDSSI.end(); ++i) - { - int idx = i->second; - if(idx > sz) - continue; - MusECore::Synth* s = MusEGlobal::synthis[idx]; - if(s) - { - // No DSSI sub-menu yet? Create it now. - if(!synpDSSI) - synpDSSI = new QMenu(parent); - //synpDSSI->insertItem(QT_TRANSLATE_NOOP("@default", s->description()) + " <" + QT_TRANSLATE_NOOP("@default", s->name()) + ">", MENU_ADD_SYNTH_ID_BASE + idx); - QAction* sD = synpDSSI->addAction(s->description() + " <" + s->name() + ">"); - sD->setData(MENU_ADD_SYNTH_ID_BASE + idx); - } - } - #endif - - #ifdef VST_SUPPORT - for(imap i = mapVST.begin(); i != mapVST.end(); ++i) - { - int idx = i->second; - if(idx > sz) - continue; - Synth* s = MusEGlobal::synthis[idx]; - if(s) - { - // No VST sub-menu yet? Create it now. - if(!synpVST) - synpVST = new QMenu(parent); - QAction* sV = synpVST->addAction(s->description() + " <" + "@default", s->name() + ">"); - sV->setData(MENU_ADD_SYNTH_ID_BASE + idx); - } - } - #endif - - for(imap i = mapOther.begin(); i != mapOther.end(); ++i) - { - int idx = i->second; - if(idx > sz) - continue; - MusECore::Synth* s = MusEGlobal::synthis[idx]; - // No Other sub-menu yet? Create it now. - if(!synpOther) - synpOther = new QMenu(parent); - //synpOther->insertItem(QT_TRANSLATE_NOOP("@default", s->description()) + " <" + QT_TRANSLATE_NOOP("@default", s->name()) + ">", MENU_ADD_SYNTH_ID_BASE + idx); - QAction* sO = synpOther->addAction(s->description() + " <" + s->name() + ">"); - sO->setData(MENU_ADD_SYNTH_ID_BASE + idx); - } - - if(synpMESS) - { - synpMESS->setIcon(*synthIcon); - synpMESS->setTitle("MESS"); - synp->addMenu(synpMESS); - } - - #ifdef DSSI_SUPPORT - if(synpDSSI) - { - synpDSSI->setIcon(*synthIcon); - synpDSSI->setTitle("DSSI"); - synp->addMenu(synpDSSI); - } - #endif - - #ifdef VST_SUPPORT - if(synpVST) - { - synpVST->setIcon(*synthIcon); - synpVST->setTitle("FST"); - synp->addMenu(synpVST); - } - #endif - - if(synpOther) - { - synpOther->setIcon(*synthIcon); - synpOther->setTitle(QObject::tr("Other")); - synp->addMenu(synpOther); + int idx = i->second; + if(idx > sz) // Sanity check + continue; + synth = MusEGlobal::synthis[idx]; + if(synth) + { + // No sub-menu yet? Create it now. + if(!mmaps[itype]) + { + mmaps[itype] = new QMenu(parent); + mmaps[itype]->setIcon(*synthIcon); + mmaps[itype]->setTitle(MusECore::synthType2String((MusECore::Synth::Type)itype)); + synp->addMenu(mmaps[itype]); + } + QAction* act = mmaps[itype]->addAction(synth->description() + " <" + synth->name() + ">"); + act->setData( MENU_ADD_SYNTH_ID_BASE * (itype + 1) + idx ); + } + } } - + return synp; } - //--------------------------------------------------------- // populateAddTrack // this is also used in "mixer" @@ -290,19 +192,27 @@ QMenu* populateAddSynth(QWidget* parent) QActionGroup* populateAddTrack(QMenu* addTrack, bool populateAll) { QActionGroup* grp = new QActionGroup(addTrack); + if (MusEGlobal::config.addHiddenTracks) + populateAll=true; - QAction* midi = addTrack->addAction(QIcon(*addtrack_addmiditrackIcon), + if (populateAll || MusECore::MidiTrack::visible()) { + QAction* midi = addTrack->addAction(QIcon(*addtrack_addmiditrackIcon), qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Midi Track"))); - midi->setData(MusECore::Track::MIDI); - grp->addAction(midi); - QAction* drum = addTrack->addAction(QIcon(*addtrack_drumtrackIcon), + midi->setData(MusECore::Track::MIDI); + grp->addAction(midi); + } + if (populateAll || MusECore::MidiTrack::visible()) { + QAction* drum = addTrack->addAction(QIcon(*addtrack_drumtrackIcon), qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Drum Track"))); - drum->setData(MusECore::Track::DRUM); - grp->addAction(drum); - QAction* wave = addTrack->addAction(QIcon(*addtrack_wavetrackIcon), + drum->setData(MusECore::Track::DRUM); + grp->addAction(drum); + } + if (populateAll || MusECore::WaveTrack::visible()) { + QAction* wave = addTrack->addAction(QIcon(*addtrack_wavetrackIcon), qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Wave Track"))); - wave->setData(MusECore::Track::WAVE); - grp->addAction(wave); + wave->setData(MusECore::Track::WAVE); + grp->addAction(wave); + } if (populateAll || MusECore::AudioOutput::visible()) { QAction* aoutput = addTrack->addAction(QIcon(*addtrack_audiooutputIcon), @@ -332,20 +242,47 @@ QActionGroup* populateAddTrack(QMenu* addTrack, bool populateAll) grp->addAction(aaux); } - // Create a sub-menu and fill it with found synth types. Make addTrack the owner. - QMenu* synp = populateAddSynth(addTrack); - synp->setIcon(*synthIcon); - synp->setTitle(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Synth"))); + if (populateAll || MusECore::SynthI::visible()) { + // Create a sub-menu and fill it with found synth types. Make addTrack the owner. + QMenu* synp = populateAddSynth(addTrack); + synp->setIcon(*synthIcon); + synp->setTitle(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Synth"))); + + // Add the sub-menu to the given menu. + addTrack->addMenu(synp); + } - // Add the sub-menu to the given menu. - addTrack->addMenu(synp); - //QObject::connect(addTrack, SIGNAL(triggered(QAction *)), MusEGlobal::song, SLOT(addNewTrack(QAction *))); return grp; } + +//--------------------------------------------------------- +// getFilterExtension +//--------------------------------------------------------- -} // namespace MusEGui +QString getFilterExtension(const QString &filter) +{ + // + // Return the first extension found. Must contain at least one * character. + // + + int pos = filter.indexOf('*'); + if(pos == -1) + return QString(); + + QString filt; + int len = filter.length(); + ++pos; + for( ; pos < len; ++pos) + { + QChar c = filter[pos]; + if((c == ')') || (c == ';') || (c == ',') || (c == ' ')) + break; + filt += filter[pos]; + } + return filt; +} QStringList localizedStringListFromCharArray(const char** array, const char* context) { @@ -355,3 +292,374 @@ QStringList localizedStringListFromCharArray(const char** array, const char* con return temp; } + +QString browseProjectFolder(QWidget* parent) +{ + QString path; + if(!MusEGlobal::config.projectBaseFolder.isEmpty()) + { + QDir d(MusEGlobal::config.projectBaseFolder); + path = d.absolutePath(); + } + + QString dir = QFileDialog::getExistingDirectory(parent, qApp->tr("Select project directory"), path); + if(dir.isEmpty()) + dir = MusEGlobal::config.projectBaseFolder; + // projDirLineEdit->setText(dir); + //return QFileDialog::getExistingDirectory(this, qApp.tr("Select project directory"), path); + return dir; +} + +QString projectTitleFromFilename(QString filename) +{ + int idx; + idx = filename.lastIndexOf(".med.bz2", -1, Qt::CaseInsensitive); + if(idx == -1) + idx = filename.lastIndexOf(".med.gz", -1, Qt::CaseInsensitive); + if(idx == -1) + idx = filename.lastIndexOf(".med", -1, Qt::CaseInsensitive); + + if(idx != -1) + filename.truncate(idx); + + QFileInfo fi(filename); + + //return fi.baseName(); + return fi.fileName(); +} + +QString projectPathFromFilename(QString filename) +{ + QFileInfo fi(filename); + return QDir::cleanPath(fi.absolutePath()); +} + +QString projectExtensionFromFilename(QString filename) +{ + int idx; + idx = filename.lastIndexOf(".med.bz2", -1, Qt::CaseInsensitive); + if(idx == -1) + idx = filename.lastIndexOf(".med.gz", -1, Qt::CaseInsensitive); + if(idx == -1) + idx = filename.lastIndexOf(".med", -1, Qt::CaseInsensitive); + if(idx == -1) + idx = filename.lastIndexOf(".bz2", -1, Qt::CaseInsensitive); + if(idx == -1) + idx = filename.lastIndexOf(".gz", -1, Qt::CaseInsensitive); + + return (idx == -1) ? QString() : filename.right(filename.size() - idx); +} + +QString getUniqueUntitledName() +{ + QString filename("untitled"); + //QTemporaryFile tf(MusEGlobal::config.projectBaseFolder +"/" + s + "XXXXXX.med"); + //if(tf.open()) + // s = MusEGui::projectTitleFromFilename(tf.fileName()); + + QString fbase(MusEGlobal::config.projectBaseFolder); + + QString nfb = fbase; + if(MusEGlobal::config.projectStoreInFolder) + nfb += "/" + filename; + QFileInfo fi(nfb + "/" + filename + ".med"); // TODO p4.0.40 Check other extensions. + if(!fi.exists()) + //return filename; + return fi.filePath(); + + // Find a new filename + QString nfn = filename; + int idx; + for (idx=2; idx<10000; idx++) { + QString num = QString::number(idx); + nfn = filename + "_" + num; + nfb = fbase; + if(MusEGlobal::config.projectStoreInFolder) + nfb += "/" + nfn; + QFileInfo fi(nfb + "/" + nfn + ".med"); + if(!fi.exists()) + //break; + return fi.filePath(); + } + + //if(idx >= 10000) + printf("MusE error: Could not make untitled project name (10000 or more untitled projects in project dir - clean up!\n"); + + //return nfn; + + nfb = fbase; + if(MusEGlobal::config.projectStoreInFolder) + nfb += "/" + filename; + return nfb + "/" + filename + ".med"; +} + + +#if 1 + +// ------------------------------------------------------------------------------------------------------- +// populateMidiPorts() +// This version creats separate devices for input and output ports. +// It does not attempt to pair them together. +// ------------------------------------------------------------------------------------------------------- +void populateMidiPorts() +{ + if(!MusEGlobal::checkAudioDevice()) + return; + + MusECore::MidiDevice* dev = 0; + + int port_num = 0; + + // If Jack is running, prefer Jack midi devices over ALSA. + if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::JACK_AUDIO) + { + std::list<QString> sl; + sl = MusEGlobal::audioDevice->inputPorts(true, 1); // Ask for second aliases. + for(std::list<QString>::iterator i = sl.begin(); i != sl.end(); ++i) + { + dev = MusECore::MidiJackDevice::createJackMidiDevice(*i, 1); + if(dev) + { + //printf("populateMidiPorts Created jack writeable device: %s\n", dev->name().toLatin1().constData()); + //dev->setOpenFlags(1); + MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); + MusECore::Route srcRoute(dev, -1); + MusECore::Route dstRoute(*i, true, -1, MusECore::Route::JACK_ROUTE); + MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); + if(++port_num == MIDI_PORTS) + return; + } + } + + sl = MusEGlobal::audioDevice->outputPorts(true, 1); // Ask for second aliases. + for(std::list<QString>::iterator i = sl.begin(); i != sl.end(); ++i) + { + dev = MusECore::MidiJackDevice::createJackMidiDevice(*i, 2); + if(dev) + { + //printf("populateMidiPorts Created jack readable device: %s\n", dev->name().toLatin1().constData()); + //dev->setOpenFlags(2); + MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); + MusECore::Route srcRoute(*i, false, -1, MusECore::Route::JACK_ROUTE); + MusECore::Route dstRoute(dev, -1); + MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); + if(++port_num == MIDI_PORTS) + return; + } + } + } + else + // If Jack is not running, use ALSA devices. + if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::DUMMY_AUDIO) + { + for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + if((*i)->deviceType() != MusECore::MidiDevice::ALSA_MIDI) + continue; + dev = *i; + // Select only sensible devices first - not thru etc. + //if( ... ) + // continue; + + //dev->setOpenFlags(1); + MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); + + if(++port_num == MIDI_PORTS) + return; + } + + //for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + //{ + // if((*i)->deviceType() != MusECore::MidiDevice::ALSA_MIDI) + // continue; + // // Select the ones ignored in the first pass. + // if(! ... ) + // continue; + // + // dev->setOpenFlags(1); + // MusEGlobal::midiSeq->msgSetMidiDevice(port_num, dev); + // + // if(++port_num == MIDI_PORTS) + // return; + //} + } + + //MusEGlobal::muse->changeConfig(true); // save configuration file + //MusEGlobal::song->update(); + +} + +#else + +// ------------------------------------------------------------------------------------------------------- +// populateMidiPorts() +// This version worked somewhat well with system devices. +// But no, it is virtually impossible to tell from the names whether ports should be paired. +// There is too much room for error - what markers to look for ("capture_"/"playback_") etc. +// It works kind of OK with 'seq' Jack Midi ALSA devices, but not for 'raw' which have a different +// naming structure ("in-hw-0-0-0"/"out-hw-0-0-0"). +// It also fails to combine if the ports were named by a client app, for example another instance of MusE. +// ------------------------------------------------------------------------------------------------------- + +void populateMidiPorts() +{ + if(!MusEGlobal::checkAudioDevice()) + return; + + MusECore::MidiDevice* dev = 0; + + int port_num = 0; + + // If Jack is running, prefer Jack midi devices over ALSA. + if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::JACK_AUDIO) + { + std::list<QString> wsl; + std::list<QString> rsl; + //wsl = MusEGlobal::audioDevice->inputPorts(true, 1); // Ask for second aliases. + wsl = MusEGlobal::audioDevice->inputPorts(true, 0); // Ask for first aliases. + //rsl = MusEGlobal::audioDevice->outputPorts(true, 1); // Ask for second aliases. + rsl = MusEGlobal::audioDevice->outputPorts(true, 0); // Ask for first aliases. + + for(std::list<QString>::iterator wi = wsl.begin(); wi != wsl.end(); ++wi) + { + QString ws = *wi; + int y = ws.lastIndexOf("_"); + if(y >= 1) + { + int x = ws.lastIndexOf("_", y-1); + if(x >= 0) + ws.remove(x, y - x); + } + + + bool match_found = false; + for(std::list<QString>::iterator ri = rsl.begin(); ri != rsl.end(); ++ri) + { + QString rs = *ri; + int y = rs.lastIndexOf("_"); + if(y >= 1) + { + int x = rs.lastIndexOf("_", y-1); + if(x >= 0) + rs.remove(x, y - x); + } + + // Do we have a matching pair? + if(rs == ws) + { + // Would like to remove the client name, but no, we need it as a distinguishing identifier. + //int z = ws.indexOf(":"); + //if(z >= 0) + // ws.remove(0, z + 1); + + dev = MusECore::MidiJackDevice::createJackMidiDevice(ws, 3); + if(dev) + { + //printf("populateMidiPorts Created jack writeable/readable device: %s\n", dev->name().toLatin1().constData()); + //dev->setOpenFlags(1); + MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); + MusECore::Route devRoute(dev, -1); + MusECore::Route wdstRoute(*wi, true, -1, MusECore::Route::JACK_ROUTE); + MusECore::Route rsrcRoute(*ri, false, -1, MusECore::Route::JACK_ROUTE); + MusEGlobal::audio->msgAddRoute(devRoute, wdstRoute); + MusEGlobal::audio->msgAddRoute(rsrcRoute, devRoute); + if(++port_num == MIDI_PORTS) + return; + } + + rsl.erase(ri); // Done with this read port. Remove. + match_found = true; + break; + } + } + + if(!match_found) + { + // No match was found. Create a single writeable device. + QString s = *wi; + // Would like to remove the client name, but no, we need it as a distinguishing identifier. + //int z = s.indexOf(":"); + //if(z >= 0) + // s.remove(0, z + 1); + dev = MusECore::MidiJackDevice::createJackMidiDevice(s, 1); + if(dev) + { + //printf("populateMidiPorts Created jack writeable device: %s\n", dev->name().toLatin1().constData()); + //dev->setOpenFlags(1); + MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); + MusECore::Route srcRoute(dev, -1); + MusECore::Route dstRoute(*wi, true, -1, MusECore::Route::JACK_ROUTE); + MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); + if(++port_num == MIDI_PORTS) + return; + } + } + } + + // Create the remaining readable ports as single readable devices. + for(std::list<QString>::iterator ri = rsl.begin(); ri != rsl.end(); ++ri) + { + QString s = *ri; + // Would like to remove the client name, but no, we need it as a distinguishing identifier. + //int z = s.indexOf(":"); + //if(z >= 0) + // s.remove(0, z + 1); + dev = MusECore::MidiJackDevice::createJackMidiDevice(s, 2); + if(dev) + { + //printf("populateMidiPorts Created jack readable device: %s\n", dev->name().toLatin1().constData()); + //dev->setOpenFlags(2); + MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); + MusECore::Route srcRoute(*ri, false, -1, MusECore::Route::JACK_ROUTE); + MusECore::Route dstRoute(dev, -1); + MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); + if(++port_num == MIDI_PORTS) + return; + } + } + } + else + // If Jack is not running, use ALSA devices. + if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::DUMMY_AUDIO) + { + for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + { + if((*i)->deviceType() != MusECore::MidiDevice::ALSA_MIDI) + continue; + dev = *i; + // Select only sensible devices first - not thru etc. + //if( ... ) + // continue; + + //dev->setOpenFlags(1); + MusEGlobal::midiSeq->msgSetMidiDevice(&MusEGlobal::midiPorts[port_num], dev); + + if(++port_num == MIDI_PORTS) + return; + } + + //for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) + //{ + // if((*i)->deviceType() != MusECore::MidiDevice::ALSA_MIDI) + // continue; + // // Select the ones ignored in the first pass. + // if(! ... ) + // continue; + // + // dev->setOpenFlags(1); + // MusEGlobal::midiSeq->msgSetMidiDevice(port_num, dev); + // + // if(++port_num == MIDI_PORTS) + // return; + //} + } + + //MusEGlobal::muse->changeConfig(true); // save configuration file + //MusEGlobal::song->update(); + +} +#endif // populateMidiPorts + + +} // namespace MusEGui + diff --git a/muse2/muse/helper.h b/muse2/muse/helper.h index e1bf93de..f9c9a78c 100644 --- a/muse2/muse/helper.h +++ b/muse2/muse/helper.h @@ -42,8 +42,15 @@ bool any_event_selected(const std::set<Part*>&, bool in_range=false); namespace MusEGui { QMenu* populateAddSynth(QWidget* parent); QActionGroup* populateAddTrack(QMenu* addTrack, bool populateAll=false); +QStringList localizedStringListFromCharArray(const char** array, const char* context); +QString getFilterExtension(const QString &filter); +QString browseProjectFolder(QWidget* parent = 0); +QString projectTitleFromFilename(QString filename); +QString projectPathFromFilename(QString filename); +QString projectExtensionFromFilename(QString filename); +QString getUniqueUntitledName(); +void populateMidiPorts(); } -QStringList localizedStringListFromCharArray(const char** array, const char* context); #endif diff --git a/muse2/muse/instruments/editinstrument.cpp b/muse2/muse/instruments/editinstrument.cpp index 742e0626..b90b872e 100644 --- a/muse2/muse/instruments/editinstrument.cpp +++ b/muse2/muse/instruments/editinstrument.cpp @@ -407,27 +407,10 @@ void EditInstrument::saveAs() if(!QDir(MusEGlobal::museUserInstruments).exists()) { - if(QMessageBox::question(this, - tr("MusE:"), - tr("The user instrument directory\n%1\ndoes not exist yet. Create it now?\n").arg(MusEGlobal::museUserInstruments) + - tr("(You can change the user instruments directory at Settings->Global Settings->Midi)"), - QMessageBox::Ok | QMessageBox::Default, - QMessageBox::Cancel | QMessageBox::Escape, - Qt::NoButton) == QMessageBox::Ok) - { - if(QDir().mkdir(MusEGlobal::museUserInstruments)) - printf("Created user instrument directory: %s\n", MusEGlobal::museUserInstruments.toLatin1().constData()); - else - { - printf("Unable to create user instrument directory: %s\n", MusEGlobal::museUserInstruments.toLatin1().constData()); - QMessageBox::critical(this, tr("MusE:"), tr("Unable to create user instrument directory '%1'").arg(MusEGlobal::museUserInstruments)); - //return; - path = MusEGlobal::museUser; - } - } - else - // return; - path = MusEGlobal::museUser; + printf("MusE Error! User instrument directory: %s does not exist. Should be created at startup!\n", MusEGlobal::museUserInstruments.toLatin1().constData()); + + //path = MusEGlobal::museUser; + //path = MusEGlobal::configPath; } //if (instrument->filePath().isEmpty()) @@ -699,27 +682,10 @@ void EditInstrument::fileSaveAs() if(!QDir(MusEGlobal::museUserInstruments).exists()) { - if(QMessageBox::question(this, - tr("MusE:"), - tr("The user instrument directory\n%1\ndoes not exist yet. Create it now?\n").arg(MusEGlobal::museUserInstruments) + - tr("(You can change the user instruments directory at Settings->Global Settings->Midi)"), - QMessageBox::Ok | QMessageBox::Default, - QMessageBox::Cancel | QMessageBox::Escape, - Qt::NoButton) == QMessageBox::Ok) - { - if(QDir().mkdir(MusEGlobal::museUserInstruments)) - printf("Created user instrument directory: %s\n", MusEGlobal::museUserInstruments.toLatin1().constData()); - else - { - printf("Unable to create user instrument directory: %s\n", MusEGlobal::museUserInstruments.toLatin1().constData()); - QMessageBox::critical(this, tr("MusE:"), tr("Unable to create user instrument directory '%1'").arg(MusEGlobal::museUserInstruments)); - //return; - path = MusEGlobal::museUser; - } - } - else - // return; - path = MusEGlobal::museUser; + printf("MusE Error! User instrument directory: %s does not exist. Should be created at startup!\n", MusEGlobal::museUserInstruments.toLatin1().constData()); + + //path = MusEGlobal::museUser; + //path = MusEGlobal::configPath; } path += QString("/%1.idf").arg(so); diff --git a/muse2/muse/instruments/minstrument.cpp b/muse2/muse/instruments/minstrument.cpp index 8e8bb6c3..22ed3737 100644 --- a/muse2/muse/instruments/minstrument.cpp +++ b/muse2/muse/instruments/minstrument.cpp @@ -936,8 +936,69 @@ QString MidiInstrument::getPatchName(int channel, int prog, MType mode, bool dru // populatePatchPopup //--------------------------------------------------------- +void MidiInstrument::populatePatchPopup(MusEGui::PopupMenu* menu, int chan, MType songType, bool drum) + { + menu->clear(); + int mask = 0; + bool drumchan = chan == 9; + switch (songType) { + case MT_XG: mask = 4; break; + case MT_GS: mask = 2; break; + case MT_GM: + if(drumchan) + { + int id = (0xff << 16) + (0xff << 8) + 0x00; // First patch + QAction* act = menu->addAction(gmdrumname); + //act->setCheckable(true); + act->setData(id); + return; + } + mask = 1; + break; + case MT_UNKNOWN: mask = 7; break; + } + if (pg.size() > 1) { + for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { + PatchGroup* pgp = *i; + //QMenu* pm = menu->addMenu(pgp->name); + MusEGui::PopupMenu* pm = new MusEGui::PopupMenu(pgp->name, menu, menu->stayOpen()); // Use the parent stayOpen here. + menu->addMenu(pm); + pm->setFont(MusEGlobal::config.fonts[0]); + const PatchList& pl = pgp->patches; + for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { + const Patch* mp = *ipl; + if ((mp->typ & mask) && + ((drum && songType != MT_GM) || + (mp->drum == drumchan)) ) + { + int id = ((mp->hbank & 0xff) << 16) + + ((mp->lbank & 0xff) << 8) + (mp->prog & 0xff); + QAction* act = pm->addAction(mp->name); + //act->setCheckable(true); + act->setData(id); + } + + } + } + } + else if (pg.size() == 1 ){ + // no groups + const PatchList& pl = pg.front()->patches; + for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { + const Patch* mp = *ipl; + if (mp->typ & mask) { + int id = ((mp->hbank & 0xff) << 16) + + ((mp->lbank & 0xff) << 8) + (mp->prog & 0xff); + QAction* act = menu->addAction(mp->name); + //act->setCheckable(true); + act->setData(id); + } + } + } + } // namespace MusECore +/* namespace MusEGui { void populatePatchPopup(MusECore::MidiInstrument* midiInstrument, PopupMenu* menu, int chan, MType songType, bool drum) @@ -1000,5 +1061,6 @@ void populatePatchPopup(MusECore::MidiInstrument* midiInstrument, PopupMenu* men } } } +*/ } // namespace MusEGui diff --git a/muse2/muse/instruments/minstrument.h b/muse2/muse/instruments/minstrument.h index 3a645d7f..385e67b4 100644 --- a/muse2/muse/instruments/minstrument.h +++ b/muse2/muse/instruments/minstrument.h @@ -143,7 +143,7 @@ class MidiInstrument { virtual void reset(int, MType); virtual QString getPatchName(int,int,MType,bool); //virtual void populatePatchPopup(QMenu*, int, MType, bool); - //virtual void populatePatchPopup(MusEGui::PopupMenu*, int, MType, bool); + virtual void populatePatchPopup(MusEGui::PopupMenu*, int, MType, bool); void read(Xml&); void write(int level, Xml&); @@ -171,8 +171,8 @@ extern void removeMidiInstrument(const MidiInstrument* instr); } // namespace MusECore -namespace MusEGui { -extern void populatePatchPopup(MusECore::MidiInstrument*, PopupMenu*, int, MType, bool); -} +//namespace MusEGui { +//extern void populatePatchPopup(MusECore::MidiInstrument*, PopupMenu*, int, MType, bool); +//} #endif diff --git a/muse2/muse/liste/editevent.cpp b/muse2/muse/liste/editevent.cpp index bca5729b..214f74c2 100644 --- a/muse2/muse/liste/editevent.cpp +++ b/muse2/muse/liste/editevent.cpp @@ -871,20 +871,17 @@ void EditCtrlDialog::instrPopup() int port = track->outPort(); MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); - ///instr->populatePatchPopup(pop, channel, MusEGlobal::song->mtype(), track->type() == MusECore::Track::DRUM); //QMenu* pup = new QMenu(this); MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); - populatePatchPopup(instr, pup, channel, MusEGlobal::song->mtype(), track->type() == MusECore::Track::DRUM); + //populatePatchPopup(instr, pup, channel, MusEGlobal::song->mtype(), track->type() == MusECore::Track::DRUM); + instr->populatePatchPopup(pup, channel, MusEGlobal::song->mtype(), track->type() == MusECore::Track::DRUM); - ///if(pop->actions().count() == 0) - /// return; if(pup->actions().count() == 0) { delete pup; return; } - ///QAction* rv = pop->exec(patchName->mapToGlobal(QPoint(10,5))); QAction* rv = pup->exec(patchName->mapToGlobal(QPoint(10,5))); if (rv) { val = rv->data().toInt(); diff --git a/muse2/muse/liste/listedit.cpp b/muse2/muse/liste/listedit.cpp index f6a77b85..6795cedb 100644 --- a/muse2/muse/liste/listedit.cpp +++ b/muse2/muse/liste/listedit.cpp @@ -45,6 +45,7 @@ #include "event.h" #include "midiport.h" #include "midictrl.h" +#include "app.h" namespace MusEGui { @@ -616,6 +617,8 @@ ListEdit::ListEdit(MusECore::PartList* pl) initShortcuts(); setWindowTitle("MusE: List Editor"); + + MusEGlobal::muse->topwinMenuInited(this); } //--------------------------------------------------------- diff --git a/muse2/muse/main.cpp b/muse2/muse/main.cpp index 0353d0d6..d946bf00 100644 --- a/muse2/muse/main.cpp +++ b/muse2/muse/main.cpp @@ -24,6 +24,8 @@ #include <QApplication> #include <QDir> #include <QFile> +#include <QFileInfo> +#include <QFileInfoList> #include <QKeyEvent> #include <QMessageBox> #include <QLocale> @@ -31,6 +33,8 @@ #include <QTimer> #include <QTranslator> #include <QIcon> +#include <QString> +#include <QStringList> #include <sys/mman.h> #include <alsa/asoundlib.h> @@ -41,6 +45,7 @@ #include "audiodev.h" #include "gconfig.h" #include "globals.h" +#include "helper.h" #include "icons.h" #include "sync.h" #include "functions.h" @@ -267,9 +272,38 @@ int main(int argc, char* argv[]) if (! cPath.exists()) cPath.mkpath("."); + // Create user templates dir if it doesn't exist + QDir utemplDir = QDir(MusEGlobal::configPath + QString("/templates")); + if(!utemplDir.exists()) + { + utemplDir.mkpath("."); + // Support old versions: Copy existing templates over. + QDir old_utemplDir = QDir(QString(getenv("HOME")) + QString("/templates")); + // printf(" old templates dir:%s\n", (QString(getenv("HOME")) + QString("/templates")).toLatin1().constData()); + if(old_utemplDir.exists()) + { + //printf(" found old templates dir\n"); + // We really just want these, even though it's possible other filenames were saved. + // Another application might have used that directory. + QStringList flt; flt << "*.med" << "*.med.gz" << "*.med.bz2" << "*.mid" << "*.midi" << "*.kar"; + old_utemplDir.setNameFilters(flt); + + QFileInfoList fil = old_utemplDir.entryInfoList(); + QFileInfo fi; + foreach(fi, fil) + { + QString fn = fi.fileName(); + QFile f(fi.absoluteFilePath()); + f.copy(utemplDir.absolutePath() + "/" + fn); + //printf(" copy old template to:%s result:%d\n", QString(utemplPath.absolutePath() + "/" + fn).toLatin1().constData(), rv); + } + } + } + QFile cConf (MusEGlobal::configName); QFile cConfTempl (MusEGlobal::museGlobalShare + QString("/templates/MusE.cfg")); - if (! cConf.exists()) + bool cConfExists = cConf.exists(); + if (!cConfExists) { printf ("creating new config...\n"); if (cConfTempl.copy(MusEGlobal::configName)) @@ -303,10 +337,51 @@ int main(int argc, char* argv[]) MusEGui::init_function_dialogs(MusEGlobal::muse); MusEGui::initShortCuts(); + MusECore::readConfiguration(); - MusEGlobal::museUserInstruments = MusEGlobal::config.userInstrumentsDir; + // Need to put a sane default here because we can't use ~ in the file name string. + if(!cConfExists) + MusEGlobal::config.projectBaseFolder = MusEGlobal::museUser + QString("/MusE"); + //MusEGlobal::museUserInstruments = MusEGlobal::config.userInstrumentsDir; + + // Create user instruments dir if it doesn't exist + { + QString uinstrPath = MusEGlobal::configPath + QString("/instruments"); + QDir uinstrDir = QDir(uinstrPath); + if(!uinstrDir.exists()) + uinstrDir.mkpath("."); + + if(!MusEGlobal::config.userInstrumentsDir.isEmpty() && MusEGlobal::config.userInstrumentsDir != uinstrPath) // Only if it is different. + { + // Support old versions: Copy existing instruments over. + QDir old_uinstrDir(MusEGlobal::config.userInstrumentsDir); + //printf(" old instruments dir:%s\n", MusEGlobal::config.userInstrumentsDir.toLatin1().constData()); + if(old_uinstrDir.exists()) + { + //printf(" found old instruments dir\n"); + QStringList flt; flt << "*.idf"; + old_uinstrDir.setNameFilters(flt); + + QFileInfoList fil = old_uinstrDir.entryInfoList(); + QFileInfo fi; + foreach(fi, fil) + { + QString fn = fi.fileName(); + QFile f(fi.absoluteFilePath()); + QFile newf(uinstrDir.absolutePath() + "/" + fn); + if(!newf.exists()) + { + f.copy(newf.fileName()); + //printf(" copy old instrument to:%s result:%d\n", newf.fileName().toLatin1().constData(), rv); + } + } + } + } + MusEGlobal::museUserInstruments = uinstrPath; + } + if (MusEGlobal::config.useDenormalBias) printf("Denormal protection enabled.\n"); // SHOW MUSE SPLASH SCREEN @@ -324,7 +399,7 @@ int main(int argc, char* argv[]) stimer->start(6000); } } - + int i; QString optstr("ahvdDmMsP:Y:l:py"); @@ -393,6 +468,7 @@ int main(int argc, char* argv[]) } */ + AL::initDsp(); if (MusEGlobal::debugMsg) @@ -438,8 +514,7 @@ int main(int argc, char* argv[]) else MusEGlobal::realTimeScheduling = MusEGlobal::audioDevice->isRealtime(); - - // What unreliable nonsense. With Jack2 this reports true even if it is not running realtime. + // ??? With Jack2 this reports true even if it is not running realtime. // Jack says: "Cannot use real-time scheduling (RR/10)(1: Operation not permitted)". The kernel is non-RT. // I cannot seem to find a reliable answer to the question, even with dummy audio and system calls. //if (MusEGlobal::debugMsg) @@ -448,11 +523,7 @@ int main(int argc, char* argv[]) MusEGlobal::useJackTransport.setValue(true); // setup the prefetch fifo length now that the segmentSize is known - // Changed by Tim. p3.3.17 - // Changed to 4 *, JUST FOR TEST!!! MusEGlobal::fifoLength = 131072 / MusEGlobal::segmentSize; - //MusEGlobal::fifoLength = (131072 / MusEGlobal::segmentSize) * 4; - argc -= optind; ++argc; @@ -498,7 +569,6 @@ int main(int argc, char* argv[]) if(MusEGlobal::loadDSSI) MusECore::initDSSI(); - // p3.3.39 MusECore::initOSC(); MusEGui::initIcons(); @@ -517,21 +587,19 @@ int main(int argc, char* argv[]) ++it; } } - + MusEGlobal::muse = new MusEGui::MusE(argc, &argv[optind]); app.setMuse(MusEGlobal::muse); MusEGlobal::muse->setWindowIcon(*MusEGui::museIcon); - - // Added by Tim. p3.3.22 if (!MusEGlobal::debugMode) { if (mlockall(MCL_CURRENT | MCL_FUTURE)) perror("WARNING: Cannot lock memory:"); } MusEGlobal::muse->show(); - MusEGlobal::muse->seqStart(); - + MusEGlobal::muse->seqStart(); + #ifdef HAVE_LASH { MusEGui::lash_client = 0; @@ -542,14 +610,28 @@ int main(int argc, char* argv[]) MusEGui::lash_client = lash_init (lash_args, muse_name, lash_flags, LASH_PROTOCOL(2,0)); lash_alsa_client_id (MusEGui::lash_client, snd_seq_client_id (MusECore::alsaSeq)); if (!noAudio) { - // p3.3.38 - //char *jack_name = ((JackAudioDevice*)MusEGlobal::audioDevice)->getJackName(); const char *jack_name = MusEGlobal::audioDevice->clientName(); lash_jack_client_name (MusEGui::lash_client, jack_name); } } } #endif /* HAVE_LASH */ + + //-------------------------------------------------- + // Auto-fill the midi ports, if appropriate. p4.0.41 + //-------------------------------------------------- + if(argc < 2 && MusEGlobal::config.startMode == 1) + { + MusEGui::populateMidiPorts(); + //MusEGlobal::muse->changeConfig(true); // save configuration file + //MusEGlobal::song->update(); + } + + //-------------------------------------------------- + // Load the default song. + //-------------------------------------------------- + MusEGlobal::muse->loadDefaultSong(argc, &argv[optind]); // p4.0.41 + QTimer::singleShot(100, MusEGlobal::muse, SLOT(showDidYouKnowDialog())); int rv = app.exec(); diff --git a/muse2/muse/marker/markerview.cpp b/muse2/muse/marker/markerview.cpp index bc9e48b9..39ce4fc0 100644 --- a/muse2/muse/marker/markerview.cpp +++ b/muse2/muse/marker/markerview.cpp @@ -26,6 +26,7 @@ #include "markerview.h" #include "xml.h" #include "globals.h" +#include "app.h" #include "sync.h" #include "icons.h" #include "song.h" @@ -157,7 +158,7 @@ void MarkerItem::setTick(unsigned v) void MarkerView::closeEvent(QCloseEvent* e) { - emit deleted(static_cast<TopWin*>(this)); + emit isDeleting(static_cast<TopWin*>(this)); emit closed(); e->accept(); } @@ -296,6 +297,8 @@ MarkerView::MarkerView(QWidget* parent) updateList(); + MusEGlobal::muse->topwinMenuInited(this); + // work around for probable QT/WM interaction bug. // for certain window managers, e.g xfce, this window is // is displayed although not specifically set to show(); diff --git a/muse2/muse/marker/markerview.h b/muse2/muse/marker/markerview.h index b50ab7a4..06e22daf 100644 --- a/muse2/muse/marker/markerview.h +++ b/muse2/muse/marker/markerview.h @@ -96,7 +96,7 @@ class MarkerView : public TopWin { void songChanged(int); signals: - void deleted(MusEGui::TopWin*); + void isDeleting(MusEGui::TopWin*); void closed(); public: diff --git a/muse2/muse/master/lmaster.cpp b/muse2/muse/master/lmaster.cpp index 52b488d0..f461e1d3 100644 --- a/muse2/muse/master/lmaster.cpp +++ b/muse2/muse/master/lmaster.cpp @@ -27,6 +27,7 @@ #include "xml.h" #include "song.h" #include "globals.h" +#include "app.h" #include "audio.h" //#include "posedit.h" //#include "sigedit.h" @@ -286,6 +287,7 @@ LMaster::LMaster() connect(keyButton, SIGNAL(clicked()), SLOT(insertKey())); initShortcuts(); + MusEGlobal::muse->topwinMenuInited(this); } //--------------------------------------------------------- diff --git a/muse2/muse/master/masteredit.cpp b/muse2/muse/master/masteredit.cpp index 191f82ab..f6169766 100644 --- a/muse2/muse/master/masteredit.cpp +++ b/muse2/muse/master/masteredit.cpp @@ -37,6 +37,7 @@ #include "doublelabel.h" ///#include "sigedit.h" #include "globals.h" +#include "app.h" #include <values.h> @@ -269,6 +270,7 @@ MasterEdit::MasterEdit() connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); initTopwinState(); + MusEGlobal::muse->topwinMenuInited(this); } //--------------------------------------------------------- diff --git a/muse2/muse/midiedit/drumedit.cpp b/muse2/muse/midiedit/drumedit.cpp index b07ebbbe..52e63da0 100644 --- a/muse2/muse/midiedit/drumedit.cpp +++ b/muse2/muse/midiedit/drumedit.cpp @@ -53,6 +53,7 @@ #include "vscale.h" #include "swidget.h" #include "globals.h" +#include "app.h" #include "icons.h" #include "filedialog.h" #include "drummap.h" @@ -535,6 +536,7 @@ DrumEdit::DrumEdit(MusECore::PartList* pl, QWidget* parent, const char* name, un initTopwinState(); + MusEGlobal::muse->topwinMenuInited(this); } //--------------------------------------------------------- diff --git a/muse2/muse/midiedit/ecanvas.cpp b/muse2/muse/midiedit/ecanvas.cpp index 9189d177..80d901bb 100644 --- a/muse2/muse/midiedit/ecanvas.cpp +++ b/muse2/muse/midiedit/ecanvas.cpp @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: ecanvas.cpp,v 1.8.2.6 2009/05/03 04:14:00 terminator356 Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -325,54 +326,54 @@ void EventCanvas::keyPress(QKeyEvent* event) } // Select items by key (PianoRoll & DrumEditor) else if (key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key) { - iCItem i, iRightmost; - CItem* rightmost = NULL; - //Get the rightmost selected note (if any) - for (i = items.begin(); i != items.end(); ++i) { - if (i->second->isSelected()) { - iRightmost = i; rightmost = i->second; - } - } - if (rightmost) { - iCItem temp = iRightmost; temp++; - //If so, deselect current note and select the one to the right - if (temp != items.end()) { - if (key != shortcuts[SHRT_SEL_RIGHT_ADD].key) - deselectAll(); - - iRightmost++; - iRightmost->second->setSelected(true); - updateSelection(); - } - } - //if (rightmost && mapx(rightmost->event().tick()) > width()) for some reason this doesn't this doesnt move the event in view - // emit followEvent(rightmost->x()); - + rciCItem i; + for (i = items.rbegin(); i != items.rend(); ++i) + if (i->second->isSelected()) + break; + + if(i == items.rend()) + i = items.rbegin(); + + if(i != items.rbegin()) + --i; + if(i->second) + { + if (key != shortcuts[SHRT_SEL_RIGHT_ADD].key) + deselectAll(); + CItem* sel = i->second; + sel->setSelected(true); + updateSelection(); + if (sel->x() + sel->width() > mapxDev(width())) + { + int mx = rmapx(sel->x()); + int newx = mx + rmapx(sel->width()) - width(); + // Leave a bit of room for the specially-drawn drum notes. But good for piano too. + emit horizontalScroll( (newx > mx ? mx - 10: newx + 10) - rmapx(xorg) ); + } + } } //Select items by key: (PianoRoll & DrumEditor) else if (key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) { - iCItem i, iLeftmost; - CItem* leftmost = NULL; - if (items.size() > 0 ) { - for (i = items.end(), i--; i != items.begin(); i--) { - if (i->second->isSelected()) { - iLeftmost = i; leftmost = i->second; - } - } - if (leftmost) { - if (iLeftmost != items.begin()) { - //Add item - if (key != shortcuts[SHRT_SEL_LEFT_ADD].key) - deselectAll(); - - iLeftmost--; - iLeftmost->second->setSelected(true); - updateSelection(); - } - } - //if (leftmost && mapx(leftmost->event().tick())< 0 ) for some reason this doesn't this doesnt move the event in view - // emit followEvent(leftmost->x()); - } + ciCItem i; + for (i = items.begin(); i != items.end(); ++i) + if (i->second->isSelected()) + break; + + if(i == items.end()) + i = items.begin(); + + if(i != items.begin()) + --i; + if(i->second) + { + if (key != shortcuts[SHRT_SEL_LEFT_ADD].key) + deselectAll(); + CItem* sel = i->second; + sel->setSelected(true); + updateSelection(); + if (sel->x() <= mapxDev(0)) + emit horizontalScroll(rmapx(sel->x() - xorg) - 10); // Leave a bit of room. + } } else if (key == shortcuts[SHRT_INC_PITCH].key) { modifySelected(NoteInfo::VAL_PITCH, 1); diff --git a/muse2/muse/midiedit/pianoroll.cpp b/muse2/muse/midiedit/pianoroll.cpp index 926f4212..8c2a7a87 100644 --- a/muse2/muse/midiedit/pianoroll.cpp +++ b/muse2/muse/midiedit/pianoroll.cpp @@ -56,6 +56,7 @@ #include "tb1.h" #include "utils.h" #include "globals.h" +#include "app.h" #include "gconfig.h" #include "icons.h" #include "audio.h" @@ -532,6 +533,7 @@ PianoRoll::PianoRoll(MusECore::PartList* pl, QWidget* parent, const char* name, } initTopwinState(); + MusEGlobal::muse->topwinMenuInited(this); } //--------------------------------------------------------- diff --git a/muse2/muse/midiedit/scoreedit.cpp b/muse2/muse/midiedit/scoreedit.cpp index b06f2487..51e1cafc 100644 --- a/muse2/muse/midiedit/scoreedit.cpp +++ b/muse2/muse/midiedit/scoreedit.cpp @@ -166,6 +166,15 @@ QColor* mycolors; // array [NUM_MYCOLORS] set<QString> ScoreEdit::names; +int ScoreCanvas::_quant_power2_init=3; +int ScoreCanvas::_pixels_per_whole_init=300; +int ScoreCanvas::note_velo_init=64; +int ScoreCanvas::note_velo_off_init=64; +int ScoreCanvas::new_len_init=0; +ScoreCanvas::coloring_mode_t ScoreCanvas::coloring_mode_init=COLOR_MODE_BLACK; +bool ScoreCanvas::preamble_contains_timesig_init=true; +bool ScoreCanvas::preamble_contains_keysig_init=true; + //--------------------------------------------------------- // ScoreEdit @@ -293,8 +302,22 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) len_actions->addAction(n32_action); len_actions->addAction(nlast_action); - nlast_action->setChecked(true); - menu_command(CMD_NOTELEN_LAST); + switch (ScoreCanvas::new_len_init) + { + case 0: nlast_action->setChecked(true); menu_command(CMD_NOTELEN_LAST); break; + case 1: n1_action->setChecked(true); menu_command(CMD_NOTELEN_1); break; + case 2: n2_action->setChecked(true); menu_command(CMD_NOTELEN_2); break; + case 4: n4_action->setChecked(true); menu_command(CMD_NOTELEN_4); break; + case 8: n8_action->setChecked(true); menu_command(CMD_NOTELEN_8); break; + case 16: n16_action->setChecked(true); menu_command(CMD_NOTELEN_16); break; + case 32: n32_action->setChecked(true); menu_command(CMD_NOTELEN_32); break; + default: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN. newLen is invalid in ScoreEdit::ScoreEdit.\n" << + " (newLen="<<ScoreCanvas::new_len_init<<"; the only valid values are 0,1,2,4,8,16 and 32)\n" << + " however, don't worry, this is no major problem, using 0 instead" << endl; + nlast_action->setChecked(true); + menu_command(CMD_NOTELEN_LAST); + } note_settings_toolbar->addSeparator(); @@ -316,7 +339,7 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) connect(velo_spinbox, SIGNAL(editingFinished()), SLOT(velo_box_changed())); connect(this,SIGNAL(velo_changed(int)), score_canvas, SLOT(set_velo(int))); note_settings_toolbar->addWidget(velo_spinbox); - velo_spinbox->setValue(64); + velo_spinbox->setValue(ScoreCanvas::note_velo_init); note_settings_toolbar->addWidget(new QLabel(tr("Off-Velocity:"), note_settings_toolbar)); velo_off_spinbox = new QSpinBox(this); @@ -327,7 +350,7 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) connect(velo_off_spinbox, SIGNAL(editingFinished()), SLOT(velo_off_box_changed())); connect(this,SIGNAL(velo_off_changed(int)), score_canvas, SLOT(set_velo_off(int))); note_settings_toolbar->addWidget(velo_off_spinbox); - velo_off_spinbox->setValue(64); + velo_off_spinbox->setValue(ScoreCanvas::note_velo_off_init); @@ -336,13 +359,16 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) quant_toolbar->addWidget(new QLabel(tr("Quantisation:"), quant_toolbar)); quant_combobox = new QComboBox(this); quant_combobox->addItem("2"); // if you add or remove items from - quant_combobox->addItem("4"); // here, also change quant_mapper[] - quant_combobox->addItem("8"); // in ScoreCanvas::set_quant()! + quant_combobox->addItem("4"); // here, also change all code regarding + quant_combobox->addItem("8"); // _quant_power2 and _quant_power2_init quant_combobox->addItem("16"); // and MAX_QUANT_POWER (must be log2(largest_value)) quant_combobox->addItem("32"); + quant_combobox->setCurrentIndex(score_canvas->quant_power2()-1); + // the above is intendedly executed BEFORE connecting. otherwise this would + // destroy pixels_per_whole_init! connect(quant_combobox, SIGNAL(currentIndexChanged(int)), score_canvas, SLOT(set_quant(int))); quant_toolbar->addWidget(quant_combobox); - quant_combobox->setCurrentIndex(2); + quant_toolbar->addSeparator(); @@ -353,7 +379,7 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) connect(px_per_whole_spinbox, SIGNAL(valueChanged(int)), score_canvas, SLOT(set_pixels_per_whole(int))); connect(score_canvas, SIGNAL(pixels_per_whole_changed(int)), px_per_whole_spinbox, SLOT(setValue(int))); quant_toolbar->addWidget(px_per_whole_spinbox); - px_per_whole_spinbox->setValue(300); + px_per_whole_spinbox->setValue(ScoreCanvas::_pixels_per_whole_init); QMenu* edit_menu = menuBar()->addMenu(tr("&Edit")); @@ -454,8 +480,18 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) menu_mapper->setMapping(color_velo_action, CMD_COLOR_VELO); menu_mapper->setMapping(color_part_action, CMD_COLOR_PART); - color_black_action->setChecked(true); - menu_command(CMD_COLOR_BLACK); + switch (ScoreCanvas::coloring_mode_init) + { + case 0: color_black_action->setChecked(true); menu_command(CMD_COLOR_BLACK); break; + case 1: color_velo_action->setChecked(true); menu_command(CMD_COLOR_VELO); break; + case 2: color_part_action->setChecked(true); menu_command(CMD_COLOR_PART); break; + default: + cerr << "ERROR: THIS SHOULD NEVER HAPPEN. noteColor is invalid in ScoreEdit::ScoreEdit.\n" << + " (noteColor="<<ScoreCanvas::coloring_mode_init<<"; the only valid values are 0,1 and 2)\n" << + " however, don't worry, this is no major problem, using 0 instead" << endl; + color_black_action->setChecked(true); + menu_command(CMD_COLOR_BLACK); + } QMenu* preamble_menu = settings_menu->addMenu(tr("Set up &preamble")); preamble_keysig_action = preamble_menu->addAction(tr("Display &key signature")); @@ -466,8 +502,8 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) preamble_keysig_action->setCheckable(true); preamble_timesig_action->setCheckable(true); - preamble_keysig_action->setChecked(true); - preamble_timesig_action->setChecked(true); + preamble_keysig_action->setChecked(ScoreCanvas::preamble_contains_keysig_init); + preamble_timesig_action->setChecked(ScoreCanvas::preamble_contains_timesig_init); QAction* set_name_action = settings_menu->addAction(tr("Set Score &name"), menu_mapper, SLOT(map())); menu_mapper->setMapping(set_name_action, CMD_SET_NAME); @@ -503,6 +539,7 @@ ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) apply_velo=true; initTopwinState(); + MusEGlobal::muse->topwinMenuInited(this); } void ScoreEdit::init_shortcuts() @@ -587,7 +624,7 @@ bool ScoreEdit::set_name(QString newname, bool emit_signal, bool emergency_name) ScoreEdit::~ScoreEdit() { - + names.erase(name); } void ScoreEdit::velo_box_changed() @@ -602,9 +639,9 @@ void ScoreEdit::velo_off_box_changed() void ScoreEdit::song_changed(int flags) { - if(_isDeleting) // Ignore while while deleting to prevent crash. - return; - + if(_isDeleting) // Ignore while while deleting to prevent crash. + return; + if (flags & (SC_SELECTION | SC_EVENT_MODIFIED | SC_EVENT_REMOVED)) { map<MusECore::Event*, MusECore::Part*> selection=get_events(score_canvas->get_all_parts(),1); @@ -673,8 +710,9 @@ void ScoreEdit::viewport_height_changed(int height) void ScoreEdit::closeEvent(QCloseEvent* e) { - _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. - + _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. + names.erase(name); + QSettings settings("MusE", "MusE-qt"); //settings.setValue("ScoreEdit/geometry", saveGeometry()); settings.setValue("ScoreEdit/windowState", saveState()); @@ -692,8 +730,8 @@ void ScoreEdit::menu_command(int cmd) { bool ok; QString newname = QInputDialog::getText(this, tr("Enter the new score title"), - tr("Enter the new score title"), QLineEdit::Normal, - name, &ok); + tr("Enter the new score title"), QLineEdit::Normal, + name, &ok); if (ok) { if (!set_name(newname)) @@ -1059,7 +1097,23 @@ void ScoreEdit::read_configuration(MusECore::Xml& xml) switch (token) { case MusECore::Xml::TagStart: - if (tag == "topwin") + if (tag=="quantPowerInit") + ScoreCanvas::_quant_power2_init=xml.parseInt(); + else if (tag=="pxPerWholeInit") + ScoreCanvas::_pixels_per_whole_init=xml.parseInt(); + else if (tag=="newNoteVeloInit") + ScoreCanvas::note_velo_init=xml.parseInt(); + else if (tag=="newNoteVeloOffInit") + ScoreCanvas::note_velo_off_init=xml.parseInt(); + else if (tag=="newLenInit") + ScoreCanvas::new_len_init=xml.parseInt(); + else if (tag=="noteColorInit") + ScoreCanvas::coloring_mode_init=(ScoreCanvas::coloring_mode_t)xml.parseInt(); + else if (tag=="preambleContainsKeysig") + ScoreCanvas::preamble_contains_keysig_init=xml.parseInt(); + else if (tag=="preambleContainsTimesig") + ScoreCanvas::preamble_contains_timesig_init=xml.parseInt(); + else if (tag == "topwin") TopWin::readConfiguration(SCORE, xml); else xml.unknown("ScoreEdit"); @@ -1079,7 +1133,18 @@ void ScoreEdit::read_configuration(MusECore::Xml& xml) void ScoreEdit::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "scoreedit"); + + xml.intTag(level, "quantPowerInit", ScoreCanvas::_quant_power2_init); + xml.intTag(level, "pxPerWholeInit", ScoreCanvas::_pixels_per_whole_init); + xml.intTag(level, "newNoteVeloInit", ScoreCanvas::note_velo_init); + xml.intTag(level, "newNoteVeloOffInit", ScoreCanvas::note_velo_off_init); + xml.intTag(level, "newLenInit", ScoreCanvas::new_len_init); + xml.intTag(level, "noteColorInit", ScoreCanvas::coloring_mode_init); + xml.intTag(level, "preambleContainsKeysig", ScoreCanvas::preamble_contains_keysig_init); + xml.intTag(level, "preambleContainsTimesig", ScoreCanvas::preamble_contains_timesig_init); + TopWin::writeConfiguration(SCORE, level, xml); + xml.etag(level, "scoreedit"); } @@ -1216,22 +1281,20 @@ ScoreCanvas::ScoreCanvas(ScoreEdit* pr, QWidget* parent_widget) : View(parent_wi dragged_event_part=NULL; last_len=384; - new_len=-1; + new_len=-1; // will be initalized with new_len_init by ScoreEdit::ScoreEdit(); - set_quant(2); //this is actually unneccessary, as while - //initalizing the quant_combobox, this gets - //called again. but for safety... - set_pixels_per_whole(300); //same as above. but safety rocks + _quant_power2=_quant_power2_init; // ScoreEdit relies on this to be done! + _pixels_per_whole_init = _pixels_per_whole_init; - set_velo(64); - set_velo_off(64); + note_velo=note_velo_init; + note_velo_off_init=note_velo_off_init; dragging_staff=false; - coloring_mode=COLOR_MODE_BLACK; - preamble_contains_keysig=true; - preamble_contains_timesig=true; + coloring_mode=coloring_mode_init; + preamble_contains_keysig=preamble_contains_keysig_init; + preamble_contains_timesig=preamble_contains_timesig_init; x_scroll_speed=0; @@ -1455,7 +1518,7 @@ void ScoreCanvas::song_changed(int flags) { if(parent && parent->deleting()) // Ignore while while deleting to prevent crash. return; - + if (flags & (SC_PART_MODIFIED | SC_PART_REMOVED | SC_PART_INSERTED | SC_TRACK_REMOVED)) { update_parts(); @@ -1534,7 +1597,7 @@ void color_image(QImage& img, const QColor& color) { uchar* ptr=img.bits(); //int bytes=img.byteCount(); - int bytes=img.bytesPerLine() * img.height(); // By Tim. For older Qt versions. Tested OK on Qt4.5. + int bytes=img.bytesPerLine() * img.height(); // By Tim. For older Qt versions. Tested OK on Qt4.5. int r,g,b; color.getRgb(&r,&g,&b); @@ -4280,16 +4343,16 @@ void ScoreCanvas::menu_command(int cmd) { switch (cmd) { - case CMD_COLOR_BLACK: coloring_mode=COLOR_MODE_BLACK; redraw(); break; - case CMD_COLOR_PART: coloring_mode=COLOR_MODE_PART; redraw(); break; - case CMD_COLOR_VELO: coloring_mode=COLOR_MODE_VELO; redraw(); break; - case CMD_NOTELEN_1: new_len=TICKS_PER_WHOLE/ 1; break; - case CMD_NOTELEN_2: new_len=TICKS_PER_WHOLE/ 2; break; - case CMD_NOTELEN_4: new_len=TICKS_PER_WHOLE/ 4; break; - case CMD_NOTELEN_8: new_len=TICKS_PER_WHOLE/ 8; break; - case CMD_NOTELEN_16: new_len=TICKS_PER_WHOLE/16; break; - case CMD_NOTELEN_32: new_len=TICKS_PER_WHOLE/32; break; - case CMD_NOTELEN_LAST: new_len=-1; break; + case CMD_COLOR_BLACK: coloring_mode_init=coloring_mode=COLOR_MODE_BLACK; redraw(); break; + case CMD_COLOR_PART: coloring_mode_init=coloring_mode=COLOR_MODE_PART; redraw(); break; + case CMD_COLOR_VELO: coloring_mode_init=coloring_mode=COLOR_MODE_VELO; redraw(); break; + case CMD_NOTELEN_1: new_len_init= 1; new_len=TICKS_PER_WHOLE/ 1; break; + case CMD_NOTELEN_2: new_len_init= 2; new_len=TICKS_PER_WHOLE/ 2; break; + case CMD_NOTELEN_4: new_len_init= 4; new_len=TICKS_PER_WHOLE/ 4; break; + case CMD_NOTELEN_8: new_len_init= 8; new_len=TICKS_PER_WHOLE/ 8; break; + case CMD_NOTELEN_16: new_len_init=16; new_len=TICKS_PER_WHOLE/16; break; + case CMD_NOTELEN_32: new_len_init=32; new_len=TICKS_PER_WHOLE/32; break; + case CMD_NOTELEN_LAST: new_len_init= 0; new_len=-1; break; default: cerr << "ERROR: ILLEGAL FUNCTION CALL: ScoreCanvas::menu_command called with unknown command ("<<cmd<<")"<<endl; @@ -4299,23 +4362,24 @@ void ScoreCanvas::menu_command(int cmd) void ScoreCanvas::preamble_keysig_slot(bool state) { preamble_contains_keysig=state; + preamble_contains_keysig_init=state; redraw(); } void ScoreCanvas::preamble_timesig_slot(bool state) { preamble_contains_timesig=state; + preamble_contains_timesig_init=state; redraw(); } void ScoreCanvas::set_quant(int val) { - int quant_mapper[]={1,2,3,4,5}; - - if ((val>=0) && (val<signed(sizeof(quant_mapper)/sizeof(*quant_mapper)))) + if ((val>=0) && (val<5)) { int old_len=quant_len(); - _quant_power2=quant_mapper[val]; + _quant_power2=val+1; + _quant_power2_init=_quant_power2; set_pixels_per_whole(pixels_per_whole() * quant_len() / old_len ); @@ -4342,6 +4406,7 @@ void ScoreCanvas::set_pixels_per_whole(int val) // zero!) _pixels_per_whole=val; + _pixels_per_whole_init=val; for (list<staff_t>::iterator it=staves.begin(); it!=staves.end(); it++) it->calc_item_pos(); @@ -4383,6 +4448,7 @@ void ScoreCanvas::maybe_close_if_empty() void ScoreCanvas::set_velo(int velo) { note_velo=velo; + note_velo_init=velo; if (parent->get_apply_velo()) modify_velocity(get_all_parts(),1, 0,velo); @@ -4391,6 +4457,7 @@ void ScoreCanvas::set_velo(int velo) void ScoreCanvas::set_velo_off(int velo) { note_velo_off=velo; + note_velo_off_init=velo; if (parent->get_apply_velo()) modify_off_velocity(get_all_parts(),1, 0,velo); @@ -4586,24 +4653,23 @@ void ScoreCanvas::add_new_parts(const std::map< MusECore::Part*, std::set<MusECo /* BUGS and potential bugs * o tied notes don't work properly when there's a key-change in * between, for example, when a cis is tied to a des - * o schedule_all_same_len_parts: if there are two clones A and B, - * and both A and B get scheduled to be expanded (because we - * have one event from A and one event from B), this causes a bug, - * because after A (and B) got resized, the B-resize is invalid! - * o when changing toolbarstate when sharing and immediately after that + * > o when changing toolbarstate when sharing and immediately after that * changing "share" status, the changed state isn't stored + * (could be solved by storing the current window when quitting/saving whatever) * ? pasting in editors sometimes fails oO? ( ERROR: reading eventlist * from clipboard failed. ignoring this one... ) [ not reproducible ] - * > o non-mdi topwin states aren't restored when launching muse2 somefile.med * ! o using super glue while a score editor displaying the glued parts * is open lets muse segfault. this may or may not be fixed in * the release branch :/ * * CURRENT TODO * > o fix valgrind problems (the two "FINDMICHJETZT" lines in scoreedit.cpp) - * > o newly created windows have to be focussed! + * > o add a songposition scrollbar-toolbar (in different sizes) + * this might be equivalent to "redo transport menu" (below). + * > o add toolbar(s) for tempo- etc spinboxes from the transport window * * IMPORTANT TODO + * o support edge-scrolling when opening a lasso * o add "dotted quarter" quantize option (for 6/8 beat) * o ticks-to-quarter spinboxes * o mirror most menus to an additional right-click context menu to avoid the long mouse pointer @@ -4628,6 +4694,7 @@ void ScoreCanvas::add_new_parts(const std::map< MusECore::Part*, std::set<MusECo * o thin out: remove unneeded ctrl messages * * less important stuff + * o allow "fixating" toolbars? * o quantize-templates (everything is forced into a specified * rhythm) * o part-templates (you specify some notes and a control-chord; @@ -4639,8 +4706,6 @@ void ScoreCanvas::add_new_parts(const std::map< MusECore::Part*, std::set<MusECo * calc_pos_add_list must be called before calc_item_pos then, * and calc_item_pos must respect the pos_add_list instead of * keeping its own pos_add variable (which is only an optimisation) - * o support edge-scrolling when opening a lasso - * o save more configuration stuff (quant, color) * * really unimportant nice-to-haves * o support in-song clef-changes @@ -4653,10 +4718,6 @@ void ScoreCanvas::add_new_parts(const std::map< MusECore::Part*, std::set<MusECo * * * stuff for the other muse developers - * o update translations - * o remove ambiguous translation: "offset"="zeitversatz" - * this is ambigous in mod. note len and WRONG in mod. velo dialogs - * * o process accurate timesignatures from muse's list (has to be implemented first in muse) * ( (2+2+3)/4 or (3+2+2)/4 instead of 7/4 ) */ diff --git a/muse2/muse/midiedit/scoreedit.h b/muse2/muse/midiedit/scoreedit.h index baea78e3..37765dda 100644 --- a/muse2/muse/midiedit/scoreedit.h +++ b/muse2/muse/midiedit/scoreedit.h @@ -668,8 +668,20 @@ class ScoreCanvas : public MusEGui::View void move_staff_below(list<staff_t>::iterator dest, list<staff_t>::iterator src); void cleanup_staves(); void maybe_close_if_empty(); - + +// defaults ---------------------------------------------------------- + public: + enum coloring_mode_t {COLOR_MODE_BLACK, COLOR_MODE_PART, COLOR_MODE_VELO}; + static int _quant_power2_init; + static int _pixels_per_whole_init; + static int note_velo_init, note_velo_off_init; + static int new_len_init; + static coloring_mode_t coloring_mode_init; + static bool preamble_contains_timesig_init; + static bool preamble_contains_keysig_init; + // member variables --------------------------------------------------- + private: int _quant_power2; int _pixels_per_whole; @@ -746,7 +758,7 @@ class ScoreCanvas : public MusEGui::View bool srec; bool held_notes[128]; - enum {COLOR_MODE_BLACK, COLOR_MODE_PART, COLOR_MODE_VELO} coloring_mode; + coloring_mode_t coloring_mode; bool preamble_contains_keysig; bool preamble_contains_timesig; @@ -776,38 +788,38 @@ class ScoreCanvas : public MusEGui::View void add_new_parts(const std::map< MusECore::Part*, std::set<MusECore::Part*> >&); - public slots: - void x_scroll_event(int); - void y_scroll_event(int); - void song_changed(int); - void fully_recalculate(); - void goto_tick(int,bool); - void pos_changed(int i, unsigned u, bool b); - void heartbeat_timer_event(); - - void set_tool(int); - void set_quant(int); - void menu_command(int); - void preamble_keysig_slot(bool); - void preamble_timesig_slot(bool); - void set_pixels_per_whole(int); + public slots: + void x_scroll_event(int); + void y_scroll_event(int); + void song_changed(int); + void fully_recalculate(); + void goto_tick(int,bool); + void pos_changed(int i, unsigned u, bool b); + void heartbeat_timer_event(); - void set_velo(int); - void set_velo_off(int); + void set_tool(int); + void set_quant(int); + void menu_command(int); + void preamble_keysig_slot(bool); + void preamble_timesig_slot(bool); + void set_pixels_per_whole(int); - void set_steprec(bool); - - void update_parts(); //re-populates the set<MusECore::Part*>s from the set<int>s + void set_velo(int); + void set_velo_off(int); + + void set_steprec(bool); + + void update_parts(); //re-populates the set<MusECore::Part*>s from the set<int>s signals: - void xscroll_changed(int); - void yscroll_changed(int); - void viewport_width_changed(int); - void canvas_width_changed(int); - void preamble_width_changed(int); - void viewport_height_changed(int); - void canvas_height_changed(int); - void pixels_per_whole_changed(int); - void pos_add_changed(); + void xscroll_changed(int); + void yscroll_changed(int); + void viewport_width_changed(int); + void canvas_width_changed(int); + void preamble_width_changed(int); + void viewport_height_changed(int); + void canvas_height_changed(int); + void pixels_per_whole_changed(int); + void pos_add_changed(); protected: virtual void draw(QPainter& p, const QRect& rect); diff --git a/muse2/muse/midiport.cpp b/muse2/muse/midiport.cpp index 19fef51b..b7d1d7b7 100644 --- a/muse2/muse/midiport.cpp +++ b/muse2/muse/midiport.cpp @@ -332,8 +332,8 @@ QMenu* midiPortsPopup(QWidget* parent, int checkPort) { MusECore::MidiDevice* md = MusEGlobal::midiPorts[pi].device(); //if(md && !md->isSynti() && (md->rwFlags() & 1)) - //if(md && (md->rwFlags() & 1)) - if(md && (md->rwFlags() & 1 || md->isSynti()) ) + if(md && (md->rwFlags() & 1)) + //if(md && (md->rwFlags() & 1 || md->isSynti()) ) // Revert. Hm, why synths? Only writeable ports. p4.0.41 break; } if(pi == MIDI_PORTS) @@ -352,8 +352,12 @@ QMenu* midiPortsPopup(QWidget* parent, int checkPort) for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* port = &MusEGlobal::midiPorts[i]; + MusECore::MidiDevice* md = port->device(); + //if(md && !(md->rwFlags() & 1 || md->isSynti()) && (i != checkPort)) + if(md && !(md->rwFlags() & 1) && (i != checkPort)) // Only writeable ports, or current one. + continue; name.sprintf("%d:%s", port->portno()+1, port->portname().toLatin1().constData()); - if(port->device() || (i == checkPort)) + if(md || (i == checkPort)) { act = p->addAction(name); act->setData(i); @@ -361,7 +365,7 @@ QMenu* midiPortsPopup(QWidget* parent, int checkPort) act->setChecked(i == checkPort); } - if(!port->device()) + if(!md) { if(!subp) // No submenu yet? Create it now. { diff --git a/muse2/muse/midiseq.cpp b/muse2/muse/midiseq.cpp index e31db7c7..1e46db11 100644 --- a/muse2/muse/midiseq.cpp +++ b/muse2/muse/midiseq.cpp @@ -44,6 +44,7 @@ #include "synth.h" #include "song.h" #include "gconfig.h" +#include <lo/lo_osc_types.h> namespace MusEGlobal { MusECore::MidiSeq* midiSeq; @@ -54,6 +55,12 @@ namespace MusECore { int MidiSeq::ticker = 0; +void initMidiSequencer() +{ + //MusEGlobal::midiSeq = new MidiSeq(MusEGlobal::realTimeScheduling ? MusEGlobal::realTimePriority : 0, "Midi"); + MusEGlobal::midiSeq = new MidiSeq("Midi"); +} + //--------------------------------------------------------- // readMsg //--------------------------------------------------------- @@ -819,16 +826,16 @@ void MidiSeq::msgMsg(int id) void MidiSeq::msgSetMidiDevice(MidiPort* port, MidiDevice* device) { - MusECore::AudioMsg msg; - msg.id = MusECore::SEQM_IDLE; - msg.a = true; - Thread::sendMsg(&msg); - - port->setMidiDevice(device); + MusECore::AudioMsg msg; + msg.id = MusECore::SEQM_IDLE; + msg.a = true; + Thread::sendMsg(&msg); + + port->setMidiDevice(device); - msg.id = MusECore::SEQM_IDLE; - msg.a = false; - Thread::sendMsg(&msg); + msg.id = MusECore::SEQM_IDLE; + msg.a = false; + Thread::sendMsg(&msg); } // This does not appear to be used anymore. Was called in Audio::process1, now Audio::processMidi is called directly. p4.0.15 Tim. diff --git a/muse2/muse/mixer/amixer.cpp b/muse2/muse/mixer/amixer.cpp index 731af9a4..439a92b9 100644 --- a/muse2/muse/mixer/amixer.cpp +++ b/muse2/muse/mixer/amixer.cpp @@ -169,7 +169,7 @@ AudioMixerApp::AudioMixerApp(QWidget* parent, MusEGlobal::MixerConfig* c) setWindowIcon(*museIcon); QMenu* menuConfig = menuBar()->addMenu(tr("&Create")); - MusEGui::populateAddTrack(menuConfig); + MusEGui::populateAddTrack(menuConfig,true); connect(menuConfig, SIGNAL(triggered(QAction *)), MusEGlobal::song, SLOT(addNewTrack(QAction *))); QMenu* menuView = menuBar()->addMenu(tr("&View")); diff --git a/muse2/muse/mixer/astrip.cpp b/muse2/muse/mixer/astrip.cpp index 10f281fb..9c393a1a 100644 --- a/muse2/muse/mixer/astrip.cpp +++ b/muse2/muse/mixer/astrip.cpp @@ -147,6 +147,7 @@ void AudioStrip::songChanged(int val) // Set the strip label's font. //label->setFont(MusEGlobal::config.fonts[1]); setLabelFont(); + setLabelText(); // Adjust minimum volume slider and label values. slider->setRange(MusEGlobal::config.minSlider-0.1, 10.0); @@ -203,7 +204,17 @@ void AudioStrip::songChanged(int val) pre->setChecked(src->prefader()); pre->blockSignals(false); } - } + + // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! + // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. + int rc = track->auxRefCount(); + int n = auxKnob.size(); + for (int idx = 0; idx < n; ++idx) + { + auxKnob[idx]->setEnabled( rc == 0 ); + auxLabel[idx]->setEnabled( rc == 0 ); + } + } if (val & SC_AUX) { int n = auxKnob.size(); for (int idx = 0; idx < n; ++idx) { @@ -333,11 +344,14 @@ void AudioStrip::updateOffState() stereo->setEnabled(val); label->setEnabled(val); + // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! + // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. + bool ae = track->auxRefCount() == 0 && val; int n = auxKnob.size(); for (int i = 0; i < n; ++i) { - auxKnob[i]->setEnabled(val); - auxLabel[i]->setEnabled(val); + auxKnob[i]->setEnabled(ae); + auxLabel[i]->setEnabled(ae); } if (pre) @@ -348,12 +362,12 @@ void AudioStrip::updateOffState() solo->setEnabled(val); if (mute) mute->setEnabled(val); - if (autoType) - autoType->setEnabled(val); - if (iR) - iR->setEnabled(val); - if (oR) - oR->setEnabled(val); + //if (autoType) + // autoType->setEnabled(val); + //if (iR) + // iR->setEnabled(val); + //if (oR) + // oR->setEnabled(val); if (off) { off->blockSignals(true); off->setChecked(track->off()); @@ -799,6 +813,12 @@ AudioStrip::AudioStrip(QWidget* parent, MusECore::AudioTrack* at) double val = MusECore::fast_log10(t->auxSend(idx))*20.0; ak->setValue(val); al->setValue(val); + + // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! + // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. + int rc = track->auxRefCount(); + ak->setEnabled( rc == 0 ); + al->setEnabled( rc == 0 ); } } else { diff --git a/muse2/muse/mixer/mstrip.cpp b/muse2/muse/mixer/mstrip.cpp index 2e51feb9..dc495aa6 100644 --- a/muse2/muse/mixer/mstrip.cpp +++ b/muse2/muse/mixer/mstrip.cpp @@ -448,9 +448,8 @@ void MidiStrip::updateOffState() // TODO: Disabled for now. //if (autoType) // autoType->setEnabled(val); - if (iR) - iR->setEnabled(val); - // TODO: Disabled for now. + //if (iR) + // iR->setEnabled(val); //if (oR) // oR->setEnabled(val); if (off) { @@ -504,6 +503,7 @@ void MidiStrip::songChanged(int val) // Set the strip label's font. //label->setFont(MusEGlobal::config.fonts[1]); setLabelFont(); + setLabelText(); } } diff --git a/muse2/muse/mixer/strip.cpp b/muse2/muse/mixer/strip.cpp index b87c4629..c26a9a15 100644 --- a/muse2/muse/mixer/strip.cpp +++ b/muse2/muse/mixer/strip.cpp @@ -29,6 +29,8 @@ #include <QColor> #include <QVBoxLayout> #include <QFrame> +#include <QMouseEvent> +#include <QMenu> #include "globals.h" #include "gconfig.h" @@ -161,7 +163,8 @@ void Strip::setLabelText() //gradient.setColorAt(0, c.darker()); //gradient.setColorAt(0, c); //gradient.setColorAt(1, c.darker()); - gradient.setColorAt(0, c.lighter()); + gradient.setColorAt(0, c); + gradient.setColorAt(0.5, c.lighter()); gradient.setColorAt(1, c); //palette.setBrush(QPalette::Button, gradient); //palette.setBrush(QPalette::Window, gradient); @@ -303,6 +306,23 @@ void Strip::resizeEvent(QResizeEvent* ev) setLabelText(); setLabelFont(); } - + +void Strip::mousePressEvent(QMouseEvent* ev) +{ + if (ev->button() == Qt::RightButton) { + QMenu* menu = new QMenu; + menu->addAction(tr("Remove track?")); + QPoint pt = QCursor::pos(); + QAction* act = menu->exec(pt, 0); + if (!act) + { + delete menu; + return; + } + MusEGlobal::song->removeTrack0(track); + MusEGlobal::audio->msgUpdateSoloStates(); + } +} + } // namespace MusEGui diff --git a/muse2/muse/mixer/strip.h b/muse2/muse/mixer/strip.h index 5b3b541b..d0cde1a6 100644 --- a/muse2/muse/mixer/strip.h +++ b/muse2/muse/mixer/strip.h @@ -73,6 +73,7 @@ class Strip : public QFrame { MusEGui::ComboBox* autoType; void setLabelText(); virtual void resizeEvent(QResizeEvent*); + virtual void mousePressEvent(QMouseEvent *); private slots: void recordToggled(bool); diff --git a/muse2/muse/node.cpp b/muse2/muse/node.cpp index dd41ba10..cb77f939 100644 --- a/muse2/muse/node.cpp +++ b/muse2/muse/node.cpp @@ -4,6 +4,7 @@ // $Id: node.cpp,v 1.36.2.25 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,6 +26,8 @@ #include <sndfile.h> #include <stdlib.h> +#include <QString> + #include "node.h" #include "globals.h" #include "gconfig.h" @@ -44,7 +47,6 @@ //#define NODE_DEBUG //#define FIFO_DEBUG -// Added by Tim. p3.3.18 //#define METRONOME_DEBUG namespace MusECore { @@ -162,10 +164,19 @@ void Track::updateInternalSoloStates() void MidiTrack::updateInternalSoloStates() { - if(this == _tmpSoloChainTrack) - return; - - Track::updateInternalSoloStates(); + if(_nodeTraversed) // Anti circular mechanism. + { + fprintf(stderr, "MidiTrack::updateInternalSoloStates %s :\n MusE Warning: Please check your routes: Circular path found!\n", name().toLatin1().constData()); + return; + } + //if(this == _tmpSoloChainTrack) + // return; + + _nodeTraversed = true; + + Track::updateInternalSoloStates(); + + _nodeTraversed = false; // Reset. } @@ -175,40 +186,61 @@ void MidiTrack::updateInternalSoloStates() void AudioTrack::updateInternalSoloStates() { - if(this == _tmpSoloChainTrack) - return; - - Track::updateInternalSoloStates(); - - if(_tmpSoloChainDoIns) + if(_nodeTraversed) // Anti circular mechanism. + { + fprintf(stderr, "AudioTrack::updateInternalSoloStates %s :\n MusE Warning: Please check your routes: Circular path found!\n", name().toLatin1().constData()); + return; + } + //if(this == _tmpSoloChainTrack) + // return; + + _nodeTraversed = true; + + Track::updateInternalSoloStates(); + + if(_tmpSoloChainDoIns) + { + if(type() == AUDIO_SOFTSYNTH) + { + const MusECore::MidiTrackList* ml = MusEGlobal::song->midis(); + for(MusECore::ciMidiTrack im = ml->begin(); im != ml->end(); ++im) { - if(type() == AUDIO_SOFTSYNTH) - { - const MusECore::MidiTrackList* ml = MusEGlobal::song->midis(); - for(MusECore::ciMidiTrack im = ml->begin(); im != ml->end(); ++im) - { - MusECore::MidiTrack* mt = *im; - if(mt->outPort() >= 0 && mt->outPort() == ((SynthI*)this)->midiPort()) - mt->updateInternalSoloStates(); - } - } - - const RouteList* rl = inRoutes(); - for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) - { - if(ir->type == Route::TRACK_ROUTE) - ir->track->updateInternalSoloStates(); - } + MusECore::MidiTrack* mt = *im; + if(mt->outPort() >= 0 && mt->outPort() == ((SynthI*)this)->midiPort()) + mt->updateInternalSoloStates(); } - else - { - const RouteList* rl = outRoutes(); - for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + } + + const RouteList* rl = inRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == Route::TRACK_ROUTE) + ir->track->updateInternalSoloStates(); + else + // Support Midi Port -> Audio Input solo chains. p4.0.37 Tim. + if(ir->type == Route::MIDI_PORT_ROUTE) + { + const MidiTrackList* ml = MusEGlobal::song->midis(); + for(ciMidiTrack im = ml->begin(); im != ml->end(); ++im) { - if(ir->type == Route::TRACK_ROUTE) - ir->track->updateInternalSoloStates(); + MidiTrack* mt = *im; + if(mt->outPort() == ir->midiPort && ((1 << mt->outChannel()) & ir->channel) ) + mt->updateInternalSoloStates(); } - } + } + } + } + else + { + const RouteList* rl = outRoutes(); + for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) + { + if(ir->type == Route::TRACK_ROUTE) + ir->track->updateInternalSoloStates(); + } + } + + _nodeTraversed = false; // Reset. } @@ -221,6 +253,8 @@ void MidiTrack::updateSoloStates(bool noDec) if(noDec && !_solo) return; + _nodeTraversed = true; // Anti circular mechanism. + _tmpSoloChainTrack = this; _tmpSoloChainDoIns = false; _tmpSoloChainNoDec = noDec; @@ -244,6 +278,8 @@ void MidiTrack::updateSoloStates(bool noDec) } } } + + _nodeTraversed = false; // Reset. } @@ -256,6 +292,8 @@ void AudioTrack::updateSoloStates(bool noDec) if(noDec && !_solo) return; + _nodeTraversed = true; // Anti circular mechanism. + _tmpSoloChainTrack = this; _tmpSoloChainNoDec = noDec; updateSoloState(); @@ -301,6 +339,8 @@ void AudioTrack::updateSoloStates(bool noDec) ir->track->updateInternalSoloStates(); } } + + _nodeTraversed = false; // Reset. } @@ -327,7 +367,6 @@ void Track::setOff(bool val) // copyData //--------------------------------------------------------- -//void AudioTrack::copyData(unsigned pos, int dstChannels, unsigned nframes, float** dstBuffer) void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer) { //Changed by T356. 12/12/09. @@ -336,60 +375,51 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // Make better use of AudioTrack::outBuffers as a post-effect pre-volume cache system for multiple calls here during processing. // Previously only WaveTrack used them. (Changed WaveTrack as well). + #ifdef NODE_DEBUG + printf("MusE: AudioTrack::copyData name:%s processed:%d\n", name().toLatin1().constData(), processed()); + #endif + if(srcStartChan == -1) srcStartChan = 0; - int srcChans = (srcChannels == -1) ? channels() : srcChannels; + int trackChans = channels(); + int srcChans = (srcChannels == -1) ? trackChans : srcChannels; int srcTotalOutChans = totalOutChannels(); if(channels() == 1) srcTotalOutChans = 1; - #ifdef NODE_DEBUG - printf("MusE: AudioTrack::copyData name:%s processed:%d\n", name().toLatin1().constData(), processed()); - #endif - // Special consideration for metronome: It is not part of the track list, // and it has no in or out routes, yet multiple output tracks may call addData on it ! // We can't tell how many output tracks call it, so we can only assume there might be more than one. // Not strictly necessary here because only addData is ever called, but just to be consistent... - //bool usedirectbuf = (outRoutes()->size() <= 1) || (type() == AUDIO_OUTPUT); - bool usedirectbuf = ((outRoutes()->size() <= 1) || (type() == AUDIO_OUTPUT)) && (this != metronome); + //bool usedirectbuf = ((outRoutes()->size() <= 1) || (type() == AUDIO_OUTPUT)) && (this != metronome); int i; - // p3.3.38 - //float* buffer[srcChannels]; float* buffer[srcTotalOutChans]; + float data[nframes * srcTotalOutChans]; - - //float data[nframes * srcChannels]; - //for(i = 0; i < srcChannels; ++i) - // buffer[i] = data + i * nframes; - // precalculate stereo volume double vol[2]; double _volume = volume(); double _pan = pan(); vol[0] = _volume * (1.0 - _pan); vol[1] = _volume * (1.0 + _pan); - float meter[srcChans]; + float meter[trackChans]; // Have we been here already during this process cycle? if(processed()) { // If there is only one (or no) output routes, it's an error - we've been called more than once per process cycle! + // No, this is no longer an error, it's deliberate. Processing no longer done in 'chains', now done randomly. p4.0.37 #ifdef NODE_DEBUG - if(usedirectbuf) - printf("MusE: AudioTrack::copyData Error! One or no out routes, but already processed! Copying local buffers anyway...\n"); + printf("MusE: AudioTrack::copyData name:%s already processed _haveData:%d\n", name().toLatin1().constData(), _haveData); #endif // Is there already some data gathered from a previous call during this process cycle? if(_haveData) { // Point the input buffers at our local cached 'pre-volume' buffers. They need processing, so continue on after. - //for(i = 0; i < srcChannels; ++i) - // buffer[i] = outBuffers[i]; - // p3.3.38 for(i = 0; i < srcTotalOutChans; ++i) buffer[i] = outBuffers[i]; } @@ -413,29 +443,16 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s { // First time here during this process cycle. - // Point the input buffers at a temporary stack buffer. - //float data[nframes * srcChannels]; - //for(i = 0; i < srcChannels; ++i) - // buffer[i] = data + i * nframes; - // p3.3.38 - float data[nframes * srcTotalOutChans]; - for(i = 0; i < srcTotalOutChans; ++i) - buffer[i] = data + i * nframes; - - // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. - // For ex. if this is an audio input, Jack will set the pointers for us in AudioInput::getData! - // p3.3.29 1/27/10 Don't do any processing at all if off. Whereas, mute needs to be ready for action at all times, - // so still call getData before it. Off is NOT meant to be toggled rapidly, but mute is ! - //if(!getData(pos, srcChannels, nframes, buffer) || off() || (isMute() && !_prefader)) - //if(off() || !getData(pos, srcChannels, nframes, buffer) || (isMute() && !_prefader)) - // p3.3.38 - if(off() || !getData(pos, srcTotalOutChans, nframes, buffer) || (isMute() && !_prefader)) - { + _haveData = false; // Reset. + _processed = true; // Set this now. + + if(off()) + { #ifdef NODE_DEBUG - printf("MusE: AudioTrack::copyData name:%s dstChannels:%d zeroing buffers\n", name().toLatin1().constData(), dstChannels); + printf("MusE: AudioTrack::copyData name:%s dstChannels:%d Off, zeroing buffers\n", name().toLatin1().constData(), dstChannels); #endif - // No data was available. Zero the supplied buffers. + // Track is off. Zero the supplied buffers. unsigned int q; for(i = 0; i < dstChannels; ++i) { @@ -448,13 +465,12 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s memset(dstBuffer[i], 0, sizeof(float) * nframes); } - for(i = 0; i < srcChans; ++i) + _efxPipe->apply(0, nframes, 0); // Just process controls only, not audio (do not 'run'). + + for(i = 0; i < trackChans; ++i) { - //_meter[i] = 0; _meter[i] = 0.0; - - /* - if(!usedirectbuf) + /*if(!usedirectbuf) { if(MusEGlobal::config.useDenormalBias) { @@ -463,22 +479,49 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s } else memset(outBuffers[i], 0, sizeof(float) * nframes); - } - */ + } */ } - _haveData = false; - _processed = true; + //_haveData = false; + //_processed = true; + //_isProcessing = false; // Unblock. return; } + + // Point the input buffers at a temporary stack buffer. + for(i = 0; i < srcTotalOutChans; ++i) + buffer[i] = data + i * nframes; + + // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. + // For ex. if this is an audio input, Jack will set the pointers for us in AudioInput::getData! + // Don't do any processing at all if off. Whereas, mute needs to be ready for action at all times, + // so still call getData before it. Off is NOT meant to be toggled rapidly, but mute is ! + if(!getData(pos, srcTotalOutChans, nframes, buffer) || (isMute() && !_prefader)) + { + #ifdef NODE_DEBUG + printf("MusE: AudioTrack::copyData name:%s srcTotalOutChans:%d zeroing buffers\n", name().toLatin1().constData(), srcTotalOutChans); + #endif + + // No data was available. Track is not off. Zero the working buffers and continue on. + unsigned int q; + for(i = 0; i < srcTotalOutChans; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(q = 0; q < nframes; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * nframes); + } + } //--------------------------------------------------- // apply plugin chain //--------------------------------------------------- - // p3.3.41 //fprintf(stderr, "AudioTrack::copyData %s efx apply srcChans:%d\n", name().toLatin1().constData(), srcChans); - _efxPipe->apply(srcChans, nframes, buffer); + _efxPipe->apply(trackChans, nframes, buffer); //--------------------------------------------------- // aux sends @@ -525,18 +568,16 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s if(_prefader) { - for(i = 0; i < srcChans; ++i) + for(i = 0; i < trackChans; ++i) { float* p = buffer[i]; meter[i] = 0.0; for(unsigned k = 0; k < nframes; ++k) { - double f = fabs(*p); + double f = fabs(*p++); if(f > meter[i]) meter[i] = f; - ++p; } - //_meter[i] = lrint(meter[i] * 32767.0); _meter[i] = meter[i]; if(_meter[i] > _peak[i]) _peak[i] = _meter[i]; @@ -570,20 +611,19 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s else memset(outBuffers[i], 0, sizeof(float) * nframes); } - } - */ + } */ + - _haveData = false; - _processed = true; + if(!_prefader) + for(i = 0; i < trackChans; ++i) // Must process ALL channels, even if unconnected. Only max 2 channels. + _meter[i] = 0.0; + return; } // If we're using local cached 'pre-volume' buffers, copy the input buffers (as they are right now: post-effect pre-volume) back to them. - if(!usedirectbuf) + //if(!usedirectbuf) { - //for(i = 0; i < srcChannels; ++i) - // AL::dsp->cpy(outBuffers[i], buffer[i], nframes); - // p3.3.38 for(i = 0; i < srcTotalOutChans; ++i) AL::dsp->cpy(outBuffers[i], buffer[i], nframes); } @@ -606,9 +646,9 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s else memset(dstBuffer[i], 0, sizeof(float) * nframes); } - _processed = true; return; } + // Force a source range to fit actual available total out channels. if((srcStartChan + srcChans) > srcTotalOutChans) srcChans = srcTotalOutChans - srcStartChan; @@ -618,132 +658,94 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // postfader metering //--------------------------------------------------- - - if(srcChans == dstChannels) + #ifdef NODE_DEBUG + printf("MusE: AudioTrack::copyData trackChans:%d srcTotalOutChans:%d srcStartChan:%d srcChans:%d dstChannels:%d\n", trackChans, srcTotalOutChans, srcStartChan, srcChans, dstChannels); + #endif + + if(!_prefader) { - if(_prefader) + for(int c = 0; c < trackChans; ++c) { - for(int c = 0; c < dstChannels; ++c) + meter[c] = 0.0; + double v = (trackChans == 1 ? _volume : vol[c]); + float* sp = buffer[c]; + for(unsigned k = 0; k < nframes; ++k) { - // p3.3.38 - //float* sp = buffer[c]; - float* sp = buffer[c + srcStartChan]; - - float* dp = dstBuffer[c]; - for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp++ * vol[c]); + float val = *sp++ * v; // If the track is mono pan has no effect on meters. + double f = fabs(val); + if(f > meter[c]) + meter[c] = f; } - } - else + _meter[c] = meter[c]; + if(_meter[c] > _peak[c]) + _peak[c] = _meter[c]; + } + } + + if(srcChans == dstChannels) + { + for(int c = 0; c < dstChannels; ++c) { - for(int c = 0; c < dstChannels; ++c) - { - meter[c] = 0.0; - - // p3.3.38 - //float* sp = buffer[c]; - float* sp = buffer[c + srcStartChan]; - - float* dp = dstBuffer[c]; - //printf("2 dstBuffer[c]=%d\n",long(dstBuffer[c])); - for(unsigned k = 0; k < nframes; ++k) - { - float val = *sp++ * vol[c]; - *dp++ = val; - double f = fabs(val); - if(f > meter[c]) - meter[c] = f; - } - //_meter[c] = lrint(meter[c] * 32767.0); - _meter[c] = meter[c]; - if(_meter[c] > _peak[c]) - _peak[c] = _meter[c]; - } + double v; + if(srcStartChan > 2) // Don't apply pan or volume to extra channels above 2. + //v = _volume; + v = 1.0; + else + if(srcChans >= 2) // If 2 channels apply pan normally. + v = vol[c]; + else + if(trackChans < 2) // If 1 channel and track is 1 channel, don't apply pan. + v = _volume; + else + v = vol[srcStartChan]; // Otherwise 1 channel but track is 2 channels. Apply the channel volume. + + float* sp = buffer[c + srcStartChan]; + float* dp = dstBuffer[c]; + for(unsigned k = 0; k < nframes; ++k) + //*dp++ = (*sp++ * vol[c]); + *dp++ = (*sp++ * v); } } else if(srcChans == 1 && dstChannels == 2) { - // p3.3.38 - //float* sp = buffer[0]; - float* sp = buffer[srcStartChan]; - - if(_prefader) + for(int c = 0; c < dstChannels; ++c) { - for(int c = 0; c < dstChannels; ++c) - { - float* dp = dstBuffer[c]; - for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp++ * vol[c]); - } - } - else - { - meter[0] = 0.0; - for(unsigned k = 0; k < nframes; ++k) - { - float val = *sp++; - double f = fabs(val) * _volume; - if(f > meter[0]) - meter[0] = f; - *(dstBuffer[0] + k) = val * vol[0]; - *(dstBuffer[1] + k) = val * vol[1]; - } - //_meter[0] = lrint(meter[0] * 32767.0); - _meter[0] = meter[0]; - if(_meter[0] > _peak[0]) - _peak[0] = _meter[0]; + double v; + if(srcStartChan > 2) // Don't apply pan or volume to extra channels above 2. + //v = _volume; + v = 1.0; + else + if(trackChans <= 1) // If track is mono apply pan. + v = vol[c]; + else + v = vol[srcStartChan]; // Otherwise track is stereo, apply the same channel volume to both. + + float* sp = buffer[srcStartChan]; + float* dp = dstBuffer[c]; + for(unsigned k = 0; k < nframes; ++k) + //*dp++ = (*sp++ * vol[c]); + *dp++ = (*sp++ * v); } } else if(srcChans == 2 && dstChannels == 1) { - // p3.3.38 - //float* sp1 = buffer[0]; - //float* sp2 = buffer[1]; + //double v1 = (srcStartChan > 2 ? _volume : vol[srcStartChan]); // Don't apply pan to extra channels above 2. + //double v2 = (srcStartChan > 2 ? _volume : vol[srcStartChan + 1]); // + double v1 = (srcStartChan > 2 ? 1.0 : vol[srcStartChan]); // Don't apply pan or volume to extra channels above 2. + double v2 = (srcStartChan > 2 ? 1.0 : vol[srcStartChan + 1]); // + float* dp = dstBuffer[0]; float* sp1 = buffer[srcStartChan]; float* sp2 = buffer[srcStartChan + 1]; - - if(_prefader) - { - float* dp = dstBuffer[0]; - for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp1++ * vol[0] + *sp2++ * vol[1]); - } - else - { - float* dp = dstBuffer[0]; - meter[0] = 0.0; - meter[1] = 0.0; - for(unsigned k = 0; k < nframes; ++k) - { - float val1 = *sp1++ * vol[0]; - float val2 = *sp2++ * vol[1]; - double f1 = fabs(val1); - if(f1 > meter[0]) - meter[0] = f1; - double f2 = fabs(val2); - if(f2 > meter[1]) - meter[1] = f2; - *dp++ = (val1 + val2); - } - //_meter[0] = lrint(meter[0] * 32767.0); - _meter[0] = meter[0]; - if(_meter[0] > _peak[0]) - _peak[0] = _meter[0]; - //_meter[1] = lrint(meter[1] * 32767.0); - _meter[1] = meter[1]; - if(_meter[1] > _peak[1]) - _peak[1] = _meter[1]; - } + for(unsigned k = 0; k < nframes; ++k) + //*dp++ = (*sp1++ * vol[0] + *sp2++ * vol[1]); + *dp++ = (*sp1++ * v1 + *sp2++ * v2); } - - _processed = true; } //--------------------------------------------------------- // addData //--------------------------------------------------------- -//void AudioTrack::addData(unsigned pos, int dstChannels, unsigned nframes, float** dstBuffer) void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer) { //Changed by T356. 12/12/09. @@ -752,21 +754,21 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr // Make better use of AudioTrack::outBuffers as a post-effect pre-volume cache system for multiple calls here during processing. // Previously only WaveTrack used them. (Changed WaveTrack as well). - //Added by Tim. p3.3.16 #ifdef NODE_DEBUG printf("MusE: AudioTrack::addData name:%s processed:%d\n", name().toLatin1().constData(), processed()); #endif - if (off()) - { - _processed = true; - return; - } + //if (off()) + //{ + // _processed = true; + // return; + //} if(srcStartChan == -1) srcStartChan = 0; - int srcChans = (srcChannels == -1) ? channels() : srcChannels; + int trackChans = channels(); + int srcChans = (srcChannels == -1) ? trackChans : srcChannels; int srcTotalOutChans = totalOutChannels(); if(channels() == 1) srcTotalOutChans = 1; @@ -774,18 +776,12 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr // Special consideration for metronome: It is not part of the track list, // and it has no in or out routes, yet multiple output tracks may call addData on it ! // We can't tell how many output tracks call it, so we can only assume there might be more than one. - //bool usedirectbuf = (outRoutes()->size() <= 1) || (type() == AUDIO_OUTPUT); - bool usedirectbuf = ((outRoutes()->size() <= 1) || (type() == AUDIO_OUTPUT)) && (this != metronome); + //bool usedirectbuf = ((outRoutes()->size() <= 1) || (type() == AUDIO_OUTPUT)) && (this != metronome); int i; - // p3.3.38 - //float* buffer[srcChannels]; float* buffer[srcTotalOutChans]; - - //float data[nframes * srcChannels]; - //for (i = 0; i < srcChannels; ++i) - // buffer[i] = data + i * nframes; + float data[nframes * srcTotalOutChans]; // precalculate stereo volume double vol[2]; @@ -793,105 +789,96 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr double _pan = pan(); vol[0] = _volume * (1.0 - _pan); vol[1] = _volume * (1.0 + _pan); - float meter[srcChans]; + //float meter[srcChans]; + float meter[trackChans]; // Have we been here already during this process cycle? if(processed()) { // If there is only one (or no) output routes, it's an error - we've been called more than once per process cycle! + // No, this is no longer an error, it's deliberate. Processing no longer done in 'chains', now done randomly. p4.0.37 #ifdef NODE_DEBUG - if(usedirectbuf) - printf("MusE: AudioTrack::addData Error! One or no out routes, but already processed! Copying local buffers anyway...\n"); + printf("MusE: AudioTrack::addData name:%s already processed _haveData:%d\n", name().toLatin1().constData(), _haveData); #endif // Is there already some data gathered from a previous call during this process cycle? if(_haveData) { // Point the input buffers at our local cached 'pre-volume' buffers. They need processing, so continue on after. - //for(i = 0; i < srcChannels; ++i) - // buffer[i] = outBuffers[i]; - // p3.3.38 for(i = 0; i < srcTotalOutChans; ++i) buffer[i] = outBuffers[i]; } else + { // No data was available from a previous call during this process cycle. Nothing to add, just return. return; + } } else { // First time here during this process cycle. - // Point the input buffers at a temporary stack buffer. - //float data[nframes * srcChannels]; - //for(i = 0; i < srcChannels; ++i) - // buffer[i] = data + i * nframes; - // p3.3.38 - float data[nframes * srcTotalOutChans]; - for(i = 0; i < srcTotalOutChans; ++i) - buffer[i] = data + i * nframes; - + _haveData = false; // Reset. + _processed = true; // Set this now. - // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. - // For ex. if this is an audio input, Jack will set the pointers for us. - //if(!getData(pos, srcChannels, nframes, buffer)) - // p3.3.38 - if(!getData(pos, srcTotalOutChans, nframes, buffer)) - { - // No data was available. Nothing to add, but zero our local buffers and the meters. - for(i = 0; i < srcChans; ++i) + if(off()) + { + #ifdef NODE_DEBUG + printf("MusE: AudioTrack::addData name:%s dstChannels:%d Track is Off \n", name().toLatin1().constData(), dstChannels); + #endif + + // Nothing to zero or add... + + _efxPipe->apply(0, nframes, 0); // Track is off. Just process controls only, not audio (do not 'run'). + + for(i = 0; i < trackChans; ++i) { - // If we're using local buffers, we must zero them so that the next thing requiring them - // during this process cycle will see zeros. - /* - if(!usedirectbuf) + _meter[i] = 0.0; + /*if(!usedirectbuf) { if(MusEGlobal::config.useDenormalBias) { - for(unsigned int q = 0; q < nframes; ++q) + for(q = 0; q < nframes; ++q) outBuffers[i][q] = MusEGlobal::denormalBias; - } - else + } + else memset(outBuffers[i], 0, sizeof(float) * nframes); - } - */ - - //_meter[i] = 0; - _meter[i] = 0.0; - } - - _haveData = false; - _processed = true; + } */ + } return; } - - /* - // p3.3.41 Added. - unsigned int q; - for(i = 0; i < srcChans; ++i) + + // Point the input buffers at a temporary stack buffer. + for(i = 0; i < srcTotalOutChans; ++i) + buffer[i] = data + i * nframes; + + // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. + // For ex. if this is an audio input, Jack will set the pointers for us. + if(!getData(pos, srcTotalOutChans, nframes, buffer)) { - if(MusEGlobal::config.useDenormalBias) - { - for(q = 0; q < nframes; ++q) + // No data was available. Track is not off. Zero the working buffers and continue on. + unsigned int q; + for(i = 0; i < srcTotalOutChans; ++i) + { + if(MusEGlobal::config.useDenormalBias) { - if(q & 1) - buffer[i][q] -= MusEGlobal::denormalBias; - else - buffer[i][q] += MusEGlobal::denormalBias; - } - } - } - */ - + for(q = 0; q < nframes; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * nframes); + } + } + //--------------------------------------------------- // apply plugin chain //--------------------------------------------------- - // p3.3.41 //fprintf(stderr, "AudioTrack::addData %s efx apply srcChans:%d nframes:%ld %e %e %e %e\n", // name().toLatin1().constData(), srcChans, nframes, buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); - _efxPipe->apply(srcChans, nframes, buffer); - // p3.3.41 + + _efxPipe->apply(trackChans, nframes, buffer); + //fprintf(stderr, "AudioTrack::addData after efx: %e %e %e %e\n", // buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); @@ -940,18 +927,16 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr if(_prefader) { - for(i = 0; i < srcChans; ++i) + for(i = 0; i < trackChans; ++i) { float* p = buffer[i]; meter[i] = 0.0; for(unsigned k = 0; k < nframes; ++k) { - double f = fabs(*p); + double f = fabs(*p++); if(f > meter[i]) meter[i] = f; - ++p; } - //_meter[i] = lrint(meter[i] * 32767.0); _meter[i] = meter[i]; if(_meter[i] > _peak[i]) _peak[i] = _meter[i]; @@ -961,8 +946,7 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr if(isMute()) { // If we're using local buffers, we must zero them. - /* - if(!usedirectbuf) + /* if(!usedirectbuf) { for(i = 0; i < srcChannels; ++i) { @@ -974,20 +958,19 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr else memset(outBuffers[i], 0, sizeof(float) * nframes); } - } - */ + } */ - _haveData = false; - _processed = true; + if(!_prefader) + //for(i = 0; i < srcChans; ++i) + for(i = 0; i < trackChans; ++i) + _meter[i] = 0.0; + return; } // If we're using local cached 'pre-volume' buffers, copy the input buffers (as they are right now: post-effect pre-volume) back to them. - if(!usedirectbuf) + //if(!usedirectbuf) { - //for(i = 0; i < srcChannels; ++i) - // AL::dsp->cpy(outBuffers[i], buffer[i], nframes); - // p3.3.38 for(i = 0; i < srcTotalOutChans; ++i) AL::dsp->cpy(outBuffers[i], buffer[i], nframes); } @@ -1010,9 +993,9 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr else memset(dstBuffer[i], 0, sizeof(float) * nframes); } - _processed = true; return; } + // Force a source range to fit actual available total out channels. if((srcStartChan + srcChans) > srcTotalOutChans) srcChans = srcTotalOutChans - srcStartChan; @@ -1022,123 +1005,88 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr // postfader metering //--------------------------------------------------- - if(srcChans == dstChannels) + #ifdef NODE_DEBUG + printf("MusE: AudioTrack::addData trackChans:%d srcTotalOutChans:%d srcChans:%d dstChannels:%d\n", trackChans, srcTotalOutChans, srcChans, dstChannels); + #endif + + if(!_prefader) { - if(_prefader) + for(int c = 0; c < trackChans; ++c) { - for(int c = 0; c < dstChannels; ++c) + meter[c] = 0.0; + double v = (trackChans == 1 ? _volume : vol[c]); + float* sp = buffer[c]; + for(unsigned k = 0; k < nframes; ++k) { - // p3.3.38 - //float* sp = buffer[c]; - float* sp = buffer[c + srcStartChan]; - - float* dp = dstBuffer[c]; - for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp++ * vol[c]); + float val = *sp++ * v; // If the track is mono pan has no effect on meters. + double f = fabs(val); + if(f > meter[c]) + meter[c] = f; } - } - else + _meter[c] = meter[c]; + if(_meter[c] > _peak[c]) + _peak[c] = _meter[c]; + } + } + + if(srcChans == dstChannels) + { + for(int c = 0; c < dstChannels; ++c) { - for(int c = 0; c < dstChannels; ++c) - { - meter[c] = 0.0; - // p3.3.38 - //float* sp = buffer[c]; - float* sp = buffer[c + srcStartChan]; - - float* dp = dstBuffer[c]; - for(unsigned k = 0; k < nframes; ++k) - { - float val = *sp++ * vol[c]; - *dp++ += val; - double f = fabs(val); - if (f > meter[c]) - meter[c] = f; - } - //_meter[c] = lrint(meter[c] * 32767.0); - _meter[c] = meter[c]; - if(_meter[c] > _peak[c]) - _peak[c] = _meter[c]; - } + double v; + if(srcStartChan > 2) // Don't apply pan or volume to extra channels above 2. + //v = _volume; + v = 1.0; + else + if(srcChans >= 2) // If 2 channels apply pan normally. + v = vol[c]; + else + if(trackChans < 2) // If 1 channel and track is 1 channel, don't apply pan. + v = _volume; + else + v = vol[srcStartChan]; // Otherwise 1 channel but track is 2 channels. Apply the channel volume. + + float* sp = buffer[c + srcStartChan]; + float* dp = dstBuffer[c]; + for(unsigned k = 0; k < nframes; ++k) + //*dp++ += (*sp++ * vol[c]); + *dp++ += (*sp++ * v); } } else if(srcChans == 1 && dstChannels == 2) { - // p3.3.38 - float* sp = buffer[srcStartChan]; - - if(_prefader) + for(int c = 0; c < dstChannels; ++c) { - for(int c = 0; c < dstChannels; ++c) - { - float* dp = dstBuffer[c]; - //float* sp = buffer[0]; - for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp++ * vol[c]); - } - } - else - { - //float* sp = buffer[0]; - meter[0] = 0.0; - for(unsigned k = 0; k < nframes; ++k) - { - float val = *sp++; - double f = fabs(val) * _volume; - if(f > meter[0]) - meter[0] = f; - *(dstBuffer[0] + k) += val * vol[0]; - *(dstBuffer[1] + k) += val * vol[1]; - } - //_meter[0] = lrint(meter[0] * 32767.0); - _meter[0] = meter[0]; - if(_meter[0] > _peak[0]) - _peak[0] = _meter[0]; + double v; + if(srcStartChan > 2) // Don't apply pan or volume to extra channels above 2. + //v = _volume; + v = 1.0; + else + if(trackChans <= 1) // If track is mono apply pan. + v = vol[c]; + else + v = vol[srcStartChan]; // Otherwise track is stereo, apply the same channel volume to both. + + float* sp = buffer[srcStartChan]; + float* dp = dstBuffer[c]; + for(unsigned k = 0; k < nframes; ++k) + //*dp++ += (*sp++ * vol[c]); + *dp++ += (*sp++ * v); } } else if(srcChans == 2 && dstChannels == 1) { - // p3.3.38 - //float* sp1 = buffer[0]; - //float* sp2 = buffer[1]; + //double v1 = (srcStartChan > 2 ? _volume : vol[srcStartChan]); // Don't apply pan to extra channels above 2. + //double v2 = (srcStartChan > 2 ? _volume : vol[srcStartChan + 1]); // + double v1 = (srcStartChan > 2 ? 1.0 : vol[srcStartChan]); // Don't apply pan or volume to extra channels above 2. + double v2 = (srcStartChan > 2 ? 1.0 : vol[srcStartChan + 1]); // float* sp1 = buffer[srcStartChan]; float* sp2 = buffer[srcStartChan + 1]; - - if(_prefader) - { - float* dp = dstBuffer[0]; - for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp1++ * vol[0] + *sp2++ * vol[1]); - } - else - { - float* dp = dstBuffer[0]; - meter[0] = 0.0; - meter[1] = 0.0; - for(unsigned k = 0; k < nframes; ++k) - { - float val1 = *sp1++ * vol[0]; - float val2 = *sp2++ * vol[1]; - double f1 = fabs(val1); - if(f1 > meter[0]) - meter[0] = f1; - double f2 = fabs(val2); - if(f2 > meter[1]) - meter[1] = f2; - *dp++ += (val1 + val2); - } - //_meter[0] = lrint(meter[0] * 32767.0); - _meter[0] = meter[0]; - if(_meter[0] > _peak[0]) - _peak[0] = _meter[0]; - //_meter[1] = lrint(meter[1] * 32767.0); - _meter[1] = meter[1]; - if(_meter[1] > _peak[1]) - _peak[1] = _meter[1]; - } + float* dp = dstBuffer[0]; + for(unsigned k = 0; k < nframes; ++k) + //*dp++ += (*sp1++ * vol[0] + *sp2++ * vol[1]); + *dp++ += (*sp1++ * v1 + *sp2++ * v2); } - - _processed = true; } //--------------------------------------------------------- @@ -1300,15 +1248,12 @@ bool AudioTrack::getData(unsigned pos, int channels, unsigned nframes, float** b printf(" calling copyData on %s...\n", ir->track->name().toLatin1().constData()); #endif - // p3.3.38 - //((AudioTrack*)ir->track)->copyData(pos, channels, nframes, buffer); ((AudioTrack*)ir->track)->copyData(pos, channels, //(ir->track->type() == Track::AUDIO_SOFTSYNTH && ir->channel != -1) ? ir->channel : 0, ir->channel, ir->channels, nframes, buffer); - // p3.3.41 //fprintf(stderr, "AudioTrack::getData %s data: nframes:%ld %e %e %e %e\n", name().toLatin1().constData(), nframes, buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); ++ir; @@ -1320,8 +1265,6 @@ bool AudioTrack::getData(unsigned pos, int channels, unsigned nframes, float** b if(ir->track->isMidiTrack()) continue; - // p3.3.38 - //((AudioTrack*)ir->track)->addData(pos, channels, nframes, buffer); ((AudioTrack*)ir->track)->addData(pos, channels, //(ir->track->type() == Track::AUDIO_SOFTSYNTH && ir->channel != -1) ? ir->channel : 0, ir->channel, @@ -1344,12 +1287,11 @@ bool AudioInput::getData(unsigned, int channels, unsigned nframes, float** buffe void* jackPort = jackPorts[ch]; //float* jackbuf = 0; - //if (jackPort) { - // p3.3.41 Do not get buffers of unconnected client ports. Causes repeating leftover data, can be loud, or DC ! + // Do not get buffers of unconnected client ports. Causes repeating leftover data, can be loud, or DC ! if (jackPort && MusEGlobal::audioDevice->connections(jackPort)) { //buffer[ch] = MusEGlobal::audioDevice->getBuffer(jackPort, nframes); - // p3.3.41 If the client port buffer is also used by another channel (connected to the same jack port), + // If the client port buffer is also used by another channel (connected to the same jack port), // don't directly set pointer, copy the data instead. // Otherwise the next channel will interfere - it will overwrite the buffer ! // Verified symptoms: Can't use a splitter. Mono noise source on a stereo track sounds in mono. Etc... @@ -1366,8 +1308,6 @@ bool AudioInput::getData(unsigned, int channels, unsigned nframes, float** buffe { for (unsigned int i=0; i < nframes; i++) buffer[ch][i] += MusEGlobal::denormalBias; - - // p3.3.41 //fprintf(stderr, "AudioInput::getData %s Jack port %p efx apply channels:%d nframes:%ld %e %e %e %e\n", // name().toLatin1().constData(), jackPort, channels, nframes, buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); } @@ -1384,8 +1324,6 @@ bool AudioInput::getData(unsigned, int channels, unsigned nframes, float** buffe memset(buffer[ch], 0, nframes * sizeof(float)); } - // p3.3.41 - //fprintf(stderr, "AudioInput::getData %s No Jack port efx apply channels:%d nframes:%ld %e %e %e %e\n", // name().toLatin1().constData(), channels, nframes, buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); } } @@ -1524,7 +1462,7 @@ void AudioTrack::record() return; } if (_recFile) { - // Line removed by Tim. p3.3.8 Oct 28, 2009 + // Line removed by Tim. Oct 28, 2009 //_recFile->seek(pos, 0); // // Fix for recorded waves being shifted ahead by an amount @@ -1570,7 +1508,6 @@ void AudioTrack::record() if( (pos >= fr) && (!MusEGlobal::song->punchout() || (!MusEGlobal::song->loop() && pos < MusEGlobal::song->rPos().frame())) ) { pos -= fr; - // Added by Tim. p3.3.8 //int position = _recFile->seek(0, SEEK_CUR); //printf("AudioTrack::record loopcnt:%d lframe:%d newpos:%d curpos:%d start:%d end:%d\n", MusEGlobal::audio->loopCount(), MusEGlobal::audio->loopFrame(), pos, position, MusEGlobal::audio->getStartRecordPos().frame(), MusEGlobal::audio->getEndRecordPos().frame()); @@ -1614,7 +1551,6 @@ void AudioOutput::processInit(unsigned nframes) void AudioOutput::process(unsigned pos, unsigned offset, unsigned n) { - //Added by Tim. p3.3.16 #ifdef NODE_DEBUG printf("MusE: AudioOutput::process name:%s processed:%d\n", name().toLatin1().constData(), processed()); #endif @@ -1622,9 +1558,6 @@ void AudioOutput::process(unsigned pos, unsigned offset, unsigned n) for (int i = 0; i < _channels; ++i) { buffer1[i] = buffer[i] + offset; } - - // p3.3.38 - //copyData(pos, _channels, n, buffer1); copyData(pos, _channels, -1, -1, n, buffer1); } @@ -1666,17 +1599,13 @@ void AudioOutput::processWrite() putFifo(_channels, _nframes, buffer); } } - // Changed by Tim. p3.3.18 + // Changed by Tim. //if (MusEGlobal::audioClickFlag && MusEGlobal::song->click()) { if (sendMetronome() && MusEGlobal::audioClickFlag && MusEGlobal::song->click()) { - // Added by Tim. p3.3.18 #ifdef METRONOME_DEBUG printf("MusE: AudioOutput::processWrite Calling metronome->addData frame:%u channels:%d frames:%lu\n", MusEGlobal::audio->pos().frame(), _channels, _nframes); #endif - - // p3.3.38 - //metronome->addData(MusEGlobal::audio->pos().frame(), _channels, _nframes, buffer); metronome->addData(MusEGlobal::audio->pos().frame(), _channels, -1, -1, _nframes, buffer); } } @@ -1721,7 +1650,6 @@ Fifo::~Fifo() { for (int i = 0; i < nbuffer; ++i) { - // p3.3.45 if(buffer[i]->buffer) { //printf("Fifo::~Fifo freeing buffer\n"); @@ -1742,7 +1670,6 @@ Fifo::~Fifo() bool Fifo::put(int segs, unsigned long samples, float** src, unsigned pos) { - // Added by Tim. p3.3.17 #ifdef FIFO_DEBUG printf("FIFO::put segs:%d samples:%lu pos:%u\n", segs, samples, pos); #endif @@ -1765,7 +1692,6 @@ bool Fifo::put(int segs, unsigned long samples, float** src, unsigned pos) // Changed by Tim. p3.3.15 //b->buffer = new float[n]; posix_memalign((void**)&(b->buffer), 16, sizeof(float) * n); - // p3.3.45 if(!b->buffer) { printf("Fifo::put could not allocate buffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); @@ -1774,7 +1700,6 @@ bool Fifo::put(int segs, unsigned long samples, float** src, unsigned pos) b->maxSize = n; } - // p3.3.45 if(!b->buffer) { printf("Fifo::put no buffer! segs:%d samples:%lu pos:%u\n", segs, samples, pos); @@ -1798,7 +1723,6 @@ bool Fifo::put(int segs, unsigned long samples, float** src, unsigned pos) bool Fifo::get(int segs, unsigned long samples, float** dst, unsigned* pos) { - // Added by Tim. p3.3.17 #ifdef FIFO_DEBUG printf("FIFO::get segs:%d samples:%lu\n", segs, samples); #endif @@ -1808,7 +1732,6 @@ bool Fifo::get(int segs, unsigned long samples, float** dst, unsigned* pos) return true; } FifoBuffer* b = buffer[ridx]; - // p3.3.45 if(!b->buffer) { printf("Fifo::get no buffer! segs:%d samples:%lu b->pos:%u\n", segs, samples, b->pos); @@ -1844,7 +1767,6 @@ void Fifo::remove() bool Fifo::getWriteBuffer(int segs, unsigned long samples, float** buf, unsigned pos) { - // Added by Tim. p3.3.17 #ifdef FIFO_DEBUG printf("Fifo::getWriteBuffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); #endif @@ -1866,7 +1788,6 @@ bool Fifo::getWriteBuffer(int segs, unsigned long samples, float** buf, unsigned // Changed by Tim. p3.3.15 //b->buffer = new float[n]; posix_memalign((void**)&(b->buffer), 16, sizeof(float) * n); - // p3.3.45 if(!b->buffer) { printf("Fifo::getWriteBuffer could not allocate buffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); @@ -1875,8 +1796,6 @@ bool Fifo::getWriteBuffer(int segs, unsigned long samples, float** buf, unsigned b->maxSize = n; } - - // p3.3.45 if(!b->buffer) { printf("Fifo::getWriteBuffer no buffer! segs:%d samples:%lu pos:%u\n", segs, samples, pos); diff --git a/muse2/muse/osc.cpp b/muse2/muse/osc.cpp index 6959803e..7daeb9ff 100644 --- a/muse2/muse/osc.cpp +++ b/muse2/muse/osc.cpp @@ -841,7 +841,8 @@ bool OscIF::oscInitGui(const QString& typ, const QString& baseName, const QStrin arguments << oscUrl << filePath << name - << QString("channel-1"); + //<< QString("channel-1"); + << (titlePrefix() + label); #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscInitGui starting QProcess\n"); @@ -885,7 +886,8 @@ bool OscIF::oscInitGui(const QString& typ, const QString& baseName, const QStrin oscUrl.toLatin1().constData(), filePath.toLatin1().constData(), name.toLatin1().constData(), - "channel 1", (void*)0); + //"channel 1", (void*)0); + label.toLatin1().constData(), (void*)0); // Should not return after execlp. If so it's an error. fprintf(stderr, "exec %s %s %s %s %s failed: %s\n", @@ -941,13 +943,13 @@ void OscIF::oscShowGui(bool v) } //for (int i = 0; i < 5; ++i) { - for (int i = 0; i < 10; ++i) { // Give it a wee bit more time? + for (int i = 0; i < 20; ++i) { // Give it a wee bit more time? if (_uiOscPath) break; sleep(1); } if (_uiOscPath == 0) { - printf("OscIF::oscShowGui(): no _uiOscPath. Error: Timeout - synth gui did not start within 10 seconds.\n"); + printf("OscIF::oscShowGui(): no _uiOscPath. Error: Timeout - synth gui did not start within 20 seconds.\n"); return; } @@ -1091,10 +1093,14 @@ bool OscDssiIF::oscInitGui() _oscSynthIF->dssiSynth()->name(), _oscSynthIF->dssiSynthI()->name(), _oscSynthIF->dssiSynth()->fileName(), _oscSynthIF->dssi_ui_filename()); } + +QString OscDssiIF::titlePrefix() const +{ + return _oscSynthIF ? _oscSynthIF->titlePrefix() : QString(); +} #endif // DSSI_SUPPORT - //--------------------------------------------------------- // OscEffectIF:: // oscSetPluginI @@ -1165,6 +1171,11 @@ bool OscEffectIF::oscInitGui() _oscPluginI->plugin()->fileName(), _oscPluginI->dssi_ui_filename()); } +QString OscEffectIF::titlePrefix() const +{ + return _oscPluginI ? _oscPluginI->titlePrefix() : QString(); +} + #else //OSC_SUPPORT void initOSC() {} diff --git a/muse2/muse/osc.h b/muse2/muse/osc.h index 30df03cb..e3f1a26d 100644 --- a/muse2/muse/osc.h +++ b/muse2/muse/osc.h @@ -74,6 +74,8 @@ class OscIF virtual bool oscInitGui() { return false; } virtual void oscShowGui(bool); virtual bool oscGuiVisible() const; + + virtual QString titlePrefix() const { return QString(); } }; class OscEffectIF : public OscIF @@ -95,6 +97,8 @@ class OscEffectIF : public OscIF virtual int oscConfigure(lo_arg**); virtual bool oscInitGui(); + + virtual QString titlePrefix() const; }; #ifdef DSSI_SUPPORT @@ -117,6 +121,8 @@ class OscDssiIF : public OscIF virtual int oscConfigure(lo_arg**); virtual bool oscInitGui(); + + virtual QString titlePrefix() const; }; #endif // DSSI_SUPPORT diff --git a/muse2/muse/part.cpp b/muse2/muse/part.cpp index eb5b1af6..da01a5d5 100644 --- a/muse2/muse/part.cpp +++ b/muse2/muse/part.cpp @@ -750,6 +750,18 @@ WavePart::WavePart(const WavePart& p) : Part(p) Part::~Part() { + if (_prevClone!=this || _nextClone!=this) + { + printf("THIS MIGHT BE A HINT FOR BUGS: Part isn't unchained in ~Part()! i'll do that now. this is\n" + "not an actual bug, actually that manual unchain should be unneccessary if this was coded\n" + "properly. but as it wasn't, and the unchain was always done manually, this might be an\n" + "indicator that it have been forgotten. either your computer will explode in 3..2..1..now,\n" + "or you can ignore this message.\n" + "\n"); + + unchainClone(this); + } + _events->incRef(-1); if (_events->refCount() <= 0) delete _events; diff --git a/muse2/muse/plugin.cpp b/muse2/muse/plugin.cpp index 2bdd0317..227a9daf 100644 --- a/muse2/muse/plugin.cpp +++ b/muse2/muse/plugin.cpp @@ -1573,65 +1573,60 @@ bool Pipeline::nativeGuiVisible(int idx) //--------------------------------------------------------- // apply +// If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- -//void Pipeline::apply(int ports, unsigned long nframes, float** buffer1) void Pipeline::apply(unsigned long ports, unsigned long nframes, float** buffer1) { // prepare a second set of buffers in case a plugin is not // capable of inPlace processing - - // Removed by Tim. p3.3.15 //float* buffer2[ports]; //float data[nframes * ports]; //for (int i = 0; i < ports; ++i) // buffer2[i] = data + i * nframes; - // p3.3.41 - //fprintf(stderr, "Pipeline::apply data: nframes:%lu %e %e %e %e\n", nframes, buffer1[0][0], buffer1[0][1], buffer1[0][2], buffer1[0][3]); + //fprintf(stderr, "Pipeline::apply data: nframes:%lu %e %e %e %e\n", nframes, buffer1[0][0], buffer1[0][1], buffer1[0][2], buffer1[0][3]); bool swap = false; for (iPluginI ip = begin(); ip != end(); ++ip) { PluginI* p = *ip; - if (p && p->on()) { - if (p->inPlaceCapable()) - { - if (swap) - //p->connect(ports, buffer2, buffer2); - //p->connect(ports, buffer, buffer); - p->apply(nframes, ports, buffer, buffer); // p4.0.21 - else - //p->connect(ports, buffer1, buffer1); - p->apply(nframes, ports, buffer1, buffer1); // - } - else - { - if (swap) - //p->connect(ports, buffer2, buffer1); - //p->connect(ports, buffer, buffer1); - p->apply(nframes, ports, buffer, buffer1); // - else - //p->connect(ports, buffer1, buffer2); - //p->connect(ports, buffer1, buffer); - p->apply(nframes, ports, buffer1, buffer); // - swap = !swap; - } - //p->apply(nframes); // Rem. p4.0.21 - } + + if(p) + { + //if (p && p->on()) { + if (p->on()) + { + //fprintf(stderr, "Pipeline::apply PluginI:%p on:%d\n", p, p->on()); + if (p->inPlaceCapable()) + { + if (swap) + p->apply(nframes, ports, buffer, buffer); + else + p->apply(nframes, ports, buffer1, buffer1); + } + else + { + if (swap) + p->apply(nframes, ports, buffer, buffer1); + else + p->apply(nframes, ports, buffer1, buffer); + swap = !swap; + } + } + else + { + p->apply(nframes, 0, 0, 0); // Do not process (run) audio, process controllers only. + } } - if (swap) + } + if (ports != 0 && swap) { - //for (int i = 0; i < ports; ++i) - for (unsigned long i = 0; i < ports; ++i) // p4.0.21 + for (unsigned long i = 0; i < ports; ++i) //memcpy(buffer1[i], buffer2[i], sizeof(float) * nframes); //memcpy(buffer1[i], buffer[i], sizeof(float) * nframes); AL::dsp->cpy(buffer1[i], buffer[i], nframes); } - - // p3.3.41 - //fprintf(stderr, "Pipeline::apply after data: nframes:%lu %e %e %e %e\n", nframes, buffer1[0][0], buffer1[0][1], buffer1[0][2], buffer1[0][3]); - } //--------------------------------------------------------- @@ -2439,6 +2434,7 @@ void PluginI::showGui() if (_plugin) { if (_gui == 0) makeGui(); + _gui->setWindowTitle(titlePrefix() + name()); if (_gui->isVisible()) _gui->hide(); else @@ -2558,7 +2554,17 @@ void PluginI::enable2AllControllers(bool v) } //--------------------------------------------------------- +// titlePrefix +//--------------------------------------------------------- + +QString PluginI::titlePrefix() const +{ + return _track->name() + QString(": "); +} + +//--------------------------------------------------------- // apply +// If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- /* @@ -2653,7 +2659,6 @@ void PluginI::apply(unsigned long n) */ #if 1 -// p4.0.21 void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float** bufOut) { // Process control value changes. @@ -2769,7 +2774,6 @@ void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float** //controls[v.idx].val = v.value; controls[v.idx].tmpVal = v.value; - /* // Need to update the automation value, otherwise it overwrites later with the last MusEGlobal::automation value. if(_track && _id != -1) { @@ -2795,7 +2799,6 @@ void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float** // enableController(k, false); //_track->recordAutomation(id, v.value); } - */ } // Now update the actual values from the temporary values... @@ -2813,20 +2816,23 @@ void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float** nsamp = n - sample; //printf("PluginI::apply ports:%lu n:%lu frame:%lu sample:%lu nsamp:%lu syncFrame:%lu loopcount:%d\n", - // ports, n, frame, sample, nsamp, syncFrame, loopcount); // REMOVE Tim. + // ports, n, frame, sample, nsamp, syncFrame, loopcount); // Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). if(nsamp == 0) continue; - connect(ports, sample, bufIn, bufOut); + if(ports != 0) + { + connect(ports, sample, bufIn, bufOut); - for(int i = 0; i < instances; ++i) - { - //fprintf(stderr, "PluginI::apply handle %d\n", i); - _plugin->apply(handle[i], nsamp); - } + for(int i = 0; i < instances; ++i) + { + //fprintf(stderr, "PluginI::apply handle %d\n", i); + _plugin->apply(handle[i], nsamp); + } + } sample += nsamp; loopcount++; // REMOVE Tim. @@ -3444,7 +3450,8 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) params = 0; paramsOut = 0; plugin = p; - setWindowTitle(plugin->name()); + //setWindowTitle(plugin->name()); + setWindowTitle(plugin->titlePrefix() + plugin->name()); QToolBar* tools = addToolBar(tr("File Buttons")); @@ -4106,16 +4113,18 @@ void PluginGui::save() void PluginGui::bypassToggled(bool val) { + setWindowTitle(plugin->titlePrefix() + plugin->name()); plugin->setOn(val); MusEGlobal::song->update(SC_ROUTE); } //--------------------------------------------------------- -// songChanged +// setOn //--------------------------------------------------------- void PluginGui::setOn(bool val) { + setWindowTitle(plugin->titlePrefix() + plugin->name()); onOff->blockSignals(true); onOff->setChecked(val); onOff->blockSignals(false); @@ -4208,11 +4217,11 @@ void PluginGui::updateControls() } - if(!MusEGlobal::automation) - return; - AutomationType at = plugin->track()->automationType(); - if(at == AUTO_OFF) - return; + //if(!MusEGlobal::automation) + // return; + //AutomationType at = plugin->track()->automationType(); + //if(at == AUTO_OFF) + // return; if (params) { //for (int i = 0; i < plugin->parameters(); ++i) { for (unsigned long i = 0; i < plugin->parameters(); ++i) { // p4.0.21 diff --git a/muse2/muse/plugin.h b/muse2/muse/plugin.h index 717f98b6..044fd863 100644 --- a/muse2/muse/plugin.h +++ b/muse2/muse/plugin.h @@ -72,18 +72,6 @@ class Xml; class MidiController; -/* -//--------------------------------------------------------- -// PluginBase -//--------------------------------------------------------- - -class PluginBase -{ - protected: - void range(unsigned long i, float*, float*) const; -}; -*/ - //--------------------------------------------------------- // Plugin //--------------------------------------------------------- @@ -155,13 +143,11 @@ class Plugin { if (plugin && plugin->cleanup) plugin->cleanup(handle); } - //void connectPort(LADSPA_Handle handle, int port, float* value) { - void connectPort(LADSPA_Handle handle, unsigned long port, float* value) { // p4.0.21 + void connectPort(LADSPA_Handle handle, unsigned long port, float* value) { if(plugin) plugin->connect_port(handle, port, value); } - //void apply(LADSPA_Handle handle, int n) { - void apply(LADSPA_Handle handle, unsigned long n) { // p4.0.21 + void apply(LADSPA_Handle handle, unsigned long n) { if(plugin) plugin->run(handle, n); } @@ -170,12 +156,10 @@ class Plugin { int oscConfigure(LADSPA_Handle /*handle*/, const char* /*key*/, const char* /*value*/); #endif - //int ports() { return plugin ? plugin->PortCount : 0; } unsigned long ports() { return _portCount; } LADSPA_PortDescriptor portd(unsigned long k) const { return plugin ? plugin->PortDescriptors[k] : 0; - //return _portDescriptors[k]; } LADSPA_PortRangeHint range(unsigned long i) { @@ -184,8 +168,7 @@ class Plugin { return plugin->PortRangeHints[i]; } - //double defaultValue(unsigned long port) const; - float defaultValue(unsigned long port) const; // p4.0.21 + float defaultValue(unsigned long port) const; void range(unsigned long i, float*, float*) const; CtrlValueType ctrlValueType(unsigned long /*i*/) const; CtrlList::Mode ctrlMode(unsigned long /*i*/) const; @@ -202,20 +185,6 @@ class Plugin { unsigned long controlInPorts() const { return _controlInPorts; } unsigned long controlOutPorts() const { return _controlOutPorts; } bool inPlaceCapable() const { return _inPlaceCapable; } - - /* - bool isLog(int k) const { - LADSPA_PortRangeHint r = plugin->PortRangeHints[pIdx[k]]; - return LADSPA_IS_HINT_LOGARITHMIC(r.HintDescriptor); - } - bool isBool(int k) const { - return LADSPA_IS_HINT_TOGGLED(plugin->PortRangeHints[pIdx[k]].HintDescriptor); - } - bool isInt(int k) const { - LADSPA_PortRangeHint r = plugin->PortRangeHints[pIdx[k]]; - return LADSPA_IS_HINT_INTEGER(r.HintDescriptor); - } - */ }; typedef std::list<Plugin>::iterator iPlugin; @@ -266,21 +235,18 @@ class PluginIBase ~PluginIBase(); virtual bool on() const = 0; virtual void setOn(bool /*val*/) = 0; - //virtual int pluginID() = 0; - virtual unsigned long pluginID() = 0; // p4.0.21 + virtual unsigned long pluginID() = 0; virtual int id() = 0; virtual QString pluginLabel() const = 0; virtual QString name() const = 0; virtual QString lib() const = 0; virtual QString dirPath() const = 0; virtual QString fileName() const = 0; + virtual QString titlePrefix() const = 0; virtual AudioTrack* track() = 0; - //virtual void enableController(int /*i*/, bool /*v*/ = true) = 0; - //virtual bool controllerEnabled(int /*i*/) const = 0; - //virtual bool controllerEnabled2(int /*i*/) const = 0; - virtual void enableController(unsigned long /*i*/, bool /*v*/ = true) = 0; // p4.0.21 + virtual void enableController(unsigned long /*i*/, bool /*v*/ = true) = 0; virtual bool controllerEnabled(unsigned long /*i*/) const = 0; virtual bool controllerEnabled2(unsigned long /*i*/) const = 0; virtual void updateControllers() = 0; @@ -288,12 +254,7 @@ class PluginIBase virtual void writeConfiguration(int /*level*/, Xml& /*xml*/) = 0; virtual bool readConfiguration(Xml& /*xml*/, bool /*readPreset*/=false) = 0; - //virtual int parameters() const = 0; - //virtual void setParam(int /*i*/, double /*val*/) = 0; - //virtual double param(int /*i*/) const = 0; - //virtual const char* paramName(int /*i*/) = 0; - //virtual LADSPA_PortRangeHint range(int /*i*/) = 0; - virtual unsigned long parameters() const = 0; // p4.0.21 + virtual unsigned long parameters() const = 0; virtual unsigned long parametersOut() const = 0; virtual void setParam(unsigned long /*i*/, float /*val*/) = 0; virtual float param(unsigned long /*i*/) const = 0; @@ -307,8 +268,6 @@ class PluginIBase virtual CtrlList::Mode ctrlMode(unsigned long /*i*/) const = 0; QString dssi_ui_filename() const; - //virtual void showGui(bool) = 0; // p4.0.20 - //virtual void showNativeGui(bool) = 0; // MusEGui::PluginGui* gui() const { return _gui; } void deleteGui(); }; @@ -353,7 +312,6 @@ class PluginBase #define AUDIO_IN (LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT) #define AUDIO_OUT (LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT) -//class PluginI { class PluginI : public PluginIBase { Plugin* _plugin; int channel; @@ -365,10 +323,8 @@ class PluginI : public PluginIBase { Port* controls; Port* controlsOut; - //int controlPorts; - //int controlOutPorts; - unsigned long controlPorts; // p4.0.21 - unsigned long controlOutPorts; // + unsigned long controlPorts; + unsigned long controlOutPorts; ///PluginGui* _gui; bool _on; @@ -400,26 +356,17 @@ class PluginI : public PluginIBase { void setTrack(AudioTrack* t) { _track = t; } AudioTrack* track() { return _track; } - //int pluginID() { return _plugin->id(); } - unsigned long pluginID() { return _plugin->id(); } // p4.0.21 + unsigned long pluginID() { return _plugin->id(); } void setID(int i); int id() { return _id; } void updateControllers(); bool initPluginInstance(Plugin*, int channels); void setChannels(int); - //void connect(int ports, float** src, float** dst); - //void apply(int n); - //void connect(unsigned ports, float** src, float** dst); - //void apply(unsigned n); - void connect(unsigned long ports, unsigned long offset, float** src, float** dst); // p4.0.21 - void apply(unsigned long n, unsigned long ports, float** bufIn, float** bufOut); // - - //void enableController(int i, bool v = true) { controls[i].enCtrl = v; } - //bool controllerEnabled(int i) const { return controls[i].enCtrl; } - //void enable2Controller(int i, bool v = true) { controls[i].en2Ctrl = v; } - //bool controllerEnabled2(int i) const { return controls[i].en2Ctrl; } - void enableController(unsigned long i, bool v = true) { controls[i].enCtrl = v; } // p4.0.21 + void connect(unsigned long ports, unsigned long offset, float** src, float** dst); + void apply(unsigned long n, unsigned long ports, float** bufIn, float** bufOut); + + void enableController(unsigned long i, bool v = true) { controls[i].enCtrl = v; } bool controllerEnabled(unsigned long i) const { return controls[i].enCtrl; } void enable2Controller(unsigned long i, bool v = true) { controls[i].en2Ctrl = v; } bool controllerEnabled2(unsigned long i) const { return controls[i].en2Ctrl; } @@ -434,6 +381,7 @@ class PluginI : public PluginIBase { QString lib() const { return _plugin->lib(); } QString dirPath() const { return _plugin->dirPath(); } QString fileName() const { return _plugin->fileName(); } + QString titlePrefix() const; #ifdef OSC_SUPPORT OscEffectIF& oscIF() { return _oscif; } @@ -453,8 +401,7 @@ class PluginI : public PluginIBase { void writeConfiguration(int level, Xml& xml); bool readConfiguration(Xml& xml, bool readPreset=false); bool loadControl(Xml& xml); - //bool setControl(const QString& s, double val); - bool setControl(const QString& s, float val); // p4.0.21 + bool setControl(const QString& s, float val); void showGui(); void showGui(bool); bool isDssiPlugin() const { return _plugin->isDssiPlugin(); } @@ -464,20 +411,8 @@ class PluginI : public PluginIBase { bool guiVisible(); bool nativeGuiVisible(); - //int parameters() const { return controlPorts; } - //void setParam(int i, double val) { controls[i].tmpVal = val; } - //double param(int i) const { return controls[i].val; } - //double defaultValue(unsigned int param) const; - //const char* paramName(int i) { return _plugin->portName(controls[i].idx); } - //LADSPA_PortDescriptor portd(int i) const { return _plugin->portd(controls[i].idx); } - //void range(int i, float* min, float* max) const { _plugin->range(controls[i].idx, min, max); } - //bool isAudioIn(int k) { return (_plugin->portd(k) & AUDIO_IN) == AUDIO_IN; } - //bool isAudioOut(int k) { return (_plugin->portd(k) & AUDIO_OUT) == AUDIO_OUT; } - //LADSPA_PortRangeHint range(int i) { return _plugin->range(controls[i].idx); } - // p4.0.21 unsigned long parameters() const { return controlPorts; } unsigned long parametersOut() const { return controlOutPorts; } - //void setParam(unsigned i, float val) { controls[i].tmpVal = val; } void setParam(unsigned long i, float val); float param(unsigned long i) const { return controls[i].val; } float paramOut(unsigned long i) const { return controlsOut[i].val; } @@ -500,8 +435,6 @@ class PluginI : public PluginIBase { // chain of connected efx inserts //--------------------------------------------------------- -//const int PipelineDepth = 4; // Moved to globaldefs.h - class Pipeline : public std::vector<PluginI*> { float* buffer[MAX_CHANNELS]; @@ -525,8 +458,7 @@ class Pipeline : public std::vector<PluginI*> { void deleteAllGuis(); bool guiVisible(int); bool nativeGuiVisible(int); - //void apply(int ports, unsigned long nframes, float** buffer); - void apply(unsigned long ports, unsigned long nframes, float** buffer); // p4.0.21 + void apply(unsigned long ports, unsigned long nframes, float** buffer); void move(int idx, bool up); bool empty(int idx) const; void setChannels(int); @@ -537,11 +469,6 @@ typedef Pipeline::const_iterator ciPluginI; extern void initPlugins(); -//extern bool ladspaDefaultValue(const LADSPA_Descriptor* plugin, int port, float* val); -//extern void ladspaControlRange(const LADSPA_Descriptor* plugin, int i, float* min, float* max); -//extern bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, int port, int ctlnum, int* min, int* max, int* def); -//extern float midi2LadspaValue(const LADSPA_Descriptor* plugin, int port, int ctlnum, int val); -// p4.0.21 extern bool ladspaDefaultValue(const LADSPA_Descriptor* plugin, unsigned long port, float* val); extern void ladspaControlRange(const LADSPA_Descriptor* plugin, unsigned long port, float* min, float* max); extern bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum, int* min, int* max, int* def); @@ -593,8 +520,7 @@ struct GuiWidgets { }; QWidget* widget; int type; - //int param; - unsigned long param; // p4.0.21 + unsigned long param; }; //--------------------------------------------------------- @@ -604,13 +530,11 @@ struct GuiWidgets { class PluginGui : public QMainWindow { Q_OBJECT - //PluginI* plugin; // plugin instance MusECore::PluginIBase* plugin; // plugin instance GuiParam* params; GuiParam* paramsOut; - //int nobj; - unsigned long nobj; // number of widgets in gw // p4.0.21 + unsigned long nobj; // number of widgets in gw GuiWidgets* gw; QAction* onOff; @@ -640,7 +564,6 @@ class PluginGui : public QMainWindow { void heartBeat(); public: - //PluginGui(MusECore::PluginI*); PluginGui(MusECore::PluginIBase*); ~PluginGui(); diff --git a/muse2/muse/remote/pyapi.cpp b/muse2/muse/remote/pyapi.cpp index 645c639e..de8f15ef 100644 --- a/muse2/muse/remote/pyapi.cpp +++ b/muse2/muse/remote/pyapi.cpp @@ -1129,7 +1129,9 @@ bool Song::event(QEvent* _e) break; } case QPybridgeEvent::SONG_ADD_TRACK: - MusEGlobal::song->addTrack((Track::TrackType)e->getP1()); // Add at end of list. + MusECore::Undo operations; + MusEGlobal::song->addTrack(operations, (Track::TrackType)e->getP1()); // Add at end of list. + MusEGlobal::song->applyOperationGroup(operations); break; case QPybridgeEvent::SONG_CHANGE_TRACKNAME: { Track* t = this->findTrack(e->getS1()); diff --git a/muse2/muse/route.cpp b/muse2/muse/route.cpp index f00c6d6c..1d494fb0 100644 --- a/muse2/muse/route.cpp +++ b/muse2/muse/route.cpp @@ -4,6 +4,7 @@ // $Id: route.cpp,v 1.18.2.3 2008/05/21 00:28:52 terminator356 Exp $ // // (C) Copyright 2003-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -37,8 +38,6 @@ //#define ROUTE_DEBUG -//#define ROUTE_MIDIPORT_NAME_PREFIX "MusE MidiPort " - namespace MusECore { const QString ROUTE_MIDIPORT_NAME_PREFIX = "MusE MidiPort "; @@ -57,9 +56,7 @@ Route::Route(void* t, int ch) type = JACK_ROUTE; } -//Route::Route(AudioTrack* t, int ch) Route::Route(Track* t, int ch, int chans) -//Route::Route(Track* t, int ch) { track = t; midiPort = -1; @@ -69,7 +66,6 @@ Route::Route(Track* t, int ch, int chans) type = TRACK_ROUTE; } -//Route::Route(MidiJackDevice* d) Route::Route(MidiDevice* d, int ch) { device = d; @@ -77,19 +73,10 @@ Route::Route(MidiDevice* d, int ch) channel = ch; channels = -1; remoteChannel = -1; - /* - //if(dynamic_cast<MidiJackDevice*>(d)) - if(d->deviceType() == MidiDevice::JACK_MIDI) - type = JACK_MIDI_ROUTE; - else - //if(dynamic_cast<MidiAlsaDevice*>(d)) - if(d->deviceType() == MidiDevice::ALSA_MIDI) - type = ALSA_MIDI_ROUTE; - */ type = MIDI_DEVICE_ROUTE; } -Route::Route(int port, int ch) // p3.3.49 +Route::Route(int port, int ch) { track = 0; midiPort = port; @@ -99,16 +86,12 @@ Route::Route(int port, int ch) // p3.3.49 type = MIDI_PORT_ROUTE; } -//Route::Route(const QString& s, bool dst, int ch) Route::Route(const QString& s, bool dst, int ch, int rtype) { - //Route node(name2route(s, dst)); Route node(name2route(s, dst, rtype)); channel = node.channel; if(channel == -1) channel = ch; - //if(channels == -1) - // channels = chans; channels = node.channels; remoteChannel = node.remoteChannel; type = node.type; @@ -123,14 +106,6 @@ Route::Route(const QString& s, bool dst, int ch, int rtype) jackPort = node.jackPort; midiPort = -1; } - /* - else - if (type == JACK_MIDI_ROUTE) - device = node.device; - else - if (type == ALSA_MIDI_ROUTE) - device = node.device; - */ else if(type == MIDI_DEVICE_ROUTE) { @@ -138,10 +113,10 @@ Route::Route(const QString& s, bool dst, int ch, int rtype) midiPort = -1; } else - if(type == MIDI_PORT_ROUTE) // p3.3.49 + if(type == MIDI_PORT_ROUTE) { track = 0; - midiPort = node.midiPort; // + midiPort = node.midiPort; } } @@ -179,31 +154,20 @@ void addRoute(Route src, Route dst) // dst.type, dst.channel, dst.name().toLatin1().constData()); if (src.type == Route::JACK_ROUTE) { - //if (dst.type != TRACK_ROUTE) - //{ - // fprintf(stderr, "addRoute: bad route 1\n"); - // exit(-1); - // return; - //} - if (dst.type == Route::TRACK_ROUTE) { if (dst.track->type() != Track::AUDIO_INPUT) { fprintf(stderr, "addRoute: source is jack, dest:%s is track but not audio input\n", dst.track->name().toLatin1().constData()); - //exit(-1); return; } if (dst.channel < 0) { fprintf(stderr, "addRoute: source is jack, dest:%s is track but invalid channel:%d\n", dst.track->name().toLatin1().constData(), dst.channel); - //exit(-1); return; } - //src.channel = src.dstChannel = dst.channel; src.channel = dst.channel; - //src.channels = dst.channels = 1; RouteList* inRoutes = dst.track->inRoutes(); for (ciRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { @@ -221,28 +185,11 @@ void addRoute(Route src, Route dst) inRoutes->push_back(src); } else - //if (dst.type == Route::JACK_MIDI_ROUTE) if (dst.type == Route::MIDI_DEVICE_ROUTE) - //if (dst.type == Route::MIDI_PORT_ROUTE) // p3.3.49 { - //MidiDevice *md = MusEGlobal::midiPorts[dst.midiPort].device(); - //if(dst.device->deviceType() == MidiDevice::JACK_MIDI) - //if(!md) - //{ - // fprintf(stderr, "addRoute: source is Jack, but no destination port device\n"); - // return; - //} - if(dst.device->deviceType() == MidiDevice::JACK_MIDI) - //if(md->deviceType() == MidiDevice::JACK_MIDI) { src.channel = dst.channel; - //src.channel = -1; - //src.channel = 0; - //src.channel = src.dstChannel = dst.channel; - //src.channels = dst.channels = 1; - //dst.channel = -1; - RouteList* routes = dst.device->inRoutes(); for (ciRoute i = routes->begin(); i != routes->end(); ++i) { @@ -262,45 +209,32 @@ void addRoute(Route src, Route dst) else { fprintf(stderr, "addRoute: source is Jack, but destination is not jack midi - type:%d\n", dst.device->deviceType()); - // exit(-1); return; } } else { fprintf(stderr, "addRoute: source is Jack, but destination is not track or midi - type:%d \n", dst.type); - // exit(-1); return; } } else if (dst.type == Route::JACK_ROUTE) { - //if (src.type != TRACK_ROUTE) - //{ - // fprintf(stderr, "addRoute: bad route 3\n"); - // exit(-1); - // return; - //} - if (src.type == Route::TRACK_ROUTE) { if (src.track->type() != Track::AUDIO_OUTPUT) { fprintf(stderr, "addRoute: destination is jack, source is track but not audio output\n"); - // exit(-1); return; } if (src.channel < 0) { fprintf(stderr, "addRoute: destination is jack, source:%s is track but invalid channel:%d\n", src.track->name().toLatin1().constData(), src.channel); - // exit(-1); return; } RouteList* outRoutes = src.track->outRoutes(); - //dst.channel = dst.dstChannel = src.channel; dst.channel = src.channel; - //dst.channels = src.channels = 1; for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { @@ -318,17 +252,11 @@ void addRoute(Route src, Route dst) outRoutes->push_back(dst); } else - //if (src.type == Route::JACK_MIDI_ROUTE) if (src.type == Route::MIDI_DEVICE_ROUTE) { if(src.device->deviceType() == MidiDevice::JACK_MIDI) { dst.channel = src.channel; - //dst.channel = -1; - //src.channel = -1; - //dst.channel = dst.dstChannel = src.channel; - //dst.channels = src.channels = 1; - RouteList* routes = src.device->outRoutes(); for (ciRoute i = routes->begin(); i != routes->end(); ++i) { @@ -348,18 +276,16 @@ void addRoute(Route src, Route dst) else { fprintf(stderr, "addRoute: destination is Jack, but source is not jack midi - type:%d\n", src.device->deviceType()); - // exit(-1); return; } } else { fprintf(stderr, "addRoute: destination is Jack, but source is not track or midi - type:%d \n", src.type); - // exit(-1); return; } } - else if(src.type == Route::MIDI_PORT_ROUTE) // p3.3.49 + else if(src.type == Route::MIDI_PORT_ROUTE) { if(dst.type != Route::TRACK_ROUTE) { @@ -367,66 +293,18 @@ void addRoute(Route src, Route dst) return; } - // p4.0.14 - /* - if(dst.track->type() == Track::AUDIO_INPUT) - { - if(src.channel < 1 || src.channel >= (1 << MIDI_CHANNELS)) - { - fprintf(stderr, "addRoute: source is midi port:%d, but channel mask:%d out of range\n", src.midiPort, src.channel); - return; - } - - MidiPort *mp = &MusEGlobal::midiPorts[src.midiPort]; - //src.channel = dst.channel = -1; - RouteList* outRoutes = mp->outRoutes(); - iRoute ir = outRoutes->begin(); - for ( ; ir != outRoutes->end(); ++ir) - { - //if (*i == dst) // route already there - ir->dump(); - if (ir->type == Route::TRACK_ROUTE && ir->track == dst.track) // Does a route to the track exist? - { - //#ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: src midi port:%d dst audio in track:%s out route already exists. ir->channel:%d |= dst.channel:%d\n", - src.midiPort, dst.track->name().toLatin1().constData(), ir->channel, dst.channel); - //#endif - ir->channel |= dst.channel; // Bitwise OR the desired channel bit with the existing bit mask. - break; - //return; - } - } - if(ir == outRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. - outRoutes->push_back(dst); - - RouteList* inRoutes = dst.track->inRoutes(); - - ir = inRoutes->begin(); - for ( ; ir != inRoutes->end(); ++ir) - { - if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == src.midiPort) // Does a route to the midi port exist? - { - fprintf(stderr, "addRoute: src midi port:%d dst audio in track:%s in route already exists. ir->channel:%d |= src.channel:%d\n", - src.midiPort, dst.track->name().toLatin1().constData(), ir->channel, src.channel); - ir->channel |= src.channel; // Bitwise OR the desired channel bit with the existing bit mask. - break; - } - } - if(ir == inRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. - inRoutes->push_back(src); - - return; - } - */ - MidiPort *mp = &MusEGlobal::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); + // Do not allow ports with synth midi devices to connect to audio ins! p4.0.17 + //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; + //} + // Actually, do not allow synth ports to connect to any track. It may be useful in some cases, + // may be desired later, but for now it's just a routing hassle. p4.0.35 + if(mp->device() && mp->device()->isSynti()) return; - } if(dst.channel < 1 || dst.channel >= (1 << MIDI_CHANNELS)) { @@ -443,46 +321,39 @@ void addRoute(Route src, Route dst) src.channel = dst.channel; RouteList* outRoutes = mp->outRoutes(); - //for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - iRoute ir = outRoutes->begin(); // p3.3.50 + iRoute ir = outRoutes->begin(); for ( ; ir != outRoutes->end(); ++ir) { - //if (*i == dst) // route already there - if (ir->type == Route::TRACK_ROUTE && ir->track == dst.track) // p3.3.50 Does a route to the track exist? + if (ir->type == Route::TRACK_ROUTE && ir->track == dst.track) // Does a route to the track exist? { - //#ifdef ROUTE_DEBUG - //fprintf(stderr, "addRoute: src midi port:%d dst track:%s route already exists.\n", src.midiPort, dst.track->name().toLatin1().constData()); - //#endif - ir->channel |= dst.channel; // p3.3.50 Bitwise OR the desired channel bit with the existing bit mask. + ir->channel |= dst.channel; // Bitwise OR the desired channel bit with the existing bit mask. break; - - //return; } } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src midi port:%d dst track name:%s pushing dst and src routes\n", src.midiPort, dst.track->name().toLatin1().constData()); #endif - if(ir == outRoutes->end()) // p3.3.50 Only if route not found, add the route, with the requested channel bits as mask to start with. + if(ir == outRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. outRoutes->push_back(dst); RouteList* inRoutes = dst.track->inRoutes(); - // p3.3.50 Make sure only one single route, with a channel mask, can ever exist. + // Make sure only one single route, with a channel mask, can ever exist. ir = inRoutes->begin(); for ( ; ir != inRoutes->end(); ++ir) { - if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == src.midiPort) // p3.3.50 Does a route to the midi port exist? + if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == src.midiPort) // Does a route to the midi port exist? { - ir->channel |= src.channel; // p3.3.50 Bitwise OR the desired channel bit with the existing bit mask. + ir->channel |= src.channel; // Bitwise OR the desired channel bit with the existing bit mask. break; } } - if(ir == inRoutes->end()) // p3.3.50 Only if route not found, add the route, with the requested channel bits as mask to start with. + if(ir == inRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. inRoutes->push_back(src); } - else if(dst.type == Route::MIDI_PORT_ROUTE) // p3.3.49 + else if(dst.type == Route::MIDI_PORT_ROUTE) { if(src.type != Route::TRACK_ROUTE) { @@ -495,7 +366,6 @@ void addRoute(Route src, Route dst) return; } - //MidiDevice *md = MusEGlobal::midiPorts[dst.midiPort].device(); //if(!md) //{ @@ -506,24 +376,17 @@ void addRoute(Route src, Route dst) dst.channel = src.channel; RouteList* outRoutes = src.track->outRoutes(); - //for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - iRoute ir = outRoutes->begin(); // p3.3.50 + iRoute ir = outRoutes->begin(); for ( ; ir != outRoutes->end(); ++ir) { - //if (*i == dst) // route already there - if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == dst.midiPort) // p3.3.50 Does a route to the midi port exist? + if (ir->type == Route::MIDI_PORT_ROUTE && ir->midiPort == dst.midiPort) // Does a route to the midi port exist? { - //#ifdef ROUTE_DEBUG - //fprintf(stderr, "addRoute: dst midi port:%d src track:%s route already exists.\n", dst.midiPort, src.track->name().toLatin1().constData()); - //#endif - //return; - - ir->channel |= dst.channel; // p3.3.50 Bitwise OR the desired channel bit with the existing bit mask. + ir->channel |= dst.channel; // Bitwise OR the desired channel bit with the existing bit mask. break; } } - if(ir == outRoutes->end()) // p3.3.50 Only if route not found, add the route, with the requested channel bits as mask to start with. + if(ir == outRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. outRoutes->push_back(dst); MidiPort *mp = &MusEGlobal::midiPorts[dst.midiPort]; @@ -533,167 +396,97 @@ void addRoute(Route src, Route dst) #endif RouteList* inRoutes = mp->inRoutes(); - // p3.3.50 Make sure only one single route, with a channel mask, can ever exist. + // Make sure only one single route, with a channel mask, can ever exist. ir = inRoutes->begin(); for ( ; ir != inRoutes->end(); ++ir) { - if (ir->type == Route::TRACK_ROUTE && ir->track == src.track) // p3.3.50 Does a route to the track exist? + if (ir->type == Route::TRACK_ROUTE && ir->track == src.track) // Does a route to the track exist? { - ir->channel |= src.channel; // p3.3.50 Bitwise OR the desired channel bit with the existing bit mask. + ir->channel |= src.channel; // Bitwise OR the desired channel bit with the existing bit mask. break; } } - if(ir == inRoutes->end()) // p3.3.50 Only if route not found, add the route, with the requested channel bits as mask to start with. + if(ir == inRoutes->end()) // Only if route not found, add the route, with the requested channel bits as mask to start with. inRoutes->push_back(src); - //inRoutes->insert(inRoutes->begin(), src); } else { - if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) // p3.3.49 + if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) { fprintf(stderr, "addRoute: source or destination are not track routes\n"); return; } - // Removed p3.3.49 - /* - //if ((src.type == Route::JACK_MIDI_ROUTE) || (src.type == Route::ALSA_MIDI_ROUTE)) - if(src.type == Route::MIDI_DEVICE_ROUTE) - { - //src.channel = src.dstChannel = dst.dstChannel = dst.channel; - src.channel = dst.channel; - //src.channels = dst.channels = 1; - RouteList* outRoutes = src.device->outRoutes(); - #ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: src name: %s looking for existing dest in out routes...\n", src.device->name().toLatin1().constData()); - #endif - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - { - if (*i == dst) // route already there - { - //#ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: src Jack or ALSA midi route already exists.\n"); - //#endif - return; - } - } - #ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: src midi dst name: %s pushing destination and source routes\n", dst.track->name().toLatin1().constData()); - #endif - - outRoutes->push_back(dst); - RouteList* inRoutes = dst.track->inRoutes(); - inRoutes->push_back(src); - } - else - */ + RouteList* outRoutes = src.track->outRoutes(); - { - ///if(dst.type == Route::MIDI_DEVICE_ROUTE) // Removed p3.3.49 - //{ - /// dst.channel = src.channel; - //src.channel = src.dstChannel = dst.dstChannel = dst.channel; - //src.channels = dst.channels = 1; - //} - //else - //{ - //src.channel = src.dstChannel = dst.dstChannel = dst.channel; - //src.channels = dst.channels = 1; - //} - - RouteList* outRoutes = src.track->outRoutes(); - - // - // Must enforce to ensure channel and channels are valid if defaults of -1 passed. - // - if(src.track->type() == Track::AUDIO_SOFTSYNTH) - { - if(src.channel == -1) - src.channel = 0; - if(src.channels == -1) - src.channels = src.track->channels(); - //if(dst.type == Route::TRACK_ROUTE) // p3.3.49 Removed - //{ - //if(dst.channel == -1) - // dst.channel = 0; - //if(dst.channels == -1) - // Yes, that's correct: dst channels = src track channels. - // dst.channels = src.track->channels(); - dst.channel = src.channel; - dst.channels = src.channels; - dst.remoteChannel = src.remoteChannel; - //} - } - //if(dst.type == Route::TRACK_ROUTE && dst.track->type() == Track::AUDIO_SOFTSYNTH) - //{ - // if(dst.channel == -1) - // dst.channel = 0; - // if(dst.channels == -1) - // Yes, that's correct: dst channels = src track channels. - // dst.channels = src.track->channels(); - //} - - for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - { - if (*i == dst) // route already there - // TODO: - //if (i->type == dst.type && i->channel == dst.channel) + // + // Must enforce to ensure channel and channels are valid if defaults of -1 passed. + // + if(src.track->type() == Track::AUDIO_SOFTSYNTH) + { + if(src.channel == -1) + src.channel = 0; + if(src.channels == -1) + src.channels = src.track->channels(); + dst.channel = src.channel; + dst.channels = src.channels; + dst.remoteChannel = src.remoteChannel; + } + + for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) + { + if (*i == dst) // route already there + // TODO: + //if (i->type == dst.type && i->channel == dst.channel) + { + //if(i->type == Route::TRACK_ROUTE) + { + //if(i->track == dst.track) { - //if(i->type == Route::TRACK_ROUTE) + //if(i->channels == dst.channels) { - //if(i->track == dst.track) - { - //if(i->channels == dst.channels) - { - //#ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: src track route already exists.\n"); - //#endif - return; - } - //else - //{ - - //} - } + //#ifdef ROUTE_DEBUG + fprintf(stderr, "addRoute: src track route already exists.\n"); + //#endif + return; } - } - } - outRoutes->push_back(dst); - RouteList* inRoutes; - - // Removed p3.3.49 - /* - //if ((dst.type == Route::JACK_MIDI_ROUTE) || (dst.type == Route::ALSA_MIDI_ROUTE)) - if(dst.type == Route::MIDI_DEVICE_ROUTE) - { - #ifdef ROUTE_DEBUG - fprintf(stderr, "addRoute: src track dst midi name: %s pushing destination and source routes\n", dst.device->name().toLatin1().constData()); - #endif - inRoutes = dst.device->inRoutes(); - } - else - */ - - { - #ifdef ROUTE_DEBUG - //fprintf(stderr, "addRoute: src track ch:%d chs:%d dst track ch:%d chs:%d name: %s pushing destination and source routes\n", src.channel, src.channels, dst.channel, dst.channels, dst.track->name().toLatin1().constData()); - fprintf(stderr, "addRoute: src track ch:%d chs:%d remch:%d dst track ch:%d chs:%d remch:%d name: %s pushing dest and source routes\n", - src.channel, src.channels, src.remoteChannel, dst.channel, dst.channels, dst.remoteChannel, dst.track->name().toLatin1().constData()); - //fprintf(stderr, "addRoute: src track ch:%d dst track ch:%d name: %s pushing destination and source routes\n", src.channel, dst.channel, dst.track->name().toLatin1().constData()); - #endif - inRoutes = dst.track->inRoutes(); - } - - - // - // make sure AUDIO_AUX is processed last - // - if (src.track->type() == Track::AUDIO_AUX) - inRoutes->push_back(src); - else - inRoutes->insert(inRoutes->begin(), src); - } + //else + //{ + + //} + } + } + } + } + outRoutes->push_back(dst); + RouteList* inRoutes; + + #ifdef ROUTE_DEBUG + //fprintf(stderr, "addRoute: src track ch:%d chs:%d dst track ch:%d chs:%d name: %s pushing destination and source routes\n", src.channel, src.channels, dst.channel, dst.channels, dst.track->name().toLatin1().constData()); + fprintf(stderr, "addRoute: src track ch:%d chs:%d remch:%d dst track ch:%d chs:%d remch:%d name: %s pushing dest and source routes\n", + src.channel, src.channels, src.remoteChannel, dst.channel, dst.channels, dst.remoteChannel, dst.track->name().toLatin1().constData()); + //fprintf(stderr, "addRoute: src track ch:%d dst track ch:%d name: %s pushing destination and source routes\n", src.channel, dst.channel, dst.track->name().toLatin1().constData()); + #endif + inRoutes = dst.track->inRoutes(); + + // + // make sure AUDIO_AUX is processed last + // + if (src.track->type() == Track::AUDIO_AUX) // REMOVE Tim. This special aux code may not be useful or needed now. + inRoutes->push_back(src); // + else + inRoutes->insert(inRoutes->begin(), src); + + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update the destination track's aux ref count, and all tracks it is routed to. + if(src.track->auxRefCount()) + //dst.track->updateAuxStates( src.track->auxRefCount() ); + src.track->updateAuxRoute( src.track->auxRefCount(), dst.track ); + else + if(src.track->type() == Track::AUDIO_AUX) + //dst.track->updateAuxStates( 1 ); + src.track->updateAuxRoute( 1, dst.track ); } } @@ -709,12 +502,6 @@ void removeRoute(Route src, Route dst) if (src.type == Route::JACK_ROUTE) { - //if (dst.type != TRACK_ROUTE) - //{ - // fprintf(stderr, "removeRoute: bad route 1\n"); - // exit(-1); - // return; - //} if(!dst.isValid()) { printf("removeRoute: source is jack, invalid destination\n"); @@ -726,7 +513,6 @@ void removeRoute(Route src, Route dst) if (dst.track->type() != Track::AUDIO_INPUT) { fprintf(stderr, "removeRoute: source is jack, destination is track but not audio input\n"); - // exit(-1); return; } RouteList* inRoutes = dst.track->inRoutes(); @@ -741,7 +527,6 @@ void removeRoute(Route src, Route dst) } } else - //if (dst.type == Route::JACK_MIDI_ROUTE) if (dst.type == Route::MIDI_DEVICE_ROUTE) { RouteList* routes = dst.device->inRoutes(); @@ -758,18 +543,11 @@ void removeRoute(Route src, Route dst) else { fprintf(stderr, "removeRoute: source is jack, destination unknown\n"); - // exit(-1); return; } } else if (dst.type == Route::JACK_ROUTE) { - //if (src.type != TRACK_ROUTE) - //{ - // fprintf(stderr, "removeRoute: bad route 3\n"); - // exit(-1); - // return; - //} if(!src.isValid()) { printf("removeRoute: destination is jack, invalid source\n"); @@ -781,7 +559,6 @@ void removeRoute(Route src, Route dst) if (src.track->type() != Track::AUDIO_OUTPUT) { fprintf(stderr, "removeRoute: destination is jack, source is track but not audio output\n"); - // exit(-1); return; } RouteList* outRoutes = src.track->outRoutes(); @@ -795,7 +572,6 @@ void removeRoute(Route src, Route dst) } } else - //if (src.type == Route::JACK_MIDI_ROUTE) if (src.type == Route::MIDI_DEVICE_ROUTE) { RouteList* routes = src.device->outRoutes(); @@ -811,11 +587,10 @@ void removeRoute(Route src, Route dst) else { fprintf(stderr, "removeRoute: destination is jack, source unknown\n"); - // exit(-1); return; } } - else if(src.type == Route::MIDI_PORT_ROUTE) // p3.3.49 + else if(src.type == Route::MIDI_PORT_ROUTE) { if(dst.type != Route::TRACK_ROUTE) { @@ -829,19 +604,18 @@ void removeRoute(Route src, Route dst) RouteList* outRoutes = mp->outRoutes(); for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { - //if (*i == dst) - if(i->type == Route::TRACK_ROUTE && i->track == dst.track) // p3.3.50 Is there a route to the track? + if(i->type == Route::TRACK_ROUTE && i->track == dst.track) // Is there a route to the track? { //printf("i->channel:%x dst.channel:%x\n", i->channel, dst.channel); - i->channel &= ~dst.channel; // p3.3.50 Unset the desired channel bits. + i->channel &= ~dst.channel; // Unset the desired channel bits. if(i->channel == 0) // Only if there are no channel bits set, erase the route. { //printf("erasing out route from midi port:%d\n", src.midiPort); outRoutes->erase(i); } - break; // For safety, keep looking and remove any more found. - // No, must break, else crash. There should only be one route anyway... + break; // For safety, keep looking and remove any more found. + // No, must break, else crash. There should only be one route anyway... } } } @@ -853,22 +627,21 @@ void removeRoute(Route src, Route dst) RouteList* inRoutes = dst.track->inRoutes(); for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { - //if (*i == src) - if (i->type == Route::MIDI_PORT_ROUTE && i->midiPort == src.midiPort) // p3.3.50 Is there a route to the midi port? + if (i->type == Route::MIDI_PORT_ROUTE && i->midiPort == src.midiPort) // Is there a route to the midi port? { - i->channel &= ~src.channel; // p3.3.50 Unset the desired channel bits. + i->channel &= ~src.channel; // Unset the desired channel bits. if(i->channel == 0) // Only if there are no channel bits set, erase the route. inRoutes->erase(i); - break; // For safety, keep looking and remove any more found. - // No, must break, else crash. There should only be one route anyway... + break; // For safety, keep looking and remove any more found. + // No, must break, else crash. There should only be one route anyway... } } } else printf("removeRoute: source is midi port:%d but destination track invalid\n", src.midiPort); } - else if(dst.type == Route::MIDI_PORT_ROUTE) // p3.3.49 + else if(dst.type == Route::MIDI_PORT_ROUTE) { if(src.type != Route::TRACK_ROUTE) { @@ -881,15 +654,14 @@ void removeRoute(Route src, Route dst) RouteList* outRoutes = src.track->outRoutes(); for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { - //if (*i == dst) - if (i->type == Route::MIDI_PORT_ROUTE && i->midiPort == dst.midiPort) // p3.3.50 Is there a route to the midi port? + if (i->type == Route::MIDI_PORT_ROUTE && i->midiPort == dst.midiPort) // Is there a route to the midi port? { - i->channel &= ~dst.channel; // p3.3.50 Unset the desired channel bits. + i->channel &= ~dst.channel; // Unset the desired channel bits. if(i->channel == 0) // Only if there are no channel bits set, erase the route. outRoutes->erase(i); - break; // For safety, keep looking and remove any more found. - // No, must break, else crash. There should only be one route anyway... + break; // For safety, keep looking and remove any more found. + // No, must break, else crash. There should only be one route anyway... } } } @@ -902,15 +674,14 @@ void removeRoute(Route src, Route dst) RouteList* inRoutes = mp->inRoutes(); for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { - //if (*i == src) - if (i->type == Route::TRACK_ROUTE && i->track == src.track) // p3.3.50 Is there a route to the track? + if (i->type == Route::TRACK_ROUTE && i->track == src.track) // Is there a route to the track? { - i->channel &= ~src.channel; // p3.3.50 Unset the desired channel bits. + i->channel &= ~src.channel; // Unset the desired channel bits. if(i->channel == 0) // Only if there are no channel bits set, erase the route. inRoutes->erase(i); - break; // For safety, keep looking and remove any more found. - // No, must break, else crash. There should only be one route anyway... + break; // For safety, keep looking and remove any more found. + // No, must break, else crash. There should only be one route anyway... } } } @@ -919,87 +690,54 @@ void removeRoute(Route src, Route dst) } else { - if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) // p3.3.49 + if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) { fprintf(stderr, "removeRoute: source and destination are not tracks\n"); return; } - // Removed p3.3.49 - /* - //if((src.type == Route::JACK_MIDI_ROUTE) || (src.type == Route::ALSA_MIDI_ROUTE)) - if(src.type == Route::MIDI_DEVICE_ROUTE) + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update the destination track's aux ref count, and all tracks it is routed to. + if(src.isValid() && dst.isValid()) { - if(src.isValid()) - { - RouteList* outRoutes = src.device->outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - { - if (*i == dst) { - outRoutes->erase(i); - break; - } - } - } + if(src.track->auxRefCount()) + //dst.track->updateAuxStates( -src.track->auxRefCount() ); + src.track->updateAuxRoute( -src.track->auxRefCount(), dst.track ); else - printf("removeRoute: source is midi but invalid\n"); - - if(dst.isValid()) + if(src.track->type() == Track::AUDIO_AUX) + //dst.track->updateAuxStates( -1 ); + src.track->updateAuxRoute( -1, dst.track ); + } + + if(src.isValid()) + { + RouteList* outRoutes = src.track->outRoutes(); + for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) { - RouteList* inRoutes = dst.track->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) - { - if (*i == src) { - inRoutes->erase(i); - break; - } - } + if (*i == dst) { + outRoutes->erase(i); + break; + } } - else - printf("removeRoute: source is midi but destination invalid\n"); - } + } else - */ + printf("removeRoute: source is track but invalid\n"); + if(dst.isValid()) { - if(src.isValid()) - { - RouteList* outRoutes = src.track->outRoutes(); - for (iRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) - { - if (*i == dst) { - outRoutes->erase(i); - break; - } - } - } - else - printf("removeRoute: source is track but invalid\n"); + RouteList* inRoutes; - if(dst.isValid()) + inRoutes = dst.track->inRoutes(); + for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) { - RouteList* inRoutes; - - //if ((dst.type == Route::JACK_MIDI_ROUTE) || (dst.type == Route::ALSA_MIDI_ROUTE)) - // Removed p3.3.49 - /* - if (dst.type == Route::MIDI_DEVICE_ROUTE) - inRoutes = dst.device->inRoutes(); - else - */ - - inRoutes = dst.track->inRoutes(); - for (iRoute i = inRoutes->begin(); i != inRoutes->end(); ++i) - { - if (*i == src) { - inRoutes->erase(i); - break; - } - } - } - else - printf("removeRoute: source is track but destination invalid\n"); - } + if (*i == src) { + inRoutes->erase(i); + break; + } + } + } + else + printf("removeRoute: source is track but destination invalid\n"); } } @@ -1013,9 +751,19 @@ void removeRoute(Route src, Route dst) // So far it only works with MidiDevice <-> Jack. //--------------------------------------------------------- -// p3.3.55 void removeAllRoutes(Route src, Route dst) { + // TODO: Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update the destination track's aux ref count, and all tracks it is routed to. + /* if(src.isValid() && dst.isValid()) + { + if(src.track->auxRefCount()) + dst.track->updateAuxStates( -src.track->auxRefCount() ); + else + if(src.track->type() == Track::TrackType::AUDIO_AUX)) + dst.track->updateAuxStates( -1 ); + } */ + if(src.isValid()) { if(src.type == Route::MIDI_DEVICE_ROUTE) @@ -1052,22 +800,11 @@ static QString track2name(const Track* n) QString Route::name() const { - // p3.3.38 Removed - /* - QString s; - if ((type == TRACK_ROUTE) && (channel != -1)) { -// if (channel != -1) { - QString c; - c.setNum(channel+1); - s = c + ":"; - } - */ - if(type == MIDI_DEVICE_ROUTE) { if(device) { - // p3.3.55 Removed for unified jack in/out devices, the actual port names are now different from device name. + // For unified jack in/out devices, the actual port names are now different from device name. // Like this: device: "MyJackDevice1" -> inport: "MyJackDevice1_in" outport: "MyJackDevice1_out" /* if(device->deviceType() == MidiDevice::JACK_MIDI) @@ -1075,8 +812,7 @@ QString Route::name() const else */ - //if(device->deviceType() == MidiDevice::ALSA_MIDI) - return device->name(); + return device->name(); } return QWidget::tr("None"); } @@ -1084,16 +820,14 @@ QString Route::name() const if(type == JACK_ROUTE) { if (!MusEGlobal::checkAudioDevice()) return ""; - //return s + MusEGlobal::audioDevice->portName(jackPort); return MusEGlobal::audioDevice->portName(jackPort); } else - if(type == MIDI_PORT_ROUTE) // p3.3.49 + if(type == MIDI_PORT_ROUTE) { return ROUTE_MIDIPORT_NAME_PREFIX + QString().setNum(midiPort); } else - //return s + track2name(track); return track2name(track); } @@ -1106,7 +840,6 @@ Route name2route(const QString& rn, bool /*dst*/, int rtype) { // printf("name2route %s\n", rn.toLatin1().constData()); int channel = -1; - //int channel = 0; QString s(rn); // Support old route style in med files. Obsolete. if (rn[0].isNumber() && rn[1]==':') @@ -1117,8 +850,6 @@ Route name2route(const QString& rn, bool /*dst*/, int rtype) if(rtype == -1) { - //if(dst) - //{ if(MusEGlobal::checkAudioDevice()) { void* p = MusEGlobal::audioDevice->findPort(s.toLatin1().constData()); @@ -1149,7 +880,6 @@ Route name2route(const QString& rn, bool /*dst*/, int rtype) return Route(*i, channel); } - // p3.3.49 if(s.left(ROUTE_MIDIPORT_NAME_PREFIX.length()) == ROUTE_MIDIPORT_NAME_PREFIX) { bool ok = false; @@ -1160,8 +890,6 @@ Route name2route(const QString& rn, bool /*dst*/, int rtype) } else { - //if(dst) - //{ if(rtype == Route::TRACK_ROUTE) { TrackList* tl = MusEGlobal::song->tracks(); @@ -1178,8 +906,6 @@ Route name2route(const QString& rn, bool /*dst*/, int rtype) AudioTrack* track = (AudioTrack*)*i; if(track->name() == s) return Route(track, channel); - //return Route(track, channel, 1); - //return Route(track, channel, track->channels()); } } } @@ -1191,7 +917,6 @@ Route name2route(const QString& rn, bool /*dst*/, int rtype) for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { if((*i)->name() == s) - //if (jmd->name() == rn) return Route(*i, channel); /* @@ -1224,7 +949,7 @@ Route name2route(const QString& rn, bool /*dst*/, int rtype) } } else - if(rtype == Route::MIDI_PORT_ROUTE) // p3.3.49 + if(rtype == Route::MIDI_PORT_ROUTE) { if(s.left(ROUTE_MIDIPORT_NAME_PREFIX.length()) == ROUTE_MIDIPORT_NAME_PREFIX) { @@ -1238,7 +963,6 @@ Route name2route(const QString& rn, bool /*dst*/, int rtype) printf(" name2route: <%s> not found\n", rn.toLatin1().constData()); return Route((Track*) 0, channel); - //return Route((Track*) 0, channel, 1); } //--------------------------------------------------------- @@ -1255,10 +979,6 @@ bool checkRoute(const QString& s, const QString& d) return false; if (src.type == Route::JACK_ROUTE) { - //if (dst.type != TRACK_ROUTE) { - // return false; - // } - if (dst.type == Route::TRACK_ROUTE) { if (dst.track->type() != Track::AUDIO_INPUT) { @@ -1274,12 +994,9 @@ bool checkRoute(const QString& s, const QString& d) } } else - //if (dst.type == Route::JACK_MIDI_ROUTE) if (dst.type == Route::MIDI_DEVICE_ROUTE) { - //src.channel = dst.channel; src.channel = -1; - //dst.channel = -1; RouteList* routes = dst.device->inRoutes(); for (ciRoute i = routes->begin(); i != routes->end(); ++i) { @@ -1293,10 +1010,6 @@ bool checkRoute(const QString& s, const QString& d) } else if (dst.type == Route::JACK_ROUTE) { - //if (src.type != TRACK_ROUTE) { - // return false; - // } - if (src.type == Route::TRACK_ROUTE) { if (src.track->type() != Track::AUDIO_OUTPUT) { @@ -1312,13 +1025,10 @@ bool checkRoute(const QString& s, const QString& d) } } else - //if (src.type == Route::JACK_MIDI_ROUTE) if (src.type == Route::MIDI_DEVICE_ROUTE) { RouteList* routes = src.device->outRoutes(); - //dst.channel = src.channel; dst.channel = -1; - //src.channel = -1; for (ciRoute i = routes->begin(); i != routes->end(); ++i) { if (*i == dst) { // route already there @@ -1329,7 +1039,7 @@ bool checkRoute(const QString& s, const QString& d) else return false; } - else if (src.type == Route::MIDI_PORT_ROUTE) // p3.3.49 + else if (src.type == Route::MIDI_PORT_ROUTE) { RouteList* outRoutes = MusEGlobal::midiPorts[src.midiPort].outRoutes(); for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) @@ -1339,13 +1049,11 @@ bool checkRoute(const QString& s, const QString& d) } } } - //else if (dst.type == Route::MIDI_PORT_ROUTE) // p3.3.49 + //else if (dst.type == Route::MIDI_PORT_ROUTE) //{ //} else { - //RouteList* outRoutes = ((src.type == Route::JACK_MIDI_ROUTE) || (src.type == Route::ALSA_MIDI_ROUTE)) ? - // src.device->outRoutes() : src.track->outRoutes(); RouteList* outRoutes = (src.type == Route::MIDI_DEVICE_ROUTE) ? src.device->outRoutes() : src.track->outRoutes(); for (ciRoute i = outRoutes->begin(); i != outRoutes->end(); ++i) @@ -1366,7 +1074,7 @@ void Route::read(Xml& xml) { QString s; int dtype = MidiDevice::ALSA_MIDI; - int port = -1; // p3.3.49 + int port = -1; unsigned char rtype = Route::TRACK_ROUTE; for (;;) @@ -1397,7 +1105,7 @@ void Route::read(Xml& xml) if(tag == "name") s = xml.s2(); else - if(tag == "mport") // p3.3.49 + if(tag == "mport") { port = xml.s2().toInt(); rtype = Route::MIDI_PORT_ROUTE; @@ -1409,7 +1117,7 @@ void Route::read(Xml& xml) #ifdef ROUTE_DEBUG printf("Route::read(): tag end type:%d channel:%d name:%s\n", rtype, channel, s.toLatin1().constData()); #endif - if(rtype == MIDI_PORT_ROUTE) // p3.3.49 + if(rtype == MIDI_PORT_ROUTE) { if(port >= 0 && port < MIDI_PORTS) { @@ -1462,7 +1170,6 @@ void Route::read(Xml& xml) MidiDevice* md = *imd; if(md->name() == s && md->deviceType() == dtype) { - // p3.3.45 // We found a device, but if it is not in use by the song (port is -1), ignore it. // This prevents loading and propagation of bogus routes in the med file. if(md->midiPort() == -1) @@ -1509,7 +1216,7 @@ void Song::readRoute(Xml& xml) case Xml::End: return; case Xml::TagStart: - // p3.3.38 2010/02/03 Support old routes in med files. Now obsolete! + // 2010/02/03 Support old routes in med files. Now obsolete! if (tag == "srcNode") src = xml.parse1(); else if (tag == "dstNode") @@ -1545,7 +1252,7 @@ void Song::readRoute(Xml& xml) if(tag == "remch") remch = xml.s2().toInt(); else - if(tag == "channelMask") // p3.3.50 New channel mask for midi port-track routes. + if(tag == "channelMask") // New channel mask for midi port-track routes. ch = xml.s2().toInt(); else printf("Song::readRoute(): unknown attribute:%s\n", tag.toLatin1().constData()); @@ -1564,17 +1271,17 @@ void Song::readRoute(Xml& xml) // Support new routes. if(sroute.isValid() && droute.isValid()) { - // p3.3.49 Support pre- 1.1-RC2 midi-device-to-track routes. Obsolete. Replaced with midi port routes. + // Support pre- 1.1-RC2 midi-device-to-track routes. Obsolete. Replaced with midi port routes. if(sroute.type == Route::MIDI_DEVICE_ROUTE && droute.type == Route::TRACK_ROUTE) { if(sroute.device->midiPort() >= 0 && sroute.device->midiPort() < MIDI_PORTS - && ch >= 0 && ch < MIDI_CHANNELS) // p3.3.50 + && ch >= 0 && ch < MIDI_CHANNELS) { sroute.midiPort = sroute.device->midiPort(); sroute.device = 0; sroute.type = Route::MIDI_PORT_ROUTE; - sroute.channel = 1 << ch; // p3.3.50 Convert to new bit-wise channel mask. + sroute.channel = 1 << ch; // Convert to new bit-wise channel mask. droute.channel = sroute.channel; addRoute(sroute, droute); @@ -1586,13 +1293,13 @@ void Song::readRoute(Xml& xml) else if(sroute.type == Route::TRACK_ROUTE && droute.type == Route::MIDI_DEVICE_ROUTE) { if(droute.device->midiPort() >= 0 && droute.device->midiPort() < MIDI_PORTS - && ch >= 0 && ch < MIDI_CHANNELS) // p3.3.50 + && ch >= 0 && ch < MIDI_CHANNELS) { droute.midiPort = droute.device->midiPort(); droute.device = 0; droute.type = Route::MIDI_PORT_ROUTE; - droute.channel = 1 << ch; // p3.3.50 Convert to new bit-wise channel mask. + droute.channel = 1 << ch; // Convert to new bit-wise channel mask. sroute.channel = droute.channel; addRoute(sroute, droute); @@ -1659,7 +1366,7 @@ void Route::dump() const printf("Route dump: jack audio port <%s> channel %d\n", MusEGlobal::audioDevice->portName(jackPort).toLatin1().constData(), channel); } else - if (type == MIDI_PORT_ROUTE) // p3.3.49 + if (type == MIDI_PORT_ROUTE) { printf("Route dump: midi port <%d> channel mask %d\n", midiPort, channel); } @@ -1672,8 +1379,6 @@ void Route::dump() const if(device->deviceType() == MidiDevice::JACK_MIDI) { if(MusEGlobal::checkAudioDevice()) - //printf("jack midi port device <%s> ", MusEGlobal::audioDevice->portName(device->clientPort()).toLatin1().constData()); - // p3.3.55 { printf("jack midi device <%s> ", device->name().toLatin1().constData()); if(device->inClientPort()) @@ -1730,36 +1435,16 @@ bool Route::operator==(const Route& a) const { //if (!MusEGlobal::checkAudioDevice()) return false; //return MusEGlobal::audioDevice->portName(jackPort) == MusEGlobal::audioDevice->portName(a.jackPort); - // p3.3.55 Simplified. - return jackPort == a.jackPort; + return jackPort == a.jackPort; // Simplified. } else - if (type == MIDI_PORT_ROUTE) // p3.3.49 + if (type == MIDI_PORT_ROUTE) { return midiPort == a.midiPort; } else if (type == MIDI_DEVICE_ROUTE) { - // p3.3.55 Changed for unified jack in/out devices, the actual port names are now different from device name. - // Like this: device: "MyJackDevice1" -> inport: "MyJackDevice1_in" outport: "MyJackDevice1_out" - /* - if(device && a.device && device->deviceType() == a.device->deviceType()) - { - if(device->deviceType() == MidiDevice::JACK_MIDI) - { - if (!MusEGlobal::checkAudioDevice()) return false; - return MusEGlobal::audioDevice->portName(device->clientPort()) == MusEGlobal::audioDevice->portName(a.device->clientPort()); - } - else - if(device->deviceType() == MidiDevice::ALSA_MIDI) - // TODO: OK ?? - return device->clientPort() == a.device->clientPort() && (channel == a.channel); - else - if(device->deviceType() == MidiDevice::SYNTH_MIDI) - return device->name() == a.device->name(); - } - */ return device == a.device; } } @@ -1767,4 +1452,56 @@ bool Route::operator==(const Route& a) const return false; } +/* +//--------------------------------------------------------- +// isCircularRoute +// Recursive. +// If dst is valid, start traversal from there, not from src. +// Returns true if circular. +//--------------------------------------------------------- + +bool isCircularRoutePath(Track* src, Track* dst) +{ + //if(isMidiTrack() || _type == AUDIO_AUX) + //if(isMidiTrack()) + // return; + + bool rv = false; + + if(dst) + { + src->setNodeTraversed(true); + rv = isCircularRoutePath(dst, NULL); + src->setNodeTraversed(false); + //if(rv) + // fprintf(stderr, " Circular route %s -> %s\n", src->name().toLatin1().constData(), dst->name().toLatin1().constData()); + return rv; + } + + if(src->nodeTraversed()) + return true; + + src->setNodeTraversed(true); + + //printf("isCircularRoute %s\n", src->name().toLatin1().constData()); + + RouteList* orl = src->outRoutes(); + for (iRoute i = orl->begin(); i != orl->end(); ++i) + { + if( !(*i).isValid() || (*i).type != Route::TRACK_ROUTE ) + continue; + Track* t = (*i).track; + //if(t->isMidiTrack()) + // continue; + rv = isCircularRoutePath(src, NULL); + if(rv) + break; + } + + src->setNodeTraversed(false); + return rv; +} +*/ + + } // namespace MusECore diff --git a/muse2/muse/route.h b/muse2/muse/route.h index 36b00d47..587369bc 100644 --- a/muse2/muse/route.h +++ b/muse2/muse/route.h @@ -4,6 +4,7 @@ // $Id: route.h,v 1.5.2.1 2008/05/21 00:28:52 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -43,17 +44,15 @@ class Xml; //--------------------------------------------------------- struct Route { - enum { TRACK_ROUTE=0, JACK_ROUTE=1, MIDI_DEVICE_ROUTE=2, MIDI_PORT_ROUTE=3 }; // p3.3.49 + enum { TRACK_ROUTE=0, JACK_ROUTE=1, MIDI_DEVICE_ROUTE=2, MIDI_PORT_ROUTE=3 }; union { - //AudioTrack* track; Track* track; - //MidiJackDevice* device; MidiDevice* device; void* jackPort; }; - int midiPort; // p3.3.49 Midi port number. Best not to put this in the union to avoid problems? + int midiPort; // Midi port number. Best not to put this in the union to avoid problems? //snd_seq_addr_t alsaAdr; @@ -75,7 +74,7 @@ struct Route { Route(void* t, int ch=-1); Route(Track* t, int ch = -1, int chans = -1); Route(MidiDevice* d, int ch); - Route(int port, int ch); // p3.3.49 + Route(int port, int ch); Route(const QString&, bool dst, int ch, int rtype = -1); Route(); @@ -84,7 +83,7 @@ struct Route { bool isValid() const { return ((type == TRACK_ROUTE) && (track != 0)) || ((type == JACK_ROUTE) && (jackPort != 0)) || ((type == MIDI_DEVICE_ROUTE) && (device != 0)) || - ((type == MIDI_PORT_ROUTE) && (midiPort >= 0) && (midiPort < MIDI_PORTS)); // p3.3.49 + ((type == MIDI_PORT_ROUTE) && (midiPort >= 0) && (midiPort < MIDI_PORTS)); } void read(Xml& xml); void dump() const; @@ -106,6 +105,7 @@ extern void removeRoute(Route, Route); extern void removeAllRoutes(Route, Route); // p3.3.55 extern Route name2route(const QString&, bool dst, int rtype = -1); extern bool checkRoute(const QString&, const QString&); +//extern bool isCircularRoutePath(Track* src, Track* dst); // Recursive. //--------------------------------------------------------- // RouteMenuMap diff --git a/muse2/muse/shortcuts.cpp b/muse2/muse/shortcuts.cpp index 132eef97..cefb198e 100644 --- a/muse2/muse/shortcuts.cpp +++ b/muse2/muse/shortcuts.cpp @@ -66,7 +66,7 @@ void initShortCuts() defShrt(SHRT_COPY, Qt::CTRL + Qt::Key_C, QT_TRANSLATE_NOOP("shortcuts", "Edit: Copy"), INVIS_SHRT, "copy"); defShrt(SHRT_COPY_RANGE, Qt::CTRL + Qt::SHIFT + Qt::Key_C, QT_TRANSLATE_NOOP("shortcuts", "Edit: Copy in range"), GLOBAL_SHRT, "copy_range"); defShrt(SHRT_UNDO, Qt::CTRL + Qt::Key_Z, QT_TRANSLATE_NOOP("shortcuts", "Edit: Undo"), INVIS_SHRT, "undo"); - defShrt(SHRT_REDO, Qt::CTRL + Qt::Key_Y, QT_TRANSLATE_NOOP("shortcuts", "Edit: Redo"), INVIS_SHRT, "redo"); + defShrt(SHRT_REDO, Qt::CTRL + Qt::SHIFT + Qt::Key_Z, QT_TRANSLATE_NOOP("shortcuts", "Edit: Redo"), INVIS_SHRT, "redo"); defShrt(SHRT_CUT, Qt::CTRL + Qt::Key_X, QT_TRANSLATE_NOOP("shortcuts", "Edit: Cut"), INVIS_SHRT, "cut"); defShrt(SHRT_PASTE, Qt::CTRL + Qt::Key_V, QT_TRANSLATE_NOOP("shortcuts", "Edit: Paste"), INVIS_SHRT, "paste"); defShrt(SHRT_PASTE_DIALOG, Qt::CTRL + Qt::SHIFT + Qt::Key_V, QT_TRANSLATE_NOOP("shortcuts", "Edit: Paste (with dialog)"), GLOBAL_SHRT, "paste_dialog"); diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index 70314035..36be50a0 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -156,6 +156,7 @@ void Song::setSig(const AL::TimeSignature& sig) Track* Song::addNewTrack(QAction* action, Track* insertAt) { +printf("Song::addNewTrack\n"); int n = action->data().toInt(); // Ignore negative numbers since this slot could be called by a menu or list etc. passing -1. if(n < 0) @@ -165,13 +166,20 @@ Track* Song::addNewTrack(QAction* action, Track* insertAt) if(n >= MENU_ADD_SYNTH_ID_BASE) { n -= MENU_ADD_SYNTH_ID_BASE; + int ntype = n / MENU_ADD_SYNTH_ID_BASE; + if(ntype >= Synth::SYNTH_TYPE_END) + return 0; + + n %= MENU_ADD_SYNTH_ID_BASE; if(n >= (int)MusEGlobal::synthis.size()) return 0; - SynthI* si = createSynthI(MusEGlobal::synthis[n]->baseName(), MusEGlobal::synthis[n]->name(), insertAt); + //printf("Song::addNewTrack synth: type:%d idx:%d class:%s label:%s\n", ntype, n, MusEGlobal::synthis[n]->baseName().toLatin1().constData(), MusEGlobal::synthis[n]->name().toLatin1().constData()); + SynthI* si = createSynthI(MusEGlobal::synthis[n]->baseName(), MusEGlobal::synthis[n]->name(), (Synth::Type)ntype, insertAt); if(!si) return 0; - + if (MusEGlobal::config.unhideTracks) SynthI::setVisible(true); + // Add instance last in midi device list. for (int i = 0; i < MIDI_PORTS; ++i) { @@ -181,15 +189,19 @@ Track* Song::addNewTrack(QAction* action, Track* insertAt) { MusEGlobal::midiSeq->msgSetMidiDevice(port, si); MusEGlobal::muse->changeConfig(true); // save configuration file - deselectTracks(); - si->setSelected(true); - update(); + if (SynthI::visible()) { + deselectTracks(); + si->setSelected(true); + update(); + } return si; } } - deselectTracks(); - si->setSelected(true); - update(SC_SELECTION); + if (SynthI::visible()) { + deselectTracks(); + si->setSelected(true); + update(SC_SELECTION); + } return si; } // Normal track. @@ -200,10 +212,14 @@ Track* Song::addNewTrack(QAction* action, Track* insertAt) if((Track::TrackType)n >= Track::AUDIO_SOFTSYNTH) return 0; - Track* t = addTrack((Track::TrackType)n, insertAt); - deselectTracks(); - t->setSelected(true); - update(SC_SELECTION); + Undo operations; + Track* t = addTrack(operations, (Track::TrackType)n, insertAt); + applyOperationGroup(operations); + if (t->isVisible()) { + deselectTracks(); + t->setSelected(true); + update(SC_SELECTION); + } return t; } } @@ -216,53 +232,63 @@ Track* Song::addNewTrack(QAction* action, Track* insertAt) // If insertAt is valid, inserts before insertAt. Else at the end after all tracks. //--------------------------------------------------------- -Track* Song::addTrack(Track::TrackType type, Track* insertAt) +Track* Song::addTrack(Undo& operations, Track::TrackType type, Track* insertAt) { + printf("Song::addTrack\n"); Track* track = 0; int lastAuxIdx = _auxs.size(); switch(type) { case Track::MIDI: track = new MidiTrack(); track->setType(Track::MIDI); + if (MusEGlobal::config.unhideTracks) MidiTrack::setVisible(true); break; case Track::DRUM: track = new MidiTrack(); track->setType(Track::DRUM); ((MidiTrack*)track)->setOutChannel(9); + if (MusEGlobal::config.unhideTracks) MidiTrack::setVisible(true); break; case Track::WAVE: track = new MusECore::WaveTrack(); ((AudioTrack*)track)->addAuxSend(lastAuxIdx); + if (MusEGlobal::config.unhideTracks) WaveTrack::setVisible(true); break; case Track::AUDIO_OUTPUT: track = new AudioOutput(); + if (MusEGlobal::config.unhideTracks) AudioOutput::setVisible(true); break; case Track::AUDIO_GROUP: track = new AudioGroup(); ((AudioTrack*)track)->addAuxSend(lastAuxIdx); + if (MusEGlobal::config.unhideTracks) AudioGroup::setVisible(true); break; case Track::AUDIO_AUX: track = new AudioAux(); + if (MusEGlobal::config.unhideTracks) AudioAux::setVisible(true); break; case Track::AUDIO_INPUT: track = new AudioInput(); ((AudioTrack*)track)->addAuxSend(lastAuxIdx); + if (MusEGlobal::config.unhideTracks) AudioInput::setVisible(true); break; case Track::AUDIO_SOFTSYNTH: printf("not implemented: Song::addTrack(SOFTSYNTH)\n"); // ((AudioTrack*)track)->addAuxSend(lastAuxIdx); break; default: - printf("Song::addTrack() illegal type %d\n", type); - abort(); + printf("THIS SHOULD NEVER HAPPEN: Song::addTrack() illegal type %d. returning NULL.\n" + "save your work if you can and expect soon crashes!\n", type); + return NULL; } track->setDefaultName(); int idx = insertAt ? _tracks.index(insertAt) : -1; - insertTrack1(track, idx); - msgInsertTrack(track, idx, true); - insertTrack3(track, idx); + // insertTrack1(track, idx); // this and the below are replaced + // msgInsertTrack(track, idx, true); // by the UndoOp-operation + // insertTrack3(track, idx); // does nothing + operations.push_back(UndoOp(UndoOp::AddTrack, idx, track)); // Add default track <-> midiport routes. if(track->isMidiTrack()) @@ -1928,7 +1954,7 @@ void Song::panic() // If clear_all is false, it will not touch things like midi ports. //--------------------------------------------------------- -void Song::clear(bool signal, bool /*clear_all*/) +void Song::clear(bool signal, bool clear_all) { if(MusEGlobal::debugMsg) printf("Song::clear\n"); @@ -1953,7 +1979,7 @@ void Song::clear(bool signal, bool /*clear_all*/) // p3.3.50 Reset this. MusEGlobal::midiPorts[i].setFoundInSongFile(false); - //if(clear_all) // Allow not touching devices. p4.0.17 TESTING: Maybe some problems... + if(clear_all) // Allow not touching devices. p4.0.17 TESTING: Maybe some problems... // This will also close the device. MusEGlobal::midiPorts[i].setMidiDevice(0); } @@ -1972,7 +1998,7 @@ void Song::clear(bool signal, bool /*clear_all*/) //if((*imd)->deviceType() == MidiDevice::JACK_MIDI) if(dynamic_cast< MidiJackDevice* >(*imd)) { - //if(clear_all) // Allow not touching devices. p4.0.17 TESTING: Maybe some problems... + if(clear_all) // Allow not touching devices. p4.0.17 TESTING: Maybe some problems... { // Remove the device from the list. MusEGlobal::midiDevices.erase(imd); @@ -2919,7 +2945,6 @@ void Song::insertTrack2(Track* track, int idx) break; default: fprintf(stderr, "unknown track type %d\n", track->type()); - // abort(); return; } @@ -2936,7 +2961,7 @@ void Song::insertTrack2(Track* track, int idx) for (iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { if ((*i)->isMidiTrack()) continue; - MusECore::WaveTrack* wt = (MusECore::WaveTrack*)*i; + MusECore::AudioTrack* wt = (MusECore::AudioTrack*)*i; if (wt->hasAuxSend()) { wt->addAuxSend(n); } @@ -2957,6 +2982,13 @@ void Song::insertTrack2(Track* track, int idx) Route src(track, r->channel, r->channels); src.remoteChannel = r->remoteChannel; r->track->outRoutes()->push_back(src); + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update the Audio Output track's aux ref count. p4.0.37 + if(r->track->auxRefCount()) + track->updateAuxRoute( r->track->auxRefCount(), NULL ); + else + if(r->track->type() == Track::AUDIO_AUX) + track->updateAuxRoute( 1, NULL ); } } else if (track->type() == Track::AUDIO_INPUT) @@ -2970,6 +3002,13 @@ void Song::insertTrack2(Track* track, int idx) Route src(track, r->channel, r->channels); src.remoteChannel = r->remoteChannel; r->track->inRoutes()->push_back(src); + // Is this track an Aux Track or else does it have Aux Tracks routed to it? + // Update the other track's aux ref count and all tracks it is connected to. p4.0.37 + if(track->auxRefCount()) + r->track->updateAuxRoute( track->auxRefCount(), NULL ); + else + if(track->type() == Track::AUDIO_AUX) + r->track->updateAuxRoute( 1, NULL ); } } else if (track->isMidiTrack()) // p3.3.50 @@ -3000,6 +3039,13 @@ void Song::insertTrack2(Track* track, int idx) Route src(track, r->channel, r->channels); src.remoteChannel = r->remoteChannel; r->track->outRoutes()->push_back(src); + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update this track's aux ref count. p4.0.37 + if(r->track->auxRefCount()) + track->updateAuxRoute( r->track->auxRefCount(), NULL ); + else + if(r->track->type() == Track::AUDIO_AUX) + track->updateAuxRoute( 1, NULL ); } rl = track->outRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) @@ -3010,6 +3056,13 @@ void Song::insertTrack2(Track* track, int idx) Route src(track, r->channel, r->channels); src.remoteChannel = r->remoteChannel; r->track->inRoutes()->push_back(src); + // Is this track an Aux Track or else does it have Aux Tracks routed to it? + // Update the other track's aux ref count and all tracks it is connected to. p4.0.37 + if(track->auxRefCount()) + r->track->updateAuxRoute( track->auxRefCount(), NULL ); + else + if(track->type() == Track::AUDIO_AUX) + r->track->updateAuxRoute( 1, NULL ); } } @@ -3022,18 +3075,9 @@ void Song::insertTrack2(Track* track, int idx) // non realtime part of insertTrack //--------------------------------------------------------- +// empty. gets executed after the realtime part void Song::insertTrack3(Track* /*track*/, int /*idx*/)//prevent compiler warning: unused parameter { - //printf("Song::insertTrack3\n"); - - /* - switch(track->type()) { - case Track::AUDIO_SOFTSYNTH: - break; - default: - break; - } - */ } //--------------------------------------------------------- @@ -3153,6 +3197,13 @@ void Song::removeTrack2(Track* track) Route src(track, r->channel, r->channels); src.remoteChannel = r->remoteChannel; r->track->outRoutes()->removeRoute(src); + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update the Audio Output track's aux ref count. p4.0.37 + if(r->track->auxRefCount()) + track->updateAuxRoute( -r->track->auxRefCount(), NULL ); + else + if(r->track->type() == Track::AUDIO_AUX) + track->updateAuxRoute( -1, NULL ); } } else if (track->type() == Track::AUDIO_INPUT) @@ -3167,6 +3218,13 @@ void Song::removeTrack2(Track* track) Route src(track, r->channel, r->channels); src.remoteChannel = r->remoteChannel; r->track->inRoutes()->removeRoute(src); + // Is this track an Aux Track or else does it have Aux Tracks routed to it? + // Update the other track's aux ref count and all tracks it is connected to. p4.0.37 + if(track->auxRefCount()) + r->track->updateAuxRoute( -track->auxRefCount(), NULL ); + else + if(track->type() == Track::AUDIO_AUX) + r->track->updateAuxRoute( -1, NULL ); } } else if (track->isMidiTrack()) // p3.3.50 @@ -3198,6 +3256,13 @@ void Song::removeTrack2(Track* track) Route src(track, r->channel, r->channels); src.remoteChannel = r->remoteChannel; r->track->outRoutes()->removeRoute(src); + // Is the source an Aux Track or else does it have Aux Tracks routed to it? + // Update this track's aux ref count. p4.0.37 + if(r->track->auxRefCount()) + track->updateAuxRoute( -r->track->auxRefCount(), NULL ); + else + if(r->track->type() == Track::AUDIO_AUX) + track->updateAuxRoute( -1, NULL ); } rl = track->outRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) @@ -3209,6 +3274,13 @@ void Song::removeTrack2(Track* track) Route src(track, r->channel, r->channels); src.remoteChannel = r->remoteChannel; r->track->inRoutes()->removeRoute(src); + // Is this track an Aux Track or else does it have Aux Tracks routed to it? + // Update the other track's aux ref count and all tracks it is connected to. p4.0.37 + if(track->auxRefCount()) + r->track->updateAuxRoute( -track->auxRefCount(), NULL ); + else + if(track->type() == Track::AUDIO_AUX) + r->track->updateAuxRoute( -1, NULL ); } } @@ -3219,21 +3291,10 @@ void Song::removeTrack2(Track* track) // non realtime part of removeTrack //--------------------------------------------------------- +//empty. gets executed after the realtime part void Song::removeTrack3(Track* /*track*/)//prevent of compiler warning: unused parameter - { - /* - switch(track->type()) { - case Track::AUDIO_SOFTSYNTH: - { - SynthI* s = (SynthI*) track; - s->deactivate3(); - } - break; - default: - break; - } - */ - } +{ +} //--------------------------------------------------------- // executeScript diff --git a/muse2/muse/song.h b/muse2/muse/song.h index 7bbd831d..2c07396c 100644 --- a/muse2/muse/song.h +++ b/muse2/muse/song.h @@ -37,6 +37,7 @@ #include "al/sig.h" #include "undo.h" #include "track.h" +#include "synth.h" class QAction; class QFont; @@ -362,7 +363,7 @@ class Song : public QObject { // Configuration //----------------------------------------- - SynthI* createSynthI(const QString& sclass, const QString& label = QString(), Track* insertAt = 0); + SynthI* createSynthI(const QString& sclass, const QString& label = QString(), Synth::Type type = Synth::SYNTH_TYPE_END, Track* insertAt = 0); void rescanAlsaPorts(); @@ -410,7 +411,7 @@ class Song : public QObject { void setQuantize(bool val); void panic(); void seqSignal(int fd); - Track* addTrack(Track::TrackType type, Track* insertAt = 0); + Track* addTrack(Undo& operations, Track::TrackType type, Track* insertAt = 0); Track* addNewTrack(QAction* action, Track* insertAt = 0); QString getScriptPath(int id, bool delivered); void populateScriptMenu(QMenu* menuPlugins, QObject* receiver); diff --git a/muse2/muse/songfile.cpp b/muse2/muse/songfile.cpp index 9adfd406..9a3c11f5 100644 --- a/muse2/muse/songfile.cpp +++ b/muse2/muse/songfile.cpp @@ -1325,13 +1325,19 @@ void MusE::readToplevels(MusECore::Xml& xml) } else if (tag == "marker") { showMarker(true); - if (toplevels.back()->type()==MusEGui::TopWin::MARKER) - toplevels.back()->readStatus(xml); + TopWin* tw = toplevels.findType(TopWin::MARKER); + if(!tw) + xml.skip("marker"); + else + tw->readStatus(xml); } else if (tag == "arrangerview") { showArranger(true); - if (toplevels.back()->type()==MusEGui::TopWin::ARRANGER) - toplevels.back()->readStatus(xml); + TopWin* tw = toplevels.findType(TopWin::ARRANGER); + if(!tw) + xml.skip("arrangerview"); + else + tw->readStatus(xml); } else if (tag == "waveedit") { if(!pl->empty()) @@ -1343,8 +1349,11 @@ void MusE::readToplevels(MusECore::Xml& xml) } else if (tag == "cliplist") { startClipList(true); - if (toplevels.back()->type()==MusEGui::TopWin::CLIPLIST) - toplevels.back()->readStatus(xml); + TopWin* tw = toplevels.findType(TopWin::CLIPLIST); + if(!tw) + xml.skip("cliplist"); + else + tw->readStatus(xml); } else xml.unknown("MusE"); @@ -1506,9 +1515,34 @@ void MusE::read(MusECore::Xml& xml, bool skipConfig, bool isTemplate) else if (tag == "configuration") if (skipConfig) //xml.skip(tag); - readConfiguration(xml,true /* only read sequencer settings */, false /* do NOT read global settings */); + readConfiguration(xml,true /* only read sequencer settings */, false /* do NOT read global settings, see below */); + // see even more below! else - readConfiguration(xml, false, false /* do NOT read global settings */); + readConfiguration(xml, false, false /* do NOT read global settings, see below */); + /* Explanation for why "do NOT read global settings": + * if you would use true here, then muse would overwrite certain global config stuff + * by the settings stored in the song. but you don't want this. imagine that you + * send a friend a .med file. your friend opens it and baaam, his configuration is + * garbled. why? well, because these readConfigurations here would have overwritten + * parts (but not all) of his global config (like MDI/SDI, toolbar states etc.) + * with the data stored in the song. (it IS stored there. dunny why, i find it pretty + * senseless.) + * + * If you've a problem which seems to be solved by replacing "false" with "true", i've + * a better solution for you: go into conf.cpp, in void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig) + * (around line 525), look for a comment like this: + * "Global and/or per-song config stuff ends here" (alternatively just search for + * "----"). Your problem is probably that some non-global setting should be loaded but + * is not. Fix it by either placing the else if (foo)... clause responsible for that + * setting to be loaded into the first part, that is, before "else if (!doReadGlobalConfig)" + * or (if the settings actually IS global and not per-song), ensure that the routine + * which writes the global (and not the song-)configuration really writes that setting. + * (it might happen that it formerly worked because it was written to the song instead + * of the global config by mistake, and now isn't loaded anymore. write it to the + * correct location.) + * + * -- flo93 + */ else if (tag == "song") { MusEGlobal::song->read(xml, isTemplate); diff --git a/muse2/muse/structure.cpp b/muse2/muse/structure.cpp index 90d02908..70b19540 100644 --- a/muse2/muse/structure.cpp +++ b/muse2/muse/structure.cpp @@ -137,7 +137,7 @@ void adjustGlobalLists(Undo& operations, int startPos, int diff) // - cut master track //--------------------------------------------------------- -void globalCut() +void globalCut(bool onlySelectedTracks) { int lpos = MusEGlobal::song->lpos(); int rpos = MusEGlobal::song->rpos(); @@ -146,17 +146,11 @@ void globalCut() Undo operations; TrackList* tracks = MusEGlobal::song->tracks(); - bool at_least_one_selected=false; - - for (iTrack it = tracks->begin(); it != tracks->end(); ++it) - if ( (*it)->selected() ) { - at_least_one_selected=true; - break; - } for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { - MidiTrack* track = dynamic_cast<MidiTrack*>(*it); - if (track == 0 || (at_least_one_selected && !track->selected())) + //MidiTrack* track = dynamic_cast<MidiTrack*>(*it); + Track* track = *it; + if (track == 0 || (onlySelectedTracks && !track->selected())) continue; PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { @@ -169,63 +163,61 @@ void globalCut() operations.push_back(UndoOp(UndoOp::DeletePart,part)); } else if ((t < lpos) && ((t+l) > lpos) && ((t+l) <= rpos)) { - // remove part tail - int len = lpos - t; - MidiPart* nPart = new MidiPart(*(MidiPart*)part); - nPart->setLenTick(len); - // - // cut Events in nPart - EventList* el = nPart->events(); - for (iEvent ie = el->lower_bound(len); ie != el->end(); ++ie) - operations.push_back(UndoOp(UndoOp::DeleteEvent,ie->second, nPart, false, false)); - - operations.push_back(UndoOp(UndoOp::ModifyPart,part, nPart, true, true)); - } + // remove part tail + int len = lpos - t; + Part *nPart; + if (track->isMidiTrack()) + nPart = new MidiPart(*(MidiPart*)part); + else + nPart = new WavePart(*(WavePart*)part); + + nPart->setLenTick(len); + // + // cut Events in nPart + EventList* el = nPart->events(); + for (iEvent ie = el->lower_bound(len); ie != el->end(); ++ie) + operations.push_back(UndoOp(UndoOp::DeleteEvent,ie->second, nPart, false, false)); + + operations.push_back(UndoOp(UndoOp::ModifyPart,part, nPart, true, true)); + } else if ((t < lpos) && ((t+l) > lpos) && ((t+l) > rpos)) { //---------------------- // remove part middle //---------------------- + Part* p1; + Part* p2; + Part* p3; + track->splitPart(part, lpos, p1, p2); + delete p2; + track->splitPart(part, rpos, p2, p3); + delete p2; + p3->setTick(lpos); + p1->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ + p3->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ - MidiPart* nPart = new MidiPart(*(MidiPart*)part); - EventList* el = nPart->events(); - iEvent is = el->lower_bound(lpos-t); - iEvent ie = el->lower_bound(rpos-t); //lower bound, because we do NOT want to erase the events at rpos-t - for (iEvent i = is; i != ie; ++i) - operations.push_back(UndoOp(UndoOp::DeleteEvent,i->second, nPart, false, false)); - - for (iEvent i = el->lower_bound(rpos-t); i != el->end(); ++i) { - Event event = i->second; - Event nEvent = event.clone(); - nEvent.setTick(nEvent.tick() - (rpos-lpos)); - // Indicate no undo, and do not do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::ModifyEvent,nEvent, event, nPart, false, false)); - } - nPart->setLenTick(l - (rpos-lpos)); // Indicate no undo, and do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::ModifyPart,part, nPart, true, true)); + operations.push_back(UndoOp(UndoOp::ModifyPart,part, p1, true, true)); + operations.push_back(UndoOp(UndoOp::AddPart,p3)); } else if ((t >= lpos) && (t < rpos) && (t+l) > rpos) { // remove part head - MidiPart* nPart = new MidiPart(*(MidiPart*)part); - EventList* el = nPart->events(); - iEvent i_end = el->lower_bound(rpos-t); //lower bound, because we do NOT want to erase the events at rpos-t - for (iEvent it = el->begin(); it!=i_end; it++) - operations.push_back(UndoOp(UndoOp::DeleteEvent,it->second, nPart, false, false)); - - for (iEvent it = el->lower_bound(rpos-t); it!=el->end(); it++) { - Event event = it->second; - Event nEvent = event.clone(); - nEvent.setTick(nEvent.tick() - (rpos-t)); - // Indicate no undo, and do not do port controller values and clone parts. - operations.push_back(UndoOp(UndoOp::ModifyEvent,nEvent, event, nPart, false, false)); - } - - nPart->setLenTick(l - (rpos-t)); - operations.push_back(UndoOp(UndoOp::ModifyPart,part, nPart, true, true)); + Part* p1; + Part* p2; + track->splitPart(part, rpos, p1, p2); + delete p1; + p2->setTick(lpos); + p2->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ + operations.push_back(UndoOp(UndoOp::ModifyPart,part, p2, true, true)); } else if (t >= rpos) { - MidiPart* nPart = new MidiPart(*(MidiPart*)part); + // move part to the left + Part *nPart; + if (track->isMidiTrack()) + nPart = new MidiPart(*(MidiPart*)part); + else + nPart = new WavePart(*(WavePart*)part); + //MidiPart* nPart = new MidiPart(*(MidiPart*)part); int nt = part->tick(); nPart->setTick(nt - (rpos -lpos)); // Indicate no undo, and do port controller values but not clone parts. @@ -246,33 +238,24 @@ void globalCut() // - insert in master track //--------------------------------------------------------- -void globalInsert() - { - Undo operations=movePartsTotheRight(MusEGlobal::song->lpos(), MusEGlobal::song->rpos()-MusEGlobal::song->lpos(), true); +void globalInsert(bool onlySelectedTracks) +{ + Undo operations=movePartsTotheRight(MusEGlobal::song->lpos(), MusEGlobal::song->rpos()-MusEGlobal::song->lpos(), onlySelectedTracks); MusEGlobal::song->applyOperationGroup(operations); - } - +} Undo movePartsTotheRight(unsigned int startTicks, int moveTicks, bool only_selected, set<Track*>* tracklist) - { +{ if (moveTicks<=0) return Undo(); Undo operations; TrackList* tracks = MusEGlobal::song->tracks(); - bool at_least_one_selected=false; - - for (iTrack it = tracks->begin(); it != tracks->end(); ++it) - if ( (*it)->selected() ) { - at_least_one_selected=true; - break; - } - for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { - MidiTrack* track = dynamic_cast<MidiTrack*>(*it); + Track* track = *it; if ( (track == 0) || - (only_selected && at_least_one_selected && !track->selected()) || + (only_selected && !track->selected()) || (tracklist && tracklist->find(track)==tracklist->end()) ) continue; PartList* pl = track->parts(); @@ -283,23 +266,23 @@ Undo movePartsTotheRight(unsigned int startTicks, int moveTicks, bool only_selec if (t + l <= startTicks) continue; if (startTicks > t && startTicks < (t+l)) { - MidiPart* nPart = new MidiPart(*(MidiPart*)part); - nPart->setLenTick(l + moveTicks); - EventList* el = nPart->events(); - - for (riEvent i = el->rbegin(); i!=el->rend(); ++i) - { - if (i->first < startTicks-t) - break; - Event event = i->second; - Event nEvent = i->second.clone(); - nEvent.setTick(nEvent.tick() + moveTicks); - operations.push_back(UndoOp(UndoOp::ModifyEvent, nEvent, event, nPart, false, false)); - } - operations.push_back(UndoOp(UndoOp::ModifyPart, part, nPart, true, true)); + // split part to insert new space + Part* p1; + Part* p2; + track->splitPart(part, startTicks, p1, p2); + p2->setTick(startTicks+moveTicks); + p2->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ + p1->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it so we must decrement it first :/ + + operations.push_back(UndoOp(UndoOp::ModifyPart, part, p1, true, true)); + operations.push_back(UndoOp(UndoOp::AddPart, p2)); } else if (t >= startTicks) { - MidiPart* nPart = new MidiPart(*(MidiPart*)part); + Part *nPart; + if (track->isMidiTrack()) + nPart = new MidiPart(*(MidiPart*)part); + else + nPart = new WavePart(*(WavePart*)part); nPart->setTick(t + moveTicks); operations.push_back(UndoOp(UndoOp::ModifyPart, part, nPart, true, false)); } @@ -317,47 +300,50 @@ Undo movePartsTotheRight(unsigned int startTicks, int moveTicks, bool only_selec // - split all parts at the song position pointer //--------------------------------------------------------- -void globalSplit() - { - int pos = MusEGlobal::song->cpos(); - Undo operations; - TrackList* tracks = MusEGlobal::song->tracks(); - bool at_least_one_selected=false; - - for (iTrack it = tracks->begin(); it != tracks->end(); ++it) - if ( (*it)->selected() ) { - at_least_one_selected=true; - break; - } - - - for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { - Track* track = *it; - if (track == 0 || (at_least_one_selected && !track->selected())) - continue; - - PartList* pl = track->parts(); - for (iPart p = pl->begin(); p != pl->end(); ++p) { - Part* part = p->second; - int p1 = part->tick(); - int l0 = part->lenTick(); - if (pos > p1 && pos < (p1+l0)) { - Part* p1; - Part* p2; - track->splitPart(part, pos, p1, p2); +void globalSplit(bool onlySelectedTracks) +{ + Undo operations=partSplitter(MusEGlobal::song->cpos(), onlySelectedTracks); + MusEGlobal::song->applyOperationGroup(operations); +} - p1->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it - p2->events()->incARef(-1); // so we must decrement it first :/ +Undo partSplitter(unsigned int pos, bool onlySelectedTracks) +{ + Undo operations; + TrackList* tracks = MusEGlobal::song->tracks(); + + for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { + Track* track = *it; + if (track == 0 || (onlySelectedTracks && !track->selected())) + continue; + + PartList* pl = track->parts(); + for (iPart p = pl->begin(); p != pl->end(); ++p) { + Part* part = p->second; + unsigned int p1 = part->tick(); + unsigned int l0 = part->lenTick(); + if (pos > p1 && pos < (p1+l0)) { + Part* p1; + Part* p2; + track->splitPart(part, pos, p1, p2); + + p1->events()->incARef(-1); // the later MusEGlobal::song->applyOperationGroup() will increment it + p2->events()->incARef(-1); // so we must decrement it first :/ + + //MusEGlobal::song->informAboutNewParts(part, p1); // is unneccessary because of ModifyPart + MusEGlobal::song->informAboutNewParts(part, p2); + operations.push_back(UndoOp(UndoOp::ModifyPart,part, p1, true, false)); + operations.push_back(UndoOp(UndoOp::AddPart,p2)); + if (MusEGlobal::debugMsg) + { + printf("in partSplitter: part1 %d\n",p1->events()->refCount()); + printf("in partSplitter: part2 %d\n",p2->events()->refCount()); + } + break; + } + } + } + return operations; +} - //MusEGlobal::song->informAboutNewParts(part, p1); // is unneccessary because of ModifyPart - MusEGlobal::song->informAboutNewParts(part, p2); - operations.push_back(UndoOp(UndoOp::ModifyPart,part, p1, true, false)); - operations.push_back(UndoOp(UndoOp::AddPart,p2)); - break; - } - } - } - MusEGlobal::song->applyOperationGroup(operations); - } } // namespace MusECore diff --git a/muse2/muse/structure.h b/muse2/muse/structure.h index b92c4e14..3a359dc0 100644 --- a/muse2/muse/structure.h +++ b/muse2/muse/structure.h @@ -28,10 +28,11 @@ namespace MusECore { Undo movePartsTotheRight(unsigned int startTick, int moveTick, bool only_selected=false, std::set<Track*>* tracklist=NULL); +Undo partSplitter(unsigned int tick, bool onlySelectedTracks=false); void adjustGlobalLists(Undo& operations, int startPos, int diff); -void globalCut(); -void globalInsert(); -void globalSplit(); +void globalCut(bool onlySelectedTracks=false); +void globalInsert(bool onlySelectedTracks=false); +void globalSplit(bool onlySelectedTracks=false); } #endif diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp index 93067835..7ab18bda 100644 --- a/muse2/muse/synth.cpp +++ b/muse2/muse/synth.cpp @@ -32,6 +32,7 @@ #include <dlfcn.h> #include <QDir> +#include <QString> //#include <QMenu> #include "app.h" @@ -62,6 +63,19 @@ namespace MusECore { extern void connectNodes(AudioTrack*, AudioTrack*); bool SynthI::_isVisible=false; +const char* synthTypes[] = { "METRONOME", "MESS", "DSSI", "VST", "UNKNOWN" }; +QString synthType2String(Synth::Type type) { return QString(synthTypes[type]); } + +Synth::Type string2SynthType(const QString& type) +{ + for(int i = 0; i < Synth::SYNTH_TYPE_END; ++i) + { + if(synthType2String((Synth::Type)i) == type) + return (Synth::Type)i; + } + return Synth::SYNTH_TYPE_END; +} + /* //--------------------------------------------------------- // description @@ -145,19 +159,17 @@ void MessSynthIF::setNativeGeometry(int x, int y, int w, int h) // search for synthesizer base class //--------------------------------------------------------- -//static Synth* findSynth(const QString& sclass) -static Synth* findSynth(const QString& sclass, const QString& label) +static Synth* findSynth(const QString& sclass, const QString& label, Synth::Type type = Synth::SYNTH_TYPE_END) { for (std::vector<Synth*>::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) { - //if ((*i)->baseName() == sclass) - //if ((*i)->name() == sclass) - if ( ((*i)->baseName() == sclass) && (label.isEmpty() || ((*i)->name() == label)) ) - - return *i; + if( ((*i)->baseName() == sclass) && + (label.isEmpty() || ((*i)->name() == label)) && + (type == Synth::SYNTH_TYPE_END || type == (*i)->synthType()) ) + return *i; } - printf("synthi class:%s label:%s not found\n", sclass.toLatin1().constData(), label.toLatin1().constData()); + printf("synthi type:%d class:%s label:%s not found\n", type, sclass.toLatin1().constData(), label.toLatin1().constData()); return 0; } @@ -166,10 +178,9 @@ static Synth* findSynth(const QString& sclass, const QString& label) // create a synthesizer instance of class "label" //--------------------------------------------------------- -static SynthI* createSynthInstance(const QString& sclass, const QString& label) +static SynthI* createSynthInstance(const QString& sclass, const QString& label, Synth::Type type = Synth::SYNTH_TYPE_END) { - //Synth* s = findSynth(sclass); - Synth* s = findSynth(sclass, label); + Synth* s = findSynth(sclass, label, type); SynthI* si = 0; if (s) { si = new SynthI(); @@ -652,13 +663,11 @@ void initMidiSynth() // If insertAt is valid, inserts before insertAt. Else at the end after all tracks. //--------------------------------------------------------- -SynthI* Song::createSynthI(const QString& sclass, const QString& label, Track* insertAt) +SynthI* Song::createSynthI(const QString& sclass, const QString& label, Synth::Type type, Track* insertAt) { //printf("Song::createSynthI calling ::createSynthI class:%s\n", sclass.toLatin1().constData()); - //SynthI* si = ::createSynthI(sclass); - //SynthI* si = ::createSynthI(sclass, label); - SynthI* si = createSynthInstance(sclass, label); + SynthI* si = createSynthInstance(sclass, label, type); if(!si) return 0; //printf("Song::createSynthI created SynthI. Before insertTrack1...\n"); @@ -705,6 +714,9 @@ void SynthI::write(int level, Xml& xml) const { xml.tag(level++, "SynthI"); AudioTrack::writeProperties(level, xml); + //xml.intTag(level, "synthType", synth()->synthType()); + xml.strTag(level, "synthType", synthType2String(synth()->synthType())); + xml.strTag(level, "class", synth()->baseName()); // To support plugins like dssi-vst where all the baseNames are the same 'dssi-vst' and the label is the name of the dll file. @@ -823,7 +835,8 @@ void SynthI::read(Xml& xml) { QString sclass; QString label; - + Synth::Type type = Synth::SYNTH_TYPE_END; + int port = -1; bool startgui = false; bool startngui = false; @@ -837,7 +850,10 @@ void SynthI::read(Xml& xml) case Xml::End: return; case Xml::TagStart: - if (tag == "class") + if (tag == "synthType") + //type = xml.parseInt(); + type = string2SynthType(xml.parse1()); + else if (tag == "class") sclass = xml.parse1(); else if (tag == "label") label = xml.parse1(); @@ -866,8 +882,15 @@ void SynthI::read(Xml& xml) break; case Xml::TagEnd: if (tag == "SynthI") { - //Synth* s = findSynth(sclass); - Synth* s = findSynth(sclass, label); + + // NOTICE: This is a hack to quietly change songs to use the new 'fluid_synth' name instead of 'fluidsynth'. + // Recent linker changes required the name change in fluidsynth's cmakelists. Nov 8, 2011 By Tim. + if(sclass == QString("fluidsynth") && + (type == Synth::SYNTH_TYPE_END || type == Synth::MESS_SYNTH) && + (label.isEmpty() || label == QString("FluidSynth")) ) + sclass = QString("fluid_synth"); + + Synth* s = findSynth(sclass, label, type); if (s == 0) return; if (initInstance(s, name())) diff --git a/muse2/muse/synth.h b/muse2/muse/synth.h index c5b63424..497395c0 100644 --- a/muse2/muse/synth.h +++ b/muse2/muse/synth.h @@ -72,6 +72,8 @@ class Synth { QString _version; public: + enum Type { METRO_SYNTH=0, MESS_SYNTH, DSSI_SYNTH, VST_SYNTH, SYNTH_TYPE_END }; + //Synth(const QFileInfo& fi); //Synth(const QFileInfo& fi, QString label); Synth(const QFileInfo& fi, QString label, QString descr, QString maker, QString ver); @@ -80,6 +82,7 @@ class Synth { //virtual const char* description() const { return ""; } //virtual const char* version() const { return ""; } + virtual Type synthType() const = 0; int instances() const { return _instances; } virtual void incInstances(int val) { _instances += val; } QString completeBaseName() /*const*/ { return info.completeBaseName(); } // ddskrjo @@ -117,6 +120,8 @@ class MessSynth : public Synth { //virtual const char* description() const; //virtual const char* version() const; + virtual Type synthType() const { return MESS_SYNTH; } + //virtual void* instantiate(); virtual void* instantiate(const QString&); @@ -363,6 +368,9 @@ class MessSynthIF : public SynthIF { virtual int getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval); }; +extern QString synthType2String(Synth::Type); +extern Synth::Type string2SynthType(const QString&); + } // namespace MusECore namespace MusEGlobal { diff --git a/muse2/muse/ticksynth.cpp b/muse2/muse/ticksynth.cpp index 1edc0b12..6d3721ea 100644 --- a/muse2/muse/ticksynth.cpp +++ b/muse2/muse/ticksynth.cpp @@ -49,6 +49,7 @@ class MetronomeSynth : public Synth { //MetronomeSynth(const QFileInfo& fi) : Synth(fi, QString("Metronome")) {} MetronomeSynth(const QFileInfo& fi) : Synth(fi, QString("Metronome"), QString("Metronome"), QString(), QString()) {} virtual ~MetronomeSynth() {} + virtual Type synthType() const { return METRO_SYNTH; } virtual void incInstances(int) {} virtual void* instantiate(); diff --git a/muse2/muse/track.cpp b/muse2/muse/track.cpp index 2091d03e..e9aa0cf6 100644 --- a/muse2/muse/track.cpp +++ b/muse2/muse/track.cpp @@ -4,6 +4,7 @@ // $Id: track.cpp,v 1.34.2.11 2009/11/30 05:05:49 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -36,10 +37,12 @@ namespace MusECore { //bool Track::_isVisible=true; -unsigned int Track::_soloRefCnt = 0; +unsigned int Track::_soloRefCnt = 0; Track* Track::_tmpSoloChainTrack = 0; bool Track::_tmpSoloChainDoIns = false; bool Track::_tmpSoloChainNoDec = false; +//bool Track::_tmpIsAuxProcessing = false; +//int Track::_tmpIsAuxProcRefCount = 0; const char* Track::_cname[] = { "Midi", "Drum", "Wave", "AudioOut", "AudioIn", "AudioGroup", @@ -199,6 +202,8 @@ int Track::y() const void Track::init() { + _auxRouteCount = 0; + _nodeTraversed = false; _activity = 0; _lastActivity = 0; _recordFlag = false; @@ -233,6 +238,8 @@ Track::Track(Track::TrackType t) //Track::Track(const Track& t) Track::Track(const Track& t, bool cloneParts) { + _auxRouteCount = t._auxRouteCount; + _nodeTraversed = t._nodeTraversed; _activity = t._activity; _lastActivity = t._lastActivity; _recordFlag = t._recordFlag; @@ -298,6 +305,8 @@ Track::~Track() Track& Track::operator=(const Track& t) { + _auxRouteCount = t._auxRouteCount; + _nodeTraversed = t._nodeTraversed; _activity = t._activity; _lastActivity = t._lastActivity; _recordFlag = t._recordFlag; @@ -414,6 +423,107 @@ void Track::dump() const _name.toLatin1().constData(), _type, _parts.size(), _selected); } +//--------------------------------------------------------- +// updateAuxRoute +// Internal use. Update all the Aux ref counts of tracks dst is connected to. +// If dst is valid, start traversal from there, not from this track. +//--------------------------------------------------------- + +void Track::updateAuxRoute(int refInc, Track* dst) +{ + //if(isMidiTrack() || _type == AUDIO_AUX) + if(isMidiTrack()) + return; + + //printf("Track::updateAuxRoute %s _auxRouteCount:%d refInc:%d\n", name().toLatin1().constData(), _auxRouteCount, refInc); + + if(dst) + { + _nodeTraversed = true; + dst->updateAuxRoute(refInc, NULL); + _nodeTraversed = false; + return; + } + + if(_type == AUDIO_AUX) + return; + + if(_nodeTraversed) + { + fprintf(stderr, "Track::updateAuxRoute %s _auxRouteCount:%d refInc:%d :\n", name().toLatin1().constData(), _auxRouteCount, refInc); + if(refInc >= 0) + fprintf(stderr, " MusE Warning: Please check your routes: Circular path found!\n"); + else + fprintf(stderr, " MusE: Circular path removed.\n"); + return; + } + + _nodeTraversed = true; + + _auxRouteCount += refInc; + if(_auxRouteCount < 0) + { + fprintf(stderr, "Track::updateAuxRoute Ref underflow! %s _auxRouteCount:%d refInc:%d\n", name().toLatin1().constData(), _auxRouteCount, refInc); + //_auxRouteCount = 0; + } + + for (iRoute i = _outRoutes.begin(); i != _outRoutes.end(); ++i) + { + if( !(*i).isValid() || (*i).type != Route::TRACK_ROUTE ) + continue; + Track* t = (*i).track; + //if(t->isMidiTrack()) + // continue; + t->updateAuxRoute(refInc, NULL); + } + + _nodeTraversed = false; +} + +//--------------------------------------------------------- +// isCircularRoute +// If dst is valid, start traversal from there, not from this track. +// Returns true if circular. +//--------------------------------------------------------- + +bool Track::isCircularRoute(Track* dst) +{ + //if(isMidiTrack() || _type == AUDIO_AUX) + //if(isMidiTrack()) + // return; + + bool rv = false; + + if(dst) + { + _nodeTraversed = true; + rv = dst->isCircularRoute(NULL); + _nodeTraversed = false; + //if(rv) + // fprintf(stderr, " Circular route %s -> %s\n", name().toLatin1().constData(), dst->name().toLatin1().constData()); + return rv; + } + + if(_nodeTraversed) + return true; + + _nodeTraversed = true; + + for (iRoute i = _outRoutes.begin(); i != _outRoutes.end(); ++i) + { + if( !(*i).isValid() || (*i).type != Route::TRACK_ROUTE ) + continue; + Track* t = (*i).track; + //if(t->isMidiTrack()) + // continue; + rv = t->isCircularRoute(NULL); + if(rv) + break; + } + + _nodeTraversed = false; + return rv; +} //--------------------------------------------------------- // MidiTrack @@ -462,7 +572,6 @@ void MidiTrack::init() { _outPort = 0; _outChannel = 0; - // Changed by Tim. p3.3.8 //_inPortMask = 0xffff; ///_inPortMask = 0xffffffff; @@ -537,7 +646,7 @@ void MidiTrack::setOutPortAndChannelAndUpdate(int port, int ch) //--------------------------------------------------------- // 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 +// before midi routing was added (the iR button). //--------------------------------------------------------- void MidiTrack::setInPortAndChannelMask(unsigned int portmask, int chanmask) @@ -550,7 +659,7 @@ void MidiTrack::setInPortAndChannelMask(unsigned int portmask, int chanmask) for(int port = 0; port < 32; ++port) // 32 is the old maximum number of ports. { - // p3.3.50 If the port was not used in the song file to begin with, just ignore it. + // If the port was not used in the song file to begin with, just ignore it. // This saves from having all of the first 32 ports' channels connected. if(!MusEGlobal::midiPorts[port].foundInSongFile()) continue; @@ -558,34 +667,18 @@ void MidiTrack::setInPortAndChannelMask(unsigned int portmask, int chanmask) //if(!(portmask & (1 << port))) // continue; - // p3.3.50 Removed. Allow to connect to port with no device so user can change device later. + // Removed. Allow to connect to port with no device so user can change device later. //MidiPort* mp = &MusEGlobal::midiPorts[port]; //MidiDevice* md = mp->device(); //if(!md) // continue; - //for(int ch = 0; ch < MIDI_CHANNELS; ++ch) // p3.3.50 Removed. - //{ - //if(!(chanmask & (1 << ch))) - // continue; - - //Route aRoute(md, ch); - //Route bRoute(this, ch); - Route aRoute(port, chanmask); // p3.3.50 + Route aRoute(port, chanmask); Route bRoute(this, chanmask); - // p3.3.50 Removed. - //iRoute iir = rl->begin(); - //for(; iir != rl->end(); ++iir) - //{ - //if(*iir == aRoute) - // if(iir->type == Route::MIDI_PORT_ROUTE && iir->midiPort == port) // p3.3.50 - // break; - //} - // Route wanted? //if((portmask & (1 << port)) && (chanmask & (1 << ch))) - if(portmask & (1 << port)) // p3.3.50 + if(portmask & (1 << port)) { // Route already exists? //if(iir != rl->end()) @@ -818,11 +911,11 @@ void MidiTrack::read(Xml& xml) //setInPortMask(xml.parseInt()); ///setInPortMask(xml.parseUInt()); //xml.skip(tag); // Obsolete. - portmask = xml.parseUInt(); // p3.3.48: Support old files. + portmask = xml.parseUInt(); // Support old files. else if (tag == "inchannelMap") ///setInChannelMask(xml.parseInt()); //xml.skip(tag); // Obsolete. - chanmask = xml.parseInt(); // p3.3.48: Support old files. + chanmask = xml.parseInt(); // Support old files. else if (tag == "locked") _locked = xml.parseInt(); else if (tag == "echo") @@ -843,7 +936,7 @@ void MidiTrack::read(Xml& xml) case Xml::TagEnd: if (tag == "miditrack" || tag == "drumtrack") { - setInPortAndChannelMask(portmask, chanmask); // p3.3.48: Support old files. + setInPortAndChannelMask(portmask, chanmask); // Support old files. return; } default: @@ -972,7 +1065,7 @@ void Track::writeRouting(int level, Xml& xml) const xml.tag(level++, s.toAscii().constData()); - // p3.3.38 New routing scheme. + // New routing scheme. s = "source"; if(r->type != Route::TRACK_ROUTE) s += QString(" type=\"%1\"").arg(r->type); @@ -994,10 +1087,10 @@ void Track::writeRouting(int level, Xml& xml) const if(r->type == Route::TRACK_ROUTE && r->track && r->track->type() == Track::AUDIO_INPUT) continue; - if(r->midiPort != -1 || !r->name().isEmpty()) // p3.3.49 + if(r->midiPort != -1 || !r->name().isEmpty()) { s = "Route"; - if(r->type == Route::MIDI_PORT_ROUTE) // p3.3.50 + if(r->type == Route::MIDI_PORT_ROUTE) { if(r->channel != -1 && r->channel != 0) s += QString(" channelMask=\"%1\"").arg(r->channel); // Use new channel mask. @@ -1019,14 +1112,14 @@ void Track::writeRouting(int level, Xml& xml) const s = "dest"; - //if(r->type == Route::MIDI_DEVICE_ROUTE) // p3.3.49 Obsolete since 1.1-RC2 + //if(r->type == Route::MIDI_DEVICE_ROUTE) // Obsolete since 1.1-RC2 // s += QString(QT_TRANSLATE_NOOP("@default", " devtype=\"%1\"")).arg(r->device->deviceType()); // //if(r->type != Route::TRACK_ROUTE) // if(r->type != Route::TRACK_ROUTE && r->type != Route::MIDI_PORT_ROUTE) s += QString(" type=\"%1\"").arg(r->type); //s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(r->name()); - if(r->type == Route::MIDI_PORT_ROUTE) // p3.3.49 + if(r->type == Route::MIDI_PORT_ROUTE) s += QString(" mport=\"%1\"/").arg(r->midiPort); else s += QString(" name=\"%1\"/").arg(Xml::xmlString(r->name())); diff --git a/muse2/muse/track.h b/muse2/muse/track.h index 25a846ac..04cf71c2 100644 --- a/muse2/muse/track.h +++ b/muse2/muse/track.h @@ -4,6 +4,7 @@ // $Id: track.h,v 1.39.2.17 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -70,9 +71,10 @@ class Track { static bool _tmpSoloChainDoIns; static bool _tmpSoloChainNoDec; - // p3.3.38 RouteList _inRoutes; RouteList _outRoutes; + bool _nodeTraversed; // Internal anti circular route traversal flag. + int _auxRouteCount; // Number of aux paths feeding this track. QString _name; bool _recordFlag; @@ -89,8 +91,6 @@ class Track { int _activity; int _lastActivity; - //int _meter[MAX_CHANNELS]; - //int _peak[MAX_CHANNELS]; double _meter[MAX_CHANNELS]; double _peak[MAX_CHANNELS]; @@ -147,7 +147,10 @@ class Track { bool noInRoute() const { return _inRoutes.empty(); } bool noOutRoute() const { return _outRoutes.empty(); } void writeRouting(int, Xml&) const; - + bool isCircularRoute(Track* dst); + int auxRefCount() const { return _auxRouteCount; } // Number of Aux Tracks with routing paths to this track. + void updateAuxRoute(int refInc, Track* dst); // Internal use. + PartList* parts() { return &_parts; } const PartList* cparts() const { return &_parts; } Part* findPart(unsigned tick); @@ -168,20 +171,21 @@ class Track { virtual void setMute(bool val); virtual void setOff(bool val); - virtual void updateSoloStates(bool noDec) = 0; - virtual void updateInternalSoloStates(); - void updateSoloState(); void setInternalSolo(unsigned int val); - static void clearSoloRefCounts(); virtual void setSolo(bool val) = 0; virtual bool isMute() const = 0; - unsigned int internalSolo() const { return _internalSolo; } bool soloMode() const { return _soloRefCnt; } bool solo() const { return _solo; } bool mute() const { return _mute; } bool off() const { return _off; } bool recordFlag() const { return _recordFlag; } + // + // Internal use... + static void clearSoloRefCounts(); + void updateSoloState(); + virtual void updateSoloStates(bool noDec) = 0; + virtual void updateInternalSoloStates(); int activity() { return _activity; } void setActivity(int v) { _activity = v; } @@ -190,8 +194,6 @@ class Track { void addActivity(int v) { _activity += v; } void resetPeaks(); static void resetAllMeter(); - //int meter(int ch) const { return _meter[ch]; } - //int peak(int ch) const { return _peak[ch]; } double meter(int ch) const { return _meter[ch]; } double peak(int ch) const { return _peak[ch]; } void resetMeter(); @@ -220,9 +222,9 @@ class MidiTrack : public Track { int _outPort; int _outChannel; //int _inPortMask; - ///unsigned int _inPortMask; // bitmask of accepted record ports - ///int _inChannelMask; // bitmask of accepted record channels - bool _recEcho; // For midi (and audio). Whether to echo incoming record events to output device. + //unsigned int _inPortMask; // bitmask of accepted record ports + //int _inChannelMask; // bitmask of accepted record channels + bool _recEcho; // For midi (and audio). Whether to echo incoming record events to output device. EventList* _events; // tmp Events during midi import MPEventList* _mpevents; // tmp Events druring recording @@ -270,17 +272,17 @@ class MidiTrack : public Track { void setOutPortAndChannelAndUpdate(int /*port*/, int /*chan*/); //void setInPortMask(int i) { _inPortMask = i; } - ///void setInPortMask(unsigned int i) { _inPortMask = i; } // Obsolete - ///void setInChannelMask(int i) { _inChannelMask = i; } // + //void setInPortMask(unsigned int i) { _inPortMask = i; } // Obsolete + //void setInChannelMask(int i) { _inChannelMask = i; } // // Backward compatibility: For reading old songs. void setInPortAndChannelMask(unsigned int /*portmask*/, int /*chanmask*/); void setRecEcho(bool b) { _recEcho = b; } int outPort() const { return _outPort; } //int inPortMask() const { return _inPortMask; } - ///unsigned int inPortMask() const { return _inPortMask; } + //unsigned int inPortMask() const { return _inPortMask; } int outChannel() const { return _outChannel; } - ///int inChannelMask() const { return _inChannelMask; } + //int inChannelMask() const { return _inChannelMask; } bool recEcho() const { return _recEcho; } virtual bool isMute() const; @@ -298,14 +300,10 @@ class MidiTrack : public Track { clefTypes getClef() { return clefType; } }; -} // namespace MusECore - -namespace MusECore { - //--------------------------------------------------------- // AudioTrack // this track can hold audio automation data and can -// hold tracktypes AUDIO, AUDIO_MASTER, AUDIO_GROUP, +// hold tracktypes WAVE, AUDIO_GROUP, AUDIO_OUTPUT, // AUDIO_INPUT, AUDIO_SOFTSYNTH, AUDIO_AUX //--------------------------------------------------------- @@ -313,25 +311,19 @@ class AudioTrack : public Track { //friend class MidiTrack; //static unsigned int _soloRefCnt; - bool _haveData; + bool _haveData; // Whether we have data from a previous process call during current cycle. CtrlListList _controller; CtrlRecList _recEvents; // recorded automation events bool _prefader; // prefader metering std::vector<double> _auxSend; - Pipeline* _efxPipe; - - AutomationType _automationType; - - //RouteList _inRoutes; - //RouteList _outRoutes; - - bool _sendMetronome; - //void readRecfile(Xml& xml); void readAuxSend(Xml& xml); - + + bool _sendMetronome; + AutomationType _automationType; + Pipeline* _efxPipe; protected: float** outBuffers; @@ -344,7 +336,7 @@ class AudioTrack : public Track { SndFile* _recFile; Fifo fifo; // fifo -> _recFile bool _processed; - + public: AudioTrack(TrackType t); //AudioTrack(TrackType t, int num_out_bufs = MAX_CHANNELS); @@ -424,12 +416,6 @@ class AudioTrack : public Track { void readVolume(Xml& xml); //void writeRouting(int, Xml&) const; - // routing - //RouteList* inRoutes() { return &_inRoutes; } - //RouteList* outRoutes() { return &_outRoutes; } - //bool noInRoute() const { return _inRoutes.empty(); } - //bool noOutRoute() const { return _outRoutes.empty(); } - virtual void preProcessAlways() { _processed = false; } virtual void addData(unsigned /*samplePos*/, int /*channels*/, int /*srcStartChan*/, int /*srcChannels*/, unsigned /*frames*/, float** /*buffer*/); virtual void copyData(unsigned /*samplePos*/, int /*channels*/, int /*srcStartChan*/, int /*srcChannels*/, unsigned /*frames*/, float** /*buffer*/); diff --git a/muse2/muse/undo.cpp b/muse2/muse/undo.cpp index eb554495..8587b108 100644 --- a/muse2/muse/undo.cpp +++ b/muse2/muse/undo.cpp @@ -50,8 +50,11 @@ const char* UndoOp::typeName() "AddTrack", "DeleteTrack", "ModifyTrack", "AddPart", "DeletePart", "ModifyPart", "AddEvent", "DeleteEvent", "ModifyEvent", - "AddTempo", "DeleteTempo", "AddSig", "DeleteSig", - "SwapTrack", "ModifyClip" + "AddTempo", "DeleteTempo", + "AddSig", "DeleteSig", + "AddKey", "DeleteKey", + "SwapTrack", "ModifyClip", "ModifyMarker", + "ModifySongLen", "DoNothing" }; return name[type]; } @@ -95,6 +98,7 @@ void UndoOp::dump() case ModifyMarker: case AddKey: case DeleteKey: + case ModifySongLen: case DoNothing: break; } @@ -530,6 +534,10 @@ void Song::doUndo2() MusEGlobal::keymap.addKey(i->a, (key_enum)i->b); updateFlags |= SC_KEY; break; + case UndoOp::ModifySongLen: + _len=i->b; + updateFlags = -1; // set all flags + break; case UndoOp::ModifyClip: case UndoOp::ModifyMarker: case UndoOp::DoNothing: @@ -767,6 +775,10 @@ void Song::doRedo2() MusEGlobal::keymap.delKey(i->a); updateFlags |= SC_KEY; break; + case UndoOp::ModifySongLen: + _len=i->a; + updateFlags = -1; // set all flags + break; case UndoOp::ModifyClip: case UndoOp::ModifyMarker: case UndoOp::DoNothing: diff --git a/muse2/muse/undo.h b/muse2/muse/undo.h index 19c252bf..af55a307 100644 --- a/muse2/muse/undo.h +++ b/muse2/muse/undo.h @@ -54,6 +54,7 @@ struct UndoOp { SwapTrack, ModifyClip, ModifyMarker, + ModifySongLen, // a = new len, b = old len DoNothing }; UndoType type; diff --git a/muse2/muse/vst.h b/muse2/muse/vst.h index b57b598d..b41e7137 100644 --- a/muse2/muse/vst.h +++ b/muse2/muse/vst.h @@ -50,6 +50,7 @@ class VstSynth : public Synth { } virtual ~VstSynth() {} + virtual Type synthType() const { return VST_SYNTH; } virtual void incInstances(int val); virtual void* instantiate(); //virtual SynthIF* createSIF() const; diff --git a/muse2/muse/wave.cpp b/muse2/muse/wave.cpp index 06759441..f22f6f97 100644 --- a/muse2/muse/wave.cpp +++ b/muse2/muse/wave.cpp @@ -105,7 +105,7 @@ SndFile::~SndFile() bool SndFile::openRead() { if (openFlag) { - printf("SndFile:: alread open\n"); + printf("SndFile:: already open\n"); return false; } QString p = path(); @@ -468,7 +468,7 @@ size_t SndFile::readWithHeap(int srcChannels, float** dst, size_t n, bool overwr { float *buffer = new float[n * sfinfo.channels]; int rn = readInternal(srcChannels,dst,n,overwrite, buffer); - delete buffer; + delete[] buffer; return rn; } @@ -588,11 +588,11 @@ size_t SndFile::write(int srcChannels, float** src, size_t n) else { printf("SndFile:write channel mismatch %d -> %d\n", srcChannels, dstChannels); - delete buffer; + delete[] buffer; return 0; } int nbr = sf_writef_float(sf, buffer, n) ; - delete buffer; + delete[] buffer; return nbr; } @@ -1120,6 +1120,22 @@ SndFileR::~SndFileR() } } +void SndFileList::clearDelete() +{ + // ~SndFile searches itself on the list (and will find for + // sure) and deletes the entry on its own. + while (!empty()) + delete *begin(); + + /* this is wrong, because ~SndFile deletes itself from the + * list, causing the iterator to be invalidated -> fail. + for (SndFileList::iterator i = begin(); i != end(); ++i) + delete *i; + clear(); + */ +} + + } // namespace MusECore namespace MusEGui { diff --git a/muse2/muse/wave.h b/muse2/muse/wave.h index 58e0ab87..535df939 100644 --- a/muse2/muse/wave.h +++ b/muse2/muse/wave.h @@ -182,11 +182,7 @@ class SndFileR { class SndFileList : public std::list<SndFile*> { public: SndFile* search(const QString& name); - void clearDelete() { - for (SndFileList::iterator i = begin(); i != end(); ++i) - delete *i; - clear(); - } + void clearDelete(); }; typedef SndFileList::iterator iSndFile; diff --git a/muse2/muse/waveedit/waveedit.cpp b/muse2/muse/waveedit/waveedit.cpp index 537d8529..1c1115b1 100644 --- a/muse2/muse/waveedit/waveedit.cpp +++ b/muse2/muse/waveedit/waveedit.cpp @@ -289,6 +289,7 @@ WaveEdit::WaveEdit(MusECore::PartList* pl) } initTopwinState(); + MusEGlobal::muse->topwinMenuInited(this); } void WaveEdit::initShortcuts() diff --git a/muse2/muse/wavetrack.cpp b/muse2/muse/wavetrack.cpp index 9edeffb6..0d699cbe 100644 --- a/muse2/muse/wavetrack.cpp +++ b/muse2/muse/wavetrack.cpp @@ -229,8 +229,8 @@ bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float* ciRoute i = irl->begin(); if(i->track->isMidiTrack()) { - if(MusEGlobal::debugMsg) - printf("WaveTrack::getData: Error: First route is a midi track route!\n"); + //if(MusEGlobal::debugMsg) + // printf("WaveTrack::getData: Error: First route is a midi track route!\n"); return false; } // p3.3.38 @@ -246,8 +246,8 @@ bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float* { if(i->track->isMidiTrack()) { - if(MusEGlobal::debugMsg) - printf("WaveTrack::getData: Error: Route is a midi track route!\n"); + //if(MusEGlobal::debugMsg) + // printf("WaveTrack::getData: Error: Route is a midi track route!\n"); //return false; continue; } diff --git a/muse2/muse/widgets/citem.h b/muse2/muse/widgets/citem.h index 1777eb23..199d18b0 100644 --- a/muse2/muse/widgets/citem.h +++ b/muse2/muse/widgets/citem.h @@ -90,7 +90,7 @@ class CItem { }; typedef std::multimap<int, CItem*, std::less<int> >::iterator iCItem; -//typedef std::multimap<int, CItem*, std::less<int> >::const_iterator ciCItem; +typedef std::multimap<int, CItem*, std::less<int> >::const_iterator ciCItem; typedef std::multimap<int, CItem*, std::less<int> >::const_reverse_iterator rciCItem; //--------------------------------------------------------- diff --git a/muse2/muse/widgets/filedialog.cpp b/muse2/muse/widgets/filedialog.cpp index 68f2718f..7f2c1681 100644 --- a/muse2/muse/widgets/filedialog.cpp +++ b/muse2/muse/widgets/filedialog.cpp @@ -123,11 +123,13 @@ void MFileDialog::userToggled(bool flag) if (lastUserDir.isEmpty()) { - lastUserDir = MusEGlobal::museUser + QString("/") + baseDir; // Initialize if first time + //lastUserDir = MusEGlobal::museUser + QString("/") + baseDir; // Initialize if first time + lastUserDir = MusEGlobal::configPath + QString("/") + baseDir; // Initialize if first time // p4.0.39 } if (testDirCreate(this, lastUserDir)) - setDirectory(MusEGlobal::museUser); + //setDirectory(MusEGlobal::museUser); + setDirectory(MusEGlobal::configPath); // p4.0.39 else setDirectory(lastUserDir); @@ -270,34 +272,6 @@ void MFileDialog::directoryChanged(const QString&) } } - -//--------------------------------------------------------- -// getFilterExtension -//--------------------------------------------------------- - -QString getFilterExtension(const QString &filter) -{ - // - // Return the first extension found. Must contain at least one * character. - // - - int pos = filter.indexOf('*'); - if(pos == -1) - return QString(); - - QString filt; - int len = filter.length(); - ++pos; - for( ; pos < len; ++pos) - { - QChar c = filter[pos]; - if((c == ')') || (c == ';') || (c == ',') || (c == ' ')) - break; - filt += filter[pos]; - } - return filt; -} - //--------------------------------------------------------- // getOpenFileName //--------------------------------------------------------- @@ -509,10 +483,12 @@ FILE* fileOpen(QWidget* parent, QString name, const QString& ext, FILE* fp = 0; if (popenFlag) { if (strcmp(mode, "r") == 0) - zip += QString(" -d < "); + //zip += QString(" -d < "); + zip += QString(" -d < \""); // p4.0.40 else - zip += QString(" > "); - zip += name; + zip += QString(" > \""); + //zip += name; + zip = zip + name + QString("\""); // p4.0.40 fp = popen(zip.toAscii().data(), mode); } else { diff --git a/muse2/muse/widgets/filedialog.h b/muse2/muse/widgets/filedialog.h index b731b65a..b4e21e2c 100644 --- a/muse2/muse/widgets/filedialog.h +++ b/muse2/muse/widgets/filedialog.h @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: filedialog.h,v 1.2.2.2 2008/01/19 13:33:46 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License diff --git a/muse2/muse/widgets/genset.cpp b/muse2/muse/widgets/genset.cpp index d7642b3b..ec2c8637 100644 --- a/muse2/muse/widgets/genset.cpp +++ b/muse2/muse/widgets/genset.cpp @@ -4,6 +4,7 @@ // $Id: genset.cpp,v 1.7.2.8 2009/12/01 03:52:40 terminator356 Exp $ // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,8 +25,10 @@ #include <stdio.h> #include <QFileDialog> +#include <QFileInfo> #include <QRect> #include <QShowEvent> +#include <QString> #include "genset.h" #include "app.h" @@ -33,6 +36,7 @@ #include "midiseq.h" #include "globals.h" #include "icons.h" +#include "helper.h" namespace MusEGui { @@ -93,12 +97,6 @@ GlobalSettingsConfig::GlobalSettingsConfig(QWidget* parent) } } - userInstrumentsPath->setText(MusEGlobal::config.userInstrumentsDir); - selectInstrumentsDirButton->setIcon(*openIcon); - defaultInstrumentsDirButton->setIcon(*undoIcon); - connect(selectInstrumentsDirButton, SIGNAL(clicked()), SLOT(selectInstrumentsPath())); - connect(defaultInstrumentsDirButton, SIGNAL(clicked()), SLOT(defaultInstrumentsPath())); - guiRefreshSelect->setValue(MusEGlobal::config.guiRefresh); minSliderSelect->setValue(int(MusEGlobal::config.minSlider)); minMeterSelect->setValue(MusEGlobal::config.minMeter); @@ -119,6 +117,9 @@ GlobalSettingsConfig::GlobalSettingsConfig(QWidget* parent) Period affects midi playback resolution. Shorter periods are desirable.</string> </property> */ + + projDirEntry->setText(MusEGlobal::config.projectBaseFolder); + projDirOpenToolButton->setIcon(*openIcon); startSongEntry->setText(MusEGlobal::config.startSong); startSongGroup->button(MusEGlobal::config.startMode)->setChecked(true); @@ -171,9 +172,14 @@ Shorter periods are desirable.</string> popsDefStayOpenCheckBox->setChecked(MusEGlobal::config.popupsDefaultStayOpen); lmbDecreasesCheckBox->setChecked(MusEGlobal::config.leftMouseButtonCanDecrease); rangeMarkerWithoutMMBCheckBox->setChecked(MusEGlobal::config.rangeMarkerWithoutMMB); - + + addHiddenCheckBox->setChecked(MusEGlobal::config.addHiddenTracks); + unhideTracksCheckBox->setChecked(MusEGlobal::config.unhideTracks); + //updateSettings(); // TESTING + connect(projDirOpenToolButton, SIGNAL(clicked()), SLOT(browseProjDir())); + connect(applyButton, SIGNAL(clicked()), SLOT(apply())); connect(okButton, SIGNAL(clicked()), SLOT(ok())); connect(cancelButton, SIGNAL(clicked()), SLOT(cancel())); @@ -258,6 +264,8 @@ void GlobalSettingsConfig::updateSettings() //dummyAudioRealRate->setText(dad ? QString().setNum(sampleRate) : "---"); //dummyAudioRealRate->setText(QString().setNum(sampleRate)); // Not used any more. p4.0.20 + projDirEntry->setText(MusEGlobal::config.projectBaseFolder); + startSongEntry->setText(MusEGlobal::config.startSong); startSongGroup->button(MusEGlobal::config.startMode)->setChecked(true); @@ -310,6 +318,9 @@ void GlobalSettingsConfig::updateSettings() lmbDecreasesCheckBox->setChecked(MusEGlobal::config.leftMouseButtonCanDecrease); rangeMarkerWithoutMMBCheckBox->setChecked(MusEGlobal::config.rangeMarkerWithoutMMB); + addHiddenCheckBox->setChecked(MusEGlobal::config.addHiddenTracks); + unhideTracksCheckBox->setChecked(MusEGlobal::config.unhideTracks); + updateMdiSettings(); } @@ -350,7 +361,9 @@ void GlobalSettingsConfig::apply() MusEGlobal::config.useOutputLimiter = outputLimiterCheckBox->isChecked(); MusEGlobal::config.vstInPlace = vstInPlaceCheckBox->isChecked(); MusEGlobal::config.rtcTicks = rtcResolutions[rtcticks]; - MusEGlobal::config.userInstrumentsDir = userInstrumentsPath->text(); + + MusEGlobal::config.projectBaseFolder = projDirEntry->text(); + MusEGlobal::config.startSong = startSongEntry->text(); MusEGlobal::config.startMode = startSongGroup->checkedId(); int das = dummyAudioSize->currentIndex(); @@ -408,6 +421,9 @@ void GlobalSettingsConfig::apply() MusEGlobal::config.leftMouseButtonCanDecrease = lmbDecreasesCheckBox->isChecked(); MusEGlobal::config.rangeMarkerWithoutMMB = rangeMarkerWithoutMMBCheckBox->isChecked(); + MusEGlobal::config.addHiddenTracks = addHiddenCheckBox->isChecked(); + MusEGlobal::config.unhideTracks = unhideTracksCheckBox->isChecked(); + //MusEGlobal::muse->showMixer(MusEGlobal::config.mixerVisible); MusEGlobal::muse->showMixer1(MusEGlobal::config.mixer1Visible); MusEGlobal::muse->showMixer2(MusEGlobal::config.mixer2Visible); @@ -442,8 +458,6 @@ void GlobalSettingsConfig::apply() MusEGlobal::muse->resize(MusEGlobal::config.geometryMain.size()); MusEGlobal::muse->move(MusEGlobal::config.geometryMain.topLeft()); - MusEGlobal::museUserInstruments = MusEGlobal::config.userInstrumentsDir; - MusEGlobal::muse->setHeartBeat(); // set guiRefresh MusEGlobal::midiSeq->msgSetRtc(); // set midi tick rate @@ -546,21 +560,6 @@ void GlobalSettingsConfig::transportCurrent() transportY->setValue(r.y()); } -void GlobalSettingsConfig::selectInstrumentsPath() - { - QString dir = QFileDialog::getExistingDirectory(this, - tr("Selects instruments directory"), - MusEGlobal::config.userInstrumentsDir); - userInstrumentsPath->setText(dir); - } - -void GlobalSettingsConfig::defaultInstrumentsPath() - { - QString dir = MusEGlobal::configPath + "/instruments"; - userInstrumentsPath->setText(dir); - } - - void GlobalSettingsConfig::traditionalPreset() { for (std::list<MdiSettings*>::iterator it = mdisettings.begin(); it!=mdisettings.end(); it++) @@ -598,5 +597,12 @@ void GlobalSettingsConfig::borlandPreset() updateMdiSettings(); } +void GlobalSettingsConfig::browseProjDir() +{ + QString dir = MusEGui::browseProjectFolder(this); + if(!dir.isEmpty()) + projDirEntry->setText(dir); +} + } // namespace MusEGui diff --git a/muse2/muse/widgets/genset.h b/muse2/muse/widgets/genset.h index 0e1697ee..b076a27f 100644 --- a/muse2/muse/widgets/genset.h +++ b/muse2/muse/widgets/genset.h @@ -4,6 +4,7 @@ // $Id: genset.h,v 1.3 2004/01/25 09:55:17 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -53,8 +54,7 @@ class GlobalSettingsConfig : public QDialog, public Ui::GlobalSettingsDialogBase void bigtimeCurrent(); void mainCurrent(); void transportCurrent(); - void selectInstrumentsPath(); - void defaultInstrumentsPath(); + void browseProjDir(); void traditionalPreset(); void mdiPreset(); void borlandPreset(); diff --git a/muse2/muse/widgets/gensetbase.ui b/muse2/muse/widgets/gensetbase.ui index ba26cf38..124ee72a 100644 --- a/muse2/muse/widgets/gensetbase.ui +++ b/muse2/muse/widgets/gensetbase.ui @@ -30,7 +30,36 @@ <string>Application</string> </attribute> <layout class="QGridLayout"> - <item row="1" column="0"> + <item row="0" column="0"> + <widget class="QGroupBox" name="projDirGroupBox"> + <property name="title"> + <string>Project directory</string> + </property> + <layout class="QHBoxLayout" name="qhboxProjDir"> + <item> + <widget class="QLabel" name="textLabel_ProjDir"> + <property name="text"> + <string>Projects:</string> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="projDirEntry"/> + </item> + <item> + <widget class="QToolButton" name="projDirOpenToolButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="2" column="0"> <widget class="QGroupBox" name="groupBox4"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> @@ -413,14 +442,14 @@ </layout> </widget> </item> - <item row="0" column="0"> + <item row="1" column="0"> <widget class="QGroupBox" name="groupBox3"> <property name="title"> <string>Start Muse</string> </property> <layout class="QGridLayout"> <item row="0" column="0"> - <layout class="QGridLayout"> + <layout class="QGridLayout" name="gridLayout11"> <item row="1" column="0"> <spacer name="spacer7_2"> <property name="orientation"> @@ -438,7 +467,7 @@ </spacer> </item> <item row="2" column="0" colspan="2"> - <layout class="QHBoxLayout"> + <layout class="QHBoxLayout" name="qhboxStartSong"> <item> <widget class="QLabel" name="textLabel1_2"> <property name="text"> @@ -811,9 +840,9 @@ Adjusts responsiveness of audio controls and <number>2</number> </property> <item row="0" column="0"> - <layout class="QGridLayout"> + <layout class="QGridLayout" name="gridLayoutwaveEditor"> <item row="0" column="0"> - <layout class="QHBoxLayout"> + <layout class="QHBoxLayout" name="qhboxLayoutWaveEditor"> <item> <widget class="QLabel" name="textLabel2_2"> <property name="sizePolicy"> @@ -1179,31 +1208,7 @@ Adjusts responsiveness of audio controls and </item> </widget> </item> - <item row="3" column="0"> - <widget class="QLabel" name="TextLabel3_4"> - <property name="text"> - <string>Instruments Directory</string> - </property> - </widget> - </item> <item row="3" column="1"> - <widget class="QLineEdit" name="userInstrumentsPath"/> - </item> - <item row="3" column="2"> - <widget class="QPushButton" name="selectInstrumentsDirButton"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="3" column="3"> - <widget class="QPushButton" name="defaultInstrumentsDirButton"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="4" column="1"> <spacer name="verticalSpacer_3"> <property name="orientation"> <enum>Qt::Vertical</enum> @@ -1223,7 +1228,7 @@ Adjusts responsiveness of audio controls and </widget> <widget class="QWidget" name="tab3"> <attribute name="title"> - <string>GUI</string> + <string>GUI Behaviour</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_3"> <property name="spacing"> @@ -1406,10 +1411,58 @@ left button behave like the middle button in such areas.</string> </property> </widget> </item> + <item row="9" column="0"> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="7" column="1"> + <widget class="QCheckBox" name="addHiddenCheckBox"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="7" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Allow adding hidden tracks in track list menu</string> + </property> + </widget> + </item> + <item row="8" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Unhide tracks when adding hidden tracks</string> + </property> + </widget> + </item> + <item row="8" column="1"> + <widget class="QCheckBox" name="unhideTracksCheckBox"> + <property name="text"> + <string/> + </property> + </widget> + </item> </layout> </widget> </item> - <item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>GUI Style</string> + </attribute> + <layout class="QGridLayout" name="gridLayoutTab2"> + <item row="0" column="0"> <widget class="QGroupBox" name="groupBox_2"> <property name="title"> <string>MDI-subwindowness and sharing menus</string> @@ -1460,8 +1513,8 @@ left button behave like the middle button in such areas.</string> <rect> <x>0</x> <y>0</y> - <width>482</width> - <height>143</height> + <width>512</width> + <height>378</height> </rect> </property> <layout class="QHBoxLayout" name="horizontalLayout_3"> diff --git a/muse2/muse/widgets/header.cpp b/muse2/muse/widgets/header.cpp index 2c1057c3..968e7f73 100644 --- a/muse2/muse/widgets/header.cpp +++ b/muse2/muse/widgets/header.cpp @@ -51,7 +51,7 @@ void Header::readStatus(MusECore::Xml& xml) for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { int logialIdx=abs((*it).toInt()); bool isHidden = (*it).toInt() < 0 ? true:false; - int section = visualIndex(logialIdx); + int section = visualIndex(logialIdx - (isHidden? 1:0)); moveSection(section, index); if (isHidden) hideSection(logialIdx-1); @@ -65,7 +65,7 @@ void Header::readStatus(MusECore::Xml& xml) bool foundIt=false; for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { int id=((*it).toInt()); - if ( id == i || i ==1-id ) + if ( id == i || i == -1 - id ) foundIt=true; } if (foundIt == false) { @@ -97,7 +97,7 @@ void Header::writeStatus(int level, MusECore::Xml& xml) const //xml.nput(level, "<%s> ", name()); xml.nput(level, "<%s> ", MusECore::Xml::xmlString(objectName()).toLatin1().constData()); int n = count(); - for (int i = n; i >= 0; --i) { + for (int i = n-1; i >= 0; --i) { if (isSectionHidden(logicalIndex(i))) xml.nput("%d ", -logicalIndex(i)-1); // hidden is stored as negative value starting from -1 else diff --git a/muse2/muse/widgets/knob.cpp b/muse2/muse/widgets/knob.cpp index 920d2028..269f65cf 100644 --- a/muse2/muse/widgets/knob.cpp +++ b/muse2/muse/widgets/knob.cpp @@ -323,6 +323,30 @@ void Knob::rangeChange() repaint(); } +void Knob::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::MidButton || e->modifiers() & Qt::ControlModifier) { + int xpos = e->x() - width() /2; + double v = float(e->y()) / height() * 1.2; + + double halfRange = (maxValue() - minValue())/2; + double midValue = minValue() + halfRange; + // apply to range + if (xpos < 0) { // left values + v = -v; + } + setValue(v * halfRange + midValue); + SliderBase::valueChange(); + + // fake a left-click to make the knob still "stick" to + // the mouse. + QMouseEvent temp(e->type(), e->pos(), Qt::LeftButton, e->buttons(), e->modifiers()); + SliderBase::mousePressEvent(&temp); + return; + } + SliderBase::mousePressEvent(e); +} + //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- diff --git a/muse2/muse/widgets/knob.h b/muse2/muse/widgets/knob.h index 483fafa6..bd8621a1 100644 --- a/muse2/muse/widgets/knob.h +++ b/muse2/muse/widgets/knob.h @@ -80,6 +80,7 @@ class Knob : public SliderBase, public ScaleIf virtual void paintEvent(QPaintEvent *); virtual void resizeEvent(QResizeEvent *e); + virtual void mousePressEvent(QMouseEvent *e); double getValue(const QPoint &p); void getScrollMode( QPoint &p, const Qt::MouseButton &button, int &scrollMode, int &direction ); void scaleChange() { repaint(); } diff --git a/muse2/muse/widgets/meter.cpp b/muse2/muse/widgets/meter.cpp index 5c79ffb2..d9ca8e20 100644 --- a/muse2/muse/widgets/meter.cpp +++ b/muse2/muse/widgets/meter.cpp @@ -229,6 +229,8 @@ void Meter::setRange(double min, double max) void Meter::paintEvent(QPaintEvent* ev) { + // For some reason upon resizing we get double calls here and in resizeEvent. + QPainter p(this); p.setRenderHint(QPainter::Antialiasing); @@ -534,8 +536,11 @@ void Meter::drawVU(QPainter& p, const QRect& rect, const QPainterPath& drawPath, void Meter::resizeEvent(QResizeEvent* ev) { - QFrame::resizeEvent(ev); + // For some reason upon resizing we get double calls here and in paintEvent. + //printf("Meter::resizeEvent w:%d h:%d\n", ev->size().width(), ev->size().height()); cur_yv = -1; // Force re-initialization. + QFrame::resizeEvent(ev); + update(); } //--------------------------------------------------------- diff --git a/muse2/muse/widgets/midisync.ui b/muse2/muse/widgets/midisync.ui index 8fc6248a..81d7451e 100644 --- a/muse2/muse/widgets/midisync.ui +++ b/muse2/muse/widgets/midisync.ui @@ -388,6 +388,19 @@ Enabled inputs in the list will </column> </widget> </item> + <item row="5" column="0"> + <widget class="QLabel" name="toBeDoneLabel"> + <property name="text"> + <string>Note: Sync delay and MTC sync currently not fully implemeted</string> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + <property name="wordWrap"> + <bool>false</bool> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/muse2/muse/widgets/mtrackinfo.cpp b/muse2/muse/widgets/mtrackinfo.cpp index b5460447..19e9729a 100644 --- a/muse2/muse/widgets/mtrackinfo.cpp +++ b/muse2/muse/widgets/mtrackinfo.cpp @@ -574,7 +574,8 @@ void MidiTrackInfo::setLabelText() //gradient.setColorAt(0, c.darker()); //gradient.setColorAt(0, c); //gradient.setColorAt(1, c.darker()); - gradient.setColorAt(0, c.lighter()); + gradient.setColorAt(0, c); + gradient.setColorAt(0.5, c.lighter()); gradient.setColorAt(1, c); //palette.setBrush(QPalette::Button, gradient); //palette.setBrush(QPalette::Window, gradient); @@ -635,14 +636,17 @@ void MidiTrackInfo::iOutputPortChanged(int index) { if(!selected) return; + int port_num = iOutput->itemData(index).toInt(); + if(port_num < 0 || port_num >= MIDI_PORTS) + return; MusECore::MidiTrack* track = (MusECore::MidiTrack*)selected; - if (index == track->outPort()) + if (port_num == track->outPort()) return; // Changed by T356. - //track->setOutPort(index); + //track->setOutPort(port_num); MusEGlobal::audio->msgIdle(true); - //audio->msgSetTrackOutPort(track, index); - track->setOutPortAndUpdate(index); + //audio->msgSetTrackOutPort(track, port_num); + track->setOutPortAndUpdate(port_num); MusEGlobal::audio->msgIdle(false); //MusEGlobal::song->update(SC_MIDI_TRACK_PROP); @@ -1081,11 +1085,9 @@ void MidiTrackInfo::instrPopup() //QMenu* pup = new QMenu; PopupMenu* pup = new PopupMenu(true); - //instr->populatePatchPopup(pop, channel, MusEGlobal::song->mtype(), track->type() == MusECore::Track::DRUM); - populatePatchPopup(instr, pup, channel, MusEGlobal::song->mtype(), track->type() == MusECore::Track::DRUM); + instr->populatePatchPopup(pup, channel, MusEGlobal::song->mtype(), track->type() == MusECore::Track::DRUM); + //populatePatchPopup(instr, pup, channel, MusEGlobal::song->mtype(), track->type() == MusECore::Track::DRUM); - //if(pop->actions().count() == 0) - // return; if(pup->actions().count() == 0) { delete pup; @@ -1095,7 +1097,6 @@ void MidiTrackInfo::instrPopup() connect(pup, SIGNAL(triggered(QAction*)), SLOT(instrPopupActivated(QAction*))); //connect(pup, SIGNAL(hovered(QAction*)), SLOT(instrPopupActivated(QAction*))); - //QAction *act = pop->exec(iPatch->mapToGlobal(QPoint(10,5))); QAction *act = pup->exec(iPatch->mapToGlobal(QPoint(10,5))); if(act) { @@ -1346,12 +1347,20 @@ void MidiTrackInfo::updateTrackInfo(int flags) //iInput->clear(); iOutput->clear(); + int item_idx = 0; for (int i = 0; i < MIDI_PORTS; ++i) { + MusECore::MidiDevice* md = MusEGlobal::midiPorts[i].device(); + if(!md) // In the case of this combo box, don't bother listing empty ports. p4.0.41 + continue; + //if(!(md->rwFlags() & 1 || md->isSynti()) && (i != outPort)) + if(!(md->rwFlags() & 1) && (i != outPort)) // Only writeable ports, or current one. p4.0.41 + continue; QString name; name.sprintf("%d:%s", i+1, MusEGlobal::midiPorts[i].portname().toLatin1().constData()); - iOutput->insertItem(i, name); + iOutput->insertItem(item_idx, name, i); if (i == outPort) - iOutput->setCurrentIndex(i); + iOutput->setCurrentIndex(item_idx); + item_idx++; } iOutput->blockSignals(false); diff --git a/muse2/muse/widgets/musewidgetsplug.cpp b/muse2/muse/widgets/musewidgetsplug.cpp index 4068da57..f707fb7c 100644 --- a/muse2/muse/widgets/musewidgetsplug.cpp +++ b/muse2/muse/widgets/musewidgetsplug.cpp @@ -157,7 +157,8 @@ MusEGlobal::GlobalConfigValues config = { -60.0, // double minSlider; false, // use Jack freewheel 20, // int guiRefresh; - QString(""), // helpBrowser + QString(""), // userInstrumentsDir // Obsolete. Must keep for compatibility. + //QString(""), // helpBrowser // Obsolete true, // extendedMidi 384, // division for smf export QString(""), // copyright string for smf export diff --git a/muse2/muse/widgets/popupmenu.cpp b/muse2/muse/widgets/popupmenu.cpp index af546ec3..af870975 100644 --- a/muse2/muse/widgets/popupmenu.cpp +++ b/muse2/muse/widgets/popupmenu.cpp @@ -341,21 +341,38 @@ void PopupMenu::popHovered(QAction* action) void PopupMenu::mouseReleaseEvent(QMouseEvent *e) { - #ifdef POPUP_MENU_DISABLE_STAY_OPEN + QAction* action = actionAt(e->pos()); + if (!(action && action == activeAction() && !action->isSeparator() && action->isEnabled())) + action=NULL; + + #ifdef POPUP_MENU_DISABLE_STAY_OPEN + if (action && action->menu() != NULL && action->isCheckable()) + action->activate(QAction::Trigger); + QMenu::mouseReleaseEvent(e); + + if (action && action->menu() != NULL && action->isCheckable()) + close(); + return; #else // Check for Ctrl to stay open. if(!_stayOpen || (!MusEGlobal::config.popupsDefaultStayOpen && (e->modifiers() & Qt::ControlModifier) == 0)) { + if (action && action->menu() != NULL && action->isCheckable()) + action->activate(QAction::Trigger); + QMenu::mouseReleaseEvent(e); + + if (action && action->menu() != NULL && action->isCheckable()) + close(); + return; } //printf("PopupMenu::mouseReleaseEvent\n"); - QAction *action = actionAt(e->pos()); - if (action && action == activeAction() && !action->isSeparator() && action->isEnabled()) + if (action) action->activate(QAction::Trigger); else QMenu::mouseReleaseEvent(e); diff --git a/muse2/muse/widgets/poslabel.cpp b/muse2/muse/widgets/poslabel.cpp index 6cdcb27e..b893c58d 100644 --- a/muse2/muse/widgets/poslabel.cpp +++ b/muse2/muse/widgets/poslabel.cpp @@ -138,7 +138,11 @@ void PosLabel::setTickValue(unsigned val) if (val == _tickValue) return; if (val >= MAX_TICK) - abort(); + { + printf("THIS SHOULD NEVER HAPPEN: val=%u > MAX_TICK=%u in PosLabel::setTickValue()!\n",val, MAX_TICK); + val=MAX_TICK-1; + } + _tickValue = val; updateValue(); } diff --git a/muse2/muse/widgets/projectcreate.ui b/muse2/muse/widgets/projectcreate.ui index 406c83d6..d03f093c 100644 --- a/muse2/muse/widgets/projectcreate.ui +++ b/muse2/muse/widgets/projectcreate.ui @@ -17,28 +17,94 @@ <item> <layout class="QVBoxLayout" name="verticalLayout"> <item> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Project Name:</string> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <item> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Projects folder:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="projDirLineEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="projDirToolButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_3"> <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Project Name:</string> + </property> + </widget> + </item> + <item> <widget class="QLineEdit" name="projectNameEdit"/> </item> <item> + <widget class="QCheckBox" name="templateCheckBox"> + <property name="text"> + <string>Project is a Template</string> + </property> + </widget> + </item> + <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeType"> - <enum>QSizePolicy::Fixed</enum> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>60</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Project song file type:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="projectFileTypeCB"/> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> - <width>75</width> + <width>40</width> <height>20</height> </size> </property> @@ -87,9 +153,16 @@ </widget> </item> <item> - <widget class="QPushButton" name="browseDirButton"> + <widget class="QToolButton" name="browseDirButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="restorePathButton"> <property name="text"> - <string>Browse</string> + <string>...</string> </property> </widget> </item> @@ -123,6 +196,18 @@ </item> </layout> </widget> + <tabstops> + <tabstop>projectNameEdit</tabstop> + <tabstop>templateCheckBox</tabstop> + <tabstop>projectFileTypeCB</tabstop> + <tabstop>createFolderCheckbox</tabstop> + <tabstop>storageDirEdit</tabstop> + <tabstop>browseDirButton</tabstop> + <tabstop>commentEdit</tabstop> + <tabstop>buttonBox</tabstop> + <tabstop>projDirLineEdit</tabstop> + <tabstop>projDirToolButton</tabstop> + </tabstops> <resources/> <connections> <connection> diff --git a/muse2/muse/widgets/projectcreateimpl.cpp b/muse2/muse/widgets/projectcreateimpl.cpp index 31973101..665d725e 100644 --- a/muse2/muse/widgets/projectcreateimpl.cpp +++ b/muse2/muse/widgets/projectcreateimpl.cpp @@ -4,6 +4,7 @@ // $Id: ./muse/widgets/projectcreateimpl.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,12 +22,16 @@ // //========================================================= #include <stdio.h> -#include <qfiledialog.h> -#include <qdir.h> +#include <QFileDialog> +#include <QDir> +#include <QStringList> + #include "projectcreateimpl.h" #include "gconfig.h" #include "globals.h" #include "app.h" +#include "helper.h" +#include "icons.h" namespace MusEGui { @@ -35,10 +40,51 @@ ProjectCreateImpl::ProjectCreateImpl(QWidget *parent) : { setupUi(this); - createFolderCheckbox->setChecked(MusEGlobal::config.projectStoreInFolder); + //bool is_new = (MusEGlobal::museProject == MusEGlobal::museProjectInitPath); + directoryPath = MusEGlobal::config.projectBaseFolder; + + QStringList filters = localizedStringListFromCharArray(MusEGlobal::project_create_file_save_pattern, "file_patterns"); + projectFileTypeCB->addItems(filters); + + QString proj_title = MusEGlobal::muse->projectTitle(); + QString proj_path = MusEGlobal::muse->projectPath(); + QString proj_ext = MusEGlobal::muse->projectExtension(); + + projectNameEdit->setText(proj_title); + + bool is_template = proj_path.startsWith(MusEGlobal::configPath + "/templates"); + + templateCheckBox->setChecked(is_template); + + projDirPath = proj_path; + + int type_idx = 0; + if(!proj_ext.isEmpty()) + { + // FIXME Imperfect. Trying to avoid adding yet another series of character strings. p4.0.40 + type_idx = projectFileTypeCB->findText(proj_ext, Qt::MatchContains | Qt::MatchCaseSensitive); + if(type_idx == -1) + type_idx = 0; + } + projectFileTypeCB->setCurrentIndex(type_idx); + + projDirToolButton->setIcon(*openIcon); + browseDirButton->setIcon(*openIcon); + restorePathButton->setIcon(*undoIcon); + + restorePathButton->setEnabled(false); // Disabled at first. + + //createFolderCheckbox->setChecked(MusEGlobal::config.projectStoreInFolder && is_new); // Suggest no folder if not new. + + connect(templateCheckBox,SIGNAL(clicked()), this, SLOT(templateButtonChanged())); + //connect(templateCheckBox,SIGNAL(clicked()), this, SLOT(updateDirectoryPath())); + connect(projDirToolButton,SIGNAL(clicked()), this, SLOT(browseProjDir())); + connect(restorePathButton,SIGNAL(clicked()), this, SLOT(restorePath())); connect(browseDirButton,SIGNAL(clicked()), this, SLOT(selectDirectory())); - connect(projectNameEdit,SIGNAL(textChanged(QString)), this, SLOT(updateDirectoryPath())); - connect(createFolderCheckbox,SIGNAL(clicked()), this, SLOT(updateDirectoryPath())); + //connect(projectNameEdit,SIGNAL(textChanged(QString)), this, SLOT(updateDirectoryPath())); + connect(projectNameEdit,SIGNAL(textChanged(QString)), this, SLOT(updateProjectName())); + connect(createFolderCheckbox,SIGNAL(clicked()), this, SLOT(createProjFolderChanged())); + connect(projectFileTypeCB,SIGNAL(currentIndexChanged(int)), this, SLOT(updateDirectoryPath())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(ok())); #if QT_VERSION >= 0x040700 projectNameEdit->setPlaceholderText("<Project Name>"); @@ -46,52 +92,155 @@ ProjectCreateImpl::ProjectCreateImpl(QWidget *parent) : // as of Qt-4.7.1 //commentEdit->setPlaceholderText("<Add information about project here>"); #endif - directoryPath = MusEGlobal::config.projectBaseFolder; updateDirectoryPath(); show(); } void ProjectCreateImpl::selectDirectory() { - QFileDialog qfd; - qfd.selectFile(directoryPath); - qfd.setFileMode(QFileDialog::DirectoryOnly); - if (qfd.exec() == QDialog::Rejected) { - return; - } - directoryPath=qfd.selectedFiles().first(); + QString dpath = templateCheckBox->isChecked() ? + (overrideTemplDirPath.isEmpty() ? (MusEGlobal::configPath + QString("/templates")) : overrideTemplDirPath) : + (overrideDirPath.isEmpty() ? directoryPath : overrideDirPath); + + QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory"), dpath); + if(dir.isEmpty()) + return; + + (templateCheckBox->isChecked() ? overrideTemplDirPath : overrideDirPath) = dir; + restorePathButton->setEnabled(true); updateDirectoryPath(); } -void ProjectCreateImpl::updateDirectoryPath() +void ProjectCreateImpl::updateProjectName() { + QString curExt = projectFileTypeCB->currentText(); + if(curExt.isEmpty()) + curExt = ".med"; + else + { + curExt = MusEGui::getFilterExtension(curExt); + // Do we have a valid extension? + if(curExt.isEmpty()) + curExt = ".med"; + } + QString name = ""; if (createFolderCheckbox->isChecked()) { if (!projectNameEdit->text().isEmpty()) - name = projectNameEdit->text() + "/" + projectNameEdit->text() + ".med"; + //name = projectNameEdit->text() + "/" + projectNameEdit->text() + ".med"; + name = projectNameEdit->text() + "/" + projectNameEdit->text() + curExt; //storageDirEdit->setText(directoryPath + name ); } else { if (!projectNameEdit->text().isEmpty()) - name = projectNameEdit->text() + ".med"; + //name = projectNameEdit->text() + ".med"; + name = projectNameEdit->text() + curExt; //storageDirEdit->setText(directoryPath +"/" + name); } - storageDirEdit->setText(directoryPath +"/" + name ); // Tim + + bool is_new = (MusEGlobal::museProject == MusEGlobal::museProjectInitPath); + + QString dpath = templateCheckBox->isChecked() ? + (overrideTemplDirPath.isEmpty() ? (MusEGlobal::configPath + QString("/templates")) : overrideTemplDirPath) : + (overrideDirPath.isEmpty() ? (is_new ? directoryPath : projDirPath) : overrideDirPath); + + QDir proj_dir(dpath); + //if(is_project && MusEGlobal::config.projectStoreInFolder) + bool is_project = dpath.startsWith(MusEGlobal::config.projectBaseFolder); + //bool is_template = dpath.startsWith(MusEGlobal::configPath + "/templates") && templateCheckBox->isChecked(); + //bool is_new = (MusEGlobal::museProject == MusEGlobal::museProjectInitPath) && MusEGlobal::config.projectStoreInFolder; + //bool is_new = (MusEGlobal::museProject == MusEGlobal::museProjectInitPath) && + MusEGlobal::config.projectStoreInFolder && + (templateCheckBox->isChecked() ? overrideTemplDirPath.isEmpty() : overrideDirPath.isEmpty()); + //bool is_template = is_new && dpath.startsWith(MusEGlobal::configPath + "/templates") && templateCheckBox->isChecked(); + if(!is_new && createFolderCheckbox->isChecked() && !templateCheckBox->isChecked() && + (templateCheckBox->isChecked() ? overrideTemplDirPath.isEmpty() : overrideDirPath.isEmpty())) + proj_dir.cdUp(); + dpath = proj_dir.absolutePath(); + + //if(!initProjPath.isEmpty()) + //{ + // initProjPath.clear(); + //} + + storageDirEdit->blockSignals(true); + storageDirEdit->setText(dpath + "/" + name ); + storageDirEdit->blockSignals(false); + + projDirLineEdit->setEnabled(!templateCheckBox->isChecked() && is_project); +} + +void ProjectCreateImpl::updateDirectoryPath() +{ + updateProjectName(); + + projDirLineEdit->blockSignals(true); + projDirLineEdit->setText(MusEGlobal::config.projectBaseFolder); + projDirLineEdit->blockSignals(false); } -QString ProjectCreateImpl::getProjectPath() +QString ProjectCreateImpl::getProjectPath() const { return storageDirEdit->text(); } -QString ProjectCreateImpl::getSongInfo() + +QString ProjectCreateImpl::getSongInfo() const { return commentEdit->toPlainText(); } + void ProjectCreateImpl::ok() { MusEGlobal::config.projectStoreInFolder = createFolderCheckbox->isChecked(); - MusEGlobal::config.projectBaseFolder = directoryPath; - MusEGlobal::muse->changeConfig(true); + //MusEGlobal::config.projectBaseFolder = directoryPath; + //MusEGlobal::muse->changeConfig(true); emit accept(); } +void ProjectCreateImpl::createProjFolderChanged() +{ + //MusEGlobal::config.projectStoreInFolder = createFolderCheckbox->isChecked(); + //MusEGlobal::muse->changeConfig(true); // Save to config file. + updateDirectoryPath(); +} + +void ProjectCreateImpl::browseProjDir() +{ + QString dir = MusEGui::browseProjectFolder(this); + if(!dir.isEmpty()) + { + directoryPath = dir; + MusEGlobal::config.projectBaseFolder = dir; + MusEGlobal::muse->changeConfig(true); // Save to config file. + updateDirectoryPath(); + } +} + +void ProjectCreateImpl::templateButtonChanged() +{ + restorePathButton->setEnabled(templateCheckBox->isChecked() ? !overrideTemplDirPath.isEmpty() : !overrideDirPath.isEmpty()); + updateDirectoryPath(); +} + +void ProjectCreateImpl::restorePath() +{ + if(templateCheckBox->isChecked()) + overrideTemplDirPath.clear(); + else + overrideDirPath.clear(); + restorePathButton->setEnabled(templateCheckBox->isChecked() ? !overrideTemplDirPath.isEmpty() : !overrideDirPath.isEmpty()); + updateDirectoryPath(); +} + +/* +bool ProjectCreateImpl::getProjectIsTemplate() const +{ + return templateCheckBox->isChecked(); +} + +QString ProjectCreateImpl::getTemplatePath() const +{ + return templDirPath; +} +*/ + } //namespace MusEGui diff --git a/muse2/muse/widgets/projectcreateimpl.h b/muse2/muse/widgets/projectcreateimpl.h index 258b0921..f08cb1bc 100644 --- a/muse2/muse/widgets/projectcreateimpl.h +++ b/muse2/muse/widgets/projectcreateimpl.h @@ -4,6 +4,7 @@ // $Id: ./muse/widgets/projectcreateimpl.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -33,18 +34,28 @@ class ProjectCreateImpl : public QDialog, Ui::ProjectCreate Q_OBJECT QString directoryPath; + QString overrideDirPath; + QString overrideTemplDirPath; + QString projDirPath; + public: explicit ProjectCreateImpl(QWidget *parent = 0); - QString getProjectPath(); - QString getSongInfo(); + QString getProjectPath() const; + QString getSongInfo() const; + //bool getProjectIsTemplate() const; + //QString getTemplatePath() const; signals: -public slots: +protected slots: + void updateProjectName(); void updateDirectoryPath(); void selectDirectory(); void ok(); - + void createProjFolderChanged(); + void browseProjDir(); + void templateButtonChanged(); + void restorePath(); }; } // namespace MusEGui diff --git a/muse2/muse/widgets/routepopup.cpp b/muse2/muse/widgets/routepopup.cpp index 80ae98bd..0f1f8264 100644 --- a/muse2/muse/widgets/routepopup.cpp +++ b/muse2/muse/widgets/routepopup.cpp @@ -43,10 +43,10 @@ namespace MusEGui { int RoutePopupMenu::addMenuItem(MusECore::AudioTrack* track, MusECore::Track* route_track, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { // totalInChannels is only used by syntis. - int toch = ((MusECore::AudioTrack*)track)->totalOutChannels(); + //int toch = ((MusECore::AudioTrack*)track)->totalOutChannels(); // If track channels = 1, it must be a mono synth. And synti channels cannot be changed by user. - if(track->channels() == 1) - toch = 1; + //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)) @@ -95,6 +95,13 @@ int RoutePopupMenu::addMenuItem(MusECore::AudioTrack* track, MusECore::Track* ro } } } + + if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. + { + if( (isOutput ? track : route_track)->isCircularRoute(isOutput ? route_track : track) ) + act->setEnabled(false); + } + return ++id; } @@ -259,6 +266,13 @@ int RoutePopupMenu::addSyntiPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id } } } + + if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. + { + if( (isOutput ? t : track)->isCircularRoute(isOutput ? track : t) ) + act->setEnabled(false); + } + ++id; } @@ -520,6 +534,13 @@ int RoutePopupMenu::nonSyntiTrackAddSyntis(MusECore::AudioTrack* t, PopupMenu* l } } } + + if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. + { + if( (isOutput ? t : track)->isCircularRoute(isOutput ? track : t) ) + act->setEnabled(false); + } + ++id; } @@ -573,6 +594,13 @@ int RoutePopupMenu::nonSyntiTrackAddSyntis(MusECore::AudioTrack* t, PopupMenu* l } } } + + if(!act->isChecked()) // If circular route exists, allow user to break it, otherwise forbidden. + { + if( (isOutput ? t : track)->isCircularRoute(isOutput ? track : t) ) + act->setEnabled(false); + } + ++id; } } @@ -1062,9 +1090,8 @@ void RoutePopupMenu::prepare() for( ; pi < MIDI_PORTS; ++pi) { MusECore::MidiDevice* md = MusEGlobal::midiPorts[pi].device(); - //if(md && !md->isSynti() && (md->rwFlags() & 2)) - //if(md && (md->rwFlags() & 2)) // p4.0.27 - if(md && (md->rwFlags() & 2 || md->isSynti()) ) // p4.0.27 + if(md && !md->isSynti() && (md->rwFlags() & 2)) + //if(md && (md->rwFlags() & 2 || md->isSynti()) ) // p4.0.27 Reverted p4.0.35 break; } if(pi == MIDI_PORTS) @@ -1091,12 +1118,9 @@ void RoutePopupMenu::prepare() // continue; // Do not list synth devices! - ///if(md && md->isSynti()) - /// continue; - ///if(md && !(md->rwFlags() & 2)) - /// continue; - // p4.0.27 Go ahead. Synths esp MESS send out stuff. - if( md && !(md->rwFlags() & 2) && !md->isSynti() ) + if( md && (!(md->rwFlags() & 2) || md->isSynti()) ) + // p4.0.27 Go ahead. Synths esp MESS send out stuff. Reverted p4.0.35 + //if( md && !(md->rwFlags() & 2) && !md->isSynti() ) continue; //printf("MusE::prepareRoutingPopupMenu adding submenu portnum:%d\n", i); diff --git a/muse2/muse/widgets/synthconfigbase.ui b/muse2/muse/widgets/synthconfigbase.ui index 97f0beaa..500241a8 100644 --- a/muse2/muse/widgets/synthconfigbase.ui +++ b/muse2/muse/widgets/synthconfigbase.ui @@ -6,12 +6,12 @@ <rect> <x>0</x> <y>0</y> - <width>630</width> + <width>810</width> <height>492</height> </rect> </property> <property name="windowTitle"> - <string>MusE: Synth Configuration</string> + <string>Midi Port and Soft Synth Configuration</string> </property> <layout class="QGridLayout"> <item row="1" column="1"> @@ -32,6 +32,11 @@ </column> <column> <property name="text"> + <string>Type</string> + </property> + </column> + <column> + <property name="text"> <string>Midi Port</string> </property> </column> @@ -137,6 +142,11 @@ </column> <column> <property name="text"> + <string>Type</string> + </property> + </column> + <column> + <property name="text"> <string>Inst</string> </property> </column> diff --git a/muse2/muse/widgets/utils.cpp b/muse2/muse/widgets/utils.cpp index 708bef07..1641b267 100644 --- a/muse2/muse/widgets/utils.cpp +++ b/muse2/muse/widgets/utils.cpp @@ -528,7 +528,8 @@ int get_paste_len() if (p->endTick() > end_tick) end_tick=p->endTick(); - + + unchainClone(p); delete p; } } diff --git a/muse2/muse/widgets/visibletracks.cpp b/muse2/muse/widgets/visibletracks.cpp index f8ce06bf..4976ecf9 100644 --- a/muse2/muse/widgets/visibletracks.cpp +++ b/muse2/muse/widgets/visibletracks.cpp @@ -29,6 +29,7 @@ #include "action.h" #include "track.h" #include "synth.h" +#include "app.h" namespace MusEGui { @@ -130,6 +131,7 @@ void VisibleTracks::visibilityChanged(QAction* action) default: break; } + MusEGlobal::muse->changeConfig(true); // save settings emit visibilityChanged(); } diff --git a/muse2/muse/widgets/visibletracks.h b/muse2/muse/widgets/visibletracks.h index d56c9ce7..bf49c068 100644 --- a/muse2/muse/widgets/visibletracks.h +++ b/muse2/muse/widgets/visibletracks.h @@ -52,13 +52,14 @@ class VisibleTracks : public QToolBar { private slots: void visibilityChanged(QAction* action); + public slots: + void updateVisibleTracksButtons(); signals: void visibilityChanged(); public: VisibleTracks(QWidget* /*parent*/, const char* name = 0); // Needs a parent ! - void updateVisibleTracksButtons(); ~VisibleTracks(); }; diff --git a/muse2/muse/xml.cpp b/muse2/muse/xml.cpp index 139437c1..45bed368 100644 --- a/muse2/muse/xml.cpp +++ b/muse2/muse/xml.cpp @@ -4,6 +4,7 @@ // $Id: xml.cpp,v 1.17.2.6 2009/12/07 20:48:45 spamatica Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -24,6 +25,7 @@ #include <stdio.h> #include <stdarg.h> +#include <QByteArray> #include <QString> #include <QColor> #include <QWidget> @@ -112,9 +114,12 @@ void Xml::nextc() void Xml::token(int cc) { - char buffer[512]; + //char buffer[512]; + QByteArray buffer; + int i = 0; - for (; i < 511;) { + //for (; i < 511;) { + for (; i < 9999999;) { // Stop at a reasonably large amount 10 million. if (c == ' ' || c == '\t' || c == cc || c == '\n' || c == EOF) break; buffer[i++] = c; @@ -131,12 +136,16 @@ void Xml::token(int cc) void Xml::stoken() { - char buffer[1024*4]; + //char buffer[1024*4]; + QByteArray buffer; + int i = 0; buffer[i] = c; ++i; next(); - for (;i < 1024*4-1;) { + + //for (;i < 1024*4-1;) { + for (;i < 10000000*4-1;) { // Stop at a reasonably large amount 10 million. if (c == '"') { buffer[i++] = c; next(); @@ -207,8 +216,10 @@ QString Xml::strip(const QString& s) Xml::Token Xml::parse() { - char buffer[1024*1024]; // increase buffer -rj - char* p; + //char buffer[1024*1024]; // increase buffer -rj + //char* p; + QByteArray buffer; + int idx = 0; again: bool endFlag = false; @@ -262,15 +273,23 @@ Xml::Token Xml::parse() } if (c == '?') { next(); - p = buffer; + //p = buffer; + //p = buffer.data(); + idx = 0; for (;;) { if (c == '?' || c == EOF || c == '>') break; - *p++ = c; + + //*p++ = c; + buffer[idx++] = c; + // TODO: check overflow next(); } - *p = 0; + + //*p = 0; + buffer[idx] = 0; + _s1 = QString(buffer); if (c == EOF) { fprintf(stderr, "XML: unexpected EOF\n"); @@ -298,15 +317,23 @@ Xml::Token Xml::parse() } goto again; } - p = buffer; + //p = buffer; + //p = buffer.data(); + idx = 0; for (;;) { if (c == '/' || c == ' ' || c == '\t' || c == '>' || c == '\n' || c == EOF) break; // TODO: check overflow - *p++ = c; + + //*p++ = c; + buffer[idx++] = c; + next(); } - *p = 0; + + //*p = 0; + buffer[idx] = 0; + _s1 = QString(buffer); // skip white space: while (c == ' ' || c == '\t' || c == '\n') @@ -355,26 +382,43 @@ Xml::Token Xml::parse() fprintf(stderr, "XML: level = 0\n"); goto error; } - p = buffer; + //p = buffer; + //p = buffer.data(); + idx = 0; for (;;) { if (c == EOF || c == '<') break; if (c == '&') { next(); if (c == '<') { // be tolerant with old muse files - *p++ = '&'; + + //*p++ = '&'; + buffer[idx++] = '&'; + continue; } - char name[32]; - char* dp = name; - *dp++ = c; - for (; dp-name < 31;) { + + //char name[32]; + //char* dp = name; + QByteArray name; + int name_idx = 0; + + //*dp++ = c; + name[name_idx++] = c; + + //for (; dp-name < 31;) { + for (; name_idx < 9999999;) { // Stop at a reasonably large amount 10 million. next(); if (c == ';') break; - *dp++ = c; + + //*dp++ = c; + name[name_idx++] = c; } - *dp = 0; + + //*dp = 0; + name[name_idx] = 0; + if (strcmp(name, "lt") == 0) c = '<'; else if (strcmp(name, "gt") == 0) @@ -388,10 +432,16 @@ Xml::Token Xml::parse() else c = '?'; } - *p++ = c; + + //*p++ = c; + buffer[idx++] = c; + next(); } - *p = 0; + + //*p = 0; + buffer[idx] = 0; + _s1 = QString(buffer); if (c == '<') |