Skip to content

Commit

Permalink
Rendering looped sections multiple times on export (#4624) (#4639)
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenChristy authored and zonkmachine committed Jan 27, 2019
1 parent 5126070 commit b68c5ee
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 34 deletions.
27 changes: 23 additions & 4 deletions include/Song.h
Expand Up @@ -103,8 +103,6 @@ class LMMS_EXPORT Song : public TrackContainer

} ;



void processNextBuffer();

inline int getLoadingTrackCount() const
Expand Down Expand Up @@ -203,9 +201,23 @@ class LMMS_EXPORT Song : public TrackContainer
{
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<MidiTime, MidiTime> getExportEndpoints() const;
int getExportProgress() const;

inline void setRenderBetweenMarkers( bool renderBetweenMarkers )
{
Expand Down Expand Up @@ -424,7 +436,14 @@ private slots:
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;
Expand Down
14 changes: 3 additions & 11 deletions src/core/ProjectRenderer.cpp
Expand Up @@ -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<MidiTime, MidiTime> 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 );
Expand Down
74 changes: 58 additions & 16 deletions src/core/Song.cpp
Expand Up @@ -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() ),
Expand Down Expand Up @@ -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() );
Expand All @@ -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());
Expand Down Expand Up @@ -476,28 +480,40 @@ void Song::setModified(bool value)
}
}

std::pair<MidiTime, MidiTime> 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 100;
}
else if (pos <= m_exportSongBegin)
{
return std::pair<MidiTime, MidiTime>(
m_playPos[Mode_PlaySong].m_timeLine->loopBegin(),
m_playPos[Mode_PlaySong].m_timeLine->loopEnd()
);
return 0;
}
else if ( m_exportLoop )
else if (pos >= m_exportLoopEnd)
{
return std::pair<MidiTime, MidiTime>( MidiTime(0, 0), MidiTime(m_length, 0) );
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, MidiTime>( MidiTime(0, 0), MidiTime(m_length+1, 0) );
pos = (pos - m_exportSongBegin);
}
}



return (float)pos/(float)m_exportEffectiveLength*100.0f;
}

void Song::playSong()
{
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/gui/ExportProjectDialog.cpp
Expand Up @@ -128,6 +128,7 @@ void ExportProjectDialog::accept()

void ExportProjectDialog::closeEvent( QCloseEvent * _ce )
{
Engine::getSong()->setLoopRenderCount(1);
if( m_renderManager ) {
m_renderManager->abortProcessing();
}
Expand Down Expand Up @@ -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 ) ) );
Expand Down
47 changes: 44 additions & 3 deletions src/gui/dialogs/export_project.ui
Expand Up @@ -7,19 +7,19 @@
<x>0</x>
<y>0</y>
<width>379</width>
<height>374</height>
<height>400</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>379</width>
<height>374</height>
<height>400</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>379</width>
<height>374</height>
<height>400</height>
</size>
</property>
<property name="windowTitle">
Expand All @@ -40,6 +40,47 @@
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="loopRepeatWidget" native="true">
<layout class="QHBoxLayout" name="loopRepeatHL">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelLoopRepeat">
<property name="text">
<string>Render Looped Section:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="loopCountSB">
<property name="suffix">
<string> time(s)</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>99</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout">
<item>
Expand Down

0 comments on commit b68c5ee

Please sign in to comment.