From b68c5ee5b56c7f0194a21458f879e78fd9dd4225 Mon Sep 17 00:00:00 2001 From: Steven Christy Date: Sun, 27 Jan 2019 03:22:47 -0600 Subject: [PATCH] Rendering looped sections multiple times on export (#4624) (#4639) --- include/Song.h | 27 +++++++++-- src/core/ProjectRenderer.cpp | 14 ++---- src/core/Song.cpp | 74 ++++++++++++++++++++++++------- src/gui/ExportProjectDialog.cpp | 2 + src/gui/dialogs/export_project.ui | 47 ++++++++++++++++++-- 5 files changed, 130 insertions(+), 34 deletions(-) diff --git a/include/Song.h b/include/Song.h index 7ab1f54f6..d88a59e2b 100644 --- a/include/Song.h +++ b/include/Song.h @@ -103,8 +103,6 @@ public: } ; - - void processNextBuffer(); inline int getLoadingTrackCount() const @@ -203,9 +201,23 @@ public: { return m_recording; } + + inline void setLoopRenderCount(int count) + { + if (count < 1) + m_loopRenderCount = 1; + else + m_loopRenderCount = count; + m_loopRenderRemaining = m_loopRenderCount; + } + + inline int getLoopRenderCount() const + { + return m_loopRenderCount; + } bool isExportDone() const; - std::pair getExportEndpoints() const; + int getExportProgress() const; inline void setRenderBetweenMarkers( bool renderBetweenMarkers ) { @@ -424,7 +436,14 @@ private: tact_t m_elapsedTacts; VstSyncController m_vstSyncController; - + + int m_loopRenderCount; + int m_loopRenderRemaining; + MidiTime m_exportSongBegin; + MidiTime m_exportLoopBegin; + MidiTime m_exportLoopEnd; + MidiTime m_exportSongEnd; + MidiTime m_exportEffectiveLength; friend class LmmsCore; friend class SongEditor; diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 57975356e..71a5aaff8 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -185,25 +185,17 @@ void ProjectRenderer::run() // Skip first empty buffer. Engine::mixer()->nextBuffer(); - const Song::PlayPos & exportPos = Engine::getSong()->getPlayPos( - Song::Mode_PlaySong ); m_progress = 0; - std::pair exportEndpoints = Engine::getSong()->getExportEndpoints(); - tick_t startTick = exportEndpoints.first.getTicks(); - tick_t endTick = exportEndpoints.second.getTicks(); - tick_t lengthTicks = endTick - startTick; // Now start processing Engine::mixer()->startProcessing(false); // Continually track and emit progress percentage to listeners. - while( exportPos.getTicks() < endTick && - Engine::getSong()->isExporting() == true - && !m_abort ) + while (!Engine::getSong()->isExportDone() && !m_abort) { m_fileDev->processNextBuffer(); - const int nprog = lengthTicks == 0 ? 100 : (exportPos.getTicks()-startTick) * 100 / lengthTicks; - if( m_progress != nprog ) + const int nprog = Engine::getSong()->getExportProgress(); + if (m_progress != nprog) { m_progress = nprog; emit progressChanged( m_progress ); diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 6f060664e..a80b0d8c7 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -86,7 +86,9 @@ Song::Song() : m_patternToPlay( NULL ), m_loopPattern( false ), m_elapsedTicks( 0 ), - m_elapsedTacts( 0 ) + m_elapsedTacts( 0 ), + m_loopRenderCount(1), + m_loopRenderRemaining(1) { for(int i = 0; i < Mode_Count; ++i) m_elapsedMilliSeconds[i] = 0; connect( &m_tempoModel, SIGNAL( dataChanged() ), @@ -330,7 +332,7 @@ void Song::processNextBuffer() } m_playPos[m_playMode].setTicks( ticks ); - if( checkLoop ) + if (checkLoop || m_loopRenderRemaining > 1) { m_vstSyncController.startCycle( tl->loopBegin().getTicks(), tl->loopEnd().getTicks() ); @@ -340,6 +342,8 @@ void Song::processNextBuffer() // beginning of the range if( m_playPos[m_playMode] >= tl->loopEnd() ) { + if (m_loopRenderRemaining > 1) + m_loopRenderRemaining--; ticks = tl->loopBegin().getTicks(); m_playPos[m_playMode].setTicks( ticks ); setToTime(tl->loopBegin()); @@ -476,29 +480,41 @@ void Song::setModified(bool value) } } -std::pair Song::getExportEndpoints() const +bool Song::isExportDone() const { - if ( m_renderBetweenMarkers ) + return !isExporting() || m_playPos[m_playMode] >= m_exportSongEnd; +} + +int Song::getExportProgress() const +{ + MidiTime pos = m_playPos[m_playMode]; + + if (pos >= m_exportSongEnd) { - return std::pair( - m_playPos[Mode_PlaySong].m_timeLine->loopBegin(), - m_playPos[Mode_PlaySong].m_timeLine->loopEnd() - ); + return 100; } - else if ( m_exportLoop ) + else if (pos <= m_exportSongBegin) { - return std::pair( MidiTime(0, 0), MidiTime(m_length, 0) ); + return 0; + } + else if (pos >= m_exportLoopEnd) + { + pos = (m_exportLoopBegin-m_exportSongBegin) + (m_exportLoopEnd - m_exportLoopBegin) * + m_loopRenderCount + (pos - m_exportLoopEnd); + } + else if ( pos >= m_exportLoopBegin ) + { + pos = (m_exportLoopBegin-m_exportSongBegin) + ((m_exportLoopEnd - m_exportLoopBegin) * + (m_loopRenderCount - m_loopRenderRemaining)) + (pos - m_exportLoopBegin); } else { - // if not exporting as a loop, we leave one bar of padding at the end of the song to accomodate reverb, etc. - return std::pair( MidiTime(0, 0), MidiTime(m_length+1, 0) ); + pos = (pos - m_exportSongBegin); } + + return (float)pos/(float)m_exportEffectiveLength*100.0f; } - - - void Song::playSong() { m_recording = false; @@ -719,15 +735,41 @@ void Song::stop() void Song::startExport() { stop(); - if(m_renderBetweenMarkers) + if (m_renderBetweenMarkers) { + m_exportSongBegin = m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin(); + m_exportSongEnd = m_exportLoopEnd = m_playPos[Mode_PlaySong].m_timeLine->loopEnd(); + m_playPos[Mode_PlaySong].setTicks( m_playPos[Mode_PlaySong].m_timeLine->loopBegin().getTicks() ); } else { + m_exportSongEnd = MidiTime(m_length, 0); + + // Handle potentially ridiculous loop points gracefully. + if (m_loopRenderCount > 1 && m_playPos[Mode_PlaySong].m_timeLine->loopEnd() > m_exportSongEnd) + { + m_exportSongEnd = m_playPos[Mode_PlaySong].m_timeLine->loopEnd(); + } + + if (!m_exportLoop) + m_exportSongEnd += MidiTime(1,0); + + m_exportSongBegin = MidiTime(0,0); + m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && + m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? + m_playPos[Mode_PlaySong].m_timeLine->loopBegin() : MidiTime(0,0); + m_exportLoopEnd = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && + m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? + m_playPos[Mode_PlaySong].m_timeLine->loopEnd() : MidiTime(0,0); + m_playPos[Mode_PlaySong].setTicks( 0 ); } + m_exportEffectiveLength = (m_exportLoopBegin - m_exportSongBegin) + (m_exportLoopEnd - m_exportLoopBegin) + * m_loopRenderCount + (m_exportSongEnd - m_exportLoopEnd); + m_loopRenderRemaining = m_loopRenderCount; + playSong(); m_exporting = true; diff --git a/src/gui/ExportProjectDialog.cpp b/src/gui/ExportProjectDialog.cpp index b50c9941b..f3b432f99 100644 --- a/src/gui/ExportProjectDialog.cpp +++ b/src/gui/ExportProjectDialog.cpp @@ -128,6 +128,7 @@ void ExportProjectDialog::accept() void ExportProjectDialog::closeEvent( QCloseEvent * _ce ) { + Engine::getSong()->setLoopRenderCount(1); if( m_renderManager ) { m_renderManager->abortProcessing(); } @@ -187,6 +188,7 @@ void ExportProjectDialog::startExport() Engine::getSong()->setExportLoop( exportLoopCB->isChecked() ); Engine::getSong()->setRenderBetweenMarkers( renderMarkersCB->isChecked() ); + Engine::getSong()->setLoopRenderCount(loopCountSB->value()); connect( m_renderManager.get(), SIGNAL( progressChanged( int ) ), progressBar, SLOT( setValue( int ) ) ); diff --git a/src/gui/dialogs/export_project.ui b/src/gui/dialogs/export_project.ui index 1ec4fe123..6b175de78 100644 --- a/src/gui/dialogs/export_project.ui +++ b/src/gui/dialogs/export_project.ui @@ -7,19 +7,19 @@ 0 0 379 - 374 + 400 379 - 374 + 400 379 - 374 + 400 @@ -40,6 +40,47 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Render Looped Section: + + + + + + + time(s) + + + 1 + + + 99 + + + 1 + + + + + +