Revision of handling of frameoffset for NPH, SPH

Change in handling of frameoffset for multistreamed instruments and sampletracks.
- Instead of holding the offset for the lifetime of the playhandle, negate the offset in the first period
- Multistream-instruments require some small changes: they have to now check for the offset and accordingly leave empty space in the start of the period (already done in this commit)
- There are possibly optimizations that can be done later
- This change is necessary so that we can have sample-exact models, and sample-exact vol/pan knobs for all instruments. Earlier multistream instruments were always rendering some frames ahead-of-time, so applying sample-exact data for them would have been impossible, since we don't have the future-values yet...
This commit is contained in:
Vesa
2014-06-29 23:13:00 +03:00
parent a7bb31159e
commit 270af579b8
23 changed files with 137 additions and 124 deletions

View File

@@ -61,7 +61,6 @@ 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 ),
@@ -119,13 +118,6 @@ 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 )
{
@@ -190,23 +182,20 @@ 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;
}
// number of frames that can be played this period
f_cnt_t framesThisPeriod = m_totalFramesPlayed == 0
? engine::mixer()->framesPerPeriod() - offset()
: engine::mixer()->framesPerPeriod();
// check if we start release during this period
if( m_released == false &&
instrumentTrack()->isSustainPedalPressed() == false &&
m_totalFramesPlayed + engine::mixer()->framesPerPeriod() > m_frames )
m_totalFramesPlayed + framesThisPeriod > m_frames )
{
noteOff( m_frames - m_totalFramesPlayed );
}
@@ -216,13 +205,18 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
// decreasing release of an instrument-track while the note is active
if( framesLeft() > 0 )
{
// clear offset frames if we're at the first period
if( m_totalFramesPlayed == 0 )
{
memset( _working_buffer, 0, sizeof( sampleFrame ) * offset() );
}
// play note!
m_instrumentTrack->playNote( this, _working_buffer );
}
if( m_released )
{
f_cnt_t todo = engine::mixer()->framesPerPeriod();
f_cnt_t todo = framesThisPeriod;
// if this note is base-note for arpeggio, always set
// m_releaseFramesToDo to bigger value than m_releaseFramesDone
@@ -238,8 +232,7 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
{
// yes, then look whether these samples can be played
// within one audio-buffer
if( m_framesBeforeRelease <=
engine::mixer()->framesPerPeriod() )
if( m_framesBeforeRelease <= framesThisPeriod )
{
// yes, then we did less releaseFramesDone
todo -= m_framesBeforeRelease;
@@ -251,8 +244,7 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
// and wait for next loop... (we're not in
// release-phase yet)
todo = 0;
m_framesBeforeRelease -=
engine::mixer()->framesPerPeriod();
m_framesBeforeRelease -= framesThisPeriod;
}
}
// look whether we're in release-phase
@@ -290,7 +282,7 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
}
// update internal data
m_totalFramesPlayed += engine::mixer()->framesPerPeriod();
m_totalFramesPlayed += framesThisPeriod;
}
@@ -318,6 +310,10 @@ f_cnt_t NotePlayHandle::framesLeft() const
fpp_t NotePlayHandle::framesLeftForCurrentPeriod() const
{
if( m_totalFramesPlayed == 0 )
{
return (fpp_t) qMin<f_cnt_t>( framesLeft(), engine::mixer()->framesPerPeriod() - offset() );
}
return (fpp_t) qMin<f_cnt_t>( framesLeft(), engine::mixer()->framesPerPeriod() );
}
@@ -352,18 +348,10 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() )
{
// send MidiNoteOff event
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(
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();
}
MidiTime::fromFrames( _s, engine::framesPerTick() ),
_s );
}
// inform attached components about MIDI finished (used for recording in Piano Roll)