From 2e9013a5b29d6e1011c6f2d0b02dc7aa28ddabe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Mon, 6 Nov 2017 19:28:45 +0100 Subject: [PATCH] qtractor: rework patches to what was submitted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Müller --- ...give-tracks-more-informational-names.patch | 330 --- ...ts-and-MIDI-manager-list-thread-safe.patch | 248 ++ ...inListDocument-to-save-load-plugin-l.patch | 164 ++ ...ackCommand-extend-to-accept-qtractor.patch | 167 ++ .../0008-Optionally-enhance-MIDI-import.patch | 2083 +++++++++++++++++ .../qtractor/qtractor_0.8.4.bb | 7 +- 6 files changed, 2667 insertions(+), 332 deletions(-) delete mode 100644 recipes-misc/recipes-multimedia/qtractor/files/0005-MIDI-import-give-tracks-more-informational-names.patch create mode 100644 recipes-misc/recipes-multimedia/qtractor/files/0005-Make-plugin-lists-and-MIDI-manager-list-thread-safe.patch create mode 100644 recipes-misc/recipes-multimedia/qtractor/files/0006-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch create mode 100644 recipes-misc/recipes-multimedia/qtractor/files/0007-qtractorImportTrackCommand-extend-to-accept-qtractor.patch create mode 100644 recipes-misc/recipes-multimedia/qtractor/files/0008-Optionally-enhance-MIDI-import.patch diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0005-MIDI-import-give-tracks-more-informational-names.patch b/recipes-misc/recipes-multimedia/qtractor/files/0005-MIDI-import-give-tracks-more-informational-names.patch deleted file mode 100644 index a9ff52af..00000000 --- a/recipes-misc/recipes-multimedia/qtractor/files/0005-MIDI-import-give-tracks-more-informational-names.patch +++ /dev/null @@ -1,330 +0,0 @@ -From fff172e17a9b718403948fac449c877cdeb046b8 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andreas=20M=C3=BCller?= -Date: Wed, 18 Oct 2017 23:39:57 +0200 -Subject: [PATCH] MIDI import: give tracks more informational names -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Name is created by the following sequence - if name is not empty stop: - -* If supplied, set track name found in MIDI file -* Try General MIDI names -* Set name of clip imported (usually shortened MIDI file name) - -Upstream-Status: Submitted [1] - -https://github.com/rncbc/qtractor/pull/86 - -Signed-off-by: Andreas Müller ---- - src/qtractorMainForm.cpp | 2 +- - src/qtractorMidiClip.cpp | 11 +++ - src/qtractorMidiClip.h | 6 ++ - src/qtractorTracks.cpp | 181 +++++++++++++++++++++++++++++++++++++++++++++-- - src/qtractorTracks.h | 6 +- - 5 files changed, 200 insertions(+), 6 deletions(-) - -diff --git a/src/qtractorMainForm.cpp b/src/qtractorMainForm.cpp -index 0fbb649..b3c0f76 100644 ---- a/src/qtractorMainForm.cpp -+++ b/src/qtractorMainForm.cpp -@@ -3787,7 +3787,7 @@ void qtractorMainForm::trackImportMidi (void) - const unsigned long iClipStart = m_pSession->editHead(); - qtractorTrack *pTrack = m_pTracks->currentTrack(); - m_pTracks->addMidiTracks( -- m_pFiles->midiListView()->openFileNames(), iClipStart, pTrack); -+ m_pFiles->midiListView()->openFileNames(), iClipStart, pTrack, true); - m_pTracks->trackView()->ensureVisibleFrame(iClipStart); - } - } -diff --git a/src/qtractorMidiClip.cpp b/src/qtractorMidiClip.cpp -index 5928846..8af1d73 100644 ---- a/src/qtractorMidiClip.cpp -+++ b/src/qtractorMidiClip.cpp -@@ -346,6 +346,7 @@ bool qtractorMidiClip::openMidiFile ( - // Set local properties... - setFilename(sFilename); - setDirty(false); -+ m_sTrackNameRead.clear(); - - // Register file path... - pSession->files()->addClipItem(qtractorFileList::Midi, this, bWrite); -@@ -476,6 +477,10 @@ bool qtractorMidiClip::openMidiFile ( - // We should have events, otherwise this clip is of no use... - //if (m_pSeq->events().count() < 1) - // return false; -+ -+ // If MIDI file sets track name: keep it -+ m_sTrackNameRead = pSeq->name(); -+ - // And initial clip name... - pSeq->setName(shortClipName(QFileInfo(m_pFile->filename()).baseName())); - } -@@ -514,6 +519,12 @@ bool qtractorMidiClip::openMidiFile ( - } - - -+QString qtractorMidiClip::getTrackName (void) -+{ -+ return m_sTrackNameRead; -+} -+ -+ - // Private cleanup. - void qtractorMidiClip::closeMidiFile (void) - { -diff --git a/src/qtractorMidiClip.h b/src/qtractorMidiClip.h -index f47528f..f45d1cc 100644 ---- a/src/qtractorMidiClip.h -+++ b/src/qtractorMidiClip.h -@@ -60,6 +60,9 @@ public: - bool openMidiFile(const QString& sFilename, int iTrackChannel = 0, - int iMode = qtractorMidiFile::Read); - -+ // Track name a read MIDI file has set -+ QString getTrackName(); -+ - // MIDI file properties accessors. - void setTrackChannel(unsigned short iTrackChannel) - { m_iTrackChannel = iTrackChannel; } -@@ -306,6 +309,9 @@ private: - QPoint m_posEditor; - QSize m_sizeEditor; - -+ // Track name read from MIDI -+ QString m_sTrackNameRead; -+ - // Default MIDI file format (for capture/record) - static unsigned short g_iDefaultFormat; - }; -diff --git a/src/qtractorTracks.cpp b/src/qtractorTracks.cpp -index 9b9c4c4..7cac92e 100644 ---- a/src/qtractorTracks.cpp -+++ b/src/qtractorTracks.cpp -@@ -2803,7 +2803,7 @@ bool qtractorTracks::addAudioTracks ( const QStringList& files, - - // Import MIDI files into new tracks... - bool qtractorTracks::addMidiTracks ( const QStringList& files, -- unsigned long iClipStart, qtractorTrack *pAfterTrack ) -+ unsigned long iClipStart, qtractorTrack *pAfterTrack, bool bEnhancedTrackNames ) - { - // Have we some? - if (files.isEmpty()) -@@ -2877,10 +2877,27 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, - } - // Time to check whether there is actual data on track... - if (pMidiClip->clipLength() > 0) { -+ int iChannel = pMidiClip->channel(); -+ // create track name -+ QString sTrackName; -+ if (bEnhancedTrackNames) { -+ // try track name from MIDI import -+ sTrackName = pMidiClip->getTrackName(); -+ // no track name set try GM names -+ if (sTrackName.isEmpty()) { -+ QString sGMName = getGMName(iChannel, pMidiClip->prog()); -+ if (!sGMName.isEmpty()) -+ sTrackName = sGMName; -+ } -+ } -+ // default: shortened clip name -+ if (sTrackName.isEmpty()) -+ sTrackName = pSession->uniqueTrackName(pMidiClip->clipName()); -+ - // Add the new track to composite command... -- pTrack->setTrackName( -- pSession->uniqueTrackName(pMidiClip->clipName())); -- pTrack->setMidiChannel(pMidiClip->channel()); -+ pTrack->setTrackName(sTrackName); -+ pTrack->setMidiChannel(iChannel); -+ - pImportTrackCommand->addTrack(pTrack); - ++iUpdate; - // Don't forget to add this one to local repository. -@@ -2984,6 +3001,162 @@ bool qtractorTracks::addMidiTrackChannel ( const QString& sPath, - } - - -+// MIDI specific: Get a GM name for channel/prog -+QString qtractorTracks::getGMName( int iChannel, int iProg ) -+{ -+ QString sName; -+ // static GM prog names -+ // no need to use numbers for multiple insstuments with same name -+ static const QStringList instrumentNamesGM = ( -+ QStringList() -+ // 0-31 -+ << QObject::tr("Acoustic Grand Piano") -+ << QObject::tr("Bright Acoustic Piano") -+ << QObject::tr("Electric Grand Piano") -+ << QObject::tr("Honky-tonk Piano") -+ << QObject::tr("Electric Piano") -+ << QObject::tr("Electric Piano") -+ << QObject::tr("Harpsichord") -+ << QObject::tr("Clavi") -+ << QObject::tr("Celesta") -+ << QObject::tr("Glockenspiel") -+ << QObject::tr("Music Box") -+ << QObject::tr("Vibraphone") -+ << QObject::tr("Marimba") -+ << QObject::tr("Xylophone") -+ << QObject::tr("Tubular Bells") -+ << QObject::tr("Dulcimer") -+ << QObject::tr("Organ") -+ << QObject::tr("Organ") -+ << QObject::tr("Rock Organ") -+ << QObject::tr("Church Organ") -+ << QObject::tr("Reed Organ") -+ << QObject::tr("Accordion") -+ << QObject::tr("Harmonica") -+ << QObject::tr("Tango Accordion") -+ << QObject::tr("Nylon Guitar") -+ << QObject::tr("Steel Guitar") -+ << QObject::tr("Jazz E-Guitar") -+ << QObject::tr("Clean E-Guitar") -+ << QObject::tr("Muted E-Guitar") -+ << QObject::tr("Overdrive Guitar") -+ << QObject::tr("Distorted Guitar") -+ << QObject::tr("Harmonic Guitar") -+ // 32-63 -+ << QObject::tr("Acoustic Bass") -+ << QObject::tr("Fingered E-Bass") -+ << QObject::tr("Picked E-Bass") -+ << QObject::tr("Fretless Bass") -+ << QObject::tr("Slap Bass") -+ << QObject::tr("Slap Bass") -+ << QObject::tr("Synth Bass") -+ << QObject::tr("Synth Bass") -+ << QObject::tr("Violin") -+ << QObject::tr("Viola") -+ << QObject::tr("Cello") -+ << QObject::tr("Contrabass") -+ << QObject::tr("Tremolo") -+ << QObject::tr("Pizicato") -+ << QObject::tr("Harp") -+ << QObject::tr("Timpani") -+ << QObject::tr("Strings") -+ << QObject::tr("Strings") -+ << QObject::tr("Synth Strings") -+ << QObject::tr("Synth Strings") -+ << QObject::tr("Choir Aah") -+ << QObject::tr("Voice Ooh") -+ << QObject::tr("Synth Voice") -+ << QObject::tr("Hit Orchestra") -+ << QObject::tr("Trumpet") -+ << QObject::tr("Trombone") -+ << QObject::tr("Tuba") -+ << QObject::tr("Trumpet Muted") -+ << QObject::tr("French Horn") -+ << QObject::tr("Brass") -+ << QObject::tr("Synth Brass") -+ << QObject::tr("SynthBrass") -+ // 64-95 -+ << QObject::tr("Soprano Sax") -+ << QObject::tr("Alto Sax") -+ << QObject::tr("Tenor Sax") -+ << QObject::tr("Baritone Sax") -+ << QObject::tr("Oboe") -+ << QObject::tr("Horn") -+ << QObject::tr("Bassoon") -+ << QObject::tr("Clarinet") -+ << QObject::tr("Piccolo") -+ << QObject::tr("Flute") -+ << QObject::tr("Recorder") -+ << QObject::tr("Pan Flute") -+ << QObject::tr("Smashing Bottle") -+ << QObject::tr("Shakuhachi") -+ << QObject::tr("Whistle") -+ << QObject::tr("Ocarina") -+ << QObject::tr("Square Lead") -+ << QObject::tr("Saw Lead") -+ << QObject::tr("Callilope Lead") -+ << QObject::tr("Chiffer Lead") -+ << QObject::tr("Charang Lead") -+ << QObject::tr("Voice Lead") -+ << QObject::tr("5th Lead") -+ << QObject::tr("Lead And Bass") -+ << QObject::tr("New Age Pad") -+ << QObject::tr("Warm Pad") -+ << QObject::tr("Polysynth Pad") -+ << QObject::tr("Choir Pad") -+ << QObject::tr("Bowed Pad") -+ << QObject::tr("Metallic Pad") -+ << QObject::tr("Halo Pad") -+ << QObject::tr("Sweep Pad") -+ // 96-127 -+ << QObject::tr("Rain") -+ << QObject::tr("Soundtrack") -+ << QObject::tr("Crystal") -+ << QObject::tr("Atmosphere") -+ << QObject::tr("Brightness") -+ << QObject::tr("Goblins") -+ << QObject::tr("Echoes") -+ << QObject::tr("Sci-Fi") -+ << QObject::tr("Sitar") -+ << QObject::tr("Banjo") -+ << QObject::tr("Shamisen") -+ << QObject::tr("Koto") -+ << QObject::tr("Kalimba") -+ << QObject::tr("Bagpipe") -+ << QObject::tr("Fiddle") -+ << QObject::tr("Shanai") -+ << QObject::tr("Tinkle Bell") -+ << QObject::tr("Agogo") -+ << QObject::tr("Steel Drums") -+ << QObject::tr("Wood Block") -+ << QObject::tr("Taiko Drum") -+ << QObject::tr("Melodic Drum") -+ << QObject::tr("Synth Drum") -+ << QObject::tr("Reverse Cymbal") -+ << QObject::tr("Fretless Noise") -+ << QObject::tr("Breath Noise") -+ << QObject::tr("Seashore") -+ << QObject::tr("Birds") -+ << QObject::tr("Oldschool Telephone") -+ << QObject::tr("Helicopter") -+ << QObject::tr("Applause") -+ << QObject::tr("Gun Shot") -+ ); -+ -+ int iNameCount = instrumentNamesGM.size(); -+ Q_ASSERT(iNameCount == 128); -+ -+ // Drums (channel 10 - we keep data zero based) -+ if (iChannel == 9) -+ sName = QObject::tr("Drums"); -+ // Instruments program name -+ else if (iProg >= 0 && iProg < iNameCount) -+ sName = instrumentNamesGM[iProg]; -+ -+ return sName; -+} -+ -+ - // Track-list active maintenance update. - void qtractorTracks::updateTrack ( qtractorTrack *pTrack ) - { -diff --git a/src/qtractorTracks.h b/src/qtractorTracks.h -index 2d9fa48..c45b12a 100644 ---- a/src/qtractorTracks.h -+++ b/src/qtractorTracks.h -@@ -71,10 +71,14 @@ public: - bool addAudioTracks(const QStringList& files, - unsigned long iClipStart, qtractorTrack *pAfterTrack = NULL); - bool addMidiTracks(const QStringList& files, -- unsigned long iClipStart, qtractorTrack *pAfterTrack = NULL); -+ unsigned long iClipStart, qtractorTrack *pAfterTrack = NULL, -+ bool bEnhancedTrackNames = false); - bool addMidiTrackChannel(const QString& sPath, int iTrackChannel, - unsigned long iClipStart, qtractorTrack *pAfterTrack = NULL); - -+ // MIDI specific: GM instrument names -+ static QString getGMName(int iChannel, int iProg); -+ - // Track-list active maintenance update. - void updateTrack(qtractorTrack *pTrack = NULL); - --- -2.9.5 - diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0005-Make-plugin-lists-and-MIDI-manager-list-thread-safe.patch b/recipes-misc/recipes-multimedia/qtractor/files/0005-Make-plugin-lists-and-MIDI-manager-list-thread-safe.patch new file mode 100644 index 00000000..1a44fa4a --- /dev/null +++ b/recipes-misc/recipes-multimedia/qtractor/files/0005-Make-plugin-lists-and-MIDI-manager-list-thread-safe.patch @@ -0,0 +1,248 @@ +From dad78145c4c427d44ad63caaa15966e8fe5693bf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andreas=20M=C3=BCller?= +Date: Sat, 4 Nov 2017 11:59:47 +0100 +Subject: [PATCH 1/4] Make plugin-lists and MIDI-manager-list thread-safe +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +During debugging (PC) and testing (RaspberryPi) the MIDI import dialog, qtractor +crashed several times with segfaults due to inconsistent list state (the MIDI- +manager case was mentioned in [1]). Since adding plugins to MIDI import the +probabilty of these crashes increases because of performing 'dangerous' +operations multiple times. + +To avoid this, lock/unlock guards were added under the following consideration: +Keep the locking time as short as possible to reduce the probability of +blocking audio-engine. For the implemetation qtractorSession::lock got a +parameter 'bStabilize' set true by default. If set to false, no events are +performed to avoid plugin-list-view updates with inconsistent data. + +Worth to mention: With this modification no more crashes happend. + +[1] https://github.com/rncbc/qtractor/issues/91 + +Upstream-Status: Submitted [2] + +[2] https://github.com/rncbc/qtractor/pull/94 + +Signed-off-by: Andreas Müller +--- + src/qtractorPlugin.cpp | 40 +++++++++++++++++++++++++++++++++------- + src/qtractorPluginCommand.cpp | 16 ---------------- + src/qtractorSession.cpp | 14 +++++++++++--- + src/qtractorSession.h | 2 +- + 4 files changed, 45 insertions(+), 27 deletions(-) + +diff --git a/src/qtractorPlugin.cpp b/src/qtractorPlugin.cpp +index e547381..651f603 100644 +--- a/src/qtractorPlugin.cpp ++++ b/src/qtractorPlugin.cpp +@@ -1620,10 +1620,17 @@ void qtractorPluginList::insertPlugin ( + // We'll get prepared before plugging it in... + pPlugin->setChannels(m_iChannels); + ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (pSession == NULL) ++ return; ++ ++ // Do not perform events while locking - list views are not yet updated. ++ pSession->lock(false); + if (pNextPlugin) + insertBefore(pPlugin, pNextPlugin); + else + append(pPlugin); ++ pSession->unlock(); + + // Now update each observer list-view... + QListIterator iter(m_views); +@@ -1654,13 +1661,15 @@ void qtractorPluginList::movePlugin ( + if (pPluginList == NULL) + return; + +- // Remove and insert back again... ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (pSession == NULL) ++ return; ++ ++ // Do not perform events while locking - list views are not yet updated. ++ pSession->lock(false); ++ // Remove it -> it is no more processed by worker thread. + pPluginList->unlink(pPlugin); +- if (pNextPlugin) { +- insertBefore(pPlugin, pNextPlugin); +- } else { +- append(pPlugin); +- } ++ pSession->unlock(); + + // DANGER: Gasp, we might be not the same... + if (pPluginList != this) { +@@ -1713,6 +1722,16 @@ void qtractorPluginList::movePlugin ( + pListView->setCurrentItem(pNextItem); + } + ++ // Views are updated so perform events during lock. ++ pSession->lock(); ++ // Insert back again -> processing continues. ++ if (pNextPlugin) { ++ insertBefore(pPlugin, pNextPlugin); ++ } else { ++ append(pPlugin); ++ } ++ pSession->unlock(); ++ + // update (both) lists for Auto-plugin-deactivation + autoDeactivatePlugins(m_bAutoDeactivated, true); + if (pPluginList != this) +@@ -1723,8 +1742,15 @@ void qtractorPluginList::movePlugin ( + // Remove-guarded plugin method. + void qtractorPluginList::removePlugin ( qtractorPlugin *pPlugin ) + { +- // Just unlink the plugin from the list... ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (pSession == NULL) ++ return; ++ ++ // Views are not touched here so perform events. ++ pSession->lock(); ++ // Remove it -> it is no more processed by worker thread. + unlink(pPlugin); ++ pSession->unlock(); + + if (pPlugin->isActivated()) + updateActivated(false); +diff --git a/src/qtractorPluginCommand.cpp b/src/qtractorPluginCommand.cpp +index 10bd717..c4321ca 100644 +--- a/src/qtractorPluginCommand.cpp ++++ b/src/qtractorPluginCommand.cpp +@@ -65,8 +65,6 @@ bool qtractorPluginCommand::addPlugins (void) + if (pSession == NULL) + return false; + +-// pSession->lock(); +- + // Add all listed plugins, in order... + QListIterator iter(m_plugins); + while (iter.hasNext()) { +@@ -78,8 +76,6 @@ bool qtractorPluginCommand::addPlugins (void) + // Avoid the disposal of the plugin reference(s). + setAutoDelete(false); + +-// pSession->unlock(); +- + return true; + } + +@@ -91,8 +87,6 @@ bool qtractorPluginCommand::removePlugins (void) + if (pSession == NULL) + return false; + +-// pSession->lock(); +- + // Unlink all listed plugins, in order... + QListIterator iter(m_plugins); + iter.toBack(); +@@ -105,8 +99,6 @@ bool qtractorPluginCommand::removePlugins (void) + // Allow the disposal of the plugin reference(s). + setAutoDelete(true); + +-// pSession->unlock(); +- + return true; + } + +@@ -284,8 +276,6 @@ bool qtractorInsertPluginCommand::redo (void) + if (pPluginList == NULL) + return false; + +-// pSession->lock(); +- + qtractorPlugin *pNextPlugin = pPlugin->next(); + + // Insert it... +@@ -297,8 +287,6 @@ bool qtractorInsertPluginCommand::redo (void) + // Whether to allow the disposal of the plugin reference. + setAutoDelete(false); + +-// pSession->unlock(); +- + return true; + } + +@@ -317,8 +305,6 @@ bool qtractorInsertPluginCommand::undo (void) + if (pPluginList == NULL) + return false; + +-// pSession->lock(); +- + qtractorPlugin *pNextPlugin = pPlugin->next(); + + // Insert it... +@@ -330,8 +316,6 @@ bool qtractorInsertPluginCommand::undo (void) + // Whether to allow the disposal of the plugin reference. + setAutoDelete(true); + +-// pSession->unlock(); +- + return true; + } + +diff --git a/src/qtractorSession.cpp b/src/qtractorSession.cpp +index bc901d6..19e72b3 100644 +--- a/src/qtractorSession.cpp ++++ b/src/qtractorSession.cpp +@@ -1250,13 +1250,17 @@ void qtractorSession::release (void) + } + + +-void qtractorSession::lock (void) ++void qtractorSession::lock (bool bStabilize) + { + // Wind up as pending lock... + if (ATOMIC_INC(&m_locks) == 1) { + // Get lost for a while... +- while (!acquire()) +- stabilize(); ++ while (!acquire()) { ++ if (bStabilize) ++ stabilize(); ++ else ++ QThread::yieldCurrentThread(); ++ } + } + } + +@@ -1901,12 +1905,16 @@ void qtractorSession::resetAllMidiControllers ( bool bForceImmediate ) + // MIDI manager list accessors. + void qtractorSession::addMidiManager ( qtractorMidiManager *pMidiManager ) + { ++ lock(); + m_midiManagers.append(pMidiManager); ++ unlock(); + } + + void qtractorSession::removeMidiManager ( qtractorMidiManager *pMidiManager ) + { ++ lock(); + m_midiManagers.remove(pMidiManager); ++ unlock(); + } + + +diff --git a/src/qtractorSession.h b/src/qtractorSession.h +index 151096e..821fa5b 100644 +--- a/src/qtractorSession.h ++++ b/src/qtractorSession.h +@@ -229,7 +229,7 @@ public: + // Session RT-safe pseudo-locking primitives. + bool acquire(); + void release(); +- void lock(); ++ void lock(bool bStabilize = false); + void unlock(); + + // Re-entrancy check. +-- +2.9.5 + diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0006-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch b/recipes-misc/recipes-multimedia/qtractor/files/0006-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch new file mode 100644 index 00000000..5ffffc97 --- /dev/null +++ b/recipes-misc/recipes-multimedia/qtractor/files/0006-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch @@ -0,0 +1,164 @@ +From b8ef1130bee18e5a28340c21941d67bd14949b2d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andreas=20M=C3=BCller?= +Date: Sat, 4 Nov 2017 20:41:50 +0100 +Subject: [PATCH 2/4] Add qtractorPluginListDocument to save/load plugin-lists + to XML +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For MIDI Import dialog it is necessary to save/load plugin-lists. The easiest +way to do is to reuse the code used when saving/loading sessions. To perform +save/load only on plugin-lists an own document class has to be created. + +Upstream-Status: Submitted [1] + +[1] https://github.com/rncbc/qtractor/pull/94 + +Signed-off-by: Andreas Müller +--- + src/qtractorPluginListDocument.cpp | 54 ++++++++++++++++++++++++++++++++++++++ + src/qtractorPluginListDocument.h | 49 ++++++++++++++++++++++++++++++++++ + src/src.pro | 2 ++ + 3 files changed, 105 insertions(+) + create mode 100644 src/qtractorPluginListDocument.cpp + create mode 100644 src/qtractorPluginListDocument.h + +diff --git a/src/qtractorPluginListDocument.cpp b/src/qtractorPluginListDocument.cpp +new file mode 100644 +index 0000000..4b895ed +--- /dev/null ++++ b/src/qtractorPluginListDocument.cpp +@@ -0,0 +1,54 @@ ++// qtractorPluginListDocument.cpp ++// ++/**************************************************************************** ++ Copyright (C) 2005-2017, rncbc aka Rui Nuno Capela. All rights reserved. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version 2 ++ of the License, or (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ ++*****************************************************************************/ ++ ++#include "qtractorPluginListDocument.h" ++#include "qtractorPlugin.h" ++ ++ ++// Constructor. ++qtractorPluginListDocument::qtractorPluginListDocument( ++ QDomDocument *pDocument, qtractorPluginList *pPluginList) ++ : qtractorDocument(pDocument, "qtractorPluginList"), ++ m_pPluginList(pPluginList) ++ ++{ ++ ++} ++ ++ ++// Destructor. ++qtractorPluginListDocument::~qtractorPluginListDocument() ++{ ++} ++ ++ ++bool qtractorPluginListDocument::loadElement(QDomElement *pElement) ++{ ++ return m_pPluginList->loadElement(this, pElement); ++} ++ ++ ++bool qtractorPluginListDocument::saveElement(QDomElement *pElement) ++{ ++ return m_pPluginList->saveElement(this, pElement); ++} ++ ++// end of qtractorPluginListDocument.cpp +diff --git a/src/qtractorPluginListDocument.h b/src/qtractorPluginListDocument.h +new file mode 100644 +index 0000000..57464d7 +--- /dev/null ++++ b/src/qtractorPluginListDocument.h +@@ -0,0 +1,49 @@ ++// qtractorPluginListDocument.h ++// ++/**************************************************************************** ++ Copyright (C) 2005-2017, rncbc aka Rui Nuno Capela. All rights reserved. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version 2 ++ of the License, or (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ ++*****************************************************************************/ ++ ++#ifndef __qtractorPluginListDocument_h ++#define __qtractorPluginListDocument_h ++ ++#include "qtractorDocument.h" ++ ++// Forward declarations. ++class qtractorPluginList; ++ ++class qtractorPluginListDocument : public qtractorDocument ++{ ++public: ++ ++ // Constructor. ++ qtractorPluginListDocument(QDomDocument *pDocument, qtractorPluginList *pPluginList); ++ // Destructor. ++ ~qtractorPluginListDocument(); ++ ++ // External storage element overrides. ++ virtual bool loadElement (QDomElement *pElement); ++ virtual bool saveElement (QDomElement *pElement); ++ ++private: ++ qtractorPluginList *m_pPluginList; ++}; ++ ++#endif // __qtractorPluginListDocument_h ++ ++// end of qtractorPluginListDocument.h +diff --git a/src/src.pro b/src/src.pro +index 5e43fa7..92c0bd8 100644 +--- a/src/src.pro ++++ b/src/src.pro +@@ -94,6 +94,7 @@ HEADERS += config.h \ + qtractorPlugin.h \ + qtractorPluginFactory.h \ + qtractorPluginCommand.h \ ++ qtractorPluginListDocument.h \ + qtractorPluginListView.h \ + qtractorPropertyCommand.h \ + qtractorRingBuffer.h \ +@@ -218,6 +219,7 @@ SOURCES += \ + qtractorPlugin.cpp \ + qtractorPluginFactory.cpp \ + qtractorPluginCommand.cpp \ ++ qtractorPluginListDocument.cpp \ + qtractorPluginListView.cpp \ + qtractorRubberBand.cpp \ + qtractorScrollView.cpp \ +-- +2.9.5 + diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0007-qtractorImportTrackCommand-extend-to-accept-qtractor.patch b/recipes-misc/recipes-multimedia/qtractor/files/0007-qtractorImportTrackCommand-extend-to-accept-qtractor.patch new file mode 100644 index 00000000..17210da8 --- /dev/null +++ b/recipes-misc/recipes-multimedia/qtractor/files/0007-qtractorImportTrackCommand-extend-to-accept-qtractor.patch @@ -0,0 +1,167 @@ +From e509eef8b5653796acb44bdd187db6574ed39da8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andreas=20M=C3=BCller?= +Date: Sun, 5 Nov 2017 00:58:28 +0100 +Subject: [PATCH 3/4] qtractorImportTrackCommand: extend to accept + qtractorEditTrackCommand +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This is neccessary to align tracks during import the way track modifications +are performed. + +* m_trackCommands is now a list of qtractorCommand. This is necessary because + qtractorEditTrackCommand is not inheriting qtractorTrackCommand. +* qtractorImportTrackCommand::addTrackEditCommand is added +* declaration of qtractorEditTrackCommand is moved before + qtractorImportTrackCommand to avoid useless forward declarations. This makes + the diff a bit human-unreadable. + +Upstream-Status: Submitted [1] + +[1] https://github.com/rncbc/qtractor/pull/94 + +Signed-off-by: Andreas Müller +--- + src/qtractorTrackCommand.cpp | 15 +++++++++---- + src/qtractorTrackCommand.h | 51 ++++++++++++++++++++++---------------------- + 2 files changed, 37 insertions(+), 29 deletions(-) + +diff --git a/src/qtractorTrackCommand.cpp b/src/qtractorTrackCommand.cpp +index b1a720e..959b566 100644 +--- a/src/qtractorTrackCommand.cpp ++++ b/src/qtractorTrackCommand.cpp +@@ -653,6 +653,13 @@ void qtractorImportTrackCommand::addTrack ( qtractorTrack *pTrack ) + m_pAfterTrack = pTrack; + } + ++void qtractorImportTrackCommand::addTrackEditCommand( ++ qtractorEditTrackCommand *pEditTrackCommand) ++{ ++ if (pEditTrackCommand) ++ m_trackCommands.append(pEditTrackCommand); ++} ++ + + // Track-import command methods. + bool qtractorImportTrackCommand::redo (void) +@@ -665,9 +672,9 @@ bool qtractorImportTrackCommand::redo (void) + } + ++m_iSaveCount; + +- QListIterator iter(m_trackCommands); ++ QListIterator iter(m_trackCommands); + while (iter.hasNext()) { +- qtractorAddTrackCommand *pTrackCommand = iter.next(); ++ qtractorCommand *pTrackCommand = iter.next(); + if (!pTrackCommand->redo()) + bResult = false; + } +@@ -679,10 +686,10 @@ bool qtractorImportTrackCommand::undo (void) + { + bool bResult = true; + +- QListIterator iter(m_trackCommands); ++ QListIterator iter(m_trackCommands); + iter.toBack(); + while (iter.hasPrevious()) { +- qtractorAddTrackCommand *pTrackCommand = iter.previous(); ++ qtractorCommand *pTrackCommand = iter.previous(); + if (!pTrackCommand->undo()) + bResult = false; + } +diff --git a/src/qtractorTrackCommand.h b/src/qtractorTrackCommand.h +index 0746fba..ab15218 100644 +--- a/src/qtractorTrackCommand.h ++++ b/src/qtractorTrackCommand.h +@@ -191,61 +191,62 @@ private: + + + //---------------------------------------------------------------------- +-// class qtractorInportTracksCommand - declaration. ++// class qtractorEditTrackCommand - declaration. + // + +-class qtractorImportTrackCommand : public qtractorCommand ++class qtractorEditTrackCommand ++ : public qtractorPropertyCommand + { + public: + + // Constructor. +- qtractorImportTrackCommand(qtractorTrack *pAfterTrack); +- +- // Destructor. +- ~qtractorImportTrackCommand(); +- +- // Add track to command list. +- void addTrack(qtractorTrack *pTrack); ++ qtractorEditTrackCommand(qtractorTrack *pTrack, ++ const qtractorTrack::Properties& props); + +- // Track-import command methods. ++ // Overridden track-edit command methods. + bool redo(); + bool undo(); + + private: + + // Instance variables. +- qtractorTrack *m_pAfterTrack; +- +- QList m_trackCommands; +- +- // Session properties backup stuff. +- qtractorSession::Properties m_sessionProps; +- qtractorPropertyCommand *m_pSaveCommand; +- int m_iSaveCount; ++ qtractorTrack *m_pTrack; + }; + + + //---------------------------------------------------------------------- +-// class qtractorEditTrackCommand - declaration. ++// class qtractorInportTracksCommand - declaration. + // + +-class qtractorEditTrackCommand +- : public qtractorPropertyCommand ++class qtractorImportTrackCommand : public qtractorCommand + { + public: + + // Constructor. +- qtractorEditTrackCommand(qtractorTrack *pTrack, +- const qtractorTrack::Properties& props); ++ qtractorImportTrackCommand(qtractorTrack *pAfterTrack); + +- // Overridden track-edit command methods. ++ // Destructor. ++ ~qtractorImportTrackCommand(); ++ ++ // Add track to command list. ++ void addTrack(qtractorTrack *pTrack); ++ // Add track edit command to command list. ++ void addTrackEditCommand(qtractorEditTrackCommand* pEditTrackCommand); ++ ++ // Track-import command methods. + bool redo(); + bool undo(); + + private: + + // Instance variables. +- qtractorTrack *m_pTrack; ++ qtractorTrack *m_pAfterTrack; ++ QList m_trackCommands; ++ ++ // Session properties backup stuff. ++ qtractorSession::Properties m_sessionProps; ++ qtractorPropertyCommand *m_pSaveCommand; ++ int m_iSaveCount; + }; + + +-- +2.9.5 + diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0008-Optionally-enhance-MIDI-import.patch b/recipes-misc/recipes-multimedia/qtractor/files/0008-Optionally-enhance-MIDI-import.patch new file mode 100644 index 00000000..2fb6db93 --- /dev/null +++ b/recipes-misc/recipes-multimedia/qtractor/files/0008-Optionally-enhance-MIDI-import.patch @@ -0,0 +1,2083 @@ +From b48b25af8979c40e58c263338446cd419db4016a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andreas=20M=C3=BCller?= +Date: Sun, 5 Nov 2017 01:14:49 +0100 +Subject: [PATCH 4/4] Optionally enhance MIDI import +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In the 'MIDI' section of settings, the following can be set to get a ready to +play MIDI import: + +* Plugins can be set. Each track imported gets the plugins with settings set in + dialog. +* Bank used for instruments and drums can be set. When using plugins using + soundfonts it is useful to set instrument bank to 0 and drum bank to 128. +* Names of created tracks can be aligned: + * MIDI filename: This is the same naming scheme as it was before this patch + * Track n: Tracks ar named 'Track n' where 'n' is an incrementing number. + * Patch name: The tracks are named from the patch names. This is my favorite + setting since it gives a quick overview what track plays which instrument - + especially on small monitors where large areas of patch section in track + header are usually hidden. + +Upstream-Status: Submitted [1] + +[1] https://github.com/rncbc/qtractor/pull/94 + +Signed-off-by: Andreas Müller +--- + src/qtractorMainForm.cpp | 14 +- + src/qtractorMidiImportExtender.cpp | 372 ++++++++++++++++++++++++ + src/qtractorMidiImportExtender.h | 102 +++++++ + src/qtractorMidiImportForm.cpp | 568 +++++++++++++++++++++++++++++++++++++ + src/qtractorMidiImportForm.h | 119 ++++++++ + src/qtractorMidiImportForm.ui | 396 ++++++++++++++++++++++++++ + src/qtractorOptions.cpp | 16 +- + src/qtractorOptions.h | 9 + + src/qtractorOptionsForm.cpp | 35 +++ + src/qtractorOptionsForm.h | 5 + + src/qtractorOptionsForm.ui | 21 +- + src/qtractorSession.cpp | 2 + + src/qtractorTracks.cpp | 47 ++- + src/qtractorTracks.h | 4 +- + src/src.pro | 5 + + 15 files changed, 1702 insertions(+), 13 deletions(-) + create mode 100644 src/qtractorMidiImportExtender.cpp + create mode 100644 src/qtractorMidiImportExtender.h + create mode 100644 src/qtractorMidiImportForm.cpp + create mode 100644 src/qtractorMidiImportForm.h + create mode 100644 src/qtractorMidiImportForm.ui + +diff --git a/src/qtractorMainForm.cpp b/src/qtractorMainForm.cpp +index 0fbb649..d2d5aa6 100644 +--- a/src/qtractorMainForm.cpp ++++ b/src/qtractorMainForm.cpp +@@ -75,6 +75,8 @@ + #include "qtractorMidiEditorForm.h" + #include "qtractorMidiEditor.h" + ++#include "qtractorMidiImportExtender.h" ++ + #include "qtractorTrackCommand.h" + #include "qtractorCurveCommand.h" + +@@ -3786,9 +3788,15 @@ void qtractorMainForm::trackImportMidi (void) + if (m_pTracks) { + const unsigned long iClipStart = m_pSession->editHead(); + qtractorTrack *pTrack = m_pTracks->currentTrack(); +- m_pTracks->addMidiTracks( +- m_pFiles->midiListView()->openFileNames(), iClipStart, pTrack); +- m_pTracks->trackView()->ensureVisibleFrame(iClipStart); ++ ++ QStringList files; ++ files = m_pFiles->midiListView()->openFileNames(); ++ if (!files.isEmpty()) { ++ qtractorMidiImportExtender midiImportExtenter; ++ m_pTracks->addMidiTracks( ++ files, iClipStart, pTrack, &midiImportExtenter); ++ m_pTracks->trackView()->ensureVisibleFrame(iClipStart); ++ } + } + } + +diff --git a/src/qtractorMidiImportExtender.cpp b/src/qtractorMidiImportExtender.cpp +new file mode 100644 +index 0000000..875f94c +--- /dev/null ++++ b/src/qtractorMidiImportExtender.cpp +@@ -0,0 +1,372 @@ ++// qtractorMidiImportExtender.cpp ++// ++/**************************************************************************** ++ Copyright (C) 2005-2017, rncbc aka Rui Nuno Capela. All rights reserved. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version 2 ++ of the License, or (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ ++*****************************************************************************/ ++ ++#include "qtractorMidiImportExtender.h" ++#include "qtractorSession.h" ++#include "qtractorPlugin.h" ++#include "qtractorOptions.h" ++#include "qtractorPluginListDocument.h" ++#include "qtractorAudioEngine.h" ++#include "qtractorMidiEngine.h" ++#include "qtractorMidiManager.h" ++#include "qtractorTrackCommand.h" ++#include "QDomDocument" ++ ++// It is not a good idea to delete plugins within a session. To avoid ++// deletion, the plugins are kept in static m_pPluginList. As soon ++// as an object of qtractorMidiImportExtender is created, a plugin-list ++// is created to. It is either deleted by: ++// * closing session which calls qtractorMidiImportExtender::clearPluginList ++// * on import / first track: After moving plugins to target track, plugin ++// list is empty and can be deleted ++qtractorPluginList *qtractorMidiImportExtender::m_pPluginList = NULL; ++ ++// Constructor. ++qtractorMidiImportExtender::qtractorMidiImportExtender() : ++ m_pPluginDomDocument(NULL), m_pPluginListDocument(NULL), ++ m_iTrackNumber(0) ++{ ++ // Get global session object ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (!pSession) ++ return; ++ // Get global options object ++ qtractorOptions *pOptions = qtractorOptions::getInstance(); ++ if (!pOptions) ++ return; ++ ++ // Create and fill plugin-lists if when called first time in session ++ if (!m_pPluginList) { ++ m_pPluginList = new qtractorPluginList(0, 0); ++ ++ // Restore plugin-list from persistant options. ++ if (!pOptions->sMidiImportPlugins.isEmpty()) { ++ QDomDocument domDocument("qtractorMidiImport"); ++ if (domDocument.setContent(pOptions->sMidiImportPlugins)) { ++ qtractorPluginListDocument pluginListDocument(&domDocument, m_pPluginList); ++ // Get root element and check for proper taq name. ++ QDomElement elem = domDocument.documentElement(); ++ if (elem.tagName() == "PluginList") ++ m_pPluginList->loadElement(&pluginListDocument, &elem); ++ } ++ } ++ // Output bus gets to be the first available output bus (master). ++ qtractorAudioBus *pAudioBus = NULL; ++ qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); ++ for (qtractorBus *pBus = (pAudioEngine->buses()).first(); ++ pBus; pBus = pBus->next()) { ++ if (pBus->busMode() & qtractorBus::Output) { ++ pAudioBus = static_cast (pBus); ++ break; ++ } ++ } ++ ++ // Plugin-List needs one or more channels - just to allow adding plugins. ++ if (pAudioBus) ++ m_pPluginList->setChannels(pAudioBus->channels(), ++ qtractorPluginList::Midi); ++ else ++ m_pPluginList->setChannels(1, qtractorPluginList::Midi); ++ } ++ ++ // Keep a shadow copy of settings. ++ m_tExtendedSettings.sMidiImportInstInst = pOptions->sMidiImportInstInst; ++ m_tExtendedSettings.sMidiImportDrumInst = pOptions->sMidiImportDrumInst; ++ m_tExtendedSettings.iMidiImportInstBank = pOptions->iMidiImportInstBank; ++ m_tExtendedSettings.iMidiImportDrumBank = pOptions->iMidiImportDrumBank; ++ m_tExtendedSettings.eMidiImportTrackNameType = (TrackNameType) pOptions->iMidiImportTrackName; ++ ++ // Get reference of the last command before dialog. ++ m_pLastCommand = NULL; ++ qtractorCommandList *pCommands = pSession->commands(); ++ m_pLastCommand = pCommands->lastCommand(); ++} ++ ++ ++// Destructor. ++qtractorMidiImportExtender::~qtractorMidiImportExtender() ++{ ++} ++ ++ ++// Save all settings to global options. ++void qtractorMidiImportExtender::saveSettings() ++{ ++ // Get global options object ++ qtractorOptions *pOptions = qtractorOptions::getInstance(); ++ if (!pOptions) ++ return; ++ ++ // Xmlize plugin list and staore in options ++ pluginListToDocument(); ++ QString strPluginListXML; ++ if (m_pPluginDomDocument) ++ // Store as XML string without whitespaces. ++ strPluginListXML = m_pPluginDomDocument->toString(-1); ++ pOptions->sMidiImportPlugins = strPluginListXML; ++ ++ // Store other options ++ pOptions->sMidiImportInstInst = m_tExtendedSettings.sMidiImportInstInst; ++ pOptions->sMidiImportDrumInst = m_tExtendedSettings.sMidiImportDrumInst; ++ pOptions->iMidiImportInstBank = m_tExtendedSettings.iMidiImportInstBank; ++ pOptions->iMidiImportDrumBank = m_tExtendedSettings.iMidiImportDrumBank; ++ pOptions->iMidiImportTrackName = (int)m_tExtendedSettings.eMidiImportTrackNameType; ++} ++ ++ ++// Undo commands done since creation. ++void qtractorMidiImportExtender::backoutCommandList() ++{ ++ // Backout all commands made in this dialog-session. ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (pSession) ++ pSession->commands()->backout(m_pLastCommand); ++} ++ ++ ++// Restore command list to the point of creation. ++void qtractorMidiImportExtender::restoreCommandList() ++{ ++ // Avoid inconsistent execution of undo menu entries for actions done ++ // within dialog. ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (pSession) { ++ qtractorCommandList *pCommands = pSession->commands(); ++ // Remove all commands without execution - tested: command-list keeps ++ // them with proper auto-delete so they are cleaned up properly on ++ // session close. ++ for (qtractorCommand *pCurrLastCommand = pCommands->lastCommand(); ++ pCurrLastCommand != m_pLastCommand; ++ pCurrLastCommand = pCommands->lastCommand()) ++ pCommands->removeLastCommand(); ++ } ++} ++ ++ ++// Accessor to plugin list. ++qtractorPluginList *qtractorMidiImportExtender::pluginList() ++{ ++ return m_pPluginList; ++} ++ ++ ++// Clear singleton plugin-list and delete plugins ++void qtractorMidiImportExtender::clearPluginList() ++{ ++ if (m_pPluginList) { ++ delete m_pPluginList; ++ m_pPluginList = NULL; ++ } ++} ++ ++// Add plugins to track and return a track-edit-command for setting bank ++qtractorEditTrackCommand *qtractorMidiImportExtender::modifyBareTrackAndGetEditCommand( ++ qtractorTrack *pTrack) ++{ ++ // Bail out if something is seriously wrong. ++ if (!pTrack) ++ return NULL; ++ qtractorPluginList *pTargetList = pTrack->pluginList(); ++ if (!pTargetList) ++ return NULL; ++ ++ // Add plugins ++ ++ // Take plugins for first track from plugin-list. ++ if (m_pPluginList) { ++ // First ensure we have a valid document for further tracks ++ pluginListToDocument(); ++ ++ // Move plugins to target. ++ while (m_pPluginList->count()) ++ pTargetList->movePlugin(m_pPluginList->first(), NULL); ++ ++ // Remove list: It is empty and no more required ++ clearPluginList(); ++ } ++ ++ // all further are cloned by our document - if there is one. ++ else if (m_pPluginListDocument) { ++ // Get root element and check for proper taq name. ++ QDomElement elem = m_pPluginDomDocument->documentElement(); ++ if (elem.tagName() == "PluginList") ++ pTargetList->loadElement(m_pPluginListDocument, &elem); ++ } ++ ++ ++ // Set bank. ++ bool bIsDrumTrack = pTrack->midiChannel() == 9; ++ int iBankNew = bIsDrumTrack ? ++ m_tExtendedSettings.iMidiImportDrumBank : ++ m_tExtendedSettings.iMidiImportInstBank; ++ ++ bool bTrackChanged = false; ++ qtractorTrack::Properties &properties = pTrack->properties(); ++ if (iBankNew >= 0) { ++ properties.midiBank = iBankNew; ++ // By default midiBankSelMethod is -1. If we don't set it here bank ++ // change is not sent to instrument. ++ properties.midiBankSelMethod = 0; ++ bTrackChanged = true; ++ // Special case for drum-tracks: Some MIDI files out in the wild do not ++ // set program for drum-tracks. Give those a reasonable default. ++ if (bIsDrumTrack && properties.midiProg < 0) ++ // Set standard drums. ++ properties.midiProg = 0; ++ } ++ ++ qtractorEditTrackCommand *pEditCommand = NULL; ++ if (bTrackChanged) ++ pEditCommand = new qtractorEditTrackCommand(pTrack, properties); ++ return pEditCommand; ++} ++ ++ ++bool qtractorMidiImportExtender::modifyFinishedTrack(qtractorTrack *pTrack) ++{ ++ // Bail out if something seriously is wrong. ++ if (!pTrack) ++ return false; ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (!pSession) ++ return false; ++ ++ bool bTrackChanged = false; ++ int iMidiChannel = pTrack->midiChannel(); ++ bool bIsDrumTrack = pTrack->midiChannel() == 9; ++ ++ // Set instrument. ++ qtractorMidiBus *pMidiBus = ++ static_cast (pTrack->outputBus()); ++ if (pMidiBus) { ++ // Get current patch ++ const qtractorMidiBus::Patch& patch = pMidiBus->patch(iMidiChannel); ++ // set instrument / bank for tracks with program set only. ++ if (patch.prog >= 0) { ++ // Set instrument. ++ QString strInstrumentNew = bIsDrumTrack ? ++ m_tExtendedSettings.sMidiImportDrumInst : ++ m_tExtendedSettings.sMidiImportInstInst; ++ ++ // Update patch if necessary. ++ if (!strInstrumentNew.isEmpty()) { ++ pMidiBus->setPatch(iMidiChannel, ++ strInstrumentNew, ++ patch.bankSelMethod, ++ patch.bank, ++ patch.prog, ++ pTrack); ++ bTrackChanged = true; ++ } ++ } ++ } ++ ++ // Set track name. ++ QString sTrackName; ++ switch (m_tExtendedSettings.eMidiImportTrackNameType) { ++ case Midifile: ++ // Default: no modification ++ break; ++ case Track: { ++ // Set names only for tracks making music after import ++ if (pTrack->midiBank() < 0 || pTrack->midiProg() < 0) ++ break; ++ sTrackName = pSession->uniqueTrackName( ++ QString("Track %1").arg(++m_iTrackNumber)); ++ break; ++ } ++ case PatchName: ++ // Instrument track. ++ if (!bIsDrumTrack) { ++ // Other bail out checks... ++ if (pTrack->midiBank() < 0 || pTrack->midiProg() < 0) ++ break; ++ qtractorPluginList *pPluginList = pTrack->pluginList(); ++ if (!pPluginList) ++ break; ++ qtractorMidiManager *pMidiManager = pPluginList->midiManager(); ++ if (!pMidiManager) ++ break; ++ ++ const qtractorMidiManager::Instruments& list ++ = pMidiManager->instruments(); ++ QString sInstrumentName = m_tExtendedSettings.sMidiImportInstInst; ++ if (!list.contains(sInstrumentName)) ++ break; ++ ++ // Banks reference... ++ const qtractorMidiManager::Banks& banks ++ = list[sInstrumentName]; ++ if (banks.contains(pTrack->midiBank())) { ++ const qtractorMidiManager::Progs& progs = banks[pTrack->midiBank()].progs; ++ sTrackName = progs[pTrack->midiProg()]; ++ } ++ } ++ // Drum track. ++ else ++ sTrackName = QObject::tr("Drums"); ++ break; ++ } ++ if (!sTrackName.isEmpty()) { ++ pSession->releaseTrackName(pTrack); ++ pTrack->setTrackName(sTrackName); ++ pSession->acquireTrackName(pTrack); ++ bTrackChanged = true; ++ } ++ ++ return bTrackChanged; ++} ++ ++ ++// Accessor to exteneded settings. ++qtractorMidiImportExtender::exportExtensionsData *qtractorMidiImportExtender::exportExtensions() ++{ ++ return &m_tExtendedSettings; ++} ++ ++// Xmlize plugin List ++void qtractorMidiImportExtender::pluginListToDocument() ++{ ++ // remove old plugin-list settings ++ if (m_pPluginDomDocument) { ++ delete m_pPluginDomDocument; ++ m_pPluginDomDocument = NULL; ++ } ++ if (m_pPluginListDocument) { ++ delete m_pPluginListDocument; ++ m_pPluginListDocument = NULL; ++ } ++ ++ // Xmlize plugin-list for options and cloning. ++ if (m_pPluginList->count()) { ++ m_pPluginDomDocument = new QDomDocument("qtractorMidiImport"); ++ m_pPluginListDocument = new qtractorPluginListDocument(m_pPluginDomDocument, m_pPluginList); ++ ++ // Save plugin settings to document. ++ QDomElement elem = m_pPluginDomDocument->createElement("PluginList"); ++ m_pPluginList->saveElement(m_pPluginListDocument, &elem); ++ m_pPluginDomDocument->appendChild(elem); ++ } ++} ++ ++ ++ ++// end of qtractorMidiImportExtender.cpp +diff --git a/src/qtractorMidiImportExtender.h b/src/qtractorMidiImportExtender.h +new file mode 100644 +index 0000000..e5a1c87 +--- /dev/null ++++ b/src/qtractorMidiImportExtender.h +@@ -0,0 +1,102 @@ ++// qtractorMidiImportExtender.h ++// ++/**************************************************************************** ++ Copyright (C) 2005-2017, rncbc aka Rui Nuno Capela. All rights reserved. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version 2 ++ of the License, or (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ ++*****************************************************************************/ ++ ++#ifndef __qtractorMidiImportExtender_h ++#define __qtractorMidiImportExtender_h ++ ++#include "QString" ++ ++// forward decalarations. ++class qtractorPluginList; ++class qtractorPluginListDocument; ++class qtractorEditTrackCommand; ++class qtractorTrack; ++class qtractorCommand; ++class QDomDocument; ++ ++ ++class qtractorMidiImportExtender ++{ ++public: ++ // Constructor. ++ qtractorMidiImportExtender(); ++ // Destructor. ++ virtual ~qtractorMidiImportExtender(); ++ ++ // Save all settings to global options. ++ void saveSettings(); ++ // Undo commands done since creation. ++ void backoutCommandList(); ++ // Restore command list to the point of creation. ++ void restoreCommandList(); ++ ++ // Accessor to plugin list. ++ qtractorPluginList *pluginList(); ++ // Clear singleton plugin-list. ++ static void clearPluginList(); ++ ++ // Methods used during import. ++ qtractorEditTrackCommand *modifyBareTrackAndGetEditCommand(qtractorTrack *pTrack); ++ bool modifyFinishedTrack(qtractorTrack *pTrack); ++ ++ // Track name set types. ++ typedef enum { Midifile, Track, PatchName } TrackNameType; ++ ++ // All extended settings except plugin list ++ typedef struct{ ++ // Instruments to set. ++ QString sMidiImportInstInst; ++ QString sMidiImportDrumInst; ++ // Banks to set ++ int iMidiImportInstBank; ++ int iMidiImportDrumBank; ++ // Type of track name to set. ++ TrackNameType eMidiImportTrackNameType; ++ } exportExtensionsData; ++ ++ // Accessor to exteneded settings. ++ exportExtensionsData *exportExtensions(); ++ ++private: ++ ++ // XMLize plugin List ++ void pluginListToDocument(); ++ ++ // pointer to singleton plugin-list ++ static qtractorPluginList *m_pPluginList; ++ ++ // Plugin list settings keeper. ++ QDomDocument *m_pPluginDomDocument; ++ qtractorPluginListDocument *m_pPluginListDocument; ++ ++ // Counter for 'Track n' name-type ++ int m_iTrackNumber; ++ ++ // All settings except plugin list. ++ exportExtensionsData m_tExtendedSettings; ++ ++ // Keep last command for backout / restore. ++ qtractorCommand *m_pLastCommand; ++}; ++ ++#endif // __qtractorMidiImportExtender_h ++ ++// end of qtractorMidiImportExtender.h +diff --git a/src/qtractorMidiImportForm.cpp b/src/qtractorMidiImportForm.cpp +new file mode 100644 +index 0000000..39ab05c +--- /dev/null ++++ b/src/qtractorMidiImportForm.cpp +@@ -0,0 +1,568 @@ ++// qtractorMidiListView.cpp ++// ++/**************************************************************************** ++ Copyright (C) 2005-2017, rncbc aka Rui Nuno Capela. All rights reserved. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version 2 ++ of the License, or (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ ++*****************************************************************************/ ++ ++#include "qtractorMidiImportForm.h" ++#include "qtractorSession.h" ++#include "qtractorMidiImportExtender.h" ++#include "qtractorMainForm.h" ++#include "qtractorPlugin.h" ++#include "qtractorMidiManager.h" ++#include "qtractorFiles.h" ++#include "qtractorCommand.h" ++#include "qtractorInstrument.h" ++#include "QPushButton" ++ ++//---------------------------------------------------------------------------- ++// qtractorMidiImportForm::MidiProgramObserver -- Local dedicated observer. ++ ++class qtractorMidiImportForm::MidiProgramObserver : public qtractorObserver ++{ ++public: ++ ++ // Constructor. ++ MidiProgramObserver(qtractorMidiImportForm *pMidiImportForm, qtractorSubject *pSubject) ++ : qtractorObserver(pSubject), m_pMidiImportForm(pMidiImportForm) {} ++ ++protected: ++ ++ // Update feedback. ++ void update(bool bUpdate) ++ { ++ if (bUpdate) { ++ const int iValue = int(value()); ++ const int iBank = (iValue >> 7) & 0x3fff; ++ m_pMidiImportForm->setMidiProgram(iBank); ++ } ++ } ++ ++private: ++ ++ // Members. ++ qtractorMidiImportForm *m_pMidiImportForm; ++}; ++ ++ ++//---------------------------------------------------------------------- ++// class qtractorMidiImportForm -- UI wrapper form. ++// ++ ++ ++// Constructor. ++qtractorMidiImportForm::qtractorMidiImportForm(qtractorMidiImportExtender *pMidiImportExtender, ++ QWidget *pParent, Qt::WindowFlags wflags ) ++ : QDialog(pParent, wflags), ++ m_pMidiImportExtender(pMidiImportExtender), ++ m_pMidiProgramObserver(NULL), m_iDirtySetup(0) ++{ ++ ++ // Get global session object. ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (!pSession) ++ return; ++ ++ // Get plugin-list to work with. ++ qtractorPluginList *pPluginList = m_pMidiImportExtender->pluginList(); ++ if (!pPluginList) ++ return; ++ ++ // Setup UI struct... ++ m_ui.setupUi(this); ++ ++ // Window modality (let plugin/tool windows rave around). ++ QDialog::setWindowModality(Qt::WindowModal); ++ ++ // Get reference of the last command before dialog. ++ m_pLastCommand = NULL; ++ qtractorCommandList *pCommands = pSession->commands(); ++ m_pLastCommand = pCommands->lastCommand(); ++ ++ // Set plugin list... ++ m_ui.PluginListView->setPluginList(pPluginList); ++ m_ui.PluginListView->refresh(); ++ ++ // Setup Observer for the instrument bank combobox. ++ if ( pPluginList->midiProgramSubject()) { ++ m_pMidiProgramObserver = new MidiProgramObserver(this, ++ pPluginList->midiProgramSubject()); ++ } ++ ++ // Fill Instrument / bank comboboxes. ++ updateInstruments(); ++ ++ // Get a pointer to settings. ++ qtractorMidiImportExtender::exportExtensionsData *pExtendedSettings = ++ m_pMidiImportExtender->exportExtensions(); ++ m_ui.InstInstComboBox->setCurrentText( ++ pExtendedSettings->sMidiImportInstInst); ++ m_ui.DrumInstComboBox->setCurrentText( ++ pExtendedSettings->sMidiImportDrumInst); ++ updateBanks(pExtendedSettings->sMidiImportInstInst, ++ pExtendedSettings->iMidiImportInstBank, Instruments); ++ updateBanks(pExtendedSettings->sMidiImportDrumInst, ++ pExtendedSettings->iMidiImportDrumBank, Drums); ++ ++ // Select track-name radio option ++ m_groupNameRadios = new QButtonGroup(this); ++ m_groupNameRadios->addButton(m_ui.NameMidiRadioButton, ++ (int) qtractorMidiImportExtender::Midifile); ++ m_groupNameRadios->addButton(m_ui.NameTrackRadioButton, ++ (int) qtractorMidiImportExtender::Track); ++ m_groupNameRadios->addButton(m_ui.NamePatchRadioButton, ++ (int) qtractorMidiImportExtender::PatchName); ++ QList radios = m_groupNameRadios->buttons(); ++ for (int iRadio = 0; iRadio < radios.count(); ++iRadio) { ++ if (m_groupNameRadios->id(radios[iRadio]) == ++ (int)pExtendedSettings->eMidiImportTrackNameType) { ++ radios[iRadio]->setChecked(true); ++ break; ++ } ++ } ++ ++ // UI signal/slot connections... ++ QObject::connect(m_ui.PluginListView, ++ SIGNAL(currentRowChanged(int)), ++ SLOT(stabilizeListButtons())); ++ QObject::connect(m_ui.PluginListView, ++ SIGNAL(contentsChanged()), ++ SLOT(pluginListChanged())); ++ QObject::connect(m_ui.AddPluginToolButton, ++ SIGNAL(clicked()), ++ SLOT(addPlugin())); ++ QObject::connect(m_ui.RemovePluginToolButton, ++ SIGNAL(clicked()), ++ SLOT(removePlugin())); ++ QObject::connect(m_ui.MoveUpPluginToolButton, ++ SIGNAL(clicked()), ++ SLOT(moveUpPlugin())); ++ QObject::connect(m_ui.MoveDownPluginToolButton, ++ SIGNAL(clicked()), ++ SLOT(moveDownPlugin())); ++ ++ QObject::connect(m_ui.InstInstComboBox, ++ SIGNAL(currentIndexChanged(const QString &)), ++ SLOT(changedInstInst(const QString &))); ++ QObject::connect(m_ui.DrumInstComboBox, ++ SIGNAL(currentIndexChanged(const QString &)), ++ SLOT(changedDrumInst(const QString &))); ++ QObject::connect(m_ui.InstBankComboBox, ++ SIGNAL(activated(int)), ++ SLOT(changed())); ++ QObject::connect(m_ui.DrumBankComboBox, ++ SIGNAL(activated(int)), ++ SLOT(changed())); ++ QObject::connect(m_groupNameRadios, ++ SIGNAL(buttonClicked(QAbstractButton *)), ++ SLOT(changed())); ++ ++ QObject::connect(m_ui.DialogButtonBox, ++ SIGNAL(accepted()), ++ SLOT(accept())); ++ QObject::connect(m_ui.DialogButtonBox, ++ SIGNAL(rejected()), ++ SLOT(reject())); ++ ++ stabilizeListButtons(); ++ m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); ++} ++ ++ ++// Destructor. ++qtractorMidiImportForm::~qtractorMidiImportForm() ++{ ++ // Do final cleanup ++ delete m_pMidiProgramObserver; ++ ++} ++ ++ ++// Accept settings (OK button slot). ++void qtractorMidiImportForm::accept() ++{ ++ // Tidy up. ++ commonCleanup(); ++ ++ // Get a pointer to settings. ++ qtractorMidiImportExtender::exportExtensionsData *pExtendedSettings = ++ m_pMidiImportExtender->exportExtensions(); ++ ++ // Instrument instruments. ++ QString strInstrument = m_ui.InstInstComboBox->currentText(); ++ if (strInstrument == getInstrumentNone()) ++ // Avoid language specific options. ++ strInstrument.clear(); ++ pExtendedSettings->sMidiImportInstInst = strInstrument; ++ ++ // Instrument drums. ++ strInstrument = m_ui.DrumInstComboBox->currentText(); ++ if (strInstrument == getInstrumentNone()) ++ // Avoid language specific options. ++ strInstrument.clear(); ++ pExtendedSettings->sMidiImportDrumInst = strInstrument; ++ ++ // Bank instruments. ++ pExtendedSettings->iMidiImportInstBank = -1; ++ int iCurrSelection = m_ui.InstBankComboBox->currentIndex(); ++ if (iCurrSelection > 0 && iCurrSelection < m_instBanks.count()) ++ pExtendedSettings->iMidiImportInstBank = m_instBanks[iCurrSelection]; ++ ++ // Bank drums. ++ pExtendedSettings->iMidiImportDrumBank = -1; ++ iCurrSelection = m_ui.DrumBankComboBox->currentIndex(); ++ if (iCurrSelection > 0 && iCurrSelection < m_drumBanks.count()) ++ pExtendedSettings->iMidiImportDrumBank = m_drumBanks[iCurrSelection]; ++ ++ // Track name type. ++ pExtendedSettings->eMidiImportTrackNameType = ++ (qtractorMidiImportExtender::TrackNameType)m_groupNameRadios->id(m_groupNameRadios->checkedButton()); ++ ++ // Just go with dialog acceptance. ++ QDialog::accept(); ++} ++ ++ ++// Reject settings (Cancel button slot). ++void qtractorMidiImportForm::reject() ++{ ++ // Tidy up. ++ commonCleanup(); ++ ++ // Backout all commands made in this dialog-session. ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (pSession) ++ pSession->commands()->backout(m_pLastCommand); ++ ++ // Just go away. ++ QDialog::reject(); ++} ++ ++ ++// Stabilize current form state. ++void qtractorMidiImportForm::stabilizeListButtons() ++{ ++ // Stabilize current plugin list state. ++ const int iPluginItemCount = m_ui.PluginListView->count(); ++ int iPluginItem = -1; ++ qtractorPlugin *pPlugin = NULL; ++ qtractorPluginListItem *pPluginItem ++ = static_cast ( ++ m_ui.PluginListView->currentItem()); ++ if (pPluginItem) { ++ iPluginItem = m_ui.PluginListView->row(pPluginItem); ++ pPlugin = pPluginItem->plugin(); ++ } ++ m_ui.RemovePluginToolButton->setEnabled(pPlugin != NULL); ++ m_ui.MoveUpPluginToolButton->setEnabled(pPluginItem && iPluginItem > 0); ++ m_ui.MoveDownPluginToolButton->setEnabled(pPluginItem && iPluginItem < iPluginItemCount - 1); ++} ++ ++ ++void qtractorMidiImportForm::pluginListChanged() ++{ ++ updateInstruments(); ++ stabilizeListButtons(); ++ changed(); ++} ++ ++ ++// Plugin-list add plugin. ++void qtractorMidiImportForm::addPlugin() ++{ ++ m_ui.PluginListView->addPlugin(); ++ changed(); ++} ++ ++ ++// Plugin-list remove plugin. ++void qtractorMidiImportForm::removePlugin() ++{ ++ m_ui.PluginListView->removePlugin(); ++ changed(); ++} ++ ++ ++// Plugin-list move plugin up. ++void qtractorMidiImportForm::moveUpPlugin() ++{ ++ m_ui.PluginListView->moveUpPlugin(); ++ changed(); ++} ++ ++ ++// Plugin-list move plugin down. ++void qtractorMidiImportForm::moveDownPlugin() ++{ ++ m_ui.PluginListView->moveDownPlugin(); ++ changed(); ++} ++ ++ ++// Instrument for instruments selection changed. ++void qtractorMidiImportForm::changedInstInst( const QString &text ) ++{ ++ if (m_iDirtySetup > 0) ++ return; ++ updateBanks(text, -1, Instruments); ++ changed(); ++} ++ ++ ++// Instrument for instruments selection changed. ++void qtractorMidiImportForm::changedDrumInst( const QString &text ) ++{ ++ if (m_iDirtySetup > 0) ++ return; ++ updateBanks(text, -1, Drums); ++ changed(); ++} ++ ++ ++// Settings changed - enable OK ++void qtractorMidiImportForm::changed() ++{ ++ m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true); ++} ++ ++ ++// Refresh instrument lists ++void qtractorMidiImportForm::updateInstruments() ++{ ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (pSession == NULL) ++ return; ++ ++ qtractorInstrumentList *pInstruments = pSession->instruments(); ++ if (pInstruments == NULL) ++ return; ++ ++ ++m_iDirtySetup; ++ ++ // keep old selections - maybe we can resstore them ++ const QString strOldInstInst = m_ui.InstInstComboBox->currentText(); ++ const QString strOldDrumInst = m_ui.DrumInstComboBox->currentText(); ++ ++ const QIcon& icon = QIcon(":/images/itemInstrument.png"); ++ m_ui.InstInstComboBox->clear(); ++ m_ui.InstInstComboBox->addItem(getInstrumentNone()); ++ m_ui.DrumInstComboBox->clear(); ++ m_ui.DrumInstComboBox->addItem(getInstrumentNone()); ++ ++ // Take care of MIDI plugin instrument names... ++ updateInstrumentsAdd(icon, ++ m_pMidiImportExtender->pluginList()->midiManager()); ++ ++ // Regular instrument names... ++ qtractorInstrumentList::ConstIterator iter = pInstruments->constBegin(); ++ const qtractorInstrumentList::ConstIterator& iter_end = pInstruments->constEnd(); ++ for ( ; iter != iter_end; ++iter) { ++ ++ m_ui.InstInstComboBox->addItem(icon, iter.value().instrumentName()); ++ m_ui.DrumInstComboBox->addItem(icon, iter.value().instrumentName()); ++ } ++ ++ // Try to restore old instrument selections if possible. ++ m_ui.InstInstComboBox->setCurrentText(strOldInstInst); ++ m_ui.DrumInstComboBox->setCurrentText(strOldDrumInst); ++ ++ // In case insrtument has changed: update banks. ++ const QString strNewInstInst = m_ui.InstInstComboBox->currentText(); ++ const QString strNewDrumInst = m_ui.DrumInstComboBox->currentText(); ++ if (strNewInstInst != strOldInstInst) ++ updateBanks(strNewInstInst, -1, Instruments); ++ if (strNewDrumInst != strOldDrumInst) ++ updateBanks(strNewDrumInst, -1, Drums); ++ ++ --m_iDirtySetup; ++} ++ ++ ++// Refresh instrument lists of given MIDI buffer manager. ++void qtractorMidiImportForm::updateInstrumentsAdd( const QIcon &icon, ++ qtractorMidiManager *pMidiManager ) ++{ ++ if (pMidiManager == NULL) ++ return; ++ ++ pMidiManager->updateInstruments(); ++ ++ const qtractorMidiManager::Instruments& list ++ = pMidiManager->instruments(); ++ qtractorMidiManager::Instruments::ConstIterator iter = list.constBegin(); ++ const qtractorMidiManager::Instruments::ConstIterator& iter_end = list.constEnd(); ++ for ( ; iter != iter_end; ++iter) { ++ m_ui.InstInstComboBox->addItem(icon, iter.key()); ++ m_ui.DrumInstComboBox->addItem(icon, iter.key()); ++ } ++} ++ ++// Refresh instrument banks lists. ++void qtractorMidiImportForm::updateBanks( ++ const QString &sInstrumentName, int iBank, enum BankType bankType ) ++{ ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (pSession == NULL) ++ return; ++ ++ qtractorInstrumentList *pInstruments = pSession->instruments(); ++ if (pInstruments == NULL) ++ return; ++ ++ // Default (none) patch bank list... ++ QComboBox *pComboBox = NULL; ++ QMap *pBankMap = NULL; ++ switch (bankType) { ++ case Instruments: ++ pComboBox = m_ui.InstBankComboBox; ++ pBankMap = &m_instBanks; ++ break; ++ case Drums: ++ pComboBox = m_ui.DrumBankComboBox; ++ pBankMap = &m_drumBanks; ++ break; ++ } ++ // Should not happen at all... ++ if (!pComboBox || !pBankMap) ++ return; ++ ++ // keep old selections - maybe we can resstore them ++ const QString strOldBank = pComboBox->currentText(); ++ ++ const QIcon& icon = QIcon(":/images/itemPatches.png"); ++ pComboBox->clear(); ++ pComboBox->addItem(icon, getBankNone()); ++ ++ int iBankIndex = 0; ++ pBankMap->clear(); ++ (*pBankMap)[iBankIndex++] = -1; ++ ++ // Care of MIDI plugin instrument banks... ++ bool bMidiManager = updateBanksAdd(icon, ++ m_pMidiImportExtender->pluginList()->midiManager(), ++ sInstrumentName, iBank, iBankIndex, ++ pComboBox, pBankMap); ++ ++ // Get instrument set alright... ++ if (!bMidiManager && pInstruments->contains(sInstrumentName)) { ++ // Try to reset old bank ++ pComboBox->setCurrentText(strOldBank); ++ if (pComboBox->currentIndex() > 0) ++ // Old bank found: done ++ return; ++ ++ // Instrument reference... ++ const qtractorInstrument& instr ++ = pInstruments->value(sInstrumentName); ++ // For proper bank selection... ++ iBankIndex = -1; ++ if (iBank >= 0) { ++ const qtractorInstrumentData& patch = instr.patch(iBank); ++ if (!patch.name().isEmpty()) ++ iBankIndex = pComboBox->findText(patch.name()); ++ } ++ } ++ else if (!bMidiManager) ++ iBankIndex = -1; ++ ++ // If there's banks we must choose at least one... ++ if (iBank < 0 && pBankMap->count() > 1) { ++ iBankIndex = 1; ++ iBank = (*pBankMap)[iBankIndex]; ++ } ++ ++ // Do the proper bank selection... ++ if (iBank < 0) { ++ pComboBox->setCurrentIndex(0); ++ } else if (iBankIndex < 0) { ++ pComboBox->setCurrentIndex(0); ++ } else { ++ pComboBox->setCurrentIndex(iBankIndex); ++ } ++} ++ ++ ++// Update instruments banks. ++bool qtractorMidiImportForm::updateBanksAdd( const QIcon &icon, ++ qtractorMidiManager *pMidiManager, ++ const QString &sInstrumentName, ++ int iBank, ++ int &iBankIndex, ++ QComboBox *pComboBox, QMap *pBankMap) ++{ ++ if (pMidiManager == NULL) ++ return false; ++ ++ // It is possible that bank layout has changed - e.g for fluidsynth-dssi ++ // loaded a new soundfont. So instead of opening and closing dialog, it ++ // is possible to select no instrument and then fluidsyth-dssi to get ++ // proper bank layout. ++ pMidiManager->updateInstruments(); ++ ++ const qtractorMidiManager::Instruments& list ++ = pMidiManager->instruments(); ++ if (!list.contains(sInstrumentName)) ++ return false; ++ ++ // Refresh bank mapping... ++ const qtractorMidiManager::Banks& banks = list[sInstrumentName]; ++ qtractorMidiManager::Banks::ConstIterator iter = banks.constBegin(); ++ const qtractorMidiManager::Banks::ConstIterator& iter_end = banks.constEnd(); ++ for ( ; iter != iter_end; ++iter) { ++ pComboBox->addItem(icon, iter.value().name); ++ (*pBankMap)[iBankIndex++] = iter.key(); ++ } ++ // Reset given bank combobox index. ++ iBankIndex = -1; ++ // For proper bank selection... ++ if (banks.contains(iBank)) { ++ const qtractorMidiManager::Bank& bank = banks[iBank]; ++ iBankIndex = pComboBox->findText(bank.name); ++ } ++ ++ // Mark that we've have something. ++ return true; ++} ++ ++ ++// Cleanup right before dialog is closed. ++void qtractorMidiImportForm::commonCleanup() ++{ ++ // Close all plugin windows before the turn into zombies. ++ for (qtractorPlugin *pPlugin = m_pMidiImportExtender->pluginList()->first(); ++ pPlugin; pPlugin = pPlugin->next()) { ++ pPlugin->closeEditor(); ++ pPlugin->closeForm(); ++ } ++ ++ // Unlink plugin-list from view. ++ m_ui.PluginListView->setPluginList(NULL); ++} ++ ++ ++// MIDI bank/program settlers. ++void qtractorMidiImportForm::setMidiProgram ( int iBank ) ++{ ++ QString sInstrumentName; ++ if (m_ui.InstInstComboBox->currentIndex() > 0) ++ sInstrumentName = m_ui.InstInstComboBox->currentText(); ++ updateBanks(sInstrumentName, iBank, Instruments); ++} ++ ++ ++// end of qtractorMidiImportForm.cpp +diff --git a/src/qtractorMidiImportForm.h b/src/qtractorMidiImportForm.h +new file mode 100644 +index 0000000..e36ac79 +--- /dev/null ++++ b/src/qtractorMidiImportForm.h +@@ -0,0 +1,119 @@ ++// qtractorMidiImportForm.h ++// ++/**************************************************************************** ++ Copyright (C) 2005-2017, rncbc aka Rui Nuno Capela. All rights reserved. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version 2 ++ of the License, or (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ ++*****************************************************************************/ ++ ++#ifndef __qtractorMidiImportForm_h ++#define __qtractorMidiImportForm_h ++ ++#include "ui_qtractorMidiImportForm.h" ++ ++// forward decalarations. ++class qtractorMidiImportExtender; ++class qtractorCommand; ++class qtractorMidiManager; ++class qtractorTrack; ++class qtractorEditTrackCommand; ++ ++ ++class qtractorMidiImportForm : public QDialog ++{ ++ Q_OBJECT ++ ++public: ++ ++ // Constructor. ++ qtractorMidiImportForm(qtractorMidiImportExtender *pMidiImportExtender, ++ QWidget *pParent = 0, Qt::WindowFlags wflags = 0); ++ // Destructor. ++ virtual ~qtractorMidiImportForm(); ++ ++ // MIDI observer callback ++ void setMidiProgram(int iBank); ++ ++protected slots: ++ ++ void accept(); ++ void reject(); ++ ++ void stabilizeListButtons(); ++ ++ void pluginListChanged(); ++ void addPlugin(); ++ void removePlugin(); ++ void moveUpPlugin(); ++ void moveDownPlugin(); ++ ++ void changedInstInst(const QString &text); ++ void changedDrumInst(const QString &text); ++ ++ void changed(); ++private: ++ ++ // Update instruments comboboxes. ++ void updateInstruments(); ++ void updateInstrumentsAdd( ++ const QIcon& icon, qtractorMidiManager *pMidiManager); ++ ++ // Update instruments comboboxes. ++ enum BankType { Instruments, Drums }; ++ void updateBanks(const QString& sInstrumentName, int iBank, enum BankType bankType); ++ bool updateBanksAdd( ++ const QIcon& icon, qtractorMidiManager *pMidiManager, ++ const QString& sInstrumentName, int iBank, int& iBankIndex, ++ QComboBox* pComboBox, QMap *pBankMap); ++ ++ // Avoid redundancy for empty texts ++ // FIXME: move to a more common place and share with e.g with track-form ++ const QString getInstrumentNone() { return tr("(No instrument)"); } ++ const QString getBankNone() { return tr("(None)"); } ++ ++ // Cleanup right before dialog is closed. ++ void commonCleanup(); ++ ++ // The Qt-designer UI struct... ++ Ui::qtractorMidiImportForm m_ui; ++ ++ // Pointer to object doing the job of MIDI import extension. ++ qtractorMidiImportExtender *m_pMidiImportExtender; ++ ++ // Keep last command for backout / restore. ++ qtractorCommand *m_pLastCommand; ++ ++ // MIDI bank/program observer. ++ class MidiProgramObserver; ++ ++ // Observer to catch instument bank change ++ MidiProgramObserver *m_pMidiProgramObserver; ++ ++ // Button group around name-type radios ++ QButtonGroup* m_groupNameRadios; ++ ++ // counter avoiding unwanted handling of change events ++ int m_iDirtySetup; ++ ++ // Entry in combobox / bank map for instruments. ++ QMap m_instBanks; ++ // Entry in combobox / bank map for drums. ++ QMap m_drumBanks; ++}; ++ ++#endif // __qtractorMidiImportForm_h ++ ++// end of qtractorMidiImportForm.h +diff --git a/src/qtractorMidiImportForm.ui b/src/qtractorMidiImportForm.ui +new file mode 100644 +index 0000000..f6e5e2d +--- /dev/null ++++ b/src/qtractorMidiImportForm.ui +@@ -0,0 +1,396 @@ ++ ++ ++ rncbc aka Rui Nuno Capela ++ qtractor - An Audio/MIDI multi-track sequencer. ++ ++ Copyright (C) 2005-2017, rncbc aka Rui Nuno Capela. All rights reserved. ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version 2 ++ of the License, or (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ ++ ++ qtractorMidiImportForm ++ ++ ++ ++ 0 ++ 0 ++ 0 ++ 0 ++ ++ ++ ++ MIDI import ++ ++ ++ Qt::StrongFocus ++ ++ ++ :/images/itemMidiFile.png ++ ++ ++ ++ ++ ++ Plugins ++ ++ ++ ++ ++ ++ Track plugins ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ 90 ++ 28 ++ ++ ++ ++ Add plugin ++ ++ ++ &Add... ++ ++ ++ :/images/formCreate.png ++ ++ ++ Qt::ToolButtonTextBesideIcon ++ ++ ++ ++ ++ ++ ++ ++ 90 ++ 28 ++ ++ ++ ++ Remove plugin ++ ++ ++ &Remove ++ ++ ++ :/images/formRemove.png ++ ++ ++ Qt::ToolButtonTextBesideIcon ++ ++ ++ ++ ++ ++ ++ Qt::Vertical ++ ++ ++ ++ 8 ++ 8 ++ ++ ++ ++ ++ ++ ++ ++ ++ 90 ++ 28 ++ ++ ++ ++ Move plugin up ++ ++ ++ &Up ++ ++ ++ :/images/formMoveUp.png ++ ++ ++ Qt::ToolButtonTextBesideIcon ++ ++ ++ ++ ++ ++ ++ ++ 90 ++ 28 ++ ++ ++ ++ Move plugin down ++ ++ ++ &Down ++ ++ ++ :/images/formMoveDown.png ++ ++ ++ Qt::ToolButtonTextBesideIcon ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ 4 ++ ++ ++ 8 ++ ++ ++ ++ ++ Instrument and bank selection ++ ++ ++ ++ ++ ++ Instruments ++ ++ ++ ++ 8 ++ ++ ++ 4 ++ ++ ++ ++ ++ &Instrument: ++ ++ ++ InstInstComboBox ++ ++ ++ ++ ++ ++ ++ ++ 250 ++ ++ ++ ++ Select instrument for all instruments ++ ++ ++ ++ ++ ++ ++ &Bank: ++ ++ ++ InstBankComboBox ++ ++ ++ ++ ++ ++ ++ ++ 250 ++ ++ ++ ++ Select bank for all instruments ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Drums ++ ++ ++ ++ 8 ++ ++ ++ 4 ++ ++ ++ ++ ++ &Instrument: ++ ++ ++ DrumInstComboBox ++ ++ ++ ++ ++ ++ ++ ++ 250 ++ ++ ++ ++ Select instrument for all drums ++ ++ ++ ++ ++ ++ ++ &Bank: ++ ++ ++ DrumBankComboBox ++ ++ ++ ++ ++ ++ ++ ++ 250 ++ ++ ++ ++ Select bank for all drums ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Track names ++ ++ ++ ++ ++ ++ Set MIDI filename (without extension) for track name ++ ++ ++ &MIDI filename ++ ++ ++ false ++ ++ ++ ++ ++ ++ ++ Set 'Track n' (n = number) for track name ++ ++ ++ &Track [n] ++ ++ ++ false ++ ++ ++ ++ ++ ++ ++ Instrument name ++ ++ ++ &Patch name ++ ++ ++ false ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ 4 ++ ++ ++ 8 ++ ++ ++ ++ ++ Qt::Horizontal ++ ++ ++ QDialogButtonBox::Cancel|QDialogButtonBox::Ok ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ qtractorPluginListView ++ QListWidget ++
qtractorPluginListView.h
++
++
++ ++ PluginListView ++ AddPluginToolButton ++ RemovePluginToolButton ++ MoveUpPluginToolButton ++ MoveDownPluginToolButton ++ InstInstComboBox ++ InstBankComboBox ++ DrumInstComboBox ++ DrumBankComboBox ++ NameMidiRadioButton ++ DialogButtonBox ++ ++ ++ ++ ++ ++
+diff --git a/src/qtractorOptions.cpp b/src/qtractorOptions.cpp +index 9be2296..2459ef8 100644 +--- a/src/qtractorOptions.cpp ++++ b/src/qtractorOptions.cpp +@@ -162,6 +162,13 @@ void qtractorOptions::loadOptions (void) + iMidiMmcMode = m_settings.value("/MmcMode", 3).toInt(); + iMidiSppMode = m_settings.value("/SppMode", 3).toInt(); + iMidiClockMode = m_settings.value("/ClockMode", 0).toInt(); ++ sMidiImportPlugins = m_settings.value("/ImportPlugins").toString(); ++ sMidiImportInstInst = m_settings.value("/ImportInstInst").toString(); ++ sMidiImportDrumInst = m_settings.value("/ImportDrumInst").toString(); ++ iMidiImportInstBank = m_settings.value("/ImportInstBank", -1).toInt(); ++ iMidiImportDrumBank = m_settings.value("/ImportDrumBank", -1).toInt(); ++ iMidiImportTrackName = m_settings.value("/ImportTrackName", 0).toInt(); ++ + m_settings.endGroup(); + + // Metronome options group. +@@ -439,7 +446,7 @@ void qtractorOptions::saveOptions (void) + m_settings.setValue("/MetroOffset", uint(iAudioMetroOffset)); + m_settings.endGroup(); + +- // MIDI rendering options group. ++ // MIDI rendering and import options group. + m_settings.beginGroup("/Midi"); + m_settings.setValue("/CaptureFormat", iMidiCaptureFormat); + m_settings.setValue("/CaptureQuantize", iMidiCaptureQuantize); +@@ -454,6 +461,13 @@ void qtractorOptions::saveOptions (void) + m_settings.setValue("/MmcMode", iMidiMmcMode); + m_settings.setValue("/SppMode", iMidiSppMode); + m_settings.setValue("/ClockMode", iMidiClockMode); ++ m_settings.setValue("/ImportPlugins", sMidiImportPlugins); ++ m_settings.setValue("/ImportInstInst", sMidiImportInstInst); ++ m_settings.setValue("/ImportDrumInst", sMidiImportDrumInst); ++ m_settings.setValue("/ImportInstBank", iMidiImportInstBank); ++ m_settings.setValue("/ImportDrumBank", iMidiImportDrumBank); ++ m_settings.setValue("/ImportTrackName", iMidiImportTrackName); ++ + m_settings.endGroup(); + + // Metronome options group. +diff --git a/src/qtractorOptions.h b/src/qtractorOptions.h +index ca8aad0..1822b63 100644 +--- a/src/qtractorOptions.h ++++ b/src/qtractorOptions.h +@@ -140,6 +140,15 @@ public: + int iMidiSppMode; + int iMidiClockMode; + ++ // MIDI import options. ++ QString sMidiImportPlugins; ++ QString sMidiImportInstInst; ++ QString sMidiImportDrumInst; ++ int iMidiImportInstBank; ++ int iMidiImportDrumBank; ++ int iMidiImportTrackName; ++ ++ + // MIDI Metronome parameters. + int iMetroChannel; + int iMetroBarNote; +diff --git a/src/qtractorOptionsForm.cpp b/src/qtractorOptionsForm.cpp +index 99072a1..f33dcb6 100644 +--- a/src/qtractorOptionsForm.cpp ++++ b/src/qtractorOptionsForm.cpp +@@ -36,6 +36,8 @@ + #include "qtractorMidiMeter.h" + + #include "qtractorPluginSelectForm.h" ++#include "qtractorMidiImportForm.h" ++#include "qtractorMidiImportExtender.h" + + #include + #include +@@ -98,6 +100,9 @@ qtractorOptionsForm::qtractorOptionsForm ( + // Have some deafult time-scale for instance... + m_pTimeScale = NULL; + ++ // Enhanced MIDI import - created on demand ++ m_pMidiImportExtender = NULL; ++ + qtractorSession *pSession = qtractorSession::getInstance(); + if (pSession) { + m_pTimeScale = new qtractorTimeScale(*pSession->timeScale()); +@@ -264,6 +269,9 @@ qtractorOptionsForm::qtractorOptionsForm ( + QObject::connect(m_ui.MidiCaptureFormatComboBox, + SIGNAL(activated(int)), + SLOT(changed())); ++ QObject::connect(m_ui.MidiImportPushButton, ++ SIGNAL(clicked()), ++ SLOT(showMidiImportDialog())); + QObject::connect(m_ui.MidiCaptureQuantizeComboBox, + SIGNAL(activated(int)), + SLOT(changed())); +@@ -502,6 +510,7 @@ qtractorOptionsForm::qtractorOptionsForm ( + qtractorOptionsForm::~qtractorOptionsForm (void) + { + if (m_pTimeScale) delete m_pTimeScale; ++ if (m_pMidiImportExtender) delete m_pMidiImportExtender; + } + + +@@ -801,6 +810,9 @@ void qtractorOptionsForm::accept (void) + m_pOptions->iMetroBeatDuration = m_ui.MetroBeatDurationSpinBox->value(); + m_pOptions->bMidiMetroBus = m_ui.MidiMetroBusCheckBox->isChecked(); + m_pOptions->iMidiMetroOffset = m_ui.MidiMetroOffsetSpinBox->value(); ++ // MIDI import options. ++ if (m_pMidiImportExtender) ++ m_pMidiImportExtender->saveSettings(); + // Display options... + m_pOptions->bConfirmRemove = m_ui.ConfirmRemoveCheckBox->isChecked(); + m_pOptions->bConfirmArchive = m_ui.ConfirmArchiveCheckBox->isChecked(); +@@ -892,6 +904,10 @@ void qtractorOptionsForm::accept (void) + // Save/commit to disk. + m_pOptions->saveOptions(); + ++ // Remove plugin-list commands from undo-menu. ++ if (m_pMidiImportExtender) ++ m_pMidiImportExtender->restoreCommandList(); ++ + // Just go with dialog acceptance + QDialog::accept(); + } +@@ -917,6 +933,11 @@ void qtractorOptionsForm::reject (void) + accept(); + return; + case QMessageBox::Discard: ++ // In case Midi-Import dialog was closed with OK but changes ++ // shall be discarded, the plugin-list changes must be discarded ++ // too. ++ if (m_pMidiImportExtender) ++ m_pMidiImportExtender->backoutCommandList(); + break; + default: // Cancel. + bReject = false; +@@ -1561,6 +1582,20 @@ void qtractorOptionsForm::chooseSessionTemplatePath (void) + } + + ++// Open dialog to change MIDI import settings ++void qtractorOptionsForm::showMidiImportDialog() ++{ ++ // First time here: create import extender ++ if (!m_pMidiImportExtender) ++ m_pMidiImportExtender = new qtractorMidiImportExtender(); ++ // Show dialog ++ qtractorMidiImportForm midiImportForm( ++ m_pMidiImportExtender, this); ++ if (midiImportForm.exec()) ++ changed(); ++} ++ ++ + // Stabilize current form state. + void qtractorOptionsForm::stabilizeForm (void) + { +diff --git a/src/qtractorOptionsForm.h b/src/qtractorOptionsForm.h +index baa6a55..ed0a845 100644 +--- a/src/qtractorOptionsForm.h ++++ b/src/qtractorOptionsForm.h +@@ -28,6 +28,7 @@ + // Forward declarations... + class qtractorOptions; + class qtractorTimeScale; ++class qtractorMidiImportExtender; + + + //---------------------------------------------------------------------------- +@@ -76,6 +77,7 @@ protected slots: + void chooseMessagesFont(); + void chooseMessagesLogPath(); + void chooseSessionTemplatePath(); ++ void showMidiImportDialog(); + void stabilizeForm(); + + protected: +@@ -114,6 +116,9 @@ private: + QStringList m_vstPaths; + + int m_iDirtyPluginPaths; ++ ++ // Midi import settings handler. ++ qtractorMidiImportExtender *m_pMidiImportExtender; + }; + + +diff --git a/src/qtractorOptionsForm.ui b/src/qtractorOptionsForm.ui +index ab60726..ff62036 100644 +--- a/src/qtractorOptionsForm.ui ++++ b/src/qtractorOptionsForm.ui +@@ -1371,7 +1371,7 @@ + + + +- Capture / Export ++ Capture / Export / Import + + + true +@@ -1419,6 +1419,25 @@ + + + ++ ++ ++ ++ ++ 50 ++ false ++ ++ ++ ++ Change defaults for MIDI import ++ ++ ++ &Import Settings... ++ ++ ++ false ++ ++ ++ + + + +diff --git a/src/qtractorSession.cpp b/src/qtractorSession.cpp +index 19e72b3..f4415b2 100644 +--- a/src/qtractorSession.cpp ++++ b/src/qtractorSession.cpp +@@ -33,6 +33,7 @@ + #include "qtractorMidiEngine.h" + #include "qtractorMidiClip.h" + #include "qtractorMidiManager.h" ++#include "qtractorMidiImportExtender.h" + + #include "qtractorPlugin.h" + #include "qtractorCurve.h" +@@ -262,6 +263,7 @@ void qtractorSession::clear (void) + + qtractorAudioClip::clearHashTable(); + qtractorMidiClip::clearHashTable(); ++ qtractorMidiImportExtender::clearPluginList(); + + m_iSessionStart = 0; + m_iSessionEnd = 0; +diff --git a/src/qtractorTracks.cpp b/src/qtractorTracks.cpp +index 2d2ff6a..86fec14 100644 +--- a/src/qtractorTracks.cpp ++++ b/src/qtractorTracks.cpp +@@ -61,9 +61,12 @@ + + #include "qtractorMidiToolsForm.h" + #include "qtractorMidiEditSelect.h" ++#include "qtractorMidiImportExtender.h" + + #include "qtractorFileList.h" + ++#include "qtractorMixer.h" ++ + #include + #include + #include +@@ -2802,8 +2805,9 @@ bool qtractorTracks::addAudioTracks ( const QStringList& files, + + + // Import MIDI files into new tracks... +-bool qtractorTracks::addMidiTracks ( const QStringList& files, +- unsigned long iClipStart, qtractorTrack *pAfterTrack ) ++bool qtractorTracks::addMidiTracks (const QStringList& files, ++ unsigned long iClipStart, qtractorTrack *pAfterTrack, ++ qtractorMidiImportExtender *pMidiImportExtender) + { + // Have we some? + if (files.isEmpty()) +@@ -2813,10 +2817,13 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, + if (pSession == NULL) + return false; + ++ // Tell the world we'll take some time... ++ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); ++ + // pSession->lock(); + +- // Account for actual updates... +- int iUpdate = 0; ++ // Collect tracks added. ++ QList addedTracks; + + // We'll build a composite command... + qtractorImportTrackCommand *pImportTrackCommand +@@ -2877,12 +2884,16 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, + } + // Time to check whether there is actual data on track... + if (pMidiClip->clipLength() > 0) { ++ addedTracks.append(pTrack); + // Add the new track to composite command... + pTrack->setTrackName( + pSession->uniqueTrackName(pMidiClip->clipName())); + pTrack->setMidiChannel(pMidiClip->channel()); + pImportTrackCommand->addTrack(pTrack); +- ++iUpdate; ++ // Enhanced dialog: do modifications on bare track ++ if (pMidiImportExtender) ++ pImportTrackCommand->addTrackEditCommand( ++ pMidiImportExtender->modifyBareTrackAndGetEditCommand(pTrack)); + // Don't forget to add this one to local repository. + if (pMainForm) + pMainForm->addMidiFile(sPath); +@@ -2894,7 +2905,7 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, + } + } + // Log this successful import operation... +- if (iUpdate > 0 && pMainForm) { ++ if (addedTracks.count() && pMainForm) { + sDescription += tr("MIDI file import \"%1\" on %2 %3.\n") + .arg(QFileInfo(sPath).fileName()) + .arg(QDate::currentDate().toString("MMM dd yyyy")) +@@ -2906,17 +2917,39 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, + + // Have we changed anything? + bool bResult = false; +- if (iUpdate > 0) { ++ if (addedTracks.count()) { + // Log to session (undoable by import-track command)... + pSession->setDescription(sDescription); + // Put it in the form of an undoable command... + bResult = pSession->execute(pImportTrackCommand); ++ // Tracks are complete now: instrument and track-name (based on ++ // program-name are depending on instrument) can be set. ++ if (bResult && pMidiImportExtender) { ++ bool bModified = false; ++ QListIterator iter(addedTracks); ++ while (iter.hasNext()) { ++ qtractorTrack *pTrack = iter.next(); ++ if (pMidiImportExtender->modifyFinishedTrack(pTrack)) { ++ // reopen to take changes effect ++ pTrack->open(); ++ bModified = true; ++ } ++ } ++ if (bModified) { ++ // Mixer needs extra invitation... ++ qtractorMixer *pMixer = pMainForm->mixer(); ++ if (pMixer) ++ pMixer->updateTracks(true); ++ } ++ } + } else { + delete pImportTrackCommand; + } + + // pSession->unlock(); + ++ QApplication::restoreOverrideCursor(); ++ + return bResult; + } + +diff --git a/src/qtractorTracks.h b/src/qtractorTracks.h +index 2d9fa48..dbe60de 100644 +--- a/src/qtractorTracks.h ++++ b/src/qtractorTracks.h +@@ -35,6 +35,7 @@ class qtractorClipRangeCommand; + class qtractorClipToolCommand; + + class qtractorMidiToolsForm; ++class qtractorMidiImportExtender; + + + //---------------------------------------------------------------------------- +@@ -71,7 +72,8 @@ public: + bool addAudioTracks(const QStringList& files, + unsigned long iClipStart, qtractorTrack *pAfterTrack = NULL); + bool addMidiTracks(const QStringList& files, +- unsigned long iClipStart, qtractorTrack *pAfterTrack = NULL); ++ unsigned long iClipStart, qtractorTrack *pAfterTrack = NULL, ++ qtractorMidiImportExtender *pMidiImportExtender = NULL); + bool addMidiTrackChannel(const QString& sPath, int iTrackChannel, + unsigned long iClipStart, qtractorTrack *pAfterTrack = NULL); + +diff --git a/src/src.pro b/src/src.pro +index 92c0bd8..e87382f 100644 +--- a/src/src.pro ++++ b/src/src.pro +@@ -45,6 +45,8 @@ HEADERS += config.h \ + qtractorFileList.h \ + qtractorFileListView.h \ + qtractorFiles.h \ ++ qtractorMidiImportExtender.h \ ++ qtractorMidiImportForm.h \ + qtractorInsertPlugin.h \ + qtractorInstrument.h \ + qtractorInstrumentMenu.h \ +@@ -202,6 +204,8 @@ SOURCES += \ + qtractorMidiEventList.cpp \ + qtractorMidiFile.cpp \ + qtractorMidiFileTempo.cpp \ ++ qtractorMidiImportExtender.cpp \ ++ qtractorMidiImportForm.cpp \ + qtractorMidiListView.cpp \ + qtractorMidiManager.cpp \ + qtractorMidiMeter.cpp \ +@@ -276,6 +280,7 @@ FORMS += \ + qtractorMidiControlForm.ui \ + qtractorMidiControlObserverForm.ui \ + qtractorMidiEditorForm.ui \ ++ qtractorMidiImportForm.ui \ + qtractorMidiSysexForm.ui \ + qtractorMidiToolsForm.ui \ + qtractorOptionsForm.ui \ +-- +2.9.5 + diff --git a/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb b/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb index 57450947..623dcb90 100644 --- a/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb +++ b/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb @@ -23,9 +23,12 @@ SRC_URI = " \ file://0002-do-nor-try-run-for-float-sse-detection.patch \ file://0003-do-nor-try-run-for-suil-libs-detection.patch \ file://0004-Add-ARM-NEON-acceleration-for-time-stretch-not-yet-t.patch \ - file://0005-MIDI-import-give-tracks-more-informational-names.patch \ + file://0005-Make-plugin-lists-and-MIDI-manager-list-thread-safe.patch \ + file://0006-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch \ + file://0007-qtractorImportTrackCommand-extend-to-accept-qtractor.patch \ + file://0008-Optionally-enhance-MIDI-import.patch \ " -SRCREV = "4af19b298679e4d80ee2496ecf0175526f894919" +SRCREV = "1152ce6c8e64c76e5fadff59ca9e84aa6a647a9a" PV = "0.8.4+git${SRCPV}" S = "${WORKDIR}/git"