From a172783cd05ff6f6fd90fea95aed59f0b6ea4ebc Mon Sep 17 00:00:00 2001 From: Vesa Date: Fri, 13 Jun 2014 12:01:15 +0300 Subject: [PATCH] 3-in-1 bugfix special Fixes bugs: #568, #289 and the incorrect timing for midi-noteoffs (see mailing list) --- include/NotePlayHandle.h | 3 ++- src/core/NotePlayHandle.cpp | 34 ++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index d2a5170b7..c564b1fc8 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -94,7 +94,7 @@ public: /*! 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 */ @@ -264,6 +264,7 @@ private: // 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; diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index db535474a..6c1b2f874 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -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 ), @@ -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(); @@ -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; @@ -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; } @@ -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( 0, m_instrumentTrack->m_soundShaping.releaseFrames() ); + m_releaseFramesToDo = qMax( 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)