From eb60d9e06ebf36e9dc0366b54bb50b192f69e29d Mon Sep 17 00:00:00 2001 From: Devin Venable Date: Mon, 29 Oct 2012 17:26:08 -0500 Subject: [PATCH] Added multiple track export feature. Here is the patchset for my multiple track export feature. It works now the way I originally envisioned. For example, after I export tracks on my little song, I see this in the directory I created: devin@devin-studio:~/lmms/projects/fff$ ls 0_Defaultpreset.wav 3_Defaultpreset.wav 6_csidSouwav.wav 1_Defaultpreset.wav 4_spacenoiseswavwav.wav 7_HHOPENwav.wav 2_Defaultpreset.wav 5_csidkickwav.wav 8_HHOPENwav.wav Each instrument or sample track is exported individually, regardless of whether in its own song track or playing as part of a BB track. The name is taken from either the song track name or from the BB track name. My goal was to get the tracks individually exported, so that I could combine them with other tracks in Ardour. Signed-off-by: Tobias Doerffel --- include/export_project_dialog.h | 20 ++-- include/song.h | 3 +- src/core/ProjectRenderer.cpp | 6 +- src/core/song.cpp | 65 +++++++----- src/gui/MainWindow.cpp | 6 ++ src/gui/export_project_dialog.cpp | 166 +++++++++++++++++++++++------- 6 files changed, 193 insertions(+), 73 deletions(-) diff --git a/include/export_project_dialog.h b/include/export_project_dialog.h index 266b9f93d..944e5fe18 100644 --- a/include/export_project_dialog.h +++ b/include/export_project_dialog.h @@ -28,17 +28,17 @@ #define _EXPORT_PROJECT_DIALOG_H #include - +#include #include "ui_export_project.h" -class ProjectRenderer; +#include "ProjectRenderer.h" class exportProjectDialog : public QDialog, public Ui::ExportProjectDialog { Q_OBJECT public: - exportProjectDialog( const QString & _file_name, QWidget * _parent ); + exportProjectDialog( const QString & _file_name, QWidget * _parent, bool multi_export ); virtual ~exportProjectDialog(); @@ -50,12 +50,20 @@ protected: private slots: void startBtnClicked( void ); void updateTitleBar( int ); - + void render(ProjectRenderer* renderer); + void multi_render(); + ProjectRenderer* prep_render(); + void pop_render(); + void accept(); private: QString m_fileName; - ProjectRenderer * m_renderer; - + QString m_dirName; + std::vector m_renderers; + bool m_multi_export; + std::vector m_unmuted; + ProjectRenderer::ExportFileFormats m_ft; + std::vector m_to_render_vec; } ; #endif diff --git a/include/song.h b/include/song.h index 3b129714e..6eafefa90 100644 --- a/include/song.h +++ b/include/song.h @@ -207,7 +207,8 @@ public slots: void resumeFromPause(); void importProject(); - void exportProject(); + void exportProject(bool multiExport=false); + void exportProjectTracks(); void startExport(); void stopExport(); diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 37de20e76..89f3f46d3 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -35,7 +35,7 @@ #ifdef LMMS_HAVE_SCHED_H #include #endif - +#include FileEncodeDevice __fileEncodeDevices[] = { @@ -126,6 +126,7 @@ ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension( void ProjectRenderer::startProcessing() { + if( isReady() ) { // have to do mixer stuff with GUI-thread-affinity in order to @@ -139,11 +140,11 @@ void ProjectRenderer::startProcessing() QThread::HighPriority #endif ); + } } - void ProjectRenderer::run() { #if 0 @@ -157,6 +158,7 @@ void ProjectRenderer::run() #endif #endif + engine::getSong()->startExport(); song::playPos & pp = engine::getSong()->getPlayPos( diff --git a/src/core/song.cpp b/src/core/song.cpp index 8962e73e7..75f4f2ef4 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -60,7 +60,8 @@ #include "templates.h" #include "text_float.h" #include "timeline.h" - +#include +using namespace std; tick_t midiTime::s_ticksPerTact = DefaultTicksPerTact; @@ -1136,9 +1137,12 @@ void song::restoreControllerStates( const QDomElement & _this ) } +void song::exportProjectTracks() +{ + exportProject(true); +} - -void song::exportProject() +void song::exportProject(bool multiExport) { if( isEmpty() ) { @@ -1151,38 +1155,51 @@ void song::exportProject() } QFileDialog efd( engine::mainWindow() ); - efd.setFileMode( QFileDialog::AnyFile ); - efd.setAcceptMode( QFileDialog::AcceptSave ); - int idx = 0; - QStringList types; - while( __fileEncodeDevices[idx].m_fileFormat != - ProjectRenderer::NumFileFormats ) + if (multiExport) { - types << tr( __fileEncodeDevices[idx].m_description ); - ++idx; - } - efd.setFilters( types ); - - QString base_filename; - if( !m_fileName.isEmpty() ) - { - efd.setDirectory( QFileInfo( m_fileName ).absolutePath() ); - base_filename = QFileInfo( m_fileName ).completeBaseName(); + efd.setFileMode( QFileDialog::Directory); + efd.setWindowTitle( tr( "Select directory for writing exported tracks..." ) ); + if( !m_fileName.isEmpty() ) + { + efd.setDirectory( QFileInfo( m_fileName ).absolutePath() ); + } } else { - efd.setDirectory( configManager::inst()->userProjectsDir() ); - base_filename = tr( "untitled" ); + efd.setFileMode( QFileDialog::AnyFile ); + int idx = 0; + QStringList types; + while( __fileEncodeDevices[idx].m_fileFormat != + ProjectRenderer::NumFileFormats ) + { + types << tr( __fileEncodeDevices[idx].m_description ); + ++idx; + } + efd.setFilters( types ); + QString base_filename; + if( !m_fileName.isEmpty() ) + { + efd.setDirectory( QFileInfo( m_fileName ).absolutePath() ); + base_filename = QFileInfo( m_fileName ).completeBaseName(); + } + else + { + efd.setDirectory( configManager::inst()->userProjectsDir() ); + base_filename = tr( "untitled" ); + } + efd.selectFile( base_filename + __fileEncodeDevices[0].m_extension ); + efd.setWindowTitle( tr( "Select file for project-export..." ) ); } - efd.selectFile( base_filename + __fileEncodeDevices[0].m_extension ); - efd.setWindowTitle( tr( "Select file for project-export..." ) ); + + efd.setAcceptMode( QFileDialog::AcceptSave ); + if( efd.exec() == QDialog::Accepted && !efd.selectedFiles().isEmpty() && !efd.selectedFiles()[0].isEmpty() ) { const QString export_file_name = efd.selectedFiles()[0]; exportProjectDialog epd( export_file_name, - engine::mainWindow() ); + engine::mainWindow(), multiExport ); epd.exec(); } } diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index ed7397c89..c725e9499 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -258,6 +258,12 @@ void MainWindow::finalize( void ) engine::getSong(), SLOT( exportProject() ), Qt::CTRL + Qt::Key_E ); + project_menu->addAction( embed::getIconPixmap( "project_export" ), + tr( "E&xport tracks..." ), + engine::getSong(), + SLOT( exportProjectTracks() ), + Qt::CTRL + Qt::Key_E ); + project_menu->addSeparator(); project_menu->addAction( embed::getIconPixmap( "exit" ), tr( "&Quit" ), qApp, SLOT( closeAllWindows() ), diff --git a/src/gui/export_project_dialog.cpp b/src/gui/export_project_dialog.cpp index c3804eaa3..a0bb87633 100644 --- a/src/gui/export_project_dialog.cpp +++ b/src/gui/export_project_dialog.cpp @@ -23,20 +23,21 @@ */ #include +#include #include #include "export_project_dialog.h" +#include "song.h" #include "engine.h" #include "MainWindow.h" -#include "ProjectRenderer.h" exportProjectDialog::exportProjectDialog( const QString & _file_name, - QWidget * _parent ) : + QWidget * _parent, bool multi_export=false ) : QDialog( _parent ), Ui::ExportProjectDialog(), m_fileName( _file_name ), - m_renderer( NULL ) + m_multi_export(multi_export) { setupUi( this ); setWindowTitle( tr( "Export project to %1" ).arg( @@ -83,7 +84,12 @@ exportProjectDialog::exportProjectDialog( const QString & _file_name, exportProjectDialog::~exportProjectDialog() { - delete m_renderer; + + for( std::vector::const_iterator it = m_renderers.begin(); + it != m_renderers.end(); ++it ) + { + delete (*it); + } } @@ -91,13 +97,31 @@ exportProjectDialog::~exportProjectDialog() void exportProjectDialog::reject() { - if( m_renderer == NULL ) + for( std::vector::const_iterator it = m_renderers.begin(); + it != m_renderers.end(); ++it ) { - accept(); + (*it)->abortProcessing(); + } +} +void exportProjectDialog::accept() +{ + // If more to render, kick off next render job + if (m_renderers.size() > 0) + { + pop_render( ); } else { - m_renderer->abortProcessing(); + // If done, then reset mute states + while(!m_unmuted.empty()) + { + track* restore_track = m_unmuted.back(); + m_unmuted.pop_back(); + restore_track->setMuted(false); + } + + QDialog::accept(); + } } @@ -106,19 +130,106 @@ void exportProjectDialog::reject() void exportProjectDialog::closeEvent( QCloseEvent * _ce ) { - if( m_renderer != NULL && m_renderer->isRunning() ) + for( std::vector::const_iterator it = m_renderers.begin(); + it != m_renderers.end(); ++it ) { - m_renderer->abortProcessing(); + if( (*it)->isRunning() ) + { + (*it)->abortProcessing(); + } } QDialog::closeEvent( _ce ); } +void exportProjectDialog::pop_render() { + track* render_track = m_to_render_vec.back(); + m_to_render_vec.pop_back(); + for (std::vector::const_iterator it = m_unmuted.begin(); + it != m_unmuted.end(); ++it) { + if ((*it) == render_track) { + (*it)->setMuted(false); + } else { + (*it)->setMuted(true); + } + } + + // Pop next render job and start + ProjectRenderer* r = m_renderers.back(); + m_renderers.pop_back(); + render(r); +} + +void exportProjectDialog::multi_render() +{ + m_dirName = m_fileName; + QString path = QDir(m_fileName).filePath("text.txt"); + std::string strTest = path.toStdString(); + + const trackContainer::trackList & tl = engine::getSong()->tracks(); + + // Check for all unmuted tracks. Remember list. + int x = 0; + for( trackContainer::trackList::const_iterator it = tl.begin(); + it != tl.end(); ++it ) + { + // Don't mute automation tracks + if (! (*it)->isMuted() && (*it)->nodeName() != "automationtrack") + { + m_unmuted.push_back((*it)); + QString nextName = (*it)->name(); + nextName = nextName.remove(QRegExp("[^a-zA-Z]")); + QString name = QString("%1_%2.wav").arg(x++).arg(nextName); + m_fileName = QDir(m_dirName).filePath(name); + std::string strTest = m_fileName.toStdString(); + prep_render(); + } + } + + m_to_render_vec = m_unmuted; + + pop_render( ); +} + +ProjectRenderer* exportProjectDialog::prep_render( + ) { + mixer::qualitySettings qs = + mixer::qualitySettings( + static_cast(interpolationCB->currentIndex()), + static_cast(oversamplingCB->currentIndex()), + sampleExactControllersCB->isChecked(), + aliasFreeOscillatorsCB->isChecked()); + ProjectRenderer::OutputSettings os = ProjectRenderer::OutputSettings( + samplerateCB->currentText().section(" ", 0, 0).toUInt(), false, + bitrateCB->currentText().section(" ", 0, 0).toUInt(), + static_cast(depthCB->currentIndex())); + ProjectRenderer* renderer = new ProjectRenderer(qs, os, m_ft, m_fileName); + m_renderers.push_back(renderer); + return renderer; +} + +void exportProjectDialog::render(ProjectRenderer* renderer) +{ + + if (renderer->isReady()) { + connect(renderer, SIGNAL( progressChanged( int ) ), progressBar, + SLOT( setValue( int ) )); + connect(renderer, SIGNAL( progressChanged( int ) ), this, + SLOT( updateTitleBar( int ) )); + connect(renderer, SIGNAL( finished() ), this, SLOT( accept() )); + connect(renderer, SIGNAL( finished() ), engine::mainWindow(), + SLOT( resetWindowTitle() )); + + renderer->startProcessing(); + } else { + accept(); + } +} void exportProjectDialog::startBtnClicked() { - ProjectRenderer::ExportFileFormats ft = ProjectRenderer::NumFileFormats; + m_ft = ProjectRenderer::NumFileFormats; for( int i = 0; i < ProjectRenderer::NumFileFormats; ++i ) { @@ -126,12 +237,12 @@ void exportProjectDialog::startBtnClicked() ProjectRenderer::tr( __fileEncodeDevices[i].m_description ) ) { - ft = __fileEncodeDevices[i].m_fileFormat; + m_ft = __fileEncodeDevices[i].m_fileFormat; break; } } - if( ft == ProjectRenderer::NumFileFormats ) + if( m_ft == ProjectRenderer::NumFileFormats ) { QMessageBox::information( this, tr( "Error" ), tr( "Error while determining file-encoder device. " @@ -146,38 +257,13 @@ void exportProjectDialog::startBtnClicked() updateTitleBar( 0 ); - mixer::qualitySettings qs = mixer::qualitySettings( - static_cast( - interpolationCB->currentIndex() ), - static_cast( - oversamplingCB->currentIndex() ), - sampleExactControllersCB->isChecked(), - aliasFreeOscillatorsCB->isChecked() ); - - ProjectRenderer::OutputSettings os = ProjectRenderer::OutputSettings( - samplerateCB->currentText().section( " ", 0, 0 ).toUInt(), - false, - bitrateCB->currentText().section( " ", 0, 0 ).toUInt(), - static_cast( - depthCB->currentIndex() ) ); - - m_renderer = new ProjectRenderer( qs, os, ft, m_fileName ); - if( m_renderer->isReady() ) + if (m_multi_export==true) { - connect( m_renderer, SIGNAL( progressChanged( int ) ), - progressBar, SLOT( setValue( int ) ) ); - connect( m_renderer, SIGNAL( progressChanged( int ) ), - this, SLOT( updateTitleBar( int ) ) ); - connect( m_renderer, SIGNAL( finished() ), - this, SLOT( accept() ) ); - connect( m_renderer, SIGNAL( finished() ), - engine::mainWindow(), SLOT( resetWindowTitle() ) ); - - m_renderer->startProcessing(); + multi_render(); } else { - accept(); + render(prep_render()); } }