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 <tobias.doerffel@gmail.com>
This commit is contained in:
Devin Venable
2012-10-29 17:26:08 -05:00
committed by Tobias Doerffel
parent 69947a624b
commit eb60d9e06e
6 changed files with 193 additions and 73 deletions

View File

@@ -28,17 +28,17 @@
#define _EXPORT_PROJECT_DIALOG_H
#include <QtGui/QDialog>
#include <vector>
#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<ProjectRenderer*> m_renderers;
bool m_multi_export;
std::vector<track*> m_unmuted;
ProjectRenderer::ExportFileFormats m_ft;
std::vector<track*> m_to_render_vec;
} ;
#endif

View File

@@ -207,7 +207,8 @@ public slots:
void resumeFromPause();
void importProject();
void exportProject();
void exportProject(bool multiExport=false);
void exportProjectTracks();
void startExport();
void stopExport();

View File

@@ -35,7 +35,7 @@
#ifdef LMMS_HAVE_SCHED_H
#include <sched.h>
#endif
#include <QMutexLocker>
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(

View File

@@ -60,7 +60,8 @@
#include "templates.h"
#include "text_float.h"
#include "timeline.h"
#include <vector>
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();
}
}

View File

@@ -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() ),

View File

@@ -23,20 +23,21 @@
*/
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtGui/QMessageBox>
#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<ProjectRenderer*>::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<ProjectRenderer*>::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<ProjectRenderer*>::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<track*>::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<mixer::qualitySettings::Interpolation>(interpolationCB->currentIndex()),
static_cast<mixer::qualitySettings::Oversampling>(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<ProjectRenderer::Depths>(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<mixer::qualitySettings::Interpolation>(
interpolationCB->currentIndex() ),
static_cast<mixer::qualitySettings::Oversampling>(
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<ProjectRenderer::Depths>(
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());
}
}