diff --git a/include/RenderManager.h b/include/RenderManager.h new file mode 100644 index 000000000..2bd35d3b6 --- /dev/null +++ b/include/RenderManager.h @@ -0,0 +1,76 @@ +/* + * RenderManager.h - Provides a uniform interface for rendering the project or + * individual tracks for the CLI and GUI. + * + * Copyright (c) 2015 Ryan Roden-Corrent + * + * This file is part of LMMS - http://lmms.io + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef RENDER_MANAGER_H +#define RENDER_MANAGER_H + +#include + +#include "ProjectRenderer.h" + +class RenderManager : public QObject +{ + Q_OBJECT +public: + RenderManager( + const Mixer::qualitySettings & qualitySettings, + const ProjectRenderer::OutputSettings & outputSettings, + ProjectRenderer::ExportFileFormats fmt, + QString outputPath); + + virtual ~RenderManager(); + + /// Export all unmuted tracks into a single file + void renderProject(); + + /// Export all unmuted tracks into individual file + void renderTracks(); + + void abortProcessing(); + +signals: + void progressChanged( int ); + void finished(); + +private slots: + void renderNextTrack(); + void updateConsoleProgress(); + +private: + QString pathForTrack( const Track *track, int num ); + void restoreMutedState(); + + const Mixer::qualitySettings m_qualitySettings; + const ProjectRenderer::OutputSettings m_outputSettings; + ProjectRenderer::ExportFileFormats m_format; + QString m_outputPath; + + ProjectRenderer* m_activeRenderer; + + QVector m_tracksToRender; + QVector m_unmuted; +} ; + +#endif diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index adc6ef3a9..082951d34 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -51,6 +51,7 @@ set(LMMS_SRCS core/ProjectRenderer.cpp core/ProjectVersion.cpp core/RemotePlugin.cpp + core/RenderManager.cpp core/RingBuffer.cpp core/SampleBuffer.cpp core/SamplePlayHandle.cpp diff --git a/src/core/RenderManager.cpp b/src/core/RenderManager.cpp new file mode 100644 index 000000000..2808aa39d --- /dev/null +++ b/src/core/RenderManager.cpp @@ -0,0 +1,214 @@ +/* + * RenderManager - exporting logic common between the CLI and GUI. + * + * Copyright (c) 2015 Ryan Roden-Corrent + * + * This file is part of LMMS - http://lmms.io + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include + +#include "RenderManager.h" +#include "Song.h" +#include "BBTrackContainer.h" +#include "BBTrack.h" +#include "debug.h" + +RenderManager::RenderManager( + const Mixer::qualitySettings & qualitySettings, + const ProjectRenderer::OutputSettings & outputSettings, + ProjectRenderer::ExportFileFormats fmt, + QString outputPath) : + m_qualitySettings(qualitySettings), + m_outputSettings(outputSettings), + m_format(fmt), + m_outputPath(outputPath), + m_activeRenderer(NULL) +{ +} + +RenderManager::~RenderManager() +{ + delete m_activeRenderer; +} + +void RenderManager::abortProcessing() +{ + if ( m_activeRenderer ) { + m_activeRenderer->abortProcessing(); + } + restoreMutedState(); +} + +// Called to render each new track when rendering tracks individually. +void RenderManager::renderNextTrack() +{ + delete m_activeRenderer; + m_activeRenderer = NULL; + + if( m_tracksToRender.isEmpty() ) + { + // nothing left to render + restoreMutedState(); + emit finished(); + } + else + { + // pop the next track from our rendering queue + Track* renderTrack = m_tracksToRender.back(); + m_tracksToRender.pop_back(); + + // mute everything but the track we are about to render + for( auto it = m_unmuted.begin(); it != m_unmuted.end(); ++it ) + { + (*it)->setMuted( (*it) != renderTrack ); + } + + // for multi-render, prefix each output file with a different number + int trackNum = m_tracksToRender.size() + 1; + + // create a renderer for this track + m_activeRenderer = new ProjectRenderer( + m_qualitySettings, + m_outputSettings, + m_format, + pathForTrack(renderTrack, trackNum)); + + if ( m_activeRenderer->isReady() ) + { + // pass progress signals through + connect( m_activeRenderer, SIGNAL( progressChanged( int ) ), + this, SIGNAL( progressChanged( int ) ) ); + + // when it is finished, render the next track + connect( m_activeRenderer, SIGNAL( finished() ), + this, SLOT( renderNextTrack() ) ); + + m_activeRenderer->startProcessing(); + } + else + { + qDebug( "Renderer failed to acquire a file device!" ); + renderNextTrack(); + } + } +} + +// Render the song into individual tracks +void RenderManager::renderTracks() +{ + const TrackContainer::TrackList & tl = Engine::getSong()->tracks(); + + // find all currently unnmuted tracks -- we want to render these. + for( auto it = tl.begin(); it != tl.end(); ++it ) + { + Track* tk = (*it); + Track::TrackTypes type = tk->type(); + + // Don't mute automation tracks + if ( tk->isMuted() == false && + ( type == Track::InstrumentTrack || type == Track::SampleTrack ) ) + { + m_unmuted.push_back(tk); + } + } + + const TrackContainer::TrackList t2 = Engine::getBBTrackContainer()->tracks(); + for( auto it = t2.begin(); it != t2.end(); ++it ) + { + Track* tk = (*it); + if ( tk->isMuted() == false ) + { + m_unmuted.push_back(tk); + } + } + + // copy the list of unmuted tracks into our rendering queue. + // we need to remember which tracks were unmuted to restore state at the end. + m_tracksToRender = m_unmuted; + + renderNextTrack(); +} + +// Render the song into a single track +void RenderManager::renderProject() +{ + m_activeRenderer = new ProjectRenderer( + m_qualitySettings, + m_outputSettings, + m_format, + m_outputPath); + + if( m_activeRenderer->isReady() ) + { + // pass progress signals through + connect( m_activeRenderer, SIGNAL( progressChanged( int ) ), + this, SIGNAL( progressChanged( int ) ) ); + + // as we have not queued any tracks, renderNextTrack will just clean up + connect( m_activeRenderer, SIGNAL( finished() ), + this, SLOT( renderNextTrack() ) ); + + m_activeRenderer->startProcessing(); + } + else + { + qDebug( "Renderer failed to acquire a file device!" ); + emit finished(); + } +} + +// Unmute all tracks that were muted while rendering tracks +void RenderManager::restoreMutedState() +{ + while( !m_unmuted.isEmpty() ) + { + Track* restoreTrack = m_unmuted.back(); + m_unmuted.pop_back(); + restoreTrack->setMuted( false ); + } +} + +// Determine the output path for a track when rendering tracks individually +QString RenderManager::pathForTrack(const Track *track, int num) +{ + QString extension = ProjectRenderer::getFileExtensionFromFormat( m_format ); + QString name = track->name(); + name = name.remove(QRegExp("[^a-zA-Z]")); + name = QString( "%1_%2%3" ).arg( num ).arg( name ).arg( extension ); + return QDir(m_outputPath).filePath(name); +} + +void RenderManager::updateConsoleProgress() +{ + if ( m_activeRenderer ) + { + m_activeRenderer->updateConsoleProgress(); + + int totalNum = m_unmuted.size(); + if ( totalNum > 0 ) + { + // we are rendering multiple tracks, append a track counter to the output + int trackNum = totalNum - m_tracksToRender.size(); + fprintf( stderr, "(%d/%d)", trackNum, totalNum ); + } + } +}