From 270af579b8572ebef22ecc3c10a36d019ca005fe Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 29 Jun 2014 23:13:00 +0300 Subject: [PATCH] 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... --- include/Mixer.h | 1 - include/NotePlayHandle.h | 13 ++++- include/PlayHandle.h | 4 +- include/SamplePlayHandle.h | 6 +- .../audio_file_processor.cpp | 5 +- plugins/bit_invader/bit_invader.cpp | 5 +- plugins/kicker/kicker.cpp | 12 ++-- plugins/monstro/Monstro.cpp | 6 +- plugins/nes/Nes.cpp | 9 +-- plugins/organic/organic.cpp | 11 ++-- plugins/papu/papu_instrument.cpp | 5 +- plugins/patman/patman.cpp | 5 +- plugins/sfxr/sfxr.cpp | 5 +- plugins/sid/sid_instrument.cpp | 5 +- plugins/stk/mallets/mallets.cpp | 5 +- .../triple_oscillator/TripleOscillator.cpp | 7 ++- plugins/vibed/vibed.cpp | 5 +- plugins/watsyn/Watsyn.cpp | 12 ++-- src/core/InstrumentSoundShaping.cpp | 2 +- src/core/Mixer.cpp | 50 ++++++++-------- src/core/NotePlayHandle.cpp | 58 ++++++++----------- src/core/SamplePlayHandle.cpp | 20 +++++-- src/tracks/InstrumentTrack.cpp | 10 +--- 23 files changed, 137 insertions(+), 124 deletions(-) diff --git a/include/Mixer.h b/include/Mixer.h index 7007e1801..c10bbffbc 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -312,7 +312,6 @@ public: // audio-buffer-mgm void bufferToPort( const sampleFrame * _buf, const fpp_t _frames, - const f_cnt_t _offset, stereoVolumeVector _volume_vector, AudioPort * _port ); diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index c564b1fc8..fc7184525 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -74,6 +74,16 @@ public: { return m_midiChannel; } + + /*! convenience function that returns offset for the first period and zero otherwise, + used by instruments to handle the offset: instruments have to check this property and + add the correct number of empty frames in the beginning of the period */ + f_cnt_t noteOffset() const + { + return m_totalFramesPlayed == 0 + ? offset() + : 0; + } const float& frequency() const { @@ -94,7 +104,7 @@ public: /*! Returns whether playback of note is finished and thus handle can be deleted */ virtual bool isFinished() const { - return m_released && framesLeft() <= 0 && m_scheduledNoteOff < 0; + return m_released && framesLeft() <= 0; } /*! Returns number of frames left for playback */ @@ -264,7 +274,6 @@ 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/include/PlayHandle.h b/include/PlayHandle.h index a9a07f113..152f82989 100644 --- a/include/PlayHandle.h +++ b/include/PlayHandle.h @@ -88,8 +88,8 @@ public: virtual void play( sampleFrame* buffer ) = 0; virtual bool isFinished( void ) const = 0; - // returns how many frames this play-handle is aligned ahead, i.e. - // at which position it is inserted in the according buffer + // returns the frameoffset at the start of the playhandle, + // ie. how many empty frames should be inserted at the start of the first period f_cnt_t offset() const { return m_offset; diff --git a/include/SamplePlayHandle.h b/include/SamplePlayHandle.h index a2ceae7c0..59537ac91 100644 --- a/include/SamplePlayHandle.h +++ b/include/SamplePlayHandle.h @@ -22,8 +22,8 @@ * */ -#ifndef _SAMPLE_PLAY_HANDLE_H -#define _SAMPLE_PLAY_HANDLE_H +#ifndef SAMPLE_PLAY_HANDLE_H +#define SAMPLE_PLAY_HANDLE_H #include "Mixer.h" #include "SampleBuffer.h" @@ -49,7 +49,7 @@ public: } - virtual void play( sampleFrame * _working_buffer ); + virtual void play( sampleFrame * buffer ); virtual bool isFinished() const; virtual bool isFromTrack( const track * _track ) const; diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 726665811..7a13f2887 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -119,6 +119,7 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); // Magic key - a frequency < 20 (say, the bottom piano note if using // a A4 base tuning) restarts the start point. The note is not actually @@ -165,14 +166,14 @@ void audioFileProcessor::playNote( NotePlayHandle * _n, if( ! _n->isFinished() ) { - if( m_sampleBuffer.play( _working_buffer, + if( m_sampleBuffer.play( _working_buffer + offset, (handleState *)_n->m_pluginData, frames, _n->frequency(), static_cast( m_loopModel.value() ) ) ) { applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, - frames,_n ); + frames + offset, _n ); emit isPlaying( ((handleState *)_n->m_pluginData)->frameIndex() ); } diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index 682f470a6..95f06f53a 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -282,9 +282,10 @@ void bitInvader::playNote( NotePlayHandle * _n, } const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); bSynth * ps = static_cast( _n->m_pluginData ); - for( fpp_t frame = 0; frame < frames; ++frame ) + for( fpp_t frame = offset; frame < frames + offset; ++frame ) { const sample_t cur = ps->nextStringSample(); for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) @@ -295,7 +296,7 @@ void bitInvader::playNote( NotePlayHandle * _n, applyRelease( _working_buffer, _n ); - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/kicker/kicker.cpp b/plugins/kicker/kicker.cpp index 0a653a75f..96e0ff4df 100644 --- a/plugins/kicker/kicker.cpp +++ b/plugins/kicker/kicker.cpp @@ -163,6 +163,8 @@ typedef KickerOsc > SweepOsc; void kickerInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { + const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); const float decfr = m_decayModel.value() * engine::mixer()->processingSampleRate() / 1000.0f; const f_cnt_t tfp = _n->totalFramesPlayed(); @@ -187,10 +189,8 @@ void kickerInstrument::playNote( NotePlayHandle * _n, _n->noteOff(); } - const fpp_t frames = _n->framesLeftForCurrentPeriod(); - SweepOsc * so = static_cast( _n->m_pluginData ); - so->update( _working_buffer, frames, engine::mixer()->processingSampleRate() ); + so->update( _working_buffer + offset, frames, engine::mixer()->processingSampleRate() ); if( _n->isReleased() ) { @@ -199,12 +199,12 @@ void kickerInstrument::playNote( NotePlayHandle * _n, for( fpp_t f = 0; f < frames; ++f ) { const float fac = ( done+f < desired ) ? ( 1.0f - ( ( done+f ) / desired ) ) : 0; - _working_buffer[f][0] *= fac; - _working_buffer[f][1] *= fac; + _working_buffer[f+offset][0] *= fac; + _working_buffer[f+offset][1] *= fac; } } - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index e34acc2fe..9feeae411 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -1245,14 +1245,16 @@ MonstroInstrument::~MonstroInstrument() void MonstroInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { + fpp_t frames = _n->framesLeftForCurrentPeriod(); + if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL ) { + _working_buffer += _n->offset(); + frames -= _n->offset(); const sample_rate_t samplerate = m_samplerate; _n->m_pluginData = new MonstroSynth( this, _n, samplerate, m_fpp ); } - const fpp_t frames = _n->framesLeftForCurrentPeriod(); - MonstroSynth * ms = static_cast( _n->m_pluginData ); ms->renderOutput( frames, _working_buffer ); diff --git a/plugins/nes/Nes.cpp b/plugins/nes/Nes.cpp index 1efa56dc9..8f5de4834 100644 --- a/plugins/nes/Nes.cpp +++ b/plugins/nes/Nes.cpp @@ -556,21 +556,22 @@ NesInstrument::~NesInstrument() void NesInstrument::playNote( NotePlayHandle * n, sampleFrame * workingBuffer ) { + const fpp_t frames = n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = n->noteOffset(); + if ( n->totalFramesPlayed() == 0 || n->m_pluginData == NULL ) { NesObject * nes = new NesObject( this, engine::mixer()->processingSampleRate(), n, engine::mixer()->framesPerPeriod() ); n->m_pluginData = nes; } - const fpp_t frames = n->framesLeftForCurrentPeriod(); - NesObject * nes = static_cast( n->m_pluginData ); - nes->renderOutput( workingBuffer, frames ); + nes->renderOutput( workingBuffer + offset, frames ); applyRelease( workingBuffer, n ); - instrumentTrack()->processAudioBuffer( workingBuffer, frames, n ); + instrumentTrack()->processAudioBuffer( workingBuffer, frames + offset, n ); } diff --git a/plugins/organic/organic.cpp b/plugins/organic/organic.cpp index a023afa69..1f4bb2996 100644 --- a/plugins/organic/organic.cpp +++ b/plugins/organic/organic.cpp @@ -227,6 +227,9 @@ QString organicInstrument::nodeName() const void organicInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { + const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); + if( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL ) { Oscillator * oscs_l[m_numOscillators]; @@ -296,10 +299,8 @@ void organicInstrument::playNote( NotePlayHandle * _n, Oscillator * osc_l = static_cast( _n->m_pluginData )->oscLeft; Oscillator * osc_r = static_cast( _n->m_pluginData)->oscRight; - const fpp_t frames = _n->framesLeftForCurrentPeriod(); - - osc_l->update( _working_buffer, frames, 0 ); - osc_r->update( _working_buffer, frames, 1 ); + osc_l->update( _working_buffer + offset, frames, 0 ); + osc_r->update( _working_buffer + offset, frames, 1 ); // -- fx section -- @@ -317,7 +318,7 @@ void organicInstrument::playNote( NotePlayHandle * _n, // -- -- - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/papu/papu_instrument.cpp b/plugins/papu/papu_instrument.cpp index 510042a4c..50ed73f7d 100644 --- a/plugins/papu/papu_instrument.cpp +++ b/plugins/papu/papu_instrument.cpp @@ -238,6 +238,7 @@ void papuInstrument::playNote( NotePlayHandle * _n, const f_cnt_t tfp = _n->totalFramesPlayed(); const int samplerate = engine::mixer()->processingSampleRate(); const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); int data = 0; int freq = _n->frequency(); @@ -400,12 +401,12 @@ void papuInstrument::playNote( NotePlayHandle * _n, for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) { sample_t s = float(buf[frame*2+ch])/32768.0; - _working_buffer[frames-framesleft+frame][ch] = s; + _working_buffer[frames-framesleft+frame+offset][ch] = s; } } framesleft -= count; } - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index 58cf987bc..9df2f1d32 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -138,6 +138,7 @@ void patmanInstrument::playNote( NotePlayHandle * _n, } const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); if( !_n->m_pluginData ) { @@ -148,12 +149,12 @@ void patmanInstrument::playNote( NotePlayHandle * _n, float play_freq = hdata->tuned ? _n->frequency() : hdata->sample->frequency(); - if( hdata->sample->play( _working_buffer, hdata->state, frames, + if( hdata->sample->play( _working_buffer + offset, hdata->state, frames, play_freq, m_loopedModel.value() ? SampleBuffer::LoopOn : SampleBuffer::LoopOff ) ) { applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, - frames, _n ); + frames + offset, _n ); } } diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index 538098360..40182b218 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -454,6 +454,7 @@ void sfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe float currentSampleRate = engine::mixer()->processingSampleRate(); fpp_t frameNum = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL ) { _n->m_pluginData = new SfxrSynth( this ); @@ -477,7 +478,7 @@ void sfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe { for( ch_cnt_t j=0; jprocessAudioBuffer( _working_buffer, frameNum, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frameNum + offset, _n ); } diff --git a/plugins/sid/sid_instrument.cpp b/plugins/sid/sid_instrument.cpp index 637982c80..dd6cd7af5 100644 --- a/plugins/sid/sid_instrument.cpp +++ b/plugins/sid/sid_instrument.cpp @@ -317,6 +317,7 @@ void sidInstrument::playNote( NotePlayHandle * _n, _n->m_pluginData = sid; } const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); cSID *sid = static_cast( _n->m_pluginData ); int delta_t = clockrate * frames / samplerate + 4; @@ -430,11 +431,11 @@ void sidInstrument::playNote( NotePlayHandle * _n, sample_t s = float(buf[frame])/32768.0; for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) { - _working_buffer[frame][ch] = s; + _working_buffer[frame+offset][ch] = s; } } - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/stk/mallets/mallets.cpp b/plugins/stk/mallets/mallets.cpp index c2d91f0c9..cd20ccd7f 100644 --- a/plugins/stk/mallets/mallets.cpp +++ b/plugins/stk/mallets/mallets.cpp @@ -265,6 +265,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n, } const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); malletsSynth * ps = static_cast( _n->m_pluginData ); ps->setFrequency( freq ); @@ -274,7 +275,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n, { add_scale = static_cast( m_strikeModel.value() ) * freq * 2.5f; } - for( fpp_t frame = 0; frame < frames; ++frame ) + for( fpp_t frame = offset; frame < frames + offset; ++frame ) { _working_buffer[frame][0] = ps->nextSampleLeft() * ( m_scalers[m_presetsModel.value()] + add_scale ); @@ -282,7 +283,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n, ( m_scalers[m_presetsModel.value()] + add_scale ); } - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/triple_oscillator/TripleOscillator.cpp b/plugins/triple_oscillator/TripleOscillator.cpp index 50bce3ecc..4ce5ee4d0 100644 --- a/plugins/triple_oscillator/TripleOscillator.cpp +++ b/plugins/triple_oscillator/TripleOscillator.cpp @@ -358,13 +358,14 @@ void TripleOscillator::playNote( NotePlayHandle * _n, Oscillator * osc_r = static_cast( _n->m_pluginData )->oscRight; const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); - osc_l->update( _working_buffer, frames, 0 ); - osc_r->update( _working_buffer, frames, 1 ); + osc_l->update( _working_buffer + offset, frames, 0 ); + osc_r->update( _working_buffer + offset, frames, 1 ); applyRelease( _working_buffer, _n ); - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/vibed/vibed.cpp b/plugins/vibed/vibed.cpp index a4d0b4d36..697812b59 100644 --- a/plugins/vibed/vibed.cpp +++ b/plugins/vibed/vibed.cpp @@ -302,10 +302,11 @@ void vibed::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) } const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); stringContainer * ps = static_cast( _n->m_pluginData ); - for( fpp_t i = 0; i < frames; ++i ) + for( fpp_t i = offset; i < frames + offset; ++i ) { _working_buffer[i][0] = 0.0f; _working_buffer[i][1] = 0.0f; @@ -324,7 +325,7 @@ void vibed::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) } } - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/watsyn/Watsyn.cpp b/plugins/watsyn/Watsyn.cpp index 72af13771..8f0abef28 100644 --- a/plugins/watsyn/Watsyn.cpp +++ b/plugins/watsyn/Watsyn.cpp @@ -343,6 +343,8 @@ void WatsynInstrument::playNote( NotePlayHandle * _n, } const fpp_t frames = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); + sampleFrame * buffer = _working_buffer + offset; WatsynObject * w = static_cast( _n->m_pluginData ); @@ -424,9 +426,9 @@ void WatsynInstrument::playNote( NotePlayHandle * _n, const float amix = 1.0 - bmix; // mix a/b streams according to mixing knob - _working_buffer[f][0] = ( abuf[f][0] * amix ) + + buffer[f][0] = ( abuf[f][0] * amix ) + ( bbuf[f][0] * bmix ); - _working_buffer[f][1] = ( abuf[f][1] * amix ) + + buffer[f][1] = ( abuf[f][1] * amix ) + ( bbuf[f][1] * bmix ); } } @@ -440,16 +442,16 @@ void WatsynInstrument::playNote( NotePlayHandle * _n, for( fpp_t f=0; f < frames; f++ ) { // mix a/b streams according to mixing knob - _working_buffer[f][0] = ( abuf[f][0] * amix ) + + buffer[f][0] = ( abuf[f][0] * amix ) + ( bbuf[f][0] * bmix ); - _working_buffer[f][1] = ( abuf[f][1] * amix ) + + buffer[f][1] = ( abuf[f][1] * amix ) + ( bbuf[f][1] * bmix ); } } applyRelease( _working_buffer, _n ); - instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); + instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 7e2f59799..629cdfa2a 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -133,7 +133,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, if( n->isReleased() == false ) { - envReleaseBegin += engine::mixer()->framesPerPeriod(); + envReleaseBegin += frames; } // because of optimizations, there's special code for several cases: diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index c9fd9b83c..a9c8cad1b 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -451,44 +451,40 @@ void Mixer::clear() -void Mixer::bufferToPort( const sampleFrame * _buf, - const fpp_t _frames, - const f_cnt_t _offset, - stereoVolumeVector _vv, - AudioPort * _port ) +void Mixer::bufferToPort( const sampleFrame * buf, + const fpp_t frames, + stereoVolumeVector vv, + AudioPort * port ) { - const int start_frame = _offset % m_framesPerPeriod; - int end_frame = start_frame + _frames; - const int loop1_frame = qMin( end_frame, m_framesPerPeriod ); + const int loop1_frame = qMin( frames, m_framesPerPeriod ); - _port->lockFirstBuffer(); - MixHelpers::addMultipliedStereo( _port->firstBuffer()+start_frame, // dst - _buf, // src - _vv.vol[0], _vv.vol[1], // coeff left/right - loop1_frame - start_frame ); // frame count - _port->unlockFirstBuffer(); + port->lockFirstBuffer(); + MixHelpers::addMultipliedStereo( port->firstBuffer(), // dst + buf, // src + vv.vol[0], vv.vol[1], // coeff left/right + loop1_frame ); // frame count + port->unlockFirstBuffer(); - _port->lockSecondBuffer(); - if( end_frame > m_framesPerPeriod ) + if( frames > m_framesPerPeriod ) { - const int frames_done = m_framesPerPeriod - start_frame; - end_frame -= m_framesPerPeriod; - end_frame = qMin( end_frame, m_framesPerPeriod ); + port->lockSecondBuffer(); + + const fpp_t framesLeft = qMin( frames - m_framesPerPeriod, m_framesPerPeriod ); - MixHelpers::addMultipliedStereo( _port->secondBuffer(), // dst - _buf+frames_done, // src - _vv.vol[0], _vv.vol[1], // coeff left/right - end_frame ); // frame count + MixHelpers::addMultipliedStereo( port->secondBuffer(), // dst + buf + m_framesPerPeriod, // src + vv.vol[0], vv.vol[1], // coeff left/right + framesLeft ); // frame count // we used both buffers so set flags - _port->m_bufferUsage = AudioPort::BothBuffers; + port->m_bufferUsage = AudioPort::BothBuffers; + port->unlockSecondBuffer(); } - else if( _port->m_bufferUsage == AudioPort::NoUsage ) + else if( port->m_bufferUsage == AudioPort::NoUsage ) { // only first buffer touched - _port->m_bufferUsage = AudioPort::FirstBuffer; + port->m_bufferUsage = AudioPort::FirstBuffer; } - _port->unlockSecondBuffer(); } diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 0a3107acd..ab3bfc3fb 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -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( framesLeft(), engine::mixer()->framesPerPeriod() - offset() ); + } return (fpp_t) qMin( 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) diff --git a/src/core/SamplePlayHandle.cpp b/src/core/SamplePlayHandle.cpp index 95613952e..5e3a4c37e 100644 --- a/src/core/SamplePlayHandle.cpp +++ b/src/core/SamplePlayHandle.cpp @@ -96,7 +96,7 @@ SamplePlayHandle::~SamplePlayHandle() -void SamplePlayHandle::play( sampleFrame * _working_buffer ) +void SamplePlayHandle::play( sampleFrame * buffer ) { //play( 0, _try_parallelizing ); if( framesDone() >= totalFrames() ) @@ -104,17 +104,27 @@ void SamplePlayHandle::play( sampleFrame * _working_buffer ) return; } - const fpp_t frames = engine::mixer()->framesPerPeriod(); + sampleFrame * workingBuffer = buffer; + const fpp_t fpp = engine::mixer()->framesPerPeriod(); + f_cnt_t frames = fpp; + + // apply offset for the first period + if( framesDone() == 0 ) + { + buffer += offset(); + frames -= offset(); + } + if( !( m_track && m_track->isMuted() ) && !( m_bbTrack && m_bbTrack->isMuted() ) ) { stereoVolumeVector v = { { m_volumeModel->value() / DefaultVolume, m_volumeModel->value() / DefaultVolume } }; - m_sampleBuffer->play( _working_buffer, &m_state, frames, + m_sampleBuffer->play( workingBuffer, &m_state, frames, BaseFreq ); - engine::mixer()->bufferToPort( _working_buffer, frames, - offset(), v, m_audioPort ); + engine::mixer()->bufferToPort( buffer, fpp, + v, m_audioPort ); } m_frame += frames; diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index b9e1e21b1..9ae4fe813 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -193,26 +193,22 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, // is no problem for us since we just bypass the envelopes+LFOs if( m_instrument->flags().testFlag( Instrument::IsSingleStreamed ) == false && n != NULL ) { - m_soundShaping.processAudioBuffer( buf, frames, n ); + const f_cnt_t offset = n->noteOffset(); + m_soundShaping.processAudioBuffer( buf + offset, frames - offset, n ); v_scale *= ( (float) n->getVolume() / DefaultVolume ); } m_audioPort.setNextFxChannel( m_effectChannelModel.value() ); - int framesToMix = frames; - int offset = 0; int panning = m_panningModel.value(); if( n ) { - framesToMix = qMin( n->framesLeftForCurrentPeriod(), framesToMix ); - offset = n->offset(); - panning += n->getPanning(); panning = tLimit( panning, PanningLeft, PanningRight ); } - engine::mixer()->bufferToPort( buf, framesToMix, offset, panningToVolumeVector( panning, v_scale ), &m_audioPort ); + engine::mixer()->bufferToPort( buf, frames, panningToVolumeVector( panning, v_scale ), &m_audioPort ); }