From a172783cd05ff6f6fd90fea95aed59f0b6ea4ebc Mon Sep 17 00:00:00 2001 From: Vesa Date: Fri, 13 Jun 2014 12:01:15 +0300 Subject: [PATCH 1/4] 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) From 3f19478b87bb9af2e0039444facafed78f6f083c Mon Sep 17 00:00:00 2001 From: Vesa Date: Fri, 13 Jun 2014 13:01:23 +0300 Subject: [PATCH 2/4] Further safeguards needed... --- src/core/NotePlayHandle.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 6c1b2f874..84933e431 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -119,7 +119,14 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, NotePlayHandle::~NotePlayHandle() { noteOff( 0 ); - + if( m_scheduledNoteOff >= 0 ) // ensure that scheduled noteoffs get triggered if somehow the nph got destructed prematurely + { + m_instrumentTrack->processOutEvent( + MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ), + MidiTime::fromFrames( m_scheduledNoteOff, engine::framesPerTick() ), + m_scheduledNoteOff ); + } + if( hasParent() == false ) { delete m_baseDetuning; @@ -199,7 +206,7 @@ void NotePlayHandle::play( sampleFrame * _working_buffer ) if( m_released == false && instrumentTrack()->isSustainPedalPressed() == false && - m_totalFramesPlayed + engine::mixer()->framesPerPeriod() >= m_frames ) + m_totalFramesPlayed + engine::mixer()->framesPerPeriod() > m_frames ) { noteOff( m_frames - m_totalFramesPlayed ); } From d1eb9886fd810807cf55df40813b986c92019ca9 Mon Sep 17 00:00:00 2001 From: Vesa Date: Fri, 13 Jun 2014 13:49:14 +0300 Subject: [PATCH 3/4] Make that 4: Fix stuck midi notes on changing master pitch --- include/InstrumentTrack.h | 2 ++ src/core/NotePlayHandle.cpp | 4 ++-- src/tracks/InstrumentTrack.cpp | 7 ++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index b09ecaa6f..20200e1a7 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -160,6 +160,8 @@ public: { return &m_baseNoteModel; } + + int baseNote() const; Piano *pianoModel() { diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 84933e431..8806d65e3 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -68,7 +68,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, m_muted( false ), m_bbTrack( NULL ), m_origTempo( engine::getSong()->getTempo() ), - m_origBaseNote( instrumentTrack->baseNoteModel()->value() ), + m_origBaseNote( instrumentTrack->baseNote() ), m_frequency( 0 ), m_unpitchedFrequency( 0 ), m_baseDetuning( NULL ), @@ -182,7 +182,7 @@ void NotePlayHandle::setPanning( panning_t panning ) int NotePlayHandle::midiKey() const { - return key() - m_origBaseNote + instrumentTrack()->baseNoteModel()->value(); + return key() - m_origBaseNote + instrumentTrack()->baseNote(); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 532431e6c..e661f4f4c 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -135,6 +135,11 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) : } +int InstrumentTrack::baseNote() const +{ + return m_baseNoteModel.value() - engine::getSong()->masterPitch(); +} + InstrumentTrack::~InstrumentTrack() @@ -537,7 +542,7 @@ void InstrumentTrack::updatePitchRange() int InstrumentTrack::masterKey( int _midi_key ) const { - int key = m_baseNoteModel.value() - engine::getSong()->masterPitch(); + int key = baseNote(); return tLimit( _midi_key - ( key - DefaultKey ), 0, NumKeys ); } From 9c584778d2076dd379bf639268da6139bd1cd1f9 Mon Sep 17 00:00:00 2001 From: Vesa Date: Fri, 13 Jun 2014 15:40:58 +0300 Subject: [PATCH 4/4] 5th: fix #563 - do not destroy IPH's on "All Notes Off" MIDI commands --- include/InstrumentTrack.h | 2 +- include/Mixer.h | 2 +- src/core/Mixer.cpp | 4 ++-- src/tracks/InstrumentTrack.cpp | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 20200e1a7..ea10447c5 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -74,7 +74,7 @@ public: virtual void processInEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ); virtual void processOutEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ); // silence all running notes played by this track - void silenceAllNotes(); + void silenceAllNotes( bool removeIPH = false ); bool isSustainPedalPressed() const { diff --git a/include/Mixer.h b/include/Mixer.h index 9a20cae5a..7007e1801 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -229,7 +229,7 @@ public: return m_playHandles; } - void removePlayHandles( track * _track ); + void removePlayHandles( track * _track, bool removeIPHs = true ); bool hasNotePlayHandles(); diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 9c35ee852..c9fd9b83c 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -686,13 +686,13 @@ void Mixer::removePlayHandle( PlayHandle * _ph ) -void Mixer::removePlayHandles( track * _track ) +void Mixer::removePlayHandles( track * _track, bool removeIPHs ) { lock(); PlayHandleList::Iterator it = m_playHandles.begin(); while( it != m_playHandles.end() ) { - if( ( *it )->isFromTrack( _track ) ) + if( ( *it )->isFromTrack( _track ) && ( removeIPHs || ( *it )->type() != PlayHandle::TypeInstrumentPlayHandle ) ) { delete *it; it = m_playHandles.erase( it ); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index e661f4f4c..be2de2497 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -144,8 +144,8 @@ int InstrumentTrack::baseNote() const InstrumentTrack::~InstrumentTrack() { - // kill all running notes - silenceAllNotes(); + // kill all running notes and the iph + silenceAllNotes( true ); // now we're save deleting the instrument delete m_instrument; @@ -402,7 +402,7 @@ void InstrumentTrack::processOutEvent( const MidiEvent& event, const MidiTime& t -void InstrumentTrack::silenceAllNotes() +void InstrumentTrack::silenceAllNotes( bool removeIPH ) { engine::mixer()->lock(); for( int i = 0; i < NumKeys; ++i ) @@ -413,7 +413,7 @@ void InstrumentTrack::silenceAllNotes() // invalidate all NotePlayHandles linked to this track m_processHandles.clear(); - engine::mixer()->removePlayHandles( this ); + engine::mixer()->removePlayHandles( this, removeIPH ); engine::mixer()->unlock(); } @@ -700,7 +700,7 @@ void InstrumentTrack::saveTrackSpecificSettings( QDomDocument& doc, QDomElement void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement ) { - silenceAllNotes(); + silenceAllNotes( true ); engine::mixer()->lock(); @@ -776,7 +776,7 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement Instrument * InstrumentTrack::loadInstrument( const QString & _plugin_name ) { - silenceAllNotes(); + silenceAllNotes( true ); engine::mixer()->lock(); delete m_instrument;