mirror of
https://github.com/LMMS/lmms.git
synced 2026-03-07 15:46:46 -05:00
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:
committed by
Tobias Doerffel
parent
69947a624b
commit
eb60d9e06e
@@ -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
|
||||
|
||||
@@ -207,7 +207,8 @@ public slots:
|
||||
void resumeFromPause();
|
||||
|
||||
void importProject();
|
||||
void exportProject();
|
||||
void exportProject(bool multiExport=false);
|
||||
void exportProjectTracks();
|
||||
|
||||
void startExport();
|
||||
void stopExport();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() ),
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user