Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rendering looped sections multiple times on export (#4624) #4639

Merged
merged 8 commits into from
Jan 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions include/Song.h
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -185,22 +185,14 @@ 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;

// 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
Original file line number Diff line number Diff line change
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 @@ -478,28 +482,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 @@ -721,15 +737,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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found that m_timeLine is null if LMMS runs without GUI. As a consequence, this breaks the command-line rendering.
I think we can work around this by not setting m_exportLoopBegin and m_exportLoopEnd when the timeline is null because we don't use that in CLI renders.

FYI, that check is not needed elsewhere because LMMS doesn't support "export between loop markers" feature on the command line(for a similar reason).


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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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