Skip to content

Commit

Permalink
3-in-1 bugfix special
Browse files Browse the repository at this point in the history
Fixes bugs: LMMS#568, LMMS#289 and the incorrect timing for midi-noteoffs (see mailing list)
  • Loading branch information
diizy committed Jun 14, 2014
1 parent 32cfe84 commit a172783
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 9 deletions.
3 changes: 2 additions & 1 deletion include/NotePlayHandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class EXPORT NotePlayHandle : public PlayHandle, public note
/*! Returns whether playback of note is finished and thus handle can be deleted */
virtual bool isFinished() const
{
return m_released && framesLeft() <= 0;
return m_released && framesLeft() <= 0 && m_scheduledNoteOff < 0;
}

/*! Returns number of frames left for playback */
Expand Down Expand Up @@ -264,6 +264,7 @@ class EXPORT NotePlayHandle : public PlayHandle, public note
// played after release
f_cnt_t m_releaseFramesDone; // number of frames done after
// release of note
f_cnt_t m_scheduledNoteOff; // variable for scheduling noteoff at next period
NotePlayHandleList m_subNotes; // used for chords and arpeggios
volatile bool m_released; // indicates whether note is released
bool m_hasParent;
Expand Down
34 changes: 26 additions & 8 deletions src/core/NotePlayHandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
m_framesBeforeRelease( 0 ),
m_releaseFramesToDo( 0 ),
m_releaseFramesDone( 0 ),
m_scheduledNoteOff( -1 ),
m_released( false ),
m_hasParent( parent != NULL ),
m_hadChildren( false ),
Expand Down Expand Up @@ -100,7 +101,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
m_instrumentTrack->midiNoteOn( *this );
}

if( !isMasterNote() || !instrumentTrack->isArpeggioEnabled() )
if( hasParent() || !instrumentTrack->isArpeggioEnabled() )
{
const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();

Expand Down Expand Up @@ -182,6 +183,15 @@ int NotePlayHandle::midiKey() const

void NotePlayHandle::play( sampleFrame * _working_buffer )
{
if( m_scheduledNoteOff >= 0 ) // always trigger scheduled noteoffs, because they're only scheduled if the note is released
{
m_instrumentTrack->processOutEvent(
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
MidiTime::fromFrames( m_scheduledNoteOff, engine::framesPerTick() ),
m_scheduledNoteOff );
m_scheduledNoteOff = -1;
}

if( m_muted )
{
return;
Expand Down Expand Up @@ -289,9 +299,9 @@ f_cnt_t NotePlayHandle::framesLeft() const
{
return m_framesBeforeRelease;
}
else if( m_released && actualReleaseFramesToDo() >= m_releaseFramesDone )
else if( m_released )
{
return m_framesBeforeRelease + actualReleaseFramesToDo() - m_releaseFramesDone;
return m_framesBeforeRelease + m_releaseFramesToDo - m_releaseFramesDone;
}
return m_frames+actualReleaseFramesToDo()-m_totalFramesPlayed;
}
Expand Down Expand Up @@ -330,15 +340,23 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )

// then set some variables indicating release-state
m_framesBeforeRelease = _s;
m_releaseFramesToDo = qMax<f_cnt_t>( 0, m_instrumentTrack->m_soundShaping.releaseFrames() );
m_releaseFramesToDo = qMax<f_cnt_t>( 0, actualReleaseFramesToDo() );

if( hasParent() || !instrumentTrack()->isArpeggioEnabled() )
{
// send MidiNoteOff event
m_instrumentTrack->processOutEvent(
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
MidiTime::fromFrames( m_framesBeforeRelease, engine::framesPerTick() ),
_s );
f_cnt_t realOffset = offset() + _s; // get actual frameoffset of release, in global time
if( realOffset < engine::mixer()->framesPerPeriod() ) // if release happens during this period, trigger midievent
{
m_instrumentTrack->processOutEvent(
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),
MidiTime::fromFrames( realOffset, engine::framesPerTick() ),
realOffset );
}
else // if release flows over to next period, use m_scheduledNoteOff to trigger it later
{
m_scheduledNoteOff = realOffset - engine::mixer()->framesPerPeriod();
}
}

// inform attached components about MIDI finished (used for recording in Piano Roll)
Expand Down

0 comments on commit a172783

Please sign in to comment.